/* $OpenBSD: trm.c,v 1.44 2022/01/09 05:42:42 jsg 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 #include #include #include #include #include #include #include #include #include #include #include /* #define TRM_DEBUG0 */ void trm_initSRB(struct trm_scsi_req_q *); void trm_check_eeprom(struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); void trm_read_all (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); void trm_write_all (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t); void trm_set_data (bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t); void trm_write_cmd(bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t); u_int8_t trm_get_data(bus_space_tag_t, bus_space_handle_t, u_int8_t); void trm_wait_30us(bus_space_tag_t, bus_space_handle_t); void *trm_srb_alloc(void *); 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_srb_reinit (struct trm_softc *, struct trm_scsi_req_q *); void trm_srb_free (void *, void *); 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 *, struct trm_dcb *); 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 *); /* * ------------------------------------------------------------ * * 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_srb_alloc * Purpose : Get the first free SRB * Inputs : * Return : NULL or a free SCSI Request block * ------------------------------------------------------------ */ void * trm_srb_alloc(void *xsc) { struct trm_softc *sc = xsc; struct trm_scsi_req_q *pSRB; mtx_enter(&sc->sc_srb_mtx); pSRB = TAILQ_FIRST(&sc->freeSRB); if (pSRB != NULL) TAILQ_REMOVE(&sc->freeSRB, pSRB, link); mtx_leave(&sc->sc_srb_mtx); #ifdef TRM_DEBUG0 printf("%s: trm_srb_alloc. pSRB = %p, next pSRB = %p\n", sc->sc_device.dv_xname, pSRB, TAILQ_FIRST(&sc->freeSRB)); #endif return pSRB; } /* * ------------------------------------------------------------ * Function : trm_RewaitSRB * Purpose : Q back to pending Q * Inputs : struct trm_dcb * - * struct trm_scsi_req_q * - * ------------------------------------------------------------ */ void trm_RewaitSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) { int intflag; intflag = splbio(); if ((pSRB->SRBFlag & TRM_ON_WAITING_SRB) != 0) { pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB; TAILQ_REMOVE(&sc->waitingSRB, pSRB, link); } if ((pSRB->SRBFlag & TRM_ON_GOING_SRB) != 0) { pSRB->SRBFlag &= ~TRM_ON_GOING_SRB; TAILQ_REMOVE(&sc->goingSRB, pSRB, link); } pSRB->SRBState = TRM_READY; pSRB->TargetStatus = SCSI_OK; pSRB->AdaptStatus = TRM_STATUS_GOOD; pSRB->SRBFlag |= TRM_ON_WAITING_SRB; TAILQ_INSERT_HEAD(&sc->waitingSRB, pSRB, link); splx(intflag); } /* * ------------------------------------------------------------ * Function : trm_StartWaitingSRB * Purpose : If there is no active DCB then run robin through * the DCB's to find the next waiting SRB * and move it to the going list. * Inputs : struct trm_softc * - * ------------------------------------------------------------ */ void trm_StartWaitingSRB(struct trm_softc *sc) { struct trm_scsi_req_q *pSRB, *next; int intflag; intflag = splbio(); if ((sc->pActiveDCB != NULL) || (TAILQ_EMPTY(&sc->waitingSRB)) || (sc->sc_Flag & (RESET_DETECT | RESET_DONE | RESET_DEV)) != 0) goto out; for (pSRB = TAILQ_FIRST(&sc->waitingSRB); pSRB != NULL; pSRB = next) { next = TAILQ_NEXT(pSRB, link); if (trm_StartSRB(sc, pSRB) == 0) { pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB; TAILQ_REMOVE(&sc->waitingSRB, pSRB, link); pSRB->SRBFlag |= TRM_ON_GOING_SRB; TAILQ_INSERT_TAIL(&sc->goingSRB, pSRB, link); break; } } out: splx(intflag); } /* * ------------------------------------------------------------ * Function : trm_scsi_cmd * Purpose : enqueues a SCSI command * Inputs : * Call By : GENERIC SCSI driver * ------------------------------------------------------------ */ void trm_scsi_cmd(struct scsi_xfer *xs) { struct trm_scsi_req_q *pSRB; bus_space_handle_t ioh; struct trm_softc *sc; bus_space_tag_t iot; struct trm_dcb *pDCB; u_int8_t target, lun; int i, error, intflag, timeout, xferflags; target = xs->sc_link->target; lun = xs->sc_link->lun; sc = xs->sc_link->bus->sb_adapter_softc; ioh = sc->sc_iohandle; iot = sc->sc_iotag; #ifdef TRM_DEBUG0 if ((xs->flags & SCSI_POLL) != 0) { sc_print_addr(xs->sc_link); printf("trm_scsi_cmd. sc = %p, xs = %p, opcode = 0x%02x\n", sc, xs, lun, xs->cmd.opcode); } #endif if (target >= TRM_MAX_TARGETS) { sc_print_addr(xs->sc_link); printf("target >= %d\n", TRM_MAX_TARGETS); xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); return; } if (lun >= TRM_MAX_LUNS) { sc_print_addr(xs->sc_link); printf("lun >= %d\n", TRM_MAX_LUNS); xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); return; } pDCB = sc->pDCB[target][lun]; if (pDCB == NULL) { /* Removed as a result of INQUIRY proving no device present */ xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); return; } xferflags = xs->flags; if (xferflags & SCSI_RESET) { #ifdef TRM_DEBUG0 sc_print_addr(xs->sc_link); printf("trm_reset via SCSI_RESET\n"); #endif trm_reset(sc); xs->error = XS_NOERROR; scsi_done(xs); return; } pSRB = xs->io; trm_srb_reinit(sc, pSRB); xs->error = XS_NOERROR; xs->status = SCSI_OK; xs->resid = 0; intflag = splbio(); /* * BuildSRB(pSRB,pDCB); */ if (xs->datalen != 0) { #ifdef TRM_DEBUG0 sc_print_addr(xs->sc_link); printf("xs->datalen=%x\n", (u_int32_t)&xs->datalen); sc_print_addr(xs->sc_link); printf("sc->sc_dmatag=0x%x\n", (u_int32_t)sc->sc_dmatag); sc_print_addr(xs->sc_link); printf("pSRB->dmamapxfer=0x%x\n", (u_int32_t)pSRB->dmamapxfer); sc_print_addr(xs->sc_link); printf("xs->data=0x%x\n", (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) { sc_print_addr(xs->sc_link); printf("DMA transfer map unable to load, error = %d\n", error); xs->error = XS_DRIVER_STUFFUP; splx(intflag); scsi_done(xs); return; } bus_dmamap_sync(sc->sc_dmatag, pSRB->dmamapxfer, 0, pSRB->dmamapxfer->dm_mapsize, (xferflags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); /* * Set up the scatter gather list */ for (i = 0; i < pSRB->dmamapxfer->dm_nsegs; i++) { pSRB->SegmentX[i].address = pSRB->dmamapxfer->dm_segs[i].ds_addr; pSRB->SegmentX[i].length = pSRB->dmamapxfer->dm_segs[i].ds_len; } pSRB->SRBTotalXferLength = xs->datalen; pSRB->SRBSGCount = pSRB->dmamapxfer->dm_nsegs; } pSRB->pSRBDCB = pDCB; pSRB->xs = xs; pSRB->ScsiCmdLen = xs->cmdlen; memcpy(pSRB->CmdBlock, &xs->cmd, xs->cmdlen); timeout_set(&xs->stimeout, trm_timeout, pSRB); pSRB->SRBFlag |= TRM_ON_WAITING_SRB; TAILQ_INSERT_TAIL(&sc->waitingSRB, pSRB, link); trm_StartWaitingSRB(sc); if ((xferflags & SCSI_POLL) == 0) { timeout_add_msec(&xs->stimeout, xs->timeout); splx(intflag); return; } splx(intflag); for (timeout = xs->timeout; timeout > 0; timeout--) { intflag = splbio(); trm_Interrupt(sc); splx(intflag); if (ISSET(xs->flags, ITSDONE)) break; DELAY(1000); } if (!ISSET(xs->flags, ITSDONE) && timeout == 0) trm_timeout(pSRB); scsi_done(xs); } /* * ------------------------------------------------------------ * 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 || sc->pDCB[target][0] == NULL) continue; if ((sc->pDCB[target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0) quirks = SDEV_NOWIDE | SDEV_NOSYNC | SDEV_NOTAGS; else if (sc->pDCB[target][0]->sc_link != NULL) 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; 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, NULL); 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->bus->sb_adapter_softc; sc_print_addr(xs->sc_link); printf("SCSI OpCode 0x%02x ", xs->cmd.opcode); if (pSRB->SRBFlag & TRM_AUTO_REQSENSE) printf("REQUEST SENSE "); printf("timed out\n"); pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT; trm_FinishSRB(sc, pSRB); #ifdef TRM_DEBUG0 sc_print_addr(xs->sc_link); printf("trm_reset via trm_timeout()\n"); #endif trm_reset(sc); trm_StartWaitingSRB(sc); } } /* * ------------------------------------------------------------ * Function : trm_StartSRB * Purpose : Send the commands in the SRB to the device * Inputs : struct trm_softc * - * struct trm_scsi_req_q * - * Return : 0 - SCSI processor is unoccupied * 1 - SCSI processor is occupied with an SRB * ------------------------------------------------------------ */ int trm_StartSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct trm_dcb *pDCB = pSRB->pSRBDCB; u_int32_t tag_mask; u_int8_t tag_id, scsicommand; #ifdef TRM_DEBUG0 printf("%s: trm_StartSRB. sc = %p, pDCB = %p, pSRB = %p\n", sc->sc_device.dv_xname, sc, pDCB, pSRB); #endif /* * If the queue is full or the SCSI processor has a pending interrupt * then try again later. */ if ((pDCB->DCBFlag & TRM_QUEUE_FULL) || (bus_space_read_2(iot, ioh, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT)) return (1); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, pDCB->target); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); if ((sc->pDCB[pDCB->target][0]->sc_link != NULL) && ((sc->pDCB[pDCB->target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0)) { sc->pDCB[pDCB->target][0]->DCBFlag |= TRM_QUIRKS_VALID; trm_ResetDevParam(sc, sc->pDCB[pDCB->target][0], sc->pDCB[pDCB->target][0]->sc_link->quirks); } /* * Flush FIFO */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); sc->MsgCnt = 1; sc->MsgBuf[0] = pDCB->IdentifyMsg; if (((pSRB->xs->flags & SCSI_POLL) != 0) || (pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE)) sc->MsgBuf[0] &= ~MSG_IDENTIFY_DISCFLAG; scsicommand = SCMD_SEL_ATN; if ((pDCB->DCBFlag & (TRM_WIDE_NEGO_ENABLE | TRM_SYNC_NEGO_ENABLE)) != 0) { scsicommand = SCMD_SEL_ATNSTOP; pSRB->SRBState = TRM_MSGOUT; } else if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) == 0) { pDCB->DCBFlag |= TRM_QUEUE_FULL; } else if ((sc->MsgBuf[0] & MSG_IDENTIFY_DISCFLAG) != 0) { if (pSRB->TagNumber == TRM_NO_TAG) { for (tag_id=1, tag_mask=2; tag_id < 32; tag_id++, tag_mask <<= 1) if ((tag_mask & pDCB->TagMask) == 0) { pDCB->TagMask |= tag_mask; pSRB->TagNumber = tag_id; break; } if (tag_id >= 32) { pDCB->DCBFlag |= TRM_QUEUE_FULL; sc->MsgCnt = 0; return 1; } } /* TODO XXXX: Should send ORDERED_Q_TAG if metadata (non-block) i/o!? */ sc->MsgBuf[sc->MsgCnt++] = MSG_SIMPLE_Q_TAG; sc->MsgBuf[sc->MsgCnt++] = pSRB->TagNumber; scsicommand = SCMD_SEL_ATN3; } pSRB->SRBState = TRM_START; pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ sc->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB; if (sc->MsgCnt > 0) { bus_space_write_1(iot, ioh, TRM_S1040_SCSI_FIFO, sc->MsgBuf[0]); if (sc->MsgCnt > 1) { DELAY(30); bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[1], sc->MsgCnt - 1); } sc->MsgCnt = 0; } /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH | DO_HWRESELECT); /* * SCSI command */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, scsicommand); return 0; } /* * ------------------------------------------------------------ * Function : trm_Interrupt * Purpose : Catch an interrupt from the adapter * Process pending device interrupts. * Inputs : void * - struct trm_softc * structure pointer * ------------------------------------------------------------ */ int trm_Interrupt(void *vsc) { void (*stateV)(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *); struct trm_scsi_req_q *pSRB; bus_space_handle_t ioh; struct trm_softc *sc = (struct trm_softc *)vsc; bus_space_tag_t iot; u_int16_t phase; u_int8_t scsi_status, scsi_intstatus; if (sc == NULL) 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)) return 0; scsi_intstatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS); #ifdef TRM_DEBUG0 printf("%s: trm_interrupt - scsi_status=0x%02x, scsi_intstatus=0x%02x\n", sc->sc_device.dv_xname, scsi_status, scsi_intstatus); #endif if ((scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) != 0) trm_Disconnect(sc); else if ((scsi_intstatus & INT_RESELECTED) != 0) trm_Reselect(sc); else if ((scsi_intstatus & INT_SCSIRESET) != 0) trm_ScsiRstDetect(sc); else if ((sc->pActiveDCB != NULL) && ((scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) != 0)) { pSRB = sc->pActiveDCB->pActiveSRB; /* * software sequential machine */ phase = (u_int16_t) pSRB->ScsiPhase; /* phase: */ /* * 62037 or 62137 * call trm_SCSI_phase0[]... "phase entry" * handle every phase before start transfer */ stateV = trm_SCSI_phase0[phase]; stateV(sc, pSRB, &scsi_status); /* * if any exception occurred * scsi_status will be modified to bus free phase * new scsi_status transfer out from previous stateV */ /* * phase:0,1,2,3,4,5,6,7 */ pSRB->ScsiPhase = scsi_status & PHASEMASK; phase = (u_int16_t) scsi_status & PHASEMASK; /* * call trm_SCSI_phase1[]... "phase entry" * handle every phase do transfer */ stateV = trm_SCSI_phase1[phase]; stateV(sc, pSRB, &scsi_status); } else { return 0; } return 1; } /* * ------------------------------------------------------------ * Function : trm_MsgOutPhase0 * Purpose : Check the state machine before sending a message out * Inputs : struct trm_softc * - * struct trm_scsi_req_q * - * u_int8_t * - scsi status, set to PH_BUS_FREE if not ready * ------------------------------------------------------------ */ void trm_MsgOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { switch (pSRB->SRBState) { case TRM_UNEXPECT_RESEL: case TRM_ABORT_SENT: *pscsi_status = PH_BUS_FREE; /* initial phase */ break; default: break; } } /* * ------------------------------------------------------------ * Function : trm_MsgOutPhase1 * Purpose : Write the message out to the bus * Inputs : struct trm_softc * - * struct trm_scsi_req_q * - * u_int8_t * - unused * ------------------------------------------------------------ */ void trm_MsgOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct trm_dcb *pDCB = sc->pActiveDCB; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); if ((pDCB->DCBFlag & TRM_WIDE_NEGO_ENABLE) != 0) { /* * WIDE DATA TRANSFER REQUEST code (03h) */ pDCB->DCBFlag &= ~TRM_WIDE_NEGO_ENABLE; pDCB->DCBFlag |= TRM_DOING_WIDE_NEGO; sc->MsgBuf[0] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG; sc->MsgBuf[1] = MSG_EXTENDED; sc->MsgBuf[2] = MSG_EXT_WDTR_LEN; sc->MsgBuf[3] = MSG_EXT_WDTR; if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0) sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_8_BIT; else sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_16_BIT; sc->MsgCnt = 5; } else if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) { pDCB->DCBFlag &= ~TRM_SYNC_NEGO_ENABLE; pDCB->DCBFlag |= TRM_DOING_SYNC_NEGO; sc->MsgCnt = 0; if ((pDCB->DCBFlag & TRM_WIDE_NEGO_DONE) == 0) sc->MsgBuf[sc->MsgCnt++] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG; sc->MsgBuf[sc->MsgCnt++] = MSG_EXTENDED; sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR_LEN; sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR; sc->MsgBuf[sc->MsgCnt++] = pDCB->MaxNegoPeriod; if (pDCB->MaxNegoPeriod > 0) sc->MsgBuf[sc->MsgCnt++] = TRM_MAX_SYNC_OFFSET; else sc->MsgBuf[sc->MsgCnt++] = 0; } if (sc->MsgCnt > 0) { bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[0], sc->MsgCnt); if (sc->MsgBuf[0] == MSG_ABORT) pSRB->SRBState = TRM_ABORT_SENT; sc->MsgCnt = 0; } /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* * Transfer information out */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); } /* * ------------------------------------------------------------ * Function : trm_CommandPhase1 * Purpose : Send commands to bus * Inputs : * ------------------------------------------------------------ */ void trm_CommandPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN | DO_CLRFIFO); bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &pSRB->CmdBlock[0], pSRB->ScsiCmdLen); pSRB->SRBState = TRM_COMMAND; /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* * Transfer information out */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); } /* * ------------------------------------------------------------ * Function : trm_DataOutPhase0 * Purpose : Ready for Data Out, clear FIFO * Inputs : u_int8_t * - SCSI status, used but not set * ------------------------------------------------------------ */ void trm_DataOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct SGentry *pseg; struct trm_dcb *pDCB; u_int32_t dLeftCounter, TempSRBXferredLength; u_int16_t scsi_status; u_int8_t TempDMAstatus, SGIndexTemp; dLeftCounter = 0; pDCB = pSRB->pSRBDCB; scsi_status = *pscsi_status; if (pSRB->SRBState != TRM_XFERPAD) { if ((scsi_status & PARITYERROR) != 0) pSRB->SRBFlag |= TRM_PARITY_ERROR; if ((scsi_status & SCSIXFERDONE) == 0) { /* * when data transfer from DMA FIFO to SCSI FIFO * if there was some data left in SCSI FIFO */ dLeftCounter = (u_int32_t)(bus_space_read_1( iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x1F); if (pDCB->SyncPeriod & WIDE_SYNC) { /* * if WIDE scsi SCSI FIFOCNT unit is word * so need to * 2 */ dLeftCounter <<= 1; } } /* * calculate all the residue data that not yet transferred * SCSI transfer counter + left in SCSI FIFO data * * .....TRM_S1040_SCSI_COUNTER (24bits) * The counter always decrement by one for every SCSI byte * transfer. * .....TRM_S1040_SCSI_FIFOCNT ( 5bits) * The counter is SCSI FIFO offset counter */ dLeftCounter += bus_space_read_4(iot, ioh, TRM_S1040_SCSI_COUNTER); if (dLeftCounter == 1) { dLeftCounter = 0; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); } if (dLeftCounter == 0 || (scsi_status & SCSIXFERCNT_2_ZERO) != 0) { TempDMAstatus = bus_space_read_1(iot, ioh, TRM_S1040_DMA_STATUS); while ((TempDMAstatus & DMAXFERCOMP) == 0) { TempDMAstatus = bus_space_read_1(iot, ioh, TRM_S1040_DMA_STATUS); } pSRB->SRBTotalXferLength = 0; } else { /* * Update SG list */ /* * if transfer not yet complete * there were some data residue in SCSI FIFO or * SCSI transfer counter not empty */ if (pSRB->SRBTotalXferLength != dLeftCounter) { /* * data that had transferred length */ TempSRBXferredLength = pSRB->SRBTotalXferLength - dLeftCounter; /* * next time to be transferred length */ pSRB->SRBTotalXferLength = dLeftCounter; /* * parsing from last time disconnect SRBSGIndex */ pseg = &pSRB->SegmentX[pSRB->SRBSGIndex]; for (SGIndexTemp = pSRB->SRBSGIndex; SGIndexTemp < pSRB->SRBSGCount; SGIndexTemp++) { /* * find last time which SG transfer be * disconnect */ if (TempSRBXferredLength >= pseg->length) TempSRBXferredLength -= pseg->length; else { /* * update last time disconnected * SG list */ /* * residue data length */ pseg->length -= TempSRBXferredLength; /* * residue data pointer */ pseg->address += TempSRBXferredLength; pSRB->SRBSGIndex = SGIndexTemp; break; } pseg++; } } } } bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER); } /* * ------------------------------------------------------------ * Function : trm_DataOutPhase1 * Purpose : Transfers data out, calls trm_DataIO_transfer * Inputs : * ------------------------------------------------------------ */ void trm_DataOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { trm_DataIO_transfer(sc, pSRB, XFERDATAOUT); } /* * ------------------------------------------------------------ * Function : trm_DataInPhase0 * Purpose : Prepare for reading data in from bus * Inputs : * ------------------------------------------------------------ */ void trm_DataInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct SGentry *pseg; u_int32_t TempSRBXferredLength, dLeftCounter; u_int16_t scsi_status; u_int8_t SGIndexTemp; dLeftCounter = 0; scsi_status = *pscsi_status; if (pSRB->SRBState != TRM_XFERPAD) { if ((scsi_status & PARITYERROR) != 0) pSRB->SRBFlag |= TRM_PARITY_ERROR; dLeftCounter += bus_space_read_4(iot, ioh, TRM_S1040_SCSI_COUNTER); if (dLeftCounter == 0 || (scsi_status & SCSIXFERCNT_2_ZERO) != 0) { while ((bus_space_read_1(iot, ioh, TRM_S1040_DMA_STATUS) & DMAXFERCOMP) == 0) ; pSRB->SRBTotalXferLength = 0; } else { /* * phase changed * * parsing the case: * when a transfer not yet complete * but be disconnected by uper layer * if transfer not yet complete * there were some data residue in SCSI FIFO or * SCSI transfer counter not empty */ if (pSRB->SRBTotalXferLength != dLeftCounter) { /* * data that had transferred length */ TempSRBXferredLength = pSRB->SRBTotalXferLength - dLeftCounter; /* * next time to be transferred length */ pSRB->SRBTotalXferLength = dLeftCounter; /* * parsing from last time disconnect SRBSGIndex */ pseg = &pSRB->SegmentX[pSRB->SRBSGIndex]; for (SGIndexTemp = pSRB->SRBSGIndex; SGIndexTemp < pSRB->SRBSGCount; SGIndexTemp++) { /* * find last time which SG transfer be * disconnect */ if (TempSRBXferredLength >= pseg->length) { TempSRBXferredLength -= pseg->length; } else { /* * update last time disconnected * SG list * * residue data length */ pseg->length -= TempSRBXferredLength; /* * residue data pointer */ pseg->address += TempSRBXferredLength; pSRB->SRBSGIndex = SGIndexTemp; break; } pseg++; } } } } } /* * ------------------------------------------------------------ * Function : trm_DataInPhase1 * Purpose : Transfer data in from bus, calls trm_DataIO_transfer * Inputs : * ------------------------------------------------------------ */ void trm_DataInPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { trm_DataIO_transfer(sc, pSRB, XFERDATAIN); } /* * ------------------------------------------------------------ * Function : trm_DataIO_transfer * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_DataIO_transfer(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int16_t ioDir) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct trm_dcb *pDCB = pSRB->pSRBDCB; u_int8_t bval; if (pSRB->SRBSGIndex < pSRB->SRBSGCount) { if (pSRB->SRBTotalXferLength != 0) { /* * load what physical address of Scatter/Gather list * table want to be transfer */ pSRB->SRBState = TRM_DATA_XFER; bus_space_write_4(iot, ioh, TRM_S1040_DMA_XHIGHADDR, 0); bus_space_write_4(iot, ioh, TRM_S1040_DMA_XLOWADDR, (pSRB->SRBSGPhyAddr + ((u_int32_t)pSRB->SRBSGIndex << 3))); /* * load how many bytes in the Scatter/Gather list table */ bus_space_write_4(iot, ioh, TRM_S1040_DMA_XCNT, ((u_int32_t)(pSRB->SRBSGCount - pSRB->SRBSGIndex) << 3)); /* * load total transfer length (24bits, * pSRB->SRBTotalXferLength) max value 16Mbyte */ bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, pSRB->SRBTotalXferLength); /* * Start DMA transfer */ bus_space_write_2(iot,ioh,TRM_S1040_DMA_COMMAND, ioDir); /* bus_space_write_2(iot, ioh, TRM_S1040_DMA_CONTROL, STARTDMAXFER);*/ /* * Set the transfer bus and direction */ bval = ioDir == XFERDATAOUT ? SCMD_DMA_OUT :SCMD_DMA_IN; } else { /* * xfer pad */ if (pSRB->SRBSGCount) pSRB->AdaptStatus = TRM_OVER_UNDER_RUN; if (pDCB->SyncPeriod & WIDE_SYNC) { bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 2); } else { bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1); } if (ioDir == XFERDATAOUT) { bus_space_write_2(iot, ioh, TRM_S1040_SCSI_FIFO, 0); } else { bus_space_read_2(iot, ioh, TRM_S1040_SCSI_FIFO); } pSRB->SRBState = TRM_XFERPAD; /* * Set the transfer bus and direction */ bval = ioDir == XFERDATAOUT ? SCMD_FIFO_OUT : SCMD_FIFO_IN; } /* * it's important for atn stop */ bus_space_write_2(iot,ioh,TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* * Tell the bus to do the transfer */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, bval); } } /* * ------------------------------------------------------------ * Function : trm_StatusPhase0 * Purpose : Update Target Status with data from SCSI FIFO * Inputs : u_int8_t * - Set to PH_BUS_FREE * ------------------------------------------------------------ */ void trm_StatusPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; pSRB->TargetStatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); pSRB->SRBState = TRM_COMPLETED; /* * initial phase */ *pscsi_status = PH_BUS_FREE; /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* * Tell bus that the message was accepted */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); } /* * ------------------------------------------------------------ * Function : trm_StatusPhase1 * Purpose : Clear FIFO of DMA and SCSI * Inputs : * ------------------------------------------------------------ */ void trm_StatusPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; if ((bus_space_read_2(iot, ioh, TRM_S1040_DMA_COMMAND) & 0x0001) != 0) { if ((bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x40) == 0) { bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); } if ((bus_space_read_2(iot, ioh, TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) { bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, CLRXFIFO); } } else { if ((bus_space_read_2(iot, ioh, TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) { bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, CLRXFIFO); } if ((bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x40) == 0) { bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); } } pSRB->SRBState = TRM_STATUS; /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* * Tell the bus that the command is complete */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_COMP); } /* * ------------------------------------------------------------ * Function : trm_MsgInPhase0 * Purpose : * Inputs : * * extended message codes: * code description * ---- ----------- * 02h Reserved * 00h MODIFY DATA POINTER * 01h SYNCHRONOUS DATA TRANSFER REQUEST * 03h WIDE DATA TRANSFER REQUEST * 04h - 7Fh Reserved * 80h - FFh Vendor specific * * ------------------------------------------------------------ */ void trm_MsgInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct trm_dcb *pDCB; u_int8_t message_in_code, bIndex, message_in_tag_id; pDCB = sc->pActiveDCB; message_in_code = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); if (pSRB->SRBState != TRM_EXTEND_MSGIN) { switch (message_in_code) { case MSG_DISCONNECT: pSRB->SRBState = TRM_DISCONNECTED; break; case MSG_EXTENDED: case MSG_SIMPLE_Q_TAG: case MSG_HEAD_OF_Q_TAG: case MSG_ORDERED_Q_TAG: pSRB->SRBState = TRM_EXTEND_MSGIN; /* * extended message (01h) */ bzero(&sc->MsgBuf[0], sizeof(sc->MsgBuf)); sc->MsgBuf[0] = message_in_code; sc->MsgCnt = 1; /* * extended message length (n) */ break; case MSG_MESSAGE_REJECT: /* * Reject message */ if ((pDCB->DCBFlag & TRM_DOING_WIDE_NEGO) != 0) { /* * do wide nego reject */ pDCB = pSRB->pSRBDCB; pDCB->DCBFlag &= ~TRM_DOING_WIDE_NEGO; pDCB->DCBFlag |= TRM_WIDE_NEGO_DONE; if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) { /* * Set ATN, in case ATN was clear */ pSRB->SRBState = TRM_MSGOUT; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_SETATN); } else { /* * Clear ATN */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN); } } else if ((pDCB->DCBFlag & TRM_DOING_SYNC_NEGO) != 0) { /* * do sync nego reject */ pDCB = pSRB->pSRBDCB; pDCB->DCBFlag &= ~TRM_DOING_SYNC_NEGO; pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN); goto re_prog; } break; case MSG_IGN_WIDE_RESIDUE: bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1); bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO); break; default: break; } } else { /* * We are collecting an extended message. Save the latest byte and then * check to see if the message is complete. If so, process it. */ sc->MsgBuf[sc->MsgCnt++] = message_in_code; #ifdef TRM_DEBUG0 printf("%s: sc->MsgBuf = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", sc->sc_device.dv_xname, sc->MsgBuf[0], sc->MsgBuf[1], sc->MsgBuf[2], sc->MsgBuf[3], sc->MsgBuf[4], sc->MsgBuf[5] ); #endif switch (sc->MsgBuf[0]) { case MSG_SIMPLE_Q_TAG: case MSG_HEAD_OF_Q_TAG: case MSG_ORDERED_Q_TAG: if (sc->MsgCnt == 2) { pSRB->SRBState = TRM_FREE; message_in_tag_id = sc->MsgBuf[1]; sc->MsgCnt = 0; TAILQ_FOREACH(pSRB, &sc->goingSRB, link) { if ((pSRB->pSRBDCB == pDCB) && (pSRB->TagNumber == message_in_tag_id)) break; } if ((pSRB != NULL) && (pSRB->SRBState == TRM_DISCONNECTED)) { pDCB->pActiveSRB = pSRB; pSRB->SRBState = TRM_DATA_XFER; } else { #ifdef TRM_DEBUG0 printf("%s: TRM_UNEXPECT_RESEL!\n", sc->sc_device.dv_xname); #endif pSRB = &sc->SRB[0]; trm_srb_reinit(sc, pSRB); 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; 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: trm_GoingSRB_Done(sc, pDCB); break; case TRM_START: case TRM_MSGOUT: /* * Selection time out */ /* If not polling just keep trying until xs->stimeout expires */ if ((pSRB->xs->flags & SCSI_POLL) == 0) { trm_RewaitSRB(sc, pSRB); } else { pSRB->TargetStatus = TRM_SCSI_SELECT_TIMEOUT; goto disc1; } break; case TRM_COMPLETED: disc1: /* * TRM_COMPLETED - remove id from mask of active tags */ pDCB->pActiveSRB = NULL; trm_FinishSRB(sc, pSRB); break; default: break; } trm_StartWaitingSRB(sc); } /* * ------------------------------------------------------------ * Function : trm_Reselect * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_Reselect(struct trm_softc *sc) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; struct trm_scsi_req_q *pSRB; struct trm_dcb *pDCB; u_int16_t RselTarLunId; u_int8_t target, lun; #ifdef TRM_DEBUG0 printf("%s: trm_Reselect\n", sc->sc_device.dv_xname); #endif pDCB = sc->pActiveDCB; if (pDCB != NULL) { /* * Arbitration lost but Reselection win */ pSRB = pDCB->pActiveSRB; trm_RewaitSRB(sc, pSRB); } /* * Read Reselected Target Id and LUN */ RselTarLunId = bus_space_read_2(iot, ioh, TRM_S1040_SCSI_TARGETID) & 0x1FFF; /* TODO XXXX: Make endian independent! */ target = RselTarLunId & 0xff; lun = (RselTarLunId >> 8) & 0xff; #ifdef TRM_DEBUG0 printf("%s: reselect - target = %d, lun = %d\n", sc->sc_device.dv_xname, target, lun); #endif if ((target < TRM_MAX_TARGETS) && (lun < TRM_MAX_LUNS)) pDCB = sc->pDCB[target][lun]; else pDCB = NULL; if (pDCB == NULL) printf("%s: reselect - target = %d, lun = %d not found\n", sc->sc_device.dv_xname, target, lun); sc->pActiveDCB = pDCB; /* TODO XXXX: This will crash if pDCB is ever NULL */ if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0) { pSRB = &sc->SRB[0]; pDCB->pActiveSRB = pSRB; } else { pSRB = pDCB->pActiveSRB; if (pSRB == NULL || (pSRB->SRBState != TRM_DISCONNECTED)) { /* * abort command */ pSRB = &sc->SRB[0]; pSRB->SRBState = TRM_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; trm_EnableMsgOut(sc, MSG_ABORT); } else pSRB->SRBState = TRM_DATA_XFER; } pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ /* * Program HA ID, target ID, period and offset */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, target); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); /* * it's important for atn stop */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); DELAY(30); /* * SCSI command * to rls the /ACK signal */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); } /* * ------------------------------------------------------------ * Function : trm_FinishSRB * Purpose : Complete execution of a SCSI command * 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, intflag; #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; intflag = splbio(); if (pSRB->TagNumber != TRM_NO_TAG) { pSRB->pSRBDCB->TagMask &= ~(1 << pSRB->TagNumber); pSRB->TagNumber = TRM_NO_TAG; } /* 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); } splx(intflag); if (xs == NULL) { return; } timeout_del(&xs->stimeout); xs->status = pSRB->TargetStatus; 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); } 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 sc_print_addr(xs->sc_link); printf(" trm_FinishSRB. TRM_PARITY_ERROR\n"); #endif xs->error = XS_DRIVER_STUFFUP; } else if ((pSRB->SRBFlag & TRM_SCSI_TIMED_OUT) != 0) { if ((pSRB->SRBFlag & TRM_AUTO_REQSENSE) == 0) xs->error = XS_TIMEOUT; else { bzero(&xs->sense, sizeof(xs->sense)); xs->status = SCSI_CHECK; xs->error = XS_SENSE; } } 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 sc_print_addr(xs->sc_link); printf("trm_FinishSRB. TRM_OVER_UNDER_RUN\n"); #endif xs->error = XS_DRIVER_STUFFUP; break; default: #ifdef TRM_DEBUG0 sc_print_addr(xs->sc_link); printf("trm_FinishSRB. AdaptStatus Error = 0x%02x\n", 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 && pDCB->sc_link == NULL) { 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 sc_print_addr(xs->sc_link); printf("trm_FinishSRB NO Device\n"); #endif free(pDCB, M_DEVBUF, 0); sc->pDCB[target][lun] = NULL; pDCB = NULL; } else pDCB->sc_link = xs->sc_link; } } /* * Notify cmd done */ #ifdef TRM_DEBUG0 if ((xs->error != 0) || (xs->status != 0) || ((xs->flags & SCSI_POLL) != 0)) { sc_print_addr(xs->sc_link); printf("trm_FinishSRB. xs->cmd.opcode = 0x%02x, xs->error = %d, xs->status = %d\n", xs->cmd.opcode, xs->error, xs->status); } #endif if (ISSET(xs->flags, SCSI_POLL)) SET(xs->flags, ITSDONE); else scsi_done(xs); } /* * ------------------------------------------------------------ * Function : trm_srb_reinit * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_srb_reinit(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) { 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; } /* * ------------------------------------------------------------ * Function : trm_srb_free * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_srb_free(void *xsc, void *xpSRB) { struct trm_softc *sc = xsc; struct trm_scsi_req_q *pSRB = xpSRB; trm_srb_reinit(sc, pSRB); if (pSRB != &sc->SRB[0]) { mtx_enter(&sc->sc_srb_mtx); TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link); mtx_leave(&sc->sc_srb_mtx); } } /* * ------------------------------------------------------------ * Function : trm_GoingSRB_Done * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_GoingSRB_Done(struct trm_softc *sc, struct trm_dcb *pDCB) { struct trm_scsi_req_q *pSRB, *pNextSRB; /* ASSUME we are inside a splbio()/splx() pair */ 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 (pDCB == NULL || 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; } } /* * ------------------------------------------------------------ * Function : trm_ResetSCSIBus * Purpose : Reset the SCSI bus * Inputs : struct trm_softc * - * ------------------------------------------------------------ */ void trm_ResetSCSIBus(struct trm_softc *sc) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; int intflag; intflag = splbio(); sc->sc_Flag |= RESET_DEV; bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); while ((bus_space_read_2(iot, ioh, TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET) == 0); splx(intflag); } /* * ------------------------------------------------------------ * Function : trm_ScsiRstDetect * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_ScsiRstDetect(struct trm_softc *sc) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; int wlval; #ifdef TRM_DEBUG0 printf("%s: trm_ScsiRstDetect\n", sc->sc_device.dv_xname); #endif wlval = 1000; /* * delay 1 sec */ while (--wlval != 0) DELAY(1000); bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER); bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); if ((sc->sc_Flag & RESET_DEV) != 0) sc->sc_Flag |= RESET_DONE; else { sc->sc_Flag |= RESET_DETECT; trm_ResetAllDevParam(sc); trm_RecoverSRB(sc); sc->pActiveDCB = NULL; sc->sc_Flag = 0; trm_StartWaitingSRB(sc); } } /* * ------------------------------------------------------------ * Function : trm_RequestSense * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_RequestSense(struct trm_softc *sc, struct trm_scsi_req_q *pSRB) { pSRB->SRBFlag |= TRM_AUTO_REQSENSE; /* * Status of initiator/target */ pSRB->AdaptStatus = TRM_STATUS_GOOD; pSRB->TargetStatus = SCSI_OK; /* * Status of initiator/target */ pSRB->SegmentX[0].address = pSRB->scsisensePhyAddr; pSRB->SegmentX[0].length = sizeof(struct scsi_sense_data); pSRB->SRBTotalXferLength = sizeof(struct scsi_sense_data); pSRB->SRBSGCount = 1; pSRB->SRBSGIndex = 0; bzero(&pSRB->CmdBlock[0], sizeof(pSRB->CmdBlock)); pSRB->CmdBlock[0] = REQUEST_SENSE; pSRB->CmdBlock[1] = (pSRB->xs->sc_link->lun) << 5; pSRB->CmdBlock[4] = sizeof(struct scsi_sense_data); pSRB->ScsiCmdLen = 6; if ((pSRB->xs != NULL) && ((pSRB->xs->flags & SCSI_POLL) == 0)) timeout_add_msec(&pSRB->xs->stimeout, pSRB->xs->timeout); if (trm_StartSRB(sc, pSRB) != 0) trm_RewaitSRB(sc, pSRB); } /* * ------------------------------------------------------------ * Function : trm_EnableMsgOut * Purpose : set up MsgBuf to send out a single byte message * Inputs : * ------------------------------------------------------------ */ void trm_EnableMsgOut(struct trm_softc *sc, u_int8_t msg) { sc->MsgBuf[0] = msg; sc->MsgCnt = 1; bus_space_write_2(sc->sc_iotag, sc->sc_iohandle, TRM_S1040_SCSI_CONTROL, DO_SETATN); } /* * ------------------------------------------------------------ * Function : trm_linkSRB * Purpose : * Inputs : * ------------------------------------------------------------ */ void trm_linkSRB(struct trm_softc *sc) { struct trm_scsi_req_q *pSRB; int i, intflag; intflag = splbio(); for (i = 0; i < TRM_MAX_SRB_CNT; i++) { pSRB = &sc->SRB[i]; pSRB->PhysSRB = sc->sc_dmamap_control->dm_segs[0].ds_addr + i * sizeof(struct trm_scsi_req_q); pSRB->SRBSGPhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr + i * sizeof(struct trm_scsi_req_q) + offsetof(struct trm_scsi_req_q, SegmentX); pSRB->scsisensePhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr + i * sizeof(struct trm_scsi_req_q) + offsetof(struct trm_scsi_req_q, scsisense); /* * map all SRB space */ if (bus_dmamap_create(sc->sc_dmatag, TRM_MAX_PHYSG_BYTE, TRM_MAX_SG_LISTENTRY, TRM_MAX_PHYSG_BYTE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &pSRB->dmamapxfer) != 0) { printf("%s: unable to create DMA transfer map\n", sc->sc_device.dv_xname); splx(intflag); return; } if (i > 0) /* We use sc->SRB[0] directly, so *don't* link it */ TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link); #ifdef TRM_DEBUG0 printf("pSRB = %p ", pSRB); #endif } #ifdef TRM_DEBUG0 printf("\n "); #endif splx(intflag); } /* * ------------------------------------------------------------ * Function : trm_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); mtx_init(&sc->sc_srb_mtx, IPL_BIO); scsi_iopool_init(&sc->sc_iopool, sc, trm_srb_alloc, trm_srb_free); sc->pActiveDCB = NULL; sc->sc_AdapterUnit = unit; sc->sc_AdaptSCSIID = pEEpromBuf->NvramScsiId; sc->sc_TagMaxNum = 2 << pEEpromBuf->NvramMaxTag; sc->sc_Flag = 0; /* * put all SRB's (except [0]) onto the freeSRB list */ trm_linkSRB(sc); /* * allocate DCB array */ for (target = 0; target < TRM_MAX_TARGETS; target++) { if (target == sc->sc_AdaptSCSIID) continue; for (lun = 0; lun < TRM_MAX_LUNS; lun++) { pDCB = (struct trm_dcb *)malloc(sizeof(struct trm_dcb), M_DEVBUF, M_NOWAIT | M_ZERO); sc->pDCB[target][lun] = pDCB; if (pDCB == NULL) continue; pDCB->target = target; pDCB->lun = lun; pDCB->pActiveSRB = NULL; } } trm_reset(sc); } /* * ------------------------------------------------------------ * Function : trm_write_all * Description : write pEEpromBuf 128 bytes to seeprom * Input : iot, ioh - chip's base address * Output : none * ------------------------------------------------------------ */ void trm_write_all(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, bus_space_handle_t ioh) { u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf; u_int8_t bAddr; /* * Enable SEEPROM */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM)); /* * Write enable */ trm_write_cmd(iot, ioh, 0x04, 0xFF); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); trm_wait_30us(iot, ioh); for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) trm_set_data(iot, ioh, bAddr, *bpEeprom); /* * Write disable */ trm_write_cmd(iot, ioh, 0x04, 0x00); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); trm_wait_30us(iot, ioh); /* * Disable SEEPROM */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM)); } /* * ------------------------------------------------------------ * Function : trm_set_data * Description : write one byte to seeprom * Input : iot, ioh - chip's base address * bAddr - address of SEEPROM * bData - data of SEEPROM * Output : none * ------------------------------------------------------------ */ void trm_set_data(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr, u_int8_t bData) { u_int8_t bSendData; int i; /* * Send write command & address */ trm_write_cmd(iot, ioh, 0x05, bAddr); /* * Write data */ for (i = 0; i < 8; i++, bData <<= 1) { bSendData = NVR_SELECT; if ((bData & 0x80) != 0) { /* Start from bit 7 */ bSendData |= NVR_BITOUT; } bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, (bSendData | NVR_CLOCK)); trm_wait_30us(iot, ioh); } bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); trm_wait_30us(iot, ioh); /* * Disable chip select */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); trm_wait_30us(iot, ioh); /* * Wait for write ready */ for (;;) { bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, (NVR_SELECT | NVR_CLOCK)); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); trm_wait_30us(iot, ioh); if (bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM) & NVR_BITIN) break; } /* * Disable chip select */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); } /* * ------------------------------------------------------------ * Function : trm_read_all * Description : read seeprom 128 bytes to pEEpromBuf * Input : pEEpromBuf, iot, ioh - chip's base address * Output : none * ------------------------------------------------------------ */ void trm_read_all(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, bus_space_handle_t ioh) { u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf; u_int8_t bAddr; /* * Enable SEEPROM */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM)); for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) *bpEeprom = trm_get_data(iot, ioh, bAddr); /* * Disable SEEPROM */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL, (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM)); } /* * ------------------------------------------------------------ * Function : trm_get_data * Description : read one byte from seeprom * Input : iot, ioh - chip's base address * bAddr - address of SEEPROM * Output : bData - data of SEEPROM * ------------------------------------------------------------ */ u_int8_t trm_get_data( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr) { u_int8_t bReadData, bData; int i; bData = 0; /* * Send read command & address */ trm_write_cmd(iot, ioh, 0x06, bAddr); for (i = 0; i < 8; i++) { /* * Read data */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, (NVR_SELECT | NVR_CLOCK)); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); /* * Get data bit while falling edge */ bReadData = bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM); bData <<= 1; if ((bReadData & NVR_BITIN) != 0) bData |= 1; trm_wait_30us(iot, ioh); } /* * Disable chip select */ bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0); return bData; } /* * ------------------------------------------------------------ * Function : trm_wait_30us * Description : wait 30 us * Input : iot, ioh - chip's base address * Output : none * ------------------------------------------------------------ */ void trm_wait_30us(bus_space_tag_t iot, bus_space_handle_t ioh) { bus_space_write_1(iot, ioh, TRM_S1040_GEN_TIMER, 5); while ((bus_space_read_1(iot, ioh, TRM_S1040_GEN_STATUS) & GTIMEOUT) == 0); } /* * ------------------------------------------------------------ * Function : trm_write_cmd * Description : write SB and Op Code into seeprom * Input : iot, ioh - chip's base address * bCmd - SB + Op Code * bAddr - address of SEEPROM * Output : none * ------------------------------------------------------------ */ void trm_write_cmd( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bCmd, u_int8_t bAddr) { u_int8_t bSendData; int i; for (i = 0; i < 3; i++, bCmd <<= 1) { /* * Program SB + OP code */ bSendData = NVR_SELECT; if (bCmd & 0x04) /* Start from bit 2 */ bSendData |= NVR_BITOUT; bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, (bSendData | NVR_CLOCK)); trm_wait_30us(iot, ioh); } for (i = 0; i < 7; i++, bAddr <<= 1) { /* * Program address */ bSendData = NVR_SELECT; if (bAddr & 0x40) { /* Start from bit 6 */ bSendData |= NVR_BITOUT; } bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData); trm_wait_30us(iot, ioh); bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, (bSendData | NVR_CLOCK)); trm_wait_30us(iot, ioh); } bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT); trm_wait_30us(iot, ioh); } /* * ------------------------------------------------------------ * Function : trm_check_eeprom * Description : read eeprom 128 bytes to pEEpromBuf and check * checksum. If it is wrong, updated with default value. * Input : eeprom, iot, ioh - chip's base address * Output : none * ------------------------------------------------------------ */ void trm_check_eeprom(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot, bus_space_handle_t ioh) { u_int32_t *dpEeprom = (u_int32_t *)pEEpromBuf->NvramTarget; u_int32_t dAddr; u_int16_t *wpEeprom = (u_int16_t *)pEEpromBuf; u_int16_t wAddr, wCheckSum; #ifdef TRM_DEBUG0 printf("\ntrm_check_eeprom\n"); #endif trm_read_all(pEEpromBuf, iot, ioh); wCheckSum = 0; for (wAddr = 0; wAddr < 64; wAddr++, wpEeprom++) wCheckSum += *wpEeprom; if (wCheckSum != 0x1234) { #ifdef TRM_DEBUG0 printf("TRM_S1040 EEPROM Check Sum ERROR (load default)\n"); #endif /* * Checksum error, load default */ pEEpromBuf->NvramSubVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2; pEEpromBuf->NvramSubVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2 >> 8); pEEpromBuf->NvramSubSysID[0] = (u_int8_t) PCI_PRODUCT_TEKRAM2_DC3X5U; pEEpromBuf->NvramSubSysID[1] = (u_int8_t) (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8); pEEpromBuf->NvramSubClass = 0; pEEpromBuf->NvramVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2; pEEpromBuf->NvramVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2 >> 8); pEEpromBuf->NvramDeviceID[0] = (u_int8_t) PCI_PRODUCT_TEKRAM2_DC3X5U; pEEpromBuf->NvramDeviceID[1] = (u_int8_t) (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8); pEEpromBuf->NvramReserved = 0; for (dAddr = 0; dAddr < 16; dAddr++, dpEeprom++) /* * NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */ *dpEeprom = 0x00000077; /* * NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */ *dpEeprom++ = 0x04000F07; /* * NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 */ *dpEeprom++ = 0x00000015; for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++) *dpEeprom = 0; pEEpromBuf->NvramCheckSum = 0; for (wAddr = 0, wCheckSum =0; wAddr < 63; wAddr++, wpEeprom++) wCheckSum += *wpEeprom; *wpEeprom = 0x1234 - wCheckSum; trm_write_all(pEEpromBuf, iot, ioh); } } /* * ------------------------------------------------------------ * Function : trm_initAdapter * Purpose : initialize the SCSI chip ctrl registers * Inputs : psh - pointer to this host adapter's structure * ------------------------------------------------------------ */ void trm_initAdapter(struct trm_softc *sc) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; u_int16_t wval; u_int8_t bval; /* * program configuration 0 */ if ((sc->sc_config & HCC_PARITY) != 0) { bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; } else { bval = PHASELATCH | INITIATOR | BLOCKRST; } bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG0, bval); /* * program configuration 1 */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG1, 0x13); /* * 250ms selection timeout */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TIMEOUT, TRM_SEL_TIMEOUT); /* * Mask all the interrupt */ bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, 0); bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, 0); /* * Reset SCSI module */ bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); /* * program Host ID */ bval = sc->sc_AdaptSCSIID; bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, bval); /* * set asynchronous transfer */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, 0); /* * Turn LED control off */ wval = bus_space_read_2(iot, ioh, TRM_S1040_GEN_CONTROL) & 0x7F; bus_space_write_2(iot, ioh, TRM_S1040_GEN_CONTROL, wval); /* * DMA config */ wval = bus_space_read_2(iot, ioh, TRM_S1040_DMA_CONFIG) | DMA_ENHANCE; bus_space_write_2(iot, ioh, TRM_S1040_DMA_CONFIG, wval); /* * Clear pending interrupt status */ bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS); /* * Enable SCSI interrupts */ bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, (EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED | EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE)); bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, EN_SCSIINTR); } /* * ------------------------------------------------------------ * Function : trm_init * Purpose : initialize the internal structures for a given SCSI host * Inputs : host - pointer to this host adapter's structure * Preconditions : when this function is called, the chip_type field of * the ACB structure MUST have been set. * ------------------------------------------------------------ */ int trm_init(struct trm_softc *sc, int unit) { const bus_space_handle_t ioh = sc->sc_iohandle; const bus_space_tag_t iot = sc->sc_iotag; bus_dma_segment_t seg; int error, rseg, all_srbs_size; /* * EEPROM CHECKSUM */ trm_check_eeprom(&trm_eepromBuf[unit], iot, ioh); /* * MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK */ /* * allocate the space for all SCSI control blocks (SRB) for DMA memory. */ all_srbs_size = TRM_MAX_SRB_CNT * sizeof(struct trm_scsi_req_q); error = bus_dmamem_alloc(sc->sc_dmatag, all_srbs_size, NBPG, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT | BUS_DMA_ZERO); if (error != 0) { printf("%s: unable to allocate SCSI REQUEST BLOCKS, error = %d\n", sc->sc_device.dv_xname, error); /*errx(error, "%s: unable to allocate SCSI request blocks", sc->sc_device.dv_xname);*/ return -1; } error = bus_dmamem_map(sc->sc_dmatag, &seg, rseg, all_srbs_size, (caddr_t *)&sc->SRB, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); if (error != 0) { printf("%s: unable to map SCSI REQUEST BLOCKS, error = %d\n", sc->sc_device.dv_xname, error); /*errx(error, "unable to map SCSI request blocks");*/ return -1; } error = bus_dmamap_create(sc->sc_dmatag, all_srbs_size, 1, all_srbs_size, 0, BUS_DMA_NOWAIT,&sc->sc_dmamap_control); if (error != 0) { printf("%s: unable to create SRB DMA maps, error = %d\n", sc->sc_device.dv_xname, error); /*errx(error, "unable to create SRB DMA maps");*/ return -1; } error = bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap_control, sc->SRB, all_srbs_size, NULL, BUS_DMA_NOWAIT); if (error != 0) { printf("%s: unable to load SRB DMA maps, error = %d\n", sc->sc_device.dv_xname, error); /*errx(error, "unable to load SRB DMA maps");*/ return -1; } #ifdef TRM_DEBUG0 printf("\n\n%s: all_srbs_size=%x\n", sc->sc_device.dv_xname, all_srbs_size); #endif trm_initACB(sc, unit); trm_initAdapter(sc); return 0; } /* ------------------------------------------------------------ * Function : trm_print_info * Purpose : Print the DCB negotiation information * Inputs : * ------------------------------------------------------------ */ void trm_print_info(struct trm_softc *sc, struct trm_dcb *pDCB) { int syncXfer, index; index = pDCB->SyncPeriod & ~(WIDE_SYNC | ALT_SYNC); printf("%s: target %d using ", sc->sc_device.dv_xname, pDCB->target); if ((pDCB->SyncPeriod & WIDE_SYNC) != 0) printf("16 bit "); else printf("8 bit "); if (pDCB->SyncOffset == 0) printf("Asynchronous "); else { syncXfer = 100000 / (trm_clock_period[index] * 4); printf("%d.%01d MHz, Offset %d ", syncXfer / 100, syncXfer % 100, pDCB->SyncOffset); } printf("data transfers "); if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0) printf("with Tag Queuing"); printf("\n"); }