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

5534 lines
129 KiB
C

#ident "@(#)esp.c 1.1 94/10/31 SMI"
/*
* Copyright (c) 1988-1991 by Sun Microsystems, Inc.
*/
#include "esp.h"
#if NESP > 0
#include <scsi/scsi.h>
#include <scsi/adapters/espvar.h>
#ifdef OPENPROMS
#include <sun/autoconf.h>
#endif /* OPENPROMS */
#ifdef sun4m
#include <sun4m/iommu.h>
#endif /* sun4m */
#define FAS100A_BUG
#ifdef FAS100A_BUG
esp_step567 = 0;
#endif
/*
* Update Grok- to allow compilation on systems without newer dmaga.h header(s)
*/
#ifndef BURSTSIZE
#define BURSTSIZE
#define BURST1 0x01
#define BURST2 0x02
#define BURST4 0x04
#define BURST8 0x08
#define BURST16 0x10
#define BURST32 0x20
#define BURST64 0x40
#define BURSTSIZE_MASK 0x7f
#define DEFAULT_BURSTSIZE BURST16|BURST8|BURST4|BURST2|BURST1
#endif /* BURSTSIZE */
#ifndef DMAGA_TURBO
#define DMAGA_TURBO 0x00400000
#endif /* !DMAGA_TURBO */
#ifndef ESC1_REV1
#define ESC1_REV1 0x4
#define SET_DMAESC_COUNT(dmar, val) (dmar)->dmaga_count = val
#define DMAESC_BSIZE 0x0800
#define DMAESC_TCZERO 0x1000
#define DMAESC_EN_TCI 0x2000
#define DMAESC_INTPEND 0x4000
#define DMAESC_PEN 0x8000
#define DMAESC_PERR 0x00010000
#define DMAESC_DRAIN 0x00020000
#define DMAESC_EN_ADD 0x00040000
#define DMAESC_SETBURST16(d) (d)->dmaga_csr |= DMAESC_BSIZE
#define DMAESC_SETBURST32(d) (d)->dmaga_csr &= ~DMAESC_BSIZE
#endif /* ESC1_REV1 */
#ifndef DMA_REV3
#define DMA_REV3 0xA /* DMA2 gate array */
#define DMAGA_BURSTMASK 0x000C0000
#define DMAGA_BURST16 0x00000000
#define DMAGA_NOBURST 0x00080000
#define DMAGA_BURST32 0x00040000
#define DMA2_SETNOBURST(d) \
(d)->dmaga_csr &= ~DMAGA_BURSTMASK, (d)->dmaga_csr |= DMAGA_NOBURST
#define DMA2_SETBURST16(d) (d)->dmaga_csr &= ~DMAGA_BURSTMASK
#define DMA2_SETBURST32(d) \
(d)->dmaga_csr &= ~DMAGA_BURSTMASK, (d)->dmaga_csr |= DMAGA_BURST32
#define DMAGA_TWO_CYCLE 0x00200000
#endif /* !DMA_REV3 */
#undef SET_DMAESC_COUNT
SET_DMAESC_COUNT(dmar, val)
struct dmaga *dmar;
int val;
{
u_int addr1 = (u_int) dmar->dmaga_addr;
u_int addr2 = (u_int) roundup(addr1 + val, MMU_PAGESIZE);
(dmar)->dmaga_count = (u_int) (addr2 - addr1);
}
/*
* Local function declarations
*/
static int esp_start(), esp_abort(), esp_reset(), esp_getcap(), esp_setcap();
static int esp_poll(), esp_watch(), esp_phasemanage();
static int esp_finish_select(), esp_reconnect(), esp_finish();
static int esp_ustart(), esp_startcmd(), esp_reset_recovery(), esp_istart();
static int esp_abort_curcmd(), esp_abort_allcmds(), esp_reset_bus();
static int esp_handle_selection();
static int esp_dopoll();
#ifdef VECTORED_INTERRUPTS
static int esp_intr();
#endif /* VECTORED_INTERRUPTS */
static void esp_internal_reset(), esp_sync_backoff();
static void esp_hw_reset(), esp_curcmd_timeout(), esp_disccmd_timeout();
static void esplog(), eprintf(), esp_stat_int_print();
static void esp_watchsubr();
static void espsvc();
static void esp_dump_datasegs(), esp_dump_cmd();
static void esp_chip_disconnect();
static void esp_makeproxy_cmd(), esp_make_sdtr();
static void esp_init_cmd(), esp_runpoll();
/*
* Phase handlers
*/
static int esp_handle_unknown(), esp_handle_cmd_start(), esp_handle_cmd_done();
static int esp_handle_msg_out(), esp_handle_msg_out_done();
static int esp_handle_clearing();
static int esp_handle_msg_in(), esp_handle_more_msgin();
static int esp_handle_msg_in_done();
static int esp_handle_c_cmplt(), esp_handle_data(), esp_handle_data_done();
/*
* Debug routines- ultimately to be hidden within ESPDEBUG
*/
#ifdef ESPDEBUG
static void esp_dump_state();
#endif /* ESPDEBUG */
static void esp_printstate();
static char *esp_state_name();
/*
* Local static data
*/
static int espconf = 0;
static int espconf2 = 0;
static int espconf3 = 0;
#define POLL_TIMEOUT (3 * 60000000) /* in usec, about 3 mins */
static struct esp *esp_softc = (struct esp *) 0;
#ifdef OPENPROMS
static int nesp;
#else /* OPENPROMS */
static int nesp = NESP;
#endif /* OPENPROMS */
static char *esp_stat_bits = ESP_STAT_BITS;
static char *esp_int_bits = ESP_INT_BITS;
static char *dmaga_bits = DMAGA_BITS;
static char *msginperr = "SCSI bus MESSAGE IN phase parity error\n";
#ifdef sun4c
static int esp_ss1_esp0sync = 0;
#endif /* sun4c */
static int esp_nhardints;
static int esp_nmultsvc;
#ifdef ESPDEBUG
static int espdebug = 0;
#endif /* ESPDEBUG */
#ifdef ESP_TEST_PARITY
static int esp_ptest_msgin;
static int esp_ptest_msg = -1;
static int esp_ptest_msgcmplt;
static int esp_ptest_status;
static int esp_ptest_data_in;
#endif /* ESP_TEST_PARITY */
#ifdef ESP_TEST_TIMEOUT
static int esp_ttest;
static int esp_stest;
#endif /* ESP_TEST_TIMEOUT */
/*
* autoconfiguration routines.
*/
#ifdef OPENPROMS
static int esp_identify(), esp_attach();
struct dev_ops esp_ops = {
1,
esp_identify,
esp_attach
};
#ifdef GASP
extern int gasp_cards;
extern u_long gasp_addrs[];
#define ESP_OFFSET 0x800000
#define Esp_addr(card) (gasp_addrs[(card)] + ESP_OFFSET)
static void
new_esp(prev, gaddr)
struct dev_info *prev;
u_long gaddr;
{
struct dev_info *dev;
dev = (struct dev_info *) kmem_zalloc (sizeof (*dev));
dev->devi_parent = prev->devi_parent;
dev->devi_next = prev->devi_next;
prev->devi_next = dev;
dev->devi_name = prev->devi_name;
dev->devi_nreg = 1;
dev->devi_reg = (struct dev_reg *)
kmem_zalloc(sizeof (struct dev_reg));
dev->devi_reg->reg_bustype = OBIO;
dev->devi_reg->reg_addr = (addr_t) gaddr;
dev->devi_reg->reg_size = sizeof (struct espreg);
dev->devi_nintr = 1;
dev->devi_intr = prev->devi_intr;
dev->devi_driver = &esp_ops;
dev->devi_nodeid = prev->devi_nodeid; /* lies. all lies */
nesp += 1;
}
#endif /* GASP */
static int
esp_identify(name)
char *name;
{
/*
* Note the octal literal for a comma.
* Avoids Shannon's C-style awk script's complaints
*/
if (strcmp(name, "esp") == 0 || strcmp(name, "SUNW\054esp") == 0) {
nesp++;
return (1);
} else {
return (0);
}
}
static int
esp_attach(dev)
register struct dev_info *dev;
{
#ifdef ESP_NEW_HW_DEBUG
static char *cdebug = "esp%d: %s %s returned as %d\n";
#endif /* ESP_NEW_HW_DEBUG */
static char *prop_ini_id = "initiator-id";
static char *prop_scsi_ini_id = "scsi-initiator-id";
static char *prop_cfreq = "clock-frequency";
static char *prop_bur_size = "burst-sizes";
static char *prop_diff = "differential";
static int next_unit = 0;
static int esp_spl = 0;
register struct dmaga *dmar;
register struct espreg *ep;
register struct esp *esp, *nsp;
register struct dev_info *ndev;
int i, cur_unit;
u_long ticks;
u_char clock_conv;
#ifdef GASP
if (next_unit == 0) {
register card;
struct dev_info *tmp = dev;
for (card = 0; card < 4; card++) {
if (gasp_cards & (1<<card)) {
new_esp(tmp, Esp_addr(card));
tmp = tmp->devi_next;
}
}
}
#endif /* GASP */
cur_unit = dev->devi_unit = next_unit++;
if (cur_unit >= nesp || dev->devi_nreg > 1 || dev->devi_nintr == 0) {
return (-1);
}
#ifdef sun4c
/*
* Check to see whether this is a useful
* slot for an sbus scsi interface
*/
if ((i = slaveslot(dev->devi_reg->reg_addr)) >= 0) {
printf("esp%d: not used - SBus slot %d is slave-only\n",
cur_unit, i);
return (-1);
}
#endif /* sun4c */
/*
* map in device registers
*/
ep = (struct espreg *) map_regs(dev->devi_reg->reg_addr,
dev->devi_reg->reg_size, dev->devi_reg->reg_bustype);
if (ep == (struct espreg *) 0) {
printf("esp%d: unable to map registers\n", cur_unit);
return (-1);
}
dmar = dma_alloc(dev->devi_reg->reg_bustype, dev->devi_reg->reg_addr);
if (!dmar) {
printf("esp%d: cannot find dma controller\n", cur_unit);
unmap_regs((addr_t) ep, dev->devi_reg->reg_size);
return (-1);
}
/*
* Initialize state of DMA gate array.
* Must clear DMAGA_RESET on the ESC before accessing the esp.
*/
if (DMAGA_REV(dmar) == ESC1_REV1) {
dmar->dmaga_csr &= ~DMAGA_RESET;
}
dmar->dmaga_csr &= ~DMAGA_WRITE;
/*
* If we haven't allocated the softc structure, do so now.
*/
esp = (struct esp *) kmem_zalloc((unsigned) (sizeof (struct esp)));
if (esp != (struct esp *) 0) {
esp->e_cmdarea = (u_char *) IOPBALLOC(FIFOSIZE);
}
if (esp == (struct esp *) 0 || esp->e_cmdarea == (u_char *) 0) {
printf("esp%d: no space for %s\n", cur_unit, (esp->e_cmdarea) ?
"cmd areas" : "data structures");
if (esp) {
(void) kmem_free((caddr_t)esp, (sizeof (struct esp)));
}
unmap_regs((addr_t) ep, dev->devi_reg->reg_size);
dma_free(dmar);
return (-1);
}
esp->e_next = (struct esp *) 0;
esp->e_unit = cur_unit;
/*
* Establish initial softc values
*/
if (esp_softc == (struct esp *) 0) {
esp_softc = esp;
timeout (esp_watch, (caddr_t) 0, hz);
} else {
for (nsp = esp_softc; nsp->e_next != (struct esp *) 0;
nsp = nsp->e_next);
nsp->e_next = esp;
}
/*
* Determine clock frequency of attached ESP chip.
*/
for (ndev = dev; ndev; ndev = ndev->devi_parent) {
i = getprop(ndev->devi_nodeid, prop_cfreq, -1);
if ((i > 0) || ndev == top_devinfo) {
#ifdef ESP_NEW_HW_DEBUG
printf (cdebug, cur_unit, ndev->devi_name,
prop_cfreq, i);
#endif /* ESP_NEW_HW_DEBUG */
break;
}
}
#ifdef ESP_NEW_HW_DEBUG
if (i != 40000000) {
printf("WARNING: found clock frequency of %d\n", i);
printf(" Assuming clock frequency of 40 MHZ\n");
i = 40000000;
}
#endif /* ESP_NEW_HW_DEBUG */
/*
* Valid clock freqs. are between 10 and 40 MHz. Otherwise
* presume 20 MHz. and complain. (Notice, that we wrap to
* zero at 40 MHz. Ick!) This test should NEVER fail!
*
* freq (MHz) clock conversion factor
* 10 2
* 10.01-15 3
* 15.01-20 4
* 20.01-25 5
* 25.01-30 6
* 30.01-35 7
* 35.01-40 8 (0)
*/
if (i > FIVE_MEG) {
clock_conv = (i + FIVE_MEG - 1)/ FIVE_MEG;
} else {
clock_conv = 0;
}
if (clock_conv < CLOCK_10MHZ || clock_conv > CLOCK_40MHZ) {
esplog(esp, LOG_ERR,
"Bad clock frequency- setting 20mhz, asynchronous mode");
esp->e_weak = 0xff;
clock_conv = CLOCK_20MHZ;
i = TWENTY_MEG;
}
esp->e_clock_conv = clock_conv;
esp->e_clock_cycle = CLOCK_PERIOD(i);
ticks = ESP_CLOCK_TICK(esp);
esp->e_stval = ESP_CLOCK_TIMEOUT(ticks);
#ifdef ESP_TEST_FAST_SYNC
IPRINTF5("%d mhz, clock_conv %d, clock_cycle %d, ticks %d, stval %d\n",
i, esp->e_clock_conv, esp->e_clock_cycle,
ticks, esp->e_stval);
#endif ESP_TEST_FAST_SYNC
/*
* The theory is that the layout of all cpu boards post SparcStation-1
* is clean enough to allow SYNC mode to be on all the time.
*/
#ifdef sun4c
if (cpu == CPU_SUN4C_60 && dev->devi_unit == 0 &&
esp_ss1_esp0sync == 0) {
esp->e_weak = 0xff;
}
#endif /* sun4c */
/*
* Calculate max locking spl so far seen..
*/
esp->e_spl = ipltospl(dev->devi_intr->int_pri);
esp_spl = MAX(esp_spl, esp->e_spl);
/*
* Make sure that everyone, including us, is locking at
* the same priority.
*/
for (nsp = esp_softc; nsp != (struct esp *) 0; nsp = nsp->e_next)
nsp->e_tran.tran_spl = esp_spl;
#ifdef ESPDEBUG
/*
* Initialize last state log.
*/
for (i = 0; i < NPHASE; i++) {
esp->e_phase[i].e_save_state = STATE_FREE;
esp->e_phase[i].e_save_stat = -1;
esp->e_phase[i].e_val1 = -1;
esp->e_phase[i].e_val2 = -1;
}
esp->e_phase_index = 0;
esp->e_xfer = 0;
#endif /* ESPDEBUG */
esp->e_tran.tran_start = esp_start;
esp->e_tran.tran_abort = esp_abort;
esp->e_tran.tran_reset = esp_reset;
esp->e_tran.tran_getcap = esp_getcap;
esp->e_tran.tran_setcap = esp_setcap;
esp->e_tran.tran_pktalloc = scsi_std_pktalloc;
esp->e_tran.tran_dmaget = scsi_std_dmaget;
esp->e_tran.tran_pktfree = scsi_std_pktfree;
esp->e_tran.tran_dmafree = scsi_std_dmafree;
esp->e_dev = dev;
esp->e_espconf = DEFAULT_HOSTID;
for (ndev = dev; ndev; ndev = ndev->devi_parent) {
i = getprop(ndev->devi_nodeid, prop_ini_id, -1);
#ifdef ESP_NEW_HW_DEBUG
printf (cdebug, cur_unit, ndev->devi_name, prop_ini_id, i);
#endif /* ESP_NEW_HW_DEBUG */
if (i == -1) {
i = getprop(ndev->devi_nodeid, prop_scsi_ini_id, -1);
#ifdef ESP_NEW_HW_DEBUG
printf (cdebug, cur_unit, ndev->devi_name,
prop_scsi_ini_id, i);
#endif /* ESP_NEW_HW_DEBUG */
}
if (i != DEFAULT_HOSTID && i >= 0 && i <= 7) {
esplog(esp, LOG_INFO, "initiator SCSI Id now %d", i);
esp->e_espconf = (u_char) i;
}
if (i > -1 || ndev == top_devinfo)
break;
}
if (scsi_options & SCSI_OPTIONS_PARITY)
esp->e_espconf |= ESP_CONF_PAREN;
esp->e_espconf |= (espconf & ~ESP_CONF_BUSID);
esp->e_reg = ep;
esp->e_dma = dmar;
/*
* Determine sbus burst size.
* top_devinfo is declared in <sun/openprom.h>.
*/
esp->e_burstsizes = 0xff;
ndev = esp->e_dev;
do {
i = getprop(ndev->devi_nodeid, prop_bur_size, -1);
if (i != -1) {
/*
* oops- we have somebody who constrains us.
*/
esp->e_burstsizes = esp->e_burstsizes & i;
#ifdef ESP_NEW_HW_DEBUG
printf (cdebug, cur_unit, ndev->devi_name,
prop_bur_size, i);
#endif /* ESP_NEW_HW_DEBUG */
}
ndev = ndev->devi_parent;
} while (ndev != top_devinfo);
/*
* No burstsize properties found- select a reasonable default
*/
i = esp->e_burstsizes;
if ((i == 0xff) || (!((i & BURST16) || (i & BURST32)))) {
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf(esp, "Bad burst size %d- setting to %d",
i, DEFAULT_BURSTSIZE);
}
#endif /* ESPDEBUG */
esp->e_burstsizes = DEFAULT_BURSTSIZE;
}
/*
* XXX: This needs to be based upon architecture. Right now,
* XXX: all OPENPROM machines are sun4c and have a dma base
* XXX: of -1mb.
*/
esp->e_dma_base = (u_long) DVMA;
esp->e_last_slot = esp->e_cur_slot = UNDEFINED;
addintr(dev->devi_intr->int_pri, esp_poll,
dev->devi_name, dev->devi_unit);
adddma(dev->devi_intr->int_pri);
report_dev(dev);
ep->esp_conf2 = 0;
ep->esp_conf2 = 0xa;
esp->e_default_period = CONVERT_PERIOD(DEFAULT_SYNC_PERIOD);
if ((ep->esp_conf2&0xf) == 0xa) {
esp->e_espconf2 = espconf2;
ep->esp_conf3 = 0;
ep->esp_conf3 = 5;
if (ep->esp_conf3 == 0x5) {
for (i = 0; i < NTARGETS; i++) {
esp->e_espconf3[i] = espconf3;
}
if (clock_conv > CLOCK_25MHZ) {
esp->e_espconf2 |= ESP_CONF2_FENABLE;
ep->esp_conf2 = esp->e_espconf2;
esp->e_default_period =
CONVERT_PERIOD(DEFAULT_FASTSYNC_PERIOD);
esp->e_type = FAST;
#ifdef ESP_TEST_FAST_SYNC
IPRINTF1("conf3=%x\n", espconf3);
IPRINTF("found FAST\n");
#endif ESP_TEST_FAST_SYNC
} else {
ep->esp_conf2 = esp->e_espconf2;
esp->e_type = ESP236;
IPRINTF("found ESP236\n");
}
ep->esp_conf3 = espconf3;
} else {
ep->esp_conf2 = esp->e_espconf2;
esp->e_type = ESP100A;
}
} else {
esp->e_type = ESP100;
}
/*
* check for differential scsi bus property
*/
if (getprop(dev->devi_nodeid, prop_diff, -1) != -1) {
IPRINTF("differential scsi bus\n");
esp->e_differential++;
}
esp_internal_reset(esp, ESP_RESET_ALL);
/*
* Whew! Now make ourselves known to the rest of the SCSI world...
*/
scsi_config(&esp->e_tran, dev);
#ifdef GASP
if (dev->devi_unit == 0 && nesp > 1) {
for (i = 1; i < nesp; i++) {
dev = dev->devi_next;
(void) esp_attach(dev);
}
}
#endif /* GASP */
return (0);
}
#else /* OPENPROMS */
static int esp_probe(), esp_slave(), esp_attach();
struct mb_driver espdriver = {
esp_probe, esp_slave, esp_attach, 0, 0, esp_poll,
ESP_SIZE, "scsibus", 0, "esp", 0, MDR_BIODMA
};
static int
esp_probe(reg, ctlr)
caddr_t reg;
int ctlr;
{
static char first_time = 1;
register struct dmaga *dmar;
register struct espreg *ep;
register struct esp *esp, *nsp;
u_long dma_base, ticks;
u_char clock_conv;
long lptr;
/*
* These shorthand defines just for this routine
*/
#define DNAME espdriver.mdr_cname
#define DUNIT ctlr
switch (cpu) {
#ifdef sun4
case CPU_SUN4_330:
dma_base = (u_long) DVMA;
clock_conv = CLOCK_25MHZ;
dmar = (struct dmaga *) (((long) reg) + STINGRAY_DMA_OFFSET);
break;
#endif /* sun4 */
#ifdef sun3x
case CPU_SUN3X_80:
dma_base = (u_long) DVMA;
clock_conv = CLOCK_20MHZ;
dmar = (struct dmaga *) (((long) reg) + HYDRA_DMA_OFFSET);
break;
#endif /* sun3x */
default:
return (0);
}
if ((peekl((long *)&(dmar->dmaga_csr), (long *)&(lptr))) == -1) {
return (0);
}
ep = (struct espreg *) reg;
if ((peekc((char *)&(ep->esp_xcnt_lo))) == -1) {
return (0);
}
esp = (struct esp *) kmem_zalloc((unsigned) (sizeof (struct esp)));
if (esp != (struct esp *) 0) {
esp->e_cmdarea = (u_char *) IOPBALLOC(FIFOSIZE);
}
if (esp == (struct esp *) 0 || esp->e_cmdarea == (u_char *) 0) {
printf("esp%d: no space for %s\n", DUNIT, (esp->e_cmdarea) ?
"cmd areas" : "data structures");
if (esp) {
(void) kmem_free((caddr_t)esp, (sizeof (struct esp)));
}
return (0);
}
esp->e_next = (struct esp *)0;
esp->e_unit = DUNIT;
/*
* Establish initial softc values
*/
if (esp_softc == (struct esp *) 0) {
esp_softc = esp;
} else {
for (nsp = esp_softc; nsp->e_next != (struct esp *) 0;
nsp = nsp->e_next);
nsp->e_next = esp;
}
esp->e_dev = &espdriver;
esp->e_espconf = DEFAULT_HOSTID;
if (scsi_options & SCSI_OPTIONS_PARITY)
esp->e_espconf |= ESP_CONF_PAREN;
esp->e_espconf |= (espconf & ~ESP_CONF_BUSID);
esp->e_burstsizes = 0;
esp->e_clock_conv = clock_conv;
esp->e_clock_cycle = FIVE_MEG * clock_conv;
ticks = ESP_CLOCK_TICK(esp);
esp->e_stval = ESP_CLOCK_TIMEOUT(ticks);
esp->e_backoff = 0;
#ifdef ESP_TEST_FAST_SYNC
IPRINTF5("%d mhz, clock_conv %d, clock_cycle %d, ticks %d, stval %d\n",
FIVE_MEG * clock_conv, esp->e_clock_conv, esp->e_clock_cycle,
ticks, esp->e_stval);
#endif ESP_TEST_FAST_SYNC
esp->e_reg = ep;
esp->e_dma = dmar;
/*
* Initialize state of DMA gate array.
*/
dmar->dmaga_csr = dmar->dmaga_csr & ~DMAGA_WRITE;
esp->e_dma_base = dma_base;
esp->e_tran.tran_start = esp_start;
esp->e_tran.tran_abort = esp_abort;
esp->e_tran.tran_reset = esp_reset;
esp->e_tran.tran_getcap = esp_getcap;
esp->e_tran.tran_setcap = esp_setcap;
esp->e_tran.tran_pktalloc = scsi_std_pktalloc;
esp->e_tran.tran_dmaget = scsi_std_dmaget;
esp->e_tran.tran_pktfree = scsi_std_pktfree;
esp->e_tran.tran_dmafree = scsi_std_dmafree;
esp->e_last_slot = esp->e_cur_slot = UNDEFINED;
if (first_time) {
first_time = 0;
timeout(esp_watch, (caddr_t) 0, hz);
}
esp_internal_reset(esp, ESP_RESET_ALL);
#undef DNAME
#undef DUNIT
return (ESP_SIZE);
}
/*ARGSUSED*/
static int
esp_slave(md, reg)
struct mb_device *md;
caddr_t reg;
{
struct esp *esp;
register struct espreg *ep;
register int i;
for (esp = esp_softc, i = 0; esp->e_next != (struct esp *) 0,
i < md->md_ctlr; esp = esp->e_next, i++);
ep = esp->e_reg;
ep->esp_conf2 = 0;
ep->esp_conf2 = 0xa;
esp->e_type = ESP100;
if ((ep->esp_conf2&0xf) == 0xa) {
ep->esp_conf2 = esp->e_espconf2 = espconf2;
ep->esp_conf3 = 0;
ep->esp_conf3 = 5;
if (ep->esp_conf3 == 0x5) {
ep->esp_conf3 = espconf3;
for (i = 0; i < NTARGETS; i++) {
esp->e_espconf3[i] = espconf3;
}
esp->e_type = ESP236;
} else
esp->e_type = ESP100A;
}
md->md_slave = MY_ID(esp) << 3;
return (1);
}
static int
esp_attach(md)
struct mb_device *md;
{
int i;
static int esp_spl = 0;
struct esp *esp, *nsp;
for (esp = esp_softc, i = 0; esp->e_next != (struct esp *) 0,
i < md->md_ctlr; esp = esp->e_next, i++);
/*
* Calculate max locking spl so far seen..
*/
esp->e_spl = pritospl(md->md_intpri);
esp_spl = MAX(esp_spl, esp->e_spl);
/*
* Make sure that everyone, including us, is locking at
* the same priority.
*/
for (nsp = esp_softc; nsp != (struct esp *) 0; nsp = nsp->e_next) {
nsp->e_tran.tran_spl = esp_spl;
}
#ifdef ESPDEBUG
/*
* Initialize last state log.
*/
for (i = 0; i < NPHASE; i++) {
esp->e_phase[i].e_save_state = STATE_FREE;
esp->e_phase[i].e_save_stat = -1;
esp->e_phase[i].e_val1 = -1;
esp->e_phase[i].e_val2 = -1;
}
esp->e_phase_index = 0;
esp->e_xfer = 0;
#endif /* ESPDEBUG */
/*
* Whew! Now make ourselves known to the rest of the SCSI world...
*/
scsi_config(&esp->e_tran, md);
}
#endif /* OPENPROMS */
/*
* Interface functions
*
* Visible to the external world via the transport structure.
*/
/*
* esp_start - Accept commands for transport
*/
static int
esp_start(sp)
register struct scsi_cmd *sp;
{
register s;
register short slot;
register struct esp *esp;
esp = (struct esp *) sp->cmd_pkt.pkt_address.a_cookie;
slot = (sp->cmd_pkt.pkt_address.a_target * NLUNS_PER_TARGET) |
(sp->cmd_pkt.pkt_address.a_lun);
if (sp->cmd_flags & CFLAG_DMAVALID) {
u_long maxdma;
switch (DMAGA_REV(esp->e_dma)) {
default:
case DMA_REV1:
case DMA_REV2:
case ESC1_REV1:
maxdma = 1<<24;
break;
case DMA_REV3:
maxdma = 1<<30; /* be reasonable - 2gb is enuff */
break;
}
if (sp->cmd_mapseg.d_count >= maxdma) {
return (TRAN_BADPKT);
}
}
s = splr(esp->e_tran.tran_spl);
if (esp->e_slots[slot] != (struct scsi_cmd *) 0) {
/*
* This is where we would see if this is a SCSI-2
* multiple command target.
*/
/*
* Else queueing error...
*/
(void) splx(s);
return (TRAN_BUSY);
}
/*
* Else, accept the command
*/
esp->e_slots[slot] = sp;
esp->e_ncmds++;
/*
* Reinitialize some fields that need it
*/
esp_init_cmd(sp);
/*
* Run the command (maybe).
*/
if ((sp->cmd_pkt.pkt_flags & FLAG_NOINTR) == 0) {
/*
* Okay- if this is command is *not* a polling command,
* and we're not pending any polling commands, *and*
* the state is FREE, *then* we can start this command.
*/
if (esp->e_npolling == 0 && esp->e_state == STATE_FREE) {
(void) esp_ustart(esp, slot);
}
} else {
esp_runpoll(esp, slot);
}
(void) splx(s);
return (TRAN_ACCEPT);
}
static int
esp_abort(ap, pkt)
struct scsi_address *ap;
struct scsi_pkt *pkt;
{
register struct esp *esp = (struct esp *) ap->a_cookie;
register struct scsi_cmd *sp = (struct scsi_cmd *) pkt;
register int s, rval;
register short slot;
/*
* find the slot for this specific address
*/
slot = (ap->a_target * NLUNS_PER_TARGET) | ap->a_lun;
/*
* Take care of the easy cases first:
*
* A specific command was passed as an argument (to be aborted) and
*
* The command is currently active; we cannot abort now
* The command does not exist here.
* The command exists here, but hasn't been started yet.
*
* No specific command was passed as an argument (abort all
* commands at this nexus), and
*
* No commands exist for this nexus.
* Commands that exist here haven't been started yet.
*
*/
s = splr(esp->e_tran.tran_spl);
if (esp->e_state != STATE_FREE && esp->e_cur_slot == slot) {
/*
* This isn't right, but it will have to do for now.
* If the currently active command is the one to be
* aborted, we don't know at this level whether it
* is safe, nor how to, queue up the ABORT OPERATION
* message. Instead, punt, and let the target driver
* decide whether to pull the chain on the bus.
*/
(void) splx(s);
return (FALSE);
}
if (sp == (struct scsi_cmd *) 0) {
sp = esp->e_slots[slot];
}
if (esp->e_slots[slot] == (struct scsi_cmd *) 0) {
sp = (struct scsi_cmd *) 0;
}
if (sp && sp == esp->e_slots[slot] && sp->cmd_pkt.pkt_state == 0) {
esp->e_slots[slot] = (struct scsi_cmd *) 0;
sp->cmd_pkt.pkt_reason = CMD_ABORTED;
esp->e_ncmds -= 1;
(*sp->cmd_pkt.pkt_comp)(sp);
sp = (struct scsi_cmd *) 0;
}
if (sp == (struct scsi_cmd *) 0) {
(void) splx(s);
return (TRUE);
}
/*
* Now deal with the hard cases. We have a valid sp for the
* single active command at this nexus (XXX later we will have
* a list of commands to abort XXX).
*/
{
/*
* In this case, the command is active but disconnected
*/
auto struct scsi_cmd local;
register struct scsi_cmd *savesp = sp;
int wasdisc;
esp->e_slots[slot] = (struct scsi_cmd *) 0;
if (sp->cmd_flags & CFLAG_CMDDISC) {
wasdisc = 1;
esp->e_ndisc -= 1;
} else {
wasdisc = 0;
}
esp->e_ncmds -= 1;
sp = &local;
esp_makeproxy_cmd(sp, ap, MSG_ABORT);
IPRINTF2 ("Sending proxy abort message to %d.%d\n",
ap->a_target, ap->a_lun);
if (esp_start(sp) == TRAN_ACCEPT &&
sp->cmd_pkt.pkt_reason == CMD_CMPLT &&
sp->cmd_cdb[ESP_PROXY_RESULT] == TRUE) {
IPRINTF2 ("Proxy abort succeeded for %d.%d\n",
ap->a_target, ap->a_lun);
rval = TRUE;
savesp->cmd_pkt.pkt_reason = CMD_ABORTED;
(*savesp->cmd_pkt.pkt_comp)(savesp);
IPRINTF2 ("Completion called for %d.%d\n",
ap->a_target, ap->a_lun);
} else {
IPRINTF2 ("Proxy abort failed for %d.%d\n",
ap->a_target, ap->a_lun);
esp->e_ncmds++;
if (wasdisc) {
esp->e_ndisc++;
savesp->cmd_flags |= CFLAG_CMDDISC;
}
esp->e_slots[slot] = savesp;
rval = FALSE;
}
if (esp->e_state == STATE_FREE) {
(void) esp_ustart(esp, NEXTSLOT(slot));
}
}
(void) splx(s);
return (rval);
}
static int
esp_reset(ap, level)
struct scsi_address *ap;
int level;
{
register s, rval;
struct esp *esp = (struct esp *) ap->a_cookie;
rval = FALSE;
s = splr(esp->e_tran.tran_spl);
if (level == RESET_ALL) {
/*
* We know that esp_reset_bus() returns ACTION_RETURN.
*/
(void) esp_reset_bus(esp);
/*
* Now call esp_dopoll() to field the reset interrupt
* which will than call esp_reset_recovery which will
* call completion for all commands.
*
* XXX: mark that we are in 'polling' mode so that
* XXX: esp_reset_recovery just returns up to esp_dopoll()
* XXX: with state == STATE_FREE. Gross, but esp_reset_recovery
* XXX: will clear the e_npolling flag, but remember that
* XXX: we were in 'polling' mode and just return ACTION_RETURN
*
*/
esp->e_npolling++;
if (esp_dopoll(esp, POLL_TIMEOUT)) {
esplog(esp, LOG_ERR, "reset scsi bus failed");
} else
rval = TRUE;
} else {
short slot = (ap->a_target * NLUNS_PER_TARGET) | ap->a_lun;
if (esp->e_state == STATE_FREE && esp->e_slots[slot] == 0 &&
esp->e_npolling == 0) {
auto struct scsi_cmd local;
register struct scsi_cmd *sp = &local;
IPRINTF2 ("Sending proxy reset message to %d.%d\n",
ap->a_target, ap->a_lun);
esp_makeproxy_cmd(sp, ap, MSG_DEVICE_RESET);
if (esp_start(sp) == TRAN_ACCEPT &&
sp->cmd_pkt.pkt_reason == CMD_CMPLT &&
sp->cmd_cdb[ESP_PROXY_RESULT] == TRUE) {
IPRINTF2 ("proxy reset of %d.%d ok\n",
ap->a_target, ap->a_lun);
rval = TRUE;
} else {
/*
* make sure the local packet is not left in
* the active slot
*/
if (esp->e_slots[slot] == &local) {
esp->e_slots[slot] =
(struct scsi_cmd *) NULL;
}
}
}
}
if (esp->e_state == STATE_FREE) {
(void) esp_ustart(esp, 0);
}
(void) splx(s);
return (rval);
}
static int
esp_commoncap(ap, cap, val, tgtonly, doset)
struct scsi_address *ap;
char *cap;
int val, tgtonly, doset;
{
register struct esp *esp = (struct esp *) ap->a_cookie;
register cidx;
register u_char tshift = (1<<ap->a_target);
register u_char ntshift = ~tshift;
register rval = FALSE;
if ((tgtonly != 0 && tgtonly != 1) || cap == (char *) 0) {
return (rval);
}
for (cidx = 0; scsi_capstrings[cidx]; cidx++) {
if (strcmp(cap, scsi_capstrings[cidx]) == 0)
break;
}
if (scsi_capstrings[cidx] == (char *) 0) {
rval = UNDEFINED;
} else if (doset && (val == 0 || val == 1)) {
/*
* At present, we can only set binary (0/1) values
*/
switch (cidx) {
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_MSG_OUT:
case SCSI_CAP_PARITY:
case SCSI_CAP_INITIATOR_ID:
/*
* None of these are settable via
* the capability interface.
*/
break;
case SCSI_CAP_DISCONNECT:
if ((scsi_options & SCSI_OPTIONS_DR) == 0) {
break;
} else if (tgtonly) {
if (val)
esp->e_nodisc &= ntshift;
else
esp->e_nodisc |= tshift;
} else {
esp->e_nodisc = (val) ? 0 : 0xff;
}
rval = TRUE;
break;
case SCSI_CAP_SYNCHRONOUS:
if ((scsi_options & SCSI_OPTIONS_SYNC) == 0) {
break;
} else if (tgtonly) {
if (val) {
esp->e_weak &= ntshift;
} else {
esp->e_weak |= tshift;
}
esp->e_sync_known &= ntshift;
} else {
if (val) {
esp->e_weak = 0;
} else {
esp->e_weak = 0xff;
}
esp->e_sync_known = 0;
}
rval = TRUE;
break;
case SCSI_CAP_WIDE_XFER:
case SCSI_CAP_UNTAGGED_QING:
case SCSI_CAP_TAGGED_QING:
default:
rval = UNDEFINED;
break;
}
} else if (doset == 0) {
switch (cidx) {
case SCSI_CAP_DMA_MAX:
switch (DMAGA_REV(esp->e_dma)) {
default:
case DMA_REV1:
case DMA_REV2:
case ESC1_REV1:
rval = 1<<24;
break;
case DMA_REV3:
rval = 1<<30;
break;
}
break;
case SCSI_CAP_MSG_OUT:
rval = TRUE;
break;
case SCSI_CAP_DISCONNECT:
if ((scsi_options & SCSI_OPTIONS_DR) &&
(tgtonly == 0 || (esp->e_nodisc & tshift) == 0)) {
rval = TRUE;
}
break;
case SCSI_CAP_SYNCHRONOUS:
if ((scsi_options & SCSI_OPTIONS_SYNC) &&
(tgtonly == 0 || esp->e_offset[ap->a_target])) {
rval = TRUE;
}
break;
case SCSI_CAP_PARITY:
if (scsi_options & SCSI_OPTIONS_PARITY)
rval = TRUE;
break;
case SCSI_CAP_INITIATOR_ID:
rval = MY_ID(esp);
break;
default:
rval = UNDEFINED;
break;
}
}
return (rval);
}
static int
esp_getcap(ap, cap, whom)
struct scsi_address *ap;
char *cap;
int whom;
{
return (esp_commoncap(ap, cap, 0, whom, 0));
}
static int
esp_setcap(ap, cap, value, whom)
struct scsi_address *ap;
char *cap;
int value, whom;
{
return (esp_commoncap(ap, cap, value, whom, 1));
}
/*
* Internal Search Routine.
*
* Search for a command to start.
*/
static int
esp_ustart(esp, start_slot)
register struct esp *esp;
short start_slot;
{
register struct scsi_cmd *sp;
register short slot, found = 0;
char dkn;
if (start_slot == UNDEFINED)
start_slot = 0;
slot = start_slot;
/*
* If all commands queued up here are
* disconnected, there is no work to do.
*
* e_ndisc should always be <= e_ncmds.
*
*/
if (esp->e_ncmds <= esp->e_ndisc) {
return (FALSE);
}
do {
sp = esp->e_slots[slot];
if (sp && (sp->cmd_flags & CFLAG_CMDDISC) == 0) {
found++;
} else {
slot = NEXTSLOT(slot);
}
} while (found == 0 && slot != start_slot);
if (!found) {
return (FALSE);
}
esp->e_cur_slot = slot;
if ((dkn = sp->cmd_pkt.pkt_pmon) >= 0) {
dk_busy |= (1<<dkn);
dk_xfer[dkn]++;
if ((sp->cmd_flags & CFLAG_DMASEND) == 0)
dk_read[dkn]++;
dk_wds[dkn] += sp->cmd_dmacount >> 6;
}
return (esp_startcmd(esp));
}
/*
* Start a command off
*/
static int
esp_startcmd(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
register struct dmaga *dmar = esp->e_dma;
register struct scsi_cmd *sp = CURRENT_CMD(esp);
int legal_cmd_len, i;
u_char cmd, nstate, tshift, target;
register caddr_t tp = (caddr_t) esp->e_cmdarea;
/*
* The only reason that this should happen
* is if we have a re-selection attempt starting.
*/
if (INTPENDING(esp)) {
ESP_PREEMPT(esp);
LOG_STATE(ACTS_PREEMPTED, esp->e_stat, Tgt(sp), Lun(sp));
espsvc(esp); /* bugid 1161712 */
return (FALSE);
}
esp->e_sdtr = esp->e_omsglen = 0;
target = Tgt(sp);
tshift = 1<<target;
#ifdef ESPDEBUG
esp->e_xfer = sp->cmd_dmacount;
#endif /* ESPDEBUG */
/*
* The ESP chip will only automatically
* send 6, 10 or 12 byte SCSI cmds.
*/
switch (legal_cmd_len = sp->cmd_cdblen) {
case CDB_GROUP0:
case CDB_GROUP1:
case CDB_GROUP5:
break;
default:
legal_cmd_len = 0;
break;
}
if (((esp->e_targets & tshift) == 0) || (esp->e_nodisc & tshift) ||
(sp->cmd_pkt.pkt_flags & FLAG_NODISCON) ||
(scsi_options & SCSI_OPTIONS_DR) == 0) {
cmd = CMD_SEL_NOATN;
nstate = STATE_SELECT_NOATN;
} else {
/*
* Okay, we're at least going to send an identify message.
*/
*tp++ = MSG_DR_IDENTIFY | Lun(sp);
if (sp->cmd_flags & CFLAG_CMDPROXY) {
/*
* This is a proxy command. It will have
* a message to send as part of post-selection
* (e.g, MSG_ABORT or MSG_DEVICE_RESET)
*/
/*
* XXX: We should check to make sure that
* this is a valid PROXY command, i.e,
* a valid message length.
*/
esp->e_omsglen = sp->cmd_cdb[ESP_PROXY_DATA];
i = 0;
while (i < esp->e_omsglen) {
esp->e_cur_msgout[i] =
sp->cmd_cdb[ESP_PROXY_DATA+1+i];
i++;
}
sp->cmd_cdb[ESP_PROXY_RESULT] = FALSE;
cmd = CMD_SEL_STOP;
nstate = STATE_SELECT_N_SENDMSG;
legal_cmd_len = 0;
LOG_STATE(ACTS_PROXY, esp->e_stat,
esp->e_cur_msgout[0], nstate);
} else if ((esp->e_sync_known & tshift) ||
(scsi_options & SCSI_OPTIONS_SYNC) == 0) {
if (legal_cmd_len) {
cmd = CMD_SEL_ATN;
nstate = STATE_SELECT_NORMAL;
} else {
cmd = CMD_SEL_STOP;
nstate = STATE_SELECT_N_STOP;
}
} else {
/*
* Set up to send an extended message after
* we select and send the identify message.
*/
if (esp->e_weak & tshift) {
esp_make_sdtr(esp, 0, 0);
} else {
esp_make_sdtr(esp,
(int) esp->e_default_period,
(int) DEFAULT_OFFSET);
}
legal_cmd_len = 0;
cmd = CMD_SEL_STOP;
nstate = STATE_SELECT_N_SENDMSG;
LOG_STATE(ACTS_SYNCHOUT, esp->e_stat,
esp->e_default_period, DEFAULT_OFFSET);
/*
* XXX: Set sync known here because the Sony CDrom
* ignores the synch negotiation msg. Net effect
* is we negotiate on every I/O request forever.
*/
esp->e_sync_known |= (1<<target);
}
}
/*
* If we are a ESP100A or a ESP236, and in SCSI-2 mode
* we could also load another message here.
*/
/*
* Now load cdb (if any)
*/
for (i = 0; i < legal_cmd_len; i++) {
*tp++ = sp->cmd_cdbp[i];
}
if (legal_cmd_len) {
LOG_STATE(ACTS_CMD_START, esp->e_stat, sp->cmd_cdbp[0], nstate);
}
/*
* calculate total dma amount:
*/
esp->e_lastcount = ((u_long) tp) - ((u_long) esp->e_cmdarea);
/*
* load rest of chip registers
*/
ep->esp_busid = target;
ep->esp_sync_period = esp->e_period[target] & SYNC_PERIOD_MASK;
ep->esp_sync_offset = esp->e_offset[target] | esp->e_req_ack_delay;
if ((esp->e_type == FAS236) || (esp->e_type == FAS100A)) {
ep->esp_conf3 = esp->e_espconf3[target];
}
if ((scsi_options & SCSI_OPTIONS_PARITY) &&
(sp->cmd_pkt.pkt_flags & FLAG_NOPARITY)) {
ep->esp_conf = esp->e_espconf & ~ESP_CONF_PAREN;
}
SET_ESP_COUNT(ep, esp->e_lastcount);
if (DMAGA_REV(dmar) == ESC1_REV1) {
SET_DMAESC_COUNT(dmar, esp->e_lastcount);
}
dmar->dmaga_addr = esp->e_lastdma =
(((caddr_t)esp->e_cmdarea) - DVMA) | esp->e_dma_base;
if (DMAGA_REV(dmar) == ESC1_REV1) {
dmar->dmaga_csr |= (dmar->dmaga_csr & ~DMAGA_WRITE) |
(DMAGA_INTEN|DMAGA_ENDVMA);
} else {
dmar->dmaga_csr = (dmar->dmaga_csr & ~DMAGA_WRITE) |
(DMAGA_INTEN|DMAGA_ENDVMA);
}
if (dmar->dmaga_csr & DMAGA_INT_MASK) {
ESP_PREEMPT(esp);
LOG_STATE(ACTS_PREEMPTED, esp->e_stat, Tgt(sp), Lun(sp));
(void) esp_poll();
return (FALSE);
}
ep->esp_cmd = cmd | CMD_DMA;
New_state(esp, nstate);
LOG_STATE(nstate, esp->e_stat, target, Lun(sp));
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf(esp, "sel %d.%d cmd[ ", target, Lun(sp));
for (i = 0; i < sp->cmd_cdblen; i++)
printf("0x%x ", sp->cmd_cdbp[i]&0xff);
printf("]\n\tstate=%s\n", esp_state_name(esp->e_state));
}
#endif /* ESPDEBUG */
return (TRUE);
}
static int
esp_link_start(esp)
register struct esp *esp;
{
esp->e_nlinked++;
if ((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_COMMAND) {
esp->e_stat = esp->e_reg->esp_stat & ~ESP_STAT_RES;
}
if ((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_COMMAND) {
/*
* XXX need a way to mark this particular failure
*/
esp_printstate(esp, "Linked command not in command phase");
/*
* XXX: Needs to be 'soft' abort
*/
return (ACTION_ABORT_CURCMD);
}
/*
* we'll let esp_phasemanage do the dirty work from here on out...
*/
New_state(esp, ACTS_CMD_START);
return (ACTION_PHASEMANAGE);
}
/*
* Finish routines
*/
static int
esp_finish(esp)
register struct esp *esp;
{
short last_slot;
register int span_states = 0;
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register action;
char dkn;
if ((dkn = sp->cmd_pkt.pkt_pmon) >= 0) {
dk_busy &= ~(1<<dkn);
}
last_slot = esp->e_last_slot = esp->e_cur_slot;
esp->e_cur_slot = UNDEFINED;
esp->e_ncmds -= 1;
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf(esp, "%d.%d; cmds=%d disc=%d npoll=%d; lastmsg 0x%x\n",
Tgt(sp), Lun(sp), esp->e_ncmds, esp->e_ndisc,
esp->e_npolling, esp->e_last_msgin);
printf("\treason '%s'; cmd state 0x%b\n",
scsi_rname(sp->cmd_pkt.pkt_reason),
sp->cmd_pkt.pkt_state, state_bits);
}
#endif /* ESPDEBUG */
if (esp->e_last_msgin == MSG_LINK_CMPLT ||
esp->e_last_msgin == MSG_LINK_CMPLT_FLAG) {
span_states++;
}
/*
* XXX: ELSE WE NEED TO SET SPAN_STATES FOR COMMANDS COMPLETING
* XXX: WITH A CHECK CONDITION.
*/
if ((sp->cmd_pkt.pkt_state & STATE_GOT_STATUS) &&
((struct scsi_status *) sp->cmd_pkt.pkt_scbp)->sts_chk) {
/*
* In the case that we are getting a check condition
* clear our knowledge of synchronous capabilities.
* This will unambiguously force a renegotiation
* prior to any possible data transfer (we hope),
* including the data transfer for a UNIT ATTENTION
* condition generated by somebody powering on and
* off a target.
*/
if (esp->e_offset[Tgt(sp)] != 0) {
esp->e_sync_known &= ~(1<<Tgt(sp));
}
}
if (sp->cmd_pkt.pkt_state & STATE_XFERRED_DATA) {
register struct dataseg *segtmp = sp->cmd_subseg.d_next;
register i;
/*
* XXX : FIX ME NEED TO MERGE TOGETHER OVERLAPPING AND
* XXX : ADJACENT SEGMENTS!!!
*/
if (segtmp != (struct dataseg *) 0 && segtmp->d_count) {
panic("esp_finish: more than one segment with data");
/* NOTREACHED */
}
/*
* Walk through all data segments and count up transfer counts
*/
i = 0;
segtmp = &sp->cmd_subseg;
while (segtmp) {
i += segtmp->d_count;
segtmp = segtmp->d_next;
}
sp->cmd_pkt.pkt_resid = sp->cmd_dmacount - i;
#ifdef ESPDEBUG
if (INFORMATIVE && sp->cmd_pkt.pkt_resid) {
eprintf(esp, "%d.%d finishes with %d resid\n",
Tgt(sp), Lun(sp), sp->cmd_pkt.pkt_resid);
}
#endif /* ESPDEBUG */
}
New_state(esp, STATE_FREE);
esp->e_slots[last_slot] = (struct scsi_cmd *) 0;
if (sp->cmd_pkt.pkt_flags & FLAG_NOINTR) {
esp->e_npolling -= 1;
(*sp->cmd_pkt.pkt_comp)(sp);
action = ACTION_RETURN;
} else if (span_states > 0) {
/*
* If we're going to start a linked command,
* hold open the state of the host adapter...
*/
esp->e_state = ACTS_SPANNING;
(*sp->cmd_pkt.pkt_comp)(sp);
esp->e_state = STATE_FREE;
/*
* This is we can check upon return that
* the target driver did the right thing...
*
* If the target driver didn't do the right
* thing, we have to abort the operation.
*/
if (esp->e_slots[last_slot] == 0) {
/*
* XXX: HERE IS WHERE WE NEED A PROXY COMMAND IN
* XXX: IN ORDER TO SEND AN ABORT OPERATION MESSAGE
* XXX: TO THE TARGET!!!
*/
esplog(esp, LOG_ERR,
"linked command not started by driver");
action = ACTION_RESET;
} else {
esp->e_cur_slot = last_slot;
action = esp_link_start(esp);
}
} else {
(*sp->cmd_pkt.pkt_comp)(sp);
action = ACTION_SEARCH;
}
return (action);
}
/*
* Interrupt Service Section
*/
/*
* Poll for command completion (i.e., no interrupts)
* time is in usec (and will not be very accurate)
*/
static int
esp_dopoll(esp, limit)
register struct esp *esp;
int limit;
{
register int i;
/*
* timeout is not very accurate since we don't know how
* long the poll takes
* also if the packet gets started fairly late, we may
* timeout prematurely
*/
if (limit == 0)
limit = POLL_TIMEOUT;
for (i = 0; i < limit; i += 100) {
if (esp_poll() == 0)
DELAY(100);
if (esp->e_state == STATE_FREE)
break;
}
if (i >= limit && esp->e_state != STATE_FREE) {
esp_printstate(esp, "polled command timeout");
return (-1);
}
return (0);
}
/*
* Autovector Interrupt Entry Point.
*/
static int
esp_poll()
{
register struct esp *esp;
register int serviced = 0;
register int search = TRUE;
if (esp_softc == (struct esp *) 0)
return (serviced);
while (search == TRUE) {
search = FALSE;
for (esp = esp_softc; esp != (struct esp *) 0;
esp = esp->e_next) {
if (esp->e_tran.tran_start) {
register s = splr(esp->e_tran.tran_spl);
#ifdef sun4m
/*
* If s > e_tran.tran_spl, it must be either
* boot or dump time, or a polled command,
* so just do it!
*/
if ((s <= esp->e_tran.tran_spl) &&
(esp->e_npolling == 0)) {
if (s != esp->e_spl) {
(void) splx(s);
continue;
}
}
#endif /* sun4m */
if (INTPENDING(esp)) {
espsvc(esp);
serviced |= (1<<(CNUM));
search = TRUE;
}
(void) splx(s);
}
}
}
if (serviced) {
/*
* Did we service more than one host
* adapter for this hardware interrupt?
*/
if (serviced & (serviced-1)) {
esp_nmultsvc++;
}
/*
* Record the fact that we fielded a hard interrupt
*/
esp_nhardints++;
serviced = 1;
}
return (serviced);
}
#ifdef VECTORED_INTERRUPTS
/*
* Vectored interrupt entry point
*/
static int
esp_intr(esp)
struct esp *esp;
{
register s = splr(esp->e_tran.tran_spl);
if (INTPENDING(esp)) {
espsvc(esp);
}
(void) splx(s);
}
#endif /* VECTORED_INTERRUPTS */
/*
* General interrupt service routine.
*/
static void
espsvc(esp)
register struct esp *esp;
{
static int (*evec[])() = {
esp_finish_select,
esp_reconnect,
esp_phasemanage,
esp_finish,
esp_reset_recovery,
esp_istart,
esp_abort_curcmd,
esp_abort_allcmds,
esp_reset_bus,
esp_handle_selection
};
register int action;
register u_char intr;
register struct espreg *ep = esp->e_reg;
register struct dmaga *dmar = esp->e_dma;
/*
* Make sure that the DMAGA is *not* still active.
* If we don't disable further interrupts, we
* get Bus timeouts in trying to access the ESP
* chip for Rev-1 DMA gate arrays.
*/
if (DMAGA_REV(dmar) == DMA_REV1) {
dmar->dmaga_csr &= ~DMAGA_INTEN;
}
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf (esp, "dma csr 0x%b addr 0x%x\n",
esp->e_dma->dmaga_csr, dmaga_bits, esp->e_dma->dmaga_addr);
}
#endif /* ESPDEBUG */
/*
* A read of ESP interrupt register clears interrupt,
* so any other volatile information needs to be latched
* up prior to reading the interrupt register.
*/
/*
* Even if we aren't in STATE_SELECTING, latch
* up the step register (for future handling
* of being in target mode operation)
*/
esp->e_step = (ep->esp_step & ESP_STEP_MASK);
esp->e_stat = ep->esp_stat;
esp->e_intr = intr = ep->esp_intr;
/*
* unclear what could cause a gross error;
* most of the time we get a data overrun after this.
*/
if (esp->e_stat & ESP_STAT_GERR) {
esplog(esp, LOG_ERR,
"gross error in esp status (%x)", esp->e_stat);
IPRINTF5(
"esp_cmd=%x, stat=%x, intr=%x, step=%x, fifoflag=%x\n",
ep->esp_cmd, esp->e_stat, esp->e_intr, esp->e_step,
ep->esp_fifo_flag);
if (esp->e_cur_slot != UNDEFINED) {
struct scsi_cmd *sp = CURRENT_CMD(esp);
if (sp->cmd_pkt.pkt_reason == CMD_CMPLT) {
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
}
} else {
action = ACTION_ABORT_ALLCMDS;
goto start_action;
}
}
/*
* was there a dmaga error that caused espsvc() to be called?
*/
if (esp->e_dma->dmaga_csr & DMAGA_ERRPEND) {
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
esplog(esp, LOG_ERR, "Unrecoverable DMA error on dma");
if (esp->e_cur_slot != UNDEFINED) {
struct scsi_cmd *sp = CURRENT_CMD(esp);
if (sp->cmd_pkt.pkt_reason == CMD_CMPLT)
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
action = ACTION_RESET;
goto start_action;
}
}
/*
* While some documentation claims that the
* ESP100A's msb in the stat register is an
* INTERRUPT PENDING bit, an errata sheet
* warned that you shouldn't depend on that
* being so (unless you're an ESP-236)
*/
if (esp->e_type != ESP236) {
esp->e_stat &= ~ESP_STAT_RES;
}
#ifdef ESPDEBUG
if (DEBUGGING) {
esp_stat_int_print(esp);
printf("\tState %s Laststate %s\n",
esp_state_name(esp->e_state),
esp_state_name(esp->e_laststate));
}
#endif /* ESPDEBUG */
/*
* Based upon the current state of the host adapter driver
* we should be able to figure out what to do with an interrupt.
* We have several possible interrupt sources, some of them
* modified by various status conditions.
*
* Basically, we'll get an interrupt through the dma gate array
* for one or more of the following three conditions:
*
* 1. The ESP is asserting an interrupt request.
*
* 2. The dma gate array counter has reached zero
* and TERMINAL COUNT interrupts have been enabled.
*
* 3. There has been a memory exception of some kind.
*
* In the latter two cases we are either in one of the SCSI
* DATA phases or are using dma in sending a command to a
* target. We will let the various handlers for these kind
* of states decode any error conditions in the gate array.
*
* The ESP asserts an interrupt with one or more of 8 possible
* bits set in its interrupt register. These conditions are
* SCSI bus reset detected, an illegal command fed to the ESP,
* one of DISCONNECT, BUS SERVICE, FUNCTION COMPLETE conditions
* for the ESP, a Reselection interrupt, or one of Selection
* or Selection with Attention.
*
* Of these possible interrupts, we can deal with some right
* here and now, irrespective of the current state of the driver.
*
*/
if (intr & ESP_INT_RESET) {
/*
* If we detect a SCSI reset, we blow away the current
* command (if there is one) and all disconnected commands
* because we now don't know the state of them at all.
*/
action = ACTION_FINRST;
} else if (intr & ESP_INT_ILLEGAL) {
/*
* This should not happen. The one situation where
* we can get an ILLEGAL COMMAND interrupt is due to
* a bug in the ESP100 during reselection which we
* should be handling in esp_reconnect().
*/
esp_printstate (esp, "ILLEGAL bit set");
action = ACTION_ABORT_CURCMD;
} else if (intr & (ESP_INT_SEL|ESP_INT_SELATN)) {
action = ACTION_SELECT;
} else if (intr & ESP_INT_RESEL) {
if (esp->e_state & STATE_SELECTING) {
action = ACTION_FINSEL;
} else if (esp->e_state != STATE_FREE) {
/*
* this 'cannot happen'.
*/
esp_printstate(esp, "illegal reselection");
action = ACTION_RESET;
} else {
action = ACTION_RESEL;
}
} else {
/*
* The rest of the reasons for an interrupt, including
* interrupts just from the dma gate array itself, can
* be handled based purely on the state that the driver
* is currently in now.
*/
if (esp->e_state & STATE_SELECTING) {
action = ACTION_FINSEL;
} else if (esp->e_state & STATE_ITPHASES) {
action = ACTION_PHASEMANAGE;
} else {
#ifdef ESPDEBUG
if (INFORMATIVE) {
esp_printstate(esp, "spurious interrupt");
}
#endif /* ESPDEBUG */
esplog(esp, LOG_ERR, "spurious interrupt");
action = ACTION_RETURN;
}
}
start_action:
while (action != ACTION_RETURN) {
action = (*evec[action])(esp);
}
/*
* Reenable interrupts for the DMA gate array
*/
if (DMAGA_REV(dmar) == DMA_REV1) {
dmar->dmaga_csr |= DMAGA_INTEN;
}
}
/*
* Manage phase transitions.
*/
static int
esp_phasemanage(esp)
struct esp *esp;
{
register u_char state;
register int action;
static int (*pvecs[])() = {
esp_handle_cmd_start,
esp_handle_cmd_done,
esp_handle_msg_out,
esp_handle_msg_out_done,
esp_handle_msg_in,
esp_handle_more_msgin,
esp_handle_msg_in_done,
esp_handle_clearing,
esp_handle_data,
esp_handle_data_done,
esp_handle_c_cmplt
};
do {
state = esp->e_state;
if (state == ACTS_UNKNOWN) {
action = esp_handle_unknown(esp);
} else if (state == STATE_FREE || state > ACTS_ENDVEC) {
esplog(esp, LOG_ERR, "lost state in phasemanage");
action = ACTION_ABORT_ALLCMDS;
} else {
action = (*pvecs[state-1]) (esp);
}
} while (action == ACTION_PHASEMANAGE);
return (action);
}
static int
esp_handle_unknown(esp)
register struct esp *esp;
{
register u_char newstate;
if (esp->e_intr & ESP_INT_DISCON) {
/*
* Okay. What to do now? Let's try (for the time being)
* assuming that the target went south and dropped busy,
* as a disconnect implies that either we received
* a completion or a disconnect message, or that we
* had sent an ABORT OPERATION or BUS DEVICE RESET
* message. In either case, we expected the disconnect
* and should have fielded it elsewhere.
*
* If we see a chip disconnect here, this is an unexpected
* loss of BSY*. Clean up the state of the chip and return.
*
*/
#ifdef ESPDEBUG
if (INFORMATIVE) {
esp_printstate(esp, "unexpected bus free");
}
#endif /* ESPDEBUG */
esp_chip_disconnect(esp);
CURRENT_CMD(esp)->cmd_pkt.pkt_reason = CMD_UNX_BUS_FREE;
LOG_STATE(ACTS_CMD_LOST, esp->e_stat, esp->e_xfer, -1);
return (ACTION_FINISH);
}
switch (esp->e_stat & ESP_PHASE_MASK) {
case ESP_PHASE_DATA_IN:
case ESP_PHASE_DATA_OUT:
newstate = ACTS_DATA;
break;
case ESP_PHASE_MSG_OUT:
newstate = ACTS_MSG_OUT;
break;
case ESP_PHASE_MSG_IN:
newstate = ACTS_MSG_IN;
break;
case ESP_PHASE_STATUS:
esp->e_reg->esp_cmd = CMD_FLUSH;
#ifdef ESP_TEST_PARITY
if (esp_ptest_status & (1<<Tgt(CURRENT_CMD(esp)))) {
esp->e_reg->esp_cmd = CMD_SET_ATN;
}
#endif /* ESP_TEST_PARITY */
esp->e_reg->esp_cmd = CMD_COMP_SEQ;
esp->e_state = ACTS_C_CMPLT;
LOG_STATE(ACTS_C_CMPLT, esp->e_stat, -1, -1);
return (ACTION_RETURN);
case ESP_PHASE_COMMAND:
newstate = ACTS_CMD_START;
break;
default:
esp_printstate(esp, "Unknown bus phase");
return (ACTION_RESET);
}
esp->e_state = newstate;
return (ACTION_PHASEMANAGE);
}
static int
esp_handle_cmd_start(esp)
struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
struct scsi_cmd *sp = CURRENT_CMD(esp);
register u_long cmd_distance;
/*
* Check for command overflow.
*/
cmd_distance = (u_long) sp->cmd_cdbp - (u_long) sp->cmd_pkt.pkt_cdbp;
if (cmd_distance > (u_long) sp->cmd_cdblen) {
sp->cmd_pkt.pkt_reason = CMD_CMD_OVR;
return (ACTION_ABORT_CURCMD);
}
if (cmd_distance == 0) {
LOG_STATE(ACTS_CMD_START, esp->e_stat, sp->cmd_cdbp[0], -1);
}
/*
* Stuff next command byte into fifo
*/
ep->esp_cmd = CMD_FLUSH;
/*
* To make sure that FLUSH is done - esp in the case of faster
* Hypersparc processors.
*/
while (FIFO_CNT(ep));
SET_ESP_COUNT(ep, 1);
ep->esp_fifo_data = *(sp->cmd_cdbp++);
ep->esp_cmd = CMD_TRAN_INFO;
New_state(esp, ACTS_CMD_DONE);
return (ACTION_RETURN);
}
static int
esp_handle_cmd_done(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register u_char intr = esp->e_intr;
/*
* The NOP command is required following a COMMAND
* or MESSAGE OUT phase in order to unlatch the
* FIFO flags register. This is needed for all
* ESP chip variants.
*/
esp->e_reg->esp_cmd = CMD_NOP;
/*
* We should have gotten a BUS SERVICE interrupt.
* If it isn't that, and it isn't a DISCONNECT
* interrupt, we have a "cannot happen" situation.
*/
if ((intr & ESP_INT_BUS) == 0) {
if ((intr & ESP_INT_DISCON) == 0) {
esp_printstate(esp, "cmd transmission error");
return (ACTION_ABORT_CURCMD);
}
} else {
sp->cmd_pkt.pkt_state |= STATE_SENT_CMD;
}
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
/*
* Begin to send a message out
*/
static int
esp_handle_msg_out(esp)
register struct esp *esp;
{
register i;
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register struct espreg *ep = esp->e_reg;
/*
* Check to make *sure* that we are really
* in MESSAGE OUT phase. If the last state
* was ACTS_MSG_OUT_DONE, then we are trying
* to resend a message that the target stated
* had a parity error in it.
*
* If this is the case, and mark completion reason as CMD_NOMSGOUT.
* XXX: Right now, we just *drive* on. Should we abort the command?
*/
if ((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_MSG_OUT &&
esp->e_laststate == ACTS_MSG_OUT_DONE) {
esplog(esp, LOG_WARNING,
"Target %d refused message resend", Tgt(sp));
sp->cmd_pkt.pkt_reason = CMD_NOMSGOUT;
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
/*
* Clean the fifo.
*/
ep->esp_cmd = CMD_FLUSH;
/*
* See if we have a valid message to send
*/
if (esp->e_omsglen == 0) {
/*
* no valid message? Send a no-op anyway...
*/
esp->e_cur_msgout[0] = MSG_NOP;
esp->e_omsglen = 1;
}
/*
* To make sure that FLUSH is done - esp in the case of faster
* Hypersparc processors.
*/
while (FIFO_CNT(ep));
for (i = 0; i < esp->e_omsglen; i++) {
ep->esp_fifo_data = esp->e_cur_msgout[i];
}
ep->esp_cmd = CMD_TRAN_INFO;
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf(esp, "msg out: %s", scsi_mname(esp->e_cur_msgout[0]));
for (i = 1; i < esp->e_omsglen; i++)
printf(" %x", esp->e_cur_msgout[i]);
printf("\n");
}
#endif /* ESPDEBUG */
New_state(esp, ACTS_MSG_OUT_DONE);
return (ACTION_RETURN);
}
static int
esp_handle_msg_out_done(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register struct espreg *ep = esp->e_reg;
register u_char msgout, phase, fifocnt;
register int target = Tgt(sp);
msgout = esp->e_cur_msgout[0];
/*
* If the ESP disconnected, then the message we sent caused
* the target to decide to drop BSY* and clear the bus.
*/
if (esp->e_intr == ESP_INT_DISCON) {
if (msgout == MSG_DEVICE_RESET || msgout == MSG_ABORT) {
esp_chip_disconnect(esp);
if (msgout == MSG_DEVICE_RESET) {
esp->e_offset[target] = 0;
esp->e_sync_known &= ~(1<<target);
}
EPRINTF2 ("Succesful %s message to target %d\n",
scsi_mname(msgout), target);
sp->cmd_pkt.pkt_reason = CMD_CMPLT;
if (sp->cmd_flags & CFLAG_CMDPROXY) {
sp->cmd_cdb[ESP_PROXY_RESULT] = TRUE;
}
return (ACTION_FINISH);
}
/*
* If the target dropped busy on any other message, it
* wasn't expected. We will let the code in esp_phasemanage()
* handle this unexpected bus free event.
*/
goto out;
}
/*
* What phase have we transitioned to?
*/
phase = esp->e_stat & ESP_PHASE_MASK;
/*
* Save current fifo count
*/
fifocnt = FIFO_CNT(ep);
/*
* As per the ESP errata sheets, this must be done for
* all ESP chip variants.
*
* This releases the FIFO counter from its latched state.
* Note that we read the fifo counter above prior to doing
* this.
*/
ep->esp_cmd = CMD_NOP;
/*
* Clean the fifo? Yes, if and only if we haven't
* transitioned to Synchronous DATA IN phase.
* The ESP chip manual notes that in the case
* that the target has shifted to Synchronous
* DATA IN phase, that while the FIFO count
* register stays latched up with the number
* of bytes not transferred out, that the fifo
* itself is cleared and will contain only
* the incoming data bytes.
*
* The manual doesn't state what happens in
* other receive cases (transition to STATUS,
* MESSAGE IN, or asynchronous DATA IN phase),
* but I'll assume that there is probably
* a single-byte pad between the fifo and
* the SCSI bus which the ESP uses to hold
* the currently asserted data on the bus
* (known valid by a true REQ* signal). In
* the case of synchronous data in, up to
* 15 bytes of data could arrive, so the
* ESP must have to make room for by clearing
* the fifo, but in other cases it can just
* hold the current byte until the next
* ESP chip command that would cause a
* data transfer.
*/
if (fifocnt != 0 && (phase != ESP_PHASE_DATA_IN ||
esp->e_offset[target] == 0)) {
ep->esp_cmd = CMD_FLUSH;
}
/*
* If we finish sending a message out, and we are
* still in message out phase, then the target has
* detected one or more parity errors in the message
* we just sent and it is asking us to resend the
* previous message.
*/
if ((esp->e_intr & ESP_INT_BUS) && phase == ESP_PHASE_MSG_OUT) {
/*
* As per SCSI-2 specification, if the message to
* be re-sent is greater than one byte, then we
* have to set ATN*.
*/
if (esp->e_omsglen > 1) {
ep->esp_cmd = CMD_SET_ATN;
}
esplog(esp, LOG_ERR,
"SCSI bus MESSAGE OUT phase parity error");
sp->cmd_pkt.pkt_statistics |= STAT_PERR;
New_state(esp, ACTS_MSG_OUT);
return (ACTION_PHASEMANAGE);
}
/*
* Count that we sent a SYNCHRONOUS DATA TRANSFER message.
*/
if (esp->e_omsglen == 5 && msgout == MSG_EXTENDED &&
esp->e_cur_msgout[2] == MSG_SYNCHRONOUS) {
esp->e_sdtr++;
}
out:
esp->e_last_msgout = msgout;
esp->e_omsglen = 0;
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
static int
esp_handle_clearing(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register action;
register u_char lmsg = esp->e_last_msgin;
if (esp->e_intr != ESP_INT_DISCON) {
if (lmsg != MSG_LINK_CMPLT && lmsg != MSG_LINK_CMPLT_FLAG) {
/*
* If the chip/target didn't disconnect from the
* bus, that is a gross fatal error.
*/
esplog (esp, LOG_WARNING,
"Target %d didn't disconnect after sending %s",
Tgt(sp), scsi_mname(lmsg));
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
return (ACTION_ABORT_CURCMD);
} else {
/*
* In this case, the last message in was a 'linked
* command complete' message and the target stays
* connected. We don't fiddle with any of the
* settings on the ESP chip. We return state
* such that the completing command is finished
* up and depend upon the finish routine to
* handle the case that a new command has to
* be available to start right away for this target.
*/
esp->e_last_msgout = 0xff;
esp->e_omsglen = 0;
return (ACTION_FINISH);
}
}
/*
* At this point the ESP chip has disconnected. The bus should
* be either quiet or someone may be attempting a reselection
* of us (or somebody else). Call the routine the sets the
* chip back to a correct and known state.
*/
esp_chip_disconnect(esp);
/*
* If the last message in was a disconnect, search
* for new work to do, else return to call esp_finish()
*/
if (lmsg == MSG_DISCONNECT) {
sp->cmd_pkt.pkt_statistics |= STAT_DISCON;
sp->cmd_flags |= CFLAG_CMDDISC;
esp->e_disconnects++;
esp->e_ndisc++;
New_state(esp, STATE_FREE);
esp->e_last_slot = esp->e_cur_slot;
esp->e_cur_slot = UNDEFINED;
action = ACTION_SEARCH;
EPRINTF2 ("disconnecting %d.%d\n", Tgt(sp), Lun(sp));
} else {
action = ACTION_FINISH;
}
esp->e_last_msgout = 0xff;
esp->e_omsglen = 0;
return (action);
}
static int
esp_handle_data(esp)
struct esp *esp;
{
register i;
struct espreg *ep = esp->e_reg;
struct dmaga *dmar = esp->e_dma;
struct scsi_cmd *sp = CURRENT_CMD(esp);
register int sending;
if (IS_53C90(esp)) {
ep->esp_cmd = CMD_NOP; /* per ESP errata sheet */
}
if ((sp->cmd_flags & CFLAG_DMAVALID) == 0) {
esp_printstate(esp, "unexpected data phase");
/*
* XXX: This isn't the right reason
*/
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
return (ACTION_ABORT_CURCMD);
} else {
sending = (sp->cmd_flags & CFLAG_DMASEND)? 1 : 0;
}
if (sp->cmd_flags & CFLAG_NEEDSEG) {
/*
* We can currently handle truncating the current
* subsegment (in case a restore pointers is followed
* by a re-entry into data phase). XXX Else we die
* horribly XXX
*/
register struct dataseg *segp = sp->cmd_cursubseg;
IPRINTF3 ("data new seg: datap 0x%x base 0x%x count 0x%x\n",
sp->cmd_data, segp->d_base, segp->d_count);
if (sp->cmd_data >= segp->d_base &&
(sp->cmd_data < (segp->d_base + segp->d_count))) {
/*
* We are backing up within the current
* segment. Adjust the count field of the
* current segment to reflect that we
* only got 'this far' with it to date.
*/
segp->d_count = sp->cmd_data - segp->d_base;
sp->cmd_flags ^= CFLAG_NEEDSEG;
} else {
/*
* XXX: bad bad bad...
*/
panic("need new seg in esp");
/* NOTREACHED */
}
}
i = scsi_chkdma(sp, ESP_MAX_DMACOUNT);
if (i == 0) {
esp_printstate(esp, "data transfer overrun");
sp->cmd_pkt.pkt_reason = CMD_DATA_OVR;
/*
* A fix for bug id 1048141- if we get data transfer
* overruns, assume we have a weak scsi bus. Note that
* this won't catch consistent underruns or other
* noise related syndromes.
*/
esp_sync_backoff(esp, sp);
return (ACTION_ABORT_CURCMD);
}
esp->e_lastcount = i;
#ifdef ESPDEBUG
esp->e_xfer = i;
#endif /* ESPDEBUG */
#ifdef sun4m
if (sp->cmd_flags & CFLAG_DMAKEEP) {
dmar->dmaga_addr = esp->e_lastdma =
(btop(sp->cmd_data) < dvmasize) ?
(sp->cmd_data | esp->e_dma_base) : sp->cmd_data;
} else {
dmar->dmaga_addr = esp->e_lastdma =
(btop(sp->cmd_data) < BIGSBUSMAP_SIZE) ?
(sp->cmd_data | BIGSBUSDVMA_BASE) : sp->cmd_data;
}
#else /* sun4m */
dmar->dmaga_addr = esp->e_lastdma =
(btop(sp->cmd_data) < dvmasize) ?
(sp->cmd_data | esp->e_dma_base) : sp->cmd_data;
#endif /* sun4m */
SET_ESP_COUNT(ep, i);
if (DMAGA_REV(dmar) == ESC1_REV1) {
SET_DMAESC_COUNT(dmar, i);
}
EPRINTF4 ("%d.%d cmd 0x%x to xfer %x\n", Tgt(sp), Lun(sp),
sp->cmd_pkt.pkt_cdbp[0], i);
if ((esp->e_stat & ESP_PHASE_MASK) == ESP_PHASE_DATA_OUT) {
if (!sending) {
esplog(esp, LOG_ERR,
"unwanted data out for Target %d", Tgt(sp));
sp->cmd_pkt.pkt_reason = CMD_DMA_DERR;
return (ACTION_ABORT_CURCMD);
}
LOG_STATE(ACTS_DATAOUT, esp->e_stat, i, -1);
} else {
if (sending) {
esplog (esp, LOG_ERR,
"unwanted data in for Target %d", Tgt(sp));
sp->cmd_pkt.pkt_reason = CMD_DMA_DERR;
return (ACTION_ABORT_CURCMD);
}
dmar->dmaga_csr |= DMAGA_WRITE;
LOG_STATE(ACTS_DATAIN, esp->e_stat, i, -1);
}
#ifdef ESP_TEST_PARITY
if (!sending && (esp_ptest_data_in & (1<<Tgt(sp)))) {
ep->esp_cmd = CMD_SET_ATN;
}
#endif /* ESP_TEST_PARITY */
dmar->dmaga_csr |= DMAGA_ENDVMA|DMAGA_INTEN;
ep->esp_cmd = CMD_TRAN_INFO|CMD_DMA;
New_state(esp, ACTS_DATA_DONE);
return (ACTION_RETURN);
}
static int
esp_handle_data_done(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
register struct dmaga *dmar = esp->e_dma;
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register u_long xfer_amt;
char spurious_data, do_drain_fifo, was_sending;
register u_char stat, tgt, fifoamt;
tgt = Tgt(sp);
stat = esp->e_stat;
was_sending = (sp->cmd_flags & CFLAG_DMASEND) ? 1 : 0;
spurious_data = do_drain_fifo = 0;
/*
* Check for DMAGA errors (parity or memory fault)
*/
if (dmar->dmaga_csr & DMAGA_ERRPEND) {
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
esplog(esp, LOG_ERR, "Unrecoverable DMA error on dma %s",
(was_sending) ? "send" : "receive");
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
return (ACTION_RESET);
}
/*
* Data Receive conditions:
*
* Check for parity errors. If we have a parity error upon
* receive, the ESP chip has asserted ATN* for us already.
*
* For Rev-1 and Rev-2 dma gate arrayts,
* make sure the last bytes have flushed.
*/
if (!was_sending) {
#ifdef ESP_TEST_PARITY
if (esp_ptest_data_in & (1<<tgt)) {
esp_ptest_data_in = 0;
stat |= ESP_STAT_PERR;
}
#endif /* ESP_TEST_PARITY */
if (stat & ESP_STAT_PERR) {
esplog (esp, LOG_ERR,
"SCSI bus DATA IN phase parity error");
esp->e_cur_msgout[0] = MSG_INITIATOR_ERROR;
esp->e_omsglen = 1;
sp->cmd_pkt.pkt_statistics |= STAT_PERR;
}
xfer_amt = 0;
/*
* For DMA gate arrays, the PACKCNT field of the DMA
* CSR register indicates how many bytes are still
* latched up and need to be drained to memory.
*
* For the DMA+ CSR, the PACKCNT field will either
* be zero or non-zero, indicating a empty/non-empty
* D_CACHE. The DRAIN bit has no effect.
*
* The DMA2 CSR (as of right now- 6/1/90) actually
* requires a write to the CSR register to force
* the drain of the D_CACHE, so the practice of
* writing the otherwise null-effect DRAIN bit is
* still required.
*/
while (DMAGA_NPACKED(dmar) != 0 && xfer_amt < 100) {
if (DMAGA_REV(dmar) != ESC1_REV1) {
dmar->dmaga_csr |= DMAGA_DRAIN;
}
DELAY(200);
xfer_amt++;
}
if (xfer_amt >= 100 && DMAGA_NPACKED(dmar)) {
esplog (esp, LOG_ERR, "DMA gate array won't drain");
/*
* This isn't quite right...
*/
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
return (ACTION_ABORT_CURCMD);
}
}
/*
* clear state of dma gate array
*/
while (dmar->dmaga_csr & DMAGA_REQPEND);
dmar->dmaga_csr |= DMAGA_FLUSH;
dmar->dmaga_csr &= ~(DMAGA_ENDVMA|DMAGA_WRITE|DMAGA_ENATC);
/*
/*
* Check to make sure we're still connected to the target.
* If the target dropped the bus, that is a fatal error.
* We don't even attempt to count what we were transferring
* here. Let esp_handle_unknown clean up for us.
*/
if (esp->e_intr != ESP_INT_BUS) {
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
/*
* Figure out how far we got.
* Latch up fifo amount first.
*/
fifoamt = FIFO_CNT(ep);
if (stat & ESP_STAT_XZERO) {
xfer_amt = esp->e_lastcount;
} else {
GET_ESP_COUNT(ep, xfer_amt);
xfer_amt = esp->e_lastcount - xfer_amt;
}
/*
* Unconditionally knock off by the amount left
* in the fifo if we were sending out the SCSI bus.
*
* If we were receiving from the SCSI bus, believe
* what the chip told us (either XZERO or by the
* value calculated from the counter register).
* The reason we don't look at the fifo for
* incoming data is that in synchronous mode
* the fifo may have further data bytes, and
* for async mode we assume that all data in
* the fifo will have been transferred before
* the esp asserts an interrupt.
*/
if (was_sending) {
xfer_amt -= fifoamt;
}
/*
* If this was a synchronous transfer, flag it.
* Also check for the errata condition of long
* last REQ/ pulse for some synchronous targets
*/
if (esp->e_offset[tgt]) {
/*
* flag that a synchronous data xfer took place
*/
sp->cmd_pkt.pkt_statistics |= STAT_SYNC;
esp->e_nsync++;
if (IS_53C90(esp)) {
static char *spur =
"Spurious %s phase from target %d\n";
u_char phase;
/*
* Okay, latch up new status register value
*/
/*
* Get a new stat from the esp chip register.
*/
esp->e_stat = stat = phase = ep->esp_stat;
phase &= ESP_PHASE_MASK;
/*
* Now, if we're still (maybe) in a data phase,
* check to be real sure that we are...
*/
if (phase == ESP_PHASE_DATA_IN) {
if (FIFO_CNT(ep) == 0)
spurious_data = 1;
} else if (phase == ESP_PHASE_DATA_OUT) {
if ((ep->esp_fifo_flag & ESP_FIFO_ONZ) == 0)
spurious_data = -1;
}
if (spurious_data) {
ep->esp_cmd = CMD_MSG_ACPT;
esplog (esp, LOG_ERR,
spur, (spurious_data < 0) ?
"data out": "data in", tgt);
/*
* 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 fix, 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.
*/
esp->e_sync_known &= ~(1<<tgt);
}
if (spurious_data == 0 && was_sending)
do_drain_fifo = 1;
} else {
/*
* The need to handle for the ESP100A the case
* of turning off/on a target, thus destroying
* it's sync. setting is covered in esp_finish()
* where a CHECK CONDITION status causes the
* esp->e_sync_known flag to be cleared.
*
* If we are doing synchronous DATA OUT,
* we should probably drain the fifo.
* If we are doing synchronous DATA IN,
* we really don't dare do that (in case
* we are going from data phase to data
* phase).
*/
if (was_sending)
do_drain_fifo = 1;
}
} else {
/*
* If we aren't doing Synchronous Data Transfers,
* definitely offload the fifo.
*/
do_drain_fifo = 1;
}
/*
* Drain the fifo here of any left over
* that weren't transferred (if desirable).
*/
if (do_drain_fifo) {
ep->esp_cmd = CMD_FLUSH;
}
/*
* adjust pointers...
*/
sp->cmd_data += xfer_amt;
sp->cmd_cursubseg->d_count += xfer_amt;
#ifdef ESPDEBUG
if ((scsi_options & SCSI_DEBUG_HA) || espdebug > 1 ||
((espdebug > 1) &&
esp->e_lastcount >= 0x200 && xfer_amt & 0x1ff)) {
eprintf (esp,
"DATA %s phase for %d.%d did 0x%x of 0x%x bytes\n",
(was_sending)? "OUT" : "IN", tgt, Lun(sp),
xfer_amt, esp->e_lastcount);
esp_stat_int_print(esp);
esp_dump_datasegs(sp);
}
#endif /* ESPDEBUG */
sp->cmd_pkt.pkt_state |= STATE_XFERRED_DATA;
New_state(esp, ACTS_UNKNOWN);
if (spurious_data == 0) {
stat &= ESP_PHASE_MASK;
if (stat == ESP_PHASE_DATA_IN || stat == ESP_PHASE_DATA_OUT) {
esp->e_state = ACTS_DATA;
}
return (ACTION_PHASEMANAGE);
} else {
return (ACTION_RETURN);
}
}
static int
esp_handle_c_cmplt(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
struct espreg *ep = esp->e_reg;
register u_char sts, msg, msgout, intr, perr;
intr = esp->e_intr;
#ifdef ESP_TEST_PARITY
if (esp_ptest_status & (1<<Tgt(sp))) {
esp_ptest_status = 0;
esp->e_stat |= ESP_STAT_PERR;
} else if ((esp_ptest_msgin & (1<<Tgt(sp))) && esp_ptest_msg == 0) {
ep->esp_cmd = CMD_SET_ATN;
esp_ptest_msgin = 0;
esp_ptest_msg = -1;
esp->e_stat |= ESP_STAT_PERR;
}
#endif /* ESP_TEST_PARITY */
if (intr == ESP_INT_DISCON) {
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
if (perr = (esp->e_stat & ESP_STAT_PERR)) {
sp->cmd_pkt.pkt_statistics |= STAT_PERR;
}
msgout = 0;
msg = sts = 0xff;
/*
* The ESP manuals state that this sequence completes
* with a BUS SERVICE interrupt if just the status
* byte was receved, else a FUNCTION COMPLETE interrupt
* if both status and a message was received.
*
* The manuals also state that ATN* is asserted if
* bad parity is detected.
*
* The one case that we cannot handle is where we detect
* bad parity for the status byte, but the target refuses
* to go to MESSAGE OUT phase right away. This means that
* if that happens, we will misconstrue the parity error
* to be for the completion message, not the status byte.
*/
if (intr == ESP_INT_BUS) {
/*
* We only got the status byte.
*/
sts = ep->esp_fifo_data;
if (perr) {
/*
* If we get a parity error on a status byte
* assume that it was a CHECK CONDITION
*/
sts = STATUS_CHECK;
esplog (esp, LOG_ERR,
"SCSI bus STATUS phase parity error");
msgout = MSG_INITIATOR_ERROR;
}
} else {
sts = ep->esp_fifo_data; /* assume good status byte */
esp->e_last_msgin = msg = ep->esp_fifo_data;
if (perr) {
esplog (esp, LOG_ERR, msginperr);
msgout = MSG_MSG_PARITY;
}
}
if (sts != 0xff) {
sp->cmd_pkt.pkt_state |= STATE_GOT_STATUS;
*(sp->cmd_scbp++) = sts;
EPRINTF1 ("Status=0x%x\n", sts);
}
LOG_STATE(ACTS_STATUS, esp->e_stat, sts, -1);
if (msgout == 0) {
EPRINTF1 ("Completion Message=%s\n", scsi_mname(msg));
if (msg == MSG_COMMAND_COMPLETE ||
msg == MSG_LINK_CMPLT ||
msg == MSG_LINK_CMPLT_FLAG) {
/*
* Actually, if the message was a 'linked command
* complete' message, the target isn't going to be
* clearing the bus.
*/
New_state(esp, ACTS_CLEARING);
} else {
esp->e_imsglen = 1;
esp->e_imsgindex = 1;
New_state(esp, ACTS_MSG_IN_DONE);
return (esp_handle_msg_in_done(esp));
}
} else {
esp->e_cur_msgout[0] = msgout;
esp->e_omsglen = 1;
New_state(esp, ACTS_UNKNOWN);
}
LOG_STATE(ACTS_C_CMPLT, esp->e_stat, esp->e_xfer, -1);
if (intr != ESP_INT_BUS) {
ep->esp_cmd = CMD_MSG_ACPT;
return (ACTION_RETURN);
} else {
return (ACTION_PHASEMANAGE);
}
}
/*
*
*/
static int
esp_handle_msg_in(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
/*
* Pick up a message byte.
* Clear the FIFO so we
* don't get confused.
*/
ep->esp_cmd = CMD_FLUSH;
/*
* To make sure that FLUSH is done - esp in the case of faster
* Hypersparc processors.
*/
while (FIFO_CNT(ep));
if (IS_53C90(esp)) {
ep->esp_cmd = CMD_NOP;
}
ep->esp_cmd = CMD_TRAN_INFO;
esp->e_imsglen = 1;
esp->e_imsgindex = 0;
New_state(esp, ACTS_MSG_IN_DONE);
return (ACTION_RETURN);
}
/*
* We come here after issuing a MSG_ACCEPT
* command and are expecting more message bytes.
* The ESP should be asserting a BUS SERVICE
* interrupt status, but may have asserted
* a different interrupt in the case that
* the target disconnected and dropped BSY*.
*
* In the case that we are eating up message
* bytes (and throwing them away unread) because
* we have ATN* asserted (we are trying to send
* a message), we do not consider it an error
* if the phase has changed out of MESSAGE IN.
*/
static int
esp_handle_more_msgin(esp)
register struct esp *esp;
{
if (esp->e_intr & ESP_INT_BUS) {
if ((esp->e_stat & ESP_PHASE_MASK) == ESP_PHASE_MSG_IN) {
/*
* Fetch another byte of a message in.
*/
esp->e_reg->esp_cmd = CMD_TRAN_INFO;
New_state(esp, ACTS_MSG_IN_DONE);
return (ACTION_RETURN);
}
/*
* If we were gobbling up a message and we have
* changed phases, handle this silently, else
* complain. In either case, we return to let
* esp_phasemanage() handle things.
*
* If it wasn't a BUS SERVICE interrupt,
* let esp_phasemanage() find out if the
* chip disconnected.
*/
if (esp->e_imsglen != 0) {
esplog (esp, LOG_WARNING,
"Premature end of extended message");
}
}
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
static int
esp_handle_msg_in_done(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register struct espreg *ep = esp->e_reg;
register sndmsg = 0;
register u_char msgin;
/*
* We can be called here for both the case where
* we had requested the ESP chip to fetch a message
* byte from the target (at the target's request).
* We can also be called in the case where we had
* been using the CMD_COMP_SEQ command to pick up
* both a status byte and a completion message from
* a target, but where the message wasn't one of
* COMMAND COMPLETE, LINKED COMMAND COMPLETE, or
* LINKED COMMAND COMPLETE (with flag). This is a
* legal (albeit extremely unusual) SCSI bus trans-
* -ition, so we have to handle it.
*/
if (esp->e_laststate != ACTS_C_CMPLT) {
#ifdef ESP_TEST_PARITY
reloop:
#endif /* ESP_TEST_PARITY */
if (esp->e_intr & ESP_INT_DISCON) {
esplog (esp, LOG_ERR,
"premature end of input message");
New_state (esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
/*
* Note that if e_imsglen is zero, then we are skipping
* input message bytes, so there is no reason to look for
* parity errors.
*/
if (esp->e_imsglen != 0 && (esp->e_stat & ESP_STAT_PERR)) {
esplog (esp, LOG_ERR, msginperr);
sndmsg = MSG_MSG_PARITY;
sp->cmd_pkt.pkt_statistics |= STAT_PERR;
ep->esp_cmd = CMD_FLUSH;
} else if ((msgin = (FIFO_CNT(ep))) != 1) {
/*
* If we have got more than one byte in the fifo,
* that is a gross screwup, and we should let the
* target know that we have completely fouled up.
*/
sndmsg = MSG_INITIATOR_ERROR;
ep->esp_cmd = CMD_FLUSH;
esplog (esp, LOG_ERR, "input message botch");
} else if (esp->e_imsglen == 0) {
/*
* If we are in the middle of gobbling up and throwing
* away a message (due to a previous message input
* error), drive on.
*/
msgin = ep->esp_fifo_data;
New_state(esp, ACTS_MSG_IN_MORE);
} else {
esp->e_imsgarea[esp->e_imsgindex++] =
msgin = ep->esp_fifo_data;
}
} else {
/*
* In this case, we have been called (from
* esp_handle_c_cmplt()) with the message
* already stored in the message array.
*/
msgin = esp->e_imsgarea[0];
}
/*
* Process this message byte (but not if we are
* going to be trying to send back some error
* anyway)
*/
if (sndmsg == 0 && esp->e_imsglen != 0) {
if (esp->e_imsgindex < esp->e_imsglen) {
EPRINTF2 ("message byte %d: 0x%x\n",
esp->e_imsgindex-1,
esp->e_imsgarea[esp->e_imsgindex-1]);
New_state(esp, ACTS_MSG_IN_MORE);
} else if (esp->e_imsglen == 1) {
#ifdef ESP_TEST_PARITY
if ((esp_ptest_msgin & (1<<Tgt(sp))) &&
esp_ptest_msg == msgin) {
esp_ptest_msgin = 0;
esp_ptest_msg = -1;
ep->esp_cmd = CMD_SET_ATN;
esp->e_stat |= ESP_STAT_PERR;
esp->e_imsgindex -= 1;
goto reloop;
}
#endif /* ESP_TEST_PARITY */
sndmsg = esp_onebyte_msg(esp);
} else if (esp->e_imsglen == 2) {
if (esp->e_imsgarea[0] == MSG_EXTENDED) {
static char *tool =
"Extended message 0x%x is too long";
/*
* Is the incoming message too long
* to be stored in our local array?
*/
if ((msgin+2) > IMSGSIZE) {
esplog(esp, LOG_ERR,
tool, esp->e_imsgarea[0]);
sndmsg = MSG_REJECT;
} else {
esp->e_imsglen = msgin + 2;
New_state(esp, ACTS_MSG_IN_MORE);
}
} else {
sndmsg = esp_twobyte_msg(esp);
}
} else {
sndmsg = esp_multibyte_msg(esp);
}
}
if (sndmsg < 0) {
/*
* If sndmsg is less than zero, one of the subsidiary
* routines needs to return some other state than
* ACTION_RETURN.
*/
return (-sndmsg);
} else if (sndmsg > 0) {
if (IS_1BYTE_MSG(sndmsg)) {
esp->e_omsglen = 1;
}
esp->e_cur_msgout[0] = (u_char) sndmsg;
/*
* The target is not guaranteed to go to message out
* phase, period. Moreover, until the entire incoming
* message is transferred, the target may (and likely
* will) continue to transfer message bytes (which
* we will have to ignore).
*
* In order to do this, we'll go to 'infinite'
* message in handling by setting the current input
* message length to a sentinel of zero.
*
* This works regardless of the message we are trying
* to send out. At the point in time which we want
* to send a message in response to an incoming message
* we do not care any more about the incoming message.
*
* If we are sending a message in response to detecting
* a parity error on input, the ESP chip has already
* set ATN* for us, but it doesn't hurt to set it here
* again anyhow.
*/
ep->esp_cmd = CMD_SET_ATN;
New_state(esp, ACTS_MSG_IN_MORE);
esp->e_imsglen = 0;
}
ep->esp_cmd = CMD_MSG_ACPT;
return (ACTION_RETURN);
}
static int
esp_onebyte_msg(esp)
register struct esp *esp;
{
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register int msgout = 0;
register u_char msgin = esp->e_imsgarea[0];
register tgt = Tgt(sp);
if (msgin & MSG_IDENTIFY) {
/*
* How did we get here? We should only see identify
* messages on a reconnection, but we'll handle this
* fine here (just in case we get this) as long as
* we believe that this is a valid identify message.
*
* For this to be a valid incoming message,
* bits 6-4 must must be zero. Also, the
* bit that says that I'm an initiator and
* can support disconnection cannot possibly
* be set here.
*/
char garbled = ((msgin & (BAD_IDENTIFY|INI_CAN_DISCON)) != 0);
esplog (esp, LOG_ERR, "%s message 0x%x from Target %d",
garbled ? "Garbled" : "Identify", msgin, tgt);
if (garbled) {
/*
* If it's a garbled message,
* try and tell the target...
*/
msgout = MSG_INITIATOR_ERROR;
} else {
New_state(esp, ACTS_UNKNOWN);
}
LOG_STATE(ACTS_MSG_IN, esp->e_stat, msgin, -1);
return (msgout);
} else if (IS_2BYTE_MSG(msgin) || IS_EXTENDED_MSG(msgin)) {
esp->e_imsglen = 2;
New_state(esp, ACTS_MSG_IN_MORE);
return (0);
}
New_state(esp, ACTS_UNKNOWN);
switch (msgin) {
case MSG_DISCONNECT:
/*
* If we 'cannot' disconnect- reject this message.
* Note that we only key off of the pkt_flags here-
* it would be inappropriate to test against scsi_options
* or esp->e_nodisc here (they might have been changed
* after this command started). I realize that this
* isn't complete coverage against this error, but it
* is the best we can do. I thought briefly about setting
* (in esp_init_cmd) the FLAG_NODISCON bit in a packet
* if either of scsi_options or esp->e_nodisc indicated
* that disconnect/reconnect has been turned off, but
* that might really bolix up the true owner of the
* packet (the target driver) who has really only
* *loaned* us this packet during transport.
*/
if (sp->cmd_pkt.pkt_flags & FLAG_NODISCON) {
msgout = MSG_REJECT;
break;
}
LOG_STATE(ACTS_DISCONNECT, esp->e_stat, esp->e_xfer, -1);
/* FALL THROUGH */
case MSG_COMMAND_COMPLETE:
case MSG_LINK_CMPLT:
case MSG_LINK_CMPLT_FLAG:
esp->e_state = ACTS_CLEARING;
LOG_STATE(ACTS_MSG_IN, esp->e_stat, msgin, -1);
break;
/* This has been taken care of above */
/* case MSG_EXTENDED: */
case MSG_NOP:
LOG_STATE(ACTS_NOP, esp->e_stat, -1, -1);
break;
case MSG_REJECT:
{
u_char reason = 0;
u_char lastmsg = esp->e_last_msgout;
/*
* The target is rejecting the last message we sent.
*
* If the last message we attempted to send out was an
* extended message, we were trying to negotiate sync
* xfers- and we're okay.
*
* Otherwise, a target has rejected a message that
* it should have handled. We will abort the operation
* in progress and set the pkt_reason value here to
* show why we have completed. The process of aborting
* may be via a message or may be via a bus reset (as
* a last resort).
*/
msgout = MSG_ABORT;
LOG_STATE(ACTS_REJECT, esp->e_stat, -1, -1);
switch (lastmsg) {
case MSG_EXTENDED:
esp->e_sdtr = 0;
esp->e_offset[tgt] = 0;
esp->e_sync_known |= (1<<tgt);
msgout = 0;
break;
case MSG_NOP:
reason = CMD_NOP_FAIL;
break;
case MSG_INITIATOR_ERROR:
reason = CMD_IDE_FAIL;
break;
case MSG_MSG_PARITY:
reason = CMD_PER_FAIL;
break;
case MSG_REJECT:
reason = CMD_REJECT_FAIL;
break;
case MSG_ABORT:
/*
* If an ABORT OPERATION message is rejected
* it is time to yank the chain on the bus...
*/
reason = CMD_ABORT_FAIL;
msgout = -ACTION_ABORT_CURCMD;
break;
default:
if (IS_IDENTIFY_MSG(lastmsg)) {
reason = CMD_ID_FAIL;
} else {
reason = CMD_TRAN_ERR;
msgout = -ACTION_ABORT_CURCMD;
}
break;
}
if (msgout) {
esplog (esp, LOG_WARNING,
"Target %d rejects our message '%s'",
tgt, scsi_mname(lastmsg));
if (sp->cmd_pkt.pkt_reason == 0) {
sp->cmd_pkt.pkt_reason = reason;
}
}
break;
}
case MSG_RESTORE_PTRS:
sp->cmd_cdbp = sp->cmd_pkt.pkt_cdbp;
sp->cmd_scbp = sp->cmd_pkt.pkt_scbp;
if (sp->cmd_data != sp->cmd_saved_data) {
sp->cmd_data = sp->cmd_saved_data;
sp->cmd_flags |= CFLAG_NEEDSEG;
}
LOG_STATE(ACTS_RESTOREDP, esp->e_stat, esp->e_xfer, -1);
break;
case MSG_SAVE_DATA_PTR:
sp->cmd_saved_data = sp->cmd_data;
LOG_STATE(ACTS_SAVEDP, esp->e_stat, esp->e_xfer, -1);
break;
/* These don't make sense for us, and */
/* will be rejected */
/* case MSG_INITIATOR_ERROR */
/* case MSG_ABORT */
/* case MSG_MSG_PARITY */
/* case MSG_DEVICE_RESET */
default:
msgout = MSG_REJECT;
esplog (esp, LOG_NOTICE,
"Rejecting message '%s' from Target %d",
scsi_mname(msgin), tgt);
LOG_STATE(ACTS_MSG_IN, esp->e_stat, msgin, -1);
break;
}
EPRINTF1 ("Message in: %s\n", scsi_mname(msgin));
return (msgout);
}
static int
esp_twobyte_msg(esp)
struct esp *esp;
{
esplog (esp, LOG_WARNING,
"Two byte message '%s' 0x%x rejected",
scsi_mname(esp->e_imsgarea[0]), esp->e_imsgarea[1]);
return (MSG_REJECT);
}
/*
* XXX: Should be able to use %d.03%d instead of three different messages.
*/
static int
esp_multibyte_msg(esp)
register struct esp *esp;
{
static char *mbs =
"Target %d now Synchronous at %d.%d MB/s max transmit rate\n";
static char *mbs1 =
"Target %d now Synchronous at %d.0%d MB/s max transmit rate\n";
static char *mbs2 =
"Target %d now Synchronous at %d.00%d MB/s max transmit rate\n";
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register struct espreg *ep = esp->e_reg;
u_char emsg = esp->e_imsgarea[2];
int tgt = Tgt(sp);
register msgout = 0;
if (emsg == MSG_SYNCHRONOUS) {
u_int period, offset, regval;
u_int minsync, maxsync, clockval;
u_int xfer_freq, xfer_div, xfer_mod;
period = esp->e_imsgarea[3]&0xff;
offset = esp->e_imsgarea[4]&0xff;
minsync = MIN_SYNC_PERIOD(esp);
maxsync = MAX_SYNC_PERIOD(esp);
IPRINTF3 ("received period %d offset %d from tgt %d\n",
period, offset, tgt);
#ifdef ESP_TEST_FAST_SYNC
IPRINTF3("calculated minsync %d, maxsync %d for tgt %d\n",
minsync, maxsync, tgt);
#endif ESP_TEST_FAST_SYNC
if ((++(esp->e_sdtr)) & 1) {
/*
* In cases where the target negotiates synchronousn
* mode before we do, and we either have sync mode
* disbled, or this target is known to be a weak
* signal target, we send back a message indicating
* a desire to stay in asynchronous mode (the SCSI-2
* spec states that if we have synchronous capability
* that we cannot reject a SYNCHRONOUS DATA TRANSFER
* REQUEST message).
*/
IPRINTF ("SYNC negotiation initiated by target\n");
msgout = MSG_EXTENDED;
if ((esp->e_weak & (1<<tgt)) ||
(scsi_options & SCSI_OPTIONS_SYNC) == 0) {
/*
* Only zero out the offset. Don't change
* the period.
*/
if ((esp->e_type == FAS236) ||
(esp->e_type == FAS100A)) {
if (period < DEFAULT_FASTSYNC_PERIOD)
period = DEFAULT_FASTSYNC_PERIOD;
} else {
if (period < DEFAULT_SYNC_PERIOD)
period = DEFAULT_SYNC_PERIOD;
}
esp_make_sdtr(esp, (int) period, 0);
goto out;
}
}
xfer_freq = regval = 0;
/*
* If the target's offset is bigger than ours,
* that's okay. We just set to our maximum.
*/
if (offset > DEFAULT_OFFSET) {
offset = DEFAULT_OFFSET;
}
if (offset && period > maxsync) {
/*
* We cannot transmit data in synchronous
* mode this slow, so convert to asynchronous
* mode.
*/
msgout = MSG_EXTENDED;
esp_make_sdtr(esp, (int) period, 0);
goto out;
} else if (offset && period < minsync) {
/*
* If the target's period is less than ours,
* that's okay. We can only transmit so fast.
*/
period = minsync;
regval = MIN_SYNC(esp);
} else if (offset) {
/*
* 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.
*/
clockval = esp->e_clock_cycle / 1000;
if (esp->e_backoff[tgt]) {
/*
* increase period by 20%
*/
period = (period * 120)/100;
}
regval = (((period << 2) + clockval - 1) / clockval);
/*
* Strictly paranoia!
*/
if (regval > MAX_SYNC(esp)) {
msgout = MSG_EXTENDED;
esp_make_sdtr(esp, (int) period, 0);
goto out;
}
}
if (offset) {
esp->e_period[tgt] = regval;
ep->esp_sync_period = esp->e_period[tgt] &
SYNC_PERIOD_MASK;
ep->esp_sync_offset = esp->e_offset[tgt] = offset |
esp->e_req_ack_delay;
if ((esp->e_type == FAS236) ||
(esp->e_type == FAS100A)) {
/*
* if transferring > 5 MB/sec then enable
* fastscsi in conf3
*/
if (period < FASTSCSI_THRESHOLD) {
if (esp->e_type == FAS236) {
esp->e_espconf3[tgt] |=
ESP_CONF3_236_FASTSCSI;
} else {
esp->e_espconf3[tgt] |=
ESP_CONF3_100A_FASTSCSI;
}
}
ep->esp_conf3 = esp->e_espconf3[tgt];
}
#ifdef ESP_TEST_FAST_SYNC
IPRINTF3("sending period %d, offset %d to tgt %d\n",
esp->e_period[tgt] & SYNC_PERIOD_MASK,
esp->e_offset[tgt] & 0xf, tgt);
IPRINTF1 ("req/ack delay = %x\n", esp->e_req_ack_delay);
IPRINTF1 ("conf3 = %x\n", esp->e_espconf3[tgt]);
#endif ESP_TEST_FAST_SYNC
/*
* Convert input clock cycle per
* byte to nanoseconds per byte.
* (ns/b), and convert that to
* k-bytes/second.
*/
xfer_freq = ESP_SYNC_KBPS((regval *
esp->e_clock_cycle) / 1000);
xfer_div = xfer_freq / 1000;
xfer_mod = xfer_freq % 1000;
/*
* This was an esplog message, but it kept on
* screwing up console messages at boot, so
* its out for the moment.
* They are now IPRINTF's because we do not want to see
* them on each renegotiation after a check condition
*/
if (xfer_mod > 99) {
IPRINTF3(mbs, tgt, xfer_div, xfer_mod);
} else if (xfer_mod > 9) {
IPRINTF3(mbs1, tgt, xfer_div, xfer_mod);
} else {
IPRINTF3(mbs2, tgt, xfer_div, xfer_mod);
}
} else if (esp->e_offset[tgt]) {
/*
* We are converting back to async mode.
*/
ep->esp_sync_period = esp->e_period[tgt] = 0;
ep->esp_sync_offset = esp->e_offset[tgt] = 0;
}
if (msgout) {
esp_make_sdtr(esp, (int) period, (int) offset);
}
esp->e_sync_known |= (1<<tgt);
} else if (emsg == MSG_MODIFY_DATA_PTR) {
register struct dataseg *segp;
long mod;
if ((sp->cmd_flags & CFLAG_DMAVALID) == 0) {
msgout = MSG_REJECT;
goto out;
}
mod = (esp->e_imsgarea[3]<<24) |
(esp->e_imsgarea[4]<<16) |
(esp->e_imsgarea[5]<<8) |
(esp->e_imsgarea[6]);
IPRINTF2 ("mod data ptr tgt %d by %d\n", tgt, mod);
sp->cmd_data += mod;
if (sp->cmd_data < sp->cmd_mapping ||
sp->cmd_data >= sp->cmd_mapping + sp->cmd_dmacount) {
sp->cmd_data -= mod;
msgout = MSG_REJECT;
goto out;
}
segp = sp->cmd_cursubseg;
if (segp->d_count) {
if (sp->cmd_data < segp->d_base ||
sp->cmd_data >= segp->d_base + segp->d_count) {
sp->cmd_flags |= CFLAG_NEEDSEG;
}
}
} else {
esplog (esp, LOG_NOTICE,
"Rejecting message %s 0x%x from Target %d",
scsi_mname(MSG_EXTENDED), emsg, tgt);
msgout = MSG_REJECT;
}
out:
New_state(esp, ACTS_UNKNOWN);
return (msgout);
}
/*
* Complete the process of selecting a target
*/
static int
esp_finish_select(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
register struct dmaga *dmar = esp->e_dma;
register struct scsi_cmd *sp = CURRENT_CMD(esp);
register int cmdamt, fifoamt;
register u_char intr = esp->e_intr;
u_char step = esp->e_step;
u_char state = esp->e_state;
int target = Tgt(sp);
/*
* Check for DMA gate array errors
*/
if (dmar->dmaga_csr & DMAGA_ERRPEND) {
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
esplog (esp, LOG_ERR,
"Unrecoverable DMA error during selection");
sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR;
return (ACTION_RESET);
}
/*
* Latch up fifo count
*/
fifoamt = FIFO_CNT(ep);
/*
* How far did we go (by the DMA gate array's reckoning)?
*/
cmdamt = dmar->dmaga_addr - esp->e_lastdma;
/*
* If the NEXTBYTE value is non-zero (and we have the
* rev 1 DMA gate array), we went one longword further
* less 4 minus the NEXTBYTE value....
*/
if (DMAGA_REV(dmar) == DMA_REV1) {
int i;
if (i = DMAGA_NEXTBYTE(dmar)) {
cmdamt -= (4-i);
}
}
/*
* Shut off DMA gate array
*/
while (dmar->dmaga_csr & DMAGA_REQPEND);
dmar->dmaga_csr |= DMAGA_FLUSH;
dmar->dmaga_csr &= ~(DMAGA_ENDVMA|DMAGA_WRITE|DMAGA_ENATC);
/*
* Now adjust cmdamt by the amount of data left in the fifo
*/
cmdamt -= fifoamt;
/*
* Be a bit defensive...
*/
if (cmdamt < 0 || cmdamt > FIFOSIZE)
cmdamt = 0;
#ifdef ESPDEBUG
if (DEBUGGING) {
eprintf (esp,
"finsel: state %s, step %d; did %d of %d; fifo %d\n",
esp_state_name(state), step, cmdamt,
esp->e_lastcount, fifoamt);
esp_stat_int_print(esp);
}
#endif /* ESPDEBUG */
/*
* Did something respond to selection?
*/
if (intr == ESP_INT_DISCON) {
/*
* This takes care of getting the bus, but no
* target responding to selection. Clean up the
* chip state.
*/
esp_chip_disconnect(esp);
/*
* There is a definite problem where the MT02
* drops BSY if you use the SELECT && STOP command,
* which leaves ATN asserted after sending an identify
* message.
*/
if (step != 0 && (esp->e_targets & (1<<target)) &&
(state == STATE_SELECT_N_SENDMSG ||
state == STATE_SELECT_N_STOP)) {
if (state == STATE_SELECT_N_SENDMSG &&
esp->e_cur_msgout[0] == MSG_EXTENDED) {
esp->e_sync_known |= (1<<target);
}
if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
/*
* Gross...
*/
New_state(esp, STATE_FREE);
return (ACTION_SEARCH);
}
}
sp->cmd_pkt.pkt_state |= STATE_GOT_BUS;
sp->cmd_pkt.pkt_reason = CMD_INCOMPLETE;
return (ACTION_FINISH);
} else if (intr == (ESP_INT_FCMP|ESP_INT_RESEL)) {
/*
* A reselection attempt glotzed our selection attempt.
* If we were running w/o checking parity on this
* command, restore parity checking.
*/
if ((scsi_options & SCSI_OPTIONS_PARITY) &&
(sp->cmd_pkt.pkt_flags & FLAG_NOPARITY)) {
ep->esp_conf = esp->e_espconf;
}
ESP_PREEMPT(esp);
LOG_STATE(ACTS_PREEMPTED, esp->e_stat, 0, -1);
return (ACTION_RESEL);
} else if (intr != (ESP_INT_BUS|ESP_INT_FCMP)) {
esplog (esp, LOG_ERR, "undetermined selection failure");
esp_stat_int_print(esp);
return (ACTION_RESET);
}
/*
* We succesfully selected a target (we think).
* Now we figure out how botched things are
* based upon the kind of selection we were
* doing and the state of the step register.
*/
esp->e_targets |= (1<<target);
switch (step) {
case ESP_STEP_ARBSEL:
/*
* In this case, we selected the target, but went
* neither into MESSAGE OUT nor COMMAND phase.
* However, this isn't a fatal error, so we just
* drive on.
*
* This might be a good point to note that we have
* a target that appears to not accomodate disconnecting,
* but it really isn't worth the effort to distinguish
* such targets especially from others.
*/
/* FALLTHROUGH */
case ESP_STEP_SENTID:
/*
* In this case, we selected the target and sent
* message byte and have stopped with ATN* still on.
* This case should only occur if we use the SELECT
* AND STOP command.
*/
/* FALLTHROUGH */
case ESP_STEP_NOTCMD:
/*
* In this case, we either didn't transition to command
* phase, or, if we were using the SELECT WITH ATN3 command,
* we possibly didn't send all message bytes.
*/
cmdamt = 0;
break;
case ESP_STEP_PCMD:
/*
* In this case, not all command bytes transferred.
*/
/* FALLTHROUGH */
case ESP_STEP_DONE:
#ifdef FAS100A_BUG
step_done:
#endif
/*
* This is the usual 'good' completion point.
* If we we sent message byte(s), we subtract
* off the number of message bytes that were
* ahead of the command.
*/
if (state == STATE_SELECT_NORMAL)
cmdamt -= 1;
break;
#ifdef FAS100A_BUG
/*
* There seems to be a bug in the FAS101 chip where
* it reports a sequence step value greater than 4.
* The workaround treats such a value as 4, thus
* avoiding an error message and reset.
*
* See bug id's 1128862 and 1140764 for more information
* about this problem.
*/
case 0x5:
case 0x6:
case 0x7:
if (esp->e_type == FAS100A || esp->e_type == ESP100A) {
esp_step567++;
goto step_done;
}
/* FALLTHROUGH */
#endif
default:
esplog (esp, LOG_ERR,
"bad sequence step (0x%x) in selection", step);
return (ACTION_RESET);
}
/*
* If we sent any messages or sent a command, as
* per ESP errata sheets, we have to hit the
* chip with a CMD_NOP in order to unlatch the
* fifo counter.
*/
ep->esp_cmd = CMD_NOP;
/*
* *Carefully* dump out any cruft left in the fifo.
* If this target has shifted to synchronous DATA IN
* phase, then the ESP has already flushed the fifo
* for us.
*/
if (fifoamt != 0 &&
((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_DATA_IN ||
esp->e_offset[target] == 0)) {
ep->esp_cmd = CMD_FLUSH;
}
/*
* OR in common state...
*/
sp->cmd_pkt.pkt_state |= (STATE_GOT_BUS|STATE_GOT_TARGET);
/*
* advance command pointer
*/
if (cmdamt > 0) {
sp->cmd_pkt.pkt_state |= STATE_SENT_CMD;
sp->cmd_cdbp = (caddr_t) (((u_long)sp->cmd_cdbp) + cmdamt);
}
/*
* initialize data information (if any)
*/
if (sp->cmd_data != sp->cmd_saved_data) {
sp->cmd_data = sp->cmd_saved_data;
sp->cmd_flags |= CFLAG_NEEDSEG;
}
New_state(esp, ACTS_UNKNOWN);
return (ACTION_PHASEMANAGE);
}
/*
* Handle the reconnection of a target
*/
static int
esp_reconnect(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
struct scsi_cmd *sp;
register target, lun;
register u_char tmp, myid = (1<<MY_ID(esp));
short slot;
static char *urecpid =
"unrecoverable SCSI bus parity error (IDENTIFY msg)";
New_state(esp, ACTS_RESEL);
/*
* Pick up target id from fifo
*/
if (FIFO_CNT(ep) != 2) {
/*
* There should only be the reselecting target's id
* and an identify message in the fifo.
*/
goto bad;
}
tmp = ep->esp_fifo_data;
if ((tmp & myid) == 0) {
/*
* Our SCSI id is missing. This 'cannot happen'.
*/
goto bad;
}
/*
* Turn off our id
*/
tmp ^= myid;
if (tmp == 0) {
/*
* There is no other SCSI id, therefore we cannot
* tell who is reselecting us. This 'cannot happen'.
*/
goto bad;
}
for (target = 0; target < NTARGETS; target++) {
if (tmp & (1<<target)) {
tmp ^= (1<<target);
break;
}
}
if (tmp) {
/*
* There is more than one reselection id on the bus.
* This 'cannot happen'.
*/
goto bad;
}
/*
* Now pick up identify message byte, and acknowledge it.
*/
if ((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_MSG_IN) {
/*
* If we aren't in MESSAGE IN phase,
* things are really screwed up.
*/
goto bad;
}
tmp = esp->e_last_msgin = ep->esp_fifo_data;
/*
* XXX: Oh boy. We have problems. What happens
* XXX: if we have a parity error on the IDENTIFY
* XXX: message? We cannot know which lun is
* XXX: reconnecting, but we really need to know
* XXX: that in order to go through all the
* XXX: rigamarole of sending a MSG_PARITY_ERR
* XXX: message back to the target.
* XXX:
* XXX: In order to minimize a panic situation,
* XXX: we'll assume a lun of zero (i.e., synthesize
* XXX: the IDENTIFY message), and only panic
* XXX: if there is more than one active lun on
* XXX: this target.
*/
if (esp->e_stat & ESP_STAT_PERR) {
tmp = MSG_IDENTIFY;
}
/*
* Check sanity of message.
*/
if (!(IS_IDENTIFY_MSG(tmp)) || (tmp & INI_CAN_DISCON)) {
goto bad;
}
lun = tmp & (NLUNS_PER_TARGET-1);
while (FIFO_CNT(ep)) {
tmp = ep->esp_fifo_data;
}
/*
* As per the ESP100 errata sheets, if a selection attempt
* is preempted by a reselection coming in, we'll get a
* spurious ILLEGAL COMMAND error interrupt from the ESP100.
* Instead of trying to figure out whether we were preempted
* or not, just gate off of whether ot not we are a ESP100
* or not.
*/
if (IS_53C90(esp)) {
tmp = ep->esp_intr;
}
/*
* I believe that this needs to be done to unlatch the ESP.
*/
ep->esp_cmd = CMD_NOP;
/*
* If this target is synchronous, here is the
* place to set it up during a reconnect.
*/
ep->esp_sync_period = esp->e_period[target] & SYNC_PERIOD_MASK;
ep->esp_sync_offset = esp->e_offset[target] | esp->e_req_ack_delay;
if ((esp->e_type == FAS236) || (esp->e_type == FAS100A)) {
ep->esp_conf3 = esp->e_espconf3[target];
}
LOG_STATE(ACTS_RESEL, esp->e_stat, target, lun);
/*
* Now search for command to reconnect to.
*/
slot = (target * NLUNS_PER_TARGET) | lun;
sp = esp->e_slots[slot];
/*
* Handle the case of a parity error on the IDENTIFY message.
* Above we set things up to assume a lun of zero. If there
* is more than one lun active, we die. In either case, if
* there is a lun active, we find it and assume that it is
* the device we are reconnecting to.
*/
if (esp->e_stat & ESP_STAT_PERR) {
tmp = 0;
for (lun = 0; lun < NLUNS_PER_TARGET; lun++) {
if (esp->e_slots[slot+lun]) {
tmp++;
if (sp == (struct scsi_cmd *) 0) {
sp = esp->e_slots[slot+lun];
}
}
}
if (tmp == 1) {
lun = Lun(sp);
slot = slot + lun;
} else if (tmp > 1) {
esplog (esp, LOG_ERR, urecpid);
goto bad;
}
}
if ((sp == (struct scsi_cmd *) 0) ||
(sp->cmd_flags & (CFLAG_CMDDISC|CFLAG_CMDPROXY)) == 0) {
/*
* There is either no command to reconnect to, or the command
* that is recorded here is not the command that the target
* is attempting to reconnect to. The latter case is a hard
* one to figure, and we only do a half-hearted job of doing
* so. Later, with SCSI-2 q'd commands, we will more likely
* depend upon queue tag messages to try and find commands
* to reconnect to.
*
* A reason for why this situation might occur is that a
* command may time out while disconnected. We knock down
* our record of the command and notify the target driver
* that the command timed out (see esp_watch()). If the
* target driver had been mistaken about how long a command
* may take to do, and the target reconnects later for
* the now timed-out command, we have to abort the operation.
*
* In the case that there is a command in the slot, and the
* command isn't marked as disconnected or proxy, then
* the target driver is attempting to talk to the target
* again. This is a very shaky edifice of race conditions
* here, as the slot may be taken up by yet another proxy
* command (generated by the target attempting to reset
* this target via the scsi_reset() function), in which
* case we'll probably die very horribly soon.
*
* Odd as it may seem, the below code seems to work pretty
* well. I've managed to test it on several targets, and
* the targets seem pretty amiable about accepting an ABORT
* OPERATION message during reconnection- even targets that
* get very bothered about ABORT OPERATION messages at other
* times.
*/
auto struct scsi_address addr;
auto struct scsi_cmd local;
register struct scsi_cmd *sp1;
if (sp) {
sp1 = sp;
} else {
sp1 = (struct scsi_cmd *) 0;
}
sp = &local;
addr.a_cookie = scsi_cookie(&esp->e_tran);
addr.a_target = target;
addr.a_lun = lun;
addr.a_sublun = 0;
esp_makeproxy_cmd(sp, &addr, MSG_ABORT);
esp_init_cmd(sp);
esp->e_npolling++;
esp->e_ncmds++;
esplog (esp, LOG_WARNING,
"No command for reconnect of Target %d Lun %d",
target, lun);
/*
* Set ATN* in order to try and send an ABORT OPERATION msg.
*/
esp->e_cur_msgout[0] = MSG_ABORT;
esp->e_omsglen = 1;
ep->esp_cmd = CMD_SET_ATN;
ep->esp_cmd = CMD_MSG_ACPT;
esp->e_cur_slot = slot;
CURRENT_CMD(esp) = sp;
New_state(esp, ACTS_UNKNOWN);
while (esp->e_npolling) {
if (esp_dopoll(esp, POLL_TIMEOUT)) {
if (esp->e_slots[slot] == &local) {
esp->e_slots[slot] =
(struct scsi_cmd *) NULL;
}
return (ACTION_RESET);
}
}
esplog (esp, LOG_INFO, "Proxy abort %s for Target %d Lun %d",
(sp->cmd_pkt.pkt_reason == CMD_CMPLT) ? "succeeded" :
"failed", target, lun);
if (sp1) {
esp->e_slots[slot] = sp1;
} else if (esp->e_slots[slot] == &local) {
esp->e_slots[slot] = (struct scsi_cmd *) NULL;
}
if (sp->cmd_pkt.pkt_reason == CMD_CMPLT &&
sp->cmd_cdb[ESP_PROXY_RESULT] == TRUE) {
return (ACTION_SEARCH);
} else
return (ACTION_RESET);
} else if (sp->cmd_flags & CFLAG_CMDPROXY) {
/*
* If we got here, we were already attempting to
* run a polled proxy command for this target.
* Set ATN and, copy in the message, and drive
* on (ignoring any parity error on the identify).
*/
esp->e_ndisc++; /* readjust this up */
ep->esp_cmd = CMD_SET_ATN;
esp->e_omsglen = sp->cmd_cdb[ESP_PROXY_DATA];
tmp = 0;
while (tmp < esp->e_omsglen) {
esp->e_cur_msgout[tmp] =
sp->cmd_cdb[ESP_PROXY_DATA+1+tmp];
tmp++;
}
sp->cmd_cdb[ESP_PROXY_RESULT] = FALSE;
IPRINTF2 ("esp_reconnect: fielding proxy cmd for %d.%d\n",
target, lun);
} else if (scsi_options & SCSI_OPTIONS_PARITY) {
/*
* If we are doing PARITY checking, check for a parity
* error on the IDENTIFY message.
*/
if (sp->cmd_pkt.pkt_flags & FLAG_NOPARITY) {
/*
* If we had detected a parity error
* on the IDENTIFY message, and this
* command is being run without checking,
* act as if we didn't get a parity
* error. The assumption here is that
* we only disable parity checking for
* targets that don't generate parity.
*/
ep->esp_conf = esp->e_espconf & ~ESP_CONF_PAREN;
} else if (esp->e_stat & ESP_STAT_PERR) {
esp->e_cur_msgout[0] = MSG_MSG_PARITY;
esp->e_omsglen = 1;
}
}
/*
* Accept the IDENTIFY message
*/
ep->esp_cmd = CMD_MSG_ACPT;
/*
* Now- if this is a SCSI-2 type target, we have to, probably,
* by hand here, pick up the QUEUE TAG message that must follow
* in order to fully qualify which nexus is being established.
*
* Good Luck, Jim....
*
*/
esp->e_cur_slot = slot;
if (esp->e_ndisc != 0)
esp->e_ndisc--;
New_state(esp, ACTS_UNKNOWN);
sp->cmd_flags &= ~CFLAG_CMDDISC;
/*
* A reconnect implies a restore pointers operation
*/
sp->cmd_cdbp = sp->cmd_pkt.pkt_cdbp;
sp->cmd_scbp = sp->cmd_pkt.pkt_scbp;
if (sp->cmd_data != sp->cmd_saved_data) {
sp->cmd_data = sp->cmd_saved_data;
sp->cmd_flags |= CFLAG_NEEDSEG;
}
/*
* And zero out the SYNC negotiation counter
*/
esp->e_sdtr = 0;
/*
* Return to await the FUNCTION COMPLETE interrupt we
* should get out of accepting the IDENTIFY message.
*/
EPRINTF2 ("Reconnecting %d.%d\n", target, lun);
return (ACTION_RETURN);
bad:
esp_printstate(esp, "failed reselection");
LOG_STATE(ACTS_BAD_RESEL, esp->e_stat, -1, -1);
return (ACTION_RESET);
}
/*
* Miscellaneous Service Subroutines
*/
static int
esp_istart(esp)
struct esp *esp;
{
if (esp->e_state == STATE_FREE && esp->e_npolling == 0 &&
esp->e_ncmds > esp->e_ndisc) {
(void) esp_ustart(esp, esp->e_last_slot);
}
return (ACTION_RETURN);
}
static void
esp_runpoll(esp, slot)
register struct esp *esp;
short slot;
{
register struct scsi_cmd *sp = esp->e_slots[slot];
int limit = sp->cmd_pkt.pkt_time * 1000000;
/*
* mark that we're going to do a polling command
*/
esp->e_npolling++;
while (esp->e_npolling) {
/*
* drain any current active command(s)
*/
if (esp->e_state != STATE_FREE) {
if (esp_dopoll(esp, POLL_TIMEOUT)) {
printf("runpoll: timeout on draining\n");
(void) esp_abort_curcmd(esp);
return;
}
}
/*
* If the draining of active commands killed the
* the current polled command, we're done..
*/
if (esp->e_npolling == 0) {
break;
}
if (esp_ustart(esp, slot) != TRUE) {
continue;
}
/*
* We're now 'running' this command.
*
* esp_dopoll will always return when
* esp->e_state is STATE_FREE, and
* we'll use the npolling token to
* decide whether that command got done.
*/
while (esp->e_npolling) {
if (esp_dopoll(esp, limit)) {
printf("runpoll: timeout on polling\n");
(void) esp_abort_curcmd(esp);
return;
}
/*
* If a preemption occurred that caused this
* command to actually not start, go around
* the loop again..
*/
if (sp->cmd_pkt.pkt_state == 0) {
break;
}
}
}
/*
* If we stored up commands to do, start them off now.
*/
if (esp->e_state == STATE_FREE)
(void) esp_ustart(esp, NEXTSLOT(slot));
}
static int
esp_reset_bus(esp)
struct esp *esp;
{
New_state(esp, ACTS_RESET);
esp_internal_reset(esp, ESP_RESET_HW & ~ESP_RESET_IGNORE_BRESET);
/*
* Now that we've reset the SCSI bus, we'll take a SCSI RESET
* interrupt and use that to clean up the state of things.
*/
return (ACTION_RETURN);
}
static int
esp_reset_recovery(esp)
struct esp *esp;
{
register struct scsi_cmd *sp;
register short slot, start_slot;
auto struct scsi_cmd *tslots[NTARGETS * NLUNS_PER_TARGET];
u_char reason, was_polling;
if ((start_slot = esp->e_cur_slot) == UNDEFINED)
start_slot = 0;
/*
* Actually- for right now just claim that all
* commands have been destroyed by a SCSI reset
* and let already set reason fields or callers
* decide otherwise for specific commands.
*/
reason = CMD_RESET;
/*
* Preserve the state of whether
* we are in polling mode or not.
*/
was_polling = esp->e_npolling;
/*
* We're blowing it all away. Remove any dead wood to the
* side so that completion routines don't get confused.
*/
bcopy((caddr_t) esp->e_slots, (caddr_t)tslots,
(sizeof (struct scsi_cmd *)) * NTARGETS * NLUNS_PER_TARGET);
if (esp->e_state != ACTS_RESET && esp->e_state != ACTS_ABORTING) {
esp_internal_reset(esp, ESP_RESET_ESP|ESP_RESET_DMA);
#ifdef ESPDEBUG
if (INFORMATIVE) {
register short z = esp->e_phase_index;
z = (z - 2) & (NPHASE - 1);
if (esp->e_phase[z].e_save_state != ACTS_RESET)
esp_printstate(esp, "unexpected SCSI bus reset");
}
#endif /* ESPDEBUG */
esplog (esp, LOG_NOTICE, "unexpected SCSI bus reset");
}
/*
* Call this routine to compltely reset the state of the softc data.
*/
esp_internal_reset(esp, ESP_RESET_SOFTC);
/*
* Hold the state of the host adapter open
*/
New_state(esp, ACTS_FROZEN);
slot = start_slot;
do {
if (sp = tslots[slot]) {
/*
* If there wasn't any other reason set
* for this completion, note it in the
* command reason state.
*/
if (sp->cmd_pkt.pkt_reason == 0) {
sp->cmd_pkt.pkt_reason = reason;
}
sp->cmd_pkt.pkt_resid = sp->cmd_dmacount;
if (sp->cmd_pkt.pkt_comp)
(*sp->cmd_pkt.pkt_comp)(sp);
}
slot = NEXTSLOT(slot);
} while (slot != start_slot);
/*
* Move the state back to free...
*/
New_state(esp, STATE_FREE);
/*
* and if anyone requeued, let our caller know to start 'em off,
* unless we were running in polled mode. It is the responsiblity
* of the top of the thread for polled mode to restart things.
*/
if (esp->e_ncmds && !was_polling) {
return (ACTION_SEARCH);
} else {
return (ACTION_RETURN);
}
}
static int
esp_handle_selection(esp)
struct esp *esp;
{
esplog (esp, LOG_NOTICE, "Unexpected Selection Attempt");
return (ACTION_RESET);
}
static void
esp_init_cmd(sp)
struct scsi_cmd *sp;
{
sp->cmd_pkt.pkt_reason = 0;
sp->cmd_pkt.pkt_resid = 0;
sp->cmd_pkt.pkt_state = sp->cmd_pkt.pkt_statistics = 0;
sp->cmd_cdbp = (caddr_t) sp->cmd_pkt.pkt_cdbp;
sp->cmd_scbp = sp->cmd_pkt.pkt_scbp;
*(sp->cmd_scbp) = 0;
sp->cmd_flags &= ~(CFLAG_NEEDSEG|CFLAG_CMDDISC|CFLAG_WATCH);
if ((sp->cmd_timeout = sp->cmd_pkt.pkt_time) != 0)
sp->cmd_flags |= CFLAG_WATCH;
if (sp->cmd_flags & CFLAG_DMAVALID) {
sp->cmd_data = sp->cmd_saved_data = sp->cmd_mapping;
sp->cmd_cursubseg = &sp->cmd_subseg;
sp->cmd_subseg.d_base = sp->cmd_mapping;
sp->cmd_subseg.d_count = 0;
sp->cmd_subseg.d_next = (struct dataseg *) 0;
}
}
static void
esp_makeproxy_cmd(sp, ap, msg)
struct scsi_cmd *sp;
struct scsi_address *ap;
int msg;
{
extern void scsi_pollintr();
bzero((caddr_t) sp, sizeof (*sp));
sp->cmd_pkt.pkt_address = *ap;
sp->cmd_pkt.pkt_comp = scsi_pollintr;
sp->cmd_pkt.pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
sp->cmd_pkt.pkt_scbp = (opaque_t) &sp->cmd_scb[0];
sp->cmd_pkt.pkt_cdbp = (opaque_t) &sp->cmd_cdb[0];
sp->cmd_pkt.pkt_pmon = (char) -1;
sp->cmd_flags = CFLAG_CMDPROXY;
sp->cmd_cdb[ESP_PROXY_TYPE] = ESP_PROXY_SNDMSG;
sp->cmd_cdb[ESP_PROXY_RESULT] = FALSE;
sp->cmd_cdb[ESP_PROXY_DATA] = 1;
sp->cmd_cdb[ESP_PROXY_DATA+1] = msg;
}
/*
* Handle resetting the chip in the case
* that a DISCONNECT interrupt is received.
*/
static void
esp_chip_disconnect(esp)
register struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
register struct scsi_cmd *sp;
/*
* We used to have some code in here that cleared
* the sync_period && sync_offset registers and
* then pushed in a CMD_FLUSH command. This seemed
* to cause some problems for reconnecting, so
* I nuked those commads. Things have been a bit
* happier since then.
*/
ep->esp_cmd = CMD_EN_RESEL;
if (esp->e_cur_slot != UNDEFINED && (sp = CURRENT_CMD(esp))) {
/*
* If we were running this command without
* parity checking, then reload the conf
* register (to re-enable parity checking).
*/
if ((scsi_options & SCSI_OPTIONS_PARITY) &&
(sp->cmd_pkt.pkt_flags & FLAG_NOPARITY)) {
ep->esp_conf = esp->e_espconf;
}
}
esp->e_sdtr = 0;
}
static void
esp_make_sdtr(esp, period, offset)
struct esp *esp;
int period, offset;
{
register u_char *p = esp->e_cur_msgout;
*p++ = (u_char) MSG_EXTENDED;
*p++ = (u_char) 3;
*p++ = (u_char) MSG_SYNCHRONOUS;
*p++ = (u_char) period;
*p++ = (u_char) offset;
esp->e_omsglen = 5;
}
/*
* Command watchdog routines
*/
static int
esp_watch()
{
struct esp *esp;
register s;
for (esp = esp_softc; esp != (struct esp *) 0; esp = esp->e_next) {
if (esp == (struct esp *) 0) {
break;
} else if (esp->e_tran.tran_start == (func_t) 0) {
continue;
}
s = splr(esp->e_tran.tran_spl);
if (esp->e_ncmds) {
esp_watchsubr(esp);
}
(void) splx(s);
}
timeout(esp_watch, (caddr_t) 0, hz);
}
static void
esp_watchsubr(esp)
register struct esp *esp;
{
register short slot;
register struct scsi_cmd *sp;
for (slot = 0; slot < (NTARGETS*NLUNS_PER_TARGET); slot++) {
/*
* No command- drive on
*/
if ((sp = esp->e_slots[slot]) == 0) {
continue;
}
#ifdef ESP_TEST_TIMEOUT
if (esp_ttest & (1<<Tgt(sp))) {
esp_ttest = 0;
esp_curcmd_timeout(esp, sp);
continue;
}
if ((esp_stest & (1<<Tgt(sp))) && (esp->e_cur_slot == slot) &&
((esp->e_state == ACTS_DATA_DONE) ||
(esp->e_state == ACTS_DATA)) &&
((sp->cmd_flags & CFLAG_CMDDISC) == 0)) {
esp_stest = 0;
esp_curcmd_timeout(esp, sp);
continue;
}
#endif
/*
* This command hasn't officially been started yet- drive on
*/
if (sp->cmd_pkt.pkt_state == 0 &&
esp->e_cur_slot != UNDEFINED && sp != CURRENT_CMD(esp)) {
continue;
}
/*
* This command not to be watched- drive on
*/
if ((sp->cmd_flags & CFLAG_WATCH) == 0) {
continue;
}
/*
* Else, knock a second off of the timer if any time left.
*/
if (sp->cmd_timeout > 0) {
sp->cmd_timeout -= 1;
continue;
}
/*
* No time left for this command. Last check
* before killing it.
*/
if (INTPENDING(esp)) {
/*
* A pending interrupt
* defers the sentence
* of death.
*/
sp->cmd_timeout++;
espsvc(esp);
break;
}
if (sp->cmd_flags & CFLAG_CMDDISC) {
esp_disccmd_timeout(esp, slot);
break;
} else {
esp_curcmd_timeout(esp, sp);
break;
}
}
}
static void
esp_curcmd_timeout(esp, sp)
register struct esp *esp;
struct scsi_cmd *sp;
{
int target = Tgt(sp);
#ifdef ESPDEBUG
register i;
register u_char *cp = (u_char *) sp->cmd_pkt.pkt_cdbp;
auto char buf[128];
#endif /* ESPDEBUG */
/*
* Various attempts have been made to recover
* from this situation, all to no avail. See
* SCCS history for their story.
*
* If we ever figure out how to recover from this,
* this routine will become a function that can
* actually return true or false depending on whether
* we could get the command going again.
*
* For now, blow it all away, and rely on the target
* driver to restart the command (if possible).
*/
esplog (esp, LOG_ERR, "Current command timeout for Target %d Lun %d",
target, Lun(sp));
#ifdef ESPDEBUG
esplog (esp, LOG_ERR, "\tState=%s (0x%x), Last State=%s (0x%x)",
esp_state_name(esp->e_state),
esp->e_state,
esp_state_name(esp->e_laststate),
esp->e_laststate);
esplog (esp, LOG_ERR, "\tCmd dump for Target %d Lun %d:",
Tgt(sp), Lun(sp));
buf[0] = '\0';
for (i = 0; i < sp->cmd_cdblen; i++) {
(void) sprintf(&buf[strlen(buf)], " 0x%x", *cp++);
if (strlen(buf) > 124)
break;
}
esplog (esp, LOG_ERR, "\tcdb=[%s ]", buf);
if (sp->cmd_pkt.pkt_state & STATE_GOT_STATUS)
esplog (esp, LOG_ERR,
"\tStatus=0x%x", sp->cmd_pkt.pkt_scbp[0]);
if (INFORMATIVE) {
esp_printstate(esp, "Current cmd timeout");
esp->e_stat = esp->e_reg->esp_stat & ~ESP_STAT_RES;
esp->e_intr = esp->e_reg->esp_intr;
esp_stat_int_print(esp);
}
#endif /* ESPDEBUG */
/*
* Current command timeout appears to relate often to noisy SCSI
* in synchronous mode. Therefore, we'll disable synchronous from
* now on for this target. Yes, I know this is kind of bogus....
*/
esp_sync_backoff(esp, sp);
#ifdef IS_IT_SOUP_YET
if (IS_53C90(esp) && esp->e_offset[target] &&
esp->e_state == ACTS_DATA_DONE) {
/*
* Can we do anything? We've tried
* 'by hand' forced initiator mode stuff..
*/
}
#endif /* IS_IT_SOUP_YET */
if (esp_abort_allcmds(esp) == ACTION_SEARCH) {
(void) esp_ustart(esp, 0);
}
}
static void
esp_sync_backoff(esp, sp)
register struct esp *esp;
register struct scsi_cmd *sp;
{
char phase = esp->e_stat & ESP_PHASE_MASK;
char state = esp->e_state;
u_char tgt = Tgt(sp);
IPRINTF3("esp_sync_backoff: target %d: state=%x, phase=%x\n",
tgt, state, phase);
#ifdef NOTNOW
/*
* Only process data phase hangs. Also, ignore any data phase
* hangs caused by request sense cmds as it's possible they could
* be caused by target reverting to asynch.
*/
if (state != ACTS_DATA && state != ACTS_DATA_DONE) {
IPRINTF2("Target %d.%d hang state not in data phase\n",
tgt, Lun(sp));
return;
} else if (phase != ESP_PHASE_DATA_IN && phase != ESP_PHASE_DATA_OUT) {
IPRINTF2("Target %d.%d hang bus not in data phase\n",
tgt, Lun(sp));
return;
} else if ((u_char) *(sp->cmd_pkt.pkt_cdbp) == SCMD_REQUEST_SENSE) {
IPRINTF2("Target %d.%d ignoring request sense hang\n",
tgt, Lun(sp));
return;
}
#endif
/*
* First we reduce xfer rate 20% and always enable slow cable mode
* and if that fails we revert to async with slow cable mode
*
* XXX: Should I try delaying REQ pulse too?
*/
if (esp->e_offset[tgt] != 0) {
#ifdef ESP_TEST_FAST_SYNC
register u_int regval, maxreg;
regval = esp->e_period[tgt];
maxreg = MAX_SYNC(esp);
IPRINTF4("regval %d maxreg %d backoff %d for tgt %d\n",
regval, maxreg, esp->e_backoff[tgt], tgt);
#endif ESP_TEST_FAST_SYNC
/*
* Compute sync transfer limits for later compensation.
*/
IPRINTF3("Target %d.%d back off using %s params\n", tgt,
Lun(sp), ((esp->e_type == FAS236) ||
(esp->e_type == FAS100A))? "FAS" : "ESP");
/*
* XXX: Should I try delaying REQ pulse too?
* XXX: Should I backoff e_default_period too?
*/
if (esp->e_backoff[tgt]) {
esp->e_period[tgt] = 0;
esp->e_offset[tgt] = 0;
esp->e_weak |= 1<<tgt;
esplog(esp, LOG_ERR,
"Target %d.%d reverting to async. mode",
tgt, Lun(sp));
} else {
(esp->e_backoff[tgt])++;
esplog(esp, LOG_ERR,
"Target %d.%d reducing sync. transfer rate",
tgt, Lun(sp));
}
/*
* Paranoia: Force sync. renegotiate
*/
esp->e_sync_known &= ~(1<<tgt);
}
if (((esp->e_type != FAS236) && (esp->e_type != FAS100A)) &&
((esp->e_espconf & ESP_CONF_SLOWMODE) == 0)) {
/*
* always enable slow cable mode
*/
esp->e_espconf |= ESP_CONF_SLOWMODE;
esp->e_reg->esp_conf |= ESP_CONF_SLOWMODE;
esplog(esp, LOG_ERR, "Reverting to slow SCSI cable mode");
}
}
static void
esp_disccmd_timeout(esp, slot)
struct esp *esp;
short slot;
{
struct scsi_cmd *sp = esp->e_slots[slot];
register int target = Tgt(sp);
esplog (esp, LOG_ERR,
"Disconnected command timeout for Target %d Lun %d",
target, Lun(sp));
#ifdef ESPDEBUG
if (INFORMATIVE) {
esp_dump_cmd(sp);
}
#endif /* ESPDEBUG */
sp->cmd_pkt.pkt_reason = CMD_TIMEOUT;
esp->e_slots[slot] = 0;
esp->e_ndisc -= 1;
esp->e_ncmds -= 1;
(*sp->cmd_pkt.pkt_comp) (sp);
}
/*
* Abort routines
*/
static int
esp_abort_curcmd(esp)
register struct esp *esp;
{
/*
* XXX We really need to do better than this! XXX
*/
if (esp->e_state != STATE_FREE) {
return (esp_abort_allcmds(esp));
} else {
return (ACTION_RETURN);
}
}
static int
esp_abort_allcmds(esp)
register struct esp *esp;
{
/*
* Reset everything. XXX DO BETTER THAN THIS! XXX
*/
New_state(esp, ACTS_ABORTING);
esp_internal_reset(esp, ESP_RESET_HW & ~ESP_RESET_IGNORE_BRESET);
/*
* Now that we've reset the SCSI bus, we'll take a SCSI RESET
* interrupt and use that to clean up the state of things.
*/
return (ACTION_RETURN);
}
/*
* Hardware and Software internal reset routines
*/
static void
esp_internal_reset(esp, reset_action)
register struct esp *esp;
int reset_action; /* how much to reset */
{
#ifdef ESPDEBUG
if (DEBUGGING) {
reset_action |= ESP_RESET_MSG;
}
#endif /* ESPDEBUG */
if (reset_action & ESP_RESET_MSG) {
esp_printstate(esp, "Reset");
}
if (reset_action & ESP_RESET_HW) {
esp_hw_reset(esp, reset_action);
}
if (reset_action & ESP_RESET_SOFTC) {
esp->e_last_slot = esp->e_cur_slot;
esp->e_cur_slot = UNDEFINED;
bzero((caddr_t) esp->e_slots,
(sizeof (struct scsi_cmd *))*NTARGETS*NLUNS_PER_TARGET);
bzero((caddr_t) esp->e_offset, NTARGETS * (sizeof (u_char)));
bzero((caddr_t) esp->e_period, NTARGETS * (sizeof (u_char)));
esp->e_sync_known = 0;
esp->e_ncmds = esp->e_npolling = esp->e_ndisc = 0;
esp->e_omsglen = 0;
esp->e_cur_msgout[0] = 0xff;
esp->e_last_msgout = 0xff;
esp->e_last_msgin = 0xff;
/*
* esp->e_weak && esp->e_nodisc are
* preserved across softc resets.
*/
New_state(esp, STATE_FREE);
}
LOG_STATE(ACTS_RESET, esp->e_stat, -1, reset_action);
}
static void
esp_hw_reset(esp, action)
struct esp *esp;
{
register struct espreg *ep = esp->e_reg;
register struct dmaga *dmar = esp->e_dma;
u_char junk, i;
if (action & ESP_RESET_DMA) {
if (action & ESP_RESET_MSG) {
eprintf (esp, "resetting DMA engine\n");
}
/*
* This approach should preserve any LANCE bits...
*/
dmar->dmaga_csr |= DMAGA_RESET;
/*
* *DON'T* use a straight assign to zero!
* (preserve bits)
*/
dmar->dmaga_csr &= ~DMAGA_RESET; /* clear it */
#ifdef ESP_NEW_HW_DEBUG
printf("esp_hw_reset: dma rev=%x type=%x\n", DMAGA_REV(dmar),
esp->e_type);
#endif
switch (DMAGA_REV(dmar)) {
case ESC1_REV1:
if (!(esp->e_burstsizes & BURST32))
DMAESC_SETBURST16(dmar);
dmar->dmaga_csr |= DMAESC_EN_ADD;
break;
case DMA_REV3:
if ((esp->e_type == FAS100A) ||
(esp->e_type == FAST)) {
dmar->dmaga_csr &= ~DMAGA_TURBO;
dmar->dmaga_csr |= DMAGA_TWO_CYCLE;
}
if (esp->e_burstsizes & BURST32) {
DMA2_SETBURST32(dmar);
}
break;
case DMA_REV2:
if (esp->e_type != ESP100)
dmar->dmaga_csr |= DMAGA_TURBO;
break;
default:
break;
}
dmar->dmaga_csr |= DMAGA_INTEN;
}
if (action & ESP_RESET_ESP) {
if (action & ESP_RESET_MSG)
eprintf (esp, "resetting ESP chip\n");
ep->esp_cmd = CMD_RESET_ESP; /* hard-reset ESP chip */
ep->esp_cmd = CMD_NOP | CMD_DMA;
ep->esp_cmd = CMD_NOP | CMD_DMA;/* FAS chips needs a 2nd NOP */
/*
* Re-load chip configurations
*/
ep->esp_clock_conv = esp->e_clock_conv & CLOCK_MASK;
ep->esp_timeout = esp->e_stval;
ep->esp_sync_period = 0;
ep->esp_sync_offset = 0;
/*
* enable default configurations
*/
ep->esp_conf = esp->e_espconf;
if (esp->e_type == FAST) {
register u_char idcode, fcode;
idcode = ep->esp_id_code;
fcode = (ep->esp_id_code & ESP_FCODE_MASK) >> 3;
if (fcode == ESP_FAS236)
esp->e_type = FAS236;
else
esp->e_type = FAS100A;
#ifdef ESP_TEST_FAST_SYNC
IPRINTF2("Family code %d, revision %d\n",
fcode, (idcode & ESP_REV_MASK));
#endif ESP_TEST_FAST_SYNC
}
switch (esp->e_type) {
case ESP236:
IPRINTF("type is ESP236\n");
ep->esp_conf2 = esp->e_espconf2;
ep->esp_conf3 = esp->e_espconf3[0];
break;
case FAS236:
IPRINTF("type is FAS236\n");
for (i = 0; i < NTARGETS; i++) {
esp->e_espconf3[i] |= ESP_CONF3_236_FASTCLK;
}
ep->esp_conf2 = esp->e_espconf2;
if (esp->e_differential) {
esp->e_req_ack_delay = 0;
} else {
esp->e_req_ack_delay = DEFAULT_REQ_ACK_DELAY_236;
}
break;
case FAS100A:
IPRINTF("type is FAS100A\n");
for (i = 0; i < NTARGETS; i++) {
esp->e_espconf3[i] |= ESP_CONF3_100A_FASTCLK;
}
ep->esp_conf2 = esp->e_espconf2;
esp->e_req_ack_delay = DEFAULT_REQ_ACK_DELAY_101;
break;
case ESP100A:
IPRINTF("type is ESP100A\n");
ep->esp_conf2 = esp->e_espconf2;
break;
case FAST:
IPRINTF("type is FAST\n");
break;
default:
IPRINTF("type is ???\n");
break;
}
/*
* Just in case...
*/
junk = ep->esp_intr;
#ifdef ESP_TEST_FAST_SYNC
IPRINTF1("clock conversion = %x\n",
esp->e_clock_conv & CLOCK_MASK);
IPRINTF2("conf = %x (read back: %x)\n",
esp->e_espconf, ep->esp_conf);
IPRINTF2("conf2 = %x (read back: %x)\n",
esp->e_espconf2, ep->esp_conf2);
IPRINTF4("conf3 (for target 0 - 3) = %x %x %x %x\n",
esp->e_espconf3[0], esp->e_espconf3[1],
esp->e_espconf3[2], esp->e_espconf3[3]);
IPRINTF4("conf3 (for target 4 - 7) = %x %x %x %x\n",
esp->e_espconf3[4], esp->e_espconf3[5],
esp->e_espconf3[6], esp->e_espconf3[7]);
IPRINTF2("req_ack_delay (0x%x) = %x\n", &esp->e_req_ack_delay,
esp->e_req_ack_delay);
#endif ESP_TEST_FAST_SYNC
}
if (action & ESP_RESET_SCSIBUS) {
if (action & ESP_RESET_MSG) {
eprintf (esp, "resetting SCSI bus (%s)\n",
(action & ESP_RESET_IGNORE_BRESET) ?
"ignored" : "not ignored");
}
if (action & ESP_RESET_IGNORE_BRESET) {
ep->esp_conf = ESP_CONF_DISRINT | esp->e_espconf;
ep->esp_cmd = CMD_RESET_SCSI;
ep->esp_conf = esp->e_espconf;
DELAY(scsi_reset_delay);
junk = ep->esp_intr;
} else {
ep->esp_cmd = CMD_RESET_SCSI;
DELAY(scsi_reset_delay);
}
}
#ifdef lint
junk = junk;
#endif /* lint */
}
/*
* Error logging, printing, and debug print routines
*/
/*VARARGS3*/
static void
esplog(esp, level, fmt, a, b, c, d, e, f, g, h, i)
struct esp *esp;
int level;
char *fmt;
int a, b, c, d, e, f, g, h, i;
{
int tmp;
auto char buf[64];
(void) sprintf(buf, "esp%d: ", CNUM);
(void) sprintf(&buf[strlen(buf)], fmt, a, b, c, d, e, f, g, h, i);
tmp = strlen(buf);
buf[tmp++] = '\n';
buf[tmp] = 0;
log (level, buf);
}
/*VARARGS2*/
static void
eprintf(esp, fmt, a, b, c, d, e, f, g, h, i)
struct esp *esp;
char *fmt;
int a, b, c, d, e, f, g, h, i;
{
printf("esp%d:\t", CNUM);
printf(fmt, a, b, c, d, e, f, g, h, i);
}
static void
esp_stat_int_print(esp)
struct esp *esp;
{
printf("\tStat=0x%b, Intr=0x%b\n", esp->e_stat,
esp_stat_bits, esp->e_intr, esp_int_bits);
}
static void
esp_printstate(esp, msg)
register struct esp *esp;
char *msg;
{
register struct espreg *ep = esp->e_reg;
register struct scsi_cmd *sp;
register struct dmaga *dmar = esp->e_dma;
u_char fifo_flag;
eprintf(esp, "%s\n", msg);
printf("\tState=%s Last State=%s\n", esp_state_name(esp->e_state),
esp_state_name(esp->e_laststate));
/*
* disable DVMA to avoid a timeout on SS1
*/
if (dmar->dmaga_csr & DMAGA_ENDVMA) {
dmar->dmaga_csr &= ~DMAGA_ENDVMA;
fifo_flag = ep->esp_fifo_flag;
dmar->dmaga_csr |= DMAGA_ENDVMA;
} else {
fifo_flag = ep->esp_fifo_flag;
}
printf("\tLatched stat=0x%b intr=0x%b fifo 0x%x\n", esp->e_stat,
esp_stat_bits, esp->e_intr, esp_int_bits, fifo_flag);
printf("\tlast msg out: %s; last msg in: %s\n",
scsi_mname(esp->e_last_msgout), scsi_mname(esp->e_last_msgin));
printf("\tDMA csr=0x%b\n\taddr=%x last=%x last_count=%x\n",
esp->e_dma->dmaga_csr, dmaga_bits, esp->e_dma->dmaga_addr,
esp->e_lastdma, esp->e_lastcount);
if (esp->e_cur_slot != UNDEFINED && (sp = CURRENT_CMD(esp))) {
esp_dump_cmd(sp);
}
#ifdef ESPDEBUG
esp_dump_state(esp);
#endif /* ESPDEBUG */
}
#ifdef ESPDEBUG
static void
esp_dump_state(esp)
register struct esp *esp;
{
register short x, z;
auto char buf[128];
z = esp->e_phase_index;
for (x = 1; x <= NPHASE; x++) {
register short y;
z = --z & (NPHASE - 1);
y = esp->e_phase[z].e_save_state;
if (y == STATE_FREE)
break;
(void) sprintf(&buf[0], "\tcurrent phase 0x%x=%s",
y, esp_state_name((u_short)y));
(void) sprintf(&buf[strlen(buf)], "\tstat=0x%x",
esp->e_phase[z].e_save_stat);
if (esp->e_phase[z].e_val1 != -1) {
(void) sprintf(&buf[strlen(buf)], "\t0x%x",
esp->e_phase[z].e_val1);
}
if (esp->e_phase[z].e_val2 != -1) {
(void) sprintf(&buf[strlen(buf)], "\t0x%x",
esp->e_phase[z].e_val2);
}
printf("%s\n", buf);
}
}
#endif /* ESPDEBUG */
static void
esp_dump_cmd(sp)
struct scsi_cmd *sp;
{
register i;
register u_char *cp = (u_char *) sp->cmd_pkt.pkt_cdbp;
printf("\tCmd dump for Target %d Lun %d:\n", Tgt(sp), Lun(sp));
printf("\tcdb=[");
for (i = 0; i < sp->cmd_cdblen; i++) {
printf(" 0x%x", *cp++);
}
if (sp->cmd_pkt.pkt_state & STATE_GOT_STATUS)
printf(" ]; Status=0x%x\n", sp->cmd_pkt.pkt_scbp[0]);
else
printf(" ]\n");
printf("\tpkt_state 0x%b pkt_flags 0x%x pkt_statistics 0x%x\n",
sp->cmd_pkt.pkt_state, state_bits, sp->cmd_pkt.pkt_flags,
sp->cmd_pkt.pkt_statistics);
printf("\tcmd_flags=0x%x cmd_timeout %d\n", sp->cmd_flags,
sp->cmd_timeout);
esp_dump_datasegs(sp);
}
static void
esp_dump_datasegs(sp)
struct scsi_cmd *sp;
{
register struct dataseg *seg;
printf("\tMapped Dma Space:\n");
for (seg = &sp->cmd_mapseg; seg; seg = seg->d_next) {
printf("\t\tBase = 0x%x Count = 0x%x\n",
seg->d_base, seg->d_count);
}
printf("\tTransfer History:\n");
for (seg = &sp->cmd_subseg; seg; seg = seg->d_next) {
printf("\t\tBase = 0x%x Count = 0x%x\n",
seg->d_base, seg->d_count);
}
}
static char *
esp_state_name(state)
u_char state;
{
if (state == STATE_FREE) {
return ("FREE");
} else if ((state & STATE_SELECTING) &&
(!(state & ACTS_LOG))) {
if (state == STATE_SELECT_NORMAL)
return ("SELECT");
else if (state == STATE_SELECT_N_STOP)
return ("SEL&STOP");
else if (state == STATE_SELECT_N_SENDMSG)
return ("SELECT_SNDMSG");
else
return ("SEL_NO_ATN");
} else {
static struct {
char *sname;
char state;
} names[] = {
"CMD_START", ACTS_CMD_START,
"CMD_DONE", ACTS_CMD_DONE,
"MSG_OUT", ACTS_MSG_OUT,
"MSG_OUT_DONE", ACTS_MSG_OUT_DONE,
"MSG_IN", ACTS_MSG_IN,
"MSG_IN_MORE", ACTS_MSG_IN_MORE,
"MSG_IN_DONE", ACTS_MSG_IN_DONE,
"CLEARING", ACTS_CLEARING,
"DATA", ACTS_DATA,
"DATA_DONE", ACTS_DATA_DONE,
"CMD_CMPLT", ACTS_C_CMPLT,
"UNKNOWN", ACTS_UNKNOWN,
"RESEL", ACTS_RESEL,
"RESET", ACTS_RESET,
"ABORTING", ACTS_ABORTING,
"SPANNING", ACTS_SPANNING,
"FROZEN", ACTS_FROZEN,
"PREEMPTED", ACTS_PREEMPTED,
"PROXY", ACTS_PROXY,
"SYNCHOUT", ACTS_SYNCHOUT,
"CMD_LOST", ACTS_CMD_LOST,
"DATAOUT", ACTS_DATAOUT,
"DATAIN", ACTS_DATAIN,
"STATUS", ACTS_STATUS,
"DISCONNECT", ACTS_DISCONNECT,
"NOP", ACTS_NOP,
"REJECT", ACTS_REJECT,
"RESTOREDP", ACTS_RESTOREDP,
"SAVEDP", ACTS_SAVEDP,
"BAD_RESEL", ACTS_BAD_RESEL,
0
};
register i;
for (i = 0; names[i].sname; i++) {
if (names[i].state == state)
return (names[i].sname);
}
}
return ("<BAD>");
}
#endif /* NESP > 0 */