Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

3764 lines
117 KiB
C

/*
*
* @(#)sm.c 1.1 92/07/30 Copyright (c) 1988 by Sun Microsystems, Inc.
*
* SCSI-OS (host adapter driver for Emulex ESP-100/100A (NCR 53C90/53C90A))
*
* COMPILE-DEFINES:
* DVMA:
* Intended to be used together with SUN's new DVMA Gate-array
* DMAPOLL:
* Special version to use POLL mode for DMA
* P4DVMA:
* Special version for a P4 based DVMA/ESP test board, which has the DVMA
* and use level 2 interrupt same as the normal SCSI driver
* P4ESP:
* Special version for a P4 based ESP test board, which use pseudo DMA.
* ESP's reg_intr has to be polled due to NO other INT pending bit
* (register) available, even it is not recommented to read "reg_intr"
* when ESP's interrupt pin is not active and could result a lost of
* interrupt because of a timing window.
*
* HISTORY:
* 5/90 JK Added bus analyzer function
* 3/90 KSAM Correct default/negotiate sync period; Fix "sm_off()",
* "sm_idle()", "sm_reset()" to match "si.c". (4.2)
* 11/89 KSAM Add slight delay for dma-drain cmd to settle. (4.1-FCS)
* 8/89 KSAM Merge into SunOS 4.1-pre beta release. (4.1-Beta)
* 7/89 KSAM Correct the initalization value of ESP2's reg_conf_2.
* (now both "reg_conf_val" & "reg_conf2_val" are global).
* 6/89 KSAM Correct an "race_cond" back to msg_out and cause a panic
* due to unit pointer is NULL. (4.1 alpha9-alpha10)
* 5/89 KSAM Add new dequeing algorithm & optical disk support (4.1)
* 4/89 KSAM Correct restore_ptr_msg for Conner 100Mb (4.0.3 FCS)
* 3/89 KSAM Correct unexpected disk_dump under synchronous (4.0.3)
* 2/89 KSAM Add synchnronous and "auto-reassign" support (4.0.3 B-2)
* 1/89 KSAM Skip "sd1 & sd3", add "sel_step" protection (4.0.3 Beta)
* 12/88 KSAM Add sm_watch() and minor cleanup (4.0.3 alpha3)
* 11/88 KSAM Add latest ESP errata support (4.0.3 alpha2)
* 10/88 KSAM Add disconnect support (4.0.3 alpha1)
* 8/88 KSAM Add Stingray support
* 7/88 KSAM Add debug trace and P4/DVMA test board support
* 6/88 KSAM Add Hydra support
* 5/88 KSAM Add prelimnary synchronous support
* 4/88 KSAM Add P4/ESP test board support
* 3/88 KSAM Add disconnect support
* 2/88 KSAM initial written (based on 4.0 SunOS)
*
* XXX: Note, data structures are allocated to only support one sm host
* adapter driver.
*
*/
#if (defined sun3x) || (defined sun4)
#include "sm.h"
#if NSM > 0
#ifndef lint
static char sccsid[] = "@(#)sm.c 1.1 92/07/30 Copyr 1988 Sun Micro";
#endif lint
#define SMSYNC /* turn on synchronous support, if scsi_disre_enable=2 */
/* can be disabled by writing "scsi_disre_enable = 1" */
#ifdef notdef
#define SMINFO /* turn on statistic info */
#define SMEPRINT /* print error printf */
#define SMDEBUG /* turn on debugging code and printfs */
#define DMAPOLL /* no DVMA */
#define P4DVMA /* using P4-DVMA board */
#define SMTRACE /* turn on trace code */
#define P4ESP /* P4 ESP special 3/60 test board */
#endif notdef
/*
* Generic scsi routines.
*/
#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>
#ifdef P4DVMA
#include <sys/mman.h>
#endif P4DVMA
#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 <mon/idprom.h>
#include <sundev/mbvar.h>
#include <sundev/smreg.h>
#include <sundev/scsi.h>
int sm_probe(), sm_slave(), sm_attach(), sm_go(), sm_done(), sm_poll();
int sm_ustart(), sm_start(), sm_deque(), sm_watch();
int sm_off(), sm_cmd(), sm_reset(), sm_dmacnt();
int sm_cmd_wait(), sm_getstatus();
char *sm_str_phase(), *sm_str_state(), *sm_str_error();
extern int nsm, scsi_host_id, scsi_reset_delay, scsi_debug;
extern int scsi_disre_enable;
extern struct scsi_ctlr smctlrs[]; /* per controller structs */
extern struct mb_ctlr *sminfo[];
extern struct mb_device *sdinfo[];
extern u_char sc_cdb_size[];
#ifdef P4DVMA
u_long *iomap_base;
extern struct seg kseg;
#endif P4DVMA
#if (defined P4ESP || defined P4DVMA)
struct mb_driver smdriver = {
sm_probe, sm_slave, sm_attach, sm_go, sm_done, sm_poll,
sizeof (struct scsi_sm_reg), "dx", sdinfo, "sm", sminfo, MDR_BIODMA,
};
/* using different disk module "dx" */
#else
struct mb_driver smdriver = {
sm_probe, sm_slave, sm_attach, sm_go, sm_done, sm_poll,
sizeof (struct scsi_sm_reg), "sd", sdinfo, "sm", sminfo, MDR_BIODMA,
};
#endif (defined P4ESP || defined P4DVMA)
/* routines available to devices specific portion of scsi driver */
struct scsi_ctlr_subr smsubr = {
sm_ustart, sm_start, sm_done, sm_cmd, sm_getstatus, sm_cmd_wait,
sm_off, sm_reset, sm_dmacnt, sm_go, sm_deque,
};
extern struct scsi_unit_subr scsi_unit_subr[];
extern int scsi_ntype;
static struct sm_snap sm_info;
struct sm_snap *smsnap = &sm_info;
static struct scsi_sm_reg *esp_reg_addr;
static struct udc_table *dvma_reg_addr;
static int timeout_un = -1;
/* Log state history of activity */
#define SM_LOG_STATE(arg0, arg1, arg2) {\
c->c_last_phase = arg0; \
c->c_phases[c->c_phase_index].phase = arg0; \
c->c_phases[c->c_phase_index].target = arg1; \
c->c_phases[c->c_phase_index].lun = arg2; \
c->c_phase_index = (++c->c_phase_index) & (NPHASE -1); \
}
#ifdef SMSYNC
u_char sync_period[NTARGETS];
u_char sync_offset[NTARGETS];
u_char fifo_data[14];
#endif SMSYNC
u_char reg_conf2_val = 0; /* configure as an older ESP */
u_char sync_target_err = 0;
u_char prev_prt_cnt = 0;
u_char prev_prt_err = 0xff;
#ifdef SMINFO
u_short target_accstate[NTARGETS];
#endif SMINFO
#ifdef SMDEBUG
struct sm_dma_info {
u_long un_addr;
u_long un_count;
u_long un_flag;
u_long req_addr;
u_long req_cnt;
u_long xfrcnt;
u_long req_dir;
};
static struct sm_dma_info dmainfo[NTARGETS];
int esp_debug = 0;
#endif SMDEBUG
#define IDM_SUN4_STING 0x23
#define IDM_SUN3X_HYDRA 0x42
#define IDM_SUN4C_CAMPUS 0x50
/* returned value from sm_cmd */
#define OK 0 /* successful */
#define FAIL 1 /* failed, may be recoverable */
#define HARD_FAIL 2 /* failed, not recoverable */
#define NO_MSG 0 /* don't print reset warning message */
#define PRINT_MSG 1 /* print reset warning message */
#ifdef SMDEBUG
/* Handy debugging 0, 1, and 2 argument printfs */
#define DPRINTF(str) \
if (scsi_debug > 2) printf(str)
#define DPRINTF1(str, arg1) \
if (scsi_debug > 3) printf(str,arg1)
#define DPRINTF2(str, arg1, arg2) \
if (scsi_debug > 4) printf(str,arg1,arg2)
#define DPRINTF3(str, arg1, arg2, arg3) \
if (scsi_debug > 4) printf(str,arg1,arg2, arg3)
#define DEBUG_DELAY(cnt) \
if (scsi_debug > 5) DELAY(cnt)
#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)
#define EPRINTF3(str, arg1, arg2, arg3) \
if (scsi_debug) printf(str,arg1,arg2,arg3)
#define EPRINTF4(str, arg1, arg2, arg3, arg4) \
if (scsi_debug) printf(str,arg1,arg2,arg3,arg4)
#else SMDEBUG
#define DEBUG_DELAY(cnt)
#define DPRINTF(str)
#define DPRINTF1(str,arg1)
#define DPRINTF2(str,arg1,arg2)
#define DPRINTF3(str, arg1, arg2, arg3)
#ifdef SMEPRINT
#define EPRINTF(str) printf(str)
#define EPRINTF1(str,arg1) printf(str,arg1)
#define EPRINTF2(str,arg1,arg2) printf(str,arg1,arg2)
#define EPRINTF3(str,arg1,arg2,arg3) printf(str,arg1,arg2,arg3)
#define EPRINTF4(str,arg1,arg2,arg3,arg4) printf(str,arg1,arg2,arg3,arg4)
#else
#define EPRINTF(str)
#define EPRINTF1(str,arg1)
#define EPRINTF2(str,arg1,arg2)
#define EPRINTF3(str,arg1,arg2,arg3)
#define EPRINTF4(str,arg1,arg2,arg3,arg4)
#endif SMEPRINT
#endif SMDEBUG
#ifdef SMTRACE
#define TRACE_CHAR(data) sm_store_trace(data)
#define TRBUF_LEN 512 /* trace buffer stuff */
int trace_index = 0;
char sm_trace[TRBUF_LEN];
static
sm_store_trace(data)
char data;
{
register i = trace_index;
sm_trace[i] = data;
if (++i >= TRBUF_LEN)
i = 0;
sm_trace[i] = '?'; /* mark end */
trace_index = i;
}
#else
#define TRACE_CHAR(data)
#endif SMTRACE
/*
* Print out the cdb.
*/
static
sm_print_cdb(un)
register struct scsi_unit *un;
{
register u_char size, i;
register u_char *cp = (u_char *) &un->un_cdb;
/* If all else fails, use structure size */
if ((size = sc_cdb_size[CDB_GROUPID(*cp)]) == 0 &&
(size = un->un_cmd_len) == 0)
size = sizeof (struct scsi_cdb);
for (i = 0; i < size; i++)
printf(" %x", *cp++);
printf("\n");
}
/*
* returns string corresponding to the smsnap cur_err entry.
*/
static char *
sm_str_error(error)
register u_char error;
{
static char *invalid_error = "";
static char *error_strings[] = ERR_STRING_DATA;
if (error > SE_LAST_ERROR)
return(invalid_error);
return(error_strings[error]);
}
/*
/*
* returns string corresponding to the state log entry.
*/
static char *
sm_str_state(state)
register u_char state;
{
static char *invalid_state = "";
static char *state_strings[] = STATE_STRING_DATA;
if (state > STATE_LAST)
return(invalid_state);
return(state_strings[state]);
}
/*
* returns string corresponding to the current phase
*/
static char *
sm_str_phase(phase)
register u_char phase;
{
register int index = phase & ESP_PHASE_MASK;
static char *phase_strings[] = PHASE_STRING_DATA;
return(phase_strings[index]);
}
/*
* print out the current hardware state
*/
static
sm_print_state(c)
register struct scsi_ctlr *c;
{
register struct scsi_sm_reg *smr;
register struct scsi_unit *un = c->c_un;
register struct udc_table *dvma;
register int dma_count;
register short x, y, z;
#ifdef SMTRACE
register int index, trace_data;
register char *traceptr;
#endif SMTRACE
if ((smsnap->cur_err == prev_prt_err) && prev_prt_cnt) {
/* If already printed once, don't print the same info. */
EPRINTF1("sm_prt_state: duplicate error, %x\n", smsnap->cur_err);
return;
} else {
/* Log the error. */
if (smsnap->cur_err != smsnap->cur_err) {
prev_prt_cnt = 0;
prev_prt_err = smsnap->cur_err;
} else {
prev_prt_cnt++;
}
}
smr = esp_reg_addr;
dvma = dvma_reg_addr;
/*dma_count = GET_ESP_COUNT;*/
dma_count = (ESP_RD.xcnt_hi <<8) | ESP_RD.xcnt_lo;
printf("sm%d Snap shot: error= 0x%x (%s error)\n", SMNUM(c),
smsnap->cur_err, sm_str_error(smsnap->cur_err));
printf(" state= 0x%x (%s), sub_state= 0x%x\n",
smsnap->cur_state, sm_str_state(smsnap->cur_state),
smsnap->sub_state);
printf(" esp cmd= 0x%x, xcnt= %d, intr= 0x%x\n",
ESP_RD.cmd, dma_count, smsnap->esp_intr);
x = ESP_RD.fifo_flag; /* needed for fifo dump */
if (smsnap->esp_stat != ESP_RD.stat) {
printf(" fifo_flag= 0x%x, status= 0x%x(0x%x), step= 0x%x\n",
ESP_RD.fifo_flag, smsnap->esp_stat,
ESP_RD.stat, ESP_RD.step);
} else {
printf(" fifo_flag= 0x%x, status= 0x%x, step= 0x%x\n",
ESP_RD.fifo_flag, smsnap->esp_stat, ESP_RD.step);
}
/* Print esp fifo contents */
if (x &= KEEP_5BITS) {
printf(" fifo:");
while (x--)
printf(" %x", ESP_RD.fifo_data);
printf("\n");
}
#ifdef SMINFO
printf(" disconnected targets= %d last status= 0x%x last msg= 0x%x\n",
smsnap->num_dis_target, smsnap->scsi_status, smsnap->scsi_message);
#endif SMINFO
if (un) {
#ifdef SMSYNC
if (scsi_disre_enable > 1) {
printf(" sync periods[0-7]:");
for (x = 0; x < NTARGETS -1; x++)
printf(" %d", sync_period[x]);
printf("\n sync offsets[0-7]:");
for (x = 0; x < NTARGETS -1; x++)
printf(" %d", sync_offset[x]);
printf("\n");
}
#endif SMSYNC
/* If not in data phase, use dma_curcnt rather than esp xcnt */
if (c->c_last_phase != STATE_DATA_IN &&
c->c_last_phase != STATE_DATA_OUT)
dma_count = un->un_dma_curcnt;
if (dvma->scsi_dmaaddr != un->un_dma_addr) {
printf(" DVMA status= 0x%x addr= 0x%x (0x%x)\n",
dvma->scsi_ctlstat,
dvma->scsi_dmaaddr, un->un_dma_addr);
} else {
printf(" DVMA status= 0x%x addr= 0x%x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
}
printf(" target= %d, lun= %d", un->un_target, un->un_lun);
printf(", count= %d(%d)\n cdb:", dma_count, un->un_dma_count);
sm_print_cdb(un);
}
/* Print out state history */
printf(" current phase= %s\n", sm_str_phase(ESP_RD.stat));
z = c->c_phase_index;
for (x = 0; x < NPHASE; x++) {
z = --z & (NPHASE -1);
y = c->c_phases[z].phase;
printf(" last phase= %s", sm_str_state((u_char)y));
if (c->c_phases[z].target != -1)
printf(" %d", c->c_phases[z].target);
if (c->c_phases[z].lun != -1)
printf(" %d\n", c->c_phases[z].lun);
else
printf("\n");
}
#ifdef SMINFO
printf(" acc_state:");
for (i = 0; i < 7; i++) {
printf(" %x", target_accstate[i]);
}
printf("\n");
#endif SMINFO
#ifdef SMTRACE
printf("\n Following shows the current SCSI-ESP trace:\n");
index = 0;
traceptr = (char *)&sm_trace[index];
while (++index < TRBUF_LEN) {
trace_data = (int)(*traceptr++) & KEEP_LBYTE;
printf(" %x", trace_data);
if ((index == 0x128) || (index == 0x256) || (index == 0x384))
DELAY(1000000);
}
printf("\n End of sm_driver debug trace\n");
#endif SMTRACE
}
/*
* This routine unlocks this driver when I/O
* has ceased, and a call to sm_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
sm_watch(c)
register struct scsi_ctlr *c;
{
register struct scsi_unit *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.
*/
un = (struct scsi_unit *)c->c_tab.b_actf;
if (un == NULL)
un = (struct scsi_unit *)c->c_disqtab.b_actf;
if (un != NULL)
sm_start(un);
timeout(sm_watch, (caddr_t)c, 10*hz);
}
/***************** BOOT-UP of SM.C ****************************************/
/*
* Determine existence of SCSI host adapter.
* Returns ZERO for failure, !ZERO= size of sm data structure if ok.
*/
sm_probe(smr, ctlr)
register struct scsi_sm_reg *smr;
int ctlr;
{
register struct scsi_ctlr *c;
register int i, j;
#ifdef P4DVMA
struct pte pte;
u_long p_addr;
#endif P4DVMA
register struct udc_table *dvma;
long dvmastat = 0;
DPRINTF2("sm_probe: ESP v_base= %x, cpu= %x\n", smr, cpu);
#ifdef P4DVMA
mmu_getkpte((caddr_t)smr, &pte);
DPRINTF1("sm_probe: pfnum= %x\n", pte.pg_pfnum);
p_addr = (u_long)mmu_ptob(pte.pg_pfnum);
DPRINTF1("sm_probe: ESP p_base= %x\n", p_addr);
DEBUG_DELAY(1000000);
TRACE_CHAR(MARK_PROBE);
TRACE_CHAR(cpu);
TRACE_CHAR((char)esp_reg_addr);
TRACE_CHAR((char)((int)esp_reg_addr >> 8));
TRACE_CHAR((char)((int)esp_reg_addr >> 16));
TRACE_CHAR((char)((int)esp_reg_addr >> 24));
#endif P4DVMA
/* for hydra and stingray, the offset of DVMA is 0x1000 from scsi */
/* controller, which will be in the same virtual page */
/* hence we could just add offset to the given "smr" */
switch (cpu) { /* external variable having machine type */
#ifdef P4DVMA
case IDM_SUN3_M25:
DPRINTF("sm_probe: using sun_3/60 offset\n");
#endif P4DVMA
case IDM_SUN4_STING:
DPRINTF("sm_probe: using Stingray offset\n");
dvma = (struct udc_table *)((int)smr + STINGRAY_DMA_OFFSET);
break;
case IDM_SUN3X_HYDRA:
DPRINTF("sm_probe: using Hydra offset\n");
dvma = (struct udc_table *)((int)smr + HYDRA_DMA_OFFSET);
break;
case IDM_SUN4C_CAMPUS:
DPRINTF("sm_probe: using sun_4C offset\n");
/* for now, just add offset, should ask regmap for space */
dvma = (struct udc_table *)((int)smr + CAMPUS_DMA_OFFSET);
break;
default:
EPRINTF1("sm_probe: unknown CPU type= %x\n", cpu);
return(ZERO);
}
DPRINTF2("sm_probe: checking DVMA_base= %x, dma_addr= %x\n",
dvma, &dvma->scsi_dmaaddr);
DEBUG_DELAY(1000000);
/* peek to see DVMA exist */
if ((peekl((long *)&(dvma->scsi_ctlstat), (long *)&(dvmastat))) == -1) {
EPRINTF2("sm_probe: peek on DVMA addr= %x, data= %x FAILED\n",
&(dvma->scsi_ctlstat), dvmastat);
return (ZERO);
}
dvmastat = dvma->scsi_ctlstat;
DPRINTF2("sm_probe: DVMA exists, vm_addr= %x, stat= %x\n",
dvma, dvmastat);
TRACE_CHAR((char)dvmastat);
TRACE_CHAR((char)(dvmastat >> 8));
TRACE_CHAR((char)(dvmastat >> 16));
TRACE_CHAR((char)(dvmastat >> 24));
if (dvmastat & DVMA_REQPEND) {
EPRINTF1("sm_probe: DVMA req_pend stuck, stat= %x\n",
dvmastat);
return (ZERO);
}
if (dvmastat & DVMA_RESET) {
DPRINTF1("sm_probe: DVMA reset asserted, stat= %x\n",
dvmastat);
/* attempt to clear it, if not successful, error return */
dvma->scsi_ctlstat = 0;
DELAY(1000);
if (dvma->scsi_ctlstat & DVMA_RESET) {
EPRINTF1("sm_probe: DVMA reset stuck, stat= %x\n",
dvma->scsi_ctlstat);
return(ZERO);
}
DPRINTF1("sm_probe: deasserted DVMA reset, stat= %x\n",
dvma->scsi_ctlstat);
}
/* write/read/compare DVMA 's address reg */
i = DVMA_TPATTERN;
dvma->scsi_dmaaddr = i;
DPRINTF2("sm_probe: DVMA test, wr= %x, rd= %x\n",
i, dvma->scsi_dmaaddr);
if (dvma->scsi_dmaaddr != i) {
EPRINTF2("sm_probe: DVMA failed, wr= %x, rd= %x\n",
i, dvma->scsi_dmaaddr);
return (ZERO);
}
dvma->scsi_dmaaddr = NULL;
DPRINTF2("sm_probe: DVMA PASSED, stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
DEBUG_DELAY(1000000);
/* check existence and operational status of DVMA before */
/* checking on ESP. If scsi_reset in DVMA is asserted, the */
/* ESP register will not be accessible and DVMA itself is */
/* NOT in a operational state */
c = &smctlrs[ctlr];
/* Check ESP existence -- peek on "ESP reg_xcnt_lo" */
if ((peekc((char *)&(ESP_RD.xcnt_lo))) == -1) {
EPRINTF1("sm_probe: peek on ESP's addr= %x FAILED\n",
&ESP_RD.xcnt_lo);
DEBUG_DELAY(1000000);
return (ZERO);
}
DPRINTF("sm_probe: ESP exist\n");
/* perform a simple write, read and compare validation test to
"ESP reg_conf". If error, return(ZERO) */
i = 0x55;
ESP_WR.conf = i;
j = ESP_RD.conf;
DPRINTF2("sm_probe: ESP wr= %x, rd= %x\n", i, j);
if ((u_char)i != (u_char)j) {
DEBUG_DELAY(1000000);
return (ZERO);
}
i = 0xaa;
ESP_WR.conf = i;
j = ESP_RD.conf;
DPRINTF2("sm_probe: ESP wr: = %x, rd= %x\n", i, j);
if ((u_char)i != (u_char)j) {
DEBUG_DELAY(1000000);
return (ZERO);
}
i = 0;
ESP_WR.conf = i;
j = ESP_RD.conf;
if ((u_char)i != (u_char)j) {
DPRINTF2("sm_probe: ESP FAILED, wr= %x, rd= %x\n", i, j);
DEBUG_DELAY(1000000);
return (ZERO);
}
DPRINTF("sm_probe: ESP testing of reg_conf PASSED\n");
/* As of now, ESP mostly on board, not VME */
c->c_flags = SCSI_ONBOARD;
/* Perform a simple write, read & compare validation test to ESP_II */
i = 0x1f;
ESP_WR.conf2 = (u_char)i; /* write */
j = ESP_RD.conf2; /* read */
#ifdef SMINFO
EPRINTF2("sm_probe: checking ESP2, i= %x, j= %x\n", i, j);
#endif SMINFO
if (i == (j & 0x1f)) { /* compare-only 5 bits are readable */
ESP_WR.conf2 = reg_conf2_val;
c->c_flags |= SCSI_ESP2;
#ifdef SMINFO
EPRINTF1("sm_probe: ESP2 found, conf_2 set to %\x\n",
ESP_RD.conf2);
#endif SMINFO
} else {
ESP_WR.conf2 = 0; /* clear it, since most bit are reserved */
#ifdef SMINFO
EPRINTF1("sm_probe: ESP2 not found, conf_2 set to %x\n",
ESP_RD.conf2);
#endif SMINFO
c->c_flags |= SCSI_ESP;
}
/* Initialize controller (c) structure */
if (scsi_disre_enable) {
DPRINTF("sm_probe: disconnect ENABLED\n");
c->c_flags |= SCSI_EN_DISCON;
} else {
c->c_flags &= ~SCSI_EN_DISCON;
DPRINTF("sm_probe: disconnect DISABLED\n");
}
#ifdef P4DVMA
get_iomapbase();
#endif P4DVMA
DEBUG_DELAY(1000000);
/* Initialize last state log */
for (i = 0; i < NPHASE; i++) {
c->c_phases[i].phase = STATE_FREE;
c->c_phases[i].target = -1;
c->c_phases[i].lun = -1;
}
c->c_phase_index = 0;
c->c_flags |= SCSI_PRESENT;
c->c_reg = (int)smr; /* store ESP register pointer */
c->c_ss = &smsubr; /* set high level subroutine pointer */
c->c_name = "sm"; /* store this driver's name */
c->c_tab.b_actf = NULL;
c->c_tab.b_active = C_INACTIVE;
esp_reg_addr = smr;
dvma_reg_addr = dvma;
smsnap->cur_retry = RESET_ALL_SCSI;
sm_reset(c, NO_MSG);
smsnap->cur_state = STATE_FREE;
c->c_un = NULL;
timeout(sm_watch, (caddr_t)c, 10*hz);
DPRINTF1("sm_probe: returning size= %x\n",
sizeof(struct scsi_sm_reg));
TRACE_CHAR(MARK_PROBE);
DEBUG_DELAY(1000000);
return (sizeof (struct scsi_sm_reg));
}
/*
* See if a slave exists.
* Since it may exist but be powered off, we always say yes.
*/
sm_slave(md, smr)
register struct mb_device *md;
struct scsi_sm_reg *smr;
{
register struct scsi_unit *un;
register int type;
#ifdef lint
smr = smr; /* for compatiblity with all existing sX.c */
#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("sm_slave: unknown type in md_flags\n");
/* link unit to its controller */
un = (struct scsi_unit *)(*scsi_unit_subr[type].ss_unit_ptr)(md);
if (un == NULL)
panic("sm_slave: md_flags scsi type not configured\n");
un->un_c = &smctlrs[md->md_ctlr];
md->md_driver->mdr_dname = scsi_unit_subr[type].ss_devname;
return (1);
}
/*
* Attach device (boot time).
*/
sm_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 = &smctlrs[md->md_ctlr];
DPRINTF1("sm_attach: type= %x\n", type);
if (type >= scsi_ntype)
panic("sm_attach: unknown type in md_flags\n");
c->c_intpri = mc->mc_intpri;
/* Call top-level driver s?attach routine */
(*scsi_unit_subr[type].ss_attach)(md);
/* As this is on-board, there is nothing further do do. */
}
/*
* 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 == ZERO), 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 md is on the scsi_ctlr's disconnect queue c->c_disqtab.
*/
sm_ustart(un)
register struct scsi_unit *un;
{
register struct scsi_ctlr *c = un->un_c;
register struct mb_device *md = un->un_md;
int s;
/* upper level had called diskort() to insert bp into unit md */
DPRINTF("sm_ustart:\n");
s = splr(pritospl(c->c_intpri));
if ((md->md_utab.b_actf != NULL) &&
(md->md_utab.b_active == MD_INACTIVE)) {
/* unit has at least one job before it got put on active list */
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 |= MD_QUEUED;
}
(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).
*/
sm_discon_queue(un)
struct scsi_unit *un;
{
register struct scsi_ctlr *c = un->un_c;
/*DPRINTF1("sm_discon_queue:\n");*/
/* a disconnect has occurred, so mb_ctlr is no longer active */
c->c_tab.b_active &= C_QUEUED;
c->c_un = NULL;
/* remove disconnected unit-md from controller active queue */
if ((c->c_tab.b_actf = (struct buf *)un->un_forw) == NULL)
c->c_tab.b_actl = NULL;
/* put unit-md on controller'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.
*/
sm_recon_queue(c, target, lun, flag)
register struct scsi_ctlr *c;
register short target, lun, flag;
{
register struct scsi_unit *un, *pun;
/*DPRINTF("sm_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) {
if ((un->un_target == target) && (un->un_lun == lun))
break;
}
/*
* Could not find the device in the disconnect queue, die!
* Note, if flag set, just return fail as we're trying
* to figure out if we've started the request or not.
*/
if (un == NULL) {
if (flag)
return (FAIL);
/* dump out disconnnect queue */
printf("sm%d: sm_recon_queue: can't find target= %d, lun= %d\n",
SMNUM(c), target, lun);
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_target= %d, lun= %d\n",
un->un_target, un->un_lun);
}
return (FAIL);
}
/*
* If inhibit reconnect flag set, exit. This is used by deque
* to determine whether to reset everything or ignore timeout.
*/
if (flag)
return(OK);
/* remove un 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;
/* requeue un at the active position of scsi_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;
}
/* scsi_ctlr now has an actively running SCSI command */
c->c_tab.b_active |= C_ACTIVE;
c->c_un = un;
/* If finished flushing disconnect que, clear interlock */
if (c->c_disqtab.b_actf == NULL && (c->c_tab.b_active & C_FLUSHING)) {
c->c_tab.b_active &= ~C_FLUSHING;
printf("sm%d: warning, scsi bus saturated\n", SMNUM(c));
}
return(OK);
}
/* starts the next SCSI command */
sm_start(un)
register struct scsi_unit *un;
{
register struct scsi_ctlr *c;
register struct mb_device *md;
register struct buf *bp;
int s;
TRACE_CHAR(MARK_START);
/*DPRINTF("sm_start:\n");*/
/*return immediately if passed NULL un */
if (un == NULL) {
EPRINTF("sm_start: un NULL\n");
return;
}
/*
* return immediately, if the ctlr is actively running a SCSI cmd.
* Only start a new job if nothing is active or reconnecting.
*/
c = un->un_c;
s = splr(pritospl(c->c_intpri));
if (c->c_tab.b_active != C_INACTIVE) {
DPRINTF1("sm_start: locked (0x%x)\n", c->c_tab.b_active);
(void)splx(s);
return;
}
/*
* if there are currently no SCSI devices queued to run
* a command, then simply return. otherwise, obtain the
* next md for which a command should be run.
*/
if ((un=(struct scsi_unit *)(c->c_tab.b_actf)) == NULL) {
DPRINTF("sm_start: no device to run\n");
(void)splx(s);
return;
}
md = un->un_md;
c->c_tab.b_active |= C_QUEUED;
/*
* 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 sigo directly.
*/
if (md->md_utab.b_active & MD_PREEMPT) {
/*DPRINTF("sm_start: starting preempted\n");*/
c->c_un = un;
sm_go(md);
goto START_RTN;
}
if (md->md_utab.b_active & MD_IN_PROGRESS) {
/*DPRINTF1("sm_start: md in progress\n");*/
goto START_RTN;
}
md->md_utab.b_active |= MD_IN_PROGRESS;
bp = md->md_utab.b_actf; /* put bp into the active position */
md->md_utab.b_forw = bp;
/* Only happens when called by sm_intr */
if (bp == NULL) {
DPRINTF("sm_start: bp is NULL\n");
goto START_RTN;
}
/*
* 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 sm_go() can be called
* for on-line formatting, we need to call mbsetup.
*/
if ((*un->un_ss->ss_start)(bp, 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)) {
sm_go(md);
} else {
if (mbugo(md) == 0)
md->md_utab.b_active |= MD_NODVMA;
}
} else {
/* error from upper level, release md */
sm_done(md);
}
START_RTN:
c->c_tab.b_active &= C_ACTIVE;
(void)splx(s);
}
/*
* Start up a scsi operation.
*/
sm_go(md)
register struct mb_device *md;
{
register struct scsi_ctlr *c;
register struct scsi_unit *un;
register struct buf *bp;
register int i; /* for disk info */
int s, type;
/*DPRINTF("sm_go:\n"); */
if (md == NULL)
panic("sm_go: queueing error1\n");
c = &smctlrs[md->md_mc->mc_ctlr];
s = splr(pritospl(c->c_intpri));
type = TYPE(md->md_flags);
un = (struct scsi_unit *)
(*scsi_unit_subr[type].ss_unit_ptr)(md);
bp = md->md_utab.b_forw;
if (bp == NULL) {
EPRINTF("sm_go: bp is NULL\n");
(void)splx(s);
return;
}
un->un_baddr = MBI_ADDR(md->md_mbinfo);
if (md->md_utab.b_active & MD_NODVMA) {
md->md_utab.b_active &= ~MD_NODVMA;
md->md_utab.b_active |= MD_PREEMPT;
(*un->un_ss->ss_mkcdb)(un);
(void)splx(s);
return;
}
c->c_tab.b_active |= C_QUEUED;
/* Diddle stats if necessary */
if ((i = un->un_md->md_dk) >= 0) {
dk_busy |= 1<<i;
dk_xfer[i]++;
if (bp->b_flags & B_READ)
dk_read[i]++;
dk_wds[i] += 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 (md->md_utab.b_active & MD_PREEMPT)
md->md_utab.b_active &= ~MD_PREEMPT;
else
(*un->un_ss->ss_mkcdb)(un);
if ((i=sm_cmd(c, un, 1)) != OK) {
TRACE_CHAR((char)i);
switch (i) {
case FAIL:
DPRINTF("sm_go: sm_cmd FAILED\n");
(*un->un_ss->ss_intr)(c, 0, SE_RETRYABLE);
break;
case HARD_FAIL:
default:
DPRINTF("sm_go: sm_cmd HARD FAIL\n");
(*un->un_ss->ss_intr)(c, 0, SE_FATAL);
break;
}
}
c->c_tab.b_active &= C_ACTIVE;
(void)splx(s);
}
/* the SCSI command is done, so start up the next command
*/
sm_done(md)
register struct mb_device *md;
{
register struct scsi_ctlr *c;
register struct scsi_unit *un;
register struct buf *bp;
int type;
int s;
TRACE_CHAR(MARK_DONE);
DPRINTF("sm_done:\n");
c = &smctlrs[md->md_mc->mc_ctlr];
s = splr(pritospl(c->c_intpri));
bp = md->md_utab.b_forw;
type = TYPE(md->md_flags);
/* more reliable than un= c->c_un */
/* since c->c_un could be NULL from disconnect */
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_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, sm_ustart will queue up md again (at tail).
* first, need to mark md as inactive (not on queue)
*/
DPRINTF1("sm_done: md_utab= %x\n", md->md_utab.b_active);
md->md_utab.b_active = MD_INACTIVE;
sm_ustart(un); /* requeue unit again if job pending */
iodone(bp);
/* start up the next command on the scsi_ctlr */
sm_start(un);
(void)splx(s);
TRACE_CHAR(MARK_DONE);
}
/*
* Bring a unit offline. Note, if unit already offline, don't print anything.
* If flag = SE_FATAL, take device offline.
*/
sm_off(c, un, flag)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
int flag;
{
struct mb_device *md = un->un_md;
char *msg = "online";
if (un->un_present) {
if (flag == SE_FATAL) {
msg = "offline";
un->un_present = 0;
}
printf("sm%d: %s%d, unit %s\n", SMNUM(c),
scsi_unit_subr[md->md_flags].ss_devname,
un->un_unit, msg);
}
}
/*
* Remove timed-out I/O request and report error to
* it's interrupt handler.
* Return OK if sucessful, FAIL if not.
*/
sm_deque(c, un)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
{
struct udc_table *dvma = dvma_reg_addr;
int s;
/*DPRINTF("sm_deque:\n");*/
/* Lock out the rest of sm till we've finished the dirty work. */
s = splr(pritospl(c->c_intpri));
/*
* If current SCSI I/O request is the one that timed out,
* Reset the SCSI bus as this looks serious.
*/
if (un == c->c_un) {
if (dvma->scsi_ctlstat & DVMA_INT_MASK) {
(void) splx(s);
/* Hardware lost int., attempt to continue... */
printf("sm%d: sm_deque: lost interrupt\n", SMNUM(c));
sm_print_state(c);
sm_intr(c);
return (OK);
} else {
/*
* Really did timeout.
* If things died in the middle of data phase,
* disable sync mode for this target.
*/
if ((c->c_last_phase == STATE_DATA_IN ||
c->c_last_phase == STATE_DATA_OUT) &&
!(sync_target_err & (1 <<un->un_target))) {
sync_target_err |= (1 << un->un_target);
printf("sm%d: target %d reverting to async operation\n",
SMNUM(c), un->un_target);
}
/* If no error to date, report timeout error. */
if (smsnap->cur_err == SE_NO_ERROR)
smsnap->cur_err = SE_TOUTERR;
(void) splx(s);
return (FAIL);
}
}
if (c->c_tab.b_actf == NULL) {
/*
* No active cmd running, search for timed out request
* on disconnect queue. If not found, we're still
* waiting to run it.
*/
if (sm_recon_queue(c, un->un_target, un->un_lun, 0)) {
(void) splx(s);
/* still waiting to run, restart timeout */
if (scsi_debug) {
printf("sm%d: I/O request still waiting to start\n",
SMNUM(c));
sm_print_state(c);
}
return (OK);
}
(void) splx(s);
/* If no error to date, report timeout error. */
if (smsnap->cur_err == SE_NO_ERROR)
smsnap->cur_err = SE_TOUTERR;
/* really did timeout in disconnect que, reset */
(*un->un_ss->ss_intr)(c, un->un_dma_count, SE_TIMEOUT);
c->c_tab.b_active &= C_QUEUED; /* clear interlock */
return (FAIL);
} else {
/*
* Another request is active. If timed out request is
* still waiting to run, restart timeout. If
* it's already disconnected, things get interesting.
* In this case we may have saturated the bus so we stop
* sending further commands and wait for the disconnect
* que to flush. If it doesn't clear, the device really
* timed out.
*/
if (sm_recon_queue(c, un->un_target, un->un_lun, 1)) {
(void) splx(s);
/* died in active que, restart timeout */
if (scsi_debug) {
printf("sm%d: I/O request still waiting to start\n",
SMNUM(c));
sm_print_state(c);
}
return (OK);
}
if (! (c->c_tab.b_active & C_FLUSHING)) {
/* flush disconnect que and see what happens... */
timeout_un = (int)un;
c->c_tab.b_active |= C_FLUSHING;
(void) splx(s);
printf("sm%d: draining disconnect que\n", SMNUM(c));
return (OK);
}
/*
* If another device timed out while we're draining
* the disconnect que, ignore it. If the one we're
* timing out on times out again, die!
*/
if (timeout_un != (int)un && (c->c_tab.b_active & C_FLUSHING)) {
(void) splx(s);
printf("sm%d: drain in progress, restarting timeout\n",
SMNUM(c));
return (OK);
}
printf("sm%d: disconnect que drain failed\n", SMNUM(c));
timeout_un = -1;
c->c_tab.b_active &= ~C_FLUSHING;
/* If no error to date, report timeout error. */
if (smsnap->cur_err == SE_NO_ERROR)
smsnap->cur_err = SE_TOUTERR;
/* died in disconnect que, reset */
(void) splx(s);
return (FAIL);
}
}
/************ HARDWARE DEPENDENT PORTIONS ************************************/
/*
* Pass a command to the SCSI bus.
* OK if fully successful,
* Return FAIL (1) on failure (may be retryable),
* HARD_FAIL (2) if we failed due to major hardware problem
*/
sm_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 ints */
{
struct mb_device *md = un->un_md;
register struct scsi_sm_reg *smr;
register struct udc_table *dvma;
register int i;
register u_char *cp;
int s, l;
smr = esp_reg_addr;
dvma = dvma_reg_addr;
TRACE_CHAR(MARK_CMD);
DPRINTF2("sm_cmd: intr= %x, state= %x\n", intr, smsnap->cur_state);
DPRINTF1("sm_cmd: active= %x\n", c->c_tab.b_active);
DPRINTF2("sm_cmd: dmaaddr= %x, cnt= %x\n",
un->un_dma_addr, un->un_dma_count);
s = splr(pritospl(c->c_intpri));
if ((intr == 1) && (c->c_tab.b_active >= C_ACTIVE)) {
/* if controller just reconnected, preempt this job */
md->md_utab.b_active |= MD_PREEMPT;
DPRINTF1("sm_cmd: current locked out, active= %x\n",
md->md_utab.b_active);
(void)splx(s);
DEBUG_DELAY(1000000);
return (OK);
}
#ifdef notdef
if ((smsnap->cur_state != STATE_FREE) &&
(smsnap->cur_state != STATE_ENRSEL_REQ)) {
EPRINTF2("sm_cmd: state= %x locked out, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
md->md_utab.b_active |= MD_PREEMPT;
(void)splx(s);
DEBUG_DELAY(1000000);
return (OK);
}
#endif notdef
c->c_tab.b_active |= C_ACTIVE;
(void)splx(s);
if (! intr) { /* POLL mode */
/* disable ESP's INT, but DVMA could be enabled later */
c->c_flags &= ~SCSI_EN_DISCON;
dvma->scsi_ctlstat &= ~DVMA_INTEN;
} else {
/*
* If disconnect/reconnect globally disabled or only
* disabled for this command set internal flag.
* Otherwise, we enable disconnects and reconnects.
*/
if (((scsi_disre_enable) == 0) ||
(un->un_flags & SC_UNF_NO_DISCON))
c->c_flags &= ~SCSI_EN_DISCON;
else
c->c_flags |= SCSI_EN_DISCON;
dvma->scsi_ctlstat |= DVMA_INTEN;
un->un_wantint = 1;
}
if (c->c_flags & SCSI_EN_DISCON) {
/* fill in the identify message before cmd bytes */
ESP_WR.fifo_data = DISCON_MSG | (un->un_lun & KEEP_3BITS);
}
/* 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 (((s = sc_cdb_size[CDB_GROUPID(un->un_cdb.cmd)]) == 0) &&
((s = un->un_cmd_len) == 0)) {
s = (int)sizeof(struct scsi_cdb);
DPRINTF2("sm%d: invalid cdb size, using size= %d\n",
SMNUM(c), sizeof (struct scsi_cdb));
}
cp = (u_char *)&un->un_cdb.cmd;
/* Write "ESP reg_fifo" with all command bytes */
DPRINTF("sm_cmd: cmd byte: ");
for (i = 0; (u_char)i < (u_char)s; i++) {
DPRINTF1(" %x ", *cp);
ESP_WR.fifo_data = *cp++;
}
#ifdef notdef
if (smsnap->cur_state == STATE_DSRSEL_DONE) {
ESP_WR.cmd = CMD_EN_RESEL;
DPRINTF("sm_cmd: re-enable resel cmd\n");
}
#endif notdef
DPRINTF1("sm_cmd: un_flag= %x\n", un->un_flags);
DEBUG_DELAY(1000000);
smsnap->cur_err = smsnap->scsi_status = SE_NO_ERROR;
smsnap->cur_state = STATE_SEL_REQ;
smsnap->cur_target = un->un_target;
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] = TAR_START;
#endif SMINFO
#ifdef SMDEBUG
dmainfo[MAXID(un->un_target)].un_addr = un->un_dma_addr;
dmainfo[MAXID(un->un_target)].un_count = un->un_dma_count;
dmainfo[MAXID(un->un_target)].un_flag = un->un_flags;
#endif SMDEBUG
DEBUG_DELAY(1000000);
ESP_WR.busid = un->un_target; /* dest-ID= write only 3 bit encoded */
ESP_WR.timeout = DEF_TIMEOUT;
#ifdef SMSYNC
if (sync_offset[un->un_target]) {
/* If disk changed, force synch negotiation on new one. */
if (un->un_present == 0) {
EPRINTF1("sm_intr: target %d, clearing sync known\n",
un->un_target);
smsnap->sync_known &= ~(1 << un->un_target);
sync_period[un->un_target] = 0;
sync_offset[un->un_target] = 0;
}
ESP_WR.sync_period = sync_period[un->un_target];
ESP_WR.sync_offset = sync_offset[un->un_target];
DPRINTF1("sm_intr: set sync on target= %d\n", un->un_target);
DPRINTF2(" sync_period= %d, offset= %d\n",
sync_period[un->un_target], sync_offset[un->un_target]);
} else {
DPRINTF("sm_intr: no sync\n");
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
}
#endif SMSYNC
/* update ESP sync regs with previous values */
if (c->c_flags & SCSI_EN_DISCON) {
DPRINTF1("\nsm_cmd: sending SEL_ATN cmd to id: %x\n",
un->un_target);
DEBUG_DELAY(1000000);
#ifdef SMSYNC
/*
* If target is there and we haven't checked if it can
* do synchronous (assuming it's enabled), do so now.
* If this is first time we've talked to it, wait until
* the next time to eliminate confusion as whether it's
* there or not.
*/
if ((smsnap->sync_known & (1 << un->un_target)) == 0 &&
un->un_present && scsi_disre_enable > 1) {
DPRINTF1("sm_cmd: start sync_chking on target= %x\n",
un->un_target);
smsnap->cur_state = STATE_SYNCHK_REQ;
ESP_WR.cmd = CMD_SEL_STOP;
} else {
ESP_WR.cmd = CMD_SEL_ATN;
}
#else SMSYNC
ESP_WR.cmd = CMD_SEL_ATN;
#endif SMSYNC
} else {
DPRINTF1("\nsm_cmd: sending SEL_NO_ATN cmd to id: %x\n",
un->un_target);
DEBUG_DELAY(1000000);
ESP_WR.cmd = CMD_SEL_NOATN;
}
SM_LOG_STATE(STATE_ARBITRATE, un->un_target, un->un_lun);
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_cmd: sending cmd= %x to target= %x\n",
un->un_cdb.cmd, un->un_target);
}
#endif SMDEBUG
/* ESP will interrupt if selection timed out */
/* Xfer set up during data phase */
if (intr)
return(OK);
DPRINTF("sm_cmd: POLL mode\n");
TRACE_CHAR(MARK_CMD + 1);
l = LOOP_CMDTOUT; /* relative short timeout */
while ((--l) && (smsnap->cur_err == SE_NO_ERROR) &&
(smsnap->cur_state != STATE_FREE)) {
if (sm_poll()) /* re-allow timeout for any interrupt */
l = LOOP_CMDTOUT;
}
DPRINTF2("sm_cmd: POLL done, state= %x, err= %x\n",
smsnap->cur_state, smsnap->cur_err);
DEBUG_DELAY(1000000);
/* returned check condition status during POLL mode (open time) */
cp = (u_char *)&un->un_scb;
cp[0] = smsnap->scsi_status; /* store the bus status */
DPRINTF2("sm_cmd: scsi_status= %x, dma_stat= %x\n",
smsnap->scsi_status, dvma->scsi_ctlstat);
TRACE_CHAR(smsnap->scsi_status);
TRACE_CHAR(dvma->scsi_ctlstat);
TRACE_CHAR(MARK_CMD);
DEBUG_DELAY(1000000);
if (l == 0) {
i = HARD_FAIL;
} else {
switch (smsnap->cur_err) {
case SE_NO_ERROR:
i = OK; /* 1 */
break;
case SE_RETRYABLE:
i = FAIL; /* 2 */
break;
case SE_FATAL: /* 3 */
default:
DPRINTF2("sm_cmd: REQ failed, err= %x, target= %x\n",
smsnap->cur_err, un->un_target);
DPRINTF2("sm_cmd: CMD= %x, state= %x\n",
un->un_cdb.cmd, smsnap->cur_state);
DPRINTF2("sm_cmd: ESP_stat= %x, scsi_stat= %x\n",
smsnap->esp_stat, smsnap->scsi_status);
DEBUG_DELAY(1000000);
i = HARD_FAIL;
}
}
c->c_tab.b_active &= C_QUEUED; /* leave active bit cleared by sm_start() */
DPRINTF1("sm_cmd: return= %x\n", i);
return(i);
}
/*
* Set up the SCSI-ESP and DVMA logic for a dma transfer
* only called within sm_intr's data_phase
*/
static
sm_dma_setup(c)
register struct scsi_ctlr *c;
{
register struct scsi_unit *un;
register struct scsi_sm_reg *smr;
register struct udc_table *dvma;
#ifdef SMTRACE
u_char *ptr;
int i;
#endif SMTRACE
un = c->c_un;
TRACE_CHAR(MARK_SETUP);
TRACE_CHAR(un->un_dma_count);
if (un->un_dma_count == 0) {
un->un_dma_curcnt = 0;
un->un_dma_curdir = SM_NO_DATA;
DPRINTF("sm_dma_setup: NO data_xfr needed\n");
return;
}
/* ensure the data buffer is within the DVMA space */
if (un->un_dma_addr < (u_int)DVMA) {
un->un_dma_addr += (u_int)DVMA;
DPRINTF2("sm_dma_setup: new_addr: %x, DVMA= %x\n",
un->un_dma_addr, (u_int)DVMA);
}
un->un_dma_curaddr = un->un_dma_addr;
un->un_dma_curcnt = un->un_dma_count;
#ifdef SMDEBUG
if (((un->un_dma_curcnt & 0xf) == 0xf) ||
((un->un_dma_curaddr & 0xf) == 0x1)) {
EPRINTF2("sm_dma_setup: DATA_XFR, addr: %x, cnt= %x\n",
un->un_dma_addr, un->un_dma_count);
esp_debug =1 ;
sm_debug_break(5);
}
#endif SMDEBUG
#ifdef P4DVMA
/* set up I-O mapper for the P4 DVMA-ESP */
set_iomap(un->un_dma_addr, un->un_dma_count);
#endif P4DVMA
#ifdef SMTRACE
DPRINTF1("sm_dma_setup: MEM_addr= %x, data= ", un->un_dma_curaddr);
ptr = (u_char *)un->un_dma_addr;
for (i = 0; i < 20; i++, ptr++) {
DPRINTF1(" %x ", *ptr);
}
DPRINTF("\n");
DEBUG_DELAY(1000000);
#endif SMTRACE
#ifdef DMAPOLL
if (un->un_flags & SC_UNF_RECV_DATA) {
DPRINTF("sm_cmd: setting a POLL-read from SCSI\n");
un->un_dma_curdir = SM_RECV_DATA; /* read */
} else {
/* set write direction */
DPRINTF("sm_cmd: setting a POLL-write to SCSI\n");
un->un_dma_curdir = SM_SEND_DATA;
}
#else DMAPOLL
smr = esp_reg_addr;
dvma = dvma_reg_addr;
dvma->scsi_dmaaddr = un->un_dma_curaddr;
if (un->un_flags & SC_UNF_RECV_DATA) {
DPRINTF("sm_dma_setup: read from SCSI\n");
un->un_dma_curdir = SM_RECV_DATA; /* read */
dvma->scsi_ctlstat |= (DVMA_ENDVMA | DVMA_WRITE); /* to memory */
SM_LOG_STATE(STATE_DATA_IN, un->un_dma_curcnt, -1);
} else {
DPRINTF("sm_dma_setup: write to SCSI\n");
un->un_dma_curdir = SM_SEND_DATA;
dvma->scsi_ctlstat |= DVMA_ENDVMA; /* enable DMA */
SM_LOG_STATE(STATE_DATA_OUT, un->un_dma_curcnt, -1);
}
DPRINTF2("sm_dma_setup: DVMA stat= %x addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
/* DVMA will be enabled for all data transfer */
#endif DMAPOLL
#ifdef not
SET_ESP_COUNT(un->un_dma_curcnt);
#endif not
ESP_WR.xcnt_lo = (u_char)un->un_dma_curcnt;
ESP_WR.xcnt_hi = (u_char)(un->un_dma_curcnt >> 8);
TRACE_CHAR(MARK_SETUP);
DEBUG_DELAY(1000000);
}
/*
* Cleanup up the SCSI-ESP and DVMA logic after a dma transfer
* even with previous errors, however, count will not be updated
*/
static
sm_dma_cleanup(c, espstat)
struct scsi_ctlr *c;
u_char espstat;
{
register struct scsi_unit *un = c->c_un;
register struct scsi_sm_reg *smr;
register struct udc_table *dvma;
u_long dvmastat, l;
u_short req_cnt, dma_cnt, esp_cnt, fifo_cnt;
register u_short xfrcnt;
#ifdef SMTRACE
int i;
u_char *ptr;
#endif SMTRACE
if ((un->un_dma_count == 0) ||
(un->un_dma_curdir == SM_NO_DATA)) {
DPRINTF2("sm_dma_cleanup: No cnt, stat= %x, intr= %x\n",
espstat, smsnap->esp_intr);
return(OK);
}
#ifdef SMTRACE
TRACE_CHAR(MARK_CLEANUP);
DPRINTF1("sm_dma_cleanup: ORG_addr= %x, NEW_data= ",
un->un_dma_addr);
ptr = (u_char *)un->un_dma_addr;
for (i = 0; i < 20; i++, ptr++) {
DPRINTF1(" %x ", *ptr);
}
DPRINTF1("\nsm_dma_cleanup: ORG_cnt= %x\n",
un->un_dma_count);
DEBUG_DELAY(1000000);
#endif SMTRACE
req_cnt = un->un_dma_curcnt;
#ifdef DMAPOLL
if (smsnap->cur_err == SE_NO_ERROR) {
/* update xfrcnt ONLY when there is NO error */
un->un_dma_count = req_cnt;
un->un_dma_addr = un->un_dma_curaddr;
DPRINTF2("sm_dma_cleanup: POLL_dma_addr= %x cnt= %x\n",
un->un_dma_addr, un->un_dma_count);
}
#else
smr = esp_reg_addr;
dvma = dvma_reg_addr;
dvmastat = dvma->scsi_ctlstat;
DPRINTF2("sm_dma_cleanup: dvma_stat= %x, addr= %x\n",
dvmastat, dvma->scsi_dmaaddr);
TRACE_CHAR(dvmastat);
TRACE_CHAR(dvmastat >> 8);
TRACE_CHAR(dvmastat >> 16);
TRACE_CHAR(dvmastat >> 24);
if ((dvmastat & DVMA_ENDVMA) == 0) {
DPRINTF2("sm_dma_cleanup: dvma= %x not enabled, espstat= %x\n",
dvmastat, espstat);
return(OK);
}
if (dvmastat & DVMA_REQPEND) {
dvmastat = LOOP_4SEC; /* must be over 2sec */
while ((dvma->scsi_ctlstat & DVMA_REQPEND) && (--dvmastat)) ;
if (dvmastat == 0) {
EPRINTF1("sm_dma_cleanup: DVMA req_pend err, stat= %x\n",
dvma->scsi_ctlstat);
smsnap->cur_err = SE_REQPEND;
return(FAIL);
}
}
/* need drain when DVMA is write (scsi read) */
if (un->un_dma_curdir == SM_RECV_DATA) { /* DVMA_WRITE */
DPRINTF("sm_dma_cleanup: send DVMA_drain\n");
DEBUG_DELAY(1000000);
/* wait for pack count to be cleared */
dvmastat = 2000;
while ((dvma->scsi_ctlstat & DVMA_PACKCNT) && (--dvmastat)) {
dvma->scsi_ctlstat |= DVMA_DRAIN;
DELAY(200);
}
if ((dvmastat == 0) && (dvma->scsi_ctlstat & DVMA_PACKCNT)) {
EPRINTF2("sm_dma_cleanup: DVMA drain_err, stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
smsnap->cur_err = SE_DVMAERR;
dvma->scsi_ctlstat |= DVMA_FLUSH; /* self-clear */
dvma->scsi_ctlstat &= ~(DVMA_ENDVMA | DVMA_WRITE);
return(FAIL);
}
if (dvma->scsi_ctlstat & DVMA_ERRPEND) {
/* memory exception error */
dvmastat = (u_long)un->un_dma_curaddr & KEEP_24BITS;
dvmastat += (u_long)req_cnt;
l = dvma->scsi_dmaaddr & KEEP_24BITS;
DPRINTF2("sm_dma_cleanup: dvma_reg_addr = %x, addr= %x\n",
l, dvmastat);
if (l < dvmastat) { /* within transfer range */
EPRINTF2("sm_dma_cleanup: DVMA mem_err, stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
smsnap->cur_err = SE_MEMERR;
dvma->scsi_ctlstat |= DVMA_FLUSH; /* self-clear */
dvma->scsi_ctlstat &= ~(DVMA_ENDVMA | DVMA_WRITE);
return(FAIL);
}
}
}
/* need to clear DVMA's read-ahead state machine */
DPRINTF("sm_dma_cleanup: send DVMA_flush\n");
DEBUG_DELAY(1000000);
dvma->scsi_ctlstat |= DVMA_FLUSH; /* self-clear */
/* disable DVMA_xfr */
dvma->scsi_ctlstat &= ~(DVMA_ENDVMA | DVMA_WRITE);
/* see how much DVMA xfr'd */
dvmastat = dvma->scsi_dmaaddr;
dma_cnt = (u_short)dvmastat - un->un_dma_curaddr;
/* see how much ESP xfr'd */
fifo_cnt = ESP_RD.fifo_flag & KEEP_5BITS;
DPRINTF2("sm_dma_cleanup: dma_cnt= %x, fifo_cnt= %x\n",
dma_cnt, fifo_cnt);
/* Figure out how much the esp chip may have xferred
* If the XZERO bit is set, we can assume that the
* ESP xferred all we asked for. */
if (espstat & ESP_STAT_XZERO) {
esp_cnt = req_cnt - fifo_cnt;
} else {
#ifdef not
xfrcnt = GET_ESP_COUNT;
#endif not
xfrcnt = ESP_RD.xcnt_hi << 8;
xfrcnt |= ((u_short)ESP_RD.xcnt_lo & KEEP_LBYTE);
esp_cnt = req_cnt - xfrcnt;
/* correct ESP xfr_cnt reported error */
DPRINTF2("sm_dma_cleanup: espcnt= %x, fifo_flag= %x\n",
esp_cnt, fifo_cnt);
#ifdef SMSYNC
if ((esp_cnt == 0) && (dma_cnt > 16)) {
EPRINTF2("sm_dma_cleanup: cnt= %x, req_cnt= %x,",
un->un_dma_count, req_cnt);
EPRINTF2(" xfrcnt= %x, dma_cnt= %x\n",
xfrcnt, dma_cnt);
esp_cnt = dma_cnt & (0xf);
}
#endif SMSYNC
esp_cnt -= fifo_cnt;
}
/* If we were sending out the scsi bus, then we believe the
* (possibly faked) transfer count register, since the esp
* chip may have prefetched to fill it's fifo. */
if (un->un_dma_curdir == SM_SEND_DATA) {
xfrcnt = esp_cnt;
} else {
/* Else, if we were coming from the scsi bus, we only count
* that which got pumped through the dma engine. */
xfrcnt = dma_cnt;
}
DPRINTF2("sm_dma_cleanup: req_cnt= %x, xfrcnt= %x\n",
req_cnt, xfrcnt);
#endif DMAPOLL
if (fifo_cnt)
ESP_WR.cmd = CMD_FLUSH;
un->un_dma_count = req_cnt - xfrcnt;
un->un_dma_addr = un->un_dma_curaddr + xfrcnt;
#ifdef SMDEBUG
dmainfo[MAXID(un->un_target)].req_addr = un->un_dma_curaddr;
dmainfo[MAXID(un->un_target)].req_cnt = req_cnt;
dmainfo[MAXID(un->un_target)].xfrcnt = xfrcnt;
dmainfo[MAXID(un->un_target)].req_dir = un->un_dma_curdir;
if (esp_debug) {
EPRINTF2("sm_dma_cleanup: final_addr= %x, cnt= %x,",
un->un_dma_curaddr, un->un_dma_curcnt);
EPRINTF2(" DVMA_ctlstat= %x, DVMA_addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
}
#endif SMDEBUG
/* clear current counts */
un->un_dma_curaddr = un->un_dma_curcnt = 0;
un->un_dma_curdir = SM_NO_DATA;
#ifdef SMSYNC
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
#endif SMSYNC
TRACE_CHAR(MARK_CLEANUP);
DEBUG_DELAY(1000000);
return(OK);
}
/*
* Poll and Service a SCSI bus interrupt.
*/
sm_poll()
{
register struct scsi_ctlr *c;
register struct udc_table *dvma = dvma_reg_addr;
int serviced = 0;
u_long dvmastat;
for (c = smctlrs; c < &smctlrs[nsm]; c++) {
if ((c->c_flags & SCSI_PRESENT) == 0)
continue;
dvmastat = dvma->scsi_ctlstat;
if ((dvmastat & DVMA_INT_MASK) == 0)
continue;
DPRINTF1("sm_poll: INT found, dma_stat=%x\n", dvmastat);
serviced = 1;
DEBUG_DELAY(1000000);
sm_intr(c);
}
return (serviced);
}
/*
* Service a scsi interrupt.
*/
sm_intr(c)
register struct scsi_ctlr *c;
{
register struct scsi_sm_reg *smr;
register struct udc_table *dvma;
register struct scsi_unit *un;
register int i, s;
u_char *cp, espstat;
u_long dvmastat, l;
DPRINTF("sm_intr:\n");
smr = esp_reg_addr;
dvma = dvma_reg_addr;
dvma->scsi_ctlstat |= DVMA_INTEN;
INTR_HANDLING:
smsnap->sub_state = TAR_START;
un = c->c_un; /* get the current unit pointer */
TRACE_CHAR(MARK_INTR);
dvmastat = dvma->scsi_ctlstat;
DPRINTF2("sm_intr: dma_stat: %x, un= %x\n", dvmastat, un);
TRACE_CHAR((char)dvmastat);
TRACE_CHAR((char)(dvmastat >> 8));
TRACE_CHAR((char)(dvmastat >> 16));
TRACE_CHAR((char)(dvmastat >> 24));
if ((dvmastat & DVMA_INT_MASK) == 0) { /* INT bit not set */
EPRINTF1("sm_intr: DVMA *NO* int_pend, stat= %x\n", dvmastat);
smsnap->cur_err = SE_SPURINT;
goto INTR_CHK_ERR;
}
/* reading ESP's interrupt register will clear INT source */
smsnap->esp_step = ESP_RD.step & KEEP_3BITS; /* only keep 3 bits */
smsnap->esp_stat = ESP_RD.stat & STAT_RES_MASK;
espstat = smsnap->esp_stat;
#ifdef SMSYNC
if (un && (sync_offset[un->un_target])) {
DPRINTF2("sm_intr_I: stat= %x, state= %x\n",
smsnap->esp_stat, smsnap->cur_state);
}
#endif SMSYNC
smsnap->esp_intr = ESP_RD.intr; /* clear INT */
DPRINTF2("sm_intr: step= %x, stat= %x\n",
smsnap->esp_step, smsnap->esp_stat);
/* check dvma's req_pend, err_pend, and int_pend */
/* should only have int_pend set, if NO error */
if (smsnap->cur_state == STATE_DATA_REQ) {
dvmastat = dvma->scsi_ctlstat;
if (dvmastat & DVMA_CHK_MASK) {
EPRINTF2("sm_intr: DVMA stat_err= %x, intr= %x\n",
dvmastat, smsnap->esp_intr);
smsnap->cur_retry++;
if (dvmastat & DVMA_REQPEND) {
EPRINTF1("sm_intr: DVMA req_pend, stat= %x\n", dvmastat);
/* DMA request pending, should not ocurred */
l = LOOP_4SEC; /* must be over 2sec */
/* wait for a short while to see it will clear itself */
while ((--l) && (dvma->scsi_ctlstat & DVMA_REQPEND));
if (l == 0) {
EPRINTF1("sm_intr: DVMA req_tout, stat= %x\n",
dvma->scsi_ctlstat);
smsnap->cur_err = SE_REQPEND;
goto INTR_CHK_ERR;
}
}
if (dvmastat & DVMA_ERRPEND) { /* memory exception error */
EPRINTF1("sm_intr: DVMA mem_err, stat= %x\n", dvmastat);
dvmastat = (u_long)un->un_dma_curaddr & KEEP_24BITS;
dvmastat += (u_long)un->un_dma_curcnt;
l = dvma->scsi_dmaaddr & KEEP_24BITS;
DPRINTF2("sm_intr: dvma_reg_addr = %x, last_addr= %x\n",
l, dvmastat);
if (l < dvmastat) { /* within transfer range */
EPRINTF2("sm_intr: DVMA mem_err, stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
smsnap->cur_err = SE_MEMERR;
goto INTR_CHK_ERR;
}
}
}
if (espstat & STAT_ERR_MASK) {
EPRINTF2("sm_intr: esp stat_err= %x, phase= %x\n",
espstat, smsnap->cur_state);
/* ESP's status reg ERROR */
if (espstat & ESP_STAT_PERR) {
/* parity error only valid at data phase */
EPRINTF("sm_intr: SCSI bus parity error\n");
smsnap->cur_err = SE_PARITY;
goto INTR_CHK_ERR;
}
if (un != NULL) {
/* if mismatch, the 'DMA" direction is wrong */
if ((((dvma->scsi_ctlstat & DVMA_WRITE) == 0) &&
(un->un_dma_curdir == SM_RECV_DATA)) ||
((dvmastat & DVMA_WRITE) &&
(un->un_dma_curdir == SM_SEND_DATA))) {
EPRINTF2("sm_intr: wrong dma dir= %x, phase= %x\n",
un->un_dma_curdir, smsnap->cur_state);
smsnap->cur_err = SE_DIRERR;
goto INTR_CHK_ERR;
}
#ifdef SMSYNC
/* phase changes during synchronous data transfer */
if (sync_offset[un->un_target] != 0) {
EPRINTF("sm_intr: sync_phase err\n");
smsnap->cur_err = SE_PHASERR;
goto INTR_CHK_ERR;
}
#endif SMSYNC
if ((ESP_RD.fifo_flag & KEEP_5BITS) == MAX_FIFO_FLAG) {
/* fifo overwritten */
EPRINTF1("sm_intr: fifo_flag ERROR= %x\n",
ESP_RD.fifo_flag);
/* the 'TOP of FIFO' had been overwritten */
smsnap->cur_err = SE_FIFOVER;
goto INTR_CHK_ERR;
}
}
/* If none of the above, GROSS error is from cmd overflown */
/* top_of_cmd had been overwritten */
/* ignore this for P4-ESP for timing reason */
if (smsnap->cur_err == SE_NO_ERROR) {
EPRINTF1("sm_intr: curcmd= %x\n", ESP_RD.cmd);
smsnap->cur_err = SE_CMDOVER;
goto INTR_CHK_ERR;
}
}
}
INTR_CHK_STAT:
/* CORRECTON for ESP ERRATA: to ensure phase change detection reset */
smsnap->esp_stat &= ESP_PHASE_MASK;
s = ESP_RD.stat & STAT_RES_MASK;
i = s & ESP_PHASE_MASK; /* check new stat */
if (i != smsnap->esp_stat) {
DPRINTF2("sm_intr: phase changed, old= %x, new= %x\n",
smsnap->esp_stat, i);
/* update both stats */
smsnap->esp_stat = i;
espstat = s;
if (i = ESP_RD.intr) {
DPRINTF2("sm_intr: INTR changed, old= %x, new= %x\n",
smsnap->esp_intr, i);
smsnap->esp_intr = i;
}
}
#ifdef SMDEBUG
if ((smsnap->cur_retry > 10) && (esp_debug)) {
EPRINTF2("sm_intr: NEW stat= %x, state= %x,",
espstat, smsnap->cur_state);
EPRINTF2(" intr= %x, retry= %x\n",
smsnap->esp_intr, smsnap->cur_retry);
sm_debug_break(4);
}
#endif SMDEBUG
if (++smsnap->cur_retry > MAX_RETRY) {
EPRINTF2("sm_intr: err in chk_stat= %x, state= %x,",
espstat, smsnap->cur_state);
EPRINTF2(" intr= %x, retry= %x\n",
smsnap->esp_intr, smsnap->cur_retry);
smsnap->cur_err = SE_PHASERR;
goto INTR_CHK_ERR;
}
TRACE_CHAR(MARK_INTR + 1);
TRACE_CHAR(smsnap->cur_retry);
TRACE_CHAR(smsnap->cur_state);
TRACE_CHAR(smsnap->esp_step);
TRACE_CHAR(espstat);
TRACE_CHAR(smsnap->esp_stat);
TRACE_CHAR(smsnap->esp_intr);
DEBUG_DELAY(1000000);
switch (smsnap->esp_intr) {
case INT_ILL_DISCON: /* discon and illegal cmd INTs */
case INT_ILL_DISCON_OK: /* discon and fcmp, illegal cmd INTs */
case INT_ILL_DISCON_OK1: /* discon and bus, illegal cmd INTS */
DPRINTF2("sm_intr: illegal discon, state= %x, stat= %x\n",
smsnap->cur_state, espstat);
case ESP_INT_DISCON: /* disconnect INT */
smsnap->sub_state |= TAR_DISCON;
switch (smsnap->cur_state) {
case STATE_MSG_DISCON: /* disconnect msg been accepted */
INTR_END_DISCON:
#ifdef SMSYNC
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
DPRINTF2("sm_intr: discon, state= %x, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
#endif SMSYNC
if ((int)++smsnap->num_dis_target > 0) {
ESP_WR.cmd = CMD_EN_RESEL;
DPRINTF1("sm_intr: Enable resel, target= %x\n",
smsnap->num_dis_target);
smsnap->cur_state = STATE_ENRSEL_REQ;
}
sm_discon_queue(un);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_DISCON;
#endif SMINFO
un = c->c_un;
goto INTR_CHK_NEXTINT;
case STATE_MSG_CMPT: /* command completed msg accepted */
INTR_END_CMPT:
#ifdef SMSYNC
ESP_WR.sync_period = ESP_WR.sync_offset = 0;
#endif SMSYNC
/* All ZERO= good, All other, bit 1= check cond, */
/* bit 3= busy, bit 4=rsv conflict are errors */
if (smsnap->scsi_status & SCSI_BUSY_MASK) {
DPRINTF1("sm_intr: bus_stat= %x\n",
smsnap->scsi_status);
#ifdef SMINFO
if (smsnap->scsi_status & 0x2)
target_accstate[MAXID(un->un_target)] |=
TAR_CHK_COND;
if (smsnap->scsi_status & 0x8)
target_accstate[MAXID(un->un_target)] |=
TAR_BUSY;
#endif SMINFO
smsnap->cur_err = SE_RETRYABLE;
}
DPRINTF1("sm_intr: discon_target= %x\n",
smsnap->num_dis_target);
if ((int)smsnap->num_dis_target > (int)0) {
ESP_WR.cmd = CMD_EN_RESEL;
DPRINTF1("sm_intr: Enable resel, target= %x\n",
smsnap->num_dis_target);
}
smsnap->cur_state = STATE_FREE;
case STATE_FREE:
smsnap->sub_state |= TAR_JOBDONE;
/* Entire job is completed, clear job now */
goto INTR_JOB_DONE;
case STATE_SEL_REQ: /* selection command timeout */
case STATE_SYNCHK_REQ: /* sync_selection cmd timeout */
DPRINTF2("sm_intr: cmd_tout, phase= %x, step= %x\n",
smsnap->cur_state, smsnap->esp_step);
smsnap->cur_err = SE_SELTOUT;
goto INTR_CHK_ERR;
case STATE_ENRSEL_REQ: /* reselection timed out */
DPRINTF2("sm_intr: cmd_tout, phase= %x, step= %x\n",
smsnap->cur_state, smsnap->esp_step);
smsnap->cur_err = SE_RSELTOUT;
goto INTR_CHK_ERR;
#ifdef SMSYNC
case STATE_MSGOUT_REQ: /* reselection timed out */
case STATE_SYNC_DONE:
DPRINTF2("sm_intr: msg_out, state= %x, stat= %x\n",
smsnap->cur_state, ESP_RD.stat);
DPRINTF2("sm_intr: un= %x, espstat= %x\n",
un, smsnap->esp_stat);
if (un != NULL)
DPRINTF2("sm_intr: curcnt= %x, count= %x\n",
un->un_dma_curcnt, un->un_dma_count);
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
#endif SMSYNC
case STATE_MSGIN_REQ:
case STATE_MSG_REJECT:
EPRINTF2("sm_intr: reject job, c_active= %x, cnt= %x,",
c->c_tab.b_active, un->un_dma_count);
EPRINTF2(" state= %x, m_active= %x\n",
smsnap->cur_state, un->un_md->md_utab.b_active);
/* preempt the job now, and retry later */
un->un_md->md_utab.b_active |= MD_PREEMPT;
un->un_wantint = 0;
c->c_tab.b_active = C_INACTIVE;
smsnap->cur_state = STATE_FREE;
if ((int)smsnap->num_dis_target > 0) {
ESP_WR.cmd = CMD_EN_RESEL;
smsnap->cur_state = STATE_ENRSEL_REQ;
}
goto INTR_CHK_NEXTINT;
default:
/* could be from target dropping BSY during data xfr */
EPRINTF("sm_intr: lost busy\n");
if (smsnap->cur_state == STATE_DATA_REQ) {
smsnap->cur_err = SE_LOST_BUSY;
(void) sm_dma_cleanup(c, espstat);
goto INTR_CHK_ERR;
}
if (smsnap->cur_state == STATE_MSGOUT_DONE) {
EPRINTF1("msg_out done, msg= %x\n",
smsnap->scsi_message);
#ifdef SMDEBUG
sm_debug_break(40);
#endif SMDEBUG
if (smsnap->scsi_message == SC_DISCONNECT)
goto INTR_END_DISCON;
else
goto INTR_END_CMPT;
}
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
}
case ESP_INT_ILLCMD: /* illegal command */
case INT_ILL_BUS: /* illegal cmd/bus INTS */
case INT_ILL_FCMP: /* illegal cmd/fcmp INTS */
DPRINTF2("sm_intr: illegal esp_cmd= %x, int= %x\n",
ESP_RD.cmd, smsnap->esp_intr);
DPRINTF2("sm_intr: pre_phase= %x, stat= %x\n",
smsnap->cur_state, espstat);
DPRINTF2("sm_intr: dmastat= %x, curcmd= %x\n",
dvma->scsi_ctlstat, ESP_RD.cmd);
if (smsnap->cur_state == STATE_FREE) {
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: err data phase, state= %x, flag= %x\n",
smsnap->cur_state, ESP_RD.fifo_flag);
EPRINTF2("sm_intr: stat= %x, cmd= %x\n",
ESP_RD.stat, ESP_RD.cmd);
}
#endif SMDEBUG
if ((int)smsnap->num_dis_target > 0) {
ESP_WR.cmd = CMD_EN_RESEL;
DPRINTF1("sm_intr: Enable resel, target= %x\n",
smsnap->num_dis_target);
smsnap->cur_state = STATE_ENRSEL_REQ;
}
goto INTR_CHK_NEXTINT;
}
/* fall down to continue */
case ESP_INT_BUS: /* Bus-service Int, command completed */
/* unless msg received and ACK still asserted */
case ESP_INT_FCMP: /* Function-completed Int */
/* with msg received and ACK being asserted */
case INT_OK_MASK: /* both FCMP and BUS Ints are set */
DPRINTF2("sm_intr: ESP f_cmp/b_service INT, stat= %x, state= %x\n",
ESP_RD.stat, smsnap->cur_state);
switch (smsnap->cur_state) {
case STATE_DSRSEL_REQ:
DPRINTF("sm_intr: resel disabled\n");
smsnap->cur_state = STATE_DSRSEL_DONE;
goto INTR_CHK_NEXTINT;
case STATE_SYNCHK_REQ:
ESP_WR.cmd = CMD_NOP;
ESP_WR.cmd = CMD_FLUSH;
DPRINTF2("sm_intr: sync_step= %x, stat= %x\n",
smsnap->esp_step, smsnap->esp_stat);
/* fall to msg_out phase */
break;
case STATE_SEL_REQ:
smsnap->cur_retry = 0;
/*
* Do not flush out fifo here due to sync data might
* be already in it.
*/
DPRINTF2("sm_intr: sel_done, step= %x, stat= %x\n",
smsnap->esp_step, espstat);
DPRINTF2("sm_intr: target= %x, lun= %x\n",
un->un_target, un->un_lun);
/* both sel w/wo ATN need 4 steps to complete OK */
/* only sel w/ATN/stop is 3 steps to complete OK */
smsnap->sub_state |= TAR_SELECT;
#ifdef SMSYNC
/* update ESP sync regs with previous values */
if (sync_offset[un->un_target]) {
ESP_WR.sync_period = sync_period[un->un_target];
ESP_WR.sync_offset = sync_offset[un->un_target];
DPRINTF2("sm_int_S: sync_period= %d, off= %d\n",
sync_period[un->un_target], sync_offset[un->un_target]);
DPRINTF1("sm_intr: set sync after SEL on target= %x\n",
un->un_target);
} else {
/*
* With sync_enabled, need to program
* sync_offset when mixing sync/async devices.
*/
DPRINTF1("sm_intr: set async on target %d\n",
un->un_target);
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
}
#endif SMSYNC
switch (smsnap->esp_step) {
case FOUR:
/* arb, sel, msg_out, cmd phases=ok */
SM_LOG_STATE(STATE_COMMAND, un->un_cdb.cmd, -1);
break;
case ONE:
/*
* SEL_W/WO_ATN: no such step sequence for async.
* May be from Synch check though.
*/
#ifdef SMSYNC
if ((smsnap->sync_known & (1 << un->un_target))
== 0) {
DPRINTF2("sm_intr: do sync_query, stat= %x, target= %x\n",
smsnap->esp_stat, un->un_target);
smsnap->cur_state = STATE_SYNCHK_REQ;
/* fall to msg_out phase */
goto INTR_CHK_STAT;
}
DPRINTF1("sm_intr: sync_sel, target= %x\n",
un->un_target);
/*
* For sync testing, arb & sel=ok, 1 msg byte out.
* Let it fall through to handle the sync
* negotiation.
*/
break;
#endif SMSYNC
/* Let it fall to check status */
case TWO:
/*
* arb, sel=ok, 1 msg byte out, no cmd bytes out.
* If async, same case as step=three.
*/
case THREE:
/*
* Arb, sel, msg_out=ok, cmd bytes not all out.
* If status phase, let it fall through to get
* target's check condition, etc.
*/
SM_LOG_STATE(STATE_COMMAND, un->un_cdb.cmd, -1);
if ((smsnap->esp_stat == ESP_PHASE_STATUS) ||
(smsnap->esp_stat == ESP_PHASE_DATA_IN) ||
(smsnap->esp_stat == ESP_PHASE_DATA_OUT))
break;
default:
/*
* All others: no such step sequence.
* case ZERO: arb=ok, sel=fail
* should not happen, should be disconnect INTR.
*/
EPRINTF2("sm_intr: bad sel_step= %x, stat= %x\n",
smsnap->esp_step, smsnap->esp_stat);
smsnap->cur_err = SE_ILLCMD;
goto INTR_CHK_ERR;
}
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_SELECT;
#endif SMINFO
break;
case STATE_ATN_REQ:
/* ATN remain asserted util last msg byte */
/* fifo should be at top after flush */
DPRINTF1("sm_intr: Send abort_msg, state= %x\n",
smsnap->cur_state);
/* send abort_msg */
if (smsnap->cur_state == STATE_DATA_REQ) {
DPRINTF("sm_intr: try to clean up xfr\n");
if (sm_dma_cleanup(c, espstat))
goto INTR_CHK_ERR;
}
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
ESP_WR.fifo_data = SC_ABORT;
ESP_WR.cmd = CMD_SET_ATN;
ESP_WR.cmd = CMD_TRAN_INFO;
smsnap->cur_state = STATE_MSGOUT_REQ;
goto INTR_CHK_NEXTINT;
case STATE_MSG_DISCON:
#ifdef SMDEBUG
EPRINTF2("sm_intr: race_cond, target= %x, intr= %x,",
un->un_target, smsnap->esp_intr);
EPRINTF2(" cmd= %x, stat= %x\n",
ESP_RD.cmd, smsnap->esp_stat);
#endif SMDEBUG
break;
case STATE_MSG_CMPT:
#ifdef SMDEBUG
EPRINTF2("sm_intr: race_cond1, target= %x, cmd= %x,",
un->un_target, ESP_RD.cmd);
EPRINTF2(" stat= %x, intr= %x\n",
smsnap->esp_stat, smsnap->esp_intr);
#endif SMDEBUG
break;
default:
break; /* ignore and fall down */
}
TRACE_CHAR(MARK_INTR + 3);
#ifdef SMDEBUG
if (ESP_RD.stat & 0x80) {
EPRINTF2("sm_intr: f_cmp INT, stat= %x, state= %x\n",
ESP_RD.stat, smsnap->cur_state);
sm_debug_break(30);
esp_debug = 1;
}
#endif SMDEBUG
switch (smsnap->esp_stat) { /* current SCSI bus phase */
case ESP_PHASE_DATA_IN: /* either data in or out */
case ESP_PHASE_DATA_OUT: /* either data in or out */
smsnap->sub_state |= TAR_DATA;
#ifdef SMSYNC
if (smsnap->cur_state == STATE_DATA_REQ) {
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: data, state= %x, offset= %x\n",
smsnap->cur_state, sync_offset[un->un_target]);
EPRINTF2("sm_intr: dma_addr= %x, DVMA_addr= %x\n",
un->un_dma_curaddr, dvma->scsi_dmaaddr);
sm_debug_break(6);
}
#endif SMDEBUG
if (sync_offset[un->un_target]) {
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: DATA_stat= %x, flag= %x\n",
espstat, i);
EPRINTF2("sm_intr: ORIG cnt= %x, addr= %x\n",
un->un_dma_curcnt, un->un_dma_curaddr);
EPRINTF2("sm_intr: DVMA stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
EPRINTF2("sm_intr: ESP cnt_hi= %x, lo= %x\n",
ESP_RD.xcnt_hi, ESP_RD.xcnt_lo);
}
#endif SMDEBUG
s = 0;
i = ESP_RD.fifo_flag;
espstat = ESP_RD.stat & STAT_RES_MASK;
smsnap->esp_stat = espstat & ESP_PHASE_MASK;
if (smsnap->esp_stat == ESP_PHASE_DATA_IN) {
if (i & 0x1f) { /* stat= 1 */
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: sync_in stat= %x, byte= %x\n",
espstat, i);
EPRINTF2("sm_intr: ORIG cnt= %x, count= %x\n",
un->un_dma_curcnt, un->un_dma_count);
}
#endif SMDEBUG
/* DVMA already enabled */
ESP_WR.xcnt_lo = (u_char)(i & KEEP_5BITS);
ESP_WR.cmd = CMD_TRAN_INFO | CMD_DMA;
EPRINTF2("sm_intr: NEW flag= %x, stat= %x\n",
ESP_RD.fifo_flag, ESP_RD.stat);
goto INTR_CHK_NEXTINT;
}
#ifdef SMDEBUG
if (esp_debug) {
DPRINTF2("sm_intr: no new REQ, flag= %x, stat= %x\n",
i, espstat);
}
#endif SMDEBUG
s++;
} else {
if (smsnap->esp_stat == ESP_PHASE_DATA_OUT) {
if (i & 0x20) {
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: sync_out stat= %x, byte= %x\n",
espstat, i);
EPRINTF2("sm_intr: ORIG cnt= %x, addr= %x\n",
un->un_dma_curcnt, un->un_dma_curaddr);
EPRINTF2("sm_intr: DVMA stat= %x, addr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
EPRINTF2("sm_intr: ESP cnt_hi= %x, lo= %x\n",
ESP_RD.xcnt_hi, ESP_RD.xcnt_lo);
}
#endif SMDEBUG
/* all dma setting is still in effect */
ESP_WR.cmd = CMD_TRAN_INFO | CMD_DMA;
EPRINTF2("sm_intr: NEW flag= %x, stat= %x\n",
ESP_RD.fifo_flag, ESP_RD.stat);
goto INTR_CHK_NEXTINT;
}
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF2("sm_intr: sync_off flag= %x, stat= %x\n",
i, espstat);
}
#endif SMDEBUG
s++;
} else {
#ifdef SMDEBUG
EPRINTF2("sm_intr: wrong phase= %x, flag= %x\n",
smsnap->esp_stat, ESP_RD.fifo_flag);
#endif SMDEBUG
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
}
}
if (s) { /* clear for the long REQ */
#ifdef SMDEBUG
if (esp_debug)
EPRINTF("sm_intr: wait for next valid REQ\n");
#endif SMDEBUG
ESP_WR.cmd = CMD_MSG_ACPT;
/* It turns out that this can also come about
* if the target resets (and goes back to async
* SCSI mode) and we don't know about it.
* The degenerate case for this is turning off
* a lunchbox- this clears it's state. The
* trouble is is that we'll get a check
* condition (likely) on the next command
* after a power-cycle for this target, but
* we'll have to go into a DATA IN phase to
* pick up the sense information for the
* Request Sense that will likely follow that
* Check Condition. As a temporary hack, I'll
* clear the 'sync_known' flag for this
* target so that the next selection for this
* target will renegotiate the sync protocol
* to be followed. */
smsnap->sync_known &= ~(1 << un->un_target);
goto INTR_CHK_NEXTINT;
}
}
#ifdef SMDEBUG
if (esp_debug)
EPRINTF("sm_intr: re-check phase again\n");
#endif SMDEBUG
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
}
#endif SMSYNC
if ((un->un_dma_count == 0) ||
(smsnap->cur_state & STATE_MSG_CMPT) ||
(smsnap->cur_state == STATE_COMP_REQ)) {
if (smsnap->cur_state == STATE_COMP_REQ) {
#ifdef SMDEBUG
EPRINTF2("sm_intr: D_err, cnt= %x, flag= %x\n",
ESP_RD.xcnt_lo, ESP_RD.fifo_flag);
EPRINTF2("sm_intr: stat= %x, cnt= %x\n",
ESP_RD.stat, un->un_dma_count);
#endif SMDEBUG
goto INTR_MSG_COMP;
} else {
DPRINTF2("sm_intr: intr= %x, un_target= %x\n",
smsnap->esp_intr, un->un_target);
if (smsnap->esp_intr == ESP_INT_FCMP) {
#ifdef SMDEBUG
EPRINTF1("sm_intr: race_cond2, state= %x\n",
smsnap->cur_state);
#endif SMDEBUG
ESP_WR.cmd = CMD_MSG_ACPT;
}
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
}
}
#ifdef SMSYNC
if (sync_offset[un->un_target]) {
ESP_WR.sync_period = sync_period[un->un_target];
ESP_WR.sync_offset = sync_offset[un->un_target];
DPRINTF1("sm_intr: set sync on target= %d\n",
un->un_target);
} else {
DPRINTF("sm_intr: set no sync on ESP\n");
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
}
#endif SMSYNC
/* valid dma xfr, enable dvma and R/W*/
/* un could be NULL from "c->c_un", */
/* which was disconnected earlier */
sm_dma_setup(c);
#ifdef DMAPOLL
ESP_WR.xcnt_lo = ESP_WR.xcnt_hi = 0;
#ifdef not
SET_ESP_COUNT(0);
#endif not
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
DPRINTF1("sm_intr_X: cur fifo_flag= %x\n",
ESP_RD.fifo_flag);
/* ESP's xfr cntr has been set up with dmacnt */
cp = (u_char *)un->un_dma_addr;
s = un->un_dma_curcnt;
DPRINTF2("sm_intr_X: XFR_addr= %x, cnt= %x\n",
cp, s);
l = LOOP_4SEC;
/* ESP xcnters had been set up ... NO DMA */
if (un->un_dma_curdir == SM_RECV_DATA) {
DPRINTF("sm_intr: DMA_POLL receive data=");
ESP_WR.cmd = CMD_TRAN_INFO;
while (--l) {
if (ESP_RD.fifo_flag) {
i = ESP_RD.fifo_data;
*cp++ = (char)i;
DPRINTF1(" %x", i);
i = ESP_RD.stat & STAT_RES_MASK;
prev_prt_err = ESP_RD.intr;
s--;
if (s == 0)
break;
ESP_WR.cmd = CMD_TRAN_INFO;
}
}
DPRINTF2("\nsm_intr: R_remain cnt= %x, flag= %x",
s, ESP_RD.fifo_flag);
DPRINTF2("\nsm_intr: intr= %x, stat= %x",
prev_prt_err, i);
if (un->un_cdb.cmd == SC_REQUEST_SENSE)
/* even not enough bytes, force DONE */
s = 0;
un->un_dma_curaddr +=
(un->un_dma_curcnt - s);
un->un_dma_curcnt = s;
DPRINTF2("\nsm_intr: XFR_done, adr: %x, cnt= %x\n",
un->un_dma_curaddr, un->un_dma_curcnt);
} else { /* write */
DPRINTF("sm_intr: DMA-POLL sending data\n");
i = *cp++;
ESP_WR.fifo_data = (char)i;
ESP_WR.cmd = CMD_TRAN_INFO;
DPRINTF1(" %x", i);
while (--l) {
if (ESP_RD.fifo_flag == 0) {
i = ESP_RD.stat & STAT_RES_MASK;
prev_prt_err = ESP_RD.intr;
s--;
if (s == 0)
break;
i = *cp++;
ESP_WR.fifo_data = (char)i;
DPRINTF1(" %x", i);
ESP_WR.cmd = CMD_TRAN_INFO;
}
}
DPRINTF2("\nsm_intr: W_remain cnt= %x, flag= %x",
s, ESP_RD.fifo_flag);
DPRINTF2("\nsm_intr: intr= %x, stat= %x",
prev_prt_err, ESP_RD.stat);
un->un_dma_curaddr
+= (un->un_dma_curcnt - s);
un->un_dma_curcnt = s;
DPRINTF2("\nsm_intr: XFR_done, adr: %x, cnt= %x\n",
un->un_dma_addr, un->un_dma_curcnt);
}
if (l == 0 && s) {
DPRINTF1("sm_intr: XFR_tout, cnt=%x\n", s);
i = ESP_RD.stat & ESP_PHASE_MASK;
DPRINTF2("sm_intr: XFR_tout, flag: %x, stat= %x\n",
ESP_RD.fifo_flag, i);
if (i == smsnap->esp_stat) {
EPRINTF2("sm_intr: XFR_tout, flag: %x, stat= %x\n",
ESP_RD.fifo_flag, i);
smsnap->cur_err = SE_TOUTERR;
goto INTR_CHK_ERR;
}
espstat = ESP_RD.stat & STAT_RES_MASK;
smsnap->esp_stat = i;
smsnap->esp_intr = ESP_RD.intr;
}
DPRINTF("sm_intr: POLL_xfr done, chk next_stat\n");
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
#else DMAPOLL
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_DATA;
#endif SMINFO
/* add in per ESP errata note */
/* ESP transfer counters had been set up */
ESP_WR.cmd = CMD_NOP | CMD_DMA;
smsnap->cur_state = STATE_DATA_REQ;
TRACE_CHAR(MARK_INTR);
DPRINTF2("sm_intr: start dma, adr= %x, adr= %x\n",
dvma->scsi_ctlstat, dvma->scsi_dmaaddr);
ESP_WR.cmd = CMD_TRAN_INFO | CMD_DMA;
goto INTR_CHK_NEXTINT;
#endif DMAPOLL
case ESP_PHASE_STATUS:
SM_LOG_STATE(STATE_STATUS, smsnap->scsi_status, -1);
smsnap->sub_state |= TAR_STATUS;
DPRINTF2("sm_intr: status phase, state= %x, stat= %x\n",
smsnap->cur_state, espstat);
if (smsnap->cur_state == STATE_DATA_REQ) {
if (sm_dma_cleanup(c, espstat))
goto INTR_CHK_ERR;
} else {
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
}
#ifdef SMSYNC
/* target rejected sync query */
if (smsnap->cur_state == STATE_SYNCHK_REQ) {
ESP_WR.cmd = CMD_TRAN_INFO;
smsnap->scsi_status = ESP_RD.fifo_data;
TRACE_CHAR(MARK_INTR + 5);
TRACE_CHAR(smsnap->scsi_status);
TRACE_CHAR(smsnap->scsi_message);
DPRINTF1("sm_intr_S: sync_BUS_ERR stat= %x\n",
smsnap->scsi_status);
s = ESP_RD.stat & STAT_RES_MASK;
espstat = s;
smsnap->esp_stat = s & ESP_PHASE_MASK;
smsnap->esp_intr = ESP_RD.intr;
DPRINTF2("sm_intr_S: stat= %x, intr= %x\n",
smsnap->esp_stat, smsnap->esp_intr);
if (smsnap->esp_intr == ESP_INT_FCMP) {
ESP_WR.cmd = CMD_MSG_ACPT;
}
smsnap->cur_state = STATE_SYNC_DONE;
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
} else {
DPRINTF("sm_intr: send complete_seq\n");
ESP_WR.cmd = CMD_COMP_SEQ;
}
#else SMSYNC
DPRINTF("sm_intr: send complete_seq\n");
ESP_WR.cmd = CMD_COMP_SEQ;
#endif SMSYNC
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_STATUS;
#endif SMINFO
smsnap->cur_state = STATE_COMP_REQ;
/* target has status and message */
DEBUG_DELAY(1000000);
break;
case ESP_PHASE_MSG_IN:
INTR_MSG_COMP:
#ifdef SMDEBUG
DPRINTF2("sm_intr: msg_in phase, state= %x, flag= %x\n",
smsnap->cur_state, ESP_RD.fifo_flag);
#endif SMDEBUG
smsnap->sub_state |= TAR_MSGIN;
DPRINTF2("sm_intr: espstat= %x, stat= %x\n",
espstat, ESP_RD.stat);
switch (smsnap->cur_state) {
case STATE_COMP_REQ: /* two byte available */
smsnap->scsi_status = ESP_RD.fifo_data;
case STATE_MSGIN_REQ: /* only one byte */
smsnap->scsi_message = ESP_RD.fifo_data;
break; /* two bytes available */
case STATE_SYNCHK_REQ:
DPRINTF1("sm_intr: sync_chk msg_in flag= %x\n",
ESP_RD.fifo_flag);
ESP_WR.cmd = CMD_NOP;
ESP_WR.cmd = CMD_FLUSH;
goto INTR_MSG_IN;
case STATE_MSG_REJECT:
DPRINTF1("sm_intr: sync msg_in flag= %x\n",
ESP_RD.fifo_flag);
DELAY(100);
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
case STATE_MSGOUT_DONE:
DPRINTF1("sm_intr: sync_msg bytes= %x\n",
ESP_RD.fifo_flag);
smsnap->scsi_message = ESP_RD.fifo_data;
break;
case STATE_MSG_DISCON:
DPRINTF1("sm_intr: msg_discon, int= %x\n",
smsnap->esp_intr);
if (smsnap->esp_intr == ESP_INT_BUS)
ESP_WR.cmd = CMD_MSG_ACPT;
ESP_WR.cmd = CMD_NOP;
goto INTR_END_DISCON;
case STATE_MSG_CMPT:
DPRINTF1("sm_intr: msg_cmpt, int= %x\n",
smsnap->esp_intr);
if (smsnap->esp_intr == ESP_INT_BUS)
ESP_WR.cmd = CMD_MSG_ACPT;
ESP_WR.cmd = CMD_NOP;
goto INTR_END_CMPT;
case STATE_DATA_REQ:
DPRINTF2("sm_intr: msg_in_data, stat= %x, cnt= %x\n",
dvma->scsi_ctlstat, un->un_dma_curcnt);
if (sm_dma_cleanup(c, espstat))
goto INTR_CHK_ERR;
/* fall through */
default:
/* get message byte */
INTR_MSG_IN:
s = ESP_RD.fifo_flag & KEEP_5BITS;
DPRINTF2("sm_intr: msg_in state= %x, flag= %x\n",
smsnap->cur_state, s);
if (s > 1) {
#ifdef SMDEBUG
EPRINTF2("sm_intr: msg_in flag= %x, state= %x\n",
s, smsnap->cur_state);
EPRINTF2("sm_intr: stat= %x, intr= %x\n",
smsnap->esp_stat, smsnap->esp_intr);
#endif SMDEBUG
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
/* let it falls */
}
if (s == 1) {
DPRINTF2("sm_intr: sync_msg, state= %x, byte= %x\n",
smsnap->cur_state, smsnap->scsi_message);
smsnap->scsi_message = ESP_RD.fifo_data;
} else {
DPRINTF1("sm_intr: SYNC_state= %x\n",
smsnap->cur_state);
smsnap->cur_state = STATE_MSGIN_REQ;
ESP_WR.cmd = CMD_TRAN_INFO;
goto INTR_CHK_NEXTINT;
}
}
DPRINTF2("sm_intr: status= %x, message= %x\n",
smsnap->scsi_status, smsnap->scsi_message);
DEBUG_DELAY(1000000);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_MSGIN;
#endif SMINFO
/* send a message accpt cmd at the END */
switch (smsnap->scsi_message) {
case SC_COMMAND_COMPLETE:
SM_LOG_STATE(STATE_MSG_CMPT,
un->un_dma_count, -1);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_COMP_MSG;
#endif SMINFO
DPRINTF("sm_intr: Cmd_comp_msg\n");
smsnap->cur_state = STATE_MSG_CMPT;
break;
case SC_NO_OP:
SM_LOG_STATE(STATE_MSG_NOP, -1, -1);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_COMP_MSG;
#endif SMINFO
smsnap->cur_state = STATE_MSG_CMPT;
/* save the scsi_status for later on */
/* a disconnect INT should come after */
/* sending out "msg_accpt" cmd */
/* at that time, we are TOTALLY done */
break;
case SC_DISCONNECT: /* target wanted to disconnect */
SM_LOG_STATE(STATE_MSG_DISCON, -1, -1);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_DIS_MSG;
#endif SMINFO
DPRINTF("sm_intr: Discon_msg\n");
/* no need to call sm_disconnect() since */
/* SAVE_PTR msg will update un_dma_info */
smsnap->cur_state = STATE_MSG_DISCON;
break;
case SC_SAVE_DATA_PTR:
SM_LOG_STATE(STATE_MSG_SAVE_PTR, un->un_dma_count, -1);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_SAVE_MSG;
#endif SMINFO
DPRINTF2("sm_intr: Saveptr_msg, stat= %x, adr= %x\n",
ESP_RD.stat, dvma->scsi_dmaaddr);
smsnap->cur_state = STATE_SPEC_SAVEPTR;
break;
case SC_RESTORE_PTRS:
SM_LOG_STATE(STATE_MSG_RESTORE_PTR, -1, -1);
DPRINTF("sm_intr: Restore_ptr_msg\n");
/* NORMALLY no need to restore pointer, since
data phase will set thing up */
/* if target see parity errors, it sends back */
/* restore_msg, from cmd_phase, data_phase */
smsnap->cur_state = STATE_SPEC_RESTORE;
break;
/* TBD: for NOW, just lock all other error message */
case SC_PARITY:
SM_LOG_STATE(STATE_MSG_PARITY, -1, -1);
EPRINTF("sm_intr: Parity_err_msg\n");
smsnap->cur_err = SE_PARITY;
smsnap->cur_state = STATE_MSG_PARITY;
break;
case SC_EXTENDED_MESSAGE:
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_EXT_MSG;
#endif SMINFO
DPRINTF1("sm_intr: extended_msg, state= %x\n",
smsnap->cur_state);
#ifdef SMSYNC
DPRINTF("sm_intr: send msg_acpt\n");
ESP_WR.cmd = CMD_MSG_ACPT;
ESP_WR.cmd = CMD_NOP; /* needed */
smsnap->cur_state = STATE_MSG_SYNC;
dvmastat = LOOP_1SEC;
s = 4;
/* fifo_data[0] is sync_msg */
fifo_data[0] = smsnap->scsi_message;
cp = (u_char *)&fifo_data[1];
while (s && (--dvmastat)) {
DELAY(10); /* needed for Quantum */
if (i = ESP_RD.intr) {
DPRINTF2("sm_intr: Xint= %x, flag= %x\n",
i, ESP_RD.fifo_flag);
if (i == ESP_INT_FCMP)
ESP_WR.cmd = CMD_MSG_ACPT;
if (i == ESP_INT_BUS)
ESP_WR.cmd = CMD_TRAN_INFO;
DELAY(10);
if (ESP_RD.fifo_flag & KEEP_5BITS) {
i = ESP_RD.fifo_data;
*cp++ = (u_char)i;
DPRINTF2("sm_intr: Mdata= %x, flag= %x\n",
i, ESP_RD.fifo_flag);
s--;
}
}
}
i = ESP_RD.stat & STAT_RES_MASK;
s = ESP_RD.intr;
DPRINTF2("sm_intr: S_end, stat= %x, intr= %x\n",
i, s);
if (dvmastat == 0) {
DPRINTF("sm_intr: SYNC not supported\n");
sync_offset[un->un_target] = 0;
} else {
/* fifo_data[0] has sync_msg */
DPRINTF2("sm_intr: msg_len= %x, code= %x\n",
fifo_data[1], fifo_data[2]);
DPRINTF2("sm_intr: period= %x, offset= %x\n",
fifo_data[3], fifo_data[4]);
if (fifo_data[1] != SC_EXT_LENGTH) {
EPRINTF1("sm_intr: bad sync_len= %x\n",
fifo_data[1]);
}
switch (fifo_data[2]) {
case ZERO: /* modify data ptr */
i = ((int)fifo_data[3] & KEEP_LBYTE) << 24;
i |= ((int)fifo_data[4] & KEEP_LBYTE) << 16;
i |= ((int)fifo_data[5] & KEEP_LBYTE) << 8;
i |= ((int)fifo_data[6] & KEEP_LBYTE);
DPRINTF1("sm_intr: M_ptr, value= %x\n", i);
/* add SIGNED argument to current cnt */
un->un_dma_addr += i;
un->un_dma_curcnt += i;
SM_LOG_STATE(STATE_MSG_EXTEND, fifo_data[2],-1);
break;
case SC_SYNCHRONOUS: /* 1= sync xfr code */
if (sm_set_sync(un))
goto INTR_MSG_REJECT;
ESP_WR.sync_period =
sync_period[un->un_target];
ESP_WR.sync_offset =
sync_period[un->un_target];
SM_LOG_STATE(STATE_MSG_SYNC,
un->un_target, -1);
break;
default:
SM_LOG_STATE(STATE_MSG_EXTEND, fifo_data[2],-1);
EPRINTF1("sm_intr: Bad sync_msg, d2= %x\n",
fifo_data[2]);
INTR_MSG_REJECT:
/* send reject msg */
DPRINTF("sm_intr_R: reject sync_msg\n");
ESP_WR.cmd = CMD_SET_ATN;
ESP_WR.cmd = CMD_MSG_ACPT;
smsnap->cur_state = STATE_MSGOUT_REQ;
}
}
DPRINTF1("sm_intr: sync_known on target= %x done\n",
un->un_target);
smsnap->sync_known |= (1 << un->un_target);
smsnap->cur_state = STATE_SYNC_DONE;
break;
#else SMSYNC
EPRINTF("sm_intr: sync_msg not supported\n");
INTR_MSG_REJECT:
/* force it goes to msg_out */
DPRINTF("sm_intr: reject sync_msg\n");
ESP_WR.cmd = CMD_SET_ATN;
ESP_WR.cmd = CMD_MSG_ACPT;
smsnap->cur_state = STATE_MSGOUT_REQ;
goto INTR_CHK_STAT;
#endif SMSYNC
case SC_DEVICE_RESET:
SM_LOG_STATE(STATE_MSG_RESET, -1, -1);
EPRINTF("sm_intr: RESET_err msg\n");
smsnap->cur_state = STATE_RESET;
smsnap->cur_err = SE_RESET;
break;
case SC_ABORT:
SM_LOG_STATE(STATE_MSG_ABORT, -1, -1);
EPRINTF("sm_intr: ABORT_err msg\n");
smsnap->cur_state = STATE_MSG_ABORT;
goto INTR_MSG_REJECT;
case SC_IDENTIFY:
case SC_DR_IDENTIFY:
SM_LOG_STATE(STATE_MSG_IDENT, -1, -1);
EPRINTF2("sm_intr: ident_msg %x, phase= %x\n",
smsnap->scsi_message, smsnap->cur_state);
EPRINTF1("sm_intr: fifo_flag= %x\n",
ESP_RD.fifo_flag);
break; /* ignored */
case SC_MSG_REJECT:
SM_LOG_STATE(STATE_MSG_REJECT, -1, -1);
#ifdef SMSYNC
if ((smsnap->sync_known & (1 << un->un_target))
== 0) {
/*
* Target does not support SYNC transfers.
* Set to ASYNC and set target
* sync_checking done.
*/
EPRINTF1("sm_intr: asynch only, target= %d\n",
un->un_target);
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
sync_period[un->un_target] = 0;
sync_offset[un->un_target] = 0;
smsnap->sync_known |= (1 << un->un_target);
} else {
EPRINTF2("sm_intr: msg reject, flag= 0x%x, target= %x\n",
ESP_RD.fifo_flag, un->un_target);
}
#endif SMSYNC
smsnap->cur_state = STATE_MSG_REJECT;
break;
/*case SC_LINK_CMD_CPLT:*/
/*case SC_FLAG_LINK_CMD_CPLT:*/
default:
SM_LOG_STATE(STATE_MSG_UNKNOWN,
smsnap->scsi_message, -1);
EPRINTF2("sm_intr: unsupported_msg: %x, phase= %x\n",
smsnap->scsi_message, smsnap->cur_state);
/* accept the message, but send an abort msg */
EPRINTF1("sm_intr: fifo_flag= %x\n", ESP_RD.fifo_flag);
/* send reject msg */
ESP_WR.fifo_data = SC_MSG_REJECT;
ESP_WR.cmd = CMD_SET_ATN;
ESP_WR.cmd = CMD_TRAN_INFO;
smsnap->cur_state = STATE_MSGOUT_REQ;
goto INTR_CHK_NEXTINT;
}
DPRINTF("sm_intr: Send msg_acpt\n");
/* send a message accpt cmd */
ESP_WR.cmd = CMD_NOP; /* needed */
ESP_WR.cmd = CMD_MSG_ACPT;
goto INTR_CHK_NEXTINT; /* will result bus_int */
case ESP_PHASE_MSG_OUT:
#ifdef SMDEBUG
if (esp_debug) {
EPRINTF1("msg_out, un= %x\n", un);
sm_debug_break(38);
}
#endif SMDEBUG
if ((un == NULL) || (smsnap->cur_state == STATE_ENRSEL_REQ))
goto INTR_CLEAR_MSGOUT;
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_MSGOUT;
#endif SMINFO
DPRINTF2("sm_intr: sync_known= %x, target= %x\n",
smsnap->sync_known, un->un_target);
#ifdef SMSYNC
/* check the target been query on sync */
if ((smsnap->sync_known & (1 << un->un_target)) == 0) {
ESP_WR.fifo_data = SC_EXTENDED_MESSAGE;
ESP_WR.fifo_data = SC_EXT_LENGTH;
ESP_WR.fifo_data = SC_SYNCHRONOUS;
ESP_WR.fifo_data = DEF_SYNC_PERIOD;
ESP_WR.fifo_data = DEF_SYNC_OFFSET;
SM_LOG_STATE(STATE_MSG_SYNC, -1, -1);
DPRINTF2("sm_intr: sync_query, flag= %x, state= %x\n",
ESP_RD.fifo_flag, smsnap->cur_state);
/* keep cur_state as synchk-req */
} else {
INTR_CLEAR_MSGOUT:
smsnap->cur_state = STATE_MSGOUT_DONE;
/* with sync support, send a NOP to clear the msg_out */
/* without this, we need a way to enable sync_offset */
DPRINTF2("sm_intr: msg_out, state= %x, flag= %x\n",
smsnap->cur_state, ESP_RD.fifo_flag);
ESP_WR.fifo_data = SC_NO_OP;
ESP_WR.cmd = CMD_SET_ATN;
}
ESP_WR.cmd = CMD_TRAN_INFO;
goto INTR_CHK_NEXTINT;
#else SMSYNC
INTR_CLEAR_MSGOUT:
/* could be from parity error */
EPRINTF2("sm_intr: unexpected msg_out, state= %x, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
ESP_WR.fifo_data = SC_ABORT;
ESP_WR.cmd = CMD_SET_ATN;
ESP_WR.cmd = CMD_TRAN_INFO;
smsnap->cur_state = STATE_MSGOUT_REQ;
goto INTR_CHK_NEXTINT;
}
#endif SMSYNC
case ESP_PHASE_COMMAND:
SM_LOG_STATE(STATE_COMMAND, un->un_cdb.cmd, -1);
DPRINTF2("sm_intr: command phase, state= %x, flag= %x\n",
smsnap->cur_state, ESP_RD.fifo_flag);
#ifdef SMSYNC
if (smsnap->cur_state != STATE_CMD_DONE) {
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
/* attempt to send command bytes out */
cp = (u_char *)&un->un_cdb.cmd;
if (((s = sc_cdb_size[CDB_GROUPID(un->un_cdb.cmd)])
== 0) && ((s = un->un_cmd_len) == 0))
s = (int)sizeof(struct scsi_cdb);
DPRINTF1("sm_intr: sync_cmd, cnt= %x\n", s);
for (i = 0; (u_char)i < (u_char)s; i++) {
DPRINTF1(" %x ", *cp);
ESP_WR.fifo_data = *cp++;
}
ESP_WR.cmd = CMD_TRAN_INFO;
smsnap->cur_state = STATE_CMD_DONE;
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_COMMAND;
#endif SMINFO
}
goto INTR_CHK_NEXTINT;
#else
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_COMMAND;
#endif SMINFO
ESP_WR.cmd = CMD_NOP;
goto INTR_CHK_STAT;
#endif SMSYNC
default: /* all others are bad, just set PHASE error */
EPRINTF1("sm_intr: bad phase: %x\n", smsnap->cur_state);
smsnap->cur_err = SE_PHASERR;
}
TRACE_CHAR(MARK_INTR + 4);
break;
case INT_ILL_RESEL: /* illegal cmd/resel */
case INT_ILL_RESEL_OK: /* illegal cmd/resel/fcmp */
case INT_ILL_RESEL_OK1: /* illegal cmd/resel/bus */
DPRINTF2("sm_intr: illegal resel= %x, int= %x\n",
ESP_RD.cmd, smsnap->esp_intr);
DPRINTF2("sm_intr: pre_phase= %x, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
/* fall down to continue */
case ESP_INT_RESEL: /* reselection INT */
case INT_RESEL_OK:
switch (smsnap->cur_state) {
case STATE_SEL_REQ:
case STATE_SYNCHK_REQ:
DPRINTF2("sm_intr: sel_collison, stat= %x, flag= %x\n",
ESP_RD.stat, ESP_RD.fifo_flag);
/* do not read off fifo_data, since we might have */
/* all nine bytes - select and reselect data */
/* but we must clear the "illegal interrupt */
DPRINTF1("\nsm_intr: collison_intr= %x\n", s);
/* leave the c_tab.b_active to continue on reselection */
un->un_md->md_utab.b_active |= MD_PREEMPT;
un->un_wantint = 0;
/* fall down */
case STATE_DSRSEL_REQ:
DPRINTF2("sm_intr: resel_cont, state= %x, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
s = ESP_RD.intr;
DPRINTF2("sm_intr: intr= %x, flag= %x\n",
s, ESP_RD.fifo_flag);
/* fall down */
case STATE_FREE:
case STATE_ENRSEL_REQ:
smsnap->sub_state = TAR_RESELECT;
/* Get target's identify message from "ESP reg_fifo" */
/* two bytes in fifo, (bus id) & (identify message+lun) */
/* target id is NOT ENCODED */
DPRINTF1("sm_intr: resel fifo_flag= %x\n",
ESP_RD.fifo_flag);
DPRINTF1("sm_intr: recon_int, state= %x\n",
smsnap->cur_state);
s = smsnap->scsi_message = ESP_RD.fifo_data;
for (i = 0; i < 7; i++) {
if (s & 1)
break;
s >>= 1;
}
/* If bus id = host id, we're in trouble. */
if ((smsnap->scsi_message & ~scsi_host_id) == 0) {
EPRINTF1("sm_intr: recon_host_err, id= %x\n",
i);
SM_LOG_STATE(STATE_BAD_RESELECT, -1, -1);
smsnap->cur_err = SE_RECONNECT;
goto INTR_CHK_ERR;
}
s = ESP_RD.fifo_data; /* ident + lun */
/* no need to service the interrupt */
if ((s & SC_IDENTIFY) == 0) {
EPRINTF1("sm_intr: bad resel ident_msg= %x\n", s);
SM_LOG_STATE(STATE_BAD_RESELECT, i, -1);
smsnap->cur_err = SE_RECONNECT;
goto INTR_CHK_ERR;
}
s &= KEEP_3BITS; /* lun */
SM_LOG_STATE(STATE_RESELECT, i, s);
/* attempt to reconnect previous disconnected job */
DPRINTF2("sm_intr: reselected id = %x, lun= %x\n",
c->c_recon_target, i);
DEBUG_DELAY(1000000);
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
#ifdef SMSYNC
if (sync_offset[i]) {
DPRINTF2("sm_intr: sync_RESEL on target= %x, stat= %x\n",
i, smsnap->esp_stat);
ESP_WR.sync_period = sync_period[i];
ESP_WR.sync_offset = sync_offset[i];
TRACE_CHAR(MARK_INTR);
} else {
DPRINTF("sm_intr: set no sync on ESP after resel\n");
ESP_WR.sync_period = 0;
ESP_WR.sync_offset = 0;
}
#endif SMSYNC
ESP_WR.cmd = CMD_MSG_ACPT;
c->c_recon_target = i; /* target */
if (sm_recon_queue(c, (short)i, (short)s, 0)) {
smsnap->cur_err = SE_RECONNECT;
goto INTR_CHK_ERR;
}
un = c->c_un;
smsnap->cur_state = STATE_RESEL_DONE;
smsnap->num_dis_target--;
DPRINTF1("sm_intr: reselect_OK, flag= %x\n",
ESP_RD.fifo_flag);
DEBUG_DELAY(1000000);
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] = TAR_RESELECT;
#endif SMINFO
smsnap->cur_retry = 0;
smsnap->cur_target = un->un_target;
goto INTR_CHK_NEXTINT;
default:
EPRINTF1("sm_intr: Bad resel_state= %x\n",
smsnap->cur_state);
smsnap->cur_err = SE_PHASERR;
goto INTR_CHK_ERR;
}
case ESP_INT_RESET: /* external scsi bus reset */
EPRINTF("sm_intr: external scsi bus reset\n");
smsnap->cur_err = SE_RESET;
goto INTR_CHK_ERR;
case ESP_INT_SELATN: /* selected with ATN -- target cmd */
case ESP_INT_SEL: /* selected without ATN -- target cmd */
default:
EPRINTF1("sm_intr: bad interrupt= %x\n", smsnap->esp_intr);
smsnap->cur_err = SE_PHASERR;
}
INTR_CHK_ERR:
TRACE_CHAR(MARK_INTR + 6);
TRACE_CHAR(smsnap->cur_err);
/* Just simply pass the status to upper level callers */
if (smsnap->cur_err) {
/* for ERROR case, no DMA update */
#ifdef SMSYNC
DPRINTF2("sm_intr: ERR= %x, target= %x\n",
smsnap->cur_err, un->un_target);
DPRINTF2("sm_intr: state= %x, stat= %x\n",
smsnap->cur_state, smsnap->esp_stat);
#endif SMSYNC
DEBUG_DELAY(1000000);
/*
* For PARITY or any error, HOST can assert ATN with
* msg=ABORT to let target know the error condition,
* other than just RESET.
*/
switch (smsnap->cur_err) {
case SE_SELTOUT: /* selection timed out */
DPRINTF2("sm_intr: selection timeout, step= %x, cmd= %x,",
smsnap->esp_step, ESP_RD.cmd);
DPRINTF2(" stat= %x, intr= %x\n",
smsnap->esp_stat, smsnap->esp_intr);
ESP_WR.cmd = CMD_FLUSH;
ESP_WR.cmd = CMD_NOP;
#ifdef SMSYNC
sync_period[un->un_target] = ESP_WR.sync_period = 0;
sync_offset[un->un_target] = ESP_WR.sync_offset = 0;
/*
* If synch check fails and we've been using it
* earlier, it must not like synch messages.
* Otherwise, it's not there....
*/
if (smsnap->cur_state == STATE_SYNCHK_REQ &&
un->un_present) {
/* Device doesn't like sych message! */
if (scsi_debug) {
printf("sm%d: target %d reverting to async operation\n",
SMNUM(c), un->un_target);
}
smsnap->sync_known |= 1 << un->un_target;
smsnap->cur_err = SE_RETRYABLE;
} else {
/* Device is not there, take it offline. */
smsnap->cur_err = SE_FATAL;
sm_off(c, un, SE_FATAL);
smsnap->sync_known &= ~(1 << un->un_target);
}
#else SMSYNC
/* Device is not there, take it offline. */
smsnap->cur_err = SE_FATAL;
sm_off(c, un, SE_FATAL); /* take unit offline */
#endif SMSYNC
break;
case SE_RECONNECT: /* failed to reconnect job */
case SE_RSELTOUT: /* reselection timed out */
case SE_RESET: /* external reset detected */
case SE_PARITY: /* internal/external parity ERR */
case SE_FIFOVER: /* ESP status error, fifo overflow */
case SE_CMDOVER: /* ESP status error, command overflow */
smsnap->cur_retry = RESET_INT_ONLY;
sm_reset(c, PRINT_MSG);
smsnap->cur_err = SE_RETRYABLE; /* retry later */
break;
case SE_MEMERR: /* memory exception error during DMA */
case SE_DVMAERR: /* DVMA Drain stuck */
case SE_MSGERR: /* ESP has un-expected message */
case SE_PHASERR: /* ESP has un-expected phase */
case SE_LOST_BUSY: /* lost busy */
case SE_TOUTERR: /* timeout error */
smsnap->cur_retry = RESET_ALL_SCSI;
sm_reset(c, PRINT_MSG);
smsnap->cur_err = SE_RETRYABLE;
break;
case SE_ILLCMD: /* illegal command detected */
case SE_DIRERR: /* DMA direction, could trash MEM */
case SE_REQPEND: /* DMA's request pending stuck */
smsnap->cur_retry = RESET_ALL_SCSI;
sm_reset(c, PRINT_MSG);
smsnap->cur_err = SE_FATAL;
break;
case SE_SPURINT:
default:
printf("sm%d: spurious interrupt\n", SMNUM(c));
sm_print_state(c);
smsnap->cur_err = SE_NO_ERROR;
goto INTR_CHK_NEXTINT;
}
smsnap->cur_state = STATE_FREE;
}
DPRINTF2("sm_intr: end_err= %x, state= %x\n",
smsnap->cur_err, smsnap->cur_state);
DEBUG_DELAY(1000000);
INTR_JOB_DONE:
if ((smsnap->cur_state == STATE_FREE) && (un != NULL)) {
/* return scsi bus status (chk condition, etc) */
cp = (u_char *)&un->un_scb;
cp[0] = smsnap->scsi_status;
c->c_tab.b_active &= C_QUEUED; /* release queue lock */
DPRINTF1("sm_intr: scsi_status= %x\n", smsnap->scsi_status);
if (un->un_wantint) { /* error or not */
un->un_wantint = 0;
/* final errors= NO_ERROR, TIMEOUT, RETRYABLE, FATAL */
/* iodone() will be indirectly called by upper-level*/
#ifdef SMDEBUG
if (esp_debug) {
if (un->un_dma_count) {
EPRINTF2("sm_intr: ret_resid= %x, flag= %x\n",
un->un_dma_count, un->un_flags);
}
EPRINTF2("sm_intr: job done, estat= %x, stat= %x\n",
smsnap->esp_stat, ESP_RD.stat);
}
#endif SMDEBUG
#ifdef SMINFO
target_accstate[MAXID(un->un_target)] |= TAR_JOBDONE;
#endif SMINFO
(*un->un_ss->ss_intr)(c, un->un_dma_count,
smsnap->cur_err);
DEBUG_DELAY(1000000);
if ((smsnap->cur_err) &&
((int)smsnap->num_dis_target > (int)0)) {
ESP_WR.cmd = CMD_EN_RESEL;
/*
* Need to re-send EN_RESEL cmd, since ESP will
* disable reselection after completing reconnection.
*/
DPRINTF1("sm_intr: num_dis_target= %x\n",
smsnap->num_dis_target);
/*
* Set ESP's internal mode to respond target
* reselection.
*/
smsnap->cur_state = STATE_ENRSEL_REQ;
/* NO interrupt will be generated from this command */
}
smsnap->cur_err = SE_NO_ERROR;
}
/*
* Re-enable RESEL after a completion of current command
* and there was a disconnect still pending.
*/
un = c->c_un;
smsnap->cur_retry = 0;
} /* All other states skip clearing active */
INTR_CHK_NEXTINT:
smsnap->sub_state &= ~TAR_START;
/* ONLY after job completed, check had previous job waiting on RESEL */
/* if so, turn on "en_resel" to pick up target's reconnection */
/* un could be NULL from flushing thorugh sm_idle */
TRACE_CHAR(MARK_INTR + 7);
TRACE_CHAR(smsnap->num_dis_target);
/* STAYING in SERVICE ROUTINE to shorten the interrupt respond time */
if (dvma->scsi_ctlstat & DVMA_INTPEND) {
DPRINTF1("sm_intr: new INT pending, dma_stat= %x\n",
dvma->scsi_ctlstat);
goto INTR_HANDLING;
}
TRACE_CHAR(MARK_INTR + 8);
TRACE_CHAR(smsnap->cur_state);
DPRINTF1("sm_intr: done, phase= %x\n", smsnap->cur_state);
/* un could be NULL from "c->c_un", which was disconnected earlier */
/* un checking has been added in sm_start() as of 9/1/88 */
if ((smsnap->cur_state == STATE_FREE) ||
(smsnap->cur_state == STATE_ENRSEL_REQ)) {
un = (struct scsi_unit *)c->c_tab.b_actf;
if (un != NULL)
sm_start(un); /* ALL done, fire up the next job */
}
TRACE_CHAR(MARK_INTR);
}
/*
* 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.
*/
sm_idle(c, flag)
register struct scsi_ctlr *c;
int flag;
{
register struct scsi_unit *un;
int i, s;
DPRINTF("sm_idle:\n");
if (c->c_flags & SCSI_FLUSHING) {
DPRINTF1("sm_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) {
DPRINTF("sm_idle: flushing disconnect que\n");
s = splr(pritospl(c->c_intpri)); /* time critical */
c->c_tab.b_active = C_ACTIVE; /* engage interlock */
/*
* Force current I/O request to be preempted and put it
* on disconnect que so we can flush it.
*/
un = (struct scsi_unit *)c->c_tab.b_actf;
if ((un != NULL) &&
(un->un_md->md_utab.b_active & MD_IN_PROGRESS)) {
EPRINTF("sm_idle: dequeued active req\n");
sm_discon_queue(un);
}
/* now in process of flushing tasks */
c->c_flags &= ~SCSI_FLUSH_DISQ;
c->c_flags |= SCSI_FLUSHING;
c->c_flush = c->c_disqtab.b_actl;
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;
/* requeue on controller active queue */
if (sm_recon_queue(c, un->un_target, un->un_lun, 0))
continue;
i = un->un_dma_curcnt;
DPRINTF2("sm_idle: target= %d, lun= %d,",
un->un_target, un->un_lun);
DPRINTF2(" state= 0x%x, resid= %d\n",
smsnap->cur_state, i);
(*un->un_ss->ss_intr)(c, i, flag);
sm_off(c, un, flag);
}
c->c_flags &= ~SCSI_FLUSHING;
c->c_tab.b_active &= C_QUEUED; /* Clear interlock */
(void) splx(s);
}
}
/*
* Return residual count for a dma.
*/
sm_dmacnt(c)
struct scsi_ctlr *c;
{
return (c->c_un->un_dma_count);
}
/* only two arguments, per upper target driver */
sm_reset(c, msg_enable)
register struct scsi_ctlr *c;
int msg_enable; /* for printing error infos */
{
register struct scsi_sm_reg *smr;
register struct udc_table *dvma;
register int i, j;
TRACE_CHAR(MARK_RESET);
TRACE_CHAR(smsnap->cur_state);
smr = esp_reg_addr;
dvma = dvma_reg_addr;
if (dvma->scsi_ctlstat & DVMA_REQPEND) {
sm_print_state(c);
smsnap->cur_state = STATE_DVMA_STUCK;
smsnap->cur_retry = RESET_EXT_ONLY;
}
/* write ESP reg_conf to disable RESET_INT */
ESP_WR.conf = ESP_CONF_DISRINT;
switch (smsnap->cur_retry) {
case RESET_EXT_ONLY:
if (msg_enable || scsi_debug) {
printf("sm%d: resetting scsi bus\n", SMNUM(c));
sm_print_state(c);
}
ESP_WR.cmd = CMD_RESET_ESP; /* hard-reset ESP chip */
ESP_WR.cmd = CMD_NOP; /* needed for ESP bug */
ESP_WR.cmd = CMD_RESET_SCSI;
ESP_WR.cmd = CMD_NOP; /* NOP needed for ESP bug */
SM_LOG_STATE(STATE_RESET, smsnap->cur_retry, 0);
DELAY(scsi_reset_delay); /* Allow reset recovery time */
break;
case RESET_INT_ONLY:
/* if being called by upper level, just do internal reset */
if (scsi_debug || (msg_enable && smsnap->cur_err != SE_RESET)) {
printf("sm%d: resetting interface\n", SMNUM(c));
sm_print_state(c);
}
/* DVMA_EN = ZERO, INTEN=ZERO, FLUSH=1 */
dvma->scsi_ctlstat = DVMA_FLUSH; /* soft dvma clear */
DELAY(10);
dvma->scsi_ctlstat = 0; /* disable int. */
/* NO ints */
ESP_WR.cmd = CMD_RESET_ESP; /* hard-reset ESP chip */
ESP_WR.cmd = CMD_NOP; /* needed for ESP bug */
SM_LOG_STATE(STATE_RESET, 0, 1);
if (smsnap->cur_err != SE_RESET)
dvma->scsi_ctlstat = DVMA_INTEN; /* enable int. */
break;
case RESET_ALL_SCSI:
default:
if (msg_enable) {
printf("sm%d: resetting scsi bus\n", SMNUM(c));
sm_print_state(c);
}
/* implied a reset to both ESP and SCSI bus */
dvma->scsi_ctlstat = DVMA_RESET; /* hard DVMA reset */
DELAY(10);
dvma->scsi_ctlstat = 0; /* clear reset */
ESP_WR.cmd = CMD_RESET_SCSI;
ESP_WR.cmd = CMD_NOP; /* NOP needed for ESP bug */
SM_LOG_STATE(STATE_RESET, -1, -1);
DELAY(scsi_reset_delay); /* Allow reset recovery time */
break;
}
i = ESP_RD.intr; /* clear reset interrupt */
/* ESP_WR.cmd = CMD_NOP; /* needed after reset */
DPRINTF1("sm_reset: esp_intr after reset= %x\n", i);
ESP_WR.clock_conv = DEF_CLK_CONV; /* 5 (@24Mhz); 4 @20Mhz */
ESP_WR.timeout = DEF_TIMEOUT; /* 93 (hex) (250msec@24Mhz) */
ESP_WR.sync_period = 0; /* async */
ESP_WR.sync_offset = 0; /* async */
/* Enable parity checking, enable reset_int, and set host bus_id. */
j = 1;
for (i = 0; i < NTARGETS -1; i++) {
if (scsi_host_id == j)
break;
j <<= 1;
}
ESP_WR.conf = i;
DPRINTF1("sm_reset: scsi_host_id= %d\n", i);
/*
* Set up some default sm_snap. Variables cur_err, cur_retry,
* and scsi_status will be cleared in sm_cmd.
*/
smsnap->num_dis_target = smsnap->cur_retry = 0;
smsnap->scsi_message = SC_COMMAND_COMPLETE;
smsnap->cur_state = smsnap->sub_state = STATE_FREE;
/*
* Force sync negotiate on all targest except ones which timed out
* in data phase on us.
*/
smsnap->sync_known = sync_target_err;
#ifdef SMSYNC
for (i = 0; i < NTARGETS -1; i++) {
#ifdef SMINFO
target_accstate[i] = 0;
#endif SMINFO
sync_period[i] = 0; /* clear sync period reg */
sync_offset[i] = 0; /* initaially set ASYNC xfr */
}
#endif SMSYNC
/* set ESP's internal mode bit to respond target reselection */
TRACE_CHAR(dvma->scsi_ctlstat);
#ifdef SMDEBUG
DPRINTF2("sm_reset: actf= %x, discon_actf= %x\n",
c->c_tab.b_actf, c->c_disqtab.b_actf);
#endif SMDEBUG
if ((c->c_disqtab.b_actf != NULL) || (c->c_tab.b_actf != NULL)) {
/* both active and disconnect queue needs to be flushed */
c->c_flags |= SCSI_FLUSH_DISQ;
DPRINTF1("sm_reset: flushing discon_queue, sync_known= %x\n",
smsnap->sync_known);
sm_idle(c, SE_TIMEOUT);
}
TRACE_CHAR(MARK_RESET);
DEBUG_DELAY(1000000);
}
sm_set_sync(un)
struct scsi_unit *un;
{
u_int period, offset, tickval = 0, regval = 0;
period = fifo_data[3] & 0xff;
offset = fifo_data[4] & 0xff;
DPRINTF2("sm_set_sync: target's negotiate_vaule= %xh, off= %x\n",
period, offset);
if (offset == 0) { /* async */
sync_offset[un->un_target] = 0;
EPRINTF1("sm_set_sync: target %d set to async\n",
un->un_target);
return(OK);
}
if (offset > DEF_SYNC_OFFSET) { /* 15 */
EPRINTF1("sm_set_sync: sync offset (%d) out of range\n", offset);
return(FAIL);
} else {
/* if not, check sync period value */
if ((period > MAX_SYNC_PERIOD) || (period < MIN_SYNC_PERIOD)) {
EPRINTF1("sm_set_sync: sync period (%d) out of range\n",
period);
return(FAIL);
} else {
/*
* Conversion method for received PERIOD value to the
* number of input clock ticks to the ESP.
*
* We adjust the input period value such that we always
* will transmit data *not* faster than the period
* value received.
*/
regval = CLOCK_PERIOD;
DPRINTF1("sm_set_sync: ESP clock_period= %d\n",
regval);
DPRINTF2("sm_set_sync: host_period (min-max)= %d nsec\n",
MIN_SYNC_CYL * regval, MAX_SYNC_CYL *regval);
while(((period<<2)+tickval) % regval)
tickval++;
DPRINTF2("sm_set_sync: target req_period= %d(nsec), diff= %d\n",
period << 2, tickval);
tickval = ((period<<2)+tickval) / regval;
/*
* Now, we have a good integer number of input ESP
* clock ticks that will guarantee safe transfer
* to the target. We have to now figure out what
* sync period register value to use. The ESP period
* register is a pretty strange beast. We also have
* to change the value if we are in 'Dirty Cable'
* mode (set in the configuration register).
*/
if (tickval < MIN_SYNC_CYL) {
regval = MIN_SYNC_CYL;
} else {
if (tickval & ~0x1f) {
regval = tickval & 0x1f;
} else {
regval = tickval;
}
sync_period[un->un_target] = regval;
sync_offset[un->un_target] = offset;
DPRINTF3("sm_set_sync: SET for target(%d)..sync_period= %d, offset= %d\n",
un->un_target, sync_period[un->un_target], sync_offset[un->un_target]);
}
}
}
return(OK);
}
/* add here for compatiblity with existing SCSI drivers */
sm_getstatus()
{
}
sm_cmd_wait()
{
}
#ifdef P4DVMA
/* special routine for the P4-I/O mapper and debugg trigger */
#define P4IOMAP_ADDR 0xff200000
#define P4IOMAP_MASK 0xffffe000
#define P4IOMAP_DTMASK 0xfffffff8
#define P4IOMAP_DT 1 /* DT descriptor type = valid page */
#define P4IOMAP_SIZE 0x2000 /* 8K byte page */
#define P4DEBUG_ADDR 0xff800000
get_iomapbase()
{
int extent, offset;
long a = 0;
u_int pageval;
register u_long *ptr;
int s;
/* find and reserve a page in kernel map for the P4-I/O Mapper */
offset = P4IOMAP_ADDR;
offset &= MMU_PAGEOFFSET;
pageval = btop(P4IOMAP_ADDR);
extent = mmu_btopr(offset + P4IOMAP_SIZE);
DPRINTF2("get_iomapbase: pageval= %x, extent= %x\n",
pageval, extent);
s = splhigh();
a = rmalloc(kernelmap, (long)extent);
(void)splx(s);
if (a == 0)
EPRINTF("get_iomapbase: out of kernel map\n");
iomap_base = (u_long *)((int)kmxtob(a) | offset);
segkmem_mapin(&kseg, (addr_t)iomap_base, (u_int)mmu_ptob(extent),
PROT_READ | PROT_WRITE, pageval, 0);
DPRINTF1("get_iomapbase: iomap_base= %x\n", iomap_base);
ptr = iomap_base;
for (offset = 0; offset < 0x800; offset++)
*ptr++ = NULL;
DPRINTF("get_iopmap: CLEAR iomap, but set first entry\n");
}
static
set_iomap(v_addr, xfrcnt)
u_long v_addr;
int xfrcnt;
{
struct pte pte;
register u_long p_addr;
register u_long *iomap_ptr;
register int numpg;
int startpg, endpg;
iomap_ptr = iomap_base;
DPRINTF2("set_iomap: dma_addr= %x, cnt= %x\n", v_addr, xfrcnt);
startpg = v_addr >> 13;
endpg = (v_addr+xfrcnt-1) >> 13;
numpg = endpg - startpg + 1;
p_addr = v_addr & 0x00ffffff; /* take 24 bit */
p_addr >>= 13; /* index entry */
p_addr <<= 2; /* long word write to I/O map */
DPRINTF2("set_iomap: virtual_iomap= %x, index= %x\n",
iomap_ptr, p_addr);
(u_long)iomap_ptr += p_addr; /* points to the entry */
DPRINTF2("set_iomap: index= %x, pgcnt= %x\n", iomap_ptr, numpg);
while (numpg) {
mmu_getkpte((caddr_t)v_addr, &pte);
DPRINTF1("set_iomap: pfnum= %x\n", pte.pg_pfnum);
p_addr = (u_long)mmu_ptob(pte.pg_pfnum);
DPRINTF1("set_iomap: physical dma_addr= %x\n", p_addr);
/* mask last 3 bits, write protect, and DT type */
(u_long)p_addr &= P4IOMAP_DTMASK;
(u_long)p_addr |= P4IOMAP_DT; /* turn on access vaild bit */
DPRINTF1("set_iomap: new_paddr= %x\n", p_addr);
*iomap_ptr = p_addr; /* store the p_addr in I/O map */
DPRINTF2("set_iomap: I/O map_addr= %x, read_in= %x\n",
iomap_ptr, *iomap_ptr);
if (--numpg == 0)
break;
v_addr += P4IOMAP_SIZE; /* 8k per entry */
DPRINTF1("set_iomap: next v_addr= %x\n", v_addr);
iomap_ptr++;
if (iomap_ptr > iomap_base + P4IOMAP_SIZE)
EPRINTF1("set_iomap: IOMAP_ERR, new ptr= %x\n",
iomap_ptr);
}
DPRINTF("set_iomap: done\n");
}
p4trigger(index)
u_long index;
{
u_long *ptr;
u_long l;
ptr = iomap_base;
(u_long)ptr += index;
l = *ptr; /* trigger the index into debug space */
}
#endif P4DVMA
#ifdef SMDEBUG
sm_debug_break(position)
int position;
{
EPRINTF1("sm_debug_break: from= %x\n", position);
}
#endif SMDEBUG
#endif NSM > 0
#endif (defined sun3x) || (defined sun4)
/* end of ESP SCSI OS driver (sm.c) ... KSAM */