#ifndef lint static char sccsid[] = "@(#)si.c 1.1 86/02/03 Copyr 1985 Sun Micro"; #endif /* * Copyright (c) 1985 by Sun Microsystems, Inc. */ #include "si.h" #if NSI > 0 /* * Generic scsi routines. */ #include "../h/param.h" #include "../h/systm.h" #include "../h/dk.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/map.h" #include "../h/vmmac.h" /* *#include "../h/ioctl.h" *#include "../h/uio.h" *#include "../h/kernel.h" */ #include "../h/dkbad.h" #include "../machine/pte.h" #include "../machine/psl.h" #include "../machine/mmu.h" #include "../machine/cpu.h" #include "../machine/scb.h" #include "../sun/dklabel.h" #include "../sun/dkio.h" #include "../sundev/mbvar.h" #include "../sundev/screg.h" #include "../sundev/sireg.h" #include "../sundev/scsi.h" struct scsi_ctlr sictlrs[NSI]; #define SINUM(si) (si - sictlrs) int siprobe(), sislave(), siattach(), sigo(), sidone(), sipoll(); int siustart(), sistart(), si_getstatus(); int si_off(), si_cmd(), si_cmdwait(), si_reset(), si_dmacnt(); struct mb_ctlr *siinfo[NSI]; extern struct mb_device *sdinfo[]; struct mb_driver sidriver = { siprobe, sislave, siattach, sigo, sidone, sipoll, sizeof (struct scsi_si_reg), "sd", sdinfo, "si", siinfo, MDR_BIODMA, }; /* routines available to devices specific portion of scsi driver */ struct scsi_ctlr_subr sisubr = { siustart, sistart, sidone, si_cmd, si_getstatus, si_cmdwait, si_off, si_reset, si_dmacnt, sigo, }; extern int scsi_debug; /* generic debug information */ extern int scsi_ntype; extern struct scsi_unit_subr scsi_unit_subr[]; int scsi_disre_enable = 0; /* enable disconnect/reconnect */ int scsi_dis_debug = 0; /* disconnect debug info */ int scsi_resel_debug = 0; /* reselection debug info */ int scsi_reset_debug = 0; /* scsi bus reset debug information */ /* * Determine existence of SCSI host adapter. */ siprobe(reg, ctlr) register struct scsi_si_reg *reg; register int ctlr; { register struct scsi_ctlr *c; /* probe for different scsi host adaptor interfaces */ c = &sictlrs[ctlr]; /* * Check for sbc - NCR 5380 Scsi Bus Ctlr chip. * sbc is common to sun3/50 onboard scsi and vme * scsi board. */ if (peekc(®->sbc_rreg.cbsr) == -1) { return (0); } /* * Determine whether the host adaptor interface is onboard or vme. */ if (cpu == CPU_SUN3_50) { /* probe for sun3/50 dma interface */ if (peek(®->udc_rdata) == -1) return (0); c->c_flags = SCSI_ONBOARD; } else { /* * Probe for vme scsi card but make sure it is not * the SC host adaptor interface. SI vme scsi host * adaptor occupies 2K bytes in the vme address space. * SC vme scsi host adaptor occupies 4K bytes in the * vme address space. So, peek past 2K bytes to * determine which host adaptor is there. */ if (peek(®->dma_addr) == -1) return (0); if (peek((int)reg+0x800) != -1) return (0); c->c_flags = 0; } /* allocate memory for sense information */ c->c_sense = (struct scsi_sense *) rmalloc(iopbmap, (long) sizeof (struct scsi_sense)); if (c->c_sense == NULL) { printf("siprobe: no iopb memory for sense.\n"); return (0); } /* init controller information */ c->c_flags |= SCSI_PRESENT; if (scsi_disre_enable) c->c_flags |= SCSI_EN_DISCON; c->c_sir = reg; c->c_ss = &sisubr; si_reset(c); return (sizeof (struct scsi_si_reg)); } /* * See if a slave exists. * Since it may exist but be powered off, we always say yes. */ /*ARGSUSED*/ sislave(md, reg) register struct mb_device *md; register struct scsi_si_reg *reg; { register struct scsi_unit *un; register int type; /* * This kludge allows autoconfig to print out "sd" for * disks and "st" for tapes. The problem is that there * is only one md_driver for scsi devices. */ type = TYPE(md->md_flags); if (type >= scsi_ntype) { panic("sislave: unknown type in md_flags"); } /* link unit to its controller */ un = (struct scsi_unit *)(*scsi_unit_subr[type].ss_unit_ptr)(md); if (un == 0) { panic("sislave: md_flags scsi type not configured in\n"); } un->un_c = &sictlrs[md->md_ctlr]; md->md_driver->mdr_dname = scsi_unit_subr[type].ss_devname; return (1); } /* * Attach device (boot time). */ siattach(md) register struct mb_device *md; { register int type = TYPE(md->md_flags); register struct mb_ctlr *mc = md->md_mc; register struct scsi_ctlr *c = &sictlrs[md->md_ctlr]; if (type >= scsi_ntype) { panic("siattach: unknown type in md_flags"); } (*scsi_unit_subr[type].ss_attach)(md); if (c->c_flags & SCSI_ONBOARD) { return; } /* * Initialize interrupt vector and address modifier register. * Address modifier specifies standard supervisor data access * with 24 bit vme addresses. May want to change this in the * future to handle 32 bit vme addresses. */ if (mc->mc_intr) { /* setup for vectored interrupts - we will pass ctlr ptr */ c->c_sir->iv_am = (mc->mc_intr->v_vec & 0xff) | VME_SUPV_DATA_24; (*mc->mc_intr->v_vptr) = (int)c; } } /* * SCSI unit start routine. * Called by SCSI device drivers. */ siustart(un) register struct scsi_unit *un; { register struct buf *dp; register struct mb_ctlr *mc; register int s; mc = un->un_mc; dp = &un->un_utab; /* * Caller guarantees: dp->b_actf != NULL && dp->b_active == 0 * Note: dp->b_active == 1 on a reconnect. */ /* * Put device on ready queue for bus. */ if (mc->mc_tab.b_actf == NULL) { mc->mc_tab.b_actf = dp; } else { mc->mc_tab.b_actl->b_forw = dp; } dp->b_forw = NULL; mc->mc_tab.b_actl = dp; dp->b_active = 1; dp->b_un.b_addr = (caddr_t) un; } /* * Set up a scsi operation. */ sistart(un) register struct scsi_unit *un; { register struct mb_ctlr *mc; register struct buf *bp, *dp; mc = un->un_mc; dp = mc->mc_tab.b_actf; /* != NULL guaranteed by caller */ un = (struct scsi_unit *) dp->b_un.b_addr; bp = dp->b_actf; for (;;) { if (bp == NULL) { /* no more blocks for this device */ un->un_utab.b_active = 0; dp = mc->mc_tab.b_actf = dp->b_forw; if (dp == NULL) { /* no more devices for this ctlr */ si_idle(un->un_c); return; } un = (struct scsi_unit *) dp->b_un.b_addr; bp = dp->b_actf; } else { if ((*un->un_ss->ss_start)(bp, un)) { mc->mc_tab.b_active = 1; un->un_c->c_un = un; if (bp == &un->un_sbuf && ((un->un_flags & SC_UNF_DVMA) == 0)) { sigo(mc); } else { (void) mbgo(mc); } return; } dp->b_actf = bp = bp->av_forw; } } } /* * Start up a scsi operation. * Called via mbgo after buffer is in memory. */ sigo(mc) register struct mb_ctlr *mc; { register struct scsi_unit *un; register struct scsi_ctlr *c; register struct buf *bp, *dp; register int unit; c = &sictlrs[mc->mc_ctlr]; dp = mc->mc_tab.b_actf; if (dp == NULL || dp->b_actf == NULL) { panic("sigo queueing error 1"); } bp = dp->b_actf; un = c->c_un; if (dp != &un->un_utab) { panic("sigo queueing error 2"); } un->un_baddr = MBI_ADDR(mc->mc_mbinfo); if ((unit = un->un_md->md_dk) >= 0) { dk_busy |= 1<<unit; dk_xfer[unit]++; dk_wds[unit] += bp->b_bcount >> 6; } (*un->un_ss->ss_mkcdb)(c, un); if (si_cmd(c, un, 1) == 0) { (*un->un_ss->ss_intr)(c, 0, SE_FATAL); si_off(un); } } /* * Handle a polling SCSI bus interrupt. */ sipoll() { register struct scsi_ctlr *c; register int serviced = 0; for (c = sictlrs; c < &sictlrs[NSI]; c++) { if ((c->c_flags & SCSI_PRESENT) == 0) continue; if ((c->c_sir->csr & (SI_CSR_SBC_IP | SI_CSR_DMA_IP | SI_CSR_DMA_CONFLICT)) == 0) { continue; } serviced = 1; siintr(c); } return (serviced); } /* * Clean up queues, free resources, and start next I/O * all done after I/O finishes * Called by mbdone after moving read data from Mainbus */ sidone(mc) register struct mb_ctlr *mc; { register struct buf *bp, *dp; register struct scsi_unit *un; register struct scsi_ctlr *c; bp = mc->mc_tab.b_actf->b_actf; c = &sictlrs[mc->mc_ctlr]; un = c->c_un; /* advance controller queue */ dp = mc->mc_tab.b_actf; mc->mc_tab.b_active = 0; mc->mc_tab.b_actf = dp->b_forw; /* advance unit queue */ dp->b_active = 0; dp->b_actf = bp->av_forw; iodone(bp); /* start next I/O on unit */ if (dp->b_actf) siustart(un); /* start next I/O on controller */ if (mc->mc_tab.b_actf && mc->mc_tab.b_active == 0) { sistart(un); } else { c->c_un = NULL; si_idle(c); } } /*ARGSUSED*/ si_off(un) register struct scsi_unit *un; { #ifdef notdef /* if done to root real bad things happen... */ un->un_present = 0; printf("scsi unit %d/%d offline\n", un->un_target, un->un_lun); if (un->un_md->md_dk > 0) { dk_mspw[un->un_md->md_dk]=0; } #endif } /* * Pass a command to the SCSI bus. */ si_cmd(c, un, intr) register struct scsi_ctlr *c; register struct scsi_unit *un; register int intr; { register u_char *cp; register int i; register int errct; register struct scsi_si_reg *sir = c->c_sir; errct = 0; do { /* disallow disconnects if waiting for command completion */ if (intr == 0) { c->c_flags &= ~SCSI_EN_DISCON; } else { if (scsi_disre_enable) c->c_flags |= SCSI_EN_DISCON; else c->c_flags &= ~SCSI_EN_DISCON; un->un_wantint = 1; } /* * For vme host adaptor interface, dma enable bit may * be set to allow reconnect interrupts to come in. * This must be disabled before arbitration/selection * of target is done. Don't worry about re-enabling * dma. If arb/sel fails, then si_idle() will re-enable. * If arb/sel succeeds then handling of command will * re-enable. * Also, disallow sbc to accept reconnect attempts. * Again, si_idle() will re-enable this if arb/sel fails. * If arb/sel succeeds then we do not want to allow * reconnects anyway. */ if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_DMA_EN; } sir->sbc_wreg.ser = 0; c->c_flags &= ~SCSI_EN_RECON; /* performing target selection */ if (si_arb_sel(c, un) == 0) { goto bad; } /* * Must split dma setup into 2 parts due to sun3/50 * which requires bcr to be set before target * changes phase on scsi bus to data phase. * * Three fields in the per scsi unit structure * hold information pertaining to the current dma * operation: un_dma_curdir, un_dma_curaddr, and * un_dma_curcnt. These fields are used to track * the amount of data dma'd especially when disconnects * and reconnects occur. * If the current command does not involve dma, * these fields are set appropriately. */ if (un->un_dma_count > 0) { if ((c->c_cdb.cmd == SC_READ) || (c->c_cdb.cmd == SC_REQUEST_SENSE)) { un->un_dma_curdir = SI_RECV_DATA; sir->csr &= ~SI_CSR_SEND; } else { un->un_dma_curdir = SI_SEND_DATA; sir->csr |= SI_CSR_SEND; } /* save current dma info for disconnect */ un->un_dma_curaddr = un->un_dma_addr; un->un_dma_curcnt = un->un_dma_count; /* tape has scsi id of 4 */ if ((un->un_target == 4) && scsi_dis_debug) { printf("si_cmd: cmd= %x, addr= %x, cnt= %x\n", c->c_cdb.cmd, un->un_dma_curaddr, un->un_dma_curcnt); } /* reset fifo */ sir->csr &= ~SI_CSR_FIFO_RES; sir->csr |= SI_CSR_FIFO_RES; /* must init bcr before target goes into data phase */ sir->bcr = un->un_dma_curcnt; /* * Currently we don't use all 24 bits of the * count register on the vme interface. To do * this changes are required other places, e.g. * in the scsi_unit structure the fields * un_dma_curcnt and un_dma_count would need to * be changed. */ if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->bcrh = 0; } } else { un->un_dma_curdir = SI_NO_DATA; un->un_dma_curaddr = 0; un->un_dma_curcnt = 0; } cp = (u_char *) &c->c_cdb; if (scsi_debug) { printf("si%d: si_cmd: target %d issuing command ", SINUM(c), un->un_target); for (i = 0; i < sizeof (struct scsi_cdb); i++) { printf("%x ", *cp++); } printf("\n"); cp = (u_char *) &c->c_cdb; } /* put scsi command out on scsi bus */ if (si_putdata(c, PHASE_COMMAND, cp, sizeof(struct scsi_cdb)) == 0) { errct++; } else { /* do final dma setup and start dma operation */ if (un->un_dma_count > 0) { if (c->c_flags & SCSI_ONBOARD) si_ob_dma_setup(c, un); else si_vme_dma_setup(c, un); } else if (un->un_wantint) { /* * If this command does not involve * any dma, we must set things up * so we get an interrupt when the * target is ready to give us status. * The interrupt we get is a phase * mismatch, however, to get this * interrupt the sbc must be in dma * mode. Also, the vme scsi card does * not generate any interrupts unless * dma is enabled in the csr. */ sir->sbc_wreg.mr |= SBC_MR_DMA; sir->sbc_wreg.tcr = TCR_UNSPECIFIED; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_SEND; sir->csr |= SI_CSR_DMA_EN; } } return (1); } } while (errct < SI_NUM_RETRIES); /* clear fields if we were not able to issue scsi cmd */ printf("si_cmd: too many errors\n"); if (un->un_dma_count != 0) { un->un_dma_curdir = 0; un->un_dma_curaddr = 0; un->un_dma_curcnt = 0; sir->bcr = 0; sir->csr &= ~SI_CSR_SEND; } bad: un->un_wantint = 0; si_idle(c); return (0); } /* * Perform the SCSI arbitration and selection phases. */ si_arb_sel(c, un) register struct scsi_ctlr *c; register struct scsi_unit *un; { register struct scsi_si_reg *sir = c->c_sir; register u_char *icrp = &sir->sbc_wreg.icr; register u_char *mrp = &sir->sbc_wreg.mr; register int j; register u_char icr; u_char id; /* wait for scsi bus to become free */ if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_BSY, 0) == 0) { printf("si_arb_sel: continuously busy\n"); si_reset(c); return (0); } /* arbitrate for the scsi bus */ sir->sbc_wreg.odr = SI_HOST_ID; for (j = 0; j < SI_NUM_RETRIES; j++) { *mrp |= SBC_MR_ARB; /* wait for sbc to begin arbitration */ if (si_sbc_wait((caddr_t)icrp, SBC_ICR_AIP, 1) == 0) { /* problem with sbc if arb never begins */ /* * Current bug in Emulex MT02 firmware. * It does not go through the reselection timeout * procedure documented in the scsi protocol spec. */ if ((sir->sbc_rreg.cbsr & SBC_CBSR_SEL) && (sir->sbc_rreg.cbsr & SBC_CBSR_IO) && (sir->sbc_rreg.cdr & SI_HOST_ID)) { printf("si_arb_sel: REselect, flgs %x\n", c->c_flags); if (scsi_resel_debug) halt(""); } else { printf("si_arb_sel: AIP never set, ??\n"); } *mrp &= ~SBC_MR_ARB; return (0); /* si_reset(c); */ } /* check to see if we won arbitration */ DELAY(SI_ARBITRATION_DELAY); if ((*icrp & SBC_ICR_LA) == 0) { if ( ((sir->sbc_rreg.cdr & ~SI_HOST_ID) < SI_HOST_ID) && ((*icrp & SBC_ICR_LA) == 0)) { /* won arbitration */ icr = *icrp & ~SBC_ICR_AIP; *icrp = icr | SBC_ICR_ATN; icr = *icrp & ~SBC_ICR_AIP; *icrp = icr | SBC_ICR_SEL; DELAY(SI_BUS_CLEAR_DELAY + SI_BUS_SETTLE_DELAY); break; } } /* lost arbitration, clear arbitration mode */ *mrp &= ~SBC_MR_ARB; } /* couldn't win arbitration */ if (j == SI_NUM_RETRIES) { /* should never happen since we have highest pri scsi id */ *mrp &= ~SBC_MR_ARB; *icrp = 0; printf("si_arb_sel: couldn't win arbitration\n"); return (0); } /* won arbitration, perform selection */ sir->sbc_wreg.odr = (1 << un->un_target) | SI_HOST_ID; icr = *icrp & ~SBC_ICR_AIP; *icrp = icr | SBC_ICR_DATA | SBC_ICR_BUSY; *mrp &= ~SBC_MR_ARB; /* wait for target to acknowledge selection */ *icrp &= ~SBC_ICR_BUSY; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_BSY, 1) == 0) { if (scsi_debug) printf("si_arb_sel: cbsr bsy never set\n"); si_reset(c); return (0); } *icrp &= ~(SBC_ICR_SEL | SBC_ICR_DATA); /* send target identify message */ if (c->c_flags & SCSI_EN_DISCON) id = SC_DR_IDENTIFY | c->c_cdb.lun; else id = SC_IDENTIFY | c->c_cdb.lun; if (si_putdata(c, PHASE_MSG_OUT, &id, 1) == 0) { if (scsi_debug) printf("si_arb_sel: put of ID MSG %x failed\n", id); } *icrp = 0; return (1); } /* * Set up the SCSI control logic for a dma transfer for vme host adaptor. */ si_vme_dma_setup(c, un) register struct scsi_ctlr *c; register struct scsi_unit *un; { register struct scsi_si_reg *sir = c->c_sir; /* setup starting dma address and number bytes to dma */ sir->dma_addr = un->un_dma_curaddr; sir->dma_count = un->un_dma_curcnt; /* set up byte packing control info */ if (sir->dma_addr & 0x2) { /* setup word dma transfers across vme bus */ sir->csr |= SI_CSR_BPCON; } else { /* setup longword dma transfers across vme bus */ sir->csr &= ~SI_CSR_BPCON; } if (scsi_debug) { printf("si_vme_dma_setup: addr %x, cnt %x, csr %x\n", sir->dma_addr, sir->dma_count, sir->csr); } /* init sbc for dma transfer */ si_sbc_dma_setup(sir, un->un_dma_curdir); /* enable dma - this must be the last step */ sir->csr |= SI_CSR_DMA_EN; } /* * Set up the SCSI control logic for a dma transfer for onboard host * adaptor. */ si_ob_dma_setup(c, un) register struct scsi_ctlr *c; register struct scsi_unit *un; { register struct scsi_si_reg *sir = c->c_sir; register struct udc_table *udct = &c->c_udct; register int addr; /* set up udc dma information */ addr = un->un_dma_curaddr; if (addr < DVMA_OFFSET) addr += DVMA_OFFSET; udct->haddr = ((addr & 0xff0000) >> 8) | UDC_ADDR_INFO; udct->laddr = addr & 0xffff; udct->hcmr = UDC_CMR_HIGH; udct->count = un->un_dma_curcnt / 2; /* #bytes -> #words */ if (un->un_dma_curdir == SI_RECV_DATA) { udct->rsel = UDC_RSEL_RECV; udct->lcmr = UDC_CMR_LRECV; } else { udct->rsel = UDC_RSEL_SEND; udct->lcmr = UDC_CMR_LSEND; if (un->un_dma_curcnt & 1) { udct->count++; } } /* initialize udc chain address register */ sir->udc_raddr = UDC_ADR_CAR_HIGH; DELAY(SI_UDC_WAIT); sir->udc_rdata = ((int)udct & 0xff0000) >> 8; DELAY(SI_UDC_WAIT); sir->udc_raddr = UDC_ADR_CAR_LOW; DELAY(SI_UDC_WAIT); sir->udc_rdata = (int)udct & 0xffff; if (scsi_debug) { printf("si_ob_dma_setup: udct= %x, %x -> %x\n", udct, un->un_dma_curaddr, addr); } /* initialize udc master mode register */ DELAY(SI_UDC_WAIT); sir->udc_raddr = UDC_ADR_MODE; DELAY(SI_UDC_WAIT); sir->udc_rdata = UDC_MODE; /* issue channel interrupt enable command, in case of error, to udc */ DELAY(SI_UDC_WAIT); sir->udc_raddr = UDC_ADR_COMMAND; DELAY(SI_UDC_WAIT); sir->udc_rdata = UDC_CMD_CIE; /* issue start chain command to udc */ DELAY(SI_UDC_WAIT); sir->udc_rdata = UDC_CMD_STRT_CHN; /* put sbc in dma mode and start dma transfer */ si_sbc_dma_setup(sir, un->un_dma_curdir); } /* * Setup and start the sbc for a dma operation. */ si_sbc_dma_setup(sir, dir) register struct scsi_si_reg *sir; register int dir; { sir->sbc_wreg.mr |= SBC_MR_DMA; if (dir == SI_RECV_DATA) { sir->sbc_wreg.tcr = TCR_DATA_IN; sir->sbc_wreg.ircv = 0; } else { sir->sbc_wreg.tcr = TCR_DATA_OUT; sir->sbc_wreg.icr = SBC_ICR_DATA; sir->sbc_wreg.send = 0; } } /* * Cleanup up the SCSI control logic after a dma transfer. */ si_dma_cleanup(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; /* disable dma controller */ if (c->c_flags & SCSI_ONBOARD) { sir->udc_raddr = UDC_ADR_COMMAND; DELAY(SI_UDC_WAIT); sir->udc_rdata = UDC_CMD_RESET; } else { sir->dma_addr = 0; sir->dma_count = 0; } /* reset fifo */ sir->csr &= ~SI_CSR_FIFO_RES; sir->csr |= SI_CSR_FIFO_RES; /* take sbc out of dma mode and reset bcr */ sir->sbc_wreg.icr = 0; sir->sbc_wreg.mr &= ~SBC_MR_DMA; sir->sbc_wreg.tcr = 0; sir->bcr = 0; } /* * Handle special dma receive situations, e.g. an odd number of bytes * in a dma transfer. * The Sun3/50 onboard interface has different situations which * must be handled than the vme interface. */ si_dma_recv(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register struct scsi_unit *un = c->c_un; register int offset; offset = un->un_dma_curaddr + (un->un_dma_curcnt - sir->bcr); /* handle the onboard scsi situations */ if (c->c_flags & SCSI_ONBOARD) { sir->udc_raddr = UDC_ADR_COUNT; /* wait for the fifo to empty */ if (si_wait((u_short *)&sir->csr, SI_CSR_FIFO_EMPTY, 1) == 0) { printf("si_dma_recv: fifo never emptied\n"); return (0); } /* handle odd byte */ if ((un->un_dma_curcnt - sir->bcr) & 1) { DVMA[offset - 1] = (sir->fifo_data & 0xff00) >> 8; /* * The udc may not dma the last word from the fifo_data * register into memory due to how the hardware turns * off the udc at the end of the dma operation. */ } else if (((sir->udc_rdata*2) - sir->bcr) == 2) { DVMA[offset - 2] = (sir->fifo_data & 0xff00) >> 8; DVMA[offset - 1] = sir->fifo_data & 0x00ff; } /* handle the vme scsi situations */ } else if ((sir->csr & SI_CSR_LOB) != 0) { /* * Grabs last few bytes which may not have been dma'd. * Worst case is when longword dma transfers are being done * and there are 3 bytes leftover. * If BPCON bit is set then longword dmas were being done, * otherwise word dmas were being done. */ if ((sir->csr & SI_CSR_BPCON) == 0) { switch (sir->csr & SI_CSR_LOB) { case SI_CSR_LOB_THREE: DVMA[offset - 3] = (sir->bpr & 0xff000000) >> 24; DVMA[offset - 2] = (sir->bpr & 0x00ff0000) >> 16; DVMA[offset - 1] = (sir->bpr & 0x0000ff00) >> 8; break; case SI_CSR_LOB_TWO: DVMA[offset - 2] = (sir->bpr & 0xff000000) >> 24; DVMA[offset - 1] = (sir->bpr & 0x00ff0000) >> 16; break; case SI_CSR_LOB_ONE: DVMA[offset - 1] = (sir->bpr & 0xff000000) >> 24; break; } } else { DVMA[offset - 1] = (sir->bpr & 0x0000ff00) >> 8; } } return (1); } /* * Handle a scsi interrupt. */ siintr(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register struct scsi_unit *un = c->c_un; register int bsr; register int status; register int resid; register int dma_cleanup; register int i; int disconnect; u_char msg; struct buf *dp; int reset_occurred; /* set misc flags */ status = SE_NO_ERROR; resid = 0; dma_cleanup = 0; disconnect = 0; reset_occurred = 0; /* * For vme host adaptor interface, must disable dma before * accessing any registers other than the csr or the * SI_CSR_DMA_CONFLICT bit in the csr will be set. */ if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_DMA_EN; } /* check for dma related errors */ if (sir->csr & (SI_CSR_DMA_IP | SI_CSR_DMA_CONFLICT)) { if (sir->csr & SI_CSR_DMA_BUS_ERR) { printf("siintr: bus error during dma\n"); } else if (sir->csr & SI_CSR_DMA_CONFLICT) { printf("siintr: invalid reg access during dma\n"); } else { if (c->c_flags & SCSI_ONBOARD) printf("siintr: dma ip, unknown reason\n"); else printf("siintr: dma overrun\n"); } /* * Either we were waiting for an interrupt on a phase change * on the scsi bus, an interrupt on a reconnect attempt, * or an interrupt upon completion of a real dma operation. * Each of these situations must be handled appropriately. */ if (sir->sbc_rreg.tcr == TCR_UNSPECIFIED) { sir->sbc_wreg.mr &= ~SBC_MR_DMA; } else if (un == NULL) { si_reset(c); si_idle(c); return; } else { dma_cleanup = 1; } status = SE_FATAL; /* check for interrupt from sbc */ } else if (sir->csr & SI_CSR_SBC_IP) { /* grab the bsr and find out why sbc interrupted */ bsr = sir->sbc_rreg.bsr; /* acknowledge sbc interrupt */ msg = sir->sbc_rreg.clr; /* * Check for sbc end of operation interrupt. * This is the normal interrupt upon completion * of a dma operation. Very soon after this * interrupt is generated a phase mismatch * should occur on the scsi bus as the target * changes from the data in/out phase to the * status phase. */ if (bsr & SBC_BSR_EDMA) { if (si_sbc_wait((caddr_t)&sir->sbc_rreg.bsr, SBC_BSR_PMTCH, 0) == 0) { dma_cleanup = 1; status = SE_FATAL; printf("si: scsi sbc dma end op\n"); goto done; } goto phmismatch; /* check for phase mismatch */ } else if ((bsr & SBC_BSR_PMTCH) == 0) { phmismatch: if ((sir->sbc_rreg.mr & SBC_MR_DMA) == 0) { if (c->c_flags & SCSI_EN_RECON) goto recon; else goto discon; } /* * Handle fake dma mode which is used when we * want an interrupt when target changes scsi * bus to status phase. */ sir->sbc_wreg.mr &= ~SBC_MR_DMA; if (sir->sbc_rreg.tcr == TCR_UNSPECIFIED) { sir->sbc_wreg.tcr = 0; } else { /* need to reset some registers after a dma */ dma_cleanup = 1; /* handle special dma recv situations */ if (un->un_dma_curdir == SI_RECV_DATA) { if (si_dma_recv(c) == 0) { status = SE_RETRYABLE; } } } discon: /* check for disconnect */ if ((sir->sbc_rreg.cbsr & SBC_CBSR_REQ) && ((sir->sbc_rreg.cbsr & CBSR_PHASE_BITS) == PHASE_MSG_IN)) { if (scsi_debug || scsi_dis_debug) printf("siintr: DISconnect\n"); if (si_disconnect(c)) { disconnect = 1; } } goto done; } recon: /* check for reconnect attempt */ if ((sir->sbc_rreg.cbsr & SBC_CBSR_SEL) && (sir->sbc_rreg.cbsr & SBC_CBSR_IO) && (sir->sbc_rreg.cdr & SI_HOST_ID)) { if (scsi_debug || scsi_dis_debug) printf("siintr: REconnect\n"); si_reconnect(c); return; } /* * Scsi bus reset occurred. Put registers in correct * state. Must cleanup disconnected tasks which will * never reconnect due to the scsi bus reset. */ if (scsi_debug) printf("siintr: got scsi bus reset\n"); sir->csr = 0; DELAY(10); sir->csr = SI_CSR_INTR_EN|SI_CSR_SCSI_RES|SI_CSR_FIFO_RES; sir->sbc_wreg.mr = SBC_MR_EEI; c->c_flags &= ~SCSI_EN_RECON; if (c->c_disqh != NULL) { c->c_flags |= SCSI_FLUSH_DISQ; c->c_flush = c->c_disqt; } status = SE_FATAL; reset_occurred = 1; /* flush disconnect tasks now if possible */ if ((un == NULL) && c->c_disqh) { si_idle(c); return; } } done: /* cleanup after a dma operation */ if (dma_cleanup) { resid = sir->bcr; si_dma_cleanup(c); } /* pass interrupt info to unit */ if (un && un->un_wantint && (disconnect == 0)) { un->un_wantint = 0; if (status == SE_NO_ERROR) { if (si_getstatus(un, 0) == 0) { status = SE_RETRYABLE; } } else if (reset_occurred == 0) { si_reset(c); } (*un->un_ss->ss_intr)(c, resid, status); } if (disconnect) { /* start next I/O activity on controller */ if ((un->un_mc->mc_tab.b_actf) && (un->un_mc->mc_tab.b_active == 0)) { sistart(un); } else { /* enable reconnect attempts */ sir->sbc_wreg.ser = SI_HOST_ID; c->c_flags |= SCSI_EN_RECON; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_SEND; sir->csr |= SI_CSR_DMA_EN; } } } } /* * Handle target disconnecting. */ si_disconnect(c) register struct scsi_ctlr *c; { register struct scsi_unit *un = c->c_un; register struct mb_ctlr *mc = un->un_mc; register struct buf *dp; register struct scsi_si_reg *sir = c->c_sir; register u_char msg; /* get disconnect message(s) */ msg = si_getdata(c, PHASE_MSG_IN); if (msg == SC_SAVE_DATA_PTR) { msg = si_getdata(c, PHASE_MSG_IN); } if (msg != SC_DISCONNECT) { return (0); } /* save dma info for reconnect */ if (un->un_dma_curdir != SI_NO_DATA) { /* debug information */ if (c->c_flags & SCSI_ONBOARD) { if (scsi_dis_debug) { sir->udc_raddr = UDC_ADR_COUNT; printf("\tdiscon: udc_cnt %x\n", sir->udc_rdata); } if ((un->un_dma_curaddr & 0xffff) != c->c_udct.laddr) { printf("discon: caddr= %x, udc= %x\n", un->un_dma_curaddr, c->c_udct.laddr); } } if (scsi_dis_debug) { printf("\taddr= %x, cnt= %x, bcr= %x, sr= %x, baddr= %x\n", un->un_dma_curaddr, un->un_dma_curcnt, sir->bcr, sir->csr, un->un_baddr); } /* * Save dma information so dma can be restarted when * a reconnect occurs. */ un->un_dma_curaddr += un->un_dma_curcnt - sir->bcr; un->un_dma_curcnt = sir->bcr; } /* * Remove this disconnected task from the ctlr ready queue and save * on disconnect queue until a reconnect is done. * Advance controller queue. Remove mainbus resource alloc info. */ dp = mc->mc_tab.b_actf; mc->mc_tab.b_active = 0; mc->mc_tab.b_actf = dp->b_forw; mc->mc_mbinfo = 0; if (c->c_disqh == NULL) c->c_disqh = dp; else c->c_disqt->b_forw = dp; dp->b_forw = NULL; c->c_disqt = dp; c->c_un = NULL; return (1); } /* * Complete reselection phase and reconnect to target. * * NOTE: this routine cannot use si_getdata() to get identify msg * from reconnecting target due to sun3/50 scsi interface. The bcr * must be setup before the target changes scsi bus to data phase * if the command being reconnected involves dma (which we do not * know until we get the identify msg). Thus we cannot acknowledge * the identify msg until some setup of the host adaptor registers * is done. */ si_reconnect(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register struct buf *dp; register struct buf *pdp; register struct scsi_unit *un; register int i; register u_char msg; register u_char lun; register u_char cdr; /* get reselecting target scsi id */ cdr = sir->sbc_rreg.cdr & ~SI_HOST_ID; /* make sure there are only 2 scsi id's set */ for (i=0; i < 8; i++) { if (cdr & (1<<i)) break; } cdr &= ~(1<<i); if (cdr != 0) { printf("si_recon: > 2 scsi ids\n"); return; } /* acknowledge reselection */ sir->sbc_wreg.icr = SBC_ICR_BUSY; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_SEL, 0) == 0) { printf("si_recon: target never rel SEL\n"); si_reset(c); return; } sir->sbc_wreg.icr = 0; /* setup for getting identify message from reconnecting target */ sir->sbc_wreg.tcr = TCR_MSG_IN; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1) == 0) { printf("si_recon: REQ not active\n"); si_reset(c); return; } if ((sir->sbc_rreg.bsr & SBC_BSR_PMTCH) == 0) { printf("si_recon: phase mismatch\n"); si_reset(c); return; } /* grab identify message */ msg = sir->sbc_rreg.cdr; sir->sbc_wreg.tcr = 0; if ((msg != SC_IDENTIFY) && (msg != SC_DR_IDENTIFY)) { printf("si: recon, not id msg\n"); si_reset(c); return; } lun = msg & 0x07; /* search disconnect q for reconnecting task */ for (dp = c->c_disqh, pdp = NULL; dp; pdp = dp, dp = dp->b_forw) { un = (struct scsi_unit *)dp->b_un.b_addr; if ((un->un_target == i) && (un->un_lun == lun)) break; } if (dp == NULL) { printf("si: recon, never found dis unit\n"); si_reset(c); return; } /* make sure there is no active I/O */ if (un->un_mc->mc_tab.b_actf != NULL) { printf("si: recon, other I/O active\n"); si_reset(c); return; } /* disable other reconnection attempts */ sir->sbc_wreg.ser = 0; c->c_flags &= ~SCSI_EN_RECON; /* remove entity from disconnect q */ if (dp == c->c_disqh) c->c_disqh = dp->b_forw; else pdp->b_forw = dp->b_forw; if (dp == c->c_disqt) c->c_disqt = pdp; dp->b_forw = NULL; /* requeue on controller queue */ siustart(un); un->un_mc->mc_tab.b_active = 1; c->c_un = un; /* restart disconnect activity */ if (un->un_dma_curdir != SI_NO_DATA) { /* restore mainbus resource allocation info */ un->un_mc->mc_mbinfo = un->un_baddr; /* do initial dma setup */ if (un->un_dma_curdir == SI_RECV_DATA) sir->csr &= ~SI_CSR_SEND; else sir->csr |= SI_CSR_SEND; sir->csr &= ~SI_CSR_FIFO_RES; sir->csr |= SI_CSR_FIFO_RES; sir->bcr = un->un_dma_curcnt; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->bcrh = 0; } if (scsi_dis_debug) { printf("\taddr= %x, cnt= %x, bcr= %x, sr= %x, baddr= %x\n", un->un_dma_curaddr, un->un_dma_curcnt, sir->bcr, sir->csr, un->un_baddr); } } /* we can finally acknowledge identify message */ sir->sbc_wreg.icr = SBC_ICR_ACK; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 0) == 0) { printf("si_recon: REQ not INactive\n"); si_reset(c); return; } sir->sbc_wreg.icr = 0; /* may get restore pointers message */ if ((sir->sbc_rreg.cbsr & SBC_CBSR_REQ) && ((sir->sbc_rreg.cbsr & CBSR_PHASE_BITS) == PHASE_MSG_IN)) { msg = si_getdata(c, PHASE_MSG_IN); } /* do final setup for dma operation and start dma */ if (un->un_dma_curdir != SI_NO_DATA) { if (c->c_flags & SCSI_ONBOARD) si_ob_dma_setup(c, un); else si_vme_dma_setup(c, un); } else if (un->un_wantint) { sir->sbc_wreg.mr |= SBC_MR_DMA; sir->sbc_wreg.tcr = TCR_UNSPECIFIED; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_SEND; sir->csr |= SI_CSR_DMA_EN; } } } /* * No current activity for the scsi bus. May need to flush some * disconnected tasks if a scsi bus reset occurred before the * target reconnected, since a scsi bus reset causes targets to * "forget" about any disconnected activity. * Also, enable reconnect attempts. */ si_idle(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register struct buf *dp; register struct scsi_unit *un; register int i; register int resid; if (c->c_flags & SCSI_FLUSHING) { if (scsi_reset_debug) printf("si_idle: flushing, flags %x\n", c->c_flags); return; } /* flush disconnect tasks if a reconnect will never occur */ if (c->c_flags & SCSI_FLUSH_DISQ) { if (scsi_reset_debug) { printf("si_idle: flush: flags= %x, disqh= %x, disqt= %x\n", c->c_flags, c->c_disqh, c->c_disqt); } /* now in process of flushing tasks */ c->c_flags &= ~SCSI_FLUSH_DISQ; c->c_flags |= SCSI_FLUSHING; for (dp = c->c_disqh; dp && c->c_flush; dp = c->c_disqh) { /* keep track of last task to flush */ if (c->c_flush == c->c_disqh) c->c_flush = NULL; /* remove tasks from disconnect q */ un = (struct scsi_unit *)dp->b_un.b_addr; c->c_disqh = dp->b_forw; dp->b_forw = NULL; /* requeue on controller q */ siustart(un); un->un_mc->mc_tab.b_active = 1; c->c_un = un; /* inform device routines of error */ if (un->un_dma_curdir != SI_NO_DATA) { un->un_mc->mc_mbinfo = un->un_baddr; resid = un->un_dma_curcnt; } else { resid = 0; } (*un->un_ss->ss_intr)(c, resid, SE_FATAL); } if (c->c_disqh == NULL) { c->c_disqt = NULL; } c->c_flags &= ~SCSI_FLUSHING; } /* enable reconnect attempts */ sir->sbc_wreg.ser = SI_HOST_ID; c->c_flags |= SCSI_EN_RECON; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_SEND; sir->csr |= SI_CSR_DMA_EN; } } /* * Get status bytes from scsi bus. */ si_getstatus(un, recurse) register struct scsi_unit *un; { register struct scsi_ctlr *c = un->un_c; register u_char *cp = (u_char *)&c->c_scb; struct scsi_cdb save_cdb; struct scsi_scb save_scb; register int i; register int b; register int save_dma_addr; register int save_dma_count; register int save_reg_dma_count; int resid; short retval = 1; int s; /* get all the status bytes */ for (i = 0;;) { b = si_getdata(c, PHASE_STATUS); if (b < 0) { break; } if (i < STATUS_LEN) { cp[i++] = b; } } /* get command complete message */ b = si_getdata(c, PHASE_MSG_IN); if (b != SC_COMMAND_COMPLETE) { if (scsi_debug) { printf("Invalid SCSI message: %x\n", b); printf("Status bytes (%d):", i); for (b = 0; b < i; b++) { printf(" %x", cp[b]); } printf("\n"); } return (0); } if (scsi_debug) { printf("si%d: si_getstatus: Got status (%d) ", SINUM(c), i); for (b = 0; b < i; b++) { printf(" %x", cp[b]); } printf("\n"); } if (c->c_scb.busy) { return (0); } /* check for sense data */ if (c->c_scb.chk) { if (recurse) { printf("scsi: chk on sense: invalid\n"); return (0); } /* save information while we get sense */ save_cdb = c->c_cdb; save_scb = c->c_scb; save_dma_addr = un->un_dma_addr; save_dma_count = un->un_dma_count; save_reg_dma_count = c->c_sir->bcr; /* set up for getting sense */ c->c_cdb.cmd = SC_REQUEST_SENSE; c->c_cdb.lun = un->un_lun; cdbaddr(&c->c_cdb, 0); c->c_cdb.count = sizeof(struct scsi_sense); un->un_dma_addr = (int)c->c_sense - (int)DVMA; un->un_dma_count = sizeof(struct scsi_sense); /* get sense */ if ((s=0),si_cmd(c, un, 0) == 0 || (s=1),si_cmdwait(c) == 0 || (s=2),si_getstatus(un, 1) == 0) { printf("scsi: cannot get sense %d\n", s); si_off(un); retval = 0; } /* restore pre sense information */ c->c_cdb = save_cdb; c->c_scb = save_scb; un->un_dma_addr = save_dma_addr; un->un_dma_count = save_dma_count; c->c_sir->bcr = save_reg_dma_count; } return (retval); } /* * Wait for a scsi dma request to complete. * Disconnects were disabled in si_cmd() when polling for command completion. */ si_cmdwait(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register struct scsi_unit *un = c->c_un; register u_char junk; /* wait for dma transfer to complete */ if (si_wait((u_short *)&sir->csr, SI_CSR_DMA_ACTIVE, 0) == 0) { printf("si_cmdwait: DMA_ACTIVE still on\n"); si_reset(c); return (0); } /* if command does not involve dma activity, then we are finished */ if (un->un_dma_curdir == SI_NO_DATA) { return (1); } /* wait for indication of dma completion */ if (si_wait((u_short *)&sir->csr, SI_CSR_SBC_IP|SI_CSR_DMA_IP|SI_CSR_DMA_CONFLICT, 1) == 0) { printf("si_cmdwait: dma op never completed\n"); si_reset(c); return (0); } /* * For vme host adaptor interface, must disable dma before * accessing any registers other than the csr or a dma * conflict error will occur. */ if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->csr &= ~SI_CSR_DMA_EN; } /* make sure dma completely complete */ if ((sir->csr & SI_CSR_SBC_IP) == 0) { if (sir->csr & SI_CSR_DMA_BUS_ERR) { printf("si_cmdwait: bus error during dma\n"); } else if (sir->csr & SI_CSR_DMA_CONFLICT) { printf("si_cmdwait: invalid reg access during dma\n"); } else { if (c->c_flags & SCSI_ONBOARD) printf("si_cmdwait: dma ip, unknown reason\n"); else printf("si_cmdwait: dma overrun\n"); } si_reset(c); return (0); } /* handle special dma recv situations */ if (un->un_dma_curdir == SI_RECV_DATA) { if (si_dma_recv(c) == 0) { si_reset(c); return (0); } } /* ack sbc interrupt and cleanup */ junk = sir->sbc_rreg.clr; si_dma_cleanup(c); return (1); } /* * Wait for a condition to be (de)asserted on the scsi bus. */ si_sbc_wait(reg, cond, set) register caddr_t reg; register u_char cond; register int set; { register int i; register u_char regval; for (i = 0; i < SI_WAIT_COUNT; i++) { regval = *reg; if ((set == 1) && (regval & cond)) { return (1); } if ((set == 0) && !(regval & cond)) { return (1); } DELAY(10); } return (0); } /* * Wait for a condition to be (de)asserted. */ si_wait(reg, cond, set) register u_short *reg; register u_short cond; register int set; { register int i; register u_short regval; for (i = 0; i < SI_WAIT_COUNT; i++) { regval = *reg; if ((set == 1) && (regval & cond)) { return (1); } if ((set == 0) && !(regval & cond)) { return (1); } DELAY(10); } return (0); } /* * Put data onto the scsi bus. */ si_putdata(c, phase, data, num) register struct scsi_ctlr *c; register u_short phase; register u_char *data; register int num; { register struct scsi_si_reg *sir = c->c_sir; register int i; /* set up tcr so a phase match will occur */ if (phase == PHASE_COMMAND) { sir->sbc_wreg.tcr = TCR_COMMAND; } else if (phase == PHASE_MSG_OUT) { sir->sbc_wreg.tcr = TCR_MSG_OUT; } else { if (scsi_debug) printf("si_putdata %d phase not supported\n", phase); return (0); } /* put all desired bytes onto scsi bus */ for (i = 0; i < num; i++ ) { /* wait for target to request a byte */ if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1) == 0) { if (scsi_debug) printf("si: putdata, REQ not active\n"); si_reset(c); return (0); } /* make sure phase match occurred */ if ((sir->sbc_rreg.bsr & SBC_BSR_PMTCH) == 0) { if (phase != PHASE_MSG_OUT) { if (scsi_debug) printf("si: putdata, phase mismatch\n"); si_reset(c); } else { sir->sbc_wreg.tcr = 0; } return (0); } /* load data */ sir->sbc_wreg.odr = *data++; sir->sbc_wreg.icr = SBC_ICR_DATA; /* complete req/ack handshake */ sir->sbc_wreg.icr |= SBC_ICR_ACK; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 0) == 0) { if (scsi_debug) printf("si: putdata, req not INactive\n"); si_reset(c); return (0); } sir->sbc_wreg.icr = 0; } sir->sbc_wreg.tcr = 0; return (1); } /* * Get data from the scsi bus. */ si_getdata(c, phase) register struct scsi_ctlr *c; register u_short phase; { register struct scsi_si_reg *sir = c->c_sir; register u_char data; /* check for valid phase */ if (phase == PHASE_STATUS) { sir->sbc_wreg.tcr = TCR_STATUS; } else if (phase == PHASE_MSG_IN) { sir->sbc_wreg.tcr = TCR_MSG_IN; } else { if (scsi_debug) printf("si: getdata %d phase not supported\n", phase); return (-1); } /* wait for target request */ if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1) == 0) { if (scsi_debug) { printf("si: getdata, REQ not active, cbsr %x\n", sir->sbc_rreg.cbsr); } sir->sbc_wreg.tcr = 0; return (-1); } /* check for correct phase on scsi bus */ if ((sir->sbc_rreg.bsr & SBC_BSR_PMTCH) == 0) { if (phase != PHASE_STATUS) { if (scsi_debug) { printf("si: getdata, bad phase\n"); } si_reset(c); } else { sir->sbc_wreg.tcr = 0; } return (-1); } /* grab data and complete req/ack handshake */ data = sir->sbc_rreg.cdr; sir->sbc_wreg.icr = SBC_ICR_ACK; if (si_sbc_wait((caddr_t)&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 0) == 0) { if (scsi_debug) printf("si: getdata, REQ not inactive\n"); si_reset(c); return (-1); } sir->sbc_wreg.icr = 0; sir->sbc_wreg.tcr = 0; return (data); } /* * Reset SCSI control logic and bus. */ si_reset(c) register struct scsi_ctlr *c; { register struct scsi_si_reg *sir = c->c_sir; register u_char junk; if (scsi_debug) { printf("scsi reset, sr= %x, bcr= %x\n", sir->csr, sir->bcr); } /* reset scsi control logic */ sir->bcr = 0; sir->csr = 0; DELAY(10); sir->csr = SI_CSR_SCSI_RES|SI_CSR_FIFO_RES; if ((c->c_flags & SCSI_ONBOARD) == 0) { sir->dma_addr = 0; sir->dma_count = 0; } /* issue scsi bus reset (make sure interrupts from sbc are disabled) */ sir->sbc_wreg.icr = SBC_ICR_RST; DELAY(10); sir->sbc_wreg.icr = 0; junk = sir->sbc_rreg.clr; /* enable sbc interrupts */ sir->csr |= SI_CSR_INTR_EN; sir->sbc_wreg.mr = SBC_MR_EEI; /* reconnection attempts are no longer enabled */ c->c_flags &= ~SCSI_EN_RECON; /* disconnect queue needs to be flushed */ if (c->c_disqh != NULL) { c->c_flags |= SCSI_FLUSH_DISQ; c->c_flush = c->c_disqt; } } /* * Return residual count for a dma. */ si_dmacnt(c) register struct scsi_ctlr *c; { if (c->c_flags & SCSI_ONBOARD) { return (c->c_sir->bcr); } else { return ( ((c->c_sir->bcrh) << 16) | (c->c_sir->bcr) ); } } #endif NSI > 0