diff options
-rw-r--r-- | distrib/notes/i386/hardware | 2 | ||||
-rw-r--r-- | share/man/man4/Makefile | 6 | ||||
-rw-r--r-- | share/man/man4/pci.4 | 6 | ||||
-rw-r--r-- | share/man/man4/trm.4 | 67 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/ic/trm.c | 2980 | ||||
-rw-r--r-- | sys/dev/ic/trm.h | 529 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
-rw-r--r-- | sys/dev/pci/trm_pci.c | 169 |
9 files changed, 3765 insertions, 6 deletions
diff --git a/distrib/notes/i386/hardware b/distrib/notes/i386/hardware index 6f116d2cb58..a56c7e62324 100644 --- a/distrib/notes/i386/hardware +++ b/distrib/notes/i386/hardware @@ -117,6 +117,8 @@ Supported hardware {:-include-:}: name cards, old ASUS cards, the DTC-3130 series, Diamond Fireport series, etc.) Tekram DC-300B and DC-320E (Adaptec AHA-154x clones) + Tekram DC-3x5U (DC-315U, DC-395U/UW/F) TRM-S1040 based + PCI SCSI host adapters Ultrastor 14f, 24f, and 34f WD-7000 SCSI host adapters RAID and Cache Controllers diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index f7d67e96753..f673c8d6d50 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.189 2002/01/24 23:00:05 mickey Exp $ +# $OpenBSD: Makefile,v 1.190 2002/02/18 01:55:30 krw Exp $ MAN= aac.4 ac97.4 addcom.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 ami.4 amphy.4 \ an.4 aria.4 ast.4 atalk.4 atapiscsi.4 \ @@ -24,8 +24,8 @@ MAN= aac.4 ac97.4 addcom.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 ami.4 amphy.4 \ rlphy.4 route.4 rt.4 rtii.4 \ scsi.4 sd.4 ses.4 sf.4 sf2r.4 sf4r.4 siop.4 sis.4 sk.4 sl.4 sm.4 \ spp.4 sppp.4 sqphy.4 ss.4 st.4 ste.4 stge.4 sv.4 \ - tb.4 tcic.4 tcp.4 termios.4 \ - ti.4 tl.4 tlphy.4 tp.4 tqphy.4 tty.4 tun.4 twe.4 tx.4 txp.4 txphy.4 \ + tb.4 tcic.4 tcp.4 termios.4 ti.4 tl.4 tlphy.4 \ + tp.4 tqphy.4 trm.4 tty.4 tun.4 twe.4 tx.4 txp.4 txphy.4 \ uaudio.4 ubsec.4 ucom.4 udp.4 uftdi.4 ugen.4 uhci.4 uhid.4 uk.4 ukbd.4 \ ukphy.4 ulpt.4 umodem.4 ums.4 umass.4 unix.4 upl.4 uplcom.4 urio.4 \ usb.4 uscanner.4 usscanner.4 uvisor.4 uyap.4 vga.4 vlan.4 vnd.4 vr.4 \ diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4 index f821aa9011f..dc948cba563 100644 --- a/share/man/man4/pci.4 +++ b/share/man/man4/pci.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pci.4,v 1.58 2002/01/24 22:43:55 mickey Exp $ +.\" $OpenBSD: pci.4,v 1.59 2002/02/18 01:55:30 krw Exp $ .\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $ .\" .\" Copyright (c) 2000 Theo de Raadt. All rights reserved. @@ -110,6 +110,10 @@ interfaces. AMD Am53c974 PCscsi-PCI .Tn SCSI interfaces. +.It Xr trm 4 +Tekram DC-3x5U (TRM-S1040 based) +.Tn SCSI +interfaces. .El .Ss RAID and cache controllers .Bl -tag -width 10n -offset ind diff --git a/share/man/man4/trm.4 b/share/man/man4/trm.4 new file mode 100644 index 00000000000..2d0683246af --- /dev/null +++ b/share/man/man4/trm.4 @@ -0,0 +1,67 @@ +.\" $OpenBSD: trm.4,v 1.1 2002/02/18 01:55:30 krw Exp $ +.\" +.\" Copyright (c) 2001-2002, Ashley R. Martens and Kenneth R. 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. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd February 16, 2002 +.Dt TRM 4 +.Os +.Sh NAME +.Nm trm +.Nd TRM-S1040 based PCI SCSI Host Adapters +.Sh SYNOPSIS +.Cd "trm* at pci? dev ? function ?" +.Cd "scsibus* at trm?" +.Sh DESCRIPTION +The +.Nm +driver supports PCI SCSI host adapters based on Tekram's TRM-S1040 +chip, including the following: +.Pp +.Bl -bullet -offset indent -compact +.It +Tekram DC395U/UW/F +.It +Tekram DC315/U +.El +.Sh SEE ALSO +.Xr cd 4 , +.Xr intro 4 , +.Xr scsi 4 , +.Xr sd 4 , +.Xr st 4 +.Pp +.Pa http://www.tekram.com/ +.Sh AUTHORS +The +.Nm +driver was written for +.Ox +by Kenneth R. Westerback and Ashley R. Martens, based on a +.Nx +driver by Erich Chen, which was ported to +.Ox +by Martin Akesson. diff --git a/sys/conf/files b/sys/conf/files index 2397a7df7a8..5fccc90a7a4 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.238 2002/02/16 04:36:33 smurph Exp $ +# $OpenBSD: files,v 1.239 2002/02/18 01:55:30 krw Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -267,6 +267,10 @@ file dev/ic/sti.c sti & (sti_pci | sti_sgc) needs-flag device iha: scsi file dev/ic/iha.c iha +# Tekram TRM-S1040 SCSI Cards (DC395U/UW/F,DC315/U) +device trm: scsi +file dev/ic/trm.c trm + # Attributes which machine-independent bus support can be attached to. # These should be defined here, because some of these busses can have # devices which provide these attributes, and we'd like to avoid hairy diff --git a/sys/dev/ic/trm.c b/sys/dev/ic/trm.c new file mode 100644 index 00000000000..5e0e9224974 --- /dev/null +++ b/sys/dev/ic/trm.c @@ -0,0 +1,2980 @@ +/* $OpenBSD: trm.c,v 1.1 2002/02/18 01:55:30 krw Exp $ + * ------------------------------------------------------------ + * O.S : OpenBSD + * File Name : trm.c + * Device Driver for Tekram DC395U/UW/F,DC315/U + * PCI SCSI Bus Master Host Adapter + * (SCSI chip set used Tekram ASIC TRM-S1040) + * + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * (C)Copyright 2001-2002 Ashley R. Martens and Kenneth R Westerback + * ------------------------------------------------------------ + * HISTORY: + * + * REV# DATE NAME DESCRIPTION + * 1.00 05/01/99 ERICH CHEN First released for NetBSD 1.4.x + * 1.01 00/00/00 MARTIN AKESSON Port to OpenBSD 2.8 + * 1.02 09/19/01 ASHLEY MARTENS Cleanup and formatting + * 2.00 01/00/02 KENNETH R WESTERBACK Rewrite of the bus and code logic + * ------------------------------------------------------------ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ------------------------------------------------------------ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/device.h> + +#include <machine/bus.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> + +#include <dev/pci/pcidevs.h> +#include <dev/ic/trm.h> + +/* #define TRM_DEBUG0 */ + +void trm_minphys(struct buf *); + +void trm_initSRB(struct trm_scsi_req_q *); + +void trm_check_eeprom(struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); +void trm_read_all (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); +void trm_write_all (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); + +void trm_set_data (bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t); +void trm_write_cmd(bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t); + +u_int8_t trm_get_data(bus_space_tag_t, bus_space_handle_t, u_int8_t); + +void trm_wait_30us(bus_space_tag_t, bus_space_handle_t); + +int trm_scsi_cmd(struct scsi_xfer *); + +struct trm_scsi_req_q *trm_GetFreeSRB(struct trm_softc *); + +void trm_DataOutPhase0(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_DataInPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_StatusPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_MsgOutPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_MsgInPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_DataOutPhase1(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_DataInPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_CommandPhase1(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_StatusPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_MsgOutPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_MsgInPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); +void trm_Nop (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); + +void trm_SetXferParams (struct trm_softc *, struct trm_dcb *, int); + +void trm_DataIO_transfer(struct trm_softc *, struct trm_scsi_req_q *, u_int16_t); + +int trm_StartSRB (struct trm_softc *, struct trm_scsi_req_q *); +void trm_ReleaseSRB (struct trm_softc *, struct trm_scsi_req_q *); +void trm_RewaitSRB (struct trm_softc *, struct trm_scsi_req_q *); +void trm_FinishSRB (struct trm_softc *, struct trm_scsi_req_q *); +void trm_RequestSense(struct trm_softc *, struct trm_scsi_req_q *); + +void trm_initAdapter (struct trm_softc *); +void trm_Disconnect (struct trm_softc *); +void trm_Reselect (struct trm_softc *); +void trm_GoingSRB_Done (struct trm_softc *); +void trm_ScsiRstDetect (struct trm_softc *); +void trm_ResetSCSIBus (struct trm_softc *); +void trm_reset (struct trm_softc *); +void trm_StartWaitingSRB (struct trm_softc *); +void trm_ResetAllDevParam(struct trm_softc *); +void trm_RecoverSRB (struct trm_softc *); +void trm_linkSRB (struct trm_softc *); + +void trm_initACB(struct trm_softc *, int); + +void trm_ResetDevParam(struct trm_softc *, struct trm_dcb *, u_int8_t); + +void trm_EnableMsgOut(struct trm_softc *, u_int8_t); + +void trm_timeout(void *); + +void trm_print_info(struct trm_softc *, struct trm_dcb *); + +/* + * Define structures + */ +struct cfdriver trm_cd = { + NULL, + "trm", + DV_DULL, + 0, + 0 +}; + +struct scsi_adapter trm_switch = { + trm_scsi_cmd, + trm_minphys, + NULL, + NULL +}; + +static struct scsi_device trm_device = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +/* + * ------------------------------------------------------------ + * + * stateV = (void *) trm_SCSI_phase0[phase] + * + * ------------------------------------------------------------ + */ +static void *trm_SCSI_phase0[8] = { + trm_DataOutPhase0, /* phase:0 */ + trm_DataInPhase0, /* phase:1 */ + trm_Nop, /* phase:2 */ + trm_StatusPhase0, /* phase:3 */ + trm_Nop, /* phase:4 */ + trm_Nop, /* phase:5 */ + trm_MsgOutPhase0, /* phase:6 */ + trm_MsgInPhase0, /* phase:7 */ +}; + +/* + * ------------------------------------------------------------ + * + * stateV = (void *) trm_SCSI_phase1[phase] + * + * ------------------------------------------------------------ + */ +static void *trm_SCSI_phase1[8] = { + trm_DataOutPhase1, /* phase:0 */ + trm_DataInPhase1, /* phase:1 */ + trm_CommandPhase1, /* phase:2 */ + trm_StatusPhase1, /* phase:3 */ + trm_Nop, /* phase:4 */ + trm_Nop, /* phase:5 */ + trm_MsgOutPhase1, /* phase:6 */ + trm_MsgInPhase1, /* phase:7 */ +}; + + +struct trm_adapter_nvram trm_eepromBuf[TRM_MAX_ADAPTER_NUM]; +/* + *Fast20: 000 50ns, 20.0 Mbytes/s + * 001 75ns, 13.3 Mbytes/s + * 010 100ns, 10.0 Mbytes/s + * 011 125ns, 8.0 Mbytes/s + * 100 150ns, 6.6 Mbytes/s + * 101 175ns, 5.7 Mbytes/s + * 110 200ns, 5.0 Mbytes/s + * 111 250ns, 4.0 Mbytes/s + * + *Fast40: 000 25ns, 40.0 Mbytes/s + * 001 50ns, 20.0 Mbytes/s + * 010 75ns, 13.3 Mbytes/s + * 011 100ns, 10.0 Mbytes/s + * 100 125ns, 8.0 Mbytes/s + * 101 150ns, 6.6 Mbytes/s + * 110 175ns, 5.7 Mbytes/s + * 111 200ns, 5.0 Mbytes/s + */ + +/* + * real period: + */ +u_int8_t trm_clock_period[8] = { + /* nanosecond divided by 4 */ + 12, /* 48 ns 20 MB/sec */ + 18, /* 72 ns 13.3 MB/sec */ + 25, /* 100 ns 10.0 MB/sec */ + 31, /* 124 ns 8.0 MB/sec */ + 37, /* 148 ns 6.6 MB/sec */ + 43, /* 172 ns 5.7 MB/sec */ + 50, /* 200 ns 5.0 MB/sec */ + 62 /* 248 ns 4.0 MB/sec */ +}; + +/* + * ------------------------------------------------------------ + * Function : trm_GetFreeSRB + * Purpose : Get the first free SRB + * Inputs : + * Return : NULL or a free SCSI Request block + * ------------------------------------------------------------ + */ +struct trm_scsi_req_q * +trm_GetFreeSRB(struct trm_softc *sc) +{ + struct trm_scsi_req_q *pSRB; + + /* ASSUME we are called from inside a splbio()/splx() region */ + + pSRB = TAILQ_FIRST(&sc->freeSRB); + + if (pSRB != NULL) + TAILQ_REMOVE(&sc->freeSRB, pSRB, link); + +#ifdef TRM_DEBUG0 + printf("%s: trm_GetFreeSRB. pSRB = %p, next pSRB = %p\n", + sc->sc_device.dv_xname, pSRB, TAILQ_FIRST(&sc->freeSRB)); +#endif + + return pSRB; +} + +/* + * ------------------------------------------------------------ + * Function : trm_RewaitSRB + * Purpose : Q back to pending Q + * Inputs : struct trm_dcb * - + * struct trm_scsi_req_q * - + * ------------------------------------------------------------ + */ +void +trm_RewaitSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) +{ + int intflag; + + intflag = splbio(); + + if ((pSRB->SRBFlag & TRM_ON_WAITING_SRB) != 0) { + pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB; + TAILQ_REMOVE(&sc->waitingSRB, pSRB, link); + } + + if ((pSRB->SRBFlag & TRM_ON_GOING_SRB) != 0) { + pSRB->SRBFlag &= ~TRM_ON_GOING_SRB; + TAILQ_REMOVE(&sc->goingSRB, pSRB, link); + } + + pSRB->SRBState = TRM_READY; + pSRB->TargetStatus = SCSI_OK; + pSRB->AdaptStatus = TRM_STATUS_GOOD; + + pSRB->SRBFlag |= TRM_ON_WAITING_SRB; + TAILQ_INSERT_HEAD(&sc->waitingSRB, pSRB, link); + + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_StartWaitingSRB + * Purpose : If there is no active DCB then run robin through + * the DCB's to find the next waiting SRB + * and move it to the going list. + * Inputs : struct trm_softc * - + * ------------------------------------------------------------ + */ +void +trm_StartWaitingSRB(struct trm_softc *sc) +{ + struct trm_scsi_req_q *pSRB; + int intflag; + + intflag = splbio(); + + if ((sc->pActiveDCB != NULL) || + (TAILQ_EMPTY(&sc->waitingSRB)) || + (sc->sc_Flag & (RESET_DETECT | RESET_DONE | RESET_DEV)) != 0) + return; + + TAILQ_FOREACH(pSRB, &sc->waitingSRB, link) { + if (trm_StartSRB(sc, pSRB) == 0) { + pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB; + TAILQ_REMOVE(&sc->waitingSRB, pSRB, link); + pSRB->SRBFlag |= TRM_ON_GOING_SRB; + TAILQ_INSERT_TAIL(&sc->goingSRB, pSRB, link); + break; + } + } + + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_scsi_cmd + * Purpose : enqueues a SCSI command + * Inputs : + * Call By : GENERIC SCSI driver + * ------------------------------------------------------------ + */ +int +trm_scsi_cmd(struct scsi_xfer *xs) +{ + struct trm_scsi_req_q *pSRB; + bus_space_handle_t ioh; + struct trm_softc *sc; + bus_space_tag_t iot; + struct trm_dcb *pDCB; + u_int8_t target, lun; + int i, error, intflag, xferflags; + + target = xs->sc_link->target; + lun = xs->sc_link->lun; + + sc = (struct trm_softc *)xs->sc_link->adapter_softc; + ioh = sc->sc_iohandle; + iot = sc->sc_iotag; + +#ifdef TRM_DEBUG0 + if ((xs->flags & SCSI_POLL) != 0) + printf("%s: trm_scsi_cmd. sc = %p, xs = %p, targ/lun = %d/%d opcode = 0x%02x\n", + sc->sc_device.dv_xname, sc, xs, target, lun, xs->cmd->opcode); +#endif + + if (target >= TRM_MAX_TARGETS) { + printf("%s: target=%d >= %d\n", + sc->sc_device.dv_xname, target, TRM_MAX_TARGETS); + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + if (lun >= TRM_MAX_LUNS) { + printf("%s: lun=%d >= %d\n", + sc->sc_device.dv_xname, lun, TRM_MAX_LUNS); + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + + pDCB = sc->pDCB[target][lun]; + if (pDCB == NULL) { + /* Removed as a result of INQUIRY proving no device present */ + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + + xferflags = xs->flags; + if (xferflags & SCSI_RESET) { +#ifdef TRM_DEBUG0 + printf("%s: trm_reset\n", sc->sc_device.dv_xname); +#endif + trm_reset(sc); + xs->error = XS_NOERROR; + return COMPLETE; + } + + if (xferflags & ITSDONE) { +#ifdef TRM_DEBUG0 + printf("%s: Is it done?\n", sc->sc_device.dv_xname); +#endif + xs->flags &= ~ITSDONE; + } + + xs->error = XS_NOERROR; + xs->status = SCSI_OK; + xs->resid = 0; + + intflag = splbio(); + + pSRB = trm_GetFreeSRB(sc); + + if (pSRB == NULL) { + xs->error = XS_DRIVER_STUFFUP; + splx(intflag); + return TRY_AGAIN_LATER; + } + + /* + * BuildSRB(pSRB,pDCB); + */ + if (xs->datalen != 0) { +#ifdef TRM_DEBUG0 + printf("%s: xs->datalen=%x\n", sc->sc_device.dv_xname, + (u_int32_t)xs->datalen); + printf("%s: sc->sc_dmatag=0x%x\n", sc->sc_device.dv_xname, + (u_int32_t)sc->sc_dmatag); + printf("%s: pSRB->dmamapxfer=0x%x\n", sc->sc_device.dv_xname, + (u_int32_t)pSRB->dmamapxfer); + printf("%s: xs->data=0x%x\n", sc->sc_device.dv_xname, + (u_int32_t)xs->data); +#endif + if ((error = bus_dmamap_load(sc->sc_dmatag, pSRB->dmamapxfer, + xs->data, xs->datalen, NULL, + (xferflags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : + BUS_DMA_WAITOK)) != 0) { + printf("%s: DMA transfer map unable to load, error = %d\n" + , sc->sc_device.dv_xname, error); + xs->error = XS_DRIVER_STUFFUP; + /* + * free SRB + */ + TAILQ_INSERT_HEAD(&sc->freeSRB, pSRB, link); + splx(intflag); + return COMPLETE; + } + + bus_dmamap_sync(sc->sc_dmatag, pSRB->dmamapxfer, + 0, pSRB->dmamapxfer->dm_mapsize, + (xferflags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + /* + * Set up the scatter gather list + */ + for (i = 0; i < pSRB->dmamapxfer->dm_nsegs; i++) { + pSRB->SegmentX[i].address = pSRB->dmamapxfer->dm_segs[i].ds_addr; + pSRB->SegmentX[i].length = pSRB->dmamapxfer->dm_segs[i].ds_len; + } + pSRB->SRBTotalXferLength = xs->datalen; + pSRB->SRBSGCount = pSRB->dmamapxfer->dm_nsegs; + } + + pSRB->pSRBDCB = pDCB; + pSRB->xs = xs; + pSRB->ScsiCmdLen = xs->cmdlen; + + memcpy(pSRB->CmdBlock, xs->cmd, xs->cmdlen); + + splx (intflag); + + timeout_set(&xs->stimeout, trm_timeout, pSRB); + + intflag = splbio(); + + pSRB->SRBFlag |= TRM_ON_WAITING_SRB; + TAILQ_INSERT_TAIL(&sc->waitingSRB, pSRB, link); + trm_StartWaitingSRB(sc); + + if ((xferflags & SCSI_POLL) == 0) { + timeout_add(&xs->stimeout, (xs->timeout * hz) / 1000); + splx(intflag); + return SUCCESSFULLY_QUEUED; + } + + while ((--xs->timeout > 0) && ((xs->flags & ITSDONE) == 0)) { + trm_Interrupt(sc); + DELAY(1000); + } + + if (xs->timeout == 0) + trm_timeout(pSRB); + + splx(intflag); + return COMPLETE; +} + +/* + * ------------------------------------------------------------ + * Function : trm_ResetAllDevParam + * Purpose : + * Inputs : struct trm_softc * + * ------------------------------------------------------------ + */ +void +trm_ResetAllDevParam(struct trm_softc *sc) +{ + struct trm_adapter_nvram *pEEpromBuf; + int target, quirks; + + pEEpromBuf = &trm_eepromBuf[sc->sc_AdapterUnit]; + + for (target = 0; target < TRM_MAX_TARGETS; target++) { + if (target == sc->sc_AdaptSCSIID) + continue; + + if ((sc->pDCB[target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0) + quirks = SDEV_NOWIDE | SDEV_NOSYNC | SDEV_NOTAGS; + else + quirks = sc->pDCB[target][0]->sc_link->quirks; + + trm_ResetDevParam(sc, sc->pDCB[target][0], quirks); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_ResetDevParam + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_ResetDevParam(struct trm_softc *sc, struct trm_dcb *pDCB, u_int8_t quirks) +{ + struct trm_adapter_nvram *pEEpromBuf = &trm_eepromBuf[sc->sc_AdapterUnit]; + u_int8_t PeriodIndex; + const int target = pDCB->target; + + pDCB->DCBFlag &= TRM_QUIRKS_VALID; + pDCB->DCBFlag |= (TRM_WIDE_NEGO_ENABLE | TRM_SYNC_NEGO_ENABLE); + + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + pDCB->MaxNegoPeriod = 0; + + pDCB->DevMode = pEEpromBuf->NvramTarget[target].NvmTarCfg0; + + pDCB->IdentifyMsg = MSG_IDENTIFY(pDCB->lun, ((pDCB->DevMode & TRM_DISCONNECT) != 0)); + + if (((quirks & SDEV_NOWIDE) == 0) && + (pDCB->DevMode & TRM_WIDE) && + ((sc->sc_config & HCC_WIDE_CARD) != 0)) + pDCB->DCBFlag |= TRM_WIDE_NEGO_16BIT; + + if (((quirks & SDEV_NOSYNC) == 0) && + ((pDCB->DevMode & TRM_SYNC) != 0)) { + PeriodIndex = pEEpromBuf->NvramTarget[target].NvmTarPeriod & 0x07; + pDCB->MaxNegoPeriod = trm_clock_period[PeriodIndex]; + } + + if (((quirks & SDEV_NOTAGS) == 0) && + ((pDCB->DevMode & TRM_TAG_QUEUING) != 0) && + ((pDCB->DevMode & TRM_DISCONNECT) != 0)) + /* TODO XXXX: Every device(lun) gets to queue TagMaxNum commands? */ + pDCB->DCBFlag |= TRM_USE_TAG_QUEUING; + + trm_SetXferParams(sc, pDCB, 0); +} + +/* + * ------------------------------------------------------------ + * Function : trm_RecoverSRB + * Purpose : Moves all SRBs from Going to Waiting for all the Link DCBs + * Inputs : struct trm_softc * - + * ------------------------------------------------------------ + */ +void +trm_RecoverSRB(struct trm_softc *sc) +{ + struct trm_scsi_req_q *pSRB; + + /* ASSUME we are inside splbio()/splx() */ + + while ((pSRB = TAILQ_FIRST(&sc->goingSRB)) != NULL) { + pSRB->SRBFlag &= ~TRM_ON_GOING_SRB; + TAILQ_REMOVE(&sc->goingSRB, pSRB, link); + pSRB->SRBFlag |= TRM_ON_WAITING_SRB; + TAILQ_INSERT_HEAD(&sc->waitingSRB, pSRB, link); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_reset + * Purpose : perform a hard reset on the SCSI bus (and TRM_S1040 chip). + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_reset (struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + int i, intflag; + +#ifdef TRM_DEBUG0 + printf("%s: trm_reset", sc->sc_device.dv_xname); +#endif + + intflag = splbio(); + + bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, 0); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, 0); + + trm_ResetSCSIBus(sc); + for (i = 0; i < 500; i++) + DELAY(1000); + + /* + * Enable all SCSI interrupts except EN_SCAM + */ + bus_space_write_1(iot, ioh, + TRM_S1040_SCSI_INTEN, + (EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED | + EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE)); + /* + * Enable DMA interrupt + */ + bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, EN_SCSIINTR); + /* + * Clear DMA FIFO + */ + bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, CLRXFIFO); + /* + * Clear SCSI FIFO + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + + trm_ResetAllDevParam(sc); + trm_GoingSRB_Done(sc); + sc->pActiveDCB = NULL; + + /* + * RESET_DETECT, RESET_DONE, RESET_DEV + */ + sc->sc_Flag = 0; + trm_StartWaitingSRB(sc); + + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_timeout + * Purpose : Prints a timeout message and aborts the timed out SCSI request + * Inputs : void * - A struct trm_scsi_req_q * structure pointer + * ------------------------------------------------------------ + */ +void +trm_timeout(void *arg1) +{ + struct trm_scsi_req_q *pSRB; + struct scsi_xfer *xs; + struct trm_softc *sc; + + pSRB = (struct trm_scsi_req_q *)arg1; + xs = pSRB->xs; + + if (xs != NULL) { + sc = xs->sc_link->adapter_softc; + sc_print_addr(xs->sc_link); + printf("%s: SCSI OpCode 0x%02x for target %d lun %d timed out\n", + sc->sc_device.dv_xname, xs->cmd->opcode, + xs->sc_link->target, xs->sc_link->lun); + pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT; + trm_FinishSRB(sc, pSRB); + trm_StartWaitingSRB(sc); + } +#ifdef TRM_DEBUG0 + else + printf("%s: trm_timeout called with xs == NULL\n", + sc->sc_device.dv_xname); +#endif +} + +/* + * ------------------------------------------------------------ + * Function : trm_StartSRB + * Purpose : Send the commands in the SRB to the device + * Inputs : struct trm_softc * - + * struct trm_scsi_req_q * - + * Return : 0 - SCSI processor is unoccupied + * 1 - SCSI processor is occupied with an SRB + * ------------------------------------------------------------ + */ +int +trm_StartSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_dcb *pDCB = pSRB->pSRBDCB; + u_int32_t tag_mask; + u_int8_t tag_id, scsicommand; + +#ifdef TRM_DEBUG0 + printf("%s: trm_StartSRB. sc = %p, pDCB = %p, pSRB = %p\n", + sc->sc_device.dv_xname, sc, pDCB, pSRB); +#endif + if ((pDCB->DCBFlag & TRM_QUEUE_FULL) != 0) + return 1; + + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, pDCB->target); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + + if ((sc->pDCB[pDCB->target][0]->sc_link != NULL) && + ((sc->pDCB[pDCB->target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0)) { + sc->pDCB[pDCB->target][0]->DCBFlag |= TRM_QUIRKS_VALID; + trm_ResetDevParam(sc, sc->pDCB[pDCB->target][0], sc->pDCB[pDCB->target][0]->sc_link->quirks); + } + + /* + * initial phase + */ + + pSRB->ScsiPhase = PH_BUS_FREE; + + /* + * Flush FIFO + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + + sc->MsgCnt = 1; + sc->MsgBuf[0] = pDCB->IdentifyMsg; + if (((pSRB->xs->flags & SCSI_POLL) != 0) || + (pSRB->CmdBlock[0] == INQUIRY) || + (pSRB->CmdBlock[0] == REQUEST_SENSE)) + sc->MsgBuf[0] &= ~MSG_IDENTIFY_DISCFLAG; + + scsicommand = SCMD_SEL_ATN; + pSRB->SRBState = TRM_START; + + if ((pDCB->DCBFlag & (TRM_WIDE_NEGO_ENABLE | TRM_SYNC_NEGO_ENABLE)) != 0) { + scsicommand = SCMD_SEL_ATNSTOP; + pSRB->SRBState = TRM_MSGOUT; + + } else if (((sc->MsgBuf[0] & MSG_IDENTIFY_DISCFLAG) != 0) && + ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0)) { + + if (pSRB->TagNumber == TRM_NO_TAG) { + for (tag_id=1, tag_mask=2; tag_id < 32; tag_id++, tag_mask <<= 1) + if ((tag_mask & pDCB->TagMask) == 0) { + pDCB->TagMask |= tag_mask; + pSRB->TagNumber = tag_id; + break; + } + + if (tag_id >= 32) { + pDCB->DCBFlag |= TRM_QUEUE_FULL; + sc->MsgCnt = 0; + return 1; + } + } + + /* TODO XXXX: Should send ORDERED_Q_TAG if metadata (non-block) i/o!? */ + sc->MsgBuf[sc->MsgCnt++] = MSG_SIMPLE_Q_TAG; + sc->MsgBuf[sc->MsgCnt++] = pSRB->TagNumber; + + scsicommand = SCMD_SEL_ATN3; + } + + if ((bus_space_read_2(iot, ioh, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) != 0) { + /* + * return 1 : + * current interrupt status is interrupt disreenable + * It's said that SCSI processor has more then one SRB it needs + * to do, SCSI processor has been occupied by one SRB. + */ + pSRB->SRBState = TRM_READY; + return 1; + } + + /* + * return 0 : + * current interrupt status is interrupt enable + * It's said that SCSI processor is unoccupied + */ + pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ + sc->pActiveDCB = pDCB; + pDCB->pActiveSRB = pSRB; + + if (sc->MsgCnt > 0) { + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_FIFO, sc->MsgBuf[0]); + if (sc->MsgCnt > 1) { + DELAY(30); + bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[1], sc->MsgCnt - 1); + } + sc->MsgCnt = 0; + } + + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH | DO_HWRESELECT); + /* + * SCSI command + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, scsicommand); + + return 0; +} + +/* + * ------------------------------------------------------------ + * Function : trm_Interrupt + * Purpose : Catch an interrupt from the adapter + * Process pending device interrupts. + * Inputs : void * - struct trm_softc * strucutre pointer + * ------------------------------------------------------------ + */ +int +trm_Interrupt(void *vsc) +{ + void (*stateV)(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); + struct trm_scsi_req_q *pSRB; + bus_space_handle_t ioh; + struct trm_softc *sc = (struct trm_softc *)vsc; + bus_space_tag_t iot; + u_int16_t phase; + u_int8_t scsi_status, scsi_intstatus; + int intflag; + + intflag = splbio(); + + if (sc == NULL) { + splx(intflag); + return 0; + } + + ioh = sc->sc_iohandle; + iot = sc->sc_iotag; + + scsi_status = bus_space_read_2(iot, ioh, TRM_S1040_SCSI_STATUS); + if (!(scsi_status & SCSIINTERRUPT)) { + splx(intflag); + return 0; + } + scsi_intstatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS); + +#ifdef TRM_DEBUG0 + printf("%s: trm_interrupt - scsi_status=0x%02x, scsi_intstatus=0x%02x\n", + sc->sc_device.dv_xname, scsi_status, scsi_intstatus); +#endif + if ((scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) != 0) + trm_Disconnect(sc); + + else if ((scsi_intstatus & INT_RESELECTED) != 0) + trm_Reselect(sc); + + else if ((scsi_intstatus & INT_SCSIRESET) != 0) + trm_ScsiRstDetect(sc); + + else if ((sc->pActiveDCB != NULL) && ((scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) != 0)) { + pSRB = sc->pActiveDCB->pActiveSRB; + /* + * software sequential machine + */ + phase = (u_int16_t) pSRB->ScsiPhase; /* phase: */ + /* + * 62037 or 62137 + * call trm_SCSI_phase0[]... "phase entry" + * handle every phase before start transfer + */ + stateV = trm_SCSI_phase0[phase]; + stateV(sc, pSRB, &scsi_status); + /* + * if any exception occured + * scsi_status will be modified to bus free phase + * new scsi_status transfer out from previous stateV + */ + /* + * phase:0,1,2,3,4,5,6,7 + */ + pSRB->ScsiPhase = scsi_status & PHASEMASK; + phase = (u_int16_t) scsi_status & PHASEMASK; + /* + * call trm_SCSI_phase1[]... "phase entry" + * handle every phase do transfer + */ + stateV = trm_SCSI_phase1[phase]; + stateV(sc, pSRB, &scsi_status); + + } else { + splx(intflag); + return 0; + } + + splx(intflag); + return 1; +} + +/* + * ------------------------------------------------------------ + * Function : trm_MsgOutPhase0 + * Purpose : Check the state machine before sending a message out + * Inputs : struct trm_softc * - + * struct trm_scsi_req_q * - + * u_int8_t * - scsi status, set to PH_BUS_FREE if not ready + * ------------------------------------------------------------ + */ +void +trm_MsgOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + switch (pSRB->SRBState) { + case TRM_UNEXPECT_RESEL: + case TRM_ABORT_SENT: + *pscsi_status = PH_BUS_FREE; /* initial phase */ + break; + + default: + break; + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_MsgOutPhase1 + * Purpose : Write the message out to the bus + * Inputs : struct trm_softc * - + * struct trm_scsi_req_q * - + * u_int8_t * - unused + * ------------------------------------------------------------ + */ +void +trm_MsgOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_dcb *pDCB = sc->pActiveDCB; + + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + + if ((pDCB->DCBFlag & TRM_WIDE_NEGO_ENABLE) != 0) { + /* + * WIDE DATA TRANSFER REQUEST code (03h) + */ + pDCB->DCBFlag &= ~TRM_WIDE_NEGO_ENABLE; + pDCB->DCBFlag |= TRM_DOING_WIDE_NEGO; + + sc->MsgBuf[0] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG; + sc->MsgBuf[1] = MSG_EXTENDED; + sc->MsgBuf[2] = MSG_EXT_WDTR_LEN; + sc->MsgBuf[3] = MSG_EXT_WDTR; + + if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0) + sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_8_BIT; + else + sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_16_BIT; + + sc->MsgCnt = 5; + + } else if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) { + + pDCB->DCBFlag &= ~TRM_SYNC_NEGO_ENABLE; + pDCB->DCBFlag |= TRM_DOING_SYNC_NEGO; + + sc->MsgCnt = 0; + + if ((pDCB->DCBFlag & TRM_WIDE_NEGO_DONE) == 0) + sc->MsgBuf[sc->MsgCnt++] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG; + + sc->MsgBuf[sc->MsgCnt++] = MSG_EXTENDED; + sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR_LEN; + sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR; + sc->MsgBuf[sc->MsgCnt++] = pDCB->MaxNegoPeriod; + + if (pDCB->MaxNegoPeriod > 0) + sc->MsgBuf[sc->MsgCnt++] = TRM_MAX_SYNC_OFFSET; + else + sc->MsgBuf[sc->MsgCnt++] = 0; + } + + if (sc->MsgCnt > 0) { + bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[0], sc->MsgCnt); + if (sc->MsgBuf[0] == MSG_ABORT) + pSRB->SRBState = TRM_ABORT_SENT; + sc->MsgCnt = 0; + } + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Transfer information out + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_CommandPhase1 + * Purpose : Send commands to bus + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_CommandPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN | DO_CLRFIFO); + + bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &pSRB->CmdBlock[0], pSRB->ScsiCmdLen); + + pSRB->SRBState = TRM_COMMAND; + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Transfer information out + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_DataOutPhase0 + * Purpose : Ready for Data Out, clear FIFO + * Inputs : u_int8_t * - SCSI status, used but not set + * ------------------------------------------------------------ + */ +void +trm_DataOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct SGentry *pseg; + struct trm_dcb *pDCB; + u_int32_t dLeftCounter, TempSRBXferredLength; + u_int16_t scsi_status; + u_int8_t TempDMAstatus, SGIndexTemp; + + dLeftCounter = 0; + + pDCB = pSRB->pSRBDCB; + scsi_status = *pscsi_status; + + if (pSRB->SRBState != TRM_XFERPAD) { + if ((scsi_status & PARITYERROR) != 0) + pSRB->SRBFlag |= TRM_PARITY_ERROR; + if ((scsi_status & SCSIXFERDONE) == 0) { + /* + * when data transfer from DMA FIFO to SCSI FIFO + * if there was some data left in SCSI FIFO + */ + dLeftCounter = (u_int32_t)(bus_space_read_1( + iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x1F); + if (pDCB->SyncPeriod & WIDE_SYNC) { + /* + * if WIDE scsi SCSI FIFOCNT unit is word + * so need to * 2 + */ + dLeftCounter <<= 1; + } + } + /* + * caculate all the residue data that not yet tranfered + * SCSI transfer counter + left in SCSI FIFO data + * + * .....TRM_S1040_SCSI_COUNTER (24bits) + * The counter always decrement by one for every SCSI byte + * transfer. + * .....TRM_S1040_SCSI_FIFOCNT ( 5bits) + * The counter is SCSI FIFO offset counter + */ + dLeftCounter += bus_space_read_4(iot, ioh, + TRM_S1040_SCSI_COUNTER); + if (dLeftCounter == 1) { + dLeftCounter = 0; + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, + DO_CLRFIFO); + } + if (dLeftCounter == 0 || + (scsi_status & SCSIXFERCNT_2_ZERO) != 0) { + TempDMAstatus = bus_space_read_1(iot, + ioh, TRM_S1040_DMA_STATUS); + while ((TempDMAstatus & DMAXFERCOMP) == 0) { + TempDMAstatus = bus_space_read_1(iot, + ioh, TRM_S1040_DMA_STATUS); + } + pSRB->SRBTotalXferLength = 0; + } else { + /* + * Update SG list + */ + /* + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + if (pSRB->SRBTotalXferLength != dLeftCounter) { + /* + * data that had transferred length + */ + TempSRBXferredLength = pSRB->SRBTotalXferLength + - dLeftCounter; + /* + * next time to be transferred length + */ + pSRB->SRBTotalXferLength = dLeftCounter; + /* + * parsing from last time disconnect SRBSGIndex + */ + pseg = &pSRB->SegmentX[pSRB->SRBSGIndex]; + for (SGIndexTemp = pSRB->SRBSGIndex; + SGIndexTemp < pSRB->SRBSGCount; + SGIndexTemp++) { + /* + * find last time which SG transfer be + * disconnect + */ + if (TempSRBXferredLength >= pseg->length) + TempSRBXferredLength -= pseg->length; + else { + /* + * update last time disconnected + * SG list + */ + /* + * residue data length + */ + pseg->length -= + TempSRBXferredLength; + /* + * residue data pointer + */ + pseg->address += + TempSRBXferredLength; + pSRB->SRBSGIndex = SGIndexTemp; + break; + } + pseg++; + } + } + } + } + bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER); +} + +/* + * ------------------------------------------------------------ + * Function : trm_DataOutPhase1 + * Purpose : Transfers data out, calls trm_DataIO_transfer + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_DataOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + trm_DataIO_transfer(sc, pSRB, XFERDATAOUT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_DataInPhase0 + * Purpose : Prepare for reading data in from bus + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_DataInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct SGentry *pseg; + u_int32_t TempSRBXferredLength, dLeftCounter; + u_int16_t scsi_status; + u_int8_t SGIndexTemp; + + dLeftCounter = 0; + + scsi_status = *pscsi_status; + if (pSRB->SRBState != TRM_XFERPAD) { + if ((scsi_status & PARITYERROR) != 0) + pSRB->SRBFlag |= TRM_PARITY_ERROR; + dLeftCounter += bus_space_read_4(iot, ioh, + TRM_S1040_SCSI_COUNTER); + if (dLeftCounter == 0 || + (scsi_status & SCSIXFERCNT_2_ZERO) != 0) { + while ((bus_space_read_1(iot, ioh, TRM_S1040_DMA_STATUS) & DMAXFERCOMP) == 0) + ; + pSRB->SRBTotalXferLength = 0; + } else { + /* + * phase changed + * + * parsing the case: + * when a transfer not yet complete + * but be disconnected by uper layer + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + if (pSRB->SRBTotalXferLength != dLeftCounter) { + /* + * data that had transferred length + */ + TempSRBXferredLength = pSRB->SRBTotalXferLength + - dLeftCounter; + /* + * next time to be transferred length + */ + pSRB->SRBTotalXferLength = dLeftCounter; + /* + * parsing from last time disconnect SRBSGIndex + */ + pseg = &pSRB->SegmentX[pSRB->SRBSGIndex]; + for (SGIndexTemp = pSRB->SRBSGIndex; + SGIndexTemp < pSRB->SRBSGCount; + SGIndexTemp++) { + /* + * find last time which SG transfer be + * disconnect + */ + if (TempSRBXferredLength >= + pseg->length) { + TempSRBXferredLength -= pseg->length; + } else { + /* + * update last time disconnected + * SG list + * + * residue data length + */ + pseg->length -= TempSRBXferredLength; + /* + * residue data pointer + */ + pseg->address += TempSRBXferredLength; + pSRB->SRBSGIndex = SGIndexTemp; + break; + } + pseg++; + } + } + } + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_DataInPhase1 + * Purpose : Transfer data in from bus, calls trm_DataIO_transfer + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_DataInPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + trm_DataIO_transfer(sc, pSRB, XFERDATAIN); +} + +/* + * ------------------------------------------------------------ + * Function : trm_DataIO_transfer + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_DataIO_transfer(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int16_t ioDir) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_dcb *pDCB = pSRB->pSRBDCB; + u_int8_t bval; + + if (pSRB->SRBSGIndex < pSRB->SRBSGCount) { + if (pSRB->SRBTotalXferLength != 0) { + /* + * load what physical address of Scatter/Gather list + * table want to be transfer + */ + pSRB->SRBState = TRM_DATA_XFER; + bus_space_write_4(iot, ioh, TRM_S1040_DMA_XHIGHADDR, 0); + bus_space_write_4(iot, ioh, + TRM_S1040_DMA_XLOWADDR, (pSRB->SRBSGPhyAddr + + ((u_int32_t)pSRB->SRBSGIndex << 3))); + /* + * load how many bytes in the Scatter/Gather list table + */ + bus_space_write_4(iot, ioh, TRM_S1040_DMA_XCNT, + ((u_int32_t)(pSRB->SRBSGCount - + pSRB->SRBSGIndex) << 3)); + /* + * load total transfer length (24bits, + * pSRB->SRBTotalXferLength) max value 16Mbyte + */ + bus_space_write_4(iot, ioh, + TRM_S1040_SCSI_COUNTER, pSRB->SRBTotalXferLength); + /* + * Start DMA transfer + */ + bus_space_write_2(iot,ioh,TRM_S1040_DMA_COMMAND, ioDir); + /* bus_space_write_2(iot, ioh, + TRM_S1040_DMA_CONTROL, STARTDMAXFER);*/ + /* + * Set the transfer bus and direction + */ + bval = ioDir == XFERDATAOUT ? SCMD_DMA_OUT :SCMD_DMA_IN; + } else { + /* + * xfer pad + */ + if (pSRB->SRBSGCount) + pSRB->AdaptStatus = TRM_OVER_UNDER_RUN; + + if (pDCB->SyncPeriod & WIDE_SYNC) { + bus_space_write_4(iot, ioh, + TRM_S1040_SCSI_COUNTER, 2); + } else { + bus_space_write_4(iot, ioh, + TRM_S1040_SCSI_COUNTER, 1); + } + + if (ioDir == XFERDATAOUT) { + bus_space_write_2(iot, + ioh, TRM_S1040_SCSI_FIFO, 0); + } else { + bus_space_read_2(iot, + ioh, TRM_S1040_SCSI_FIFO); + } + pSRB->SRBState = TRM_XFERPAD; + /* + * Set the transfer bus and direction + */ + bval = ioDir == XFERDATAOUT ? SCMD_FIFO_OUT : SCMD_FIFO_IN; + } + /* + * it's important for atn stop + */ + bus_space_write_2(iot,ioh,TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Tell the bus to do the transfer + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, bval); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_StatusPhase0 + * Purpose : Update Target Status with data from SCSI FIFO + * Inputs : u_int8_t * - Set to PH_BUS_FREE + * ------------------------------------------------------------ + */ +void +trm_StatusPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + + pSRB->TargetStatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); + + pSRB->SRBState = TRM_COMPLETED; + /* + * initial phase + */ + *pscsi_status = PH_BUS_FREE; + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Tell bus that the message was accepted + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_StatusPhase1 + * Purpose : Clear FIFO of DMA and SCSI + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_StatusPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + + if ((bus_space_read_2(iot, ioh, TRM_S1040_DMA_COMMAND) & 0x0001) != 0) { + if ((bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x40) + == 0) { + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, + DO_CLRFIFO); + } + if ((bus_space_read_2(iot, ioh, + TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) { + bus_space_write_1(iot, ioh, + TRM_S1040_DMA_CONTROL, CLRXFIFO); + } + } else { + if ((bus_space_read_2(iot, ioh, + TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) { + bus_space_write_1(iot, ioh, + TRM_S1040_DMA_CONTROL, CLRXFIFO); + } + if ((bus_space_read_1(iot, ioh, + TRM_S1040_SCSI_FIFOCNT) & 0x40) == 0) { + bus_space_write_2(iot, ioh, + TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + } + } + pSRB->SRBState = TRM_STATUS; + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Tell the bus that the command is complete + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_COMP); +} + +/* + * ------------------------------------------------------------ + * Function : trm_MsgInPhase0 + * Purpose : + * Inputs : + * + * extended message codes: + * code description + * ---- ----------- + * 02h Reserved + * 00h MODIFY DATA POINTER + * 01h SYNCHRONOUS DATA TRANSFER REQUEST + * 03h WIDE DATA TRANSFER REQUEST + * 04h - 7Fh Reserved + * 80h - FFh Vendor specific + * + * ------------------------------------------------------------ + */ +void +trm_MsgInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_dcb *pDCB; + u_int8_t message_in_code, bIndex, message_in_tag_id; + + pDCB = sc->pActiveDCB; + + message_in_code = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); + + if (pSRB->SRBState != TRM_EXTEND_MSGIN) { + switch (message_in_code) { + case MSG_DISCONNECT: + pSRB->SRBState = TRM_DISCONNECTED; + break; + + case MSG_EXTENDED: + case MSG_SIMPLE_Q_TAG: + case MSG_HEAD_OF_Q_TAG: + case MSG_ORDERED_Q_TAG: + pSRB->SRBState = TRM_EXTEND_MSGIN; + /* + * extended message (01h) + */ + bzero(&sc->MsgBuf[0], sizeof(sc->MsgBuf)); + sc->MsgBuf[0] = message_in_code; + sc->MsgCnt = 1; + /* + * extended message length (n) + */ + break; + + case MSG_MESSAGE_REJECT: + /* + * Reject message + */ + if ((pDCB->DCBFlag & TRM_DOING_WIDE_NEGO) != 0) { + /* + * do wide nego reject + */ + pDCB = pSRB->pSRBDCB; + + pDCB->DCBFlag &= ~TRM_DOING_WIDE_NEGO; + pDCB->DCBFlag |= TRM_WIDE_NEGO_DONE; + + if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) { + /* + * Set ATN, in case ATN was clear + */ + pSRB->SRBState = TRM_MSGOUT; + bus_space_write_2(iot, ioh, + TRM_S1040_SCSI_CONTROL, DO_SETATN); + } else { + /* + * Clear ATN + */ + bus_space_write_2(iot, ioh, + TRM_S1040_SCSI_CONTROL, DO_CLRATN); + } + + } else if ((pDCB->DCBFlag & TRM_DOING_SYNC_NEGO) != 0) { + /* + * do sync nego reject + */ + pDCB = pSRB->pSRBDCB; + + pDCB->DCBFlag &= ~TRM_DOING_SYNC_NEGO; + + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN); + goto re_prog; + } + break; + + case MSG_IGN_WIDE_RESIDUE: + bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1); + bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); + break; + + default: + break; + } + + } else { + + /* + * We are collecting an extended message. Save the latest byte and then + * check to see if the message is complete. If so, process it. + */ + sc->MsgBuf[sc->MsgCnt++] = message_in_code; +#ifdef TRM_DEBUG0 + printf("%s: sc->MsgBuf = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + sc->sc_device.dv_xname, + sc->MsgBuf[0], sc->MsgBuf[1], sc->MsgBuf[2], sc->MsgBuf[3], sc->MsgBuf[4], sc->MsgBuf[5] ); +#endif + switch (sc->MsgBuf[0]) { + case MSG_SIMPLE_Q_TAG: + case MSG_HEAD_OF_Q_TAG: + case MSG_ORDERED_Q_TAG: + if (sc->MsgCnt == 2) { + pSRB->SRBState = TRM_FREE; + message_in_tag_id = sc->MsgBuf[1]; + sc->MsgCnt = 0; + TAILQ_FOREACH(pSRB, &sc->goingSRB, link) { + if ((pSRB->pSRBDCB == pDCB) && (pSRB->TagNumber == message_in_tag_id)) + break; + } + if ((pSRB != NULL) && (pSRB->SRBState == TRM_DISCONNECTED)) { + pDCB->pActiveSRB = pSRB; + pSRB->SRBState = TRM_DATA_XFER; + } else { + pSRB = &sc->SRB[0]; + pSRB->SRBState = TRM_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + trm_EnableMsgOut(sc, MSG_ABORT_TAG); + } + } + break; + + case MSG_EXTENDED: + /* TODO XXXX: Correctly handling target initiated negotiations? */ + if ((sc->MsgBuf[2] == MSG_EXT_WDTR) && (sc->MsgCnt == 4)) { + /* + * ====================================== + * WIDE DATA TRANSFER REQUEST + * ====================================== + * byte 0 : Extended message (01h) + * byte 1 : Extended message length (02h) + * byte 2 : WIDE DATA TRANSFER code (03h) + * byte 3 : Transfer width exponent + */ + + pSRB->SRBState = TRM_FREE; + pDCB->DCBFlag &= ~(TRM_WIDE_NEGO_ENABLE | TRM_DOING_WIDE_NEGO); + + if (sc->MsgBuf[1] != MSG_EXT_WDTR_LEN) + goto reject_offer; + + switch (sc->MsgBuf[3]) { + case MSG_EXT_WDTR_BUS_32_BIT: + if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0) + sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_8_BIT; + else + sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_16_BIT; + break; + + case MSG_EXT_WDTR_BUS_16_BIT: + if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0) { + sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_8_BIT; + break; + } + pDCB->SyncPeriod |= WIDE_SYNC; + /* FALL THROUGH == ACCEPT OFFER */ + + case MSG_EXT_WDTR_BUS_8_BIT: + pSRB->SRBState = TRM_MSGOUT; + pDCB->DCBFlag |= (TRM_SYNC_NEGO_ENABLE | TRM_WIDE_NEGO_DONE); + + if (pDCB->MaxNegoPeriod == 0) { + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + goto re_prog; + } + break; + + default: + pDCB->DCBFlag &= ~TRM_WIDE_NEGO_ENABLE; + pDCB->DCBFlag |= TRM_WIDE_NEGO_DONE; +reject_offer: + sc->MsgCnt = 1; + sc->MsgBuf[0] = MSG_MESSAGE_REJECT; + break; + } + + /* Echo accepted offer, or send revised offer */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_SETATN); + + } else if ((sc->MsgBuf[2] == MSG_EXT_SDTR) && (sc->MsgCnt == 5)) { + /* + * ================================= + * SYNCHRONOUS DATA TRANSFER REQUEST + * ================================= + * byte 0 : Extended message (01h) + * byte 1 : Extended message length (03) + * byte 2 : SYNCHRONOUS DATA TRANSFER code (01h) + * byte 3 : Transfer period factor + * byte 4 : REQ/ACK offset + */ + + pSRB->SRBState = TRM_FREE; + pDCB->DCBFlag &= ~(TRM_SYNC_NEGO_ENABLE | TRM_DOING_SYNC_NEGO); + + if (sc->MsgBuf[1] != MSG_EXT_SDTR_LEN) + goto reject_offer; + + if ((sc->MsgBuf[3] == 0) || (sc->MsgBuf[4] == 0)) { + /* + * Asynchronous transfers + */ + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + + } else { + /* + * Synchronous transfers + */ + /* + * REQ/ACK offset + */ + pDCB->SyncOffset = sc->MsgBuf[4]; + + for (bIndex = 0; bIndex < 7; bIndex++) + if (sc->MsgBuf[3] <= trm_clock_period[bIndex]) + break; + + pDCB->SyncPeriod |= (bIndex | ALT_SYNC); + } + +re_prog: /* + * program SCSI control register + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + + trm_SetXferParams(sc, pDCB, (pDCB->DCBFlag & TRM_QUIRKS_VALID)); + } + break; + + default: + break; + } + } + + /* + * initial phase + */ + *pscsi_status = PH_BUS_FREE; + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * Tell bus that the message was accepted + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_MsgInPhase1 + * Purpose : Clear the FIFO + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_MsgInPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1); + + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* + * SCSI command + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN); +} + +/* + * ------------------------------------------------------------ + * Function : trm_Nop + * Purpose : EMPTY + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_Nop(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) +{ +} + +/* + * ------------------------------------------------------------ + * Function : trm_SetXferParams + * Purpose : Set the Sync period, offset and mode for each device that has + * the same target as the given one (struct trm_dcb *) + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_SetXferParams(struct trm_softc *sc, struct trm_dcb *pDCB, int print_info) +{ + struct trm_dcb *pDCBTemp; + int lun, target; + + /* + * set all lun device's period, offset + */ +#ifdef TRM_DEBUG0 + printf("%s: trm_SetXferParams\n", sc->sc_device.dv_xname); +#endif + + target = pDCB->target; + for(lun = 0; lun < TRM_MAX_LUNS; lun++) { + pDCBTemp = sc->pDCB[target][lun]; + if (pDCBTemp != NULL) { + pDCBTemp->DevMode = pDCB->DevMode; + pDCBTemp->MaxNegoPeriod = pDCB->MaxNegoPeriod; + pDCBTemp->SyncPeriod = pDCB->SyncPeriod; + pDCBTemp->SyncOffset = pDCB->SyncOffset; + pDCBTemp->DCBFlag = pDCB->DCBFlag; + } + } + + if (print_info) + trm_print_info(sc, pDCB); +} + +/* + * ------------------------------------------------------------ + * Function : trm_Disconnect + * Purpose : + * Inputs : + * + * ---SCSI bus phase + * PH_DATA_OUT 0x00 Data out phase + * PH_DATA_IN 0x01 Data in phase + * PH_COMMAND 0x02 Command phase + * PH_STATUS 0x03 Status phase + * PH_BUS_FREE 0x04 Invalid phase used as bus free + * PH_BUS_FREE 0x05 Invalid phase used as bus free + * PH_MSG_OUT 0x06 Message out phase + * PH_MSG_IN 0x07 Message in phase + * ------------------------------------------------------------ + */ +void +trm_Disconnect(struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + struct trm_scsi_req_q *pSRB, *pNextSRB; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_dcb *pDCB; + int j; + +#ifdef TRM_DEBUG0 + printf("%s: trm_Disconnect\n", sc->sc_device.dv_xname); +#endif + + pDCB = sc->pActiveDCB; + if (pDCB == NULL) { + /* TODO: Why use a loop? Why not use DELAY(400)? */ + for(j = 400; j > 0; --j) + DELAY(1); /* 1 msec */ + bus_space_write_2(iot, ioh, + TRM_S1040_SCSI_CONTROL, (DO_CLRFIFO | DO_HWRESELECT)); + return; + } + + pSRB = pDCB->pActiveSRB; + sc->pActiveDCB = NULL; + pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ + bus_space_write_2(iot, ioh, + TRM_S1040_SCSI_CONTROL, (DO_CLRFIFO | DO_HWRESELECT)); + DELAY(100); + + switch (pSRB->SRBState) { + case TRM_UNEXPECT_RESEL: + pSRB->SRBState = TRM_FREE; + break; + + case TRM_ABORT_SENT: + pSRB = TAILQ_FIRST(&sc->goingSRB); + while (pSRB != NULL) { + /* + * Need to save pNextSRB because trm_FinishSRB() puts + * pSRB in freeSRB queue, and thus its links no longer + * point to members of the goingSRB queue. This is why + * TAILQ_FOREACH() will not work for this traversal. + */ + pNextSRB = TAILQ_NEXT(pSRB, link); + if (pSRB->pSRBDCB == pDCB) { + /* TODO XXXX: Is TIMED_OUT the best state to report? */ + pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT; + trm_FinishSRB(sc, pSRB); + } + pSRB = pNextSRB; + } + break; + + case TRM_START: + case TRM_MSGOUT: + /* + * Selection time out + */ + /* If not polling just keep trying until xs->stimeout expires */ + if ((pSRB->xs->flags & SCSI_POLL) == 0) { + trm_RewaitSRB(sc, pSRB); + } else { + pSRB->TargetStatus = TRM_SCSI_SELECT_TIMEOUT; + goto disc1; + } + break; + + case TRM_COMPLETED: +disc1: + /* + * TRM_COMPLETED - remove id from mask of active tags + */ + pDCB->pActiveSRB = NULL; + trm_FinishSRB(sc, pSRB); + break; + + default: + break; + } + + trm_StartWaitingSRB(sc); +} + +/* + * ------------------------------------------------------------ + * Function : trm_Reselect + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_Reselect(struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_scsi_req_q *pSRB; + struct trm_dcb *pDCB; + u_int16_t RselTarLunId; + u_int8_t target, lun; + +#ifdef TRM_DEBUG0 + printf("%s: trm_Reselect\n", sc->sc_device.dv_xname); +#endif + + pDCB = sc->pActiveDCB; + if (pDCB != NULL) { + /* + * Arbitration lost but Reselection win + */ + pSRB = pDCB->pActiveSRB; + trm_RewaitSRB(sc, pSRB); + } + + /* + * Read Reselected Target Id and LUN + */ + RselTarLunId = bus_space_read_2(iot, ioh, TRM_S1040_SCSI_TARGETID) & 0x1FFF; + /* TODO XXXX: Make endian independant! */ + target = RselTarLunId & 0xff; + lun = (RselTarLunId >> 8) & 0xff; + +#ifdef TRM_DEBUG0 + printf("%s: reselect - target = %d, lun = %d\n", + sc->sc_device.dv_xname, target, lun); +#endif + + if ((target < TRM_MAX_TARGETS) && (lun < TRM_MAX_LUNS)) + pDCB = sc->pDCB[target][lun]; + else + pDCB = NULL; + + if (pDCB == NULL) + printf("%s: reselect - target = %d, lun = %d not found\n", + sc->sc_device.dv_xname, target, lun); + + sc->pActiveDCB = pDCB; + + /* TODO XXXX: This will crash if pDCB is ever NULL */ + if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0) { + pSRB = &sc->SRB[0]; + pDCB->pActiveSRB = pSRB; + } else { + pSRB = pDCB->pActiveSRB; + if (pSRB == NULL || (pSRB->SRBState != TRM_DISCONNECTED)) { + /* + * abort command + */ + pSRB = &sc->SRB[0]; + pSRB->SRBState = TRM_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + trm_EnableMsgOut(sc, MSG_ABORT); + } else + pSRB->SRBState = TRM_DATA_XFER; + } + pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ + + /* + * Program HA ID, target ID, period and offset + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, target); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + + /* + * it's important for atn stop + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + DELAY(30); + + /* + * SCSI command + * to rls the /ACK signal + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + +/* + * ------------------------------------------------------------ + * Function : trm_FinishSRB + * Purpose : Complete execution of a SCSI command + * Signal completion to the generic SCSI driver + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_FinishSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) +{ + struct scsi_inquiry_data *ptr; + struct scsi_sense_data *s1, *s2; + struct scsi_xfer *xs = pSRB->xs; + struct trm_dcb *pDCB = pSRB->pSRBDCB; + int target, lun; + +#ifdef TRM_DEBUG0 + printf("%s: trm_FinishSRB. sc = %p, pSRB = %p\n", + sc->sc_device.dv_xname, sc, pSRB); +#endif + pDCB->DCBFlag &= ~TRM_QUEUE_FULL; + + if (xs == NULL) { + trm_ReleaseSRB(sc, pSRB); + return; + } + + timeout_del(&xs->stimeout); + + xs->status = pSRB->TargetStatus; + + switch (xs->status) { + case SCSI_INTERM_COND_MET: + case SCSI_COND_MET: + case SCSI_INTERM: + case SCSI_OK: + switch (pSRB->AdaptStatus) { + case TRM_STATUS_GOOD: + if ((pSRB->SRBFlag & TRM_PARITY_ERROR) != 0) { +#ifdef TRM_DEBUG0 + printf("%s: trm_FinishSRB. TRM_PARITY_ERROR\n", + sc->sc_device.dv_xname); +#endif + xs->error = XS_DRIVER_STUFFUP; + + } else if ((pSRB->SRBFlag & TRM_SCSI_TIMED_OUT) != 0) { + xs->error = XS_TIMEOUT; + + } else if ((pSRB->SRBFlag & TRM_AUTO_REQSENSE) != 0) { + s1 = &pSRB->scsisense; + s2 = &xs->sense; + + *s2 = *s1; + + xs->status = SCSI_CHECK; + xs->error = XS_SENSE; + + } else + xs->error = XS_NOERROR; + break; + + case TRM_OVER_UNDER_RUN: +#ifdef TRM_DEBUG0 + printf("%s: trm_FinishSRB. TRM_OVER_UNDER_RUN\n", + sc->sc_device.dv_xname); +#endif + xs->error = XS_DRIVER_STUFFUP; + break; + + default: +#ifdef TRM_DEBUG0 + printf("%s: trm_FinishSRB. AdaptStatus Error = 0x%02x\n", + sc->sc_device.dv_xname, pSRB->AdaptStatus); +#endif + xs->error = XS_DRIVER_STUFFUP; + break; + } + break; + + case SCSI_TERMINATED: + case SCSI_ACA_ACTIVE: + case SCSI_CHECK: + if ((pSRB->SRBFlag & TRM_AUTO_REQSENSE) != 0) + xs->error = XS_DRIVER_STUFFUP; + else { + trm_RequestSense(sc, pSRB); + return; + } + break; + + case SCSI_QUEUE_FULL: + /* this says no more until someone completes */ + pDCB->DCBFlag |= TRM_QUEUE_FULL; + trm_RewaitSRB(sc, pSRB); + return; + + case SCSI_RESV_CONFLICT: + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + + case TRM_SCSI_UNEXP_BUS_FREE: + xs->status = SCSI_OK; + xs->error = XS_DRIVER_STUFFUP; + break; + + case TRM_SCSI_BUS_RST_DETECTED: + xs->status = SCSI_OK; + xs->error = XS_RESET; + break; + + case TRM_SCSI_SELECT_TIMEOUT: + xs->status = SCSI_OK; + xs->error = XS_SELTIMEOUT; + break; + + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + + target = xs->sc_link->target; + lun = xs->sc_link->lun; + + if ((xs->flags & SCSI_POLL) != 0) { + + if (xs->cmd->opcode == INQUIRY) { + + ptr = (struct scsi_inquiry_data *) xs->data; + + if ((xs->error != XS_NOERROR) || + ((ptr->device & SID_QUAL_BAD_LU) == SID_QUAL_BAD_LU)) { +#ifdef TRM_DEBUG0 + printf("%s: trm_FinishSRB NO Device:target= %d,lun= %d\n", + sc->sc_device.dv_xname, target, lun); +#endif + free(pDCB, M_DEVBUF); + sc->pDCB[target][lun] = NULL; + pDCB = NULL; + + } else + pDCB->sc_link = xs->sc_link; + } + } + + trm_ReleaseSRB(sc, pSRB); + + xs->flags |= ITSDONE; + + /* + * Notify cmd done + */ +#ifdef TRM_DEBUG0 + if ((xs->error != 0) || (xs->status != 0) || ((xs->flags & SCSI_POLL) != 0)) + printf("%s: trm_FinishSRB. %d/%d xs->cmd->opcode = 0x%02x, xs->error = %d, xs->status = %d\n", + sc->sc_device.dv_xname, target, lun, xs->cmd->opcode, xs->error, xs->status); +#endif + + scsi_done(xs); +} + +/* + * ------------------------------------------------------------ + * Function : trm_ReleaseSRB + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_ReleaseSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) +{ + struct scsi_xfer *xs = pSRB->xs; + int intflag; + + intflag = splbio(); + + if (pSRB->TagNumber != TRM_NO_TAG) { + pSRB->pSRBDCB->TagMask &= ~(1 << pSRB->TagNumber); + pSRB->TagNumber = TRM_NO_TAG; + } + + if (xs != NULL) { + timeout_del(&xs->stimeout); + + if (xs->datalen != 0) { + bus_dmamap_sync(sc->sc_dmatag, pSRB->dmamapxfer, + 0, pSRB->dmamapxfer->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmatag, pSRB->dmamapxfer); + } + } + + /* SRB may have started & finished, or be waiting and timed out */ + if ((pSRB->SRBFlag & TRM_ON_WAITING_SRB) != 0) { + pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB; + TAILQ_REMOVE(&sc->waitingSRB, pSRB, link); + } + if ((pSRB->SRBFlag & TRM_ON_GOING_SRB) != 0) { + pSRB->SRBFlag &= ~TRM_ON_GOING_SRB; + TAILQ_REMOVE(&sc->goingSRB, pSRB, link); + } + + bzero(&pSRB->SegmentX[0], sizeof(pSRB->SegmentX)); + bzero(&pSRB->CmdBlock[0], sizeof(pSRB->CmdBlock)); + bzero(&pSRB->scsisense, sizeof(pSRB->scsisense)); + + pSRB->SRBTotalXferLength = 0; + pSRB->SRBSGCount = 0; + pSRB->SRBSGIndex = 0; + pSRB->SRBFlag = 0; + + pSRB->SRBState = TRM_FREE; + pSRB->AdaptStatus = TRM_STATUS_GOOD; + pSRB->TargetStatus = SCSI_OK; + pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ + + pSRB->xs = NULL; + pSRB->pSRBDCB = NULL; + + if (pSRB != &sc->SRB[0]) + TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link); + + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_GoingSRB_Done + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_GoingSRB_Done(struct trm_softc *sc) +{ + struct trm_scsi_req_q *pSRB; + + /* ASSUME we are inside a splbio()/splx() pair */ + + while ((pSRB = TAILQ_FIRST(&sc->goingSRB)) != NULL) { + /* TODO XXXX: Is TIMED_OUT the best status? */ + pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT; + trm_FinishSRB(sc, pSRB); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_ResetSCSIBus + * Purpose : Reset the SCSI bus + * Inputs : struct trm_softc * - + * ------------------------------------------------------------ + */ +void +trm_ResetSCSIBus(struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + int intflag; + + intflag = splbio(); + + sc->sc_Flag |= RESET_DEV; + + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + while ((bus_space_read_2(iot, ioh, + TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET) == 0); + + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_ScsiRstDetect + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_ScsiRstDetect(struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + int wlval; + +#ifdef TRM_DEBUG0 + printf("%s: trm_ScsiRstDetect\n", sc->sc_device.dv_xname); +#endif + + wlval = 1000; + /* + * delay 1 sec + */ + while (--wlval != 0) + DELAY(1000); + + bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER); + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); + + if ((sc->sc_Flag & RESET_DEV) != 0) + sc->sc_Flag |= RESET_DONE; + else { + sc->sc_Flag |= RESET_DETECT; + trm_ResetAllDevParam(sc); + trm_RecoverSRB(sc); + sc->pActiveDCB = NULL; + sc->sc_Flag = 0; + trm_StartWaitingSRB(sc); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_RequestSense + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_RequestSense(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) +{ + pSRB->SRBFlag |= TRM_AUTO_REQSENSE; + + /* + * Status of initiator/target + */ + pSRB->AdaptStatus = TRM_STATUS_GOOD; + pSRB->TargetStatus = SCSI_OK; + /* + * Status of initiator/target + */ + + pSRB->SegmentX[0].address = pSRB->scsisensePhyAddr; + pSRB->SegmentX[0].length = sizeof(struct scsi_sense_data); + pSRB->SRBTotalXferLength = sizeof(struct scsi_sense_data); + pSRB->SRBSGCount = 1; + pSRB->SRBSGIndex = 0; + + bzero(&pSRB->CmdBlock[0], sizeof(pSRB->CmdBlock)); + + pSRB->CmdBlock[0] = REQUEST_SENSE; + pSRB->CmdBlock[1] = (pSRB->xs->sc_link->lun) << 5; + pSRB->CmdBlock[4] = sizeof(struct scsi_sense_data); + + pSRB->ScsiCmdLen = 6; + + if ((pSRB->xs != NULL) && ((pSRB->xs->flags & SCSI_POLL) == 0)) + timeout_add(&pSRB->xs->stimeout, (pSRB->xs->timeout/1000) * hz); + + if (trm_StartSRB(sc, pSRB) != 0) + trm_RewaitSRB(sc, pSRB); +} + +/* + * ------------------------------------------------------------ + * Function : trm_EnableMsgOut + * Purpose : set up MsgBuf to send out a single byte message + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_EnableMsgOut(struct trm_softc *sc, u_int8_t msg) +{ + sc->MsgBuf[0] = msg; + sc->MsgCnt = 1; + + bus_space_write_2(sc->sc_iotag, sc->sc_iohandle, TRM_S1040_SCSI_CONTROL, DO_SETATN); +} + +/* + * ------------------------------------------------------------ + * Function : trm_linkSRB + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_linkSRB(struct trm_softc *sc) +{ + struct trm_scsi_req_q *pSRB; + int i, intflag; + + intflag = splbio(); + + for (i = 0; i < TRM_MAX_SRB_CNT; i++) { + pSRB = &sc->SRB[i]; + + pSRB->PhysSRB = sc->sc_dmamap_control->dm_segs[0].ds_addr + + i * sizeof(struct trm_scsi_req_q); + + pSRB->SRBSGPhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr + + i * sizeof(struct trm_scsi_req_q) + + offsetof(struct trm_scsi_req_q, SegmentX); + + pSRB->scsisensePhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr + + i * sizeof(struct trm_scsi_req_q) + + offsetof(struct trm_scsi_req_q, scsisense); + + /* + * map all SRB space + */ + if (bus_dmamap_create(sc->sc_dmatag, TRM_MAX_PHYSG_BYTE, + TRM_MAX_SG_LISTENTRY, TRM_MAX_PHYSG_BYTE, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &pSRB->dmamapxfer) != 0) { + printf("%s: unable to create DMA transfer map\n", + sc->sc_device.dv_xname); + splx(intflag); + return; + } + + if (i > 0) + /* We use sc->SRB[0] directly, so *don't* link it */ + TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link); +#ifdef TRM_DEBUG0 + printf("pSRB = %p ", pSRB); +#endif + } +#ifdef TRM_DEBUG0 + printf("\n "); +#endif + splx(intflag); +} + +/* + * ------------------------------------------------------------ + * Function : trm_minphys + * Purpose : limited by the number of segments in the dma segment list + * Inputs : *buf + * ------------------------------------------------------------ + */ +void +trm_minphys(struct buf *bp) +{ + if (bp->b_bcount > (TRM_MAX_SG_LISTENTRY-1) * (long) NBPG) { + bp->b_bcount = (TRM_MAX_SG_LISTENTRY-1) * (long) NBPG; + } + minphys(bp); +} + +/* + * ------------------------------------------------------------ + * Function : trm_initACB + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_initACB(struct trm_softc *sc, int unit) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + struct trm_adapter_nvram *pEEpromBuf; + struct trm_dcb *pDCB; + int target, lun; + + pEEpromBuf = &trm_eepromBuf[unit]; + sc->sc_config = HCC_AUTOTERM | HCC_PARITY; + + if ((bus_space_read_1(iot, ioh, TRM_S1040_GEN_STATUS) & WIDESCSI) != 0) + sc->sc_config |= HCC_WIDE_CARD; + + if ((pEEpromBuf->NvramChannelCfg & NAC_POWERON_SCSI_RESET) != 0) + sc->sc_config |= HCC_SCSI_RESET; + + TAILQ_INIT(&sc->freeSRB); + TAILQ_INIT(&sc->waitingSRB); + TAILQ_INIT(&sc->goingSRB); + + sc->pActiveDCB = NULL; + sc->sc_AdapterUnit = unit; + sc->sc_AdaptSCSIID = pEEpromBuf->NvramScsiId; + sc->sc_TagMaxNum = 2 << pEEpromBuf->NvramMaxTag; + sc->sc_Flag = 0; + + /* + * put all SRB's (except [0]) onto the freeSRB list + */ + trm_linkSRB(sc); + + /* + * allocate DCB array + */ + for (target = 0; target < TRM_MAX_TARGETS; target++) { + if (target == sc->sc_AdaptSCSIID) + continue; + + for (lun = 0; lun < TRM_MAX_LUNS; lun++) { + pDCB = (struct trm_dcb *)malloc(sizeof(struct trm_dcb), M_DEVBUF, M_NOWAIT); + sc->pDCB[target][lun] = pDCB; + + if (pDCB == NULL) + continue; + + bzero(pDCB, sizeof(struct trm_dcb)); + + pDCB->target = target; + pDCB->lun = lun; + pDCB->pActiveSRB = NULL; + } + } + + sc->sc_adapter.scsi_cmd = trm_scsi_cmd; + sc->sc_adapter.scsi_minphys = trm_minphys; + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_AdaptSCSIID; + sc->sc_link.openings = 30; /* So TagMask (32 bit integer) always has space */ + sc->sc_link.device = &trm_device; + sc->sc_link.adapter = &sc->sc_adapter; + sc->sc_link.adapter_buswidth = ((sc->sc_config & HCC_WIDE_CARD) == 0) ? 8:16; + + trm_reset(sc); +} + +/* + * ------------------------------------------------------------ + * Function : trm_write_all + * Description : write pEEpromBuf 128 bytes to seeprom + * Input : iot, ioh - chip's base address + * Output : none + * ------------------------------------------------------------ + */ +void +trm_write_all(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, + bus_space_handle_t ioh) +{ + u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf; + u_int8_t bAddr; + + /* + * Enable SEEPROM + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, + (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM)); + /* + * Write enable + */ + trm_write_cmd(iot, ioh, 0x04, 0xFF); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); + trm_wait_30us(iot, ioh); + for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) + trm_set_data(iot, ioh, bAddr, *bpEeprom); + /* + * Write disable + */ + trm_write_cmd(iot, ioh, 0x04, 0x00); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); + trm_wait_30us(iot, ioh); + /* + * Disable SEEPROM + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, + (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM)); +} + +/* + * ------------------------------------------------------------ + * Function : trm_set_data + * Description : write one byte to seeprom + * Input : iot, ioh - chip's base address + * bAddr - address of SEEPROM + * bData - data of SEEPROM + * Output : none + * ------------------------------------------------------------ + */ +void +trm_set_data(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr, + u_int8_t bData) +{ + u_int8_t bSendData; + int i; + + /* + * Send write command & address + */ + trm_write_cmd(iot, ioh, 0x05, bAddr); + /* + * Write data + */ + for (i = 0; i < 8; i++, bData <<= 1) { + bSendData = NVR_SELECT; + if ((bData & 0x80) != 0) { /* Start from bit 7 */ + bSendData |= NVR_BITOUT; + } + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, + (bSendData | NVR_CLOCK)); + trm_wait_30us(iot, ioh); + } + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); + trm_wait_30us(iot, ioh); + /* + * Disable chip select + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); + trm_wait_30us(iot, ioh); + /* + * Wait for write ready + */ + for (;;) { + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, + (NVR_SELECT | NVR_CLOCK)); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); + trm_wait_30us(iot, ioh); + if (bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM) & NVR_BITIN) + break; + } + /* + * Disable chip select + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); +} + +/* + * ------------------------------------------------------------ + * Function : trm_read_all + * Description : read seeprom 128 bytes to pEEpromBuf + * Input : pEEpromBuf, iot, ioh - chip's base address + * Output : none + * ------------------------------------------------------------ + */ +void +trm_read_all(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, + bus_space_handle_t ioh) +{ + u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf; + u_int8_t bAddr; + + /* + * Enable SEEPROM + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, + (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM)); + + for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) + *bpEeprom = trm_get_data(iot, ioh, bAddr); + + /* + * Disable SEEPROM + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, + (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM)); +} + +/* + * ------------------------------------------------------------ + * Function : trm_get_data + * Description : read one byte from seeprom + * Input : iot, ioh - chip's base address + * bAddr - address of SEEPROM + * Output : bData - data of SEEPROM + * ------------------------------------------------------------ + */ +u_int8_t +trm_get_data( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr) +{ + u_int8_t bReadData, bData; + int i; + + bData = 0; + + /* + * Send read command & address + */ + trm_write_cmd(iot, ioh, 0x06, bAddr); + + for (i = 0; i < 8; i++) { + /* + * Read data + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, + (NVR_SELECT | NVR_CLOCK)); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); + /* + * Get data bit while falling edge + */ + bReadData = bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM); + bData <<= 1; + if ((bReadData & NVR_BITIN) != 0) + bData |= 1; + trm_wait_30us(iot, ioh); + } + /* + * Disable chip select + */ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); + + return bData; +} + +/* + * ------------------------------------------------------------ + * Function : trm_wait_30us + * Description : wait 30 us + * Input : iot, ioh - chip's base address + * Output : none + * ------------------------------------------------------------ + */ +void +trm_wait_30us(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + bus_space_write_1(iot, ioh, TRM_S1040_GEN_TIMER, 5); + + while ((bus_space_read_1(iot, ioh, TRM_S1040_GEN_STATUS) & GTIMEOUT) + == 0); +} + +/* + * ------------------------------------------------------------ + * Function : trm_write_cmd + * Description : write SB and Op Code into seeprom + * Input : iot, ioh - chip's base address + * bCmd - SB + Op Code + * bAddr - address of SEEPROM + * Output : none + * ------------------------------------------------------------ + */ +void +trm_write_cmd( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bCmd, + u_int8_t bAddr) +{ + u_int8_t bSendData; + int i; + + for (i = 0; i < 3; i++, bCmd <<= 1) { + /* + * Program SB + OP code + */ + bSendData = NVR_SELECT; + if (bCmd & 0x04) /* Start from bit 2 */ + bSendData |= NVR_BITOUT; + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, + (bSendData | NVR_CLOCK)); + trm_wait_30us(iot, ioh); + } + + for (i = 0; i < 7; i++, bAddr <<= 1) { + /* + * Program address + */ + bSendData = NVR_SELECT; + if (bAddr & 0x40) { /* Start from bit 6 */ + bSendData |= NVR_BITOUT; + } + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); + trm_wait_30us(iot, ioh); + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, + (bSendData | NVR_CLOCK)); + trm_wait_30us(iot, ioh); + } + bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); + trm_wait_30us(iot, ioh); +} + +/* + * ------------------------------------------------------------ + * Function : trm_check_eeprom + * Description : read eeprom 128 bytes to pEEpromBuf and check + * checksum. If it is worong, updated with default value + * Input : eeprom, iot, ioh - chip's base address + * Output : none + * ------------------------------------------------------------ + */ +void +trm_check_eeprom(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, + bus_space_handle_t ioh) +{ + u_int32_t *dpEeprom = (u_int32_t *)pEEpromBuf->NvramTarget; + u_int32_t dAddr; + u_int16_t *wpEeprom = (u_int16_t *)pEEpromBuf; + u_int16_t wAddr, wCheckSum; + +#ifdef TRM_DEBUG0 + printf("\ntrm_check_eeprom\n"); +#endif + trm_read_all(pEEpromBuf, iot, ioh); + wCheckSum = 0; + for (wAddr = 0; wAddr < 64; wAddr++, wpEeprom++) + wCheckSum += *wpEeprom; + + if (wCheckSum != 0x1234) { +#ifdef TRM_DEBUG0 + printf("TRM_S1040 EEPROM Check Sum ERROR (load default)\n"); +#endif + /* + * Checksum error, load default + */ + pEEpromBuf->NvramSubVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2; + pEEpromBuf->NvramSubVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2 + >> 8); + pEEpromBuf->NvramSubSysID[0] = (u_int8_t) + PCI_PRODUCT_TEKRAM2_DC3X5U; + pEEpromBuf->NvramSubSysID[1] = (u_int8_t) + (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8); + pEEpromBuf->NvramSubClass = 0; + pEEpromBuf->NvramVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2; + pEEpromBuf->NvramVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2 + >> 8); + pEEpromBuf->NvramDeviceID[0] = (u_int8_t) + PCI_PRODUCT_TEKRAM2_DC3X5U; + pEEpromBuf->NvramDeviceID[1] = (u_int8_t) + (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8); + pEEpromBuf->NvramReserved = 0; + + for (dAddr = 0; dAddr < 16; dAddr++, dpEeprom++) + /* + * NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 + */ + *dpEeprom = 0x00000077; + + /* + * NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId + */ + *dpEeprom++ = 0x04000F07; + + /* + * NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 + */ + *dpEeprom++ = 0x00000015; + for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++) + *dpEeprom = 0; + + pEEpromBuf->NvramCheckSum = 0; + for (wAddr = 0, wCheckSum =0; wAddr < 63; wAddr++, wpEeprom++) + wCheckSum += *wpEeprom; + + *wpEeprom = 0x1234 - wCheckSum; + trm_write_all(pEEpromBuf, iot, ioh); + } +} + +/* + * ------------------------------------------------------------ + * Function : trm_initAdapter + * Purpose : initialize the SCSI chip ctrl registers + * Inputs : psh - pointer to this host adapter's structure + * ------------------------------------------------------------ + */ +void +trm_initAdapter(struct trm_softc *sc) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + u_int16_t wval; + u_int8_t bval; + + /* + * program configuration 0 + */ + if ((sc->sc_config & HCC_PARITY) != 0) { + bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; + } else { + bval = PHASELATCH | INITIATOR | BLOCKRST; + } + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG0, bval); + /* + * program configuration 1 + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG1, 0x13); + /* + * 250ms selection timeout + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TIMEOUT, TRM_SEL_TIMEOUT); + /* + * Mask all the interrupt + */ + bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, 0); + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, 0); + /* + * Reset SCSI module + */ + bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + /* + * program Host ID + */ + bval = sc->sc_AdaptSCSIID; + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, bval); + /* + * set ansynchronous transfer + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, 0); + /* + * Turn LED control off + */ + wval = bus_space_read_2(iot, ioh, TRM_S1040_GEN_CONTROL) & 0x7F; + bus_space_write_2(iot, ioh, TRM_S1040_GEN_CONTROL, wval); + /* + * DMA config + */ + wval = bus_space_read_2(iot, ioh, TRM_S1040_DMA_CONFIG) | DMA_ENHANCE; + bus_space_write_2(iot, ioh, TRM_S1040_DMA_CONFIG, wval); + /* + * Clear pending interrupt status + */ + bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS); + /* + * Enable SCSI interrupts + */ + bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, + (EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED | + EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE)); + bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, EN_SCSIINTR); +} + +/* + * ------------------------------------------------------------ + * Function : trm_init + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure + * Preconditions : when this function is called, the chip_type field of + * the ACB structure MUST have been set. + * ------------------------------------------------------------ + */ +int +trm_init(struct trm_softc *sc, int unit) +{ + const bus_space_handle_t ioh = sc->sc_iohandle; + const bus_space_tag_t iot = sc->sc_iotag; + bus_dma_segment_t seg; + int error, rseg, all_srbs_size; + + /* + * EEPROM CHECKSUM + */ + trm_check_eeprom(&trm_eepromBuf[unit], iot, ioh); + + /* + * MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK + */ + /* + * allocate the space for all SCSI control blocks (SRB) for DMA memory. + */ + all_srbs_size = TRM_MAX_SRB_CNT * sizeof(struct trm_scsi_req_q); + + error = bus_dmamem_alloc(sc->sc_dmatag, all_srbs_size, NBPG, 0, &seg, + 1, &rseg, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: unable to allocate SCSI REQUEST BLOCKS, error = %d\n", + sc->sc_device.dv_xname, error); + /*errx(error, "%s: unable to allocate SCSI request blocks", + sc->sc_device.dv_xname);*/ + return -1; + } + + error = bus_dmamem_map(sc->sc_dmatag, &seg, rseg, all_srbs_size, + (caddr_t *)&sc->SRB, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); + if (error != 0) { + printf("%s: unable to map SCSI REQUEST BLOCKS, error = %d\n", + sc->sc_device.dv_xname, error); + /*errx(error, "unable to map SCSI request blocks");*/ + return -1; + } + + error = bus_dmamap_create(sc->sc_dmatag, all_srbs_size, 1, + all_srbs_size, 0, BUS_DMA_NOWAIT,&sc->sc_dmamap_control); + if (error != 0) { + printf("%s: unable to create SRB DMA maps, error = %d\n", + sc->sc_device.dv_xname, error); + /*errx(error, "unable to create SRB DMA maps");*/ + return -1; + } + + error = bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap_control, + sc->SRB, all_srbs_size, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: unable to load SRB DMA maps, error = %d\n", + sc->sc_device.dv_xname, error); + /*errx(error, "unable to load SRB DMA maps");*/ + return -1; + } +#ifdef TRM_DEBUG0 + printf("\n\n%s: all_srbs_size=%x\n", + sc->sc_device.dv_xname, all_srbs_size); +#endif + bzero(sc->SRB, all_srbs_size); + trm_initACB(sc, unit); + trm_initAdapter(sc); + + return 0; +} + +/* ------------------------------------------------------------ + * Function : trm_print_info + * Purpose : Print the DCB negotiation information + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_print_info(struct trm_softc *sc, struct trm_dcb *pDCB) +{ + int syncXfer, index; + + index = pDCB->SyncPeriod & ~(WIDE_SYNC | ALT_SYNC); + + printf("%s: target %d using ", sc->sc_device.dv_xname, pDCB->target); + if ((pDCB->SyncPeriod & WIDE_SYNC) != 0) + printf("16 bit "); + else + printf("8 bit "); + + if (pDCB->SyncOffset == 0) + printf("Asynchronous "); + else { + syncXfer = 100000 / (trm_clock_period[index] * 4); + printf("%d.%01d MHz, Offset %d ", + syncXfer / 100, syncXfer % 100, pDCB->SyncOffset); + } + printf("data transfers "); + + if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0) + printf("with Tag Queuing"); + + printf("\n"); +} diff --git a/sys/dev/ic/trm.h b/sys/dev/ic/trm.h new file mode 100644 index 00000000000..2251baf89bf --- /dev/null +++ b/sys/dev/ic/trm.h @@ -0,0 +1,529 @@ +/* $OpenBSD: trm.h,v 1.1 2002/02/18 01:55:30 krw Exp $ + * ------------------------------------------------------------ + * O.S : OpenBSD + * File Name : trm.h + * Device Driver for Tekram DC395U/UW/F,DC315/U + * PCI SCSI Bus Master Host Adapter + * (SCSI chip set used Tekram ASIC TRM-S1040) + * + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * (C)Copyright 2001-2002 Ashley R. Martens and Kenneth R Westerback + * ------------------------------------------------------------ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ------------------------------------------------------------ + */ + +#ifndef _TRM_H +#define _TRM_H + +/* + * ------------------------------------------------------------ + * Segment Entry + * ------------------------------------------------------------ + */ +struct SGentry +{ + u_int32_t address; + u_int32_t length; +}; + +/* + * ----------------------------------------------------------------------- + * feature of chip set MAX value + * ----------------------------------------------------------------------- + */ + +#define TRM_MAX_ADAPTER_NUM 4 +#define TRM_MAX_TARGETS 16 +#define TRM_MAX_LUNS 8 +#define TRM_MAX_SG_LISTENTRY 32 +#define TRM_MAX_CMD_PER_LUN 32 +#define TRM_MAX_SRB_CNT TRM_MAX_CMD_PER_LUN*4 +#define TRM_MAX_PHYSG_BYTE ((TRM_MAX_SG_LISTENTRY - 1) << PGSHIFT) +#define TRM_MAX_SYNC_OFFSET 15 +#define TRM_SEL_TIMEOUT 153 /* 250 ms selection timeout (@40MHz) */ + +/* + *----------------------------------------------------------------------- + * SCSI Request Block + *----------------------------------------------------------------------- + */ +struct trm_scsi_req_q +{ + TAILQ_ENTRY(trm_scsi_req_q) link; + bus_dmamap_t dmamapxfer; + u_int32_t PhysSRB; + u_int32_t SRBTotalXferLength; + u_int32_t SRBSGPhyAddr; /* a segment starting address */ + + u_int16_t SRBState; /* State machine */ +#define TRM_FREE 0x0000 +#define TRM_WAIT 0x0001 +#define TRM_READY 0x0002 +#define TRM_MSGOUT 0x0004 /*arbitration+msg_out 1st byte */ +#define TRM_EXTEND_MSGIN 0x0010 +#define TRM_COMMAND 0x0020 +#define TRM_START 0x0040 /*arbitration+msg_out+command_out */ +#define TRM_DISCONNECTED 0x0080 +#define TRM_DATA_XFER 0x0100 +#define TRM_XFERPAD 0x0200 +#define TRM_STATUS 0x0400 +#define TRM_COMPLETED 0x0800 +#define TRM_ABORT_SENT 0x1000 +#define TRM_UNEXPECT_RESEL 0x8000 + + u_int8_t AdaptStatus; +#define TRM_STATUS_GOOD 0x00 +#define TRM_SELECTION_TIMED_OUT 0x11 +#define TRM_OVER_UNDER_RUN 0x12 +#define TRM_UNEXP_BUS_FREE 0x13 +#define TRM_TARGET_PHASE_F 0x14 +#define TRM_INVALID_CCB_OP 0x16 +#define TRM_LINK_CCB_BAD 0x17 +#define TRM_BAD_TARGET_DIR 0x18 +#define TRM_DUPLICATE_CCB 0x19 +#define TRM_BAD_CCB_OR_SG 0x1A +#define TRM_ABORT 0xFF + + u_int8_t CmdBlock[12]; + + u_int8_t ScsiCmdLen; + u_int8_t ScsiPhase; + + u_int8_t SRBFlag; +#define TRM_AUTO_REQSENSE 0x01 +#define TRM_SCSI_TIMED_OUT 0x02 +#define TRM_PARITY_ERROR 0x04 +#define TRM_ON_GOING_SRB 0x08 +#define TRM_ON_WAITING_SRB 0x10 + + u_int8_t SRBSGCount; + u_int8_t SRBSGIndex; + u_int8_t TagNumber; +#define TRM_NO_TAG 0x00 + + u_int8_t TargetStatus; /* SCSI status codes + Tekram: */ +#define TRM_SCSI_UNEXP_BUS_FREE 0xFD /* Unexpect Bus Free */ +#define TRM_SCSI_BUS_RST_DETECTED 0xFE /* Scsi Bus Reset detected */ +#define TRM_SCSI_SELECT_TIMEOUT 0xFF /* Selection Time out */ + + struct trm_dcb *pSRBDCB; + + struct SGentry SegmentX[TRM_MAX_SG_LISTENTRY]; + + struct scsi_xfer *xs; + + struct scsi_sense_data scsisense; + u_int32_t scsisensePhyAddr; +}; + +TAILQ_HEAD(SRB_HEAD, trm_scsi_req_q); + +/* + *----------------------------------------------------------------------- + * Device Control Block + *----------------------------------------------------------------------- + */ +struct trm_dcb +{ + u_int32_t TagMask; + + u_int16_t DCBFlag; +#define TRM_WIDE_NEGO_ENABLE 0x0001 +#define TRM_DOING_WIDE_NEGO 0x0002 +#define TRM_WIDE_NEGO_DONE 0x0004 +#define TRM_SYNC_NEGO_ENABLE 0x0008 +#define TRM_DOING_SYNC_NEGO 0x0010 +#define TRM_USE_TAG_QUEUING 0x0020 +#define TRM_QUEUE_FULL 0x0040 +#define TRM_WIDE_NEGO_16BIT 0x0080 +#define TRM_QUIRKS_VALID 0x0100 +#define TRM_BAD_DCB 0x0200 + + u_int8_t DevMode; /* trm_target_nvram.NvmTarCfg0 */ + + u_int8_t MaxNegoPeriod; /* Maximum allow sync period */ + u_int8_t SyncPeriod; /* Current sync period */ + u_int8_t SyncOffset; /* Current sync offset */ + + u_int8_t target; /* SCSI Target ID */ + u_int8_t lun; /* SCSI Logical Unit Number */ + + u_int8_t IdentifyMsg; + + struct scsi_link *sc_link; + struct trm_scsi_req_q *pActiveSRB; +}; + +/* + *----------------------------------------------------------------------- + * Adapter Control Block + *----------------------------------------------------------------------- + */ +struct trm_softc +{ + struct device sc_device; + + bus_space_handle_t sc_iohandle; + bus_space_tag_t sc_iotag; + bus_dma_tag_t sc_dmatag; + bus_dmamap_t sc_dmamap_control; /* map the control structures */ + + u_int16_t sc_AdapterUnit; /* nth Adapter this driver */ + + u_int8_t sc_AdaptSCSIID; /* Adapter SCSI Target ID */ + u_int8_t sc_TagMaxNum; + + u_int8_t sc_config; +#define HCC_WIDE_CARD 0x20 +#define HCC_SCSI_RESET 0x10 +#define HCC_PARITY 0x08 +#define HCC_AUTOTERM 0x04 +#define HCC_LOW8TERM 0x02 +#define HCC_UP8TERM 0x01 + + u_int8_t sc_Flag; +#define RESET_DEV 0x01 +#define RESET_DETECT 0x02 +#define RESET_DONE 0x04 + + u_int8_t MsgCnt; + u_int8_t MsgBuf[6]; + + /* + *---------------------------------- + * Link to the generic SCSI driver + *---------------------------------- + */ + struct scsi_adapter sc_adapter; + struct scsi_link sc_link; + + struct SRB_HEAD freeSRB; + struct SRB_HEAD goingSRB; + struct SRB_HEAD waitingSRB; + + struct trm_dcb *pActiveDCB; + struct trm_dcb *pDCB[TRM_MAX_TARGETS][TRM_MAX_LUNS]; + + struct trm_scsi_req_q *SRB; +}; + +/* + * The SEEPROM structure for TRM_S1040 + */ +struct trm_target_nvram +{ + u_int8_t NvmTarCfg0; /* Target configuration byte 0 */ +#define TRM_WIDE 0x20 /* Wide negotiate */ +#define TRM_TAG_QUEUING 0x10 /* Enable SCSI tag queuing */ +#define TRM_SEND_START 0x08 /* Send start command SPINUP */ +#define TRM_DISCONNECT 0x04 /* Enable SCSI disconnect */ +#define TRM_SYNC 0x02 /* Sync negotiation */ +#define TRM_PARITY 0x01 /* (it should be defined at NAC ) */ + + u_int8_t NvmTarPeriod; /* Target period */ + u_int8_t NvmTarCfg2; /* Target configuration byte 2 */ + u_int8_t NvmTarCfg3; /* Target configuration byte 3 */ +}; + +struct trm_adapter_nvram +{ + u_int8_t NvramSubVendorID[2]; /*0,1 Sub Vendor ID */ + u_int8_t NvramSubSysID[2]; /*2,3 Sub System ID */ + u_int8_t NvramSubClass; /*4 Sub Class */ + u_int8_t NvramVendorID[2]; /*5,6 Vendor ID */ + u_int8_t NvramDeviceID[2]; /*7,8 Device ID */ + u_int8_t NvramReserved; /*9 Reserved */ + struct trm_target_nvram NvramTarget[TRM_MAX_TARGETS]; /* 10 */ + u_int8_t NvramScsiId; /*74 Host Adapter SCSI ID */ + u_int8_t NvramChannelCfg; /*75 Channel configuration */ +#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */ +#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */ +#define NAC_GREATER_1G 0x02 /* > 1G support enable */ +#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */ + u_int8_t NvramDelayTime; /*76 Power on delay time */ + u_int8_t NvramMaxTag; /*77 Maximum tags */ + u_int8_t NvramReserved0; /*78 */ + u_int8_t NvramBootTarget; /*79 */ + u_int8_t NvramBootLun; /*80 */ + u_int8_t NvramReserved1; /*81 */ + u_int16_t Reserved[22]; /*82,..125 */ + u_int16_t NvramCheckSum; /*126,127 */ +}; + +/* + * The PCI configuration register offsets for the TRM_S1040, and + * the associated bit definitions. + */ + +#define TRM_S1040_ID 0x00 /* Vendor and Device ID */ +#define TRM_S1040_COMMAND 0x04 /* PCI command register */ +#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */ +#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */ +#define TRM_S1040_INTLINE 0x3C /* Interrupt line */ + +#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */ +#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */ +#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */ +#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */ +#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */ +#define COMMANDABORT 0x0040 /* SCSI command abort */ +#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */ +#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */ +#define PARITYERROR 0x0008 /* SCSI parity error */ +#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */ +#define PH_DATA_OUT 0x00 /* Data out phase */ +#define PH_DATA_IN 0x01 /* Data in phase */ +#define PH_COMMAND 0x02 /* Command phase */ +#define PH_STATUS 0x03 /* Status phase */ +#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */ +#define PH_MSG_OUT 0x06 /* Message out phase */ +#define PH_MSG_IN 0x07 /* Message in phase */ +#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */ +#define DO_CLRATN 0x0400 /* Clear ATN */ +#define DO_SETATN 0x0200 /* Set ATN */ +#define DO_CMDABORT 0x0100 /* Abort SCSI command */ +#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */ +#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */ +#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */ +#define DO_DATALATCH 0x0002 /* Enable SCSI bus data latch */ +#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */ +#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */ +#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */ +#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */ +#define INT_SCAM 0x80 /* SCAM selection interrupt */ +#define INT_SELECT 0x40 /* Selection interrupt */ +#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */ +#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */ +#define INT_RESELECTED 0x08 /* Reselected interrupt */ +#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */ +#define INT_BUSSERVICE 0x02 /* Bus service interrupt */ +#define INT_CMDDONE 0x01 /* SCSI command done interrupt */ +#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */ +/* + * Bit Name Definition + * 07-05 0 RSVD Reversed. Always 0. + * 04 0 OFFSET4 Reversed for LVDS. Always 0. + * 03-00 0 OFFSET[03:00] Offset number from 0 to 15 + */ +#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */ +#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */ +#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */ +#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */ +/* + * SYNCM 7 6 5 4 3 2 1 0 + * Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 + * Default 0 0 0 0 0 0 0 0 + * + * + * Bit Name Definition + * --- ---- ---------- + * 07-06 0 RSVD Reversed. Always read 0 + * 05 0 LVDS Reversed. Always read 0 + * 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI transfer. + * 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. + * + * @@ When this bit is set, + * the synchronous period bits 2:0 + * in the Synchronous Mode register + * are used to transfer data + * at the Fast-20 rate. + * @@ When this bit is reset, + * the synchronous period bits 2:0 + * in the Synchronous Mode Register + * are used to transfer data + * at the Fast-40 rate. + * + * 02-00 0 PERIOD[2:0]/SXPD[02:00] Synchronous SCSI Transfer Rate. + * These 3 bits specify + * the Synchronous SCSI Transfer Rate + * for Fast-20 and Fast-10. + * These bits are also reset + * by a SCSI Bus reset. + * + * For Fast-10 bit ALTPD = 0 and LVDS = 0 + * and bit2,bit1,bit0 is defined as follows : + * + * 000 100ns, 10.0 Mbytes/s + * 001 150ns, 6.6 Mbytes/s + * 010 200ns, 5.0 Mbytes/s + * 011 250ns, 4.0 Mbytes/s + * 100 300ns, 3.3 Mbytes/s + * 101 350ns, 2.8 Mbytes/s + * 110 400ns, 2.5 Mbytes/s + * 111 450ns, 2.2 Mbytes/s + * + * For Fast-20 bit ALTPD = 1 and LVDS = 0 + * and bit2,bit1,bit0 is defined as follows : + * + * 000 50ns, 20.0 Mbytes/s + * 001 75ns, 13.3 Mbytes/s + * 010 100ns, 10.0 Mbytes/s + * 011 125ns, 8.0 Mbytes/s + * 100 150ns, 6.6 Mbytes/s + * 101 175ns, 5.7 Mbytes/s + * 110 200ns, 5.0 Mbytes/s + * 111 250ns, 4.0 Mbytes/s + * + * For Fast-40 bit ALTPD = 0 and LVDS = 1 + * and bit2,bit1,bit0 is defined as follows : + * + * 000 25ns, 40.0 Mbytes/s + * 001 50ns, 20.0 Mbytes/s + * 010 75ns, 13.3 Mbytes/s + * 011 100ns, 10.0 Mbytes/s + * 100 125ns, 8.0 Mbytes/s + * 101 150ns, 6.6 Mbytes/s + * 110 175ns, 5.7 Mbytes/s + * 111 200ns, 5.0 Mbytes/s + */ +#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */ +#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */ +#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */ +#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */ +#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */ +#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */ +#define EN_SELECT 0x40 /* Enable selection interrupt */ +#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */ +#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */ +#define EN_RESELECTED 0x08 /* Enable reselected interrupt */ +#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt*/ +#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */ +#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */ +#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */ +#define PHASELATCH 0x40 /* Enable phase latch */ +#define INITIATOR 0x20 /* Enable initiator mode */ +#define PARITYCHECK 0x10 /* Enable parity check */ +#define BLOCKRST 0x01 /* Disable SCSI reset1 */ +#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */ +#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */ +#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */ +#define ACTIVE_NEG 0x02 /* Enable active negation */ +#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */ +#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */ +#define SCMD_COMP 0x12 /* Command complete */ +#define SCMD_SEL_ATN 0x60 /* Selection with ATN */ +#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */ +#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */ +#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */ +#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */ +#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */ +#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */ +#define SCMD_MSGACCEPT 0xD8 /* Message accept */ +/* + * Code Command Description + * + * 02 Enable reselection with FIFO + * 40 Select without ATN with FIFO + * 60 Select with ATN with FIFO + * 64 Select with ATN3 with FIFO + * A0 Select with ATN and stop with FIFO + * C0 Transfer information out with FIFO + * C1 Transfer information out with DMA + * C2 Transfer information in with FIFO + * C3 Transfer information in with DMA + * 12 Initiator command complete with FIFO + * 50 Initiator transfer information out sequence without ATN with FIFO + * 70 Initiator transfer information out sequence with ATN with FIFO + * 74 Initiator transfer information out sequence with ATN3 with FIFO + * 52 Initiator transfer information in sequence without ATN with FIFO + * 72 Initiator transfer information in sequence with ATN with FIFO + * 76 Initiator transfer information in sequence with ATN3 with FIFO + * 90 Initiator transfer information out command complete with FIFO + * 92 Initiator transfer information in command complete with FIFO + * D2 Enable selection + * 08 Reselection + * 48 Disconnect command with FIFO + * 88 Terminate command with FIFO + * C8 Target command complete with FIFO + * 18 SCAM Arbitration/ Selection + * 5A Enable reselection + * 98 Select without ATN with FIFO + * B8 Select with ATN with FIFO + * D8 Message Accepted + * 58 NOP + */ +#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */ +#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */ +#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */ +#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */ +#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */ +#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */ +#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */ +#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */ +#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */ +#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */ +#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */ +#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */ +#define TCR0_OFFSET_MASK 0x001F /* Offset number */ +#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */ +#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */ +#define NON_TAG_BUSY 0x0080 /* Non tag command active */ +#define ACTTAG_MASK 0x007F /* Active tags */ + +#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */ +#define XFERDATAIN 0x0103 /* Transfer data in */ +#define XFERDATAOUT 0x0102 /* Transfer data out */ +#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */ +#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */ +#define STOPDMAXFER 0x08 /* Stop DMA transfer */ +#define ABORTXFER 0x04 /* Abort DMA transfer */ +#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */ +#define STARTDMAXFER 0x01 /* Start DMA transfer */ +#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */ +#define XFERPENDING 0x80 /* Transfer pending */ +#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define SCSICOMP 0x01 /* SCSI complete interrupt */ +#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */ +#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */ +#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */ +#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature */ +#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W) */ +#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */ +#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */ +#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */ + +#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */ +#define EN_EEPROM 0x10 /* Enable EEPROM programming */ +#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */ +#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */ +#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */ +#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */ +#define GTIMEOUT 0x80 /* Global timer reach 0 */ +#define CON5068 0x10 /* External 50/68 pin connected */ +#define CON68 0x08 /* Internal 68 pin connected */ +#define CON50 0x04 /* Internal 50 pin connected */ +#define WIDESCSI 0x02 /* Wide SCSI card */ +#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */ +#define NVR_BITOUT 0x08 /* Serial data out */ +#define NVR_BITIN 0x04 /* Serial data in */ +#define NVR_CLOCK 0x02 /* Serial clock */ +#define NVR_SELECT 0x01 /* Serial select */ +#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */ +#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */ +#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */ + +int trm_Interrupt(void *); +int trm_init(struct trm_softc *, int); + +#endif /* trm_h */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 34201effde1..01fc09e530c 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.126 2002/02/14 18:13:58 mickey Exp $ +# $OpenBSD: files.pci,v 1.127 2002/02/18 01:55:30 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. @@ -386,6 +386,10 @@ file dev/pci/cmpci.c cmpci attach iha at pci with iha_pci file dev/pci/iha_pci.c iha_pci +# Tekram TRM-S1040 SCSI Cards (DC395U/UW/F,DC315/U) +attach trm at pci with trm_pci +file dev/pci/trm_pci.c trm_pci + # AMD Am53c974 PCscsi-PCI SCSI controllers device pcscp: scsi, ncr53c9x attach pcscp at pci diff --git a/sys/dev/pci/trm_pci.c b/sys/dev/pci/trm_pci.c new file mode 100644 index 00000000000..32d39e0d56d --- /dev/null +++ b/sys/dev/pci/trm_pci.c @@ -0,0 +1,169 @@ +/* $OpenBSD: trm_pci.c,v 1.1 2002/02/18 01:55:30 krw Exp $ + * ------------------------------------------------------------ + * O.S : OpenBSD + * FILE NAME : trm_pci.c + * BY : Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for Tekram DC395U/UW/F,DC315/U + * PCI SCSI Bus Master Host Adapter + * (SCSI chip set used Tekram ASIC TRM-S1040) + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * (C)Copyright 2001-2002 Ashley R. Martens and Kenneth R. Westerback + * ------------------------------------------------------------ + * HISTORY: + * + * REV# DATE NAME DESCRIPTION + * 1.00 05/01/99 ERICH CHEN First released for NetBSD 1.4.x + * 1.01 00/00/00 MARTIN AKESSON Port to OpenBSD 2.8 + * 1.02 Sep 19, 2001 ASHLEY MARTENS Cleanup and formatting + * ------------------------------------------------------------ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ------------------------------------------------------------ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/trm.h> + +int trm_pci_probe (struct device *, void *, void *); +void trm_pci_attach (struct device *, struct device *, void *); + +struct cfattach trm_pci_ca = { + sizeof(struct trm_softc), + trm_pci_probe, + trm_pci_attach, + NULL, /* Detach */ + NULL, /* Activate */ + NULL /* ZeroRef */ +}; + + +/* + * ------------------------------------------------------------ + * Function : trm_pci_probe + * Purpose : Check the slots looking for a board we recognize. + * If we find one, note ti's address (slot) and call + * the actual probe routine to check it out. + * Inputs : + * ------------------------------------------------------------ + */ +int +trm_pci_probe(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_TEKRAM2) { + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_TEKRAM2_DC3X5U: + return 1; + } + } + return 0; +} + +/* + * ------------------------------------------------------------ + * Function : trm_pci_attach + * Purpose : + * Inputs : + * ------------------------------------------------------------ + */ +void +trm_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct pci_attach_args *pa = aux; + bus_space_handle_t ioh;/* bus space handle */ + pci_intr_handle_t ih; + struct trm_softc *sc = (void *)self; + bus_space_tag_t iot; /* bus space tag */ + const char *intrstr; + pcireg_t command; + int unit; + + unit = sc->sc_device.dv_unit; + + /* + * These cards do not allow memory mapped accesses. + * pa_pc: chipset tag + * pa_tag: pci tag + */ + command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + command |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); + + if (PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_TEKRAM2_DC3X5U) + return; + + /* + * mask for get correct base address of pci IO port + */ + if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL, 0) != 0) { + printf("%s: unable to map registers\n", sc->sc_device.dv_xname); + return; + } + + /* + * test checksum of eeprom & initial "ACB" adapter control block + */ + sc->sc_iotag = iot; + sc->sc_iohandle = ioh; + sc->sc_dmatag = pa->pa_dmat; + + if (trm_init(sc, unit) != 0) { + printf("%s: trm_init failed", sc->sc_device.dv_xname); + return; + } + + /* + * Map and establish interrupt + */ + if (pci_intr_map(pa, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_device.dv_xname); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + + if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, trm_Interrupt, sc, + sc->sc_device.dv_xname) == NULL) { + printf("\n%s: couldn't establish interrupt", sc->sc_device.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + } else { + if (intrstr != NULL) + printf(": %s\n", intrstr); + + /* Tell SCSI layer about our SCSI bus */ + config_found(&sc->sc_device, &sc->sc_link, scsiprint); + } +} |