Files
Arquivotheca.SunOS-4.1.4/sys/sundev/se.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

2582 lines
63 KiB
C

/* @(#)se.c 1.1 94/10/31 Copyright (c) 1988 by Sun Microsystems, Inc. */
#include "se.h"
#if NSE > 0
#ifndef lint
static char sccsid[] = "@(#)se.c 1.1 94/10/31 Copyr 1988 Sun Micro";
#endif lint
/*#define SCSI_DEBUG /* Turn on debugging code */
#define REL4 /* Enable release 4.00 mods */
/*
* Generic scsi routines.
*/
#ifndef REL4
#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/sereg.h"
#include "../sundev/scsi.h"
#else REL4
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dk.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <sys/map.h>
#include <sys/vmmac.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/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/sereg.h>
#include <sundev/scsi.h>
#endif REL4
/* Shorthand, to make the code look a bit cleaner. */
#define SENUM(se) (se - se_ctlrs)
#define SEUNIT(dev) (minor(dev) & 0x03)
int se_watch();
int se_probe(), se_slave(), se_attach(), se_go(), se_done(), se_poll();
int se_ustart(), se_start(), se_getstatus(), se_reconnect(), se_deque();
int se_off(), se_cmd(), se_cmdwait(), se_reset(), se_dmacnt();
char * se_str_phase();
extern int nse, scsi_host_id, scsi_reset_delay;
extern u_char sc_cdb_size[];
extern struct scsi_ctlr se_ctlrs[]; /* per controller structs */
extern struct mb_ctlr *se_info[];
extern struct mb_device *sdinfo[];
struct mb_driver sedriver = {
se_probe, se_slave, se_attach, se_go, se_done, se_poll,
sizeof (struct scsi_si_reg), "sd", sdinfo, "se", se_info, MDR_BIODMA,
};
/* routines available to devices specific portion of scsi driver */
struct scsi_ctlr_subr se_subr = {
se_ustart, se_start, se_done, se_cmd, se_getstatus, se_cmdwait,
se_off, se_reset, se_dmacnt, se_go, se_deque,
};
extern int scsi_ntype;
static int scsi_disconnect_enable = 0; /* DISABLE DISCONNECTS */
/* extern int scsi_disre_enable; /* global disconnect control */
extern struct scsi_unit_subr scsi_unit_subr[];
/*
* Software copy of ser state (debug only)
*/
static u_char ser_state = 0;
static u_char junk;
/*
* Patchable delays for debugging.
*/
int se_arbitration_delay = SE_ARBITRATION_DELAY;
int se_bus_clear_delay = SE_BUS_CLEAR_DELAY;
#ifdef notdef
int se_bus_free_delay = SE_BUS_SETTLE_DELAY;
#endif notdef
int se_bus_settle_delay = SE_BUS_SETTLE_DELAY;
/*
* possible return values from se_arb_sel, se_cmd,
* se_putdata, se_putcmd, and se_reconnect.
*/
#define OK 0 /* successful */
#define FAIL 1 /* failed maybe recoverable */
#define HARD_FAIL 2 /* failed not recoverable */
#define SCSI_FAIL 3 /* failed due to scsi bus fault */
#define RESEL_FAIL 4 /* failed due to target reselection */
/* possible return values from se_process_complete_msg() */
#define CMD_CMPLT_DONE 0 /* cmd processing done */
#define CMD_CMPLT_WAIT 1 /* cmd processing waiting
* on sense cmd complete
*/
/*
* possible argument values for se_reset
*/
#define NO_MSG 0 /* don't print reset warning message */
#define PRINT_MSG 1 /* print reset warning message */
/*
* additional messages
*/
#define SEL_FLG 0
#define RESEL_FLG 1
#define SC_EXTNDD_MSSG 0x01 /* extended message */
#define SC_MSSG_REJ 0x07 /* message reject */
#define SC_LNKD_CMD_CMPLT 0x0A /* linked command complete */
#define SC_FLG_LNKD_CMD_CMPLT 0x0B /* linked command complete(with flag) */
#ifdef SCSI_DEBUG
extern int scsi_debug; /* 0 = normal operaion
* 1 = enable error logging and error msgs.
* 2 = as above + debug msgs.
* 3 = enable all info msgs.
*/
u_int seintr_winner = 0; /* # of times we had an intr at end of siintr */
u_int seintr_loser = 0; /* # of times we didn't have an intr at end */
#define SE_WIN seintr_winner++
#define SE_LOSE seintr_loser++
/* Check for possible illegal SCSI-3 register access. */
#define SE_VME_OK(c, ser, str) {\
if ((IS_VME(c)) && (ser->csr & SE_CSR_DMA_EN)) \
printf("se%d: reg access during dma <%s>, csr 0x%x\n", \
SENUM(c), str, ser);\
}
#define SE_DMA_OK(c, ser, str) {\
if (IS_VME(c)) { \
if (ser->csr & SE_CSR_DMA_EN) \
printf("%s: DMA DISABLED\n", str); \
if (ser->csr & SE_CSR_DMA_CONFLICT) { \
printf("%s: invalid reg access during dma\n", str); \
DELAY(50000000); \
} \
ser->csr &= ~SE_CSR_DMA_EN; \
} \
}
#define SCSI_TRACE(where, ser, un) \
if (scsi_debug) scsi_trace(where, ser, un)
#define SCSI_RECON_TRACE(where, c, data0, data1, data2) \
if (scsi_debug) scsi_recon_trace(where, c, data0, data1, data2)
#define DEBUG_DELAY(cnt) \
if (scsi_debug) DELAY(cnt)
/* Handy debugging 0, 1, and 2 argument printfs */
#define DPRINTF(str) \
if (scsi_debug > 1) printf(str)
#define DPRINTF1(str, arg1) \
if (scsi_debug > 1) printf(str,arg1)
#define DPRINTF2(str, arg1, arg2) \
if (scsi_debug > 1) printf(str,arg1,arg2)
/* Handy error reporting 0, 1, and 2 argument printfs */
#define EPRINTF(str) \
if (scsi_debug) printf(str)
#define EPRINTF1(str, arg1) \
if (scsi_debug) printf(str,arg1)
#define EPRINTF2(str, arg1, arg2) \
if (scsi_debug) printf(str,arg1,arg2)
#else SCSI_DEBUG
#define SE_WIN
#define SE_LOSE
#define SE_VME_OK(c, ser, str)
#define SE_DMA_OK(c, ser, str)
#define DEBUG_DELAY(cnt)
#define DPRINTF(str)
#define DPRINTF1(str, arg2)
#define DPRINTF2(str, arg1, arg2)
#define EPRINTF(str)
#define EPRINTF1(str, arg2)
#define EPRINTF2(str, arg1, arg2)
#define SCSI_TRACE(where, ser, un)
#define SCSI_RECON_TRACE(where, c, data0, data1, data2)
#endif SCSI_DEBUG
/*
* trace buffer stuff.
*/
#ifdef SCSI_DEBUG
#define TRBUF_LEN 64
struct trace_buf {
u_char wh[2]; /* where in code */
u_char r[6]; /* 5380 registers */
u_short csr;
u_short bcr;
u_int dma_addr;
u_int dma_count;
};
struct disre_trace_buf {
u_char wh[2];
struct scsi_cdb cdb;
u_short target;
u_short lun;
u_int data[3];
u_int dma_count;
};
/* trace buffer */
struct trace_buf se_scsi_trace_buf[TRBUF_LEN];
int se_stbi = 0;
/* disconnect/reconnect trace buffer */
struct disre_trace_buf se_scsi_recon_trace_buf[TRBUF_LEN];
int se_strbi = 0;
static
scsi_trace(where, ser, un)
register char where;
register struct scsi_si_reg *ser;
struct scsi_unit *un;
{
register u_char *r = &(SBC_RD.cdr);
register u_int i;
register struct trace_buf *tb = &(se_scsi_trace_buf[se_stbi]);
#ifdef lint
un= un;
#endif lint
tb->wh[0] = tb->wh[1] = where;
for (i = 0; i < 6; i++)
tb->r[i] = *r++;
tb->csr = ser->csr;
tb->bcr = 0;
tb->dma_addr = ser->dma_addr;
tb->dma_count = ser->dma_cntr;
if (++se_stbi >= TRBUF_LEN)
se_stbi = 0;
se_scsi_trace_buf[se_stbi].wh[0] = '?'; /* mark end */
}
static
scsi_recon_trace(where, c, data0, data1, data2)
char where;
register struct scsi_ctlr *c;
register int data0, data1, data2;
{
register struct scsi_unit *un = c->c_un;
register struct disre_trace_buf *tb = &(se_scsi_recon_trace_buf[se_strbi]);
tb->wh[0] = tb->wh[1] = where;
tb->cdb = un->un_cdb;
tb->target = un->un_target;
tb->lun = un->un_lun;
tb->data[0] = data0;
tb->data[1] = data1;
tb->data[2] = data2;
if (++se_strbi >= TRBUF_LEN)
se_strbi = 0;
se_scsi_recon_trace_buf[se_strbi].wh[0] = '?'; /* mark end */
}
#endif SCSI_DEBUG
/*
* This routine unlocks this driver when I/O
* has ceased, and a call to se_start is
* needed to start I/O up again. Refer to
* xd.c and xy.c for similar routines upon
* which this routine is based
*/
static
se_watch(c)
register struct scsi_ctlr *c;
{
register struct scsi_unit *un;
un = c->c_un;
/*
* Find a valid un to call s?start with to for next command
* to be queued in case DVMA ran out. Try the pending que
* then the disconnect que. Otherwise, driver will hang
* if DVMA runs out. The reason for this kludge is that
* sestart should really be passed c instead of un, but it
* would break lots of 3rd party S/W if this were fixed.
*/
if (un == NULL) {
un = (struct scsi_unit *)c->c_tab.b_actf;
if (un == NULL)
un = (struct scsi_unit *)c->c_disqtab.b_actf;
}
se_start(un);
timeout(se_watch, (caddr_t)c, 10*hz);
}
/*
* Determine existence of SCSI host adapter.
*/
se_probe(ser, ctlr)
register struct scsi_si_reg *ser;
register int ctlr;
{
register struct scsi_ctlr *c;
c = &se_ctlrs[ctlr];
/*
* Check for sbc - NCR 5380 Scsi Bus Ctlr chip.
* sbc is common * to sun3/50 onboard scsi and vme
* scsi board.
*/
if (peekc((char *)&ser->sbc_rreg.cbsr) == -1) {
return (0);
}
/*
* Determine whether the host adaptor interface is onboard or vme.
*/
/* probe for sun3/50 dma interface */
if (cpu == CPU_SUN3_50) {
return (0);
} 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((short *)&ser->dma_addr) == -1) {
return (0);
}
if (peek((short *)&ser->csr) == -1) {
return (0);
}
ser->csr = 0x0000;
if(peek((short *)&ser->csr) != 0x0402) {
return (0);
}
c->c_flags = 0;
}
/* probe for different scsi host adaptor interfaces */
EPRINTF2("se%d: seprobe: ser= 0x%x, ", ctlr, (u_int)ser);
EPRINTF1("c= 0x%x\n", (u_int)c );
/* init controller information */
if (scsi_disconnect_enable) {
/* DPRINTF("seprobe: disconnects enabled\n"); */
c->c_flags |= SCSI_EN_DISCON;
} else {
EPRINTF("seprobe: all disconnects disabled\n");
}
/* init controller information */
c->c_flags |= SCSI_PRESENT;
c->c_reg = (int)ser;
c->c_ss = &se_subr;
c->c_name = "se";
c->c_tab.b_actf = NULL;
c->c_tab.b_active = 0;
se_reset(c, NO_MSG); /* quietly reset the scsi bus */
c->c_un = NULL;
timeout(se_watch, (caddr_t)c, 10*hz);
#ifdef lint
se_intr(&se_ctlrs[nse - 1]);
#endif lint
return (sizeof (struct scsi_si_reg));
}
/*
* See if a slave exists.
* Since it may exist but be powered off, we always say yes.
*/
/*ARGSUSED*/
se_slave(md, ser)
register struct mb_device *md;
register struct scsi_si_reg *ser;
{
register struct scsi_unit *un;
register int type;
#ifdef lint
ser = ser;
#endif lint
/*
* 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("se_slave: 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("se_slave: md_flags scsi type not configured in\n");
}
un->un_c = &se_ctlrs[md->md_ctlr];
md->md_driver->mdr_dname = scsi_unit_subr[type].ss_devname;
return (1);
}
/*
* Attach device (boot time).
*/
se_attach(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 = &se_ctlrs[md->md_ctlr];
register struct scsi_si_reg *ser;
ser = (struct scsi_si_reg *)c->c_reg;
/* DPRINTF("se_attach:\n");*/
#ifdef SCSI_DEBUG
if (! scsi_disconnect_enable && scsi_debug)
printf("se%d: seattach: disconnects disabled\n", SENUM(c));
#endif SCSI_DEBUG
if (type >= scsi_ntype) {
panic("se_attach: unknown type in md_flags");
}
(*scsi_unit_subr[type].ss_attach)(md);
/* Initialize interrupt vector */
if (mc->mc_intr) {
/* setup for vectored interrupts - we will pass ctlr ptr */
ser->ivec2 = (mc->mc_intr->v_vec & 0xff);
(*mc->mc_intr->v_vptr) = (int)c;
}
}
/*
* corresponding to un is an md. if this md has SCSI commands
* queued up (md->md_utab.b_actf != NULL) and md is currently
* not active (md->md_utab.b_active == 0), this routine queues the
* un on its queue of devices (c->c_tab) for which to run commands
* Note that md is active if its un is already queued up on c->c_tab,
* or its un is on the scsi_ctlr's disconnect queue c->c_disqtab.
*/
se_ustart(un)
register struct scsi_unit *un;
{
register struct scsi_ctlr *c = un->un_c;
register struct mb_ctlr *mc = un->un_mc;
register struct mb_device *md = un->un_md;
int s;
DPRINTF("se_ustart:\n");
s = splr(pritospl(mc->mc_intpri));
if (md->md_utab.b_actf != NULL && md->md_utab.b_active == 0) {
if (c->c_tab.b_actf == NULL)
c->c_tab.b_actf = (struct buf *)un;
else
((struct scsi_unit *)(c->c_tab.b_actl))->un_forw = un;
un->un_forw = NULL;
c->c_tab.b_actl = (struct buf *)un;
md->md_utab.b_active = 1;
}
(void)splx(s);
}
/*
* this un is removed from the active
* position of the scsi_ctlr's queue of devices (c->c_tab.b_actf) and
* queued on scsi_ctlr's disconnect queue (c->c_disqtab).
*/
se_discon_queue (un)
struct scsi_unit *un;
{
register struct scsi_ctlr *c = un->un_c;
/* DPRINTF("se_discon_queue:\n"); */
/* a disconnect has occurred, so mb_ctlr is no longer active */
c->c_tab.b_active = 0;
c->c_un = NULL;
/* remove disconnected md from mb_ctlr's queue */
if((c->c_tab.b_actf = (struct buf *)un->un_forw) == NULL)
c->c_tab.b_actl = NULL;
/* put md on scsi_ctlr's disconnect queue */
if (c->c_disqtab.b_actf == NULL)
c->c_disqtab.b_actf = (struct buf *)un;
else
((struct scsi_unit *)(c->c_disqtab.b_actl))->un_forw = un;
c->c_disqtab.b_actl = (struct buf *)un;
un->un_forw = NULL;
}
/*
* searches the scsi_ctlr's disconnect queue for an md whose
* corresponding un is the scsi unit defined by (target,lun).
* then requeues this md on mc_ctlr's queue of devices.
*/
se_recon_queue (c, target, lun)
register struct scsi_ctlr *c;
register u_int target;
register u_int lun;
{
register struct scsi_unit *un;
register struct scsi_unit *pun;
register struct scsi_si_reg *ser;
ser = (struct scsi_si_reg *)c->c_reg;
#ifdef lint
ser = ser;
#endif lint
DPRINTF("se_recon_queue:\n");
/* search the disconnect queue */
for (un=(struct scsi_unit *)(c->c_disqtab.b_actf), pun=NULL; un;
pun=un, un=un->un_forw) {
DPRINTF("se_recon_queue: looking at next item on discon. q\n");
if ((un->un_target == target) &&
(un->un_lun == lun))
break;
}
DPRINTF("se_recon_queue: done searching disconnect queue\n");
/* could not find the device in the disconnect queue */
if (un == NULL) {
printf("se%d: se_reconnect: can't find dis unit: target %d lun %d\n",
SENUM(c), target, lun);
/* dump out disconnnect queue */
printf(" disconnect queue:\n");
for (un = (struct scsi_unit *)(c->c_disqtab.b_actf), pun = NULL; un;
pun = un, un = un->un_forw) {
printf("\tun = 0x%x --- target = %d, lun = %d\n",
un, un->un_target, un->un_lun);
}
/*
* If we got here, we're probably in deep yogurt.
* We should reset, but instead we'll pass the buck.
*/
return (FAIL);
}
/* remove md from the disconnect queue */
if (un == (struct scsi_unit *)(c->c_disqtab.b_actf))
c->c_disqtab.b_actf = (struct buf *)(un->un_forw);
else
pun->un_forw = un->un_forw;
if (un == (struct scsi_unit *)c->c_disqtab.b_actl)
c->c_disqtab.b_actl = (struct buf *)pun;
DPRINTF("se_recon_queue: done removing md from discon q\n");
/* requeue un at the active position of mb_ctlr's queue
* of devices.
*/
if (c->c_tab.b_actf == NULL) {
un->un_forw = NULL;
c->c_tab.b_actf = c->c_tab.b_actl = (struct buf *)un;
} else {
un->un_forw = (struct scsi_unit *)(c->c_tab.b_actf);
c->c_tab.b_actf = (struct buf *)un;
}
DPRINTF("se_recon_queue: done re-qing un\n");
/* scsi_ctlr now has an actively running SCSI command */
c->c_tab.b_active = 1;
/* indicate which unit is now running */
c->c_un = un;
DPRINTF("se_recon_queue: before returning\n");
return (OK);
}
/* starts the next SCSI command */
se_start (un)
struct scsi_unit *un;
{
struct scsi_ctlr *c;
struct mb_device *md;
struct buf *bp;
int s;
/* DPRINTF("se_start:\n"); */
/* return immediately if passed NULL un */
if (un == NULL)
return;
c=un->un_c;
/* return immediately, if the ctlr is already actively
* running a SCSI command
*/
s = splr(pritospl(3));
if (c->c_tab.b_active != 0) {
(void)splx(s);
return;
}
/* if there are currently no SCSI devices queued to run
* a command, then simply return. otherwise, obtain the
* next un for which a command should be run.
*/
if ((un=(struct scsi_unit *)(c->c_tab.b_actf)) == NULL) {
(void)splx(s);
return;
}
md = un->un_md;
c->c_tab.b_active = 1;
(void)splx(s);
/* at this point we have obtained the next
* device for which to run a SCSI command,
* and we have also obtained the buf
* corresponding to that device's next
* command.
*/
/* if an attempt was already made to run
* this command, but the attempt was
* pre-empted by a SCSI bus reselection
* then DVMA has already been set up, and
* we can call se_go directly
*/
if (un->un_flags & SC_UNF_PREEMPT) {
DPRINTF("se_start: starting pre-empted");
un->un_c->c_un = un;
se_go(md);
return;
}
/*
* put bp into the active position
*/
bp = md->md_utab.b_actf;
md->md_utab.b_forw = bp;
/* only happens when called by intr */
if (bp == NULL)
return;
/* special commands which are initiated by
* the high-level driver, are run using
* its special buffer, un->un_sbuf.
* in most cases, main bus set-up has
* already been done, so se_go can be called
* for on-line formatting, we need to
* call mbsetup.
*/
if ((*un->un_ss->ss_start)(bp, un)) {
un->un_c->c_un = un;
if (bp == &un->un_sbuf &&
((un->un_flags & SC_UNF_DVMA) == 0) &&
((un->un_flags & SC_UNF_SPECIAL_DVMA) == 0)) {
se_go(md);
return;
} else {
(void)mbugo(md);
return;
}
} else {
se_done(md);
}
}
/*
* Start up a scsi operation.
* Called via mbgo after buffer is in memory.
*/
se_go(md)
register struct mb_device *md; /* fire up this mb ctlr */
{
register struct mb_ctlr *mc = md->md_mc;
register struct scsi_ctlr *c = &se_ctlrs[mc->mc_ctlr];
register struct scsi_unit *un = c->c_un;
register struct buf *bp = md->md_utab.b_forw;
register int unit;
register int err;
/* DPRINTF("se_go:\n"); */
if (md == NULL || md->md_utab.b_forw == NULL) {
panic("se_go: queueing error1\n");
}
un->un_baddr = MBI_ADDR(md->md_mbinfo);
/*
* Diddle stats if necessary.
*/
if ((unit = un->un_md->md_dk) >= 0) {
dk_busy |= 1 << unit;
dk_xfer[unit]++;
if (bp->b_flags & B_READ)
dk_read[unit]++;
dk_wds[unit] += bp->b_bcount >> 6;
}
/*
* Make the command block and fire it up in interrupt mode.
* If it fails right off the bat, call the interrupt routine
* to handle the failure.
*/
if (un->un_flags & SC_UNF_PREEMPT)
un->un_flags &= ~SC_UNF_PREEMPT;
else
(*un->un_ss->ss_mkcdb)(un);
if ((err=se_cmd(c, un, 1)) != OK) {
/* note that in the non-polled case (here) that
* SCSI_FAIL is not a possible return value from
* in se_cmd.
*/
if (err == FAIL) {
/* DPRINTF("se_go: se_cmd FAILED\n"); */
(*un->un_ss->ss_intr)(c, 0, SE_RETRYABLE);
} else if (err == HARD_FAIL) {
EPRINTF("se_go: se_cmd hard FAIL\n");
(*un->un_ss->ss_intr)(c, 0, SE_FATAL);
se_off(c, un);
}
}
}
/*
* Handle a polling SCSI bus interrupt. Used
* for multiple board support.
*/
se_poll()
{
register struct scsi_ctlr *c;
register struct scsi_si_reg *ser;
register int serviced = 0;
/* DPRINTF("se_poll:\n"); */
for (c = se_ctlrs; c < &se_ctlrs[nse]; c++) {
if ((c->c_flags & SCSI_PRESENT) == 0)
continue;
ser = (struct scsi_si_reg *)c->c_reg;
/* si.c: in addition, check for CSR_DMA_IP or
DMA_CONFLICT condition */
if ((ser->csr & (SE_CSR_SBC_IP)) == 0) {
continue;
}
serviced = 1;
se_intr(c);
}
return(serviced);
}
/* the SCSI command is done, so start up the next command
*/
se_done(md)
struct mb_device *md;
{
struct mb_ctlr *mc = md->md_mc;
struct scsi_ctlr *c = &se_ctlrs[mc->mc_ctlr];
struct scsi_unit *un;
struct buf *bp = md->md_utab.b_forw;
int type;
/* DPRINTF("se_done:\n"); */
/* more reliable than un = c->c_un; */
type = TYPE(md->md_flags);
un = (struct scsi_unit *)
(*scsi_unit_subr[type].ss_unit_ptr)(md);
/* advance mb_device queue */
md->md_utab.b_actf = bp->av_forw;
/* we are done, so clear buf in active position of
* md's queue. then call iodone to complete i/o
*/
md->md_utab.b_forw = NULL;
/* just got done with i/o so mark ctlr inactive
* then advance to next md on the ctlr.
*/
c->c_tab.b_active = 0;
c->c_tab.b_actf = (struct buf *)(un->un_forw);
/* advancing the ctlr's queue has removed md from
* the queue. if there are any more i/o for this
* md, se_ustart will queue up md again. at tail.
* first, need to mark md as inactive (not on queue)
*/
md->md_utab.b_active = 0;
se_ustart(un);
iodone(bp);
/* check if the ctlr's queue is NULL, and if so,
* idle the controller
*/
if (c->c_tab.b_actf == NULL) {
se_idle(c);
return;
}
/* old se.c: check if SC_UNF_GET_SENSE set, call se_go() instead */
/* start up the next command on the scsi_ctlr */
se_start(un);
}
/*
* Bring a unit offline.
*/
/*ARGSUSED*/
se_off(c, un)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
{
#ifdef notdef
register struct mb_device *md;
md = un->un_md;
#endif notdef
#ifdef lint
c = c;
#endif lint
/*
* Be warned, if you take the root device offline,
* the system is going to have a heartattack !
* Note, if unit is already offline, don't bother
* to print error message.
*/
if (un->un_present) {
#ifdef SCSI_DEBUG
if (scsi_debug) printf("se%d: %s%d, unit offline\n", SENUM(c),
scsi_unit_subr[md->md_flags].ss_devname,
SEUNIT(un->un_unit));
#endif SCSI_DEBUG
un->un_present = 0;
}
#ifdef notdef
/*
* This really doesn't work. It would be a nice idea to
* fix it sometime.
*/
if (md->md_dk > 0) {
dk_mspw[md->md_dk]=0;
}
#endif notdef
}
/*
* Pass a command to the SCSI bus.
* OK if fully successful,
* Return FAIL on failure (may be retryable),
* SCSI_FAIL if we failed due to timing problem with SCSI bus. (terminal)
* RESEL_FAIL if we failed due to target being in process of reselecting us.
* (posponed til after reconnect done)
*/
se_cmd(c, un, intr)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
register int intr; /* if 0, run cmd to completion
* in polled mode
* if 1, allow disconnects
* if enabled and use
* interrupts
*/
{
register struct scsi_si_reg *ser;
register int err;
register u_char size;
register int i;
u_char id;
u_char msg;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_cmd:\n");
/* disallow disconnects if waiting for command completion */
if (intr == 0) {
c->c_flags &= ~SCSI_EN_DISCON;
} else {
/*
* If disconnect/reconnect globally disabled or only
* disabled for this command set internal flag.
* Otherwise, we enable disconnects and reconnects.
*/
if (!scsi_disconnect_enable || (un->un_flags & SC_UNF_NO_DISCON))
c->c_flags &= ~SCSI_EN_DISCON;
else
c->c_flags |= SCSI_EN_DISCON;
un->un_wantint = 1;
}
/* disable Reconnect */
/* SBC_WR.ser = ser_state = 0; */
/* si.c: allow Reconnect, check odd-byte boundary,
and set time-critical */
un->un_flags &= ~SC_UNF_DMA_INITIALIZED;
/* performing target selection */
if ((err = se_arb_sel(c, ser, un)) != OK) {
/*
* May not be able to execute this command at this time due
* to a target reselecting us. Indicate this in the unit
* structure for when we perform this command later.
*/
/* si.c: does not set SBC_WR.ser */
SBC_WR.ser = ser_state = scsi_host_id;
if (err == RESEL_FAIL) {
DPRINTF1("se_cmd: preempted, tgt= %x\n",
un->un_target);
un->un_flags |= SC_UNF_PREEMPT;
err = OK; /* not an error */
/* si.c: check lost reselect INT */
}
un->un_wantint = 0;
return (err);
}
/* si.c: clear resel and phase INTs and re-enable time-INT */
/*
* We need to send out an identify message to target.
*/
if (c->c_flags & SCSI_EN_DISCON) {
/* Tell target we do disconnects */
/* DPRINTF("se_cmd: disconnects ENABLED\n"); */
id = SC_DR_IDENTIFY | un->un_cdb.lun;
SBC_WR.tcr = TCR_MSG_OUT;
if (se_wait_phase(ser, PHASE_MSG_OUT) == OK) {
if (se_putdata(c, PHASE_MSG_OUT, &id, 1) != OK) {
EPRINTF1("se%d: se_cmd: id msg failed\n",
SENUM(c));
se_print_state(ser, c);
se_reset(c, PRINT_MSG);
return (FAIL);
}
}
} else {
/*
* Play dumb! Since we didn't raise ATN, target
* thinks we don't do disconnects.
*/
DPRINTF("se_cmd: NO disconnect\n");
}
/*
* 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) {
/* si.c: reset fifo */
if (un->un_flags & SC_UNF_RECV_DATA) {
un->un_dma_curdir = SE_RECV_DATA;
ser->csr &= ~SE_CSR_SEND;
} else {
un->un_dma_curdir = SE_SEND_DATA;
ser->csr |= SE_CSR_SEND;
}
/* save current dma info for disconnect */
un->un_dma_curaddr = un->un_dma_addr;
un->un_dma_curcnt = un->un_dma_count;
se_vme_dma_setup(c,un);
} else {
un->un_dma_curdir = SE_NO_DATA;
un->un_dma_curaddr = 0;
un->un_dma_curcnt = 0;
/* si.c: clear "bcr", se.c does not use it */
}
#ifdef SCSI_DEBUG
if (scsi_debug) {
printf("se%d: se_cmd: target %d, command= ",
SENUM(c),un->un_target);
se_print_cdb(un);
}
#endif SCSI_DEBUG
RETRY_CMD_PHASE:
if (se_wait_phase(ser, PHASE_COMMAND) != OK) {
/*
* Handle synchronous messages (6 bytes) and other
* unknown messages. Note, all messages will be
* rejected. It would be nice someday to figure
* out what to do with them; but not today.
*/
register u_char *icrp = &SBC_WR.icr;
if ((SBC_RD.cbsr & CBSR_PHASE_BITS) == PHASE_MSG_IN) {
*icrp = SBC_ICR_ATN;
msg = se_getdata(c, PHASE_MSG_IN);
EPRINTF1("se_cmd: rejecting msg 0x%x\n", msg);
i = 128; /* accept 128 message bytes (overkill) */
while((se_getdata(c, PHASE_MSG_IN) != -1) && --i);
if ((SBC_RD.cbsr & CBSR_PHASE_BITS) == PHASE_MSG_OUT) {
msg = SC_MSG_REJECT;
*icrp = 0; /* turn off ATN */
(void) se_putdata(c, PHASE_MSG_OUT, &msg, 1);
}
/* Should never fail this check. */
if( i > 0 )
goto RETRY_CMD_PHASE;
}
/* we've had a target failure, report it and quit */
printf("se%d: se_cmd: no command phase\n", SENUM(c));
se_reset(c, PRINT_MSG);
return (FAIL);
}
/*
* Determine size of the cdb. Since we're smart, we
* look at group code and guess. If we don't
* recognize the group id, we use the specified
* cdb length. If both are zero, use max. size
* of data structure.
*/
if ((size = sc_cdb_size[CDB_GROUPID(un->un_cdb.cmd)]) == 0 &&
(size = un->un_cmd_len) == 0) {
#ifdef SCSI_DEBUG
if (scsi_debug) printf("se%d: invalid cdb size, using size= %d\n",
SENUM(c), sizeof (struct scsi_cdb));
#endif SCSI_DEBUG
size = sizeof (struct scsi_cdb);
}
/* si.c: set SBC_WR.ser with HOST_ID here */
/* si.c: se_putcmd() is called rather than se_putdata() */
if (se_putdata(c, PHASE_COMMAND,
(u_char *)&un->un_cdb, (int)size) != OK) {
printf("se%d: se_cmd: cmd put failed\n", SENUM(c));
se_print_state(ser, c);
se_reset(c, PRINT_MSG);
return (FAIL);
}
/* If not polled I/O mode, we're done */
if (intr) {
/* si.c: did not setup dma until DATA phases,
came in from INTERRPT routine */
/* do final dma setup and start dma operation */
if (un->un_dma_count != 0) {
se_sbc_dma_setup(c, ser, (int)un->un_dma_curdir);
} else if (un->un_wantint) {
SBC_WR.mr |= SBC_MR_DMA;
SBC_WR.tcr = TCR_UNSPECIFIED;
ser->csr &= ~SE_CSR_SEND;
}
/* si.c: check lost phase change INT */
return (OK);
}
/*
* Polled SCSI data transfer mode.
*/
if (un->un_dma_curdir != SE_NO_DATA) {
register u_char phase;
if (un->un_dma_curdir == SE_RECV_DATA)
phase = PHASE_DATA_IN;
else
phase = PHASE_DATA_OUT;
/*
* Check if we have a problem with the command
* not going into data phase. If we do,
* then we'll skip down and get any status.
* Of course, this means that the command
* failed.
*/
if ((se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_LONG_WAIT, 1) == OK) &&
((SBC_RD.cbsr & CBSR_PHASE_BITS) == phase)) {
/*
* Must actually start DMA xfer here - setup
* has already been done. So, put sbc in dma
* mode and start dma transfer.
*/
se_sbc_dma_setup(c, ser, (int)un->un_dma_curdir);
/*
* Wait for DMA to finish. If it fails,
* attempt to get status and report failure.
*/
if ((err=se_cmdwait(c)) != OK) {
EPRINTF("se_cmd: cmdwait failed\n");
se_print_state(ser, c);
if (err != SCSI_FAIL)
msg = se_getstatus(un);
return (err);
}
} else {
EPRINTF("se_cmd: skipping data phase\n");
}
}
/*
* Get completion status for polled command.
* Note, if <0, it's retryable; if 0, it's fatal.
* Someday I should give polled status results
* more options. For now, everything is FATAL.
*/
EPRINTF("se_cmd: getting status\n");
if ((err=se_getstatus(un)) <= 0) {
if (err == 0)
return (HARD_FAIL);
else
return (FAIL);
}
return (OK);
}
/*
* Perform the SCSI arbitration and selection phases.
* Returns FAIL if unsuccessful,
* returns RESEL_FAIL if unsuccessful due to target reselecting,
* returns OK if all was cool.
*/
static
se_arb_sel(c, ser, un)
register struct scsi_ctlr *c;
register struct scsi_si_reg *ser;
register struct scsi_unit *un;
{
register u_char *icrp = &SBC_WR.icr;
register u_char *mrp = &SBC_WR.mr;
register int j;
register u_char icr;
int ret_val;
int s;
int sel_retries = 0;
DPRINTF("se_arb_sel:\n");
/*
* It seems that the tcr must be 0 for arbitration to work.
*/
SBC_WR.tcr = 0;
/* si.c: clear mrp with SBC_MR_ARB, but not SBC_MR_DMA */
*mrp &= ~SBC_MR_DMA;
*icrp = 0;
#ifdef SCSI_DEBUG
if (scsi_debug > 2)
se_print_state(ser, c);
#endif SCSI_DEBUG
SE_RETRY_SEL:
/* old se.c: wait for bus to become free here */
for (j = 0; j < SE_ARB_RETRIES; j++) {
/* wait for scsi bus to become free */
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_BSY,
SE_WAIT_COUNT, 0) != OK) {
printf("se%d: se_arb_sel: scsi bus continuously busy\n",
SENUM(c));
se_reset(c, PRINT_MSG);
ret_val = SCSI_FAIL;
goto SE_ARB_SEL_EXIT;
}
/* arbitrate for the scsi bus */
SBC_WR.odr = scsi_host_id;
*mrp |= SBC_MR_ARB; /* turn on arb */
/* wait for sbc to begin arbitration */
if (se_sbc_wait((caddr_t)icrp, SBC_ICR_AIP,
SE_SHORT_WAIT, 1) != OK) {
/*
* sbc may never begin arbitration due to a
* target reselecting us, the initiator.
*/
*mrp &= ~SBC_MR_ARB;
if ((SBC_RD.cbsr & SBC_CBSR_SEL) &&
(SBC_RD.cbsr & SBC_CBSR_IO) &&
(SBC_RD.cdr & scsi_host_id)) {
DPRINTF("se_arb_sel: reselect\n");
ret_val = RESEL_FAIL;
goto SE_ARB_SEL_EXIT;
}
EPRINTF1("se_arb_sel: AIP never set, cbsr= 0x%x\n",
SBC_RD.cbsr);
/* old se.c: exit */
goto SE_ARB_SEL_RETRY;
}
/* check to see if we won arbitration */
s = splr(pritospl(7));
DELAY(se_arbitration_delay);
if ((*icrp & SBC_ICR_LA) == 0 &&
((SBC_RD.cdr & ~scsi_host_id) < scsi_host_id)) {
/*
* WON ARBITRATION! Perform selection.
* If disconnect/reconnect enabled, set ATN.
* If not, skip ATN so target won't do disconnects.
*/
/* DPRINTF("se_arb_sel: won arb\n"); */
icr = SBC_ICR_SEL | SBC_ICR_BUSY | SBC_ICR_DATA;
if (c->c_flags & SCSI_EN_DISCON) {
icr |= SBC_ICR_ATN;
}
DELAY(se_bus_clear_delay + se_bus_settle_delay);
SBC_WR.odr = (1 << un->un_target) | scsi_host_id;
*icrp = icr;
*mrp &= ~SBC_MR_ARB; /* turn off arb */
/* si.c: delay(bus-clear_delay+bus_settle_delay) */
goto SE_ARB_SEL_WON;
}
(void)splx(s);
SE_ARB_SEL_RETRY:
*mrp &= ~SBC_MR_ARB; /* turn off arb */
EPRINTF("se_arb_sel: lost arbitration\n");
#ifdef SCSI_DEBUG
se_print_state(ser, c);
#endif SCSI_DEBUG
/* si.c: check cbsr with RESEL, if failed, error */
}
/*
* FAILED ARBITRATION even with retries.
* This shouldn't ever happen since
* we have the highest priority id on the scsi bus.
*/
*icrp = 0;
printf("se%d: se_arb_sel: never won arbitration\n", SENUM(c));
se_print_state(ser, c);
se_reset(c, PRINT_MSG);
ret_val = FAIL; /* may be retryable */
SE_ARB_SEL_EXIT:
return (ret_val);
SE_ARB_SEL_WON:
/* wait for target to acknowledge selection */
*icrp &= ~SBC_ICR_BUSY;
(void)splx(s);
DELAY(1);
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_BSY,
SE_SHORT_WAIT, 1) != OK) {
DPRINTF("se_arb_sel: busy never set\n");
#ifdef SCSI_DEBUG
if (scsi_debug > 2)
se_print_state(ser, c);
#endif SCSI_DEBUG
*icrp = 0;
junk = SBC_RD.clr; /* clear intr */
if (un->un_present && (++sel_retries < SE_SEL_RETRIES))
goto SE_RETRY_SEL;
ret_val = HARD_FAIL;
goto SE_ARB_SEL_EXIT;
}
/* DPRINTF("se_arb_sel: selected\n"); */
*icrp &= ~(SBC_ICR_SEL | SBC_ICR_DATA);
return (OK);
}
/*
* Set up the SCSI control logic for a dma transfer
* for vme host adaptor. Requires 32-bit buffer alignment!!!!
*/
se_vme_dma_setup(c, un)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
{
register struct scsi_si_reg *ser;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_vme_dma_setup:\n");
/* si:c set "dma_addr with dma_curaddr" and "count with 0" */
/* setup starting dma address and number bytes to dma */
ser->dma_addr = un->un_dma_count - un->un_dma_curcnt;
ser->dma_cntr = un->un_dma_curcnt;
if (un->un_dma_curdir == SE_SEND_DATA) {
bcopy((caddr_t)DVMA + un->un_dma_curaddr,
(caddr_t)ser->dma_buf,
un->un_dma_curcnt);
}
}
/*
* Setup and start the sbc for a dma operation.
*/
se_sbc_dma_setup(c, ser, dir)
register struct scsi_ctlr *c;
register struct scsi_si_reg *ser;
register int dir;
{
register struct scsi_unit *un = c->c_un;
DPRINTF("se_sbc_dma_setup:\n");
un->un_flags |= SC_UNF_DMA_INITIALIZED;
SBC_WR.icr |= (SBC_ICR_BUSY | SBC_ICR_DATA);
SBC_WR.mr |= SBC_MR_DMA;
if (dir == SE_RECV_DATA) {
SBC_WR.tcr = TCR_DATA_IN;
SBC_WR.ircv = 0; /* this starts DMA RECV process */
} else {
SBC_WR.tcr = TCR_DATA_OUT;
SBC_WR.icr = SBC_ICR_DATA;
SBC_WR.send = 0; /* this starts DMA SEND process */
}
}
/*
* Cleanup up the SCSI control logic after a dma transfer.
*/
se_dma_cleanup(c)
register struct scsi_ctlr *c;
{
register struct scsi_si_reg *ser;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_dma_cleanup:\n");
/* disable dma controller */
ser->dma_addr = 0;
ser->dma_cntr = 0;
/* take sbc out of dma mode */
SBC_WR.icr = 0;
SBC_WR.mr &= ~SBC_MR_DMA;
SBC_WR.tcr = TCR_DATA_OUT;
}
u_char msk_tbl[7] [4] ={
{0x00,0xff,0x00,0xff}, /* bad entry */
{0xf5,0x10,0xe2,0x02}, /* Selection/Reselection */
{0xf6,0x90,0xc2,0x40}, /* End of Process */
{0xf7,0x10,0x7f,0x00}, /* SCSI Bus Reset */
{0xbc,0x38,0xc2,0x40}, /* Parity Error */
{0xfd,0x10,0xe2,0x60}, /* Bus Mismatch Error */
{0xf7,0x14,0xff,0x00} /* Loss of BSY- */
};
#define SELECTION 1
#define EOP 2
#define BUS_RESET 3
#define PARITY 4
#define BUS_MISMATCH 5
#define LOSS_OF_BSY 6
/*
* Determine the cause of the interrupt.
* Returns interrupt cause or 0 for unsuccessful.
*/
get_int_typ(cbsr,bsr)
register u_char cbsr;
register u_char bsr;
{
register int i;
for (i = 0; i < 7; i++) {
if (((bsr & msk_tbl[i][0]) == msk_tbl[i][1]) &&
((cbsr & msk_tbl[i][2]) == msk_tbl[i][3]))
return (i);
}
return (0);
}
/*
* Handle a scsi interrupt.
*/
se_intr(c)
register struct scsi_ctlr *c;
{
register struct scsi_si_reg *ser;
register struct scsi_unit *un = c->c_un;
register int status = SE_NO_ERROR;
register int resid = 0;
register int dma_cleanup = 0; /* need to clean up after dma */
register int int_typ;
register int disconnect = 0; /* had a disconnect */
register int reset_occurred = 0;
register int i; /* for debugging */
register u_char cbsr;
register u_char cdr;
register u_char bsr;
register u_char msg;
register u_int lun;
#ifdef notdef
register int err;
#endif notdef
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_intr:\n");
if (ser->csr & SE_CSR_SBC_IP) {
/*
* These two registers determine what type of interrupt
* occurred, NCR 5380 SCSI Interface Chip Design Manual,
* Section 8, pg 19, Ints
*/
cbsr = SBC_RD.cbsr;
bsr = SBC_RD.bsr;
int_typ = get_int_typ(cbsr,bsr);
/* acknowledge sbc interrupt */
junk = SBC_RD.clr;
switch (int_typ) { /* service the appropriate Int */
case 0: /* This intr should never happen */
se_reset(c, PRINT_MSG);
return;
case SELECTION: /* (Re)Selection Interrupt tom */
EPRINTF("se_intr: (re)selection\n");
if (cbsr & SBC_CBSR_IO) {
EPRINTF(" se_intr: RESELECTION Interrupt\n");
if (SBC_RD.cdr & scsi_host_id) {
cdr = SBC_RD.cdr & ~scsi_host_id;
for (i = 0; i < 8; i++) {
if (cdr & (1 << i))
break;
}
cdr &= ~(1 << i);
if (cdr != 0) {
se_reset(c, PRINT_MSG);
return;
}
c->c_recon_target = i;
/* disable reconnects */
SBC_WR.ser = ser_state = 0;
/* assert BSY within 200 usec */
SBC_WR.icr |= SBC_ICR_BUSY;
/* old se.c: missing "SE_WAIT_COUNT" argument */
if(se_sbc_wait((caddr_t)&SBC_RD.cbsr,
SBC_CBSR_SEL,SE_WAIT_COUNT,0) != OK) {
se_reset(c, PRINT_MSG);
return;
}
SBC_WR.icr &= ~SBC_ICR_BUSY;
/* old se.c: missing "SE_WAIT_COUNT" argument */
if(se_sbc_wait((caddr_t)&SBC_RD.cbsr,
SBC_CBSR_REQ,SE_WAIT_COUNT,1) != OK) {
se_reset(c, PRINT_MSG);
return;
}
if((SBC_RD.cbsr & CBSR_PHASE_BITS) !=
PHASE_MSG_IN) {
junk = SBC_RD.clr;
se_reset(c, PRINT_MSG);
return;
}
junk = SBC_RD.clr;
msg = SBC_RD.cdr;
lun = msg & 0x07;
msg &= 0xF0;
if ((msg == SC_IDENTIFY) ||
(msg == SC_DR_IDENTIFY) ) {
if (se_reconnect(c,
(u_int)c->c_recon_target,lun) !=
OK) {
se_reset(c, PRINT_MSG);
}
return;
}
} /* end of if(scsi_host_id) */
} /* end of if(SBC_CBSR_IO) */
else {
EPRINTF(" se_intr: SELECTION Interrupt\n");
#ifdef notdef
/* err = se_sel(c,un,SEL_FLG); */
#ifdef lint
/* err = err; */
#endif lint
#endif notdef
EPRINTF(" se_intr: break of (RE)SELECTION Int\n");
return;
}
break;
case EOP: /* End of Process Interrupt */
EPRINTF("se_intr: eop\n");
cbsr = SBC_RD.cbsr; /* may have changed on DMA send */
switch (cbsr & CBSR_PHASE_BITS) {
case PHASE_STATUS:
EPRINTF("se_intr: status\n");
if (SBC_RD.tcr == TCR_UNSPECIFIED) {
SBC_WR.mr &= ~SBC_MR_DMA;
SBC_WR.tcr = TCR_DATA_OUT;
} else
dma_cleanup = 1;
break;
default:
printf("se_intr: EOP - default\n");
dma_cleanup = 1;
status = SE_FATAL;
break;
}
break;
case BUS_MISMATCH: /* Bus Phase Mismatch Interrupt */
EPRINTF("se_intr: bus mismatch\n");
cbsr = SBC_RD.cbsr; /* may have changed on DMA send */
switch (cbsr & CBSR_PHASE_BITS) {
case PHASE_DATA_OUT:
printf(" se_intr: BUS_MISMATCH phase, PHASE_DATA_OUT\n");
case PHASE_DATA_IN:
if ((un == NULL ) ||
((int)un->un_dma_curcnt < 0) ||
(un->un_dma_curdir == SE_NO_DATA) ) {
se_reset(c, PRINT_MSG);
return;
}
se_sbc_dma_setup(c, ser, (int)un->un_dma_curdir);
return;
case PHASE_COMMAND:
printf(" se_intr: BUS_MISMATCH phase, PHASE_COMMAND\n");
break;
case PHASE_STATUS:
EPRINTF("se_intr: status\n");
if (SBC_RD.tcr == TCR_UNSPECIFIED) {
SBC_WR.mr &= ~SBC_MR_DMA;
SBC_WR.tcr = TCR_DATA_OUT;
} else {
dma_cleanup = 1;
if (un->un_dma_curdir == SE_RECV_DATA) {
/*
* move the received data
* to DVMA
*/
bcopy((caddr_t)ser->dma_buf,
(caddr_t)DVMA +
un->un_dma_curaddr,
un->un_dma_curcnt -
ser->dma_cntr);
}
}
break;
case PHASE_MSG_IN:
EPRINTF("se_intr: msg\n");
msg = se_getdata(c,PHASE_MSG_IN);
switch (msg) {
case SC_COMMAND_COMPLETE:
printf(" se_intr: MSG_IN phase, SC_COMMAND_COMPLETE\n");
se_print_state(ser, c);
break;
case SC_EXTNDD_MSSG:
printf(" se_intr: MSG_IN phase, SC_EXTNDD_MSSG\n");
se_print_state(ser, c);
break;
case SC_SAVE_DATA_PTR:
printf(" se_intr: MSG_IN phase, SC_SAVE_DATA_PTR\n");
/*
* un->un_dma_curcnt = ser->dma_cntr;
* un->un_dma_curaddr = ser->dma_addr;
*/
return;
case SC_RESTORE_PTRS:
printf(" se_intr: MSG_IN phase, SC_RESTORE_PTRS\n");
se_print_state(ser, c);
ser->dma_cntr = un->un_dma_curcnt;
ser->dma_addr = un->un_dma_curaddr;
return;
case SC_DISCONNECT:
printf(" se_intr: MSG_IN phase, SC_DISCONNECT\n");
disconnect = 1;
SBC_WR.ser = ser_state = 0;
if (se_disconnect(c) != OK) {
se_reset(c, PRINT_MSG);
return;
}
SBC_WR.ser = ser_state = scsi_host_id;
break;
case SC_MSSG_REJ:
printf(" se_intr: MSG_IN phase, SC_MSSG_REJ\n");
se_print_state(ser, c);
break;
case SC_LNKD_CMD_CMPLT:
printf(" se_intr: MSG_IN phase, SC_LNKD_CMD_CMPLT\n");
break;
case SC_FLG_LNKD_CMD_CMPLT:
printf(" se_intr: MSG_IN phase, SC_FLG_LNKD_CMD_CMPLT\n");
se_print_state(ser, c);
break;
default:
printf("unknown MSG_IN command\n");
se_print_state(ser, c);
return;
} /* end of switch(msg) */
break;
case PHASE_MSG_OUT:
EPRINTF(" se_intr: MSG_OUT phase\n");
break;
default:
se_print_state(ser, c);
panic("se_intr: Phase Mismatch Error, Unknown Phase");
break;
} /* end of switch(cbsr&CBSR_PHASE_BITS) */
break;
case BUS_RESET: /* SCSI Bus Reset */
printf(" se_intr: BUS_RESET Interrupt\n");
ser->csr = 0;
DELAY(100);
ser->csr = SE_CSR_INTR_EN | SE_CSR_SCSI_RES;
if (c->c_disqtab.b_actf != NULL) {
c->c_flags |= SCSI_FLUSH_DISQ;
c->c_flush = c->c_disqtab.b_actl;
}
status = SE_FATAL;
reset_occurred = 1;
if ((un == NULL) && c->c_disqtab.b_actf) {
/* need to flush some disconnect tasks */
se_idle(c);
return;
}
break;
case PARITY: /* Parity Error Interrupt */
status = SE_FATAL;
se_print_state(ser, c);
panic("se_intr: Parity Error Interrupt");
break;
case LOSS_OF_BSY: /* Loss of BSY- signal */
printf(" se_intr: Loss of BSY- Interrupt\n");
status = SE_FATAL;
se_print_state(ser, c);
panic("se_intr: Loss of BSY-");
break;
default:
break;
} /* end of switch(int_typ) */
if (dma_cleanup) {
resid = (unsigned)ser->dma_cntr;
if (un->un_dma_curdir == SE_SEND_DATA) {
if (resid == 0xFFFF)
resid = 0;
else
resid += 1;
}
se_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 (se_getstatus(un) == 0) {
status = SE_RETRYABLE;
}
} else if (reset_occurred == 0) {
se_reset(c, PRINT_MSG);
}
(*un->un_ss->ss_intr)(c, resid, status);
}
/* si.c: check for reconnect already pending, if there is,
go back to beginning of INT routine */
/* old se.c: check if next job is ready and controller not
currentlty active, then check if the job had been
preemptted, if so call se_cmd and send it out.
Otherwise, call se_start() to do next job */
/* old se.c: update mbinfo with un_baddr */
se_start(un);
DPRINTF("se_intr: se_start end\n");
} else {
se_print_state(ser, c);
panic("se_intr: Spurious Level 2 Interrupt");
}
}
/*
* Handle target disconnecting.
*/
se_disconnect(c)
register struct scsi_ctlr *c;
{
register struct scsi_unit *un = c->c_un;
register struct scsi_si_reg *ser;
register u_short dma_counter;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_disconnect\n");
dma_counter = ser->dma_cntr;
/* save dma info for reconnect */
if (un->un_dma_curdir != SE_NO_DATA) {
if (un->un_flags & SC_UNF_DMA_INITIALIZED) {
if ( (un->un_dma_curdir == SE_SEND_DATA)
&& (dma_counter != un->un_dma_curcnt)
&& (dma_counter != 0)) {
printf("FIX THIS HERE!!!!\n");
} else if ( un->un_dma_curdir == SE_RECV_DATA) {
/*
* we have to move the read data here
* because of lack of semaphores on
* critical device. i.e. 64K buffer
*/
dma_counter = un->un_dma_curcnt;
printf(" se_disconnect: put the data transfer here!!\n");
}
} else {
/* we havent transfered any data yet */
dma_counter = un->un_dma_count;
}
/*
* if (un->un_dma_curdir == SE_RECV_DATA) {
* if (!se_dma_recv(c)) {
* return (FAIL);
* }
* }
*/
/*
* Save dma information so dma can be
* restarted when a reconnect occurs.
*/
un->un_dma_curcnt = ser->dma_cntr;
un->un_dma_curaddr = ser->dma_addr;
/*
* un->un_dma_curaddr += un->un_dma_curcnt - ser->dma_cntr;
* un->un_dma_curcnt = ser->dma_cntr;
*/
}
/*
* Remove this disconnected task from the ctlr ready queue and
* save on disconnect queue until a reconnect is done.
*/
se_discon_queue(un);
SBC_WR.mr &= ~SBC_MR_DMA;
ser->dma_cntr = 0;
return (OK);
}
/*
* Complete reselection phase and reconnect to target.
*
* Return true if sucessful, 0 if not.
*
* NOTE: TODO: think about whether we really want to do the
* resets w/in this code, or let the return of 0 indicate
* to the caller that we need to reset.
*
* NOTE: this routine cannot use se_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.
* NOTE: se.c does not use bcr
*/
se_reconnect(c,target,lun)
register struct scsi_ctlr *c;
register u_int target;
register u_int lun;
{
register struct scsi_si_reg *ser;
register struct scsi_unit *un;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_reconnect\n");
if (se_recon_queue(c, target, lun) != OK) {
DPRINTF("se_reconnect: failed se_recon_queue\n");
return (FAIL);
}
/* disable other reconnection attempts */
/* si.c: do not reset this */
SBC_WR.ser = ser_state = 0;
un = c->c_un;
/* restart disconnect activity */
if (un->un_dma_curdir != SE_NO_DATA) {
/* restore mainbus resource allocation info */
/* old se.c: update md_mbinfo from un_baddr */
/* do initial dma setup */
if (un->un_dma_curdir == SE_RECV_DATA)
ser->csr &= ~SE_CSR_SEND;
else
ser->csr |= SE_CSR_SEND;
se_vme_dma_setup(c,un);
}
/* we can finally acknowledge identify message */
SBC_WR.icr = SBC_ICR_ACK;
/* old se.c: missing "SE_WAIT_COUNT" argument */
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr,
SBC_CBSR_REQ, SE_WAIT_COUNT,0) != OK) {
printf("se_recon: REQ not INactive, cbsr 0x%x\n",
SBC_RD.cbsr);
se_reset(c, PRINT_MSG);
return (FAIL);
}
SBC_WR.icr = 0;
/* si.c: does not clear SBC_MR_DMA */
SBC_WR.mr |= SBC_MR_DMA;
return (OK);
}
/*
* Remove timed-out I/O request and report error to
* it's interrupt handler.
* Return OK if sucessful, FAIL if not.
*/
se_deque(c, un)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
{
register struct scsi_si_reg *ser;
register u_int target;
register u_int lun;
register struct scsi_unit *pun;
int s;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_deque:\n");
target = un->un_target;
lun = un->un_lun;
se_print_state(ser, c);
s = splr(pritospl(un->un_mc->mc_intpri));
un = c->c_un; /* get current unit */
/*
* If current SCSI I/O request is the one that timed out. Kill it.
*/
if ((int)un != NULL &&
un->un_c->c_tab.b_active != 0 &&
un->un_target == target &&
un->un_lun == lun) {
EPRINTF("se_deque: failed request at top of que\n");
(*un->un_ss->ss_intr)(c, un->un_dma_count, SE_TIMEOUT);
(void) splx(s);
return (OK);
}
for (un = (struct scsi_unit *)c->c_disqtab.b_actf, pun = NULL;
un; pun = un, un = un->un_forw) {
DPRINTF1("\tmd= 0x%x --- ", md);
DPRINTF2("target= %d, lun= %d\n", un->un_target,
un->un_lun);
if ((un->un_target == target) && (un->un_lun == lun))
break;
}
/* Failed to find entry, dump out disconnnect queue */
if (un == NULL) {
printf("se_deque: can't find: target %d, lun %d\n que:\n",
target, lun);
(void) splx(s);
return (FAIL);
}
/* Remove entity from disconnect queue */
if (un == (struct scsi_unit *)c->c_disqtab.b_actf)
c->c_disqtab.b_actf = (struct buf *)(un->un_forw);
else
pun->un_forw = un->un_forw;
if (un == (struct scsi_unit *)c->c_disqtab.b_actl)
c->c_disqtab.b_actl = (struct buf *)pun;
un->un_forw = NULL;
/* Requeue at front of controller queue */
if (un->un_c->c_tab.b_actf == NULL) {
EPRINTF("se_deque: reactivating request\n");
if (un->un_c->c_tab.b_active != 0) {
EPRINTF("se_deque: we have a reactivate problem\n");
}
un->un_c->c_tab.b_actf = (struct buf *)un;
un->un_c->c_tab.b_actl = (struct buf *)un;
un->un_c->c_tab.b_active = 1;
c->c_un = un;
(*un->un_ss->ss_intr)(c, un->un_dma_count, SE_TIMEOUT);
} else {
EPRINTF("se_deque: can't reactive request\n");
se_reset(c, PRINT_MSG);
}
(void) splx(s);
return (OK);
}
/*
* 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.
*/
se_idle(c)
register struct scsi_ctlr *c;
{
register struct scsi_si_reg *ser;
register struct scsi_unit *un;
register int resid;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_idle:\n");
if (c->c_flags & SCSI_FLUSHING) {
EPRINTF1("se_idle: flushing, flags 0x%x\n", c->c_flags);
return;
}
/* flush disconnect tasks if a reconnect will never occur */
if (c->c_flags & SCSI_FLUSH_DISQ) {
EPRINTF("se_idle: flushing disconnect que\n");
/* now in process of flushing tasks */
c->c_flags &= ~SCSI_FLUSH_DISQ;
c->c_flags |= SCSI_FLUSHING;
for (un = (struct scsi_unit *)c->c_disqtab.b_actf;
un && c->c_flush;
un = (struct scsi_unit *)c->c_disqtab.b_actf) {
/* keep track of last task to flush */
if (c->c_flush == (struct buf *) un)
c->c_flush = NULL;
/* remove tasks from disconnect queue */
c->c_disqtab.b_actf = (struct buf *)un->un_forw;
un->un_forw = NULL;
/* requeue on controller q */
se_ustart(un);
un->un_c->c_tab.b_active = 1;
c->c_un = un;
/* inform device routines of error */
if (un->un_dma_curdir != SE_NO_DATA) {
/* old se.c: store md_mbinfo with un_baddr */
resid = un->un_dma_curcnt;
} else {
resid = 0;
}
(*un->un_ss->ss_intr)(c, resid, SE_RETRYABLE);
/* si.c: call si_off(c.un) */
}
if (c->c_disqtab.b_actf == NULL)
c->c_disqtab.b_actl = NULL;
c->c_flags &= ~SCSI_FLUSHING;
}
/* enable reconnect attempts */
SBC_WR.ser = ser_state = scsi_host_id;
if ((c->c_flags & SCSI_ONBOARD) == 0) {
ser->dma_cntr = 0;
ser->csr &= ~SE_CSR_SEND;
}
}
#ifdef SCSI_DEBUG
u_char scsi_old_status[32];
#endif SCSI_DEBUG
/*
* Get status bytes from scsi bus.
*/
se_getstatus(un)
register struct scsi_unit *un;
{
register struct scsi_ctlr *c = un->un_c;
register struct scsi_si_reg *ser;
register u_char *cp = (u_char *)&un->un_scb;
register int i;
register int b;
register u_char msg;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_getstatus:\n");
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_LONG_WAIT, 1) != OK) {
printf("se%d: se_getstatus: REQ inactive\n", SENUM(c));
se_print_state(ser, c);
return (0);
}
if (se_wait_phase(ser, PHASE_STATUS) != OK) {
printf("se%d: se_getstatus: no STATUS phase\n", SENUM(c));
se_print_state(ser, c);
return (0);
}
/* get all the status bytes */
for (i = 0;;) {
b = se_getdata(c, PHASE_STATUS);
if (b < 0) {
break;
}
if (i < STATUS_LEN) {
#ifdef SCSI_DEBUG
scsi_old_status[i] = b; /* debug */
#endif SCSI_DEBUG
cp[i++] = b;
}
}
#ifdef SCSI_DEBUG
if (scsi_debug > 2) {
int x;
register u_char *p = (u_char *)&un->un_scb;
printf("se_getstatus: %d status bytes", i);
for (x = 0; x < i; x++)
printf(" %x", *p++);
printf("\n");
}
#endif SCSI_DEBUG
/* si.c: wait for MSG_IN phase */
/* get command complete message */
msg = se_getdata(c, PHASE_MSG_IN);
if (msg != SC_COMMAND_COMPLETE) {
EPRINTF1("si_getstatus: bogus msg_in 0x%x\n", msg);
}
SBC_WR.tcr = TCR_UNSPECIFIED;
/*
* Check status for error condition, return -1 if error.
* Otherwise, return positive byte count for no error.
*/
if (cp[0] & SCB_STATUS_MASK)
return (-1); /* retryable */
return (i); /* no error */
}
/*
* Wait for a scsi dma request to complete.
* Disconnects were disabled in se_cmd when polling for command completion.
* Called by drivers in order to poll on command completion.
*/
se_cmdwait(c)
register struct scsi_ctlr *c;
{
register struct scsi_si_reg *ser;
register struct scsi_unit *un = c->c_un;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF("se_cmdwait:\n");
/* si.c: wait for dma transfer completed */
/* if command does not involve dma activity, then we are finished */
if (un->un_dma_curdir == SE_NO_DATA) {
DPRINTF("se_cmdwait: no data\n");
return (OK);
}
/* wait for dma completion */
/* si.c: check SI_CSR_DMA_IP and SI_CSR_DMA_CONFLICT
in addition to SI_CSR_SBC_IP */
if (se_wait((u_short *)&ser->csr, SE_CSR_SBC_IP, 1) != OK) {
EPRINTF("se_cmdwait: dma still in progress\n");
se_reset(c, PRINT_MSG);
return (SCSI_FAIL);
}
if ((SBC_RD.mr & SBC_MR_DMA) == 0) {
EPRINTF("se_cmdwait: sbc DMA disabled\n");
se_reset(c, PRINT_MSG);
return (SCSI_FAIL);
}
/* si.c: handle special dma recv a little different than here */
if ( (un->un_dma_curdir == SE_RECV_DATA) ) {
if (ser->dma_cntr == un->un_dma_curcnt) {
EPRINTF("se_cmdwait: DMA failed\n");
se_reset(c, PRINT_MSG);
return (FAIL);
} else { /* move the info to DVMA space */
bcopy((caddr_t)ser->dma_buf,
(caddr_t)DVMA + un->un_dma_curaddr,
un->un_dma_curcnt - ser->dma_cntr);
}
} else if ((un->un_dma_curdir==SE_SEND_DATA) &&
(ser->dma_cntr != 0xFFFF)) {
EPRINTF("se_cmdwait: DMA failed\n");
se_reset(c, PRINT_MSG);
return (SCSI_FAIL);
}
/* ack sbc interrupt and cleanup */
junk = SBC_RD.clr;
se_dma_cleanup(c);
/* si.c: check SI_CSR_SBC_IP, if set, read csr to clear INT */
return (OK);
}
/*
* Wait for a condition to be (de)asserted on the scsi bus.
* Returns OK for successful. Otherwise, returns
* FAIL.
*/
static
se_sbc_wait(reg, cond, wait_count, set)
register caddr_t reg;
register u_char cond;
register int wait_count;
register int set;
{
register int i;
register u_char regval;
for (i = 0; i < wait_count; i++) {
regval = *reg;
/* si.c: only check regval agaist cond , not equal */
if ((set == 1) && ((regval & cond) == cond)) {
return (OK);
}
if ((set == 0) && !(regval & cond)) {
return (OK);
}
DELAY(10);
}
DPRINTF("se_sbc_wait: timeout\n");
return (FAIL);
}
/*
* Wait for a condition to be (de)asserted.
* Used for monitor DMA controller.
* Returns OK for successful. Otherwise,
* returns FAIL.
*/
static
se_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 < SE_WAIT_COUNT; i++) {
regval = *reg;
/* si.c: does not check (regval & cond) == cond */
if ((set == 1) && ((regval & cond) == cond)) {
return (OK);
}
if ((set == 0) && !(regval & cond)) {
return (OK);
}
DELAY(10);
}
return (FAIL);
}
/*
* Wait for a phase on the SCSI bus.
* Returns OK for successful. Otherwise,
* returns FAIL.
*/
static
se_wait_phase(ser, phase)
register struct scsi_si_reg *ser;
register u_char phase;
{
register int i;
DPRINTF2("se_wait_phase: %s phase (0x%x)\n",se_str_phase(phase),phase);
for (i = 0; i < SE_WAIT_COUNT; i++) {
if ((SBC_RD.cbsr & CBSR_PHASE_BITS) == phase)
return (OK);
DELAY(10);
}
return (FAIL);
}
/*
* Put data onto the scsi bus.
* Returns OK if successful, FAIL otherwise.
*/
static
se_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 *ser;
register int i;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF2("se_putdata: %s phase (0x%x) ", se_str_phase(phase), phase);
/* DPRINTF1("num %d\n", num); */
/*
* Set up tcr so we can transmit data.
*/
SBC_WR.tcr = phase >> 2;
/* put bytes onto scsi bus */
for (i = 0; i < num; i++ ) {
/* wait for target to request a byte */
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_WAIT_COUNT, 1) != OK) {
printf("se%d: putdata, REQ not active\n", SENUM(c));
return (FAIL);
}
/* load data */
/* DPRINTF2("putting byte # %d, = 0x%x\n", i, *data); */
SBC_WR.odr = *data++;
SBC_WR.icr = SBC_ICR_DATA;
/* complete req/ack handshake */
SBC_WR.icr |= SBC_ICR_ACK;
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_WAIT_COUNT, 0) != OK) {
printf("se%d: putdata, req not INactive\n", SENUM(c));
return (FAIL);
}
SBC_WR.icr = 0; /* clear ack */
}
SBC_WR.tcr = TCR_UNSPECIFIED;
junk = SBC_RD.clr; /* clear int */
/* si.c: clear SBC_WR.icr here, rather than up in the loop */
return (OK);
}
/*
* Get data from the scsi bus.
* Returns a single byte of data, -1 if unsuccessful.
*/
static
se_getdata(c, phase)
register struct scsi_ctlr *c;
register u_short phase;
{
register struct scsi_si_reg *ser;
register u_char icr;
register u_char data;
register int s;
ser = (struct scsi_si_reg *)c->c_reg;
DPRINTF2("se_getdata: %s phase (0x%x)",se_str_phase(phase),phase);
/* wait for target request */
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_WAIT_COUNT, 1) != OK) {
printf("se%d: getdata, REQ not active, cbsr 0x%x\n",
SENUM(c), SBC_RD.cbsr);
se_print_state(ser, c);
return (-1);
}
if ((SBC_RD.cbsr & CBSR_PHASE_BITS) != phase) {
/* not the phase we expected */
DPRINTF1("se_getdata: unexpected phase, expecting %s\n",
se_str_phase(phase));
/* se_print_state(ser, c); */
return (-1);
}
/* grab data and complete req/ack handshake */
data = SBC_RD.cdr;
icr = SBC_WR.icr;
DPRINTF1(" icr= %x ", icr);
SBC_WR.icr = icr | SBC_ICR_ACK;
if (se_sbc_wait((caddr_t)&SBC_RD.cbsr, SBC_CBSR_REQ,
SE_WAIT_COUNT, 0) != OK) {
printf("se%d: getdata, REQ not inactive\n", SENUM(c));
return (-1);
}
if ((phase == PHASE_MSG_IN) &&
((data == SC_COMMAND_COMPLETE) || (data == SC_DISCONNECT))) {
/* DPRINTF("se_getdata: done with ACK\n"); */
s = splr(pritospl(7));
/* si.c: set SBC_WR.tcr = TCR_UNSPECIFIED and
also read SBC_RD.clr to clear INT */
SBC_WR.icr = icr; /* clear ack */
SBC_WR.mr &= ~SBC_MR_DMA;
(void) splx(s);
} else {
SBC_WR.icr = icr; /* clear ack */
}
DPRINTF1(" data= 0x%x\n", data);
return (data);
}
/*
* Reset SCSI control logic and bus.
*/
se_reset(c, msg_enable)
register struct scsi_ctlr *c;
register int msg_enable;
{
struct scsi_si_reg *ser;
ser = (struct scsi_si_reg *)c->c_reg;
if (msg_enable) {
printf("se%d: resetting scsi bus\n", SENUM(c));
se_print_state(ser, c);
DEBUG_DELAY(10000000);
}
/* reset scsi control logic */
ser->csr = 0;
DELAY(100);
ser->csr = SE_CSR_SCSI_RES;
ser->dma_addr = 0;
ser->dma_cntr = 0;
/* issue scsi bus reset (make sure INTS from sbc are disabled) */
SBC_WR.icr = SBC_ICR_RST;
DELAY(1000);
SBC_WR.icr = 0; /* clear reset */
/* give reset scsi devices time to recover (> 2 Sec) */
DELAY(scsi_reset_delay);
junk = SBC_RD.clr;
/* enable sbc interrupts */
ser->csr |= SE_CSR_INTR_EN;
/* disconnect queue needs flushing */
if (c->c_disqtab.b_actf != NULL) {
c->c_flags |= SCSI_FLUSH_DISQ;
c->c_flush = c->c_disqtab.b_actl;
se_idle(c);
}
}
/*
* Return residual count for a dma.
*/
se_dmacnt(c)
register struct scsi_ctlr *c;
{
register struct scsi_unit *un = c->c_un;
register struct scsi_si_reg *ser;
register u_short cnt;
ser = (struct scsi_si_reg *)c->c_reg;
cnt = ser->dma_cntr;
if (un->un_dma_curdir == SE_SEND_DATA) {
if (cnt == 0xFFFF) { /* SUCCESS on SEND of data */
return (0);
} else {
return ((int)cnt + 1);
}
}
return ((int)cnt); /* residual amount after read */
}
/*
* Print out the cdb.
*/
static
se_print_cdb(un)
register struct scsi_unit *un;
{
register u_char size;
register u_char *cp;
register u_char i;
cp = (u_char *) &un->un_cdb;
if ((size = sc_cdb_size[CDB_GROUPID(*cp)]) == 0 &&
(size = un->un_cmd_len) == 0) {
/* If all else fails, use structure size */
size = sizeof (struct scsi_cdb);
}
for (i = 0; i < size; i++)
printf(" %x", *cp++);
printf("\n");
}
/*
* returns string corresponding to the phase
*/
static char *
se_str_phase(phase)
register u_char phase;
{
register int index = (phase & CBSR_PHASE_BITS) >> 2;
static char *phase_strings[] = {
"DATA_OUT",
"DATA_IN",
"COMMAND",
"STATUS",
"",
"",
"MSG_OUT",
"MSG_IN",
"BUS FREE",
};
if (((phase & SBC_CBSR_BSY) == 0) && (index == 0))
return (phase_strings[8]);
else
return (phase_strings[index]);
}
/*
* returns string corresponding to the last phase.
* Note, also encoded are internal messages in
* addition to the last bus phase.
*/
static char *
se_str_lastphase(phase)
register u_char phase;
{
static char *invalid_phase = "";
static char *phase_strings[] = {
"Identify MSG", /* 0x80 */
"Disconnect MSG", /* 0x81 */
"Cmd complete MSG", /* 0x82 */
"Restore ptr MSG", /* 0x83 */
"Save ptr MSG", /* 0x84 */
"Spurious" /* 0x85 */
};
if (phase == 0x00)
return (invalid_phase);
if (phase >= 0x80) {
if (phase > 0x85)
return (invalid_phase);
return (phase_strings[phase - 0x80]);
} else {
return (se_str_phase(phase));
}
}
/*
* print out the current hardware state
*/
static
se_print_state(ser, c)
register struct scsi_si_reg *ser;
register struct scsi_ctlr *c;
{
register struct scsi_unit *un = c->c_un;
/* si.c: c_last_phase is always up-to-date */
/* but old se.c: never update c_last_phase */
printf("\tlast phase= 0x%x (%s)\n",
c->c_last_phase, se_str_lastphase((u_char)c->c_last_phase));
printf("\tcsr= 0x%x tcr= 0x%x ser= 0x%x\n",
ser->csr, SBC_RD.tcr, ser_state);
printf("\tcbsr= 0x%x (%s) cdr= 0x%x mr= 0x%x bsr= 0x%x\n",
SBC_RD.cbsr, se_str_phase(SBC_RD.cbsr), SBC_RD.cdr,
SBC_RD.mr, SBC_RD.bsr);
if (un != NULL) {
printf("\ttarget= %d, lun= %d ", un->un_target, un->un_lun);
printf("DMA addr= 0x%x count= %d (%d)\n",
un->un_dma_curaddr, un->un_dma_curcnt,
un->un_dma_count);
printf("\tcdb= ");
se_print_cdb(un);
}
}
#endif NSE > 0