|
|
researchv9-SUN3(old)
#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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.