📄 scsi_sii.c
字号:
* 128K data buffer allocation strategy: * 1KB - SII controller for non READ/WRITE DMA transfers * 16KB - for each tape unit * 8KB - for each cdrom unit * ??KB - for each disk unit * ?? is what's left after tapes and cdroms divided by # of disks. * ?? must be >= 8KB, should be >= 16KB, if not reduce * number of cdroms to 2, then number of tapes to 2. * If that don't fix it panic! * In any "real" configuration, we should * never hit these limits. */ if ((nNSII == 1) || (sii_firstcall == 0)) { dboff = 0x0; /* * Setup 128 byte ram buffer slots for each target to * be used for non READ/WRITE DMA Transfers on the SII. */ for(i=0; i<NDPS; i++) { sc->sc_siidboff[i] = dboff;#ifdef vax dboff += 128;#endif vax#ifdef mips dboff += 256;#endif mips }#ifdef vax dboff = (1 * 1024);#endif vax#ifdef mips dboff = (2 * 1024);#endif mips /* determine rz slot size, must be > 16kb */ cz_slotsize = tz_slotsize = rz_slotsize = 16 * 1024; for (cntlr=0; cntlr<nNSII; cntlr++) { sc = &sz_softc[cntlr]; for (targid=0; targid<NDPS; targid++) { if (targid == sc->sc_sysid) continue; if (sc->sc_alive[targid] == 0) continue; sc->sc_dboff[targid][0] = dboff; sc->sc_dboff[targid][1] = dboff+SII_MAX_DMA_XFER_LENGTH; sc->sc_segcnt[targid] = 64 * 1024; /* Get rid of in freds code */ dboff += rz_slotsize; PRINTD(targid, 0x20, ("sii_probe: cntlr=%d targid=%d devtype=%x ", cntlr, targid, sc->sc_devtyp[targid])); PRINTD(targid, 0x20, ("req/ack=%d slotsize=%d\n", sc->sc_siireqack[targid], sc->sc_segcnt[targid])); } } } if (sii_firstcall) sii_firstcall = 0;#ifdef SZDEBUG PRINTD(-1, 0x8, ("sii_probe: done probing the SCSI bus\n")); if(sii_debug_probe) { siidebug = save_siidebug; siitarget = save_siitarget; }#endif SZDEBUG return(alive);}/****************************************************************** * * Start a SCSI operation on the SII chip. * ******************************************************************/sii_scsistart(sc, targid, bp)register struct sz_softc *sc;int targid;register struct buf *bp;{ int cntlr = sc->sc_siinum; register struct sii_regs *siiaddr = (struct sii_regs *)sc->sc_scsiaddr; volatile char *stv; int retval; int timer; int phase; u_short cstat, dstat; int flags; /* * If "bp" is "0" we use polled scsi mode, disallow reselect * attempts, and disable interrupts for all DMA transfers. We * poll for DMA completion instead of allowing interrupts. */ siiaddr->sii_csr |= SII_SLE;#ifdef mips wbflush();#endif mips if(bp == (struct buf *)0) { siiaddr->sii_csr &= ~(SII_RSE | SII_IE); sc->scsi_polled_mode = 1; } else { /* Have to clear out the rambuffer area. It is possible for a target to not not return all the requested bytes. We have to make sure that at least the extra bytes are cleared. */ if(sc->sc_rzspecial[targid]) { struct mode_sel_sns_params *msp; /* Some risk here using get_validbuf() w/out any error checking. It is probably low, at this point the target should not be doing anything, and ready for the upcomming command. Note: this should be moved into the DMA level code. */ msp = (struct mode_sel_sns_params *)sc->sc_rzparams[targid]; if(sc->sc_actcmd[targid] == SZ_MODSNS) { stv = sc->sc_rambuff + (sc->sc_dboff[targid][get_validbuf(sc, targid)] * 2); (sc->wmbzero)(stv, msp->msp_length); } } siiaddr->sii_csr |= (SII_RSE | SII_IE); sc->scsi_polled_mode = 0; }#ifdef mips wbflush();#endif mips /* Perform target arbitration and selection */ if((retval = sii_select_target(sc, targid)) != SZ_SUCCESS) { return(retval); }BEGIN_LOOP: /* Loop through all bus phases until command complete */ sc->scsi_completed[targid] = 0; sc->scsi_bus_idle = 0; cstat = siiaddr->sii_cstat; dstat = siiaddr->sii_dstat; do {/*XPRINTF(XPR_NFS, "B: %x", sc->sc_siidmacount[targid],0,0,0);cprintf("B: %x %x", sc->sc_siidmacount[targid],bp,0,0);*/ if(cstat & (SII_CI|SII_DI)) { /* Check for a BUS ERROR */ if(cstat & SII_BER) { siiaddr->sii_cstat = SII_BER;#ifdef mips wbflush();#endif mips } /* Check for a PARITY ERROR */ if(dstat & SII_IPE) { flags = SZ_HARDERR | SZ_LOGREGS; scsi_logerr(sc, 0, targid, SZ_ET_PARITY, 0, 0, flags); PRINTD(targid, 0x10, ("sii_scsistart: scsi %d parity error\n", cntlr)); goto HANDLE_ABORT; } /* Check for a BUS RESET */ if(cstat & SII_RST_ONBUS) { siiaddr->sii_cstat = SII_RST_ONBUS;#ifdef mips wbflush();#endif mips scsi_logerr(sc, 0, -1, SZ_ET_BUSRST, 0, 0, SZ_HARDERR); PRINTD(targid, 0x10, ("sii_scsistart: scsi %d bus reset\n", cntlr)); goto HANDLE_ABORT; } /* Check for a STATE CHANGE */ if(cstat & SII_SCH) { /* If SZ_BUSYTARG is set, the target returned a BUSY status for the current command. The target has disconnected from the bus. Return SZ_IP, and allow the interrupt handler take care of the disconnect. */ if( bp && (sc->sc_szflags[targid] & SZ_BUSYTARG) ) { PRINTD(targid, 0x4, ("sii_scsistart: target %d BUSY rtn SZ_IP\n", targid )); return(SZ_IP); } siiaddr->sii_cstat = SII_SCH; /* clear the intr */# ifdef mips wbflush(); /* wait for write buffers */# endif mips if(sii_state_change(sc, &targid) != SZ_SUCCESS) goto HANDLE_ABORT; } /* If disconnected and went to BUS FREE STATE then break */ if(sc->scsi_bus_idle) break; /* Check for a PHASE MISMATCH */ if(dstat & SII_MIS) { /* Check for a BUS ERROR */ if(cstat & SII_BER) { siiaddr->sii_cstat = SII_BER;#ifdef mips wbflush();#endif mips } /* Always clear DID DMA flag on a phase change */ sc->sc_szflags[targid] &= ~SZ_DID_DMA; /* Handle the current bus phase */ if(sii_phase_change(sc, dstat) != SZ_SUCCESS) goto HANDLE_ABORT; /* Wait for the next bus phase */ if(!sc->scsi_completed[targid] && !(sc->sc_szflags[targid] & (SZ_WAS_DISCON|SZ_DID_DMA))) { timer = 1000; while(--timer && !(siiaddr->sii_dstat & SII_MIS)); dstat = siiaddr->sii_dstat; if(!sc->scsi_polled_mode && (timer == 0) && !(dstat & (SII_CI|SII_DI)) && (sc->sc_actcmd[targid] != SZ_RQSNS)) { PRINTD(targid, 0x4, ("sii_scsistart: SII_MIS didn't set rtn SZ_IP\n")); return(SZ_IP); } } } /* Check for fragmented DMA transfers (>8K) */ if(sc->scsi_polled_mode && !(dstat & SII_MIS) && (dstat & (SII_TBE|SII_IBF)) && ((sc->sc_fstate == SZ_DATAI_PHA) || (sc->sc_fstate == SZ_DATAO_PHA))) { /* Restart the DMA transfer */ if(!sii_restartdma(sc)) goto HANDLE_ABORT; } /* Sometimes the target stays in the same phase */ if((dstat & (SII_IBF|SII_TBE)) && !(dstat & SII_MIS) && ((sc->sc_fstate != SZ_DATAI_PHA) && (sc->sc_fstate != SZ_DATAO_PHA))) { /* Check for a BUS ERROR */ if(cstat & SII_BER) { siiaddr->sii_cstat = SII_BER;#ifdef mips wbflush();#endif mips } /* Handle the current bus phase */ if(sii_phase_change(sc, dstat) != SZ_SUCCESS) goto HANDLE_ABORT; } } dstat = siiaddr->sii_dstat; cstat = siiaddr->sii_cstat; /* } while(sc->scsi_polled_mode && /* dont spin in here!!!! */ } while(!sc->scsi_bus_idle && !(sc->sc_szflags[targid] & (SZ_WAS_DISCON|SZ_DID_DMA)));/*XPRINTF(XPR_NFS, "E: %x", sc->sc_siidmacount[targid],0,0,0);cprintf("E: %x", sc->sc_siidmacount[targid],0,0,0);*/ /* * Check the status of the current SCSI operation. If the SCSI * operation completed or disconnected then start the next SCSI * operation, otherwise wait for the DMA to complete. * */ if(sc->scsi_bus_idle || (sc->sc_szflags[targid] & SZ_WAS_DISCON)) { if(sc->scsi_completed[targid]) { PRINTD(targid, 0x4, ("sii_scsistart: COMMAND COMPLETED successfully\n")); sc->scsi_completed[targid] = 0; sc->sc_active = 0; if (sc->sc_szflags[targid] & SZ_BUSYTARG) return(SZ_IP); if(sc->sc_status[targid] == SZ_GOOD) return(SZ_SUCCESS); else return(SZ_RET_ERR); } else if(sc->sc_szflags[targid] & SZ_WAS_DISCON) { PRINTD(targid, 0x4, ("sii_scsistart: COMMAND IN PROGRESS disconnected\n")); return(SZ_IP); } else { sc->sc_active = 0;#ifdef NO_TIMER /* JAG */ if(sc->sc_szflags[targid] & SZ_TIMERON) { untimeout(sii_timer, (caddr_t)sc->sc_unit[targid]); sc->sc_szflags[targid] &= ~SZ_TIMERON; }#endif NO_TIMER /* JAG */ return(SZ_RET_ERR); } } else if(sc->sc_szflags[targid] & SZ_DID_DMA) { /* Poll and busy wait for DMA completion */ if(sc->scsi_polled_mode) { PRINTD(targid, 0x4, ("sii_scsistart: COMMAND IN PROGRESS dma poll mode\n")); siiaddr->sii_csr &= ~SII_IE;#ifdef mips wbflush();#endif mips SZWAIT_UNTIL((siiaddr->sii_dstat & SII_DNE),sii_wait_count,retval); siiaddr->sii_dstat = SII_DNE;#ifdef mips wbflush();#endif mips sc->sc_szflags[targid] &= ~SZ_DID_DMA; /* Update the remaining dma count for this current transfer */ if(sc->sc_siidmacount[targid] > SII_MAX_DMA_XFER_LENGTH) sc->sc_siidmacount[targid] -= (SII_MAX_DMA_XFER_LENGTH - siiaddr->sii_dmlotc); else sc->sc_siidmacount[targid] -= (sc->sc_siidmacount[targid] - siiaddr->sii_dmlotc); if(retval >= sii_wait_count) goto HANDLE_ABORT; else goto BEGIN_LOOP; } /* Wait for interrupt to signal DMA completion */ else { PRINTD(targid, 0x4, ("sii_scsistart: COMMAND IN PROGRESS dma interrupt mode\n")); return(SZ_IP); } } else if(!sc->scsi_polled_mode) return(SZ_IP); /* dont spin in here!!! */HANDLE_ABORT: /* Abort the current SCSI operation due to error */ PRINTD(targid, 0x10, ("sii_scsistart: command aborted (bus=%d target=%d cmd=0x%x)\n", cntlr, targid, sc->sc_curcmd[targid])); PRINTD(targid, 0x10, ("", sii_dumpregs(cntlr, 0))); flags = SZ_HARDERR | SZ_LOGCMD | SZ_LOGREGS; scsi_logerr(sc, 0, targid, SZ_ET_CMDABRTD, 0, 0, flags); sii_reset(sc); siiaddr->sii_cstat = 0xffff; siiaddr->sii_dstat = 0xffff;#ifdef mips wbflush();#endif mips sc->sc_selstat[targid] = SZ_IDLE; sc->sc_active = 0; return(SZ_RET_ABORT);}/****************************************************************** * * Perform the arbitration/selection phases for the SII chip. * ******************************************************************/sii_select_target(sc, targid)register struct sz_softc *sc;int targid;{ int cntlr = sc->sc_siinum; register struct sii_regs *siiaddr = (struct sii_regs *)sc->sc_scsiaddr; int retval, i; int retries = 3; int sii_select_wait = 5000; int sii_rse_flag; /* Loop till retries exhausted */ for(i = 0; i < retries; i++) { /* * Determine if reselections are currently enabled. If so, * disable them during the selection. */ sii_rse_flag = siiaddr->sii_csr & SII_RSE; if (sii_rse_flag) { siiaddr->sii_csr &= ~SII_RSE;#ifdef mips wbflush();#endif mips } /* Do a quick check on the bus to see if it is in the busy state. If BUSY or SEL is asserted, another device on the bus is in a selection phase. Return SZ_BUSBUSY. SZ_BUSBUSY can be returned and the statemachine can leave. This is a single Initiator bus, and a selection is either mine or a reselection, in which the interrupt handler will be called. */ if( (siiaddr->sii_sc1 & (SII_SC1_BSY | SII_SC1_SEL)) !=0 ) { PRINTD(targid, 0x104, ("sii_select_target: Bus BUSY on select of ID %d\n",targid)); /* * Turn reselections back on before leaving.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -