diff options
author | mjacob <mjacob@cvs.openbsd.org> | 1999-03-17 05:26:10 +0000 |
---|---|---|
committer | mjacob <mjacob@cvs.openbsd.org> | 1999-03-17 05:26:10 +0000 |
commit | 45819d403e389eec8c222b4bb1d6f9e69a3b1c81 (patch) | |
tree | 58b38f4f25ec6f48e1acd3aa4c658cca44d241f4 | |
parent | ff2af7542270ca4ea643938ce7bfa439193412ba (diff) |
complete update of ISP driver- includes 2100 FC support
-rw-r--r-- | sys/dev/ic/isp.c | 3992 | ||||
-rw-r--r-- | sys/dev/ic/isp_openbsd.c | 438 | ||||
-rw-r--r-- | sys/dev/ic/isp_openbsd.h | 274 | ||||
-rw-r--r-- | sys/dev/ic/ispmbox.h | 779 | ||||
-rw-r--r-- | sys/dev/ic/ispreg.h | 299 | ||||
-rw-r--r-- | sys/dev/ic/ispvar.h | 386 |
6 files changed, 5448 insertions, 720 deletions
diff --git a/sys/dev/ic/isp.c b/sys/dev/ic/isp.c index d051a824bd4..4af2fbcea8a 100644 --- a/sys/dev/ic/isp.c +++ b/sys/dev/ic/isp.c @@ -1,14 +1,14 @@ -/* $NetBSD: isp.c,v 1.7 1997/06/08 06:31:52 thorpej Exp $ */ - +/* $OpenBSD: isp.c,v 1.4 1999/03/17 05:26:08 mjacob Exp $ */ +/* release_03_16_99 */ /* - * Machine Independent (well, as best as possible) + * Machine and OS Independent (well, as best as possible) * code for the Qlogic ISP SCSI adapters. * - * Specific probe attach and support routines for Qlogic ISP SCSI adapters. - * - * Copyright (c) 1997 by Matthew Jacob - * NASA AMES Research Center. + *--------------------------------------- + * Copyright (c) 1997, 1998 by Matthew Jacob + * NASA/Ames Research Center * All rights reserved. + *--------------------------------------- * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,142 +37,373 @@ /* * Inspiration and ideas about this driver are from Erik Moe's Linux driver - * (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c) + * (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c). Some + * ideas dredged from the Solaris driver. */ -#include <sys/types.h> -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/errno.h> -#include <sys/ioctl.h> -#include <sys/device.h> -#include <sys/malloc.h> -#include <sys/buf.h> -#include <sys/proc.h> -#include <sys/user.h> - - -#include <scsi/scsi_all.h> -#include <scsi/scsiconf.h> - -#include <scsi/scsi_message.h> -#include <scsi/scsi_debug.h> -#include <scsi/scsiconf.h> +/* + * Include header file appropriate for platform we're building on. + */ -#include <vm/vm.h> -#include <vm/vm_param.h> -#include <vm/pmap.h> +#ifdef __NetBSD__ +#include <dev/ic/isp_netbsd.h> +#endif +#ifdef __FreeBSD__ +#include <dev/isp/isp_freebsd.h> +#endif +#ifdef __OpenBSD__ +#include <dev/ic/isp_openbsd.h> +#endif +#ifdef __linux__ +#include "isp_linux.h" +#endif -#include <dev/ic/ispreg.h> -#include <dev/ic/ispvar.h> -#include <dev/ic/ispmbox.h> +/* + * General defines + */ #define MBOX_DELAY_COUNT 1000000 / 100 -struct cfdriver isp_cd = { - NULL, "isp", DV_DULL -}; - -static void ispminphys __P((struct buf *)); -static int32_t ispscsicmd __P((struct scsi_xfer *xs)); -static int isp_mboxcmd __P((struct ispsoftc *, mbreg_t *)); - -static struct scsi_adapter isp_switch = { - ispscsicmd, ispminphys, 0, 0 +/* + * Local static data + */ +#ifdef ISP_TARGET_MODE +static const char tgtiqd[36] = { + 0x03, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x4C, 0x4F, 0x47, 0x49, 0x43, 0x20, 0x20, +#ifdef __NetBSD__ + 0x4E, 0x45, 0x54, 0x42, 0x53, 0x44, 0x20, 0x20, +#else +# ifdef __FreeBSD__ + 0x46, 0x52, 0x45, 0x45, 0x42, 0x52, 0x44, 0x20, +# else +# ifdef __OpenBSD__ + 0x4F, 0x50, 0x45, 0x4E, 0x42, 0x52, 0x44, 0x20, +# else +# ifdef linux + 0x4C, 0x49, 0x4E, 0x55, 0x58, 0x20, 0x20, 0x20, +# else +# endif +# endif +# endif +#endif + 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x31 }; +#endif -static struct scsi_device isp_dev = { NULL, NULL, NULL, NULL }; -static int isp_poll __P((struct ispsoftc *, struct scsi_xfer *, int)); -static int isp_parse_status __P((struct ispsoftc *, ispstatusreq_t *)); -static void isp_lostcmd __P((struct ispsoftc *, struct scsi_xfer *)); +/* + * Local function prototypes. + */ +static int isp_parse_async __P((struct ispsoftc *, int)); +static int isp_handle_other_response +__P((struct ispsoftc *, ispstatusreq_t *, u_int8_t *)); +#ifdef ISP_TARGET_MODE +static int isp_modify_lun __P((struct ispsoftc *, int, int, int)); +static void isp_notify_ack __P((struct ispsoftc *, void *)); +static void isp_handle_atio __P((struct ispsoftc *, void *)); +static void isp_handle_atio2 __P((struct ispsoftc *, void *)); +static void isp_handle_ctio __P((struct ispsoftc *, void *)); +static void isp_handle_ctio2 __P((struct ispsoftc *, void *)); +#endif +static void isp_parse_status +__P((struct ispsoftc *, ispstatusreq_t *, ISP_SCSI_XFER_T *)); +static void isp_fastpost_complete __P((struct ispsoftc *, int)); +static void isp_fibre_init __P((struct ispsoftc *)); +static void isp_mark_getpdb_all __P((struct ispsoftc *)); +static int isp_getpdb __P((struct ispsoftc *, int, isp_pdb_t *)); +static int isp_fclink_test __P((struct ispsoftc *)); +static void isp_fw_state __P((struct ispsoftc *)); +static void isp_dumpregs __P((struct ispsoftc *, const char *)); +static void isp_dumpxflist __P((struct ispsoftc *)); +static void isp_mboxcmd __P((struct ispsoftc *, mbreg_t *)); + +static void isp_update __P((struct ispsoftc *)); +static void isp_setdfltparm __P((struct ispsoftc *)); +static int isp_read_nvram __P((struct ispsoftc *)); +static void isp_rdnvram_word __P((struct ispsoftc *, int, u_int16_t *)); /* * Reset Hardware. * - * Only looks at sc_dev.dv_xname, sc_iot and sc_ioh fields. + * Hit the chip over the head, download new f/w and set it running. + * + * Locking done elsewhere. */ void isp_reset(isp) struct ispsoftc *isp; { mbreg_t mbs; - int loops, i; - u_int8_t clock; + int loops, i, dodnld = 1; + char *revname; isp->isp_state = ISP_NILSTATE; + /* - * Do MD specific pre initialization + * Basic types (SCSI, FibreChannel and PCI or SBus) + * have been set in the MD code. We figure out more + * here. */ - ISP_RESET0(isp); + isp->isp_dblev = DFLT_DBLEVEL; /* - * Try and get old clock rate out before we hit the - * chip over the head- but if and only if we don't - * know our desired clock rate. + * After we've fired this chip up, zero out the conf1 register + * for SCSI adapters and other settings for the 2100. */ - clock = isp->isp_mdvec->dv_clock; - if (clock == 0) { - mbs.param[0] = MBOX_GET_CLOCK_RATE; - (void) isp_mboxcmd(isp, &mbs); - if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - clock = mbs.param[1]; - printf("board-clock 0x%x ", clock); + + /* + * Get the current running firmware revision out of the + * chip before we hit it over the head (if this is our + * first time through). Note that we store this as the + * 'ROM' firmware revision- which it may not be. In any + * case, we don't really use this yet, but we may in + * the future. + */ + if (isp->isp_used == 0) { + /* + * Just in case it was paused... + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + mbs.param[0] = MBOX_ABOUT_FIRMWARE; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + /* + * If this fails, it probably means we're running + * an old prom, if anything at all... + */ + isp->isp_romfw_rev = 0; } else { - clock = 0; + isp->isp_romfw_rev = + (((u_int16_t) mbs.param[1]) << 10) + mbs.param[2]; } + isp->isp_used = 1; } /* - * Hit the chip over the head with hammer. + * Put it into PAUSE mode. */ + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + + if (IS_FC(isp)) { + revname = "2100"; + } else if (IS_1080(isp)) { + sdparam *sdp = isp->isp_param; + revname = "1080"; + sdp->isp_clock = 0; /* don't set clock */ + sdp->isp_diffmode = 1; + sdp->isp_ultramode = 1; + } else { + sdparam *sdp = isp->isp_param; + i = ISP_READ(isp, BIU_CONF0) & BIU_CONF0_HW_MASK; + switch (i) { + default: + PRINTF("%s: unknown chip rev. 0x%x- assuming a 1020\n", + isp->isp_name, i); + /* FALLTHROUGH */ + case 1: + revname = "1020"; + isp->isp_type = ISP_HA_SCSI_1020; + sdp->isp_clock = 40; + break; + case 2: + /* + * Some 1020A chips are Ultra Capable, but don't + * run the clock rate up for that unless told to + * do so by the Ultra Capable bits being set. + */ + revname = "1020A"; + isp->isp_type = ISP_HA_SCSI_1020A; + sdp->isp_clock = 40; + break; + case 3: + revname = "1040"; + isp->isp_type = ISP_HA_SCSI_1040; + sdp->isp_clock = 60; + break; + case 4: + revname = "1040A"; + isp->isp_type = ISP_HA_SCSI_1040A; + sdp->isp_clock = 60; + break; + case 5: + revname = "1040B"; + isp->isp_type = ISP_HA_SCSI_1040B; + sdp->isp_clock = 60; + break; + } + /* + * Now, while we're at it, gather info about ultra + * and/or differential mode. + */ + if (ISP_READ(isp, SXP_PINS_DIFF) & SXP_PINS_DIFF_MODE) { + PRINTF("%s: Differential Mode\n", isp->isp_name); + sdp->isp_diffmode = 1; + } else { + sdp->isp_diffmode = 0; + } + i = ISP_READ(isp, RISC_PSR); + if (isp->isp_bustype == ISP_BT_SBUS) { + i &= RISC_PSR_SBUS_ULTRA; + } else { + i &= RISC_PSR_PCI_ULTRA; + } + if (i != 0) { + PRINTF("%s: Ultra Mode Capable\n", isp->isp_name); + sdp->isp_ultramode = 1; + /* + * If we're in Ultra Mode, we have to be 60Mhz clock- + * even for the SBus version. + */ + sdp->isp_clock = 60; + } else { + sdp->isp_ultramode = 0; + /* + * Clock is known. Gronk. + */ + } + + /* + * Machine dependent clock (if set) overrides + * our generic determinations. + */ + if (isp->isp_mdvec->dv_clock) { + if (isp->isp_mdvec->dv_clock < sdp->isp_clock) { + sdp->isp_clock = isp->isp_mdvec->dv_clock; + } + } + + } - ISP_WRITE(isp, BIU_ICR, BIU_ICR_SOFT_RESET); /* - * Give the ISP a chance to recover... + * Do MD specific pre initialization */ - delay(100); + ISP_RESET0(isp); + +again: /* - * Clear data && control DMA engines. + * Hit the chip over the head with hammer, + * and give the ISP a chance to recover. */ - ISP_WRITE(isp, CDMA_CONTROL, + + if (IS_SCSI(isp)) { + ISP_WRITE(isp, BIU_ICR, BIU_ICR_SOFT_RESET); + /* + * A slight delay... + */ + SYS_DELAY(100); + +#if 0 + PRINTF("%s: mbox0-5: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + isp->isp_name, ISP_READ(isp, OUTMAILBOX0), + ISP_READ(isp, OUTMAILBOX1), ISP_READ(isp, OUTMAILBOX2), + ISP_READ(isp, OUTMAILBOX3), ISP_READ(isp, OUTMAILBOX4), + ISP_READ(isp, OUTMAILBOX5)); +#endif + + /* + * Clear data && control DMA engines. + */ + ISP_WRITE(isp, CDMA_CONTROL, DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT); - ISP_WRITE(isp, DDMA_CONTROL, + ISP_WRITE(isp, DDMA_CONTROL, DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT); + + + } else { + ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET); + /* + * A slight delay... + */ + SYS_DELAY(100); + + /* + * Clear data && control DMA engines. + */ + ISP_WRITE(isp, CDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + ISP_WRITE(isp, TDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + ISP_WRITE(isp, RDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + } + /* * Wait for ISP to be ready to go... */ loops = MBOX_DELAY_COUNT; - while ((ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET) != 0) { - delay(100); + for (;;) { + if (isp->isp_type & ISP_HA_SCSI) { + if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET)) + break; + } else { + if (!(ISP_READ(isp, BIU2100_CSR) & BIU2100_SOFT_RESET)) + break; + } + SYS_DELAY(100); if (--loops < 0) { - printf("chip reset timed out\n", isp->isp_name); + isp_dumpregs(isp, "chip reset timed out"); return; } } + /* - * More initialization + * After we've fired this chip up, zero out the conf1 register + * for SCSI adapters and other settings for the 2100. */ - ISP_WRITE(isp, BIU_CONF1, 0); + if (IS_SCSI(isp)) { + ISP_WRITE(isp, BIU_CONF1, 0); + } else { + ISP_WRITE(isp, BIU2100_CSR, 0); + } + + /* + * Reset RISC Processor + */ ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); - delay(100); + SYS_DELAY(100); - if (isp->isp_mdvec->dv_conf1) { - ISP_SETBITS(isp, BIU_CONF1, isp->isp_mdvec->dv_conf1); - if (isp->isp_mdvec->dv_conf1 & BIU_BURST_ENABLE) { + /* + * Establish some initial burst rate stuff. + * (only for the 1XX0 boards). This really should + * be done later after fetching from NVRAM. + */ + if (IS_SCSI(isp)) { + u_int16_t tmp = isp->isp_mdvec->dv_conf1; + /* + * Busted FIFO. Turn off all but burst enables. + */ + if (isp->isp_type == ISP_HA_SCSI_1040A) { + tmp &= BIU_BURST_ENABLE; + } + ISP_SETBITS(isp, BIU_CONF1, tmp); + if (tmp & BIU_BURST_ENABLE) { ISP_SETBITS(isp, CDMA_CONF, DMA_ENABLE_BURST); ISP_SETBITS(isp, DDMA_CONF, DMA_ENABLE_BURST); } +#ifdef PTI_CARDS + if (((sdparam *) isp->isp_param)->isp_ultramode) { + while(ISP_READ(isp, RISC_MTR) != 0x1313) { + ISP_WRITE(isp, RISC_MTR, 0x1313); + ISP_WRITE(isp, HCCR, HCCR_CMD_STEP); + } + } else { + ISP_WRITE(isp, RISC_MTR, 0x1212); + } + /* + * PTI specific register + */ + ISP_WRITE(isp, RISC_EMB, DUAL_BANK) +#else + ISP_WRITE(isp, RISC_MTR, 0x1212); +#endif } else { - ISP_WRITE(isp, BIU_CONF1, 0); + ISP_WRITE(isp, RISC_MTR2100, 0x1212); } -#if 0 - ISP_WRITE(isp, RISC_MTR, 0x1212); /* FM */ -#endif ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); /* release paused processor */ /* @@ -183,258 +414,333 @@ isp_reset(isp) /* * Enable interrupts */ - ISP_WRITE(isp, BIU_ICR, - BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS); + ENABLE_INTS(isp); /* - * Do some sanity checking. + * Wait for everything to finish firing up... */ - - mbs.param[0] = MBOX_NO_OP; - (void) isp_mboxcmd(isp, &mbs); - if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("NOP test failed\n"); - return; + loops = MBOX_DELAY_COUNT; + while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) { + SYS_DELAY(100); + if (--loops < 0) { + PRINTF("%s: MBOX_BUSY never cleared on reset\n", + isp->isp_name); + return; + } } - mbs.param[0] = MBOX_MAILBOX_REG_TEST; - mbs.param[1] = 0xdead; - mbs.param[2] = 0xbeef; - mbs.param[3] = 0xffff; - mbs.param[4] = 0x1111; - mbs.param[5] = 0xa5a5; - (void) isp_mboxcmd(isp, &mbs); + /* + * Up until this point we've done everything by just reading or + * setting registers. From this point on we rely on at least *some* + * kind of firmware running in the card. + */ + + /* + * Do some sanity checking. + */ + mbs.param[0] = MBOX_NO_OP; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("Mailbox Register test didn't complete\n"); - return; - } - i = 0; - if (mbs.param[1] != 0xdead) { - printf("Register Test Failed @reg %d (got %x)\n", - 1, mbs.param[1]); - i++; - } - if (mbs.param[2] != 0xbeef) { - printf("Register Test Failed @reg %d (got %x)\n", - 2, mbs.param[2]); - i++; - } - if (mbs.param[3] != 0xffff) { - printf("Register Test Failed @reg %d (got %x)\n", - 3, mbs.param[3]); - i++; - } - if (mbs.param[4] != 0x1111) { - printf("Register Test Failed @reg %d (got %x)\n", - 4, mbs.param[4]); - i++; - } - if (mbs.param[5] != 0xa5a5) { - printf("Register Test Failed @reg %d (got %x)\n", - 5, mbs.param[5]); - i++; - } - if (i) { + isp_dumpregs(isp, "NOP test failed"); return; } - /* - * Download new Firmware - */ - for (i = 0; i < isp->isp_mdvec->dv_fwlen; i++) { - mbs.param[0] = MBOX_WRITE_RAM_WORD; - mbs.param[1] = isp->isp_mdvec->dv_codeorg + i; - mbs.param[2] = isp->isp_mdvec->dv_ispfw[i]; - (void) isp_mboxcmd(isp, &mbs); + if (isp->isp_type & ISP_HA_SCSI) { + mbs.param[0] = MBOX_MAILBOX_REG_TEST; + mbs.param[1] = 0xdead; + mbs.param[2] = 0xbeef; + mbs.param[3] = 0xffff; + mbs.param[4] = 0x1111; + mbs.param[5] = 0xa5a5; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("f/w download failed\n"); + isp_dumpregs(isp, + "Mailbox Register test didn't complete"); + return; + } + if (mbs.param[1] != 0xdead || mbs.param[2] != 0xbeef || + mbs.param[3] != 0xffff || mbs.param[4] != 0x1111 || + mbs.param[5] != 0xa5a5) { + isp_dumpregs(isp, "Register Test Failed"); return; } + } /* - * Verify that it downloaded correctly. + * Download new Firmware, unless requested not to do so. + * This is made slightly trickier in some cases where the + * firmware of the ROM revision is newer than the revision + * compiled into the driver. So, where we used to compare + * versions of our f/w and the ROM f/w, now we just see + * whether we have f/w at all and whether a config flag + * has disabled our download. */ - mbs.param[0] = MBOX_VERIFY_CHECKSUM; - mbs.param[1] = isp->isp_mdvec->dv_codeorg; - (void) isp_mboxcmd(isp, &mbs); - if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("ram checksum failure\n"); - return; + if ((isp->isp_mdvec->dv_fwlen == 0) || + (isp->isp_confopts & ISP_CFG_NORELOAD)) { + dodnld = 0; } - /* - * Now start it rolling... - */ + if (dodnld && isp->isp_mdvec->dv_fwlen) { + for (i = 0; i < isp->isp_mdvec->dv_fwlen; i++) { + mbs.param[0] = MBOX_WRITE_RAM_WORD; + mbs.param[1] = isp->isp_mdvec->dv_codeorg + i; + mbs.param[2] = isp->isp_mdvec->dv_ispfw[i]; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: F/W download failed at word %d\n", + isp->isp_name, i); + dodnld = 0; + goto again; + } + } - mbs.param[0] = MBOX_EXEC_FIRMWARE; - mbs.param[1] = isp->isp_mdvec->dv_codeorg; - (void) isp_mboxcmd(isp, &mbs); + /* + * Verify that it downloaded correctly. + */ + mbs.param[0] = MBOX_VERIFY_CHECKSUM; + mbs.param[1] = isp->isp_mdvec->dv_codeorg; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_dumpregs(isp, "ram checksum failure"); + return; + } + } else { + IDPRINTF(3, ("%s: skipping f/w download\n", isp->isp_name)); + } /* - * Set CLOCK RATE + * Now start it rolling. + * + * If we didn't actually download f/w, + * we still need to (re)start it. */ - if (clock) { - mbs.param[0] = MBOX_SET_CLOCK_RATE; - mbs.param[1] = clock; - (void) isp_mboxcmd(isp, &mbs); - if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("failed to set CLOCKRATE\n"); - return; + + mbs.param[0] = MBOX_EXEC_FIRMWARE; + if (isp->isp_mdvec->dv_codeorg) + mbs.param[1] = isp->isp_mdvec->dv_codeorg; + else + mbs.param[1] = 0x1000; + isp_mboxcmd(isp, &mbs); + + if (isp->isp_type & ISP_HA_SCSI) { + sdparam *sdp = isp->isp_param; + /* + * Set CLOCK RATE, but only if asked to. + */ + if (sdp->isp_clock) { + mbs.param[0] = MBOX_SET_CLOCK_RATE; + mbs.param[1] = sdp->isp_clock; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_dumpregs(isp, "failed to set CLOCKRATE"); + /* but continue */ + } else { + IDPRINTF(3, ("%s: setting input clock to %d\n", + isp->isp_name, sdp->isp_clock)); + } } } mbs.param[0] = MBOX_ABOUT_FIRMWARE; - (void) isp_mboxcmd(isp, &mbs); + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("ABOUT FIRMWARE command failed\n"); + isp_dumpregs(isp, "ABOUT FIRMWARE command failed"); return; } - printf("F/W rev %d.%d ", mbs.param[1], mbs.param[2]); + PRINTF("%s: Board Revision %s, %s F/W Revision %d.%d\n", + isp->isp_name, revname, dodnld? "loaded" : "resident", + mbs.param[1], mbs.param[2]); + if (isp->isp_type & ISP_HA_FC) { + if (ISP_READ(isp, BIU2100_CSR) & BIU2100_PCI64) { + PRINTF("%s: in 64-Bit PCI slot\n", isp->isp_name); + } + } + isp->isp_fwrev = (((u_int16_t) mbs.param[1]) << 10) + mbs.param[2]; + if (isp->isp_romfw_rev && dodnld) { + PRINTF("%s: Last F/W revision was %d.%d\n", isp->isp_name, + isp->isp_romfw_rev >> 10, isp->isp_romfw_rev & 0x3ff); + } + isp_fw_state(isp); isp->isp_state = ISP_RESETSTATE; } /* - * Initialize Hardware to known state + * Initialize Parameters of Hardware to a known state. + * + * Locks are held before coming here. */ + void isp_init(isp) struct ispsoftc *isp; { + sdparam *sdp; mbreg_t mbs; - int s, i, l; + int tgt; /* - * Set Default Host Adapter Parameters - * XXX: Should try and get them out of NVRAM - */ - - isp->isp_adapter_enabled = 1; - isp->isp_req_ack_active_neg = 1; - isp->isp_data_line_active_neg = 1; - isp->isp_cmd_dma_burst_enable = 1; - isp->isp_data_dma_burst_enabl = 1; - isp->isp_fifo_threshold = 2; - isp->isp_initiator_id = 7; - isp->isp_async_data_setup = 6; - isp->isp_selection_timeout = 250; - isp->isp_max_queue_depth = 256; - isp->isp_tag_aging = 8; - isp->isp_bus_reset_delay = 3; - isp->isp_retry_count = 0; - isp->isp_retry_delay = 1; - for (i = 0; i < MAX_TARGETS; i++) { - isp->isp_devparam[i].dev_flags = DPARM_DEFAULT; - isp->isp_devparam[i].exc_throttle = 16; - isp->isp_devparam[i].sync_period = 25; - isp->isp_devparam[i].sync_offset = 12; - isp->isp_devparam[i].dev_enable = 1; - } + * Must do first. + */ + isp_setdfltparm(isp); + /* + * Set up DMA for the request and result mailboxes. + */ + if (ISP_MBOXDMASETUP(isp) != 0) { + PRINTF("%s: can't setup dma mailboxes\n", isp->isp_name); + return; + } + + /* + * If we're fibre, we have a completely different + * initialization method. + */ + if (IS_FC(isp)) { + isp_fibre_init(isp); + return; + } + sdp = isp->isp_param; - s = splbio(); + /* + * If we have fast memory timing enabled, turn it on. + */ + if (sdp->isp_fast_mttr) { + ISP_WRITE(isp, RISC_MTR, 0x1313); + } + /* + * Set (possibly new) Initiator ID. + */ mbs.param[0] = MBOX_SET_INIT_SCSI_ID; - mbs.param[1] = isp->isp_initiator_id; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = sdp->isp_initiator_id; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set initiator id\n", isp->isp_name); + isp_dumpregs(isp, "failed to set initiator id"); return; } + /* + * Set Retry Delay and Count + */ mbs.param[0] = MBOX_SET_RETRY_COUNT; - mbs.param[1] = isp->isp_retry_count; - mbs.param[2] = isp->isp_retry_delay; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = sdp->isp_retry_count; + mbs.param[2] = sdp->isp_retry_delay; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set retry count and delay\n", - isp->isp_name); + isp_dumpregs(isp, "failed to set retry count and delay"); return; } + /* + * Set ASYNC DATA SETUP time. This is very important. + */ mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME; - mbs.param[1] = isp->isp_async_data_setup; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = sdp->isp_async_data_setup; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set async data setup time\n", - isp->isp_name); + isp_dumpregs(isp, "failed to set async data setup time"); return; } + /* + * Set ACTIVE Negation State. + */ mbs.param[0] = MBOX_SET_ACTIVE_NEG_STATE; mbs.param[1] = - (isp->isp_req_ack_active_neg << 4) | - (isp->isp_data_line_active_neg << 5); - (void) isp_mboxcmd(isp, &mbs); + (sdp->isp_req_ack_active_neg << 4) | + (sdp->isp_data_line_active_neg << 5); + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set active negation state\n", - isp->isp_name); + isp_dumpregs(isp, "failed to set active neg state"); return; } + /* + * Set the Tag Aging limit + */ mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT; - mbs.param[1] = isp->isp_tag_aging; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = sdp->isp_tag_aging; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set tag age limit\n", isp->isp_name); + isp_dumpregs(isp, "failed to set tag age limit"); return; } + /* + * Set selection timeout. + */ + mbs.param[0] = MBOX_SET_SELECT_TIMEOUT; - mbs.param[1] = isp->isp_selection_timeout; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = sdp->isp_selection_timeout; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set selection timeout\n", isp->isp_name); + isp_dumpregs(isp, "failed to set selection timeout"); return; } - for (i = 0; i < MAX_TARGETS; i++) { - if (isp->isp_devparam[i].dev_enable == 0) + /* + * Set current per-target parameters to a safe minimum. + */ + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + int maxlun, lun; + u_int16_t sdf; + + if (sdp->isp_devparam[tgt].dev_enable == 0) continue; + sdf = DPARM_SAFE_DFLT; + /* + * It is not quite clear when this changed over so that + * we could force narrow and async, so assume >= 7.55. + * + * Otherwise, a SCSI bus reset issued below will force + * the back to the narrow, async state (but see note + * below also). Technically we should also do without + * Parity. + */ + if (isp->isp_fwrev >= ISP_FW_REV(7, 55)) { + sdf |= DPARM_NARROW | DPARM_ASYNC; + } + mbs.param[0] = MBOX_SET_TARGET_PARAMS; - mbs.param[1] = i << 8; - mbs.param[2] = isp->isp_devparam[i].dev_flags << 8; - mbs.param[3] = - (isp->isp_devparam[i].sync_offset << 8) | - (isp->isp_devparam[i].sync_period); - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = tgt << 8; + mbs.param[2] = sdf; + mbs.param[3] = 0; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set target parameters\n", - isp->isp_name); - return; + sdf = DPARM_SAFE_DFLT; + mbs.param[0] = MBOX_SET_TARGET_PARAMS; + mbs.param[1] = tgt << 8; + mbs.param[2] = sdf; + mbs.param[3] = 0; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: failed even to set defaults for " + "target %d\n", isp->isp_name, tgt); + continue; + } } + sdp->isp_devparam[tgt].cur_dflags = sdf; - for (l = 0; l < MAX_LUNS; l++) { + maxlun = (isp->isp_fwrev >= ISP_FW_REV(7, 55))? 32 : 8; + for (lun = 0; lun < maxlun; lun++) { mbs.param[0] = MBOX_SET_DEV_QUEUE_PARAMS; - mbs.param[1] = (i << 8) | l; - mbs.param[2] = isp->isp_max_queue_depth; - mbs.param[3] = isp->isp_devparam[i].exc_throttle; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = (tgt << 8) | lun; + mbs.param[2] = sdp->isp_max_queue_depth; + mbs.param[3] = sdp->isp_devparam[tgt].exc_throttle; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: failed to set device queue " - "parameters\n", isp->isp_name); - return; + PRINTF("%s: failed to set device queue " + "parameters for target %d, lun %d\n", + isp->isp_name, tgt, lun); + break; } } - } - - - /* - * Set up DMA for the request and result mailboxes. - */ - if (ISP_MBOXDMASETUP(isp)) { - (void) splx(s); - printf("%s: can't setup DMA for mailboxes\n", isp->isp_name); - return; + /* + * And mark this as an unannounced device + */ + sdp->isp_devparam[tgt].dev_announced = 0; } mbs.param[0] = MBOX_INIT_RES_QUEUE; @@ -443,10 +749,9 @@ isp_init(isp) mbs.param[3] = (u_int16_t) (isp->isp_result_dma & 0xffff); mbs.param[4] = 0; mbs.param[5] = 0; - (void) isp_mboxcmd(isp, &mbs); + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: set of response queue failed\n", isp->isp_name); + isp_dumpregs(isp, "set of response queue failed"); return; } isp->isp_residx = 0; @@ -456,280 +761,778 @@ isp_init(isp) mbs.param[2] = (u_int16_t) (isp->isp_rquest_dma >> 16); mbs.param[3] = (u_int16_t) (isp->isp_rquest_dma & 0xffff); mbs.param[4] = 0; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[5] = 0; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: set of request queue failed\n", isp->isp_name); + isp_dumpregs(isp, "set of request queue failed"); return; } - isp->isp_reqidx = 0; + isp->isp_reqidx = isp->isp_reqodx = 0; - /* - * Unfortunately, this is the only way right now for - * forcing a sync renegotiation. If we boot off of - * an Alpha, it's put the chip in SYNC mode, but we - * haven't necessarily set up the parameters the - * same, so we'll have to yank the reset line to - * get everyone to renegotiate. + /* + * Turn on Fast Posting */ +#ifndef ISP_NO_FASTPOST_SCSI + if (isp->isp_fwrev >= ISP_FW_REV(7, 55)) { + mbs.param[0] = MBOX_SET_FW_FEATURES; + mbs.param[1] = FW_FEATURE_FAST_POST; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: unable to enable FAST Posting\n", + isp->isp_name); + } + } +#endif - + /* + * Let the outer layers decide whether to issue a SCSI bus reset. + */ +#if 0 + /* + * XXX: See whether or not for 7.55 F/W or later we + * XXX: can do without this, and see whether we should + * XXX: honor the NVRAM SCSI_RESET_DISABLE token. + */ mbs.param[0] = MBOX_BUS_RESET; - mbs.param[1] = 2; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = 3; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - (void) splx(s); - printf("%s: SCSI bus reset failed\n", isp->isp_name); + isp_dumpregs(isp, "SCSI bus reset failed"); } + /* + * This is really important to have set after a bus reset. + */ isp->isp_sendmarker = 1; - (void) splx(s); +#endif isp->isp_state = ISP_INITSTATE; } /* - * Complete attachment of Hardware, include subdevices. + * Fibre Channel specific initialization. + * + * Locks are held before coming here. */ -void -isp_attach(isp) +static void +isp_fibre_init(isp) struct ispsoftc *isp; { - isp->isp_state = ISP_RUNSTATE; - isp->isp_link.adapter_softc = isp; - isp->isp_link.adapter_target = isp->isp_initiator_id; - isp->isp_link.adapter = &isp_switch; - isp->isp_link.device = &isp_dev; - isp->isp_link.openings = RQUEST_QUEUE_LEN / (MAX_TARGETS - 1); - isp->isp_link.adapter_buswidth = MAX_TARGETS; - config_found((void *)isp, &isp->isp_link, scsiprint); -} + fcparam *fcp; + isp_icb_t *icbp; + mbreg_t mbs; + int count, loopid; + + fcp = isp->isp_param; + + /* + * For systems that don't have BIOS methods for which + * we can easily change the NVRAM based loopid, we'll + * override that here. Note that when we initialize + * the firmware we may get back a different loopid than + * we asked for anyway. XXX This is probably not the + * best way to figure this out XXX + */ +#ifndef __i386__ + loopid = DEFAULT_LOOPID; +#else + loopid = fcp->isp_loopid; +#endif + +#if defined(ISP2100_FABRIC) && defined(ISP2100_SCCLUN) + PRINTF("%s: Fabric Support, Expanded Lun Support\n", isp->isp_name); +#endif +#if defined(ISP2100_FABRIC) && !defined(ISP2100_SCCLUN) + PRINTF("%s: Fabric Support\n", isp->isp_name); +#endif +#if !defined(ISP2100_FABRIC) && defined(ISP2100_SCCLUN) + PRINTF("%s: Expanded Lun Support\n", isp->isp_name); +#endif + icbp = (isp_icb_t *) fcp->isp_scratch; + MEMZERO(icbp, sizeof (*icbp)); + + icbp->icb_version = ICB_VERSION1; +#ifdef ISP_TARGET_MODE + fcp->isp_fwoptions = ICBOPT_TGT_ENABLE|ICBOPT_INI_TGTTYPE; +#else + fcp->isp_fwoptions = 0; +#endif + fcp->isp_fwoptions |= ICBOPT_INI_ADISC|ICBOPT_FAIRNESS; + fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE; + fcp->isp_fwoptions |= ICBOPT_HARD_ADDRESS; +#ifndef ISP_NO_FASTPOST_FC + fcp->isp_fwoptions |= ICBOPT_FAST_POST; +#endif +#ifdef CHECKME + fcp->isp_fwoptions |= ICBOPT_USE_PORTNAME; +#endif +#ifdef ISP2100_FABRIC + fcp->isp_fwoptions |= ICBOPT_FULL_LOGIN; +#endif + + icbp->icb_fwoptions = fcp->isp_fwoptions; + icbp->icb_maxfrmlen = fcp->isp_maxfrmlen; + if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || + icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) { + PRINTF("%s: bad frame length (%d) from NVRAM- using %d\n", + isp->isp_name, fcp->isp_maxfrmlen, ICB_DFLT_FRMLEN); + } + icbp->icb_maxalloc = fcp->isp_maxalloc; + icbp->icb_execthrottle = fcp->isp_execthrottle; + icbp->icb_retry_delay = fcp->isp_retry_delay; + icbp->icb_retry_count = fcp->isp_retry_count; + icbp->icb_hardaddr = loopid; + + MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, fcp->isp_wwn); + if (icbp->icb_fwoptions & ICBOPT_USE_PORTNAME) { + u_int64_t portname = fcp->isp_wwn | (2LL << 56); + MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, portname); + } + icbp->icb_rqstqlen = RQUEST_QUEUE_LEN; + icbp->icb_rsltqlen = RESULT_QUEUE_LEN; + icbp->icb_rqstaddr[RQRSP_ADDR0015] = + (u_int16_t) (isp->isp_rquest_dma & 0xffff); + icbp->icb_rqstaddr[RQRSP_ADDR1631] = + (u_int16_t) (isp->isp_rquest_dma >> 16); + icbp->icb_respaddr[RQRSP_ADDR0015] = + (u_int16_t) (isp->isp_result_dma & 0xffff); + icbp->icb_respaddr[RQRSP_ADDR1631] = + (u_int16_t) (isp->isp_result_dma >> 16); + MemoryBarrier(); + + for (count = 0; count < 10; count++) { + mbs.param[0] = MBOX_INIT_FIRMWARE; + mbs.param[1] = 0; + mbs.param[2] = (u_int16_t) (fcp->isp_scdma >> 16); + mbs.param[3] = (u_int16_t) (fcp->isp_scdma & 0xffff); + mbs.param[4] = 0; + mbs.param[5] = 0; + mbs.param[6] = 0; + mbs.param[7] = 0; + + isp_mboxcmd(isp, &mbs); + + switch (mbs.param[0]) { + case MBOX_COMMAND_COMPLETE: + count = 10; + break; + case ASYNC_PDB_CHANGED: + isp_mark_getpdb_all(isp); + /* FALL THROUGH */ + case ASYNC_LIP_OCCURRED: + case ASYNC_LOOP_UP: + case ASYNC_LOOP_DOWN: + case ASYNC_LOOP_RESET: + case ASYNC_CHANGE_NOTIFY: + if (count > 9) { + PRINTF("%s: too many retries to get going- " + "giving up\n", isp->isp_name); + return; + } + break; + default: + isp_dumpregs(isp, "INIT FIRMWARE failed"); + return; + } + } + isp->isp_reqidx = isp->isp_reqodx = 0; + isp->isp_residx = 0; + isp->isp_sendmarker = 1; + + /* + * Whatever happens, we're now committed to being here. + */ + isp->isp_state = ISP_INITSTATE; + fcp->isp_fwstate = FW_CONFIG_WAIT; + +#ifdef ISP_TARGET_MODE + if (isp_modify_lun(isp, 0, 1, 1)) { + PRINTF("%s: failed to enable target mode\n", isp->isp_name); + } +#endif +} /* - * Free any associated resources prior to decommissioning. + * Fibre Channel Support- get the port database for the id. + * + * Locks are held before coming here. Return 0 if success, + * else failure. */ -void -isp_uninit(isp) + +static void +isp_mark_getpdb_all(isp) struct ispsoftc *isp; { + isp_pdb_t *p; + fcparam *fcp = (fcparam *) isp->isp_param; + for (p = &fcp->isp_pdb[0]; p < &fcp->isp_pdb[MAX_FC_TARG]; p++) { + p->pdb_options = INVALID_PDB_OPTIONS; + } +} + +static int +isp_getpdb(isp, id, pdbp) + struct ispsoftc *isp; + int id; + isp_pdb_t *pdbp; +{ +#ifdef GETPDB_WORKING_YET + fcparam *fcp = (fcparam *) isp->isp_param; + mbreg_t mbs; + + /* + * Get Port Queue Parameters first- this is + * a Q&D way to see whether we're logged into + * this port. + */ + mbs.param[0] = MBOX_GET_DEV_QUEUE_PARAMS; + mbs.param[1] = id << 8; + mbs.param[2] = 0; +#ifdef ISP2100_SCCLUN + mbs.param[3] = 0; +#endif + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) + return (-1); + + mbs.param[0] = MBOX_GET_PORT_DB; + mbs.param[1] = id << 8; + mbs.param[2] = (u_int16_t) (fcp->isp_scdma >> 16); + mbs.param[3] = (u_int16_t) (fcp->isp_scdma & 0xffff); + mbs.param[4] = 0; + mbs.param[5] = 0; + mbs.param[6] = 0; + mbs.param[7] = 0; + isp_mboxcmd(isp, &mbs); + switch (mbs.param[0]) { + case MBOX_COMMAND_COMPLETE: + MemoryBarrier(); + MEMCPY(pdbp, fcp->isp_scratch, sizeof (isp_pdb_t)); + break; + case MBOX_HOST_INTERFACE_ERROR: + PRINTF("%s: DMA error getting port database\n", isp->isp_name); + return (-1); + case MBOX_COMMAND_PARAM_ERROR: + /* Not Logged In */ + IDPRINTF(3, ("%s: Comand Param Error on Get Port Database\n", + isp->isp_name)); + return (-1); + default: + PRINTF("%s: error 0x%x getting port database for ID %d\n", + isp->isp_name, mbs.param[0], id); + return (-1); + } +#else + pdbp->pdb_options = 1; +#endif + return (0); } /* - * minphys our xfers + * Make sure we have good FC link and know our Loop ID. */ -static void -ispminphys(bp) - struct buf *bp; +static int +isp_fclink_test(isp) + struct ispsoftc *isp; { + mbreg_t mbs; + int count; + u_int8_t lwfs; + fcparam *fcp; + + fcp = isp->isp_param; + + /* + * Wait up to N microseconds for F/W to go to a ready state. + * This is a platform specific + */ + lwfs = FW_CONFIG_WAIT; + for (count = 0; count < FC_FW_READY_DELAY; count += 100) { + isp_fw_state(isp); + if (lwfs != fcp->isp_fwstate) { + PRINTF("%s: Firmware State %s -> %s\n", + isp->isp_name, isp2100_fw_statename((int)lwfs), + isp2100_fw_statename((int)fcp->isp_fwstate)); + lwfs = fcp->isp_fwstate; + } + if (fcp->isp_fwstate == FW_READY) { + break; + } + SYS_DELAY(100); /* wait 100 microseconds */ + } + + /* + * If we haven't gone to 'ready' state, return. + */ + if (fcp->isp_fwstate != FW_READY) { + return (-1); + } /* - * XX: Only the 1020 has a 24 bit limit. + * Get our Loop ID (if possible). We really need to have it. */ - if (bp->b_bcount >= (1 << 24)) { - bp->b_bcount = (1 << 24) - 1; + mbs.param[0] = MBOX_GET_LOOP_ID; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: GET LOOP ID failed\n", isp->isp_name); + return (-1); } - minphys(bp); + fcp->isp_loopid = mbs.param[1]; + fcp->isp_alpa = mbs.param[2]; + PRINTF("%s: Loop ID %d, ALPA 0x%x\n", isp->isp_name, + fcp->isp_loopid, fcp->isp_alpa); + return (0); + } /* - * start an xfer + * Start a command. Locking is assumed done in the caller. */ -static int32_t + +int32_t ispscsicmd(xs) - struct scsi_xfer *xs; + ISP_SCSI_XFER_T *xs; { struct ispsoftc *isp; u_int8_t iptr, optr; - ispreq_t *req; - int s, i; + union { + ispreq_t *_reqp; + ispreqt2_t *_t2reqp; + } _u; +#define reqp _u._reqp +#define t2reqp _u._t2reqp +#define UZSIZE max(sizeof (ispreq_t), sizeof (ispreqt2_t)) + int i, rqidx; + + XS_INITERR(xs); + isp = XS_ISP(xs); + + if (isp->isp_state != ISP_RUNSTATE) { + PRINTF("%s: adapter not ready\n", isp->isp_name); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_COMPLETE); + } + + /* + * We *could* do the different sequence type that has close + * to the whole Queue Entry for the command,... + */ + + if (XS_CDBLEN(xs) > ((isp->isp_type & ISP_HA_FC)? 16 : 12)) { + PRINTF("%s: unsupported cdb length (%d)\n", + isp->isp_name, XS_CDBLEN(xs)); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_COMPLETE); + } + + /* + * Check to see whether we have good firmware state still or + * need to refresh our port database for this target. + */ + if (IS_FC(isp)) { + fcparam *fcp = isp->isp_param; + isp_pdb_t *pdbp = &fcp->isp_pdb[XS_TGT(xs)]; + + /* + * Check for f/w being in ready state. Well, okay, + * our cached copy of it... + */ + if (fcp->isp_fwstate != FW_READY) { + if (isp_fclink_test(isp)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + } + /* + * Here's the spot we would need to find out whether + * the port names have changed, whether it's still + * a target role, etc.. + */ + if (pdbp->pdb_options == INVALID_PDB_OPTIONS) { + /* + * If we don't know what it is- don't talk to it. + * This also handles cases where it's not logged + * into this port/target. + */ + if (isp_getpdb(isp, XS_TGT(xs), pdbp)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); +#ifdef GETPDB_WORKING_YET + } else { + isp_async(isp, ISPASYNC_PDB_CHANGE_COMPLETE, + (void *) (long) XS_TGT(xs)); +#endif + } + } + } - isp = xs->sc_link->adapter_softc; + /* + * Next check to see if any HBA or Device + * parameters need to be updated. + */ + if (isp->isp_update) { + isp_update(isp); + } - optr = ISP_READ(isp, OUTMAILBOX4); + optr = isp->isp_reqodx = ISP_READ(isp, OUTMAILBOX4); iptr = isp->isp_reqidx; - req = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); - iptr = (iptr + 1) & (RQUEST_QUEUE_LEN - 1); + reqp = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN); if (iptr == optr) { - printf("%s: Request Queue Overflow\n", isp->isp_name); - xs->error = XS_DRIVER_STUFFUP; - return (TRY_AGAIN_LATER); + IDPRINTF(2, ("%s: Request Queue Overflow\n", isp->isp_name)); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); } - s = splbio(); if (isp->isp_sendmarker) { - ipsmarkreq_t *marker = (ipsmarkreq_t *) req; + u_int8_t niptr; + ispmarkreq_t *marker = (ispmarkreq_t *) reqp; - bzero((void *) marker, sizeof (*marker)); + MEMZERO((void *) marker, sizeof (*marker)); marker->req_header.rqs_entry_count = 1; marker->req_header.rqs_entry_type = RQSTYPE_MARKER; marker->req_modifier = SYNC_ALL; - isp->isp_sendmarker = 0; - - if (((iptr + 1) & (RQUEST_QUEUE_LEN - 1)) == optr) { - ISP_WRITE(isp, INMAILBOX4, iptr); - isp->isp_reqidx = iptr; - (void) splx(s); - printf("%s: Request Queue Overflow+\n", isp->isp_name); - xs->error = XS_DRIVER_STUFFUP; - return (TRY_AGAIN_LATER); + /* + * Unconditionally update the input pointer anyway. + */ + ISP_WRITE(isp, INMAILBOX4, iptr); + isp->isp_reqidx = iptr; + + niptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN); + if (niptr == optr) { + IDPRINTF(2, ("%s: Request Queue Overflow+\n", + isp->isp_name)); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); } - req = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); - iptr = (iptr + 1) & (RQUEST_QUEUE_LEN - 1); + reqp = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = niptr; } + MEMZERO((void *) reqp, UZSIZE); + reqp->req_header.rqs_entry_count = 1; + if (isp->isp_type & ISP_HA_FC) { + reqp->req_header.rqs_entry_type = RQSTYPE_T2RQS; + } else { + reqp->req_header.rqs_entry_type = RQSTYPE_REQUEST; + } + reqp->req_header.rqs_flags = 0; + reqp->req_header.rqs_seqno = isp->isp_seqno++; - bzero((void *) req, sizeof (*req)); - req->req_header.rqs_entry_count = 1; - req->req_header.rqs_entry_type = RQSTYPE_REQUEST; - req->req_header.rqs_flags = 0; - req->req_header.rqs_seqno = isp->isp_seqno++; - - for (i = 0; i < RQUEST_QUEUE_LEN; i++) { - if (isp->isp_xflist[i] == NULL) + for (rqidx = 0; rqidx < RQUEST_QUEUE_LEN; rqidx++) { + if (isp->isp_xflist[rqidx] == NULL) break; } - if (i == RQUEST_QUEUE_LEN) { - panic("%s: ran out of xflist pointers", isp->isp_name); - /* NOTREACHED */ + if (rqidx == RQUEST_QUEUE_LEN) { + IDPRINTF(2, ("%s: out of xflist pointers\n", isp->isp_name)); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); } else { - isp->isp_xflist[i] = xs; - req->req_handle = i; + /* + * Never have a handle that is zero, so + * set req_handle off by one. + */ + isp->isp_xflist[rqidx] = xs; + reqp->req_handle = rqidx+1; } - req->req_flags = 0; - req->req_lun_trn = xs->sc_link->lun; - req->req_target = xs->sc_link->target; - req->req_cdblen = xs->cmdlen; - bcopy((void *)xs->cmd, req->req_cdb, xs->cmdlen); - -#if 0 - printf("%s(%d.%d): START%d cmd 0x%x datalen %d\n", isp->isp_name, - xs->sc_link->target, xs->sc_link->lun, - req->req_header.rqs_seqno, *(u_char *) xs->cmd, xs->datalen); + if (isp->isp_type & ISP_HA_FC) { + /* + * See comment in isp_intr + */ + XS_RESID(xs) = 0; + /* + * Fibre Channel always requires some kind of tag. + * If we're marked as "Can't Tag", just do simple + * instead of ordered tags. It's pretty clear to me + * that we shouldn't do head of queue tagging in + * this case. + */ + if (XS_CANTAG(xs)) { + t2reqp->req_flags = XS_KINDOF_TAG(xs); + } else { + t2reqp->req_flags = REQFLAG_STAG; + } + } else { + sdparam *sdp = (sdparam *)isp->isp_param; + if ((sdp->isp_devparam[XS_TGT(xs)].cur_dflags & DPARM_TQING) && + XS_CANTAG(xs)) { + reqp->req_flags = XS_KINDOF_TAG(xs); + } else { + reqp->req_flags = 0; + } + } + reqp->req_target = XS_TGT(xs); + if (isp->isp_type & ISP_HA_SCSI) { + reqp->req_lun_trn = XS_LUN(xs); + reqp->req_cdblen = XS_CDBLEN(xs); + } else { +#ifdef ISP2100_SCCLUN + reqp->req_scclun = XS_LUN(xs); +#else + reqp->req_lun_trn = XS_LUN(xs); #endif - req->req_time = xs->timeout / 1000; - req->req_seg_count = 0; - if (ISP_DMASETUP(isp, xs, req, &iptr, optr)) { - (void) splx(s); - xs->error = XS_DRIVER_STUFFUP; - return (COMPLETE); - } - xs->error = 0; - ISP_WRITE(isp, INMAILBOX4, iptr); - isp->isp_reqidx = iptr; - (void) splx(s); - if ((xs->flags & SCSI_POLL) == 0) { - return (SUCCESSFULLY_QUEUED); } + MEMCPY(reqp->req_cdb, XS_CDBP(xs), XS_CDBLEN(xs)); + + IDPRINTF(5, ("%s(%d.%d): START%d cmd 0x%x datalen %d\n", isp->isp_name, + XS_TGT(xs), XS_LUN(xs), reqp->req_header.rqs_seqno, + reqp->req_cdb[0], XS_XFRLEN(xs))); + + reqp->req_time = XS_TIME(xs) / 1000; + if (reqp->req_time == 0 && XS_TIME(xs)) + reqp->req_time = 1; /* - * If we can't use interrupts, poll on completion. + * Always give a bit more leeway to commands after a bus reset. */ - if (isp_poll(isp, xs, xs->timeout)) { -#if 0 - /* XXX try to abort it, or whatever */ - if (isp_poll(isp, xs, xs->timeout) { - /* XXX really nuke it */ - } -#endif + if (isp->isp_sendmarker && reqp->req_time < 5) + reqp->req_time = 5; + + i = ISP_DMASETUP(isp, xs, reqp, &iptr, optr); + if (i != CMD_QUEUED) { /* - * If no other error occurred but we didn't finish, - * something bad happened. + * Take memory of it away... */ - if ((xs->flags & ITSDONE) == 0 && xs->error == XS_NOERROR) { - isp_lostcmd(isp, xs); - xs->error = XS_DRIVER_STUFFUP; - } + isp->isp_xflist[rqidx] = NULL; + /* + * dmasetup sets actual error in packet, and + * return what we were given to return. + */ + return (i); } - return (COMPLETE); + XS_SETERR(xs, HBA_NOERROR); + MemoryBarrier(); + ISP_WRITE(isp, INMAILBOX4, iptr); + isp->isp_reqidx = iptr; + isp->isp_nactive++; + if (isp->isp_sendmarker) + isp->isp_sendmarker = 0; + return (CMD_QUEUED); +#undef reqp +#undef t2reqp } /* - * Interrupt Service Routine(s) + * isp control + * Locks (ints blocked) assumed held. */ int -isp_poll(isp, xs, mswait) +isp_control(isp, ctl, arg) struct ispsoftc *isp; - struct scsi_xfer *xs; - int mswait; + ispctl_t ctl; + void *arg; { + ISP_SCSI_XFER_T *xs; + mbreg_t mbs; + int i; - while (mswait) { - /* Try the interrupt handling routine */ - (void)isp_intr((void *)isp); + switch (ctl) { + default: + PRINTF("%s: isp_control unknown control op %x\n", + isp->isp_name, ctl); + break; - /* See if the xs is now done */ - if (xs->flags & ITSDONE) - return (0); - delay(1000); /* wait one millisecond */ - mswait--; + case ISPCTL_RESET_BUS: + /* + * This is really important to have set after a bus reset. + */ + isp->isp_sendmarker = 1; + + /* + * Issue a bus reset. + */ + mbs.param[0] = MBOX_BUS_RESET; + if (isp->isp_type & ISP_HA_SCSI) { + mbs.param[1] = + ((sdparam *) isp->isp_param)->isp_bus_reset_delay; + if (mbs.param[1] < 2) + mbs.param[1] = 2; + } else { + /* + * Unparameterized. + */ + mbs.param[1] = 5; + } + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_dumpregs(isp, "isp_control SCSI bus reset failed"); + break; + } + PRINTF("%s: driver initiated bus reset\n", isp->isp_name); + return (0); + + case ISPCTL_RESET_DEV: + mbs.param[0] = MBOX_ABORT_TARGET; + mbs.param[1] = ((long)arg) << 8; + mbs.param[2] = 3; /* 'delay', in seconds */ + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_dumpregs(isp, "Target Reset Failed"); + break; + } + PRINTF("%s: Target %d Reset Succeeded\n", isp->isp_name, + (int) ((long) arg)); + isp->isp_sendmarker = 1; + return (0); + + case ISPCTL_ABORT_CMD: + xs = (ISP_SCSI_XFER_T *) arg; + for (i = 0; i < RQUEST_QUEUE_LEN; i++) { + if (xs == isp->isp_xflist[i]) { + break; + } + } + if (i == RQUEST_QUEUE_LEN) { + PRINTF("%s: isp_control- cannot find command to abort " + "in active list\n", isp->isp_name); + break; + } + mbs.param[0] = MBOX_ABORT; +#ifdef ISP2100_SCCLUN + if (isp->isp_type & ISP_HA_FC) { + mbs.param[1] = XS_TGT(xs) << 8; + mbs.param[4] = 0; + mbs.param[5] = 0; + mbs.param[6] = XS_LUN(xs); + } else { + mbs.param[1] = XS_TGT(xs) << 8 | XS_LUN(xs); + } +#else + mbs.param[1] = XS_TGT(xs) << 8 | XS_LUN(xs); +#endif + mbs.param[2] = (i+1) >> 16; + mbs.param[3] = (i+1) & 0xffff; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: isp_control MBOX_ABORT failure (code %x)\n", + isp->isp_name, mbs.param[0]); + break; + } + PRINTF("%s: command for target %d lun %d was aborted\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + return (0); + + case ISPCTL_UPDATE_PARAMS: + isp_update(isp); + return(0); + + case ISPCTL_FCLINK_TEST: + return (isp_fclink_test(isp)); } - return (1); + return (-1); } +/* + * Interrupt Service Routine(s). + * + * External (OS) framework has done the appropriate locking, + * and the locking will be held throughout this function. + */ + int isp_intr(arg) void *arg; { - struct scsi_xfer *xs; + ISP_SCSI_XFER_T *complist[RESULT_QUEUE_LEN], *xs; struct ispsoftc *isp = arg; - u_int16_t iptr, optr, isr; + u_int8_t iptr, optr; + u_int16_t isr; + int i, nlooked = 0, ndone = 0; isr = ISP_READ(isp, BIU_ISR); - if (isr == 0 || (isr & BIU_ISR_RISC_INT) == 0) { -#if 0 - if (isr) { - printf("%s: isp_intr isr=%x\n", isp->isp_name, isr); + if (isp->isp_type & ISP_HA_FC) { + if (isr == 0 || (isr & BIU2100_ISR_RISC_INT) == 0) { + if (isr) { + IDPRINTF(4, ("%s: isp_intr isr=%x\n", + isp->isp_name, isr)); + } + return (0); + } + } else { + if (isr == 0 || (isr & BIU_ISR_RISC_INT) == 0) { + if (isr) { + IDPRINTF(4, ("%s: isp_intr isr=%x\n", + isp->isp_name, isr)); + } + return (0); } -#endif - return (0); } + if (ISP_READ(isp, BIU_SEMA) & 1) { + u_int16_t mbox = ISP_READ(isp, OUTMAILBOX0); + if (mbox & 0x4000) { + if (mbox != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: isp_intr sees 0x%x\n", + isp->isp_name,mbox); + } + ISP_WRITE(isp, BIU_SEMA, 0); + } else { + u_int32_t fhandle = isp_parse_async(isp, (int) mbox); + ISP_WRITE(isp, BIU_SEMA, 0); + if (fhandle < 0) { + return (1); + } else if (fhandle > 0) { + xs = (ISP_SCSI_XFER_T *) + isp->isp_xflist[fhandle - 1]; + isp->isp_xflist[fhandle - 1] = NULL; + /* + * Since we don't have a result queue entry + * item, we must believe that SCSI status is + * zero and that all data transferred. + */ + XS_RESID(xs) = 0; + XS_STS(xs) = 0; + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, fhandle - 1); + } + if (isp->isp_nactive > 0) + isp->isp_nactive--; + complist[ndone++] = xs; + } + } + } + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + optr = isp->isp_residx; iptr = ISP_READ(isp, OUTMAILBOX5); - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); - ISP_WRITE(isp, BIU_ICR, - BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS); - if (ISP_READ(isp, BIU_SEMA) & 1) { - u_int16_t mbox0 = ISP_READ(isp, OUTMAILBOX0); - switch (mbox0) { - case ASYNC_BUS_RESET: - case ASYNC_TIMEOUT_RESET: - printf("%s: bus or timeout reset\n", isp->isp_name); - isp->isp_sendmarker = 1; - break; - default: - printf("%s: async %x\n", isp->isp_name, mbox0); - break; - } - ISP_WRITE(isp, BIU_SEMA, 0); -#if 0 - } else { - if (optr == iptr) { - printf("why'd we interrupt? isr %x iptr %x optr %x\n", - isr, optr, iptr); - } -#endif + if (optr == iptr) { + IDPRINTF(4, ("why intr? isr %x iptr %x optr %x\n", + isr, optr, iptr)); } + ENABLE_INTS(isp); while (optr != iptr) { ispstatusreq_t *sp; + u_int8_t oop; int buddaboom = 0; sp = (ispstatusreq_t *) ISP_QUEUE_ENTRY(isp->isp_result, optr); - - optr = (optr + 1) & (RESULT_QUEUE_LEN-1); + oop = optr; + optr = ISP_NXT_QENTRY(optr, RESULT_QUEUE_LEN); + nlooked++; + MemoryBarrier(); if (sp->req_header.rqs_entry_type != RQSTYPE_RESPONSE) { - printf("%s: not RESPONSE in RESPONSE Queue (0x%x)\n", - isp->isp_name, sp->req_header.rqs_entry_type); + if (isp_handle_other_response(isp, sp, &optr) == 0) { + ISP_WRITE(isp, INMAILBOX5, optr); + continue; + } + /* + * It really has to be a bounced request just copied + * from the request queue to the response queue. + */ + if (sp->req_header.rqs_entry_type != RQSTYPE_REQUEST) { ISP_WRITE(isp, INMAILBOX5, optr); continue; } + PRINTF("%s: not RESPONSE in RESPONSE Queue " + "(type 0x%x) @ idx %d (next %d)\n", isp->isp_name, + sp->req_header.rqs_entry_type, oop, optr); buddaboom = 1; } @@ -738,68 +1541,148 @@ isp_intr(arg) ISP_WRITE(isp, INMAILBOX5, optr); continue; } - printf("%s: rqs_flags=%x\n", isp->isp_name, + PRINTF("%s: rqs_flags=%x", isp->isp_name, sp->req_header.rqs_flags & 0xf); + if (sp->req_header.rqs_flags & RQSFLAG_FULL) { + PRINTF("%s: internal queues full\n", + isp->isp_name); + /* XXXX: this command *could* get restarted */ + buddaboom++; + } + if (sp->req_header.rqs_flags & RQSFLAG_BADHEADER) { + PRINTF("%s: bad header\n", isp->isp_name); + buddaboom++; + } + if (sp->req_header.rqs_flags & RQSFLAG_BADPACKET) { + PRINTF("%s: bad request packet\n", + isp->isp_name); + buddaboom++; + } } - if (sp->req_handle >= RQUEST_QUEUE_LEN) { - printf("%s: bad request handle %d\n", isp->isp_name, + if (sp->req_handle > RQUEST_QUEUE_LEN || sp->req_handle < 1) { + PRINTF("%s: bad request handle %d\n", isp->isp_name, sp->req_handle); ISP_WRITE(isp, INMAILBOX5, optr); continue; } - xs = (struct scsi_xfer *) isp->isp_xflist[sp->req_handle]; + xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[sp->req_handle - 1]; if (xs == NULL) { - printf("%s: NULL xs in xflist\n", isp->isp_name); + PRINTF("%s: NULL xs in xflist (handle %x)\n", + isp->isp_name, sp->req_handle); + isp_dumpxflist(isp); ISP_WRITE(isp, INMAILBOX5, optr); continue; } - isp->isp_xflist[sp->req_handle] = NULL; + isp->isp_xflist[sp->req_handle - 1] = NULL; if (sp->req_status_flags & RQSTF_BUS_RESET) { isp->isp_sendmarker = 1; } if (buddaboom) { - xs->error = XS_DRIVER_STUFFUP; + XS_SETERR(xs, HBA_BOTCH); } - if (sp->req_state_flags & RQSF_GOT_SENSE) { - bcopy(sp->req_sense_data, &xs->sense, - sizeof (xs->sense)); - xs->error = XS_SENSE; + XS_STS(xs) = sp->req_scsi_status & 0xff; + if (isp->isp_type & ISP_HA_SCSI) { + if (sp->req_state_flags & RQSF_GOT_SENSE) { + MEMCPY(XS_SNSP(xs), sp->req_sense_data, + XS_SNSLEN(xs)); + XS_SNS_IS_VALID(xs); + } + /* + * A new synchronous rate was negotiated for this + * target. Mark state such that we'll go look up + * that which has changed later. + */ + if (sp->req_status_flags & RQSTF_NEGOTIATION) { + sdparam *sdp = isp->isp_param; + isp->isp_update = 1; + sdp->isp_devparam[XS_TGT(xs)].dev_refresh = 1; + } + } else { + if (XS_STS(xs) == SCSI_CHECK) { + XS_SNS_IS_VALID(xs); + MEMCPY(XS_SNSP(xs), sp->req_sense_data, + XS_SNSLEN(xs)); + sp->req_state_flags |= RQSF_GOT_SENSE; + } + } + if (XS_NOERR(xs) && XS_STS(xs) == SCSI_BUSY) { + XS_SETERR(xs, HBA_TGTBSY); } - xs->status = sp->req_scsi_status; - if (xs->error == 0 && xs->status == SCSI_BUSY) - xs->error = XS_BUSY; if (sp->req_header.rqs_entry_type == RQSTYPE_RESPONSE) { - if (xs->error == 0) - xs->error = isp_parse_status(isp, sp); + if (XS_NOERR(xs)) { + if (sp->req_completion_status != RQCS_COMPLETE) { + isp_parse_status(isp, sp, xs); + } else { + XS_SETERR(xs, HBA_NOERROR); + } + } } else { - printf("%s: unknown return %x\n", isp->isp_name, + PRINTF("%s: unknown return %x\n", isp->isp_name, sp->req_header.rqs_entry_type); - if (xs->error == 0) - xs->error = XS_DRIVER_STUFFUP; + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } } - xs->resid = sp->req_resid; - xs->flags |= ITSDONE; - if (xs->datalen) { - ISP_DMAFREE(isp, xs, sp->req_handle); + if (isp->isp_type & ISP_HA_SCSI) { + XS_RESID(xs) = sp->req_resid; + } else if (sp->req_scsi_status & RQCS_RU) { + XS_RESID(xs) = sp->req_resid; + IDPRINTF(4, ("%s: cnt %d rsd %d\n", isp->isp_name, + XS_XFRLEN(xs), sp->req_resid)); } -#if 0 - printf("%s(%d.%d): FINISH%d cmd 0x%x resid %d STS %x", - isp->isp_name, xs->sc_link->target, xs->sc_link->lun, - sp->req_header.rqs_seqno, *(u_char *) xs->cmd, - xs->resid, xs->status); - if (sp->req_state_flags & RQSF_GOT_SENSE) { - printf(" Skey: %x", xs->sense.flags); - if (xs->error != XS_SENSE) { - printf(" BUT NOT SET"); + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, sp->req_handle - 1); + } + /* + * XXX: If we have a check condition, but no Sense Data, + * XXX: mark it as an error (ARQ failed). We need to + * XXX: to do a more distinct job because there may + * XXX: cases where ARQ is disabled. + */ + if (XS_STS(xs) == SCSI_CHECK && !(XS_IS_SNS_VALID(xs))) { + if (XS_NOERR(xs)) { + PRINTF("%s: ARQ failure for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + XS_SETERR(xs, HBA_ARQFAIL); } } - printf(" xs->error %d\n", xs->error); -#endif + if ((isp->isp_dblev >= 5) || + (isp->isp_dblev > 2 && !XS_NOERR(xs))) { + PRINTF("%s(%d.%d): FIN%d dl%d resid%d STS %x", + isp->isp_name, XS_TGT(xs), XS_LUN(xs), + sp->req_header.rqs_seqno, XS_XFRLEN(xs), + XS_RESID(xs), XS_STS(xs)); + if (sp->req_state_flags & RQSF_GOT_SENSE) { + PRINTF(" Skey: %x", XS_SNSKEY(xs)); + if (!(XS_IS_SNS_VALID(xs))) { + PRINTF(" BUT NOT SET"); + } + } + PRINTF(" XS_ERR=0x%x\n", (unsigned int) XS_ERR(xs)); + } + + if (isp->isp_nactive > 0) + isp->isp_nactive--; + complist[ndone++] = xs; /* defer completion call until later */ + } + + /* + * If we looked at any commands, then it's valid to find out + * what the outpointer is. It also is a trigger to update the + * ISP's notion of what we've seen so far. + */ + if (nlooked) { ISP_WRITE(isp, INMAILBOX5, optr); - scsi_done(xs); + isp->isp_reqodx = ISP_READ(isp, OUTMAILBOX4); } isp->isp_residx = optr; + for (i = 0; i < ndone; i++) { + xs = complist[i]; + if (xs) { + XS_CMD_DONE(xs); + } + } return (1); } @@ -808,153 +1691,1284 @@ isp_intr(arg) */ static int -isp_parse_status(isp, sp) +isp_parse_async(isp, mbox) + struct ispsoftc *isp; + int mbox; +{ + u_int32_t fast_post_handle = 0; + + switch (mbox) { + case MBOX_COMMAND_COMPLETE: /* sometimes these show up */ + break; + case ASYNC_BUS_RESET: + isp_async(isp, ISPASYNC_BUS_RESET, NULL); + isp->isp_sendmarker = 1; +#ifdef ISP_TARGET_MODE + isp_notify_ack(isp, NULL); +#endif + break; + + case ASYNC_SYSTEM_ERROR: + mbox = ISP_READ(isp, OUTMAILBOX1); + PRINTF("%s: Internal FW Error @ RISC Addr 0x%x\n", + isp->isp_name, mbox); + isp_restart(isp); + /* no point continuing after this */ + return (-1); + + case ASYNC_RQS_XFER_ERR: + PRINTF("%s: Request Queue Transfer Error\n", isp->isp_name); + break; + + case ASYNC_RSP_XFER_ERR: + PRINTF("%s: Response Queue Transfer Error\n", isp->isp_name); + break; + + case ASYNC_QWAKEUP: + /* don't need to be chatty */ + mbox = ISP_READ(isp, OUTMAILBOX4); + break; + + case ASYNC_TIMEOUT_RESET: + PRINTF("%s: timeout initiated SCSI bus reset\n", isp->isp_name); + isp->isp_sendmarker = 1; +#ifdef ISP_TARGET_MODE + isp_notify_ack(isp, NULL); +#endif + break; + + case ASYNC_DEVICE_RESET: + isp->isp_sendmarker = 1; + PRINTF("%s: device reset\n", isp->isp_name); +#ifdef ISP_TARGET_MODE + isp_notify_ack(isp, NULL); +#endif + break; + + case ASYNC_EXTMSG_UNDERRUN: + PRINTF("%s: extended message underrun\n", isp->isp_name); + break; + + case ASYNC_SCAM_INT: + PRINTF("%s: SCAM interrupt\n", isp->isp_name); + break; + + case ASYNC_HUNG_SCSI: + PRINTF("%s: stalled SCSI Bus after DATA Overrun\n", + isp->isp_name); + /* XXX: Need to issue SCSI reset at this point */ + break; + + case ASYNC_KILLED_BUS: + PRINTF("%s: SCSI Bus reset after DATA Overrun\n", + isp->isp_name); + break; + + case ASYNC_BUS_TRANSIT: + PRINTF("%s: LBD->HVD Transition 0x%x\n", + isp->isp_name, ISP_READ(isp, OUTMAILBOX1)); + break; + + case ASYNC_CMD_CMPLT: + fast_post_handle = (ISP_READ(isp, OUTMAILBOX2) << 16) | + ISP_READ(isp, OUTMAILBOX1); + IDPRINTF(3, ("%s: fast post completion of %u\n", isp->isp_name, + fast_post_handle)); + break; + + case ASYNC_CTIO_DONE: + /* Should only occur when Fast Posting Set for 2100s */ + PRINTF("%s: CTIO done\n", isp->isp_name); + break; + + case ASYNC_LIP_OCCURRED: + isp->isp_sendmarker = 1; + PRINTF("%s: LIP occurred\n", isp->isp_name); + break; + + case ASYNC_LOOP_UP: + isp->isp_sendmarker = 1; + isp_async(isp, ISPASYNC_LOOP_UP, NULL); + break; + + case ASYNC_LOOP_DOWN: + isp_async(isp, ISPASYNC_LOOP_DOWN, NULL); + break; + + case ASYNC_LOOP_RESET: + isp->isp_sendmarker = 1; + PRINTF("%s: Loop RESET\n", isp->isp_name); +#ifdef ISP_TARGET_MODE + isp_notify_ack(isp, NULL); +#endif + break; + + case ASYNC_PDB_CHANGED: + isp->isp_sendmarker = 1; + isp_mark_getpdb_all(isp); + PRINTF("%s: Port Database Changed\n", isp->isp_name); + break; + + case ASYNC_CHANGE_NOTIFY: + break; + + default: + PRINTF("%s: unknown async code 0x%x\n", isp->isp_name, mbox); + break; + } + return (fast_post_handle); +} + +static int +isp_handle_other_response(isp, sp, optrp) + struct ispsoftc *isp; + ispstatusreq_t *sp; + u_int8_t *optrp; +{ + u_int8_t iptr, optr; + int reqsize = 0; + void *ireqp = NULL; +#ifdef ISP_TARGET_MODE + union { + at_entry_t *atio; + at2_entry_t *at2io; + ct_entry_t *ctio; + ct2_entry_t *ct2io; + lun_entry_t *lunen; + in_entry_t *inot; + in_fcentry_t *inot_fc; + na_entry_t *nack; + na_fcentry_t *nack_fc; + void *voidp; +#define atio un.atio +#define at2io un.at2io +#define ctio un.ctio +#define ct2io un.ct2io +#define lunen un.lunen +#define inot un.inot +#define inot_fc un.inot_fc +#define nack un.nack +#define nack_fc un.nack_fc + } un; + + un.voidp = sp; +#endif + + + switch (sp->req_header.rqs_entry_type) { + case RQSTYPE_REQUEST: + return (-1); +#ifdef ISP_TARGET_MODE + case RQSTYPE_NOTIFY_ACK: + { + static const char *f = + "%s: Notify Ack Status 0x%x Sequence Id 0x%x\n" + /* + * The ISP is acknowleding our ack of an Immediate Notify. + */ + if (isp->isp_type & ISP_HA_FC) { + PRINTF(f, isp->isp_name, + nack_fc->na-status, nack_fc->na_seqid); + } else { + PRINTF(f, isp->isp_name, + nack->na_status, nack->na_seqid); + } + break; + } + case RQSTYPE_NOTIFY: + { + u_int16_t seqid, status; + + /* + * Either the ISP received a SCSI message it cannot handle + * or some other out of band condition (e.g., Port Logout) + * or it is returning an Immediate Notify entry we sent. + */ + if (isp->isp_type & ISP_HA_FC) { + status = inot_fc->status; + seqid = inot_fc->in_seqid; + } else { + status = inot->status; + seqid = inot->seqid & 0xff; + } + PRINTF("%s: Immediate Notify Status 0x%x Sequence Id 0x%x\n", + isp->isp_name, status, seqid); + + switch (status) { + case IN_MSG_RECEIVED: + case IN_IDE_RECEIVED: + ptisp_got_msg(ptp, &inot); + break; + case IN_RSRC_UNAVAIL: + PRINTF("%s: Firmware out of ATIOs\n", isp->isp_name); + break; + case IN_ABORT_TASK: + PRINTF("%s: Abort Task iid %d rx_id 0x%x\n", + inot_fc->in_iid, seqid); + break; + case IN_PORT_LOGOUT: + PRINTF("%s: Port Logout for Initiator %d\n", + isp->isp_name, inot_fc->in_iid); + break; + default: + PRINTF("%s: bad status (0x%x) in Immediate Notify\n", + isp->isp_name, status); + break; + + } + isp_notify_ack(isp, un.voidp); + reqsize = 0; + break; + } + case RQSTYPE_ENABLE_LUN: + case RQSTYPE_MODIFY_LUN: + if (lunen->req_status != 1) { + PRINTF("%s: ENABLE/MODIFY LUN returned status 0x%x\n", + isp->isp_name, lunen->req_status); + } + break; + case RQSTYPE_ATIO2: + { + fcparam *fcp = isp->isp_param; + ispctiot2_t local, *ct2 = NULL; + ispatiot2_t *at2 = (ispatiot2_t *) sp; + int s, lun; + +#ifdef ISP2100_SCCLUN + lun = at2->req_scclun; +#else + lun = at2->req_lun; +#endif + PRINTF("%s: atio2 loopid %d for lun %d rxid 0x%x flags0x%x " + "tflags0x%x ecodes0x%x rqstatus0x%x\n", isp->isp_name, + at2->req_initiator, lun, at2->req_rxid, + at2->req_flags, at2->req_taskflags, at2->req_execodes, + at2->req_status); + + switch (at2->req_status & ~ATIO_SENSEVALID) { + case ATIO_PATH_INVALID: + PRINTF("%s: ATIO2 Path Invalid\n", isp->isp_name); + break; + case ATIO_NOCAP: + PRINTF("%s: ATIO2 No Cap\n", isp->isp_name); + break; + case ATIO_BDR_MSG: + PRINTF("%s: ATIO2 BDR Received\n", isp->isp_name); + break; + case ATIO_CDB_RECEIVED: + ct2 = &local; + break; + default: + PRINTF("%s: unknown req_status 0x%x\n", isp->isp_name, + at2->req_status); + break; + } + if (ct2 == NULL) { + /* + * Just do an ACCEPT on this fellow. + */ + at2->req_header.rqs_entry_type = RQSTYPE_ATIO2; + at2->req_header.rqs_flags = 0; + at2->req_flags = 1; + ireqp = at2; + reqsize = sizeof (*at2); + break; + } + PRINTF("%s: datalen %d cdb0=0x%x\n", isp->isp_name, + at2->req_datalen, at2->req_cdb[0]); + MEMZERO((void *) ct2, sizeof (*ct2)); + ct2->req_header.rqs_entry_type = RQSTYPE_CTIO2; + ct2->req_header.rqs_entry_count = 1; + ct2->req_header.rqs_flags = 0; + ct2->req_header.rqs_seqno = isp->isp_seqno++; + ct2->req_handle = (at2->req_initiator << 16) | lun; +#ifndef ISP2100_SCCLUN + ct2->req_lun = lun; +#endif + ct2->req_initiator = at2->req_initiator; + ct2->req_rxid = at2->req_rxid; + + ct2->req_flags = CTIO_SEND_STATUS; + switch (at2->req_cdb[0]) { + case 0x0: /* TUR */ + ct2->req_flags |= CTIO_NODATA | CTIO2_SMODE0; + ct2->req_m.mode0.req_scsi_status = CTIO2_STATUS_VALID; + break; + + case 0x3: /* REQUEST SENSE */ + case 0x12: /* INQUIRE */ + ct2->req_flags |= CTIO_SEND_DATA | CTIO2_SMODE0; + ct2->req_m.mode0.req_scsi_status = CTIO2_STATUS_VALID; + ct2->req_seg_count = 1; + if (at2->req_cdb[0] == 0x12) { + s = sizeof(tgtiqd); + MEMCPY(fcp->isp_scratch, tgtiqd, s); + } else { + s = at2->req_datalen; + MEMZERO(fcp->isp_scratch, s); + } + ct2->req_m.mode0.req_dataseg[0].ds_base = + fcp->isp_scdma; + ct2->req_m.mode0.req_dataseg[0].ds_count = s; + ct2->req_m.mode0.req_datalen = s; +#if 1 + if (at2->req_datalen < s) { + ct2->req_m.mode1.req_scsi_status |= + CTIO2_RESP_VALID|CTIO2_RSPOVERUN; + } else if (at2->req_datalen > s) { + ct2->req_m.mode1.req_scsi_status |= + CTIO2_RESP_VALID|CTIO2_RSPUNDERUN; + } +#endif + break; + + default: /* ALL OTHERS */ + ct2->req_flags |= CTIO_NODATA | CTIO2_SMODE1; + ct2->req_m.mode1.req_scsi_status = 0; +#if 1 + if (at2->req_datalen) { + ct2->req_m.mode1.req_scsi_status |= + CTIO2_RSPUNDERUN; +#if BYTE_ORDER == BIG_ENDIAN + ct2->req_resid[1] = at2->req_datalen & 0xff; + ct2->req_resid[0] = + (at2->req_datalen >> 8) & 0xff; + ct2->req_resid[3] = + (at2->req_datalen >> 16) & 0xff; + ct2->req_resid[2] = + (at2->req_datalen >> 24) & 0xff; +#else + ct2->req_resid[0] = at2->req_datalen & 0xff; + ct2->req_resid[1] = + (at2->req_datalen >> 8) & 0xff; + ct2->req_resid[2] = + (at2->req_datalen >> 16) & 0xff; + ct2->req_resid[3] = + (at2->req_datalen >> 24) & 0xff; +#endif + } +#endif + if ((at2->req_status & ATIO_SENSEVALID) == 0) { + ct2->req_m.mode1.req_sense_len = 18; + ct2->req_m.mode1.req_scsi_status |= 2; + ct2->req_m.mode1.req_response[0] = 0x70; + ct2->req_m.mode1.req_response[2] = 0x2; + } else { + ct2->req_m.mode1.req_sense_len = 18; + ct2->req_m.mode1.req_scsi_status |= + at2->req_scsi_status; + MEMCPY(ct2->req_m.mode1.req_response, + at2->req_sense, sizeof (at2->req_sense)); + } + break; + } + reqsize = sizeof (*ct2); + ireqp = ct2; + break; + } + case RQSTYPE_CTIO2: + { + ispatiot2_t *at2; + ispctiot2_t *ct2 = (ispctiot2_t *) sp; + PRINTF("%s: CTIO2 returned status 0x%x\n", isp->isp_name, + ct2->req_status); + /* + * Return the ATIO to the board. + */ + at2 = (ispatiot2_t *) sp; + at2->req_header.rqs_entry_type = RQSTYPE_ATIO2; + at2->req_header.rqs_entry_count = 1; + at2->req_header.rqs_flags = 0; + at2->req_header.rqs_seqno = isp->isp_seqno++; + at2->req_status = 1; + reqsize = sizeof (*at2); + ireqp = at2; + break; + } +#undef atio +#undef at2io +#undef ctio +#undef ct2io +#undef lunen +#undef inot +#undef inot_fc +#undef nack +#undef nack_fc +#endif + default: + PRINTF("%s: other response type %x\n", isp->isp_name, + sp->req_header.rqs_entry_type); + break; + } + if (reqsize) { + void *reqp; + optr = isp->isp_reqodx = ISP_READ(isp, OUTMAILBOX4); + iptr = isp->isp_reqidx; + reqp = (void *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN); + if (iptr == optr) { + PRINTF("%s: Request Queue Overflow other response\n", + isp->isp_name); + } else { + MEMCPY(reqp, ireqp, reqsize); + ISP_WRITE(isp, INMAILBOX4, iptr); + isp->isp_reqidx = iptr; + } + } + return (0); +} + +#ifdef ISP_TARGET_MODE + +static void isp_tmd_newcmd_dflt __P((void *, tmd_cmd_t *)); +static void isp_tmd_event_dflt __P((void *, int)); +static void isp_tmd_notify_dflt __P((void *, tmd_notify_t *)); + +static void isp_tgt_data_xfer __P ((tmd_cmd_t *)); +static void isp_tgt_endcmd __P ((tmd_cmd_t *, u_int8_t)); +static void isp_tgt_done __P ((tmd_cmd_t *)); + +static void +isp_tmd_newcmd_dflt(arg0, cmdp) + void *arg0; + tmd_cmd_t *cmdp; +{ +} + +static void +isp_tmd_event_dflt(arg0, event) + void *arg0; + int event; +{ +} + +static void +isp_tmd_notify_dflt(arg0, npt) + void *arg0; + tmd_notify_t *npt; +{ +} + +/* + * Locks held, and ints disabled (if FC). + * + * XXX: SETUP ONLY FOR INITIAL ENABLING RIGHT NOW + */ +static int +isp_modify_lun(isp, lun, icnt, ccnt) + struct ispsoftc *isp; + int lun; /* logical unit to enable, modify, or disable */ + int icnt; /* immediate notify count */ + int ccnt; /* command count */ +{ + isplun_t *ip = NULL; + u_int8_t iptr, optr; + + optr = isp->isp_reqodx = ISP_READ(isp, OUTMAILBOX4); + iptr = isp->isp_reqidx; + ip = (isplun_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN); + if (iptr == optr) { + PRINTF("%s: Request Queue Overflow in isp_modify_lun\n", + isp->isp_name); + return (-1); + } + + MEMZERO((void *) ip, sizeof (*ip)); + ip->req_header.rqs_entry_type = RQSTYPE_ENABLE_LUN; + ip->req_header.rqs_entry_count = 1; + ip->req_header.rqs_seqno = isp->isp_seqno++; + ip->req_handle = RQSTYPE_ENABLE_LUN; + if (isp->isp_type & ISP_HA_SCSI) { + ip->req_lun = lun; + } + ip->req_cmdcount = ccnt; + ip->req_imcount = icnt; + ip->req_timeout = 0; /* default 30 seconds */ + ISP_WRITE(isp, INMAILBOX4, iptr); + isp->isp_reqidx = iptr; + return (0); +} + +static void +isp_notify_ack(isp, ptrp) + struct ispsoftc *isp; + void *ptrp; +{ + void *reqp; + u_int8_t iptr, optr; + union { + na_fcentry_t _naf; + na_entry_t _nas; + } un; + + MEMZERO((caddr_t)&un, sizeof (un)); + un._nas.na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK; + un._nas.na_header.rqs_entry_count = 1; + + if (isp->isp_type & ISP_HA_FC) { + na_fcentry_t *na = &un._nas; + if (ptrp) { + in_fcentry_t *inp = ptrp; + na->na_iid = inp->in_iid; + na->na_lun = inp->in_lun; + na->na_task_flags = inp->in_task_flags; + na->na_seqid = inp->in_seqid; + na->na_status = inp->in_status; + } else { + na->na_flags = NAFC_RST_CLRD; + } + } else { + na_entry_t *na = &un._nas; + if (ptrp) { + in_entry_t *inp = ptrp; + na->na_iid = inp->in_iid; + na->na_lun = inp->in_lun; + na->na_tgt = inp->in_tgt; + na->na_seqid = inp->in_seqid; + } else { + na->na_flags = NA_RST_CLRD; + } + } + optr = isp->isp_reqodx = ISP_READ(isp, OUTMAILBOX4); + iptr = isp->isp_reqidx; + reqp = (void *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN); + if (iptr == optr) { + PRINTF("%s: Request Queue Overflow For isp_notify_ack\n", + isp->isp_name); + } else { + MEMCPY(reqp, ireqp, sizeof (un)); + ISP_WRITE(isp, INMAILBOX4, iptr); + isp->isp_reqidx = iptr; + } +} + +/* + * These are dummy stubs for now until the outside framework is plugged in. + */ + +static void +isp_handle_atio (isp, aep) + struct ispsoftc *isp; + at_entry_t *aep; +{ + int status, connected; + tmd_cmd_t local, *cdp = &local; + + /* + * Get the ATIO status and see if we're still connected. + */ + status = aep->at_status; + connected = ((aep->at_flags & AT_NODISC) != 0); + + PRINTF("%s: ATIO status=0x%x, connected=%d\n", isp->isp_name, + status, connected); + + /* + * The firmware status (except for the SenseValid bit) indicates + * why this ATIO was sent to us. + * If SenseValid is set, the firware has recommended Sense Data. + * If the Disconnects Disabled bit is set in the flags field, + * we're still connected on the SCSI bus - i.e. the initiator + * did not set DiscPriv in the identify message. We don't care + * about this so it's ignored. + */ + switch(status & ~TGTSVALID) { + case AT_PATH_INVALID: + /* + * ATIO rejected by the firmware due to disabled lun. + */ + PRINTF("%s: Firmware rejected ATIO for disabled lun %d\n", + isp->isp_name, aep->at_lun); + break; + + case AT_PHASE_ERROR: + /* + * Bus Pase Sequence error. + * + * The firmware should have filled in the correct + * sense data. + */ + + + if (status & TGTSVALID) { + MEMCPY(&cdp->cd_sensedata, aep->at_sense, + sizeof (cdp->cd_sensedata)); + PRINTF("%s: Bus Phase Sequence error key 0x%x\n", + isp->isp_name, cdp->cd_sensedata[2] & 0xf); + } else { + PRINTF("%s: Bus Phase Sequence With No Sense\n", + isp->isp_name); + } + (*isp->isp_tmd_newcmd)(isp, cdp); + break; + + case AT_NOCAP: + /* + * Requested Capability not available + * We sent an ATIO that overflowed the firmware's + * command resource count. + */ + PRINTF("%s: Firmware rejected ATIO, command count overflow\n", + isp->isp_name); + break; + + case AT_BDR_MSG: + /* + * If we send an ATIO to the firmware to increment + * its command resource count, and the firmware is + * recovering from a Bus Device Reset, it returns + * the ATIO with this status. + */ + PRINTF("%s: ATIO returned with BDR received\n", isp->isp_name); + break; + + case AT_CDB: + /* + * New CDB + */ + cdp->cd_hba = isp; + cdp->cd_iid = aep->at_iid; + cdp->cd_tgt = aep->at_tgt; + cdp->cd_lun = aep->at_lun; + cdp->cd_tagtype = aep->at_tag_type; + cdp->cd_tagval = aep->at_tag_val; + MEMCPY(cdp->cd_cdb, aep->at_cdb, 16); + PRINTF("%s: CDB 0x%x itl %d/%d/%d\n", isp->isp_name, + cdp->cd_cdb[0], cdp->cd_iid, cdp->cd_tgt, cdp->cd_lun); + (*isp->isp_tmd_newcmd)(isp, cdp); + break; + + default: + PRINTF("%s: Unknown status (0x%x) in ATIO\n", + isp->isp_name, status); + cdp->cd_hba = isp; + cdp->cd_iid = aep->at_iid; + cdp->cd_tgt = aep->at_tgt; + cdp->cd_lun = aep->at_lun; + cdp->cd_tagtype = aep->at_tag_type; + cdp->cd_tagval = aep->at_tag_val; + isp_tgtcmd_done(cdp); + break; + } +} + +static void +isp_handle_atio2(isp, aep) + struct ispsoftc *isp; + at2_entry_t *aep; +{ + int status; + tmd_cmd_t local, *cdp = &local; + + /* + * Get the ATIO2 status. + */ + status = aep->at_status; + PRINTD("%s: ATIO2 status=0x%x\n", status); + + /* + * The firmware status (except for the SenseValid bit) indicates + * why this ATIO was sent to us. + * If SenseValid is set, the firware has recommended Sense Data. + */ + switch(status & ~TGTSVALID) { + case AT_PATH_INVALID: + /* + * ATIO rejected by the firmware due to disabled lun. + */ + PRINTF("%s: Firmware rejected ATIO2 for disabled lun %d\n", + isp->isp_name, aep->at_lun); + break; + + case AT_NOCAP: + /* + * Requested Capability not available + * We sent an ATIO that overflowed the firmware's + * command resource count. + */ + PRINTF("%s: Firmware rejected ATIO2, command count overflow\n", + isp->isp_name); + break; + + case AT_BDR_MSG: + /* + * If we send an ATIO to the firmware to increment + * its command resource count, and the firmware is + * recovering from a Bus Device Reset, it returns + * the ATIO with this status. + */ + PRINTF("%s: ATIO2 returned with BDR rcvd\n", isp->isp_name); + break; + + case AT_CDB: + /* + * New CDB + */ + cdp->cd_hba = isp; + cdp->cd_iid = aep->at_iid; + cdp->cd_tgt = 0; + cdp->cd_lun = aep->at_lun; + MEMCPY(cdp->cd_cdb, aep->at_cdb, 16); + cdp->cd_rxid = aep->at_rxid; + cdp->cp_origdlen = aep->at_datalen; + cdp->cp_totbytes = 0; + PRINTF("%s: CDB 0x%x rx_id 0x%x itl %d/%d/%d dlen %d\n", + isp->isp_name, cdp->cd_cdb[0], cdp->cd_tagval, cdp->cd_iid, + cdp->cd_tgt, cdp->cd_lun, aep->at_datalen); + (*isp->isp_tmd_newcmd)(isp, cdp); + break; + + default: + PRINTF("%s: Unknown status (0x%x) in ATIO2\n", + isp->isp_name, status); + cdp->cd_hba = isp; + cdp->cd_iid = aep->at_iid; + cdp->cd_tgt = aep->at_tgt; + cdp->cd_lun = aep->at_lun; + cdp->cp_rxid = aep->at_rxid; + isp_tgtcmd_done(cdp); + break; + } +} + +static void +isp_handle_ctio(isp, cep) + struct ispsoftc *isp; + ct_entry_t *aep; +{ +} + +static void +isp_handle_ctio2(isp, cep) + struct ispsoftc *isp; + at2_entry_t *aep; +{ +} +#endif + +static void +isp_parse_status(isp, sp, xs) struct ispsoftc *isp; ispstatusreq_t *sp; + ISP_SCSI_XFER_T *xs; { switch (sp->req_completion_status) { case RQCS_COMPLETE: - return (XS_NOERROR); - break; + XS_SETERR(xs, HBA_NOERROR); + return; + case RQCS_INCOMPLETE: if ((sp->req_state_flags & RQSF_GOT_TARGET) == 0) { - return (XS_SELTIMEOUT); + IDPRINTF(3, ("%s: Selection Timeout for target %d\n", + isp->isp_name, XS_TGT(xs))); + XS_SETERR(xs, HBA_SELTIMEOUT); + return; } - printf("%s: incomplete, state %x\n", - isp->isp_name, sp->req_state_flags); + PRINTF("%s: command incomplete for target %d lun %d, state " + "0x%x\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs), + sp->req_state_flags); break; - case RQCS_DATA_UNDERRUN: - return (XS_NOERROR); - case RQCS_TIMEOUT: - return (XS_TIMEOUT); + + case RQCS_DMA_ERROR: + PRINTF("%s: DMA error for command on target %d, lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_TRANSPORT_ERROR: + PRINTF("%s: transport error\n", isp->isp_name); + isp_prtstst(sp); + break; + case RQCS_RESET_OCCURRED: - printf("%s: reset occurred\n", isp->isp_name); + IDPRINTF(2, ("%s: bus reset destroyed command for target %d " + "lun %d\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs))); isp->isp_sendmarker = 1; - break; + XS_SETERR(xs, HBA_BUSRESET); + return; + case RQCS_ABORTED: - printf("%s: command aborted\n", isp->isp_name); + PRINTF("%s: command aborted for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); isp->isp_sendmarker = 1; + XS_SETERR(xs, HBA_ABORTED); + return; + + case RQCS_TIMEOUT: + IDPRINTF(2, ("%s: command timed out for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs))); + XS_SETERR(xs, HBA_CMDTIMEOUT); + return; + + case RQCS_DATA_OVERRUN: + if (isp->isp_type & ISP_HA_FC) { + XS_RESID(xs) = sp->req_resid; + break; + } + XS_SETERR(xs, HBA_DATAOVR); + return; + + case RQCS_COMMAND_OVERRUN: + PRINTF("%s: command overrun for command on target %d, lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_STATUS_OVERRUN: + PRINTF("%s: status overrun for command on target %d, lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_BAD_MESSAGE: + PRINTF("%s: message not COMMAND COMPLETE after status on " + "target %d, lun %d\n", isp->isp_name, XS_TGT(xs), + XS_LUN(xs)); + break; + + case RQCS_NO_MESSAGE_OUT: + PRINTF("%s: No MESSAGE OUT phase after selection on " + "target %d, lun %d\n", isp->isp_name, XS_TGT(xs), + XS_LUN(xs)); + break; + + case RQCS_EXT_ID_FAILED: + PRINTF("%s: EXTENDED IDENTIFY failed on target %d, lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_IDE_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected INITIATOR DETECTED " + "ERROR message\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ABORT_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected ABORT message\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_REJECT_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected MESSAGE REJECT message\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_NOP_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected NOP message\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_PARITY_ERROR_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected MESSAGE PARITY ERROR " + "message\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_DEVICE_RESET_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected BUS DEVICE RESET " + "message\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ID_MSG_FAILED: + PRINTF("%s: target %d lun %d rejected IDENTIFY " + "message\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_UNEXP_BUS_FREE: + PRINTF("%s: target %d lun %d had an unexpected bus free\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_DATA_UNDERRUN: + if (isp->isp_type & ISP_HA_FC) { + XS_RESID(xs) = sp->req_resid; + /* an UNDERRUN is not a botch ??? */ + } + XS_SETERR(xs, HBA_NOERROR); + return; + + case RQCS_XACT_ERR1: + PRINTF("%s: HBA attempted queued transaction with disconnect " + "not set for target %d lun %d\n", isp->isp_name, XS_TGT(xs), + XS_LUN(xs)); + break; + + case RQCS_XACT_ERR2: + PRINTF("%s: HBA attempted queued transaction to target " + "routine %d on target %d\n", isp->isp_name, XS_LUN(xs), + XS_TGT(xs)); + break; + + case RQCS_XACT_ERR3: + PRINTF("%s: HBA attempted queued transaction for target %d lun " + "%d when queueing disabled\n", isp->isp_name, XS_TGT(xs), + XS_LUN(xs)); + break; + + case RQCS_BAD_ENTRY: + PRINTF("%s: invalid IOCB entry type detected\n", isp->isp_name); + break; + + case RQCS_QUEUE_FULL: + PRINTF("%s: internal queues full for target %d lun %d " + "status 0x%x\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs), + XS_STS(xs)); + /* + * If QFULL or some other status byte is set, then this + * isn't an error, per se. + */ + if (XS_STS(xs) != 0) { + XS_SETERR(xs, HBA_NOERROR); + return; + } + break; + + case RQCS_PHASE_SKIPPED: + PRINTF("%s: SCSI phase skipped (e.g., COMMAND COMPLETE w/o " + "STATUS phase) for target %d lun %d\n", isp->isp_name, + XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ARQS_FAILED: + PRINTF("%s: Auto Request Sense failed for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + XS_SETERR(xs, HBA_ARQFAIL); + return; + + case RQCS_WIDE_FAILED: + PRINTF("%s: Wide Negotiation failed for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + if (isp->isp_type & ISP_HA_SCSI) { + sdparam *sdp = isp->isp_param; + isp->isp_update = 1; + sdp->isp_devparam[XS_TGT(xs)].dev_flags &= ~DPARM_WIDE; + sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; + } + XS_SETERR(xs, HBA_NOERROR); + return; + + case RQCS_SYNCXFER_FAILED: + PRINTF("%s: SDTR Message failed for target %d lun %d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + if (isp->isp_type & ISP_HA_SCSI) { + sdparam *sdp = isp->isp_param; + isp->isp_update = 1; + sdp->isp_devparam[XS_TGT(xs)].dev_flags &= ~DPARM_SYNC; + sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; + } + break; + + case RQCS_LVD_BUSERR: + PRINTF("%s: Bad LVD Bus condition while talking to target %d " + "lun %d\n", isp->isp_name, XS_TGT(xs), XS_LUN(xs)); break; + + case RQCS_PORT_UNAVAILABLE: + /* + * No such port on the loop. Moral equivalent of SELTIMEO + */ + IDPRINTF(3, ("%s: Port Unavailable for target %d\n", + isp->isp_name, XS_TGT(xs))); + XS_SETERR(xs, HBA_SELTIMEOUT); + return; + + case RQCS_PORT_LOGGED_OUT: + /* + * It was there (maybe)- treat as a selection timeout. + */ + PRINTF("%s: port logout for target %d\n", + isp->isp_name, XS_TGT(xs)); + XS_SETERR(xs, HBA_SELTIMEOUT); + return; + + case RQCS_PORT_CHANGED: + PRINTF("%s: port changed for target %d\n", + isp->isp_name, XS_TGT(xs)); + break; + + case RQCS_PORT_BUSY: + PRINTF("%s: port busy for target %d\n", + isp->isp_name, XS_TGT(xs)); + XS_SETERR(xs, HBA_TGTBSY); + return; + default: - printf("%s: comp status %x\n", isp->isp_name, + PRINTF("%s: comp status %x\n", isp->isp_name, sp->req_completion_status); break; } - return (XS_DRIVER_STUFFUP); + XS_SETERR(xs, HBA_BOTCH); +} + +static void +isp_fastpost_complete(isp, fph) + struct ispsoftc *isp; + int fph; +{ + ISP_SCSI_XFER_T *xs; + + if (fph < 1) + return; + xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[fph - 1]; + isp->isp_xflist[fph - 1] = NULL; + if (xs == NULL) { + PRINTF("%s: fast posting handle 0x%x not found\n", + isp->isp_name, fph - 1); + return; + } + /* + * Since we don't have a result queue entry item, + * we must believe that SCSI status is zero and + * that all data transferred. + */ + XS_RESID(xs) = 0; + XS_STS(xs) = 0; + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, fph - 1); + } + XS_CMD_DONE(xs); } #define HINIB(x) ((x) >> 0x4) #define LONIB(x) ((x) & 0xf) #define MAKNIB(a, b) (((a) << 4) | (b)) static u_int8_t mbpcnt[] = { - MAKNIB(1, 1), /* MBOX_NO_OP */ - MAKNIB(5, 5), /* MBOX_LOAD_RAM */ - MAKNIB(2, 0), /* MBOX_EXEC_FIRMWARE */ - MAKNIB(5, 5), /* MBOX_DUMP_RAM */ - MAKNIB(3, 3), /* MBOX_WRITE_RAM_WORD */ - MAKNIB(2, 3), /* MBOX_READ_RAM_WORD */ - MAKNIB(6, 6), /* MBOX_MAILBOX_REG_TEST */ - MAKNIB(2, 3), /* MBOX_VERIFY_CHECKSUM */ - MAKNIB(1, 3), /* MBOX_ABOUT_FIRMWARE */ - MAKNIB(0, 0), /* 0x0009 */ - MAKNIB(0, 0), /* 0x000a */ - MAKNIB(0, 0), /* 0x000b */ - MAKNIB(0, 0), /* 0x000c */ - MAKNIB(0, 0), /* 0x000d */ - MAKNIB(1, 2), /* MBOX_CHECK_FIRMWARE */ - MAKNIB(0, 0), /* 0x000f */ - MAKNIB(5, 5), /* MBOX_INIT_REQ_QUEUE */ - MAKNIB(6, 6), /* MBOX_INIT_RES_QUEUE */ - MAKNIB(4, 4), /* MBOX_EXECUTE_IOCB */ - MAKNIB(2, 2), /* MBOX_WAKE_UP */ - MAKNIB(1, 6), /* MBOX_STOP_FIRMWARE */ - MAKNIB(4, 4), /* MBOX_ABORT */ - MAKNIB(2, 2), /* MBOX_ABORT_DEVICE */ - MAKNIB(3, 3), /* MBOX_ABORT_TARGET */ - MAKNIB(2, 2), /* MBOX_BUS_RESET */ - MAKNIB(2, 3), /* MBOX_STOP_QUEUE */ - MAKNIB(2, 3), /* MBOX_START_QUEUE */ - MAKNIB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */ - MAKNIB(2, 3), /* MBOX_ABORT_QUEUE */ - MAKNIB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */ - MAKNIB(0, 0), /* 0x001e */ - MAKNIB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */ - MAKNIB(1, 2), /* MBOX_GET_INIT_SCSI_ID */ - MAKNIB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */ - MAKNIB(1, 3), /* MBOX_GET_RETRY_COUNT */ - MAKNIB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */ - MAKNIB(1, 2), /* MBOX_GET_CLOCK_RATE */ - MAKNIB(1, 2), /* MBOX_GET_ACT_NEG_STATE */ - MAKNIB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */ - MAKNIB(1, 3), /* MBOX_GET_PCI_PARAMS */ - MAKNIB(2, 4), /* MBOX_GET_TARGET_PARAMS */ - MAKNIB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */ - MAKNIB(0, 0), /* 0x002a */ - MAKNIB(0, 0), /* 0x002b */ - MAKNIB(0, 0), /* 0x002c */ - MAKNIB(0, 0), /* 0x002d */ - MAKNIB(0, 0), /* 0x002e */ - MAKNIB(0, 0), /* 0x002f */ - MAKNIB(2, 2), /* MBOX_SET_INIT_SCSI_ID */ - MAKNIB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */ - MAKNIB(3, 3), /* MBOX_SET_RETRY_COUNT */ - MAKNIB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */ - MAKNIB(2, 2), /* MBOX_SET_CLOCK_RATE */ - MAKNIB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */ - MAKNIB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */ - MAKNIB(3, 3), /* MBOX_SET_PCI_CONTROL_PARAMS */ - MAKNIB(4, 4), /* MBOX_SET_TARGET_PARAMS */ - MAKNIB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */ - MAKNIB(0, 0), /* 0x003a */ - MAKNIB(0, 0), /* 0x003b */ - MAKNIB(0, 0), /* 0x003c */ - MAKNIB(0, 0), /* 0x003d */ - MAKNIB(0, 0), /* 0x003e */ - MAKNIB(0, 0), /* 0x003f */ - MAKNIB(1, 2), /* MBOX_RETURN_BIOS_BLOCK_ADDR */ - MAKNIB(6, 1), /* MBOX_WRITE_FOUR_RAM_WORDS */ - MAKNIB(2, 3) /* MBOX_EXEC_BIOS_IOCB */ + MAKNIB(1, 1), /* 0x00: MBOX_NO_OP */ + MAKNIB(5, 5), /* 0x01: MBOX_LOAD_RAM */ + MAKNIB(2, 0), /* 0x02: MBOX_EXEC_FIRMWARE */ + MAKNIB(5, 5), /* 0x03: MBOX_DUMP_RAM */ + MAKNIB(3, 3), /* 0x04: MBOX_WRITE_RAM_WORD */ + MAKNIB(2, 3), /* 0x05: MBOX_READ_RAM_WORD */ + MAKNIB(6, 6), /* 0x06: MBOX_MAILBOX_REG_TEST */ + MAKNIB(2, 3), /* 0x07: MBOX_VERIFY_CHECKSUM */ + MAKNIB(1, 3), /* 0x08: MBOX_ABOUT_FIRMWARE */ + MAKNIB(0, 0), /* 0x09: */ + MAKNIB(0, 0), /* 0x0a: */ + MAKNIB(0, 0), /* 0x0b: */ + MAKNIB(0, 0), /* 0x0c: */ + MAKNIB(0, 0), /* 0x0d: */ + MAKNIB(1, 2), /* 0x0e: MBOX_CHECK_FIRMWARE */ + MAKNIB(0, 0), /* 0x0f: */ + MAKNIB(5, 5), /* 0x10: MBOX_INIT_REQ_QUEUE */ + MAKNIB(6, 6), /* 0x11: MBOX_INIT_RES_QUEUE */ + MAKNIB(4, 4), /* 0x12: MBOX_EXECUTE_IOCB */ + MAKNIB(2, 2), /* 0x13: MBOX_WAKE_UP */ + MAKNIB(1, 6), /* 0x14: MBOX_STOP_FIRMWARE */ + MAKNIB(4, 4), /* 0x15: MBOX_ABORT */ + MAKNIB(2, 2), /* 0x16: MBOX_ABORT_DEVICE */ + MAKNIB(3, 3), /* 0x17: MBOX_ABORT_TARGET */ + MAKNIB(2, 2), /* 0x18: MBOX_BUS_RESET */ + MAKNIB(2, 3), /* 0x19: MBOX_STOP_QUEUE */ + MAKNIB(2, 3), /* 0x1a: MBOX_START_QUEUE */ + MAKNIB(2, 3), /* 0x1b: MBOX_SINGLE_STEP_QUEUE */ + MAKNIB(2, 3), /* 0x1c: MBOX_ABORT_QUEUE */ + MAKNIB(2, 4), /* 0x1d: MBOX_GET_DEV_QUEUE_STATUS */ + MAKNIB(0, 0), /* 0x1e: */ + MAKNIB(1, 3), /* 0x1f: MBOX_GET_FIRMWARE_STATUS */ + MAKNIB(1, 3), /* 0x20: MBOX_GET_INIT_SCSI_ID, MBOX_GET_LOOP_ID */ + MAKNIB(1, 2), /* 0x21: MBOX_GET_SELECT_TIMEOUT */ + MAKNIB(1, 3), /* 0x22: MBOX_GET_RETRY_COUNT */ + MAKNIB(1, 2), /* 0x23: MBOX_GET_TAG_AGE_LIMIT */ + MAKNIB(1, 2), /* 0x24: MBOX_GET_CLOCK_RATE */ + MAKNIB(1, 2), /* 0x25: MBOX_GET_ACT_NEG_STATE */ + MAKNIB(1, 2), /* 0x26: MBOX_GET_ASYNC_DATA_SETUP_TIME */ + MAKNIB(1, 3), /* 0x27: MBOX_GET_PCI_PARAMS */ + MAKNIB(2, 4), /* 0x28: MBOX_GET_TARGET_PARAMS */ + MAKNIB(2, 4), /* 0x29: MBOX_GET_DEV_QUEUE_PARAMS */ + MAKNIB(0, 0), /* 0x2a: */ + MAKNIB(0, 0), /* 0x2b: */ + MAKNIB(0, 0), /* 0x2c: */ + MAKNIB(0, 0), /* 0x2d: */ + MAKNIB(0, 0), /* 0x2e: */ + MAKNIB(0, 0), /* 0x2f: */ + MAKNIB(2, 2), /* 0x30: MBOX_SET_INIT_SCSI_ID */ + MAKNIB(2, 2), /* 0x31: MBOX_SET_SELECT_TIMEOUT */ + MAKNIB(3, 3), /* 0x32: MBOX_SET_RETRY_COUNT */ + MAKNIB(2, 2), /* 0x33: MBOX_SET_TAG_AGE_LIMIT */ + MAKNIB(2, 2), /* 0x34: MBOX_SET_CLOCK_RATE */ + MAKNIB(2, 2), /* 0x35: MBOX_SET_ACTIVE_NEG_STATE */ + MAKNIB(2, 2), /* 0x36: MBOX_SET_ASYNC_DATA_SETUP_TIME */ + MAKNIB(3, 3), /* 0x37: MBOX_SET_PCI_CONTROL_PARAMS */ + MAKNIB(4, 4), /* 0x38: MBOX_SET_TARGET_PARAMS */ + MAKNIB(4, 4), /* 0x39: MBOX_SET_DEV_QUEUE_PARAMS */ + MAKNIB(0, 0), /* 0x3a: */ + MAKNIB(0, 0), /* 0x3b: */ + MAKNIB(0, 0), /* 0x3c: */ + MAKNIB(0, 0), /* 0x3d: */ + MAKNIB(0, 0), /* 0x3e: */ + MAKNIB(0, 0), /* 0x3f: */ + MAKNIB(1, 2), /* 0x40: MBOX_RETURN_BIOS_BLOCK_ADDR */ + MAKNIB(6, 1), /* 0x41: MBOX_WRITE_FOUR_RAM_WORDS */ + MAKNIB(2, 3), /* 0x42: MBOX_EXEC_BIOS_IOCB */ + MAKNIB(0, 0), /* 0x43: */ + MAKNIB(0, 0), /* 0x44: */ + MAKNIB(0, 0), /* 0x45: */ + MAKNIB(0, 0), /* 0x46: */ + MAKNIB(0, 0), /* 0x47: */ + MAKNIB(0, 0), /* 0x48: */ + MAKNIB(0, 0), /* 0x49: */ + MAKNIB(2, 1), /* 0x4a: MBOX_SET_FIRMWARE_FEATURES */ + MAKNIB(1, 2), /* 0x4b: MBOX_GET_FIRMWARE_FEATURES */ + MAKNIB(0, 0), /* 0x4c: */ + MAKNIB(0, 0), /* 0x4d: */ + MAKNIB(0, 0), /* 0x4e: */ + MAKNIB(0, 0), /* 0x4f: */ + MAKNIB(0, 0), /* 0x50: */ + MAKNIB(0, 0), /* 0x51: */ + MAKNIB(0, 0), /* 0x52: */ + MAKNIB(0, 0), /* 0x53: */ + MAKNIB(8, 0), /* 0x54: MBOX_EXEC_COMMAND_IOCB_A64 */ + MAKNIB(0, 0), /* 0x55: */ + MAKNIB(0, 0), /* 0x56: */ + MAKNIB(0, 0), /* 0x57: */ + MAKNIB(0, 0), /* 0x58: */ + MAKNIB(0, 0), /* 0x59: */ + MAKNIB(0, 0), /* 0x5a: */ + MAKNIB(0, 0), /* 0x5b: */ + MAKNIB(0, 0), /* 0x5c: */ + MAKNIB(0, 0), /* 0x5d: */ + MAKNIB(0, 0), /* 0x5e: */ + MAKNIB(0, 0), /* 0x5f: */ + MAKNIB(8, 6), /* 0x60: MBOX_INIT_FIRMWARE */ + MAKNIB(0, 0), /* 0x60: MBOX_GET_INIT_CONTROL_BLOCK (FORMAT?) */ + MAKNIB(2, 1), /* 0x62: MBOX_INIT_LIP */ + MAKNIB(8, 1), /* 0x63: MBOX_GET_FC_AL_POSITION_MAP */ + MAKNIB(8, 1), /* 0x64: MBOX_GET_PORT_DB */ + MAKNIB(3, 1), /* 0x65: MBOX_CLEAR_ACA */ + MAKNIB(3, 1), /* 0x66: MBOX_TARGET_RESET */ + MAKNIB(3, 1), /* 0x67: MBOX_CLEAR_TASK_SET */ + MAKNIB(3, 1), /* 0x68: MBOX_ABORT_TASK_SET */ + MAKNIB(1, 2), /* 0x69: MBOX_GET_FW_STATE */ + MAKNIB(2, 8), /* 0x6a: MBOX_GET_PORT_NAME */ + MAKNIB(8, 1), /* 0x6b: MBOX_GET_LINK_STATUS */ + MAKNIB(4, 4), /* 0x6c: MBOX_INIT_LIP_RESET */ + MAKNIB(0, 0), /* 0x6d: */ + MAKNIB(0, 0), /* 0x6e: */ + MAKNIB(0, 0), /* 0x6f: */ + MAKNIB(0, 0), /* 0x70: */ + MAKNIB(0, 0), /* 0x71: */ + MAKNIB(4, 1) /* 0x72: MBOX_INIT_LIP_LOGIN */ }; #define NMBCOM (sizeof (mbpcnt) / sizeof (mbpcnt[0])) -static int +static void isp_mboxcmd(isp, mbp) struct ispsoftc *isp; mbreg_t *mbp; { int outparam, inparam; - int loops; - - if (mbp->param[0] > NMBCOM) { - printf("%s: bad command %x\n", isp->isp_name, mbp->param[0]); - return (-1); + int loops, dld = 0; + u_int8_t opcode; + + if (mbp->param[0] == ISP2100_SET_PCI_PARAM) { + opcode = mbp->param[0] = MBOX_SET_PCI_PARAMETERS; + inparam = 4; + outparam = 4; + goto command_known; + } else if (mbp->param[0] > NMBCOM) { + PRINTF("%s: bad command %x\n", isp->isp_name, mbp->param[0]); + return; } + opcode = mbp->param[0]; inparam = HINIB(mbpcnt[mbp->param[0]]); outparam = LONIB(mbpcnt[mbp->param[0]]); + if (inparam == 0 && outparam == 0) { - printf("%s: no parameters for %x\n", isp->isp_name, + PRINTF("%s: no parameters for %x\n", isp->isp_name, mbp->param[0]); - return (-1); + return; } + + /* + * Check for variants + */ +#ifdef ISP2100_SCCLUN + if (isp->isp_type & ISP_HA_FC) { + switch (mbp->param[0]) { + case MBOX_ABORT: + inparam = 7; + break; + case MBOX_ABORT_DEVICE: + case MBOX_START_QUEUE: + case MBOX_STOP_QUEUE: + case MBOX_SINGLE_STEP_QUEUE: + case MBOX_ABORT_QUEUE: + case MBOX_GET_DEV_QUEUE_STATUS: + inparam = 3; + break; + default: + break; + } + } +#endif + +command_known: + +#define NEW_MB_WAY 1 +#ifdef NEW_MB_WAY /* - * Make sure we can send some words.. + * Set semaphore on mailbox registers to win any races to acquire them. + */ + ISP_WRITE(isp, BIU_SEMA, 1); +#endif + + + /* + * Make sure we can send some words. Check to see id there's + * an async mbox event pending. */ loops = MBOX_DELAY_COUNT; while ((ISP_READ(isp, HCCR) & HCCR_HOST_INT) != 0) { - delay(100); + SYS_DELAY(100); + if (ISP_READ(isp, BIU_SEMA) & 1) { + int fph; + u_int16_t mbox = ISP_READ(isp, OUTMAILBOX0); + /* + * We have a pending MBOX async event. + */ + if (mbox & 0x8000) { + fph = isp_parse_async(isp, (int) mbox); + ISP_WRITE(isp, BIU_SEMA, 0); + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + if (fph < 0) { + return; + } else if (fph > 0) { + isp_fastpost_complete(isp, fph); + } + SYS_DELAY(100); + goto command_known; + } + /* + * We have a pending MBOX completion? Might be + * from a previous command. We can't (sometimes) + * just clear HOST INTERRUPT, so we'll just silently + * eat this here. + */ + if (mbox == MBOX_COMMAND_COMPLETE) { + ISP_WRITE(isp, BIU_SEMA, 0); + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + SYS_DELAY(100); + goto command_known; + } + } if (--loops < 0) { - printf("%s: isp_mboxcmd timeout #1\n", isp->isp_name); - return (-1); + if (dld++ > 10) { + PRINTF("%s: isp_mboxcmd could not get command " + "started\n", isp->isp_name); + return; + } + ISP_WRITE(isp, BIU_SEMA, 0); + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + goto command_known; } } /* - * Write input parameters + * Write input parameters. */ switch (inparam) { + case 8: ISP_WRITE(isp, INMAILBOX7, mbp->param[7]); mbp->param[7] = 0; + case 7: ISP_WRITE(isp, INMAILBOX6, mbp->param[6]); mbp->param[6] = 0; case 6: ISP_WRITE(isp, INMAILBOX5, mbp->param[5]); mbp->param[5] = 0; case 5: ISP_WRITE(isp, INMAILBOX4, mbp->param[4]); mbp->param[4] = 0; case 4: ISP_WRITE(isp, INMAILBOX3, mbp->param[3]); mbp->param[3] = 0; @@ -964,29 +2978,35 @@ isp_mboxcmd(isp, mbp) } /* - * Clear semaphore on mailbox registers + * Clear RISC int condition. */ - ISP_WRITE(isp, BIU_SEMA, 0); + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); /* - * Clear RISC int condition. + * Clear semaphore on mailbox registers so that the Qlogic + * may update outgoing registers. */ - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + ENABLE_INTS(isp); /* * Set Host Interrupt condition so that RISC will pick up mailbox regs. */ ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); +#ifndef NEW_MB_WAY /* - * Wait until RISC int is set + * Wait until RISC int is set, except 2100 */ - loops = MBOX_DELAY_COUNT; - while ((ISP_READ(isp, BIU_ISR) & BIU_ISR_RISC_INT) != 0) { - delay(100); - if (--loops < 0) { - printf("%s: isp_mboxcmd timeout #2\n", isp->isp_name); - return (-1); + if ((isp->isp_type & ISP_HA_FC) == 0) { + loops = MBOX_DELAY_COUNT; + while ((ISP_READ(isp, BIU_ISR) & BIU_ISR_RISC_INT) == 0) { + SYS_DELAY(100); + if (--loops < 0) { + PRINTF("%s: isp_mboxcmd timeout #2\n", + isp->isp_name); + return; + } } } @@ -995,30 +3015,91 @@ isp_mboxcmd(isp, mbp) */ loops = MBOX_DELAY_COUNT; while ((ISP_READ(isp, BIU_SEMA) & 1) == 0) { - delay(100); + SYS_DELAY(100); + /* + * Wierd- I've seen the case where the semaphore register + * isn't getting set- sort of a violation of the protocol.. + */ + if (ISP_READ(isp, OUTMAILBOX0) & 0x4000) + break; + if (--loops < 0) { + PRINTF("%s: isp_mboxcmd timeout #3\n", isp->isp_name); + return; + } + } +#else + /* + * Wait until HOST INT has gone away (meaning that the Qlogic + * has picked up the mailbox command. Wait a long time. + */ + loops = MBOX_DELAY_COUNT * 5; + while ((ISP_READ(isp, HCCR) & HCCR_CMD_CLEAR_RISC_INT) != 0) { + SYS_DELAY(100); if (--loops < 0) { - printf("%s: isp_mboxcmd timeout #3\n", isp->isp_name); - return (-1); + PRINTF("%s: isp_mboxcmd timeout #2\n", isp->isp_name); + return; } } /* - * Make sure that the MBOX_BUSY has gone away + * While the Semaphore registers isn't set, wait for the Qlogic + * to process the mailbox command. Again- wait a long time. */ - loops = MBOX_DELAY_COUNT; - while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) { - delay(100); + loops = MBOX_DELAY_COUNT * 5; + while ((ISP_READ(isp, BIU_SEMA) & 1) == 0) { + SYS_DELAY(100); + /* + * Wierd- I've seen the case where the semaphore register + * isn't getting set- sort of a violation of the protocol.. + */ + if (ISP_READ(isp, OUTMAILBOX0) & 0x4000) + break; if (--loops < 0) { - printf("%s: isp_mboxcmd timeout #4\n", isp->isp_name); - return (-1); + PRINTF("%s: isp_mboxcmd timeout #3\n", isp->isp_name); + return; } } +#endif + /* + * Make sure that the MBOX_BUSY has gone away + */ + loops = MBOX_DELAY_COUNT; + for (;;) { + u_int16_t mbox = ISP_READ(isp, OUTMAILBOX0); + if (mbox == MBOX_BUSY) { + if (--loops < 0) { + PRINTF("%s: isp_mboxcmd timeout #4\n", + isp->isp_name); + return; + } + SYS_DELAY(100); + continue; + } + /* + * We have a pending MBOX async event. + */ + if (mbox & 0x8000) { + int fph = isp_parse_async(isp, (int) mbox); + ISP_WRITE(isp, BIU_SEMA, 0); + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + if (fph < 0) { + return; + } else if (fph > 0) { + isp_fastpost_complete(isp, fph); + } + SYS_DELAY(100); + continue; + } + break; + } /* * Pick up output parameters. */ switch (outparam) { + case 8: mbp->param[7] = ISP_READ(isp, OUTMAILBOX7); + case 7: mbp->param[6] = ISP_READ(isp, OUTMAILBOX6); case 6: mbp->param[5] = ISP_READ(isp, OUTMAILBOX5); case 5: mbp->param[4] = ISP_READ(isp, OUTMAILBOX4); case 4: mbp->param[3] = ISP_READ(isp, OUTMAILBOX3); @@ -1036,33 +3117,778 @@ isp_mboxcmd(isp, mbp) * Release semaphore on mailbox registers */ ISP_WRITE(isp, BIU_SEMA, 0); - return (0); + + /* + * Just to be chatty here... + */ + switch(mbp->param[0]) { + case MBOX_COMMAND_COMPLETE: + break; + case MBOX_INVALID_COMMAND: + IDPRINTF(2, ("%s: mbox cmd %x failed with INVALID_COMMAND\n", + isp->isp_name, opcode)); + break; + case MBOX_HOST_INTERFACE_ERROR: + PRINTF("%s: mbox cmd %x failed with HOST_INTERFACE_ERROR\n", + isp->isp_name, opcode); + break; + case MBOX_TEST_FAILED: + PRINTF("%s: mbox cmd %x failed with TEST_FAILED\n", + isp->isp_name, opcode); + break; + case MBOX_COMMAND_ERROR: + PRINTF("%s: mbox cmd %x failed with COMMAND_ERROR\n", + isp->isp_name, opcode); + break; + case MBOX_COMMAND_PARAM_ERROR: + switch (opcode) { + case MBOX_GET_PORT_DB: + case MBOX_GET_PORT_NAME: + case MBOX_GET_DEV_QUEUE_PARAMS: + break; + default: + PRINTF("%s: mbox cmd %x failed with " + "COMMAND_PARAM_ERROR\n", isp->isp_name, opcode); + } + break; + + /* + * Be silent about these... + */ + + case ASYNC_LIP_OCCURRED: + case ASYNC_LOOP_UP: + case ASYNC_LOOP_DOWN: + case ASYNC_LOOP_RESET: + case ASYNC_CHANGE_NOTIFY: + break; + case ASYNC_PDB_CHANGED: + isp_mark_getpdb_all(isp); + break; + + default: + /* + * The expected return of EXEC_FIRMWARE is zero. + */ + if ((opcode == MBOX_EXEC_FIRMWARE && mbp->param[0] != 0) || + (opcode != MBOX_EXEC_FIRMWARE)) { + PRINTF("%s: mbox cmd %x failed with error %x\n", + isp->isp_name, opcode, mbp->param[0]); + } + break; + } } -static void -isp_lostcmd(struct ispsoftc *isp, struct scsi_xfer *xs) +void +isp_lostcmd(isp, xs) + struct ispsoftc *isp; + ISP_SCSI_XFER_T *xs; { mbreg_t mbs; - mbs.param[0] = MBOX_GET_FIRMWARE_STATUS; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[0] = MBOX_GET_FIRMWARE_STATUS; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("%s: couldn't GET FIRMWARE STATUS\n", isp->isp_name); + isp_dumpregs(isp, "couldn't GET FIRMWARE STATUS"); return; } - printf("%s: lost command, %d commands active of total %d\n", - isp->isp_name, mbs.param[1], mbs.param[2]); - if (xs == NULL || xs->sc_link == NULL) + if (mbs.param[1]) { + PRINTF("%s: %d commands on completion queue\n", + isp->isp_name, mbs.param[1]); + } + if (XS_NULL(xs)) return; mbs.param[0] = MBOX_GET_DEV_QUEUE_STATUS; - mbs.param[1] = xs->sc_link->target << 8 | xs->sc_link->lun; - (void) isp_mboxcmd(isp, &mbs); + mbs.param[1] = (XS_TGT(xs) << 8) | XS_LUN(xs); + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - printf("%s: couldn't GET DEVICE STATUS\n", isp->isp_name); + isp_dumpregs(isp, "couldn't GET DEVICE QUEUE STATUS"); return; } - printf("%s: lost command, target %d lun %d, State: %x\n", - isp->isp_name, mbs.param[1] >> 8, mbs.param[1] & 0x7, - mbs.param[2] & 0xff); + PRINTF("%s: lost command for target %d lun %d, %d active of %d, " + "Queue State: %x\n", isp->isp_name, XS_TGT(xs), + XS_LUN(xs), mbs.param[2], mbs.param[3], mbs.param[1]); + + isp_dumpregs(isp, "lost command"); + /* + * XXX: Need to try and do something to recover. + */ +} + +static void +isp_dumpregs(isp, msg) + struct ispsoftc *isp; + const char *msg; +{ + PRINTF("%s: %s\n", isp->isp_name, msg); + if (isp->isp_type & ISP_HA_SCSI) + PRINTF(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1)); + else + PRINTF(" biu_csr=%x", ISP_READ(isp, BIU2100_CSR)); + PRINTF(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR), + ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA)); + PRINTF("risc_hccr=%x\n", ISP_READ(isp, HCCR)); + + + if (isp->isp_type & ISP_HA_SCSI) { + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + PRINTF(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n", + ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS), + ISP_READ(isp, CDMA_FIFO_STS)); + PRINTF(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n", + ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS), + ISP_READ(isp, DDMA_FIFO_STS)); + PRINTF(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n", + ISP_READ(isp, SXP_INTERRUPT), + ISP_READ(isp, SXP_GROSS_ERR), + ISP_READ(isp, SXP_PINS_CONTROL)); + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + } + PRINTF(" mbox regs: %x %x %x %x %x\n", + ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1), + ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3), + ISP_READ(isp, OUTMAILBOX4)); + ISP_DUMPREGS(isp); +} + +static void +isp_dumpxflist(isp) + struct ispsoftc *isp; +{ + volatile ISP_SCSI_XFER_T *xs; + int i, hdp; + + for (hdp = i = 0; i < RQUEST_QUEUE_LEN; i++) { + xs = isp->isp_xflist[i]; + if (xs == NULL) { + continue; + } + if (hdp == 0) { + PRINTF("%s: active requests\n", isp->isp_name); + hdp++; + } + PRINTF(" Active Handle %d: tgt %d lun %d dlen %d\n", + i+1, XS_TGT(xs), XS_LUN(xs), XS_XFRLEN(xs)); + } +} + +static void +isp_fw_state(isp) + struct ispsoftc *isp; +{ + mbreg_t mbs; + if (isp->isp_type & ISP_HA_FC) { + int once = 0; + fcparam *fcp = isp->isp_param; +again: + mbs.param[0] = MBOX_GET_FW_STATE; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + switch (mbs.param[0]) { + case ASYNC_PDB_CHANGED: + isp_mark_getpdb_all(isp); + /* FALL THROUGH */ + case ASYNC_LIP_OCCURRED: + case ASYNC_LOOP_UP: + case ASYNC_LOOP_DOWN: + case ASYNC_LOOP_RESET: + case ASYNC_CHANGE_NOTIFY: + if (once++ < 2) { + goto again; + } + break; + } + isp_dumpregs(isp, "GET FIRMWARE STATE failed"); + return; + } + fcp->isp_fwstate = mbs.param[1]; + } +} + +static void +isp_update(isp) + struct ispsoftc *isp; +{ + int tgt; + mbreg_t mbs; + sdparam *sdp; + + isp->isp_update = 0; + + if (isp->isp_type & ISP_HA_FC) { + return; + } + + sdp = isp->isp_param; + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + u_int16_t flags, period, offset, changed; + int get; + + if (sdp->isp_devparam[tgt].dev_enable == 0) { + continue; + } + + if (sdp->isp_devparam[tgt].dev_update) { + mbs.param[0] = MBOX_SET_TARGET_PARAMS; + mbs.param[2] = sdp->isp_devparam[tgt].dev_flags; + mbs.param[3] = + (sdp->isp_devparam[tgt].sync_offset << 8) | + (sdp->isp_devparam[tgt].sync_period); + sdp->isp_devparam[tgt].dev_update = 0; + sdp->isp_devparam[tgt].dev_refresh = 1; + isp->isp_update = 1; + get = 0; + } else if (sdp->isp_devparam[tgt].dev_refresh) { + mbs.param[0] = MBOX_GET_TARGET_PARAMS; + sdp->isp_devparam[tgt].dev_refresh = 0; + get = 1; + } else { + continue; + } + mbs.param[1] = tgt << 8; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + PRINTF("%s: failed to %cet SCSI parameters for " + "target %d\n", isp->isp_name, (get)? 'g' : 's', + tgt); + continue; + } + + if (get == 0) { + /* + * XXX: Need a SYNC_TARGET for efficiency... + */ + isp->isp_sendmarker = 1; + sdp->isp_devparam[tgt].cur_dflags = + sdp->isp_devparam[tgt].dev_flags; + continue; + } + flags = mbs.param[2]; + period = mbs.param[3] & 0xff; + offset = mbs.param[3] >> 8; + if (sdp->isp_devparam[tgt].cur_dflags != flags || + sdp->isp_devparam[tgt].sync_period != period || + sdp->isp_devparam[tgt].sync_offset != offset) { + IDPRINTF(3, ("%s: tgt %d flags 0x%x period %d " + "off %d\n", isp->isp_name, tgt, flags, + period, offset)); + changed = 1; + } else { + changed = 0; + } + + sdp->isp_devparam[tgt].cur_dflags = flags; + sdp->isp_devparam[tgt].dev_flags = flags; + sdp->isp_devparam[tgt].sync_period = period; + sdp->isp_devparam[tgt].sync_offset = offset; + if (sdp->isp_devparam[tgt].dev_announced == 0 || changed) { + if (isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, &tgt)) + sdp->isp_devparam[tgt].dev_announced = 0; + else + sdp->isp_devparam[tgt].dev_announced = 1; + } + } +} + +static void +isp_setdfltparm(isp) + struct ispsoftc *isp; +{ + int i; + mbreg_t mbs; + sdparam *sdp; + + /* + * Been there, done that, got the T-shirt... + */ + if (isp->isp_gotdparms) { + IDPRINTF(3, ("%s: already have dparms\n", isp->isp_name)); + return; + } + isp->isp_gotdparms = 1; + + if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0 && + (isp_read_nvram(isp) == 0)) { + return; + } + if (IS_FC(isp)) { + fcparam *fcp = (fcparam *) isp->isp_param; + fcp->isp_maxfrmlen = ICB_DFLT_FRMLEN; + fcp->isp_maxalloc = 256; + fcp->isp_execthrottle = 16; + fcp->isp_retry_delay = 5; + fcp->isp_retry_count = 0; + /* + * It would be nice to fake up a WWN in case we don't + * get one out of NVRAM. Solaris does this for SOCAL + * cards that don't have SBus properties- it sets up + * a WWN based upon the system MAC Address. + */ + fcp->isp_wwn = 0; + return; + } + + sdp = (sdparam *) isp->isp_param; + mbs.param[0] = MBOX_GET_ACT_NEG_STATE; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + IDPRINTF(2, ("could not GET ACT NEG STATE\n")); + sdp->isp_req_ack_active_neg = 1; + sdp->isp_data_line_active_neg = 1; + } else { + sdp->isp_req_ack_active_neg = (mbs.param[1] >> 4) & 0x1; + sdp->isp_data_line_active_neg = (mbs.param[1] >> 5) & 0x1; + } + + for (i = 0; i < MAX_TARGETS; i++) { + sdp->isp_devparam[i].dev_flags = DPARM_DEFAULT; + sdp->isp_devparam[i].cur_dflags = DPARM_SAFE_DFLT; + if (isp->isp_type < ISP_HA_SCSI_1040 || + (sdp->isp_clock && sdp->isp_clock < 60)) { + sdp->isp_devparam[i].sync_offset = + ISP_10M_SYNCPARMS >> 8; + sdp->isp_devparam[i].sync_period = + ISP_10M_SYNCPARMS & 0xff; + } else { + sdp->isp_devparam[i].sync_offset = + ISP_20M_SYNCPARMS >> 8; + sdp->isp_devparam[i].sync_period = + ISP_20M_SYNCPARMS & 0xff; + } + + /* + * Don't get current target parameters if we've been + * told not to use NVRAM- it's really the same thing. + */ + if (isp->isp_confopts & ISP_CFG_NONVRAM) + continue; + + mbs.param[0] = MBOX_GET_TARGET_PARAMS; + mbs.param[1] = i << 8; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + continue; + } + sdp->isp_devparam[i].dev_flags = mbs.param[2]; + /* + * The maximum period we can really see + * here is 100 (decimal), or 400 ns. + * For some unknown reason we sometimes + * get back wildass numbers from the + * boot device's parameters. + * + * XXX: Hmm- this may be based on a different + * XXX: clock rate. + */ + if ((mbs.param[3] & 0xff) <= 0x64) { + sdp->isp_devparam[i].sync_period = mbs.param[3] & 0xff; + sdp->isp_devparam[i].sync_offset = mbs.param[3] >> 8; + } + + /* + * It is not safe to run Ultra Mode with a clock < 60. + */ + if (((sdp->isp_clock && sdp->isp_clock < 60) || + (isp->isp_type < ISP_HA_SCSI_1020A)) && + (sdp->isp_devparam[i].sync_period == + (ISP_20M_SYNCPARMS & 0xff))) { + sdp->isp_devparam[i].sync_offset = + ISP_10M_SYNCPARMS >> 8; + sdp->isp_devparam[i].sync_period = + ISP_10M_SYNCPARMS & 0xff; + } + } + + /* + * Set Default Host Adapter Parameters + */ + sdp->isp_cmd_dma_burst_enable = 1; + sdp->isp_data_dma_burst_enabl = 1; + sdp->isp_fifo_threshold = 0; + sdp->isp_initiator_id = 7; + if (isp->isp_type >= ISP_HA_SCSI_1040) { + sdp->isp_async_data_setup = 9; + } else { + sdp->isp_async_data_setup = 6; + } + sdp->isp_selection_timeout = 250; + sdp->isp_max_queue_depth = 128; + sdp->isp_tag_aging = 8; + sdp->isp_bus_reset_delay = 3; + sdp->isp_retry_count = 0; + sdp->isp_retry_delay = 1; + + for (i = 0; i < MAX_TARGETS; i++) { + sdp->isp_devparam[i].exc_throttle = 16; + sdp->isp_devparam[i].dev_enable = 1; + } +} + +/* + * Re-initialize the ISP and complete all orphaned commands + * with a 'botched' notice. + * + * Locks held prior to coming here. + */ + +void +isp_restart(isp) + struct ispsoftc *isp; +{ + ISP_SCSI_XFER_T *tlist[RQUEST_QUEUE_LEN], *xs; + int i; + + for (i = 0; i < RQUEST_QUEUE_LEN; i++) { + tlist[i] = (ISP_SCSI_XFER_T *) isp->isp_xflist[i]; + isp->isp_xflist[i] = NULL; + } + isp_reset(isp); + if (isp->isp_state == ISP_RESETSTATE) { + isp_init(isp); + if (isp->isp_state == ISP_INITSTATE) { + isp->isp_state = ISP_RUNSTATE; + } + } + if (isp->isp_state != ISP_RUNSTATE) { + PRINTF("%s: isp_restart cannot restart ISP\n", isp->isp_name); + } + + for (i = 0; i < RQUEST_QUEUE_LEN; i++) { + xs = tlist[i]; + if (XS_NULL(xs)) { + continue; + } + if (isp->isp_nactive > 0) + isp->isp_nactive--; + XS_RESID(xs) = XS_XFRLEN(xs); + XS_SETERR(xs, HBA_BUSRESET); + XS_CMD_DONE(xs); + } +} + +/* + * NVRAM Routines + */ + +static int +isp_read_nvram(isp) + struct ispsoftc *isp; +{ + int i, amt; + u_int8_t csum, minversion; + union { + u_int8_t _x[ISP2100_NVRAM_SIZE]; + u_int16_t _s[ISP2100_NVRAM_SIZE>>1]; + } _n; +#define nvram_data _n._x +#define nvram_words _n._s + + if (IS_FC(isp)) { + amt = ISP2100_NVRAM_SIZE; + minversion = 1; + } else { + amt = ISP_NVRAM_SIZE; + minversion = 2; + } + + /* + * Just read the first two words first to see if we have a valid + * NVRAM to continue reading the rest with. + */ + for (i = 0; i < 2; i++) { + isp_rdnvram_word(isp, i, &nvram_words[i]); + } + if (nvram_data[0] != 'I' || nvram_data[1] != 'S' || + nvram_data[2] != 'P') { + if (isp->isp_bustype != ISP_BT_SBUS) { + PRINTF("%s: invalid NVRAM header\n", isp->isp_name); + } + return (-1); + } + for (i = 2; i < amt>>1; i++) { + isp_rdnvram_word(isp, i, &nvram_words[i]); + } + for (csum = 0, i = 0; i < amt; i++) { + csum += nvram_data[i]; + } + if (csum != 0) { + PRINTF("%s: invalid NVRAM checksum\n", isp->isp_name); + return (-1); + } + if (ISP_NVRAM_VERSION(nvram_data) < minversion) { + PRINTF("%s: version %d NVRAM not understood\n", isp->isp_name, + ISP_NVRAM_VERSION(nvram_data)); + return (-1); + } + + if (isp->isp_type & ISP_HA_SCSI) { + sdparam *sdp = (sdparam *) isp->isp_param; + + sdp->isp_fifo_threshold = + ISP_NVRAM_FIFO_THRESHOLD(nvram_data) | + (ISP_NVRAM_FIFO_THRESHOLD_128(nvram_data) << 2); + + sdp->isp_initiator_id = + ISP_NVRAM_INITIATOR_ID(nvram_data); + + sdp->isp_bus_reset_delay = + ISP_NVRAM_BUS_RESET_DELAY(nvram_data); + + sdp->isp_retry_count = + ISP_NVRAM_BUS_RETRY_COUNT(nvram_data); + + sdp->isp_retry_delay = + ISP_NVRAM_BUS_RETRY_DELAY(nvram_data); + + sdp->isp_async_data_setup = + ISP_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data); + + if (isp->isp_type >= ISP_HA_SCSI_1040) { + if (sdp->isp_async_data_setup < 9) { + sdp->isp_async_data_setup = 9; + } + } else { + if (sdp->isp_async_data_setup != 6) { + sdp->isp_async_data_setup = 6; + } + } + + sdp->isp_req_ack_active_neg = + ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data); + + sdp->isp_data_line_active_neg = + ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data); + + sdp->isp_data_dma_burst_enabl = + ISP_NVRAM_DATA_DMA_BURST_ENABLE(nvram_data); + + sdp->isp_cmd_dma_burst_enable = + ISP_NVRAM_CMD_DMA_BURST_ENABLE(nvram_data); + + sdp->isp_tag_aging = + ISP_NVRAM_TAG_AGE_LIMIT(nvram_data); + + sdp->isp_selection_timeout = + ISP_NVRAM_SELECTION_TIMEOUT(nvram_data); + + sdp->isp_max_queue_depth = + ISP_NVRAM_MAX_QUEUE_DEPTH(nvram_data); + + sdp->isp_fast_mttr = ISP_NVRAM_FAST_MTTR_ENABLE(nvram_data); + if (isp->isp_dblev > 2) { + static char *true = "true"; + static char *false = "false"; + PRINTF("%s: NVRAM values:\n", isp->isp_name); + PRINTF(" Fifo Threshold = 0x%x\n", + sdp->isp_fifo_threshold); + PRINTF(" Bus Reset Delay = %d\n", + sdp->isp_bus_reset_delay); + PRINTF(" Retry Count = %d\n", + sdp->isp_retry_count); + PRINTF(" Retry Delay = %d\n", + sdp->isp_retry_delay); + PRINTF(" Tag Age Limit = %d\n", + sdp->isp_tag_aging); + PRINTF(" Selection Timeout = %d\n", + sdp->isp_selection_timeout); + PRINTF(" Max Queue Depth = %d\n", + sdp->isp_max_queue_depth); + PRINTF(" Async Data Setup = 0x%x\n", + sdp->isp_async_data_setup); + PRINTF(" REQ/ACK Active Negation = %s\n", + sdp->isp_req_ack_active_neg? true : false); + PRINTF(" Data Line Active Negation = %s\n", + sdp->isp_data_line_active_neg? true : false); + PRINTF(" Data DMA Burst Enable = %s\n", + sdp->isp_data_dma_burst_enabl? true : false); + PRINTF(" Cmd DMA Burst Enable = %s\n", + sdp->isp_cmd_dma_burst_enable? true : false); + PRINTF(" Fast MTTR = %s\n", + sdp->isp_fast_mttr? true : false); + } + for (i = 0; i < MAX_TARGETS; i++) { + sdp->isp_devparam[i].dev_enable = + ISP_NVRAM_TGT_DEVICE_ENABLE(nvram_data, i); + sdp->isp_devparam[i].exc_throttle = + ISP_NVRAM_TGT_EXEC_THROTTLE(nvram_data, i); + sdp->isp_devparam[i].sync_offset = + ISP_NVRAM_TGT_SYNC_OFFSET(nvram_data, i); + sdp->isp_devparam[i].sync_period = + ISP_NVRAM_TGT_SYNC_PERIOD(nvram_data, i); + + if (isp->isp_type < ISP_HA_SCSI_1040) { + /* + * If we're not ultra, we can't possibly + * be a shorter period than this. + */ + if (sdp->isp_devparam[i].sync_period < 0x19) { + sdp->isp_devparam[i].sync_period = + 0x19; + } + if (sdp->isp_devparam[i].sync_offset > 0xc) { + sdp->isp_devparam[i].sync_offset = + 0x0c; + } + } else { + if (sdp->isp_devparam[i].sync_offset > 0x8) { + sdp->isp_devparam[i].sync_offset = 0x8; + } + } + sdp->isp_devparam[i].dev_flags = 0; + if (ISP_NVRAM_TGT_RENEG(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_RENEG; + if (ISP_NVRAM_TGT_QFRZ(nvram_data, i)) { + PRINTF("%s: not supporting QFRZ option for " + "target %d\n", isp->isp_name, i); + } + sdp->isp_devparam[i].dev_flags |= DPARM_ARQ; + if (ISP_NVRAM_TGT_ARQ(nvram_data, i) == 0) { + PRINTF("%s: not disabling ARQ option for " + "target %d\n", isp->isp_name, i); + } + if (ISP_NVRAM_TGT_TQING(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_TQING; + if (ISP_NVRAM_TGT_SYNC(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_SYNC; + if (ISP_NVRAM_TGT_WIDE(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_WIDE; + if (ISP_NVRAM_TGT_PARITY(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_PARITY; + if (ISP_NVRAM_TGT_DISC(nvram_data, i)) + sdp->isp_devparam[i].dev_flags |= DPARM_DISC; + if (isp->isp_dblev > 2) { + PRINTF(" Target %d: Enabled %d Throttle %d " + "Offset %d Period %d Flags 0x%x\n", i, + sdp->isp_devparam[i].dev_enable, + sdp->isp_devparam[i].exc_throttle, + sdp->isp_devparam[i].sync_offset, + sdp->isp_devparam[i].sync_period, + sdp->isp_devparam[i].dev_flags); + } + } + } else { + fcparam *fcp = (fcparam *) isp->isp_param; + union { + struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t hi32; + u_int32_t lo32; +#else + u_int32_t lo32; + u_int32_t hi32; +#endif + } wds; + u_int64_t full64; + } wwnstore; + + wwnstore.full64 = ISP2100_NVRAM_NODE_NAME(nvram_data); + PRINTF("%s: Adapter WWN 0x%08x%08x\n", isp->isp_name, + wwnstore.wds.hi32, wwnstore.wds.lo32); + fcp->isp_wwn = wwnstore.full64; + wwnstore.full64 = ISP2100_NVRAM_BOOT_NODE_NAME(nvram_data); + if (wwnstore.full64 != 0) { + PRINTF("%s: BOOT DEVICE WWN 0x%08x%08x\n", + isp->isp_name, wwnstore.wds.hi32, + wwnstore.wds.lo32); + } + fcp->isp_maxalloc = + ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data); + fcp->isp_maxfrmlen = + ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data); + fcp->isp_retry_delay = + ISP2100_NVRAM_RETRY_DELAY(nvram_data); + fcp->isp_retry_count = + ISP2100_NVRAM_RETRY_COUNT(nvram_data); + fcp->isp_loopid = + ISP2100_NVRAM_HARDLOOPID(nvram_data); + fcp->isp_execthrottle = + ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data); + fcp->isp_fwoptions = ISP2100_NVRAM_OPTIONS(nvram_data); + if (isp->isp_dblev > 2) { + PRINTF("%s: NVRAM values:\n", isp->isp_name); + PRINTF(" Max IOCB Allocation = %d\n", + fcp->isp_maxalloc); + PRINTF(" Max Frame Length = %d\n", + fcp->isp_maxfrmlen); + PRINTF(" Execution Throttle = %d\n", + fcp->isp_execthrottle); + PRINTF(" Retry Count = %d\n", + fcp->isp_retry_count); + PRINTF(" Retry Delay = %d\n", + fcp->isp_retry_delay); + PRINTF(" Hard Loop ID = %d\n", + fcp->isp_loopid); + PRINTF(" Options = 0x%x\n", + fcp->isp_fwoptions); + PRINTF(" HBA Options = 0x%x\n", + ISP2100_NVRAM_HBA_OPTIONS(nvram_data)); + } + } + IDPRINTF(3, ("%s: NVRAM is valid\n", isp->isp_name)); + return (0); +} + +static void +isp_rdnvram_word(isp, wo, rp) + struct ispsoftc *isp; + int wo; + u_int16_t *rp; +{ + int i, cbits; + u_int16_t bit, rqst; + + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); + SYS_DELAY(2); + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); + SYS_DELAY(2); + + if (isp->isp_type & ISP_HA_FC) { + wo &= ((ISP2100_NVRAM_SIZE >> 1) - 1); + rqst = (ISP_NVRAM_READ << 8) | wo; + cbits = 10; + } else { + wo &= ((ISP_NVRAM_SIZE >> 1) - 1); + rqst = (ISP_NVRAM_READ << 6) | wo; + cbits = 8; + } + + /* + * Clock the word select request out... + */ + for (i = cbits; i >= 0; i--) { + if ((rqst >> i) & 1) { + bit = BIU_NVRAM_SELECT | BIU_NVRAM_DATAOUT; + } else { + bit = BIU_NVRAM_SELECT; + } + ISP_WRITE(isp, BIU_NVRAM, bit); + SYS_DELAY(2); + ISP_WRITE(isp, BIU_NVRAM, bit | BIU_NVRAM_CLOCK); + SYS_DELAY(2); + ISP_WRITE(isp, BIU_NVRAM, bit); + SYS_DELAY(2); + } + /* + * Now read the result back in (bits come back in MSB format). + */ + *rp = 0; + for (i = 0; i < 16; i++) { + u_int16_t rv; + *rp <<= 1; + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); + SYS_DELAY(2); + rv = ISP_READ(isp, BIU_NVRAM); + if (rv & BIU_NVRAM_DATAIN) { + *rp |= 1; + } + SYS_DELAY(2); + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); + SYS_DELAY(2); + } + ISP_WRITE(isp, BIU_NVRAM, 0); + SYS_DELAY(2); +#if BYTE_ORDER == BIG_ENDIAN + *rp = ((*rp >> 8) | ((*rp & 0xff) << 8)); +#endif } diff --git a/sys/dev/ic/isp_openbsd.c b/sys/dev/ic/isp_openbsd.c new file mode 100644 index 00000000000..cf83909bccc --- /dev/null +++ b/sys/dev/ic/isp_openbsd.c @@ -0,0 +1,438 @@ +/* $OpenBSD: isp_openbsd.c,v 1.1 1999/03/17 05:26:09 mjacob Exp $ */ +/* release_03_16_99 */ +/* + * Platform (OpenBSD) dependent common attachment code for Qlogic adapters. + * + *--------------------------------------- + * Copyright (c) 1997, 1998, 1999 by Matthew Jacob + * NASA/Ames Research Center + * All rights reserved. + *--------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The author may be reached via electronic communications at + * + * mjacob@nas.nasa.gov + * mjacob@feral.com + * + * or, via United States Postal Address + * + * Matthew Jacob + * Feral Software + * 2339 3rd Street + * Suite 24 + * San Francisco, CA, 94107 + */ + +#include <dev/ic/isp_openbsd.h> + +static void ispminphys __P((struct buf *)); +static int32_t ispcmd __P((ISP_SCSI_XFER_T *)); + +static struct scsi_device isp_dev = { NULL, NULL, NULL, NULL }; +static int isp_poll __P((struct ispsoftc *, ISP_SCSI_XFER_T *, int)); +static void isp_watch __P((void *)); + +struct cfdriver isp_cd = { + NULL, "isp", DV_DULL +}; + + +#define FC_OPENINGS RQUEST_QUEUE_LEN / (MAX_FC_TARG-1) +#define PI_OPENINGS RQUEST_QUEUE_LEN / (MAX_TARGETS-1) + +/* + * Complete attachment of hardware, include subdevices. + */ +void +isp_attach(isp) + struct ispsoftc *isp; +{ + isp->isp_osinfo._adapter.scsi_cmd = ispcmd; + isp->isp_osinfo._adapter.scsi_minphys = ispminphys; + + isp->isp_state = ISP_RUNSTATE; + /* + * OpenBSD will lose on the 1240 support because you don't + * get multiple SCSI busses per adapter instance. + */ +#if 0 + isp->isp_osinfo._link.channel = SCSI_CHANNEL_ONLY_ONE; +#endif + isp->isp_osinfo._link.adapter_softc = isp; + isp->isp_osinfo._link.device = &isp_dev; + isp->isp_osinfo._link.adapter = &isp->isp_osinfo._adapter; + + if (isp->isp_type & ISP_HA_FC) { + isp->isp_osinfo._link.openings = FC_OPENINGS; + isp->isp_osinfo._link.adapter_buswidth = MAX_FC_TARG; + /* We can set max lun width here */ + isp->isp_osinfo._link.adapter_target = + ((fcparam *)isp->isp_param)->isp_loopid; + } else { + isp->isp_osinfo._link.openings = PI_OPENINGS; + isp->isp_osinfo._link.adapter_buswidth = MAX_TARGETS; + /* We can set max lun width here */ + isp->isp_osinfo._link.adapter_target = + ((sdparam *)isp->isp_param)->isp_initiator_id; + } + if (isp->isp_osinfo._link.openings < 2) + isp->isp_osinfo._link.openings = 2; + + /* + * Send a SCSI Bus Reset (used to be done as part of attach, + * but now left to the OS outer layers). + * + * XXX: For now, skip resets for FC because the method by which + * XXX: we deal with loop down after issuing resets (which causes + * XXX: port logouts for all devices) needs interrupts to run so + * XXX: that async events happen. + */ + if (IS_SCSI(isp)) { + (void) isp_control(isp, ISPCTL_RESET_BUS, NULL); + /* + * Wait for it to settle. + */ + delay(2 * 1000000); + } + + /* + * Start the watchdog. + * + * The wathdog will, ridiculously enough, also enable Sync negotiation. + */ + isp->isp_dogactive = 1; + timeout(isp_watch, isp, 30 * hz); + + /* + * And attach children (if any). + */ + config_found((void *)isp, &isp->isp_osinfo._link, scsiprint); +} + +/* + * minphys our xfers + * + * Unfortunately, the buffer pointer describes the target device- not the + * adapter device, so we can't use the pointer to find out what kind of + * adapter we are and adjust accordingly. + */ + +static void +ispminphys(bp) + struct buf *bp; +{ + /* + * XX: Only the 1020 has a 24 bit limit. + */ + if (bp->b_bcount >= (1 << 24)) { + bp->b_bcount = (1 << 24); + } + minphys(bp); +} + +static int +ispcmd(xs) + ISP_SCSI_XFER_T *xs; +{ + struct ispsoftc *isp; + int result; + int s; + + isp = xs->sc_link->adapter_softc; + s = splbio(); + + if (isp->isp_state < ISP_RUNSTATE) { + DISABLE_INTS(isp); + isp_init(isp); + if (isp->isp_state != ISP_INITSTATE) { + ENABLE_INTS(isp); + (void) splx(s); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_COMPLETE); + } + isp->isp_state = ISP_RUNSTATE; + ENABLE_INTS(isp); + } + DISABLE_INTS(isp); + result = ispscsicmd(xs); + ENABLE_INTS(isp); + if (result != CMD_QUEUED || (xs->flags & SCSI_POLL) == 0) { + (void) splx(s); + return (result); + } + + /* + * If we can't use interrupts, poll on completion. + */ + if (isp_poll(isp, xs, xs->timeout)) { + /* + * If no other error occurred but we didn't finish, + * something bad happened. + */ + if ((xs->flags & ITSDONE) == 0) { + isp->isp_nactive--; + if (isp->isp_nactive < 0) + isp->isp_nactive = 0; + if (xs->error == XS_NOERROR) { + isp_lostcmd(isp, xs); + xs->error = XS_DRIVER_STUFFUP; + } + } + } + (void) splx(s); + return (COMPLETE); +} + +static int +isp_poll(isp, xs, mswait) + struct ispsoftc *isp; + ISP_SCSI_XFER_T *xs; + int mswait; +{ + + while (mswait) { + /* Try the interrupt handling routine */ + (void)isp_intr((void *)isp); + + /* See if the xs is now done */ + if (XS_IS_CMD_DONE(xs)) { + return (0); + } + delay(1000); /* wait one millisecond */ + mswait--; + } + return (1); +} + +#define DTHR 2 + +static void +isp_watch(arg) + void *arg; +{ + static int delay_throttle_count = DTHR; + int i; + struct ispsoftc *isp = arg; + ISP_SCSI_XFER_T *xs; + int s = splbio(); + + /* + * Look for completely dead commands. + */ + for (i = 0; i < RQUEST_QUEUE_LEN; i++) { + if ((xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[i]) == NULL) { + continue; + } + if (XS_TIME(xs) == 0) { + continue; + } + XS_TIME(xs) -= (WATCH_INTERVAL * 1000); + /* + * Avoid later thinking that this + * transaction is not being timed. + * Then give ourselves to watchdog + * periods of grace. + */ + if (xs->timeout == 0) { + xs->timeout = 1; + } else if (xs->timeout > -(2 * WATCH_INTERVAL * 1000)) { + continue; + } + delay_throttle_count = DTHR; + if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { + printf("%s: isp_watch failed to abort command\n", + isp->isp_name); + isp_restart(isp); + break; + } + } + + if (delay_throttle_count) { + if (--delay_throttle_count == 0) { + sdparam *sdp = isp->isp_param; + for (i = 0; i < MAX_TARGETS; i++) { + sdp->isp_devparam[i].dev_flags |= + DPARM_WIDE|DPARM_SYNC|DPARM_TQING; + sdp->isp_devparam[i].dev_update = 1; + } + isp->isp_update = 1; + } + } + timeout(isp_watch, isp, WATCH_INTERVAL * hz); + isp->isp_dogactive = 1; + (void) splx(s); +} + +/* + * Free any associated resources prior to decommissioning and + * set the card to a known state (so it doesn't wake up and kick + * us when we aren't expecting it to). + * + * Locks are held before coming here. + */ +void +isp_uninit(isp) + struct ispsoftc *isp; +{ + int s = splbio(); + /* + * Leave with interrupts disabled. + */ + DISABLE_INTS(isp); + + /* + * Turn off the watchdog (if active). + */ + if (isp->isp_dogactive) { + untimeout(isp_watch, isp); + isp->isp_dogactive = 0; + } + + splx(s); +} + +int +isp_async(isp, cmd, arg) + struct ispsoftc *isp; + ispasync_t cmd; + void *arg; +{ + int s = splbio(); + switch (cmd) { + case ISPASYNC_NEW_TGT_PARAMS: + if (IS_SCSI(isp)) { + sdparam *sdp = isp->isp_param; + char *wt; + int ns, flags, tgt; + + tgt = *((int *) arg); + + flags = sdp->isp_devparam[tgt].dev_flags; + if (flags & DPARM_SYNC) { + ns = sdp->isp_devparam[tgt].sync_period * 4; + } else { + ns = 0; + } + switch (flags & (DPARM_WIDE|DPARM_TQING)) { + case DPARM_WIDE: + wt = ", 16 bit wide\n"; + break; + case DPARM_TQING: + wt = ", Tagged Queueing Enabled\n"; + break; + case DPARM_WIDE|DPARM_TQING: + wt = ", 16 bit wide, Tagged Queueing Enabled\n"; + break; + default: + wt = "\n"; + break; + } + if (ns) { + printf("%s: Target %d at %dMHz Max Offset %d%s", + isp->isp_name, tgt, 1000 / ns, + sdp->isp_devparam[tgt].sync_offset, wt); + } else { + printf("%s: Target %d Async Mode%s", + isp->isp_name, tgt, wt); + } + } + break; + case ISPASYNC_BUS_RESET: + printf("%s: SCSI bus reset detected\n", isp->isp_name); + break; + case ISPASYNC_LOOP_DOWN: + printf("%s: Loop DOWN\n", isp->isp_name); + break; + case ISPASYNC_LOOP_UP: + printf("%s: Loop UP\n", isp->isp_name); + break; + case ISPASYNC_PDB_CHANGE_COMPLETE: +#if 0 + if (isp->isp_type & ISP_HA_FC) { + int i; + static char *roles[4] = { + "No", "Target", "Initiator", "Target/Initiator" + }; + for (i = 0; i < MAX_FC_TARG; i++) { + isp_pdb_t *pdbp = + &((fcparam *)isp->isp_param)->isp_pdb[i]; + if (pdbp->pdb_options == INVALID_PDB_OPTIONS) + continue; + printf("%s: Loop ID %d, %s role\n", + isp->isp_name, pdbp->pdb_loopid, + roles[(pdbp->pdb_prli_svc3 >> 4) & 0x3]); + printf(" Node Address 0x%x WWN 0x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + BITS2WORD(pdbp->pdb_portid_bits), + pdbp->pdb_portname[0], pdbp->pdb_portname[1], + pdbp->pdb_portname[2], pdbp->pdb_portname[3], + pdbp->pdb_portname[4], pdbp->pdb_portname[5], + pdbp->pdb_portname[6], pdbp->pdb_portname[7]); + if (pdbp->pdb_options & PDB_OPTIONS_ADISC) + printf(" Hard Address 0x%x WWN 0x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + BITS2WORD(pdbp->pdb_hardaddr_bits), + pdbp->pdb_nodename[0], + pdbp->pdb_nodename[1], + pdbp->pdb_nodename[2], + pdbp->pdb_nodename[3], + pdbp->pdb_nodename[4], + pdbp->pdb_nodename[5], + pdbp->pdb_nodename[6], + pdbp->pdb_nodename[7]); + switch (pdbp->pdb_prli_svc3 & SVC3_ROLE_MASK) { + case SVC3_TGT_ROLE|SVC3_INI_ROLE: + printf(" Master State=%s, Slave State=%s\n", + isp2100_pdb_statename(pdbp->pdb_mstate), + isp2100_pdb_statename(pdbp->pdb_sstate)); + break; + case SVC3_TGT_ROLE: + printf(" Master State=%s\n", + isp2100_pdb_statename(pdbp->pdb_mstate)); + break; + case SVC3_INI_ROLE: + printf(" Slave State=%s\n", + isp2100_pdb_statename(pdbp->pdb_sstate)); + break; + default: + break; + } + } + break; + } +#else + break; +#endif + case ISPASYNC_CHANGE_NOTIFY: + printf("%s: Name Server Database Changed\n", isp->isp_name); + break; + default: + break; + } + (void) splx(s); + return (0); +} diff --git a/sys/dev/ic/isp_openbsd.h b/sys/dev/ic/isp_openbsd.h new file mode 100644 index 00000000000..03e1d29cffa --- /dev/null +++ b/sys/dev/ic/isp_openbsd.h @@ -0,0 +1,274 @@ +/* $OpenBSD: isp_openbsd.h,v 1.1 1999/03/17 05:26:09 mjacob Exp $ */ +/* release_03_16_99 */ +/* + * OpenBSD Specific definitions for the Qlogic ISP Host Adapter + * + *--------------------------------------- + * Copyright (c) 1997, 1998, 1999 by Matthew Jacob + * NASA/Ames Research Center + * All rights reserved. + *--------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _ISP_OPENBSD_H +#define _ISP_OPENBSD_H + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> + + +#include <scsi/scsi_all.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <scsi/scsi_message.h> +#include <scsi/scsi_debug.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> + +#define ISP_PLATFORM_VERSION_MAJOR 0 +#define ISP_PLATFORM_VERSION_MINOR 1 + +#define ISP_SCSI_XFER_T struct scsi_xfer +struct isposinfo { + struct device _dev; + struct scsi_link _link; + struct scsi_adapter _adapter; +}; + +#define MAXISPREQUEST 64 + +#include <dev/ic/ispreg.h> +#include <dev/ic/ispvar.h> +#include <dev/ic/ispmbox.h> + +#define PRINTF printf +#define IDPRINTF(lev, x) if (isp->isp_dblev >= lev) printf x + +#define MEMZERO bzero +#define MEMCPY(dst, src, count) bcopy((src), (dst), (count)) + +#ifdef __alpha__ +#define MemoryBarrier alpha_mb +#else +#define MemoryBarrier() +#endif + +#if defined(SCSIDEBUG) +#define DFLT_DBLEVEL 3 +#else +#if defined(DEBUG) +#define DFLT_DBLEVEL 2 +#else +#define DFLT_DBLEVEL 1 +#endif +#endif + +#define ISP_LOCKVAL_DECL int isp_spl_save +#define ISP_ILOCKVAL_DECL ISP_LOCKVAL_DECL +#define ISP_LOCK(x) isp_spl_save = splbio() +#define ISP_UNLOCK(x) (void) splx(isp_spl_save) +#define ISP_ILOCK ISP_LOCK +#define ISP_IUNLOCK ISP_UNLOCK + + +#define XS_NULL(xs) xs == NULL || xs->sc_link == NULL +#define XS_ISP(xs) (xs)->sc_link->adapter_softc +#define XS_LUN(xs) ((int) (xs)->sc_link->lun) +#define XS_TGT(xs) ((int) (xs)->sc_link->target) +#define XS_RESID(xs) (xs)->resid +#define XS_XFRLEN(xs) (xs)->datalen +#define XS_CDBLEN(xs) (xs)->cmdlen +#define XS_CDBP(xs) (xs)->cmd +#define XS_STS(xs) (xs)->status +#define XS_TIME(xs) (xs)->timeout +#define XS_SNSP(xs) (&(xs)->sense) +#define XS_SNSLEN(xs) (sizeof (xs)->sense) +#define XS_SNSKEY(xs) ((xs)->sense.flags) + +#define HBA_NOERROR XS_NOERROR +#define HBA_BOTCH XS_DRIVER_STUFFUP +#define HBA_CMDTIMEOUT XS_TIMEOUT +#define HBA_SELTIMEOUT XS_SELTIMEOUT +#define HBA_TGTBSY XS_BUSY +#ifdef XS_RESET +#define HBA_BUSRESET XS_RESET +#else +#define HBA_BUSRESET XS_DRIVER_STUFFUP +#endif +#define HBA_ABORTED XS_DRIVER_STUFFUP +#define HBA_DATAOVR XS_DRIVER_STUFFUP +#define HBA_ARQFAIL XS_DRIVER_STUFFUP + +#define XS_SNS_IS_VALID(xs) (xs)->error = XS_SENSE +#define XS_IS_SNS_VALID(xs) ((xs)->error == XS_SENSE) + +#define XS_INITERR(xs) (xs)->error = 0 +#define XS_SETERR(xs, v) (xs)->error = v +#define XS_ERR(xs) (xs)->error +#define XS_NOERR(xs) (xs)->error == XS_NOERROR + +#define XS_CMD_DONE(xs) (xs)->flags |= ITSDONE, scsi_done(xs) +#define XS_IS_CMD_DONE(xs) (((xs)->flags & ITSDONE) != 0) + +/* + * We use whether or not we're a polled command to decide about tagging. + */ +#define XS_CANTAG(xs) (((xs)->flags & SCSI_POLL) != 0) + +/* + * This is our default tag (ordered). + */ +#define XS_KINDOF_TAG(xs) REQFLAG_STAG + +#define CMD_COMPLETE COMPLETE +#define CMD_EAGAIN TRY_AGAIN_LATER +#define CMD_QUEUED SUCCESSFULLY_QUEUED + +#define isp_name isp_osinfo._dev.dv_xname + +#define FC_FW_READY_DELAY (12 * 1000000) + +#define SYS_DELAY(x) delay(x) + +#define WATCH_INTERVAL 30 + +extern void isp_attach __P((struct ispsoftc *)); +extern void isp_uninit __P((struct ispsoftc *)); + +static inline void isp_prtstst __P((ispstatusreq_t *)); +static inline const char *isp2100_fw_statename __P((int)); +static inline const char * isp2100_pdb_statename __P((int)); + +static inline void +isp_prtstst(sp) + ispstatusreq_t *sp; +{ + char buf[128]; + sprintf(buf, "states->"); + if (sp->req_state_flags & RQSF_GOT_BUS) + sprintf(buf, "%s%s", buf, "GOT_BUS "); + if (sp->req_state_flags & RQSF_GOT_TARGET) + sprintf(buf, "%s%s", buf, "GOT_TGT "); + if (sp->req_state_flags & RQSF_SENT_CDB) + sprintf(buf, "%s%s", buf, "SENT_CDB "); + if (sp->req_state_flags & RQSF_XFRD_DATA) + sprintf(buf, "%s%s", buf, "XFRD_DATA "); + if (sp->req_state_flags & RQSF_GOT_STATUS) + sprintf(buf, "%s%s", buf, "GOT_STS "); + if (sp->req_state_flags & RQSF_GOT_SENSE) + sprintf(buf, "%s%s", buf, "GOT_SNS "); + if (sp->req_state_flags & RQSF_XFER_COMPLETE) + sprintf(buf, "%s%s", buf, "XFR_CMPLT "); + sprintf(buf, "%s%s", buf, "\n"); + sprintf(buf, "%s%s", buf, "status->"); + if (sp->req_status_flags & RQSTF_DISCONNECT) + sprintf(buf, "%s%s", buf, "Disconnect "); + if (sp->req_status_flags & RQSTF_SYNCHRONOUS) + sprintf(buf, "%s%s", buf, "Sync_xfr "); + if (sp->req_status_flags & RQSTF_PARITY_ERROR) + sprintf(buf, "%s%s", buf, "Parity "); + if (sp->req_status_flags & RQSTF_BUS_RESET) + sprintf(buf, "%s%s", buf, "Bus_Reset "); + if (sp->req_status_flags & RQSTF_DEVICE_RESET) + sprintf(buf, "%s%s", buf, "Device_Reset "); + if (sp->req_status_flags & RQSTF_ABORTED) + sprintf(buf, "%s%s", buf, "Aborted "); + if (sp->req_status_flags & RQSTF_TIMEOUT) + sprintf(buf, "%s%s", buf, "Timeout "); + if (sp->req_status_flags & RQSTF_NEGOTIATION) + sprintf(buf, "%s%s", buf, "Negotiation "); + sprintf(buf, "%s%s", buf, "\n"); + printf(buf); +} + +static inline const char * +isp2100_fw_statename(state) + int state; +{ + static char buf[16]; + switch(state) { + case FW_CONFIG_WAIT: return "Config Wait"; + case FW_WAIT_AL_PA: return "Waiting for AL_PA"; + case FW_WAIT_LOGIN: return "Wait Login"; + case FW_READY: return "Ready"; + case FW_LOSS_OF_SYNC: return "Loss Of Sync"; + case FW_ERROR: return "Error"; + case FW_REINIT: return "Re-Init"; + case FW_NON_PART: return "Non-Participating"; + default: + sprintf(buf, "0x%x", state); + return buf; + } +} + +static inline const char * +isp2100_pdb_statename(pdb_state) + int pdb_state; +{ + static char buf[16]; + switch(pdb_state) { + case PDB_STATE_DISCOVERY: return "Port Discovery"; + case PDB_STATE_WDISC_ACK: return "Waiting Port Discovery ACK"; + case PDB_STATE_PLOGI: return "Port Login"; + case PDB_STATE_PLOGI_ACK: return "Wait Port Login ACK"; + case PDB_STATE_PRLI: return "Process Login"; + case PDB_STATE_PRLI_ACK: return "Wait Process Login ACK"; + case PDB_STATE_LOGGED_IN: return "Logged In"; + case PDB_STATE_PORT_UNAVAIL: return "Port Unavailable"; + case PDB_STATE_PRLO: return "Process Logout"; + case PDB_STATE_PRLO_ACK: return "Wait Process Logout ACK"; + case PDB_STATE_PLOGO: return "Port Logout"; + case PDB_STATE_PLOG_ACK: return "Wait Port Logout ACK"; + default: + sprintf(buf, "0x%x", pdb_state); + return buf; + } +} + +/* + * Keep these off for now... + */ + +#define ISP_NO_FASTPOST_SCSI 1 +#define ISP_NO_FASTPOST_FC 1 + +#define ISP_DISABLE_1080_SUPPORT 1 + +#endif /* _ISP_OPENBSD_H */ diff --git a/sys/dev/ic/ispmbox.h b/sys/dev/ic/ispmbox.h index 0905106f742..950f7dadca5 100644 --- a/sys/dev/ic/ispmbox.h +++ b/sys/dev/ic/ispmbox.h @@ -1,11 +1,13 @@ -/* $NetBSD: ispmbox.h,v 1.1.1.1 1997/03/12 20:44:51 cgd Exp $ */ - +/* $OpenBSD: ispmbox.h,v 1.2 1999/03/17 05:26:09 mjacob Exp $ */ +/* release_03_16_99 */ /* - * Mailbox and Command Definitions for for Qlogic ISP SCSI adapters. + * Mailbox and Queue Entry Definitions for for Qlogic ISP SCSI adapters. * - * Copyright (c) 1997 by Matthew Jacob + *--------------------------------------- + * Copyright (c) 1997, 1998 by Matthew Jacob * NASA/Ames Research Center * All rights reserved. + *--------------------------------------- * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,8 +32,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * */ - #ifndef _ISPMBOX_H #define _ISPMBOX_H @@ -95,6 +97,7 @@ #define MBOX_SET_ACTIVE_NEG_STATE 0x0035 #define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036 #define MBOX_SET_SBUS_CONTROL_PARAMS 0x0037 +#define MBOX_SET_PCI_PARAMETERS 0x0037 #define MBOX_SET_TARGET_PARAMS 0x0038 #define MBOX_SET_DEV_QUEUE_PARAMS 0x0039 /* 3a */ @@ -106,14 +109,74 @@ #define MBOX_RETURN_BIOS_BLOCK_ADDR 0x0040 #define MBOX_WRITE_FOUR_RAM_WORDS 0x0041 #define MBOX_EXEC_BIOS_IOCB 0x0042 +#define MBOX_SET_FW_FEATURES 0x004a +#define MBOX_GET_FW_FEATURES 0x004b +#define FW_FEATURE_LVD_NOTIFY 0x2 +#define FW_FEATURE_FAST_POST 0x1 + +/* These are for the ISP2100 FC cards */ +#define MBOX_GET_LOOP_ID 0x20 +#define MBOX_EXEC_COMMAND_IOCB_A64 0x54 +#define MBOX_INIT_FIRMWARE 0x60 +#define MBOX_GET_INIT_CONTROL_BLOCK 0x61 +#define MBOX_INIT_LIP 0x62 +#define MBOX_GET_FC_AL_POSITION_MAP 0x63 +#define MBOX_GET_PORT_DB 0x64 +#define MBOX_CLEAR_ACA 0x65 +#define MBOX_TARGET_RESET 0x66 +#define MBOX_CLEAR_TASK_SET 0x67 +#define MBOX_ABORT_TASK_SET 0x68 +#define MBOX_GET_FW_STATE 0x69 +#define MBOX_GET_PORT_NAME 0x6a +#define MBOX_GET_LINK_STATUS 0x6b +#define MBOX_INIT_LIP_RESET 0x6c +#define MBOX_INIT_LIP_LOGIN 0x72 + +#define ISP2100_SET_PCI_PARAM 0x00ff #define MBOX_BUSY 0x04 typedef struct { - u_int16_t param[6]; + u_int16_t param[8]; } mbreg_t; /* + * Mailbox Command Complete Status Codes + */ +#define MBOX_COMMAND_COMPLETE 0x4000 +#define MBOX_INVALID_COMMAND 0x4001 +#define MBOX_HOST_INTERFACE_ERROR 0x4002 +#define MBOX_TEST_FAILED 0x4003 +#define MBOX_COMMAND_ERROR 0x4005 +#define MBOX_COMMAND_PARAM_ERROR 0x4006 + +/* + * Asynchronous event status codes + */ +#define ASYNC_BUS_RESET 0x8001 +#define ASYNC_SYSTEM_ERROR 0x8002 +#define ASYNC_RQS_XFER_ERR 0x8003 +#define ASYNC_RSP_XFER_ERR 0x8004 +#define ASYNC_QWAKEUP 0x8005 +#define ASYNC_TIMEOUT_RESET 0x8006 +#define ASYNC_DEVICE_RESET 0x8007 +#define ASYNC_EXTMSG_UNDERRUN 0x800A +#define ASYNC_SCAM_INT 0x800B +#define ASYNC_HUNG_SCSI 0x800C +#define ASYNC_KILLED_BUS 0x800D +#define ASYNC_BUS_TRANSIT 0x800E /* LVD -> HVD, eg. */ +#define ASYNC_CMD_CMPLT 0x8020 +#define ASYNC_CTIO_DONE 0x8021 + +/* for ISP2100 only */ +#define ASYNC_LIP_OCCURRED 0x8010 +#define ASYNC_LOOP_UP 0x8011 +#define ASYNC_LOOP_DOWN 0x8012 +#define ASYNC_LOOP_RESET 0x8013 +#define ASYNC_PDB_CHANGED 0x8014 +#define ASYNC_CHANGE_NOTIFY 0x8015 + +/* * Command Structure Definitions */ @@ -141,12 +204,33 @@ typedef struct { #define RQSFLAG_FULL 0x02 #define RQSFLAG_BADHEADER 0x04 #define RQSFLAG_BADPACKET 0x08 + /* RQS entry_type definitions */ -#define RQSTYPE_REQUEST 1 -#define RQSTYPE_DATASEG 2 -#define RQSTYPE_RESPONSE 3 -#define RQSTYPE_MARKER 4 -#define RQSTYPE_CMDONLY 5 +#define RQSTYPE_REQUEST 0x01 +#define RQSTYPE_DATASEG 0x02 +#define RQSTYPE_RESPONSE 0x03 +#define RQSTYPE_MARKER 0x04 +#define RQSTYPE_CMDONLY 0x05 +#define RQSTYPE_ATIO 0x06 /* Target Mode */ +#define RQSTYPE_CTIO0 0x07 /* Target Mode */ +#define RQSTYPE_SCAM 0x08 +#define RQSTYPE_A64 0x09 +#define RQSTYPE_A64_CONT 0x0a +#define RQSTYPE_ENABLE_LUN 0x0b /* Target Mode */ +#define RQSTYPE_MODIFY_LUN 0x0c /* Target Mode */ +#define RQSTYPE_NOTIFY 0x0d /* Target Mode */ +#define RQSTYPE_NOTIFY_ACK 0x0e /* Target Mode */ +#define RQSTYPE_CTIO1 0x0f /* Target Mode */ +#define RQSTYPE_STATUS_CONT 0x10 +#define RQSTYPE_T2RQS 0x11 + +#define RQSTYPE_T4RQS 0x15 +#define RQSTYPE_ATIO2 0x16 +#define RQSTYPE_CTIO2 0x17 +#define RQSTYPE_CSET0 0x18 +#define RQSTYPE_T3RQS 0x19 + +#define RQSTYPE_CTIO3 0x1f #define ISP_RQDSEG 4 @@ -163,13 +247,34 @@ typedef struct { u_int16_t req_cdblen; #define req_modifier req_cdblen /* marker packet */ u_int16_t req_flags; - u_int16_t _res1; + u_int16_t req_reserved; u_int16_t req_time; u_int16_t req_seg_count; u_int8_t req_cdb[12]; ispds_t req_dataseg[ISP_RQDSEG]; } ispreq_t; +#define ISP_RQDSEG_T2 3 +typedef struct { + isphdr_t req_header; + u_int32_t req_handle; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t req_target; + u_int8_t req_lun_trn; +#else + u_int8_t req_lun_trn; + u_int8_t req_target; +#endif + u_int16_t req_scclun; + u_int16_t req_flags; + u_int16_t _res2; + u_int16_t req_time; + u_int16_t req_seg_count; + u_int32_t req_cdb[4]; + u_int32_t req_totalcnt; + ispds_t req_dataseg[ISP_RQDSEG_T2]; +} ispreqt2_t; + /* req_flag values */ #define REQFLAG_NODISCON 0x0001 #define REQFLAG_HTAG 0x0002 @@ -183,6 +288,13 @@ typedef struct { #define REQFLAG_DATA_UNKNOWN 0x0060 #define REQFLAG_DISARQ 0x0100 +#define REQFLAG_FRC_ASYNC 0x0200 +#define REQFLAG_FRC_SYNC 0x0400 +#define REQFLAG_FRC_WIDE 0x0800 +#define REQFLAG_NOPARITY 0x1000 +#define REQFLAG_STOPQ 0x2000 +#define REQFLAG_XTRASNS 0x4000 +#define REQFLAG_PRIORITY 0x8000 typedef struct { isphdr_t req_header; @@ -223,7 +335,7 @@ typedef struct { u_int8_t req_modifier; u_int8_t _res2; #endif -} ipsmarkreq_t; +} ispmarkreq_t; #define SYNC_DEVICE 0 #define SYNC_TARGET 1 @@ -243,6 +355,18 @@ typedef struct { u_int8_t req_sense_data[32]; } ispstatusreq_t; +/* + * For Qlogic 2100, the high order byte of SCSI status has + * additional meaning. + */ +#define RQCS_RU 0x800 /* Residual Under */ +#define RQCS_RO 0x400 /* Residual Over */ +#define RQCS_SV 0x200 /* Sense Length Valid */ +#define RQCS_RV 0x100 /* Residual Valid */ + +/* + * Completion Status Codes. + */ #define RQCS_COMPLETE 0x0000 #define RQCS_INCOMPLETE 0x0001 #define RQCS_DMA_ERROR 0x0002 @@ -265,15 +389,37 @@ typedef struct { #define RQCS_ID_MSG_FAILED 0x0013 #define RQCS_UNEXP_BUS_FREE 0x0014 #define RQCS_DATA_UNDERRUN 0x0015 +#define RQCS_XACT_ERR1 0x0018 +#define RQCS_XACT_ERR2 0x0019 +#define RQCS_XACT_ERR3 0x001A +#define RQCS_BAD_ENTRY 0x001B +#define RQCS_QUEUE_FULL 0x001C +#define RQCS_PHASE_SKIPPED 0x001D +#define RQCS_ARQS_FAILED 0x001E +#define RQCS_WIDE_FAILED 0x001F +#define RQCS_SYNCXFER_FAILED 0x0020 +#define RQCS_LVD_BUSERR 0x0021 +/* 2100 Only Completion Codes */ +#define RQCS_PORT_UNAVAILABLE 0x0028 +#define RQCS_PORT_LOGGED_OUT 0x0029 +#define RQCS_PORT_CHANGED 0x002A +#define RQCS_PORT_BUSY 0x002B +/* + * State Flags (not applicable to 2100) + */ #define RQSF_GOT_BUS 0x0100 #define RQSF_GOT_TARGET 0x0200 #define RQSF_SENT_CDB 0x0400 -#define RQSF_TRANFERRED_DATA 0x0800 +#define RQSF_XFRD_DATA 0x0800 #define RQSF_GOT_STATUS 0x1000 #define RQSF_GOT_SENSE 0x2000 +#define RQSF_XFER_COMPLETE 0x4000 +/* + * Status Flags (not applicable to 2100) + */ #define RQSTF_DISCONNECT 0x0001 #define RQSTF_SYNCHRONOUS 0x0002 #define RQSTF_PARITY_ERROR 0x0004 @@ -283,4 +429,609 @@ typedef struct { #define RQSTF_TIMEOUT 0x0040 #define RQSTF_NEGOTIATION 0x0080 +/* + * FC (ISP2100) specific data structures + */ + +/* + * Initialization Control Block + * + * Version One format. + */ +typedef struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t _reserved0; + u_int8_t icb_version; +#else + u_int8_t icb_version; + u_int8_t _reserved0; +#endif + u_int16_t icb_fwoptions; + u_int16_t icb_maxfrmlen; + u_int16_t icb_maxalloc; + u_int16_t icb_execthrottle; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t icb_retry_delay; + u_int8_t icb_retry_count; +#else + u_int8_t icb_retry_count; + u_int8_t icb_retry_delay; +#endif + u_int8_t icb_nodename[8]; + u_int16_t icb_hardaddr; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t _reserved1; + u_int8_t icb_iqdevtype; +#else + u_int8_t icb_iqdevtype; + u_int8_t _reserved1; +#endif + u_int8_t icb_portname[8]; + u_int16_t icb_rqstout; + u_int16_t icb_rspnsin; + u_int16_t icb_rqstqlen; + u_int16_t icb_rsltqlen; + u_int16_t icb_rqstaddr[4]; + u_int16_t icb_respaddr[4]; +} isp_icb_t; +#define ICB_VERSION1 1 + +#define ICBOPT_HARD_ADDRESS (1<<0) +#define ICBOPT_FAIRNESS (1<<1) +#define ICBOPT_FULL_DUPLEX (1<<2) +#define ICBOPT_FAST_POST (1<<3) +#define ICBOPT_TGT_ENABLE (1<<4) +#define ICBOPT_INI_DISABLE (1<<5) +#define ICBOPT_INI_ADISC (1<<6) +#define ICBOPT_INI_TGTTYPE (1<<7) +#define ICBOPT_PDBCHANGE_AE (1<<8) +#define ICBOPT_NOLIP (1<<9) +#define ICBOPT_SRCHDOWN (1<<10) +#define ICBOPT_PREVLOOP (1<<11) +#define ICBOPT_STOP_ON_QFULL (1<<12) +#define ICBOPT_FULL_LOGIN (1<<13) +#define ICBOPT_USE_PORTNAME (1<<14) + + +#define ICB_MIN_FRMLEN 256 +#define ICB_MAX_FRMLEN 2112 +#define ICB_DFLT_FRMLEN 1024 + +#define RQRSP_ADDR0015 0 +#define RQRSP_ADDR1631 1 +#define RQRSP_ADDR3247 2 +#define RQRSP_ADDR4863 3 + + +#define ICB_NNM0 7 +#define ICB_NNM1 6 +#define ICB_NNM2 5 +#define ICB_NNM3 4 +#define ICB_NNM4 3 +#define ICB_NNM5 2 +#define ICB_NNM6 1 +#define ICB_NNM7 0 + +#define MAKE_NODE_NAME_FROM_WWN(array, wwn) \ + array[ICB_NNM0] = (u_int8_t) ((wwn >> 0) & 0xff), \ + array[ICB_NNM1] = (u_int8_t) ((wwn >> 8) & 0xff), \ + array[ICB_NNM2] = (u_int8_t) ((wwn >> 16) & 0xff), \ + array[ICB_NNM3] = (u_int8_t) ((wwn >> 24) & 0xff), \ + array[ICB_NNM4] = (u_int8_t) ((wwn >> 32) & 0xff), \ + array[ICB_NNM5] = (u_int8_t) ((wwn >> 40) & 0xff), \ + array[ICB_NNM6] = (u_int8_t) ((wwn >> 48) & 0xff), \ + array[ICB_NNM7] = (u_int8_t) ((wwn >> 56) & 0xff) + +/* + * Port Data Base Element + */ + +typedef struct { + u_int16_t pdb_options; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t pdb_sstate; + u_int8_t pdb_mstate; +#else + u_int8_t pdb_mstate; + u_int8_t pdb_sstate; +#endif +#if BYTE_ORDER == BIG_ENDIAN +#define BITS2WORD(x) \ + (x)[1] << 16 | (x)[2] << 8 | (x)[3] +#else +#define BITS2WORD(x) \ + (x)[0] << 16 | (x)[3] << 8 | (x)[2] +#endif + u_int8_t pdb_hardaddr_bits[4]; + u_int8_t pdb_portid_bits[4]; + u_int8_t pdb_nodename[8]; + u_int8_t pdb_portname[8]; + u_int16_t pdb_execthrottle; + u_int16_t pdb_exec_count; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t pdb_retry_delay; + u_int8_t pdb_retry_count; +#else + u_int8_t pdb_retry_count; + u_int8_t pdb_retry_delay; +#endif + u_int16_t pdb_resalloc; + u_int16_t pdb_curalloc; + u_int16_t pdb_qhead; + u_int16_t pdb_qtail; + u_int16_t pdb_tl_next; + u_int16_t pdb_tl_last; + u_int16_t pdb_features; /* PLOGI, Common Service */ + u_int16_t pdb_pconcurrnt; /* PLOGI, Common Service */ + u_int16_t pdb_roi; /* PLOGI, Common Service */ +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t pdb_initiator; /* PLOGI, Class 3 Control Flags */ + u_int8_t pdb_target; +#else + u_int8_t pdb_target; + u_int8_t pdb_initiator; /* PLOGI, Class 3 Control Flags */ +#endif + u_int16_t pdb_rdsiz; /* PLOGI, Class 3 */ + u_int16_t pdb_ncseq; /* PLOGI, Class 3 */ + u_int16_t pdb_noseq; /* PLOGI, Class 3 */ + u_int16_t pdb_labrtflg; + u_int16_t pdb_lstopflg; + u_int16_t pdb_sqhead; + u_int16_t pdb_sqtail; + u_int16_t pdb_ptimer; + u_int16_t pdb_nxt_seqid; + u_int16_t pdb_fcount; + u_int16_t pdb_prli_len; + u_int16_t pdb_prli_svc0; + u_int16_t pdb_prli_svc3; + u_int16_t pdb_loopid; + u_int16_t pdb_il_ptr; + u_int16_t pdb_sl_ptr; +} isp_pdb_t; + +#define INVALID_PDB_OPTIONS 0xDEAD + +#define PDB_OPTIONS_XMITTING (1<<11) +#define PDB_OPTIONS_LNKXMIT (1<<10) +#define PDB_OPTIONS_ABORTED (1<<9) +#define PDB_OPTIONS_ADISC (1<<1) + +#define PDB_STATE_DISCOVERY 0 +#define PDB_STATE_WDISC_ACK 1 +#define PDB_STATE_PLOGI 2 +#define PDB_STATE_PLOGI_ACK 3 +#define PDB_STATE_PRLI 4 +#define PDB_STATE_PRLI_ACK 5 +#define PDB_STATE_LOGGED_IN 6 +#define PDB_STATE_PORT_UNAVAIL 7 +#define PDB_STATE_PRLO 8 +#define PDB_STATE_PRLO_ACK 9 +#define PDB_STATE_PLOGO 10 +#define PDB_STATE_PLOG_ACK 11 + +#define SVC3_TGT_ROLE 0x10 +#define SVC3_INI_ROLE 0x20 +#define SVC3_ROLE_MASK 0x30 + +/* + * Target Mode Structures + */ +#define TGTSVALID 0x80 /* scsi status & sense data valid */ +#define SUGGSENSELEN 18 + +/* + * Structure for Enable Lun and Modify Lun queue entries + */ +typedef struct { + isphdr_t le_header; + u_int32_t le_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t le_lun; + u_int8_t le_rsvd; + u_int8_t le_ops; /* Modify LUN only */ + u_int8_t le_tgt; /* Not for FC */ +#endif + u_int32_t le_flags; /* Not for FC */ +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t le_status; + u_int8_t le_rsvd2; + u_int8_t le_cmd_count; + u_int8_t le_in_count; + u_int8_t le_cdb6len; /* Not for FC */ + u_int8_t le_cdb7len; /* Not for FC */ +#endif + u_int16_t le_timeout; + u_int16_t le_reserved[20]; +} lun_entry_t; + +/* + * le_flags values + */ +#define LUN_TQAE 0x00000001 /* Tagged Queue Action Enable */ +#define LUN_DSSM 0x01000000 /* Disable Sending SDP Message */ +#define LUN_DM 0x40000000 /* Disconnects Mandatory */ + +/* + * le_ops values + */ +#define LUN_CCINCR 0x01 /* increment command count */ +#define LUN_CCDECR 0x02 /* decrement command count */ +#define LUN_ININCR 0x40 /* increment immed. notify count */ +#define LUN_INDECR 0x80 /* decrement immed. notify count */ + +/* + * le_status values + */ +#define LUN_ERR 0x04 /* request completed with error */ +#define LUN_INVAL 0x06 /* invalid request */ +#define LUN_NOCAP 0x16 /* can't provide requested capability */ +#define LUN_ENABLED 0x3E /* LUN already enabled */ + +/* + * Immediate Notify Entry structure + */ +#define IN_MSGLEN 8 /* 8 bytes */ +#define IN_RSVDLEN 8 /* 8 words */ +typedef struct { + isphdr_t in_header; + u_int32_t in_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t in_lun; /* lun */ + u_int8_t in_iid; /* initiator */ + u_int8_t in_rsvd; + u_int8_t in_tgt; /* target */ +#endif + u_int32_t in_flags; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t in_status; + u_int8_t in_rsvd2; + u_int8_t in_tag_val; /* tag value */ + u_int8_t in_tag_type; /* tag type */ +#endif + u_int16_t in_seqid; /* sequence id */ + u_int8_t in_msg[IN_MSGLEN]; /* SCSI message bytes */ + u_int16_t in_reserved[IN_RSVDLEN]; + u_int8_t in_sense[SUGGSENSELEN]; /* suggested sense data */ +} in_entry_t; + +typedef struct { + isphdr_t in_header; + u_int32_t in_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t in_lun; /* lun */ + u_int8_t in_iid; /* initiator */ +#endif + u_int16_t in_rsvd; + u_int32_t in_rsvd2; + u_int16_t in_status; + u_int16_t in_task_flags; + u_int16_t in_seqid; /* sequence id */ +} in_fcentry_t; + +/* + * Values for the in_status field + */ +#define IN_NO_RCAP 0x16 /* requested capability not available */ +#define IN_IDE_RECEIVED 0x33 /* Initiator Detected Error msg received */ +#define IN_RSRC_UNAVAIL 0x34 /* resource unavailable */ +#define IN_MSG_RECEIVED 0x36 /* SCSI message received */ +#define IN_PORT_LOGOUT 0x29 /* port has logged out (FC) */ +#define IN_ABORT_TASK 0x20 /* task named in RX_ID is being aborted (FC) */ + +/* + * Notify Acknowledge Entry structure + */ +#define NA_RSVDLEN 22 +typedef struct { + isphdr_t na_header; + u_int32_t na_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t na_lun; /* lun */ + u_int8_t na_iid; /* initiator */ + u_int8_t na_rsvd; + u_int8_t na_tgt; /* target */ +#endif + u_int32_t na_flags; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t na_status; + u_int8_t na_event; +#endif + u_int16_t na_seqid; /* sequence id */ + u_int16_t na_reserved[NA_RSVDLEN]; +} na_entry_t; + +/* + * Value for the na_event field + */ +#define NA_RST_CLRD 0x80 /* Clear an async event notification */ + +#define NA2_RSVDLEN 21 +typedef struct { + isphdr_t na_header; + u_int32_t na_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t na_lun; /* lun */ + u_int8_t na_iid; /* initiator */ +#endif + u_int16_t na_rsvd; + u_int16_t na_flags; + u_int16_t na_rsvd2; + u_int16_t na_status; + u_int16_t na_task_flags; + u_int16_t na_seqid; /* sequence id */ + u_int16_t na_reserved[NA2_RSVDLEN]; +} na_fcentry_t; +#define NAFC_RST_CLRD 0x40 + +/* + * Value for the na_event field + */ +#define NA_RST_CLRD 0x80 /* Clear an async event notification */ +/* + * Accept Target I/O Entry structure + */ +#define ATIO_CDBLEN 26 + +typedef struct { + isphdr_t at_header; + u_int32_t at_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t at_lun; /* lun */ + u_int8_t at_iid; /* initiator */ + u_int8_t at_cdblen; /* cdb length */ + u_int8_t at_tgt; /* target */ +#endif + u_int32_t at_flags; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t at_status; /* firmware status */ + u_int8_t at_scsi_status; /* scsi status */ + u_int8_t at_tag_val; /* tag value */ + u_int8_t at_tag_type; /* tag type */ +#endif + u_int8_t at_cdb[ATIO_CDBLEN]; /* received CDB */ + u_int8_t at_sense[SUGGSENSELEN]; /* suggested sense data */ +} at_entry_t; + +/* + * at_flags values + */ +#define AT_NODISC 0x00008000 /* disconnect disabled */ +#define AT_TQAE 0x00000001 /* Tagged Queue Action enabled */ + +/* + * at_status values + */ +#define AT_PATH_INVALID 0x07 /* ATIO sent to firmware for disabled lun */ +#define AT_PHASE_ERROR 0x14 /* Bus phase sequence error */ +#define AT_NOCAP 0x16 /* Requested capability not available */ +#define AT_BDR_MSG 0x17 /* Bus Device Reset msg received */ +#define AT_CDB 0x3D /* CDB received */ + +/* + * Accept Target I/O Entry structure, Type 2 + */ +#define ATIO2_CDBLEN 16 + +typedef struct { + isphdr_t at_header; + u_int32_t at_reserved2; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t at_lun; /* lun */ + u_int8_t at_iid; /* initiator */ +#endif + u_int16_t at_rxid; /* response ID */ + u_int16_t at_flags; + u_int16_t at_status; /* firmware status */ +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t at_reserved1; + u_int8_t at_taskcodes; + u_int8_t at_taskflags; + u_int8_t at_execodes; +#endif + u_int8_t at_cdb[ATIO2_CDBLEN]; /* received CDB */ + u_int32_t at_datalen; /* allocated data len */ + u_int16_t at_scclun; + u_int16_t at_reserved3; + u_int16_t at_scsi_status; + u_int8_t at_sense[SUGGSENSELEN]; /* suggested sense data */ +} at2_entry_t; + +#define ATIO2_TC_ATTR_MASK 0x7 +#define ATIO2_TC_ATTR_SIMPLEQ 0 +#define ATIO2_TC_ATTR_HEADOFQ 1 +#define ATIO2_TC_ATTR_ORDERED 2 +#define ATIO2_TC_ATTR_ACAQ 4 +#define ATIO2_TC_ATTR_UNTAGGED 5 +#define TC2TT(code) \ + (((code) == ATIO2_TC_ATTR_SIMPLEQ)? 0x20 : \ + (((code) == ATIO2_TC_ATTR_HEADOFQ)? 0x21 : \ + (((code) == ATIO2_TC_ATTR_ORDERED)? 0x22 : \ + (((code) == ATIO2_TC_ATTR_ACAQ)? 0x24 : 0)))) + + +/* + * Continue Target I/O Entry structure + * Request from driver. The response from the + * ISP firmware is the same except that the last 18 + * bytes are overwritten by suggested sense data if + * the 'autosense valid' bit is set in the status byte. + */ +typedef struct { + isphdr_t ct_header; + u_int32_t ct_reserved; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t ct_lun; /* lun */ + u_int8_t ct_iid; /* initiator id */ + u_int8_t ct_rsvd; + u_int8_t ct_tgt; /* our target id */ +#endif + u_int32_t ct_flags; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t ct_status; /* isp status */ + u_int8_t ct_scsi_status; /* scsi status */ + u_int8_t ct_tag_val; /* tag value */ + u_int8_t ct_tag_type; /* tag type */ +#endif + u_int32_t ct_xfrlen; /* transfer length */ + u_int32_t ct_resid; /* residual length */ + u_int16_t ct_timeout; + u_int16_t ct_seg_count; + ispds_t ct_dataseg[ISP_RQDSEG]; +} ct_entry_t; + +/* + * ct_flags values + */ +#define CT_TQAE 0x00000001 /* Tagged Queue Action enable */ +#define CT_DATA_IN 0x00000040 /* Data direction */ +#define CT_DATA_OUT 0x00000080 /* Data direction */ +#define CT_NO_DATA 0x000000C0 /* Data direction */ +#define CT_DATAMASK 0x000000C0 /* Data direction */ +#define CT_NODISC 0x00008000 /* Disconnects disabled */ +#define CT_DSDP 0x01000000 /* Disable Save Data Pointers */ +#define CT_SENDRDP 0x04000000 /* Send Restore Pointers msg */ +#define CT_SENDSTATUS 0x80000000 /* Send SCSI status byte */ + +/* + * ct_status values + * - set by the firmware when it returns the CTIO + */ +#define CT_OK 0x01 /* completed without error */ +#define CT_ABORTED 0x02 /* aborted by host */ +#define CT_ERR 0x04 /* see sense data for error */ +#define CT_INVAL 0x06 /* request for disabled lun */ +#define CT_NOPATH 0x07 /* invalid ITL nexus */ +#define CT_INVRXID 0x08 /* (FC only) Invalid RX_ID */ +#define CT_RSELTMO 0x0A /* reselection timeout after 2 tries */ +#define CT_TIMEOUT 0x0B /* timed out */ +#define CT_RESET 0x0E /* SCSI Bus Reset occurred */ +#define CT_PHASE_ERROR 0x14 /* Bus phase sequence error */ +#define CT_BDR_MSG 0x17 /* Bus Device Reset msg received */ +#define CT_TERMINATED 0x19 /* due to Terminate Transfer mbox cmd */ +#define CT_LOGOUT 0x29 /* port logout not acknowledged yet */ +#define CT_NOACK 0x35 /* Outstanding Immed. Notify. entry */ + +/* + * When the firmware returns a CTIO entry, it may overwrite the last + * part of the structure with sense data. This starts at offset 0x2E + * into the entry, which is in the middle of ct_dataseg[1]. Rather + * than define a new struct for this, I'm just using the sense data + * offset. + */ +#define CTIO_SENSE_OFFSET 0x2E + +/* + * Entry length in u_longs. All entries are the same size so + * any one will do as the numerator. + */ +#define UINT32_ENTRY_SIZE (sizeof(at_entry_t)/sizeof(u_int32_t)) + +/* + * QLA2100 CTIO (type 2) entry + */ +#define MAXRESPLEN 26 +typedef struct { + isphdr_t ct_header; + u_int32_t ct_reserved; +#if BYTE_ORDER == BIG_ENDIAN +#else + u_int8_t ct_lun; /* lun */ + u_int8_t ct_iid; /* initiator id */ +#endif + u_int16_t ct_rxid; /* response ID */ + u_int16_t ct_flags; + u_int16_t ct_status; /* isp status */ + u_int16_t ct_timeout; + u_int16_t ct_seg_count; + u_int32_t ct_reloff; /* relative offset */ + u_int32_t ct_resid; /* residual length */ + union { + /* + * The three different modes that the target driver + * can set the CTIO2 up as. + * + * The first is for sending FCP_DATA_IUs as well as + * (optionally) sending a terminal SCSI status FCP_RSP_IU. + * + * The second is for sending SCSI sense data in an FCP_RSP_IU. + * Note that no FCP_DATA_IUs will be sent. + * + * The third is for sending FCP_RSP_IUs as built specifically + * in system memory as located by the isp_dataseg. + */ + struct { + u_int32_t _reserved; + u_int16_t _reserved2; + u_int16_t ct_scsi_status; + u_int32_t ct_xfrlen; + ispds_t ct_dataseg[ISP_RQDSEG_T2]; + } m0; + struct { + u_int16_t _reserved; + u_int16_t _reserved2; + u_int16_t ct_senselen; + u_int16_t ct_scsi_status; + u_int16_t ct_resplen; + u_int8_t ct_resp[MAXRESPLEN]; + } m1; + struct { + u_int32_t _reserved; + u_int16_t _reserved2; + u_int16_t _reserved3; + u_int32_t ct_datalen; + ispds_t ct_fcp_rsp_iudata; + } m2; + /* + * CTIO2 returned from F/W... + */ + struct { + u_int32_t _reserved[4]; + u_int16_t ct_scsi_status; + u_int8_t ct_sense[SUGGSENSELEN]; + } fw; + } rsp; +} ct2_entry_t; +/* + * ct_flags values for CTIO2 + */ +#define CT2_FLAG_MMASK 0x0003 +#define CT2_FLAG_MODE0 0x0000 +#define CT2_FLAG_MODE1 0x0001 +#define CT2_FLAG_MODE2 0x0002 +#define CT2_DATA_IN CT_DATA_IN +#define CT2_DATA_OUT CT_DATA_OUT +#define CT2_NO_DATA CT_NO_DATA +#define CT2_DATAMASK CT_DATA_MASK +#define CT2_CCINCR 0x0100 +#define CT2_FASTPOST 0x0200 +#define CT2_SENDSTATUS 0x8000 + +/* + * ct_status values are (mostly) the same as that for ct_entry. + */ + +/* + * ct_scsi_status values- the low 8 bits are the normal SCSI status + * we know and love. The upper 8 bits are validity markers for FCP_RSP_IU + * fields. + */ +#define CT2_RSPLEN_VALID 0x0100 +#define CT2_SNSLEN_VALID 0x0200 +#define CT2_DATA_OVER 0x0400 +#define CT2_DATA_UNDER 0x0800 + #endif /* _ISPMBOX_H */ diff --git a/sys/dev/ic/ispreg.h b/sys/dev/ic/ispreg.h index 3f5a1943285..1699af8e25a 100644 --- a/sys/dev/ic/ispreg.h +++ b/sys/dev/ic/ispreg.h @@ -1,13 +1,14 @@ -/* $NetBSD: ispreg.h,v 1.1.1.1 1997/03/12 20:44:51 cgd Exp $ */ - +/* $OpenBSD: ispreg.h,v 1.2 1999/03/17 05:26:09 mjacob Exp $ */ +/* release_03_16_99 */ /* * Machine Independent (well, as best as possible) register * definitions for Qlogic ISP SCSI adapters. * + *--------------------------------------- * Copyright (c) 1997 by Matthew Jacob * NASA/Ames Research Center * All rights reserved. - * + *--------------------------------------- * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,7 +33,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #ifndef _ISPREG_H #define _ISPREG_H @@ -57,62 +57,84 @@ * Sad but true, different architectures have different offsets. */ -#define BIU_REGS_OFF 0x00 +#define BIU_REGS_OFF 0x00 -#define PCI_MBOX_REGS_OFF 0x70 +#define PCI_MBOX_REGS_OFF 0x70 +#define PCI_MBOX_REGS2100_OFF 0x10 #define SBUS_MBOX_REGS_OFF 0x80 -#define PCI_SXP_REGS_OFF 0x80 +#define PCI_SXP_REGS_OFF 0x80 #define SBUS_SXP_REGS_OFF 0x200 -#define PCI_RISC_REGS_OFF 0x80 +#define PCI_RISC_REGS_OFF 0x80 #define SBUS_RISC_REGS_OFF 0x400 +/* Bless me! Chip designers have putzed it again! */ +#define ISP1080_DMA_REGS_OFF 0x60 +#define DMA_REGS_OFF 0x00 /* same as BIU block */ + /* * NB: The *_BLOCK definitions have no specific hardware meaning. * They serve simply to note to the MD layer which block of * registers offsets are being accessed. */ +#define _NREG_BLKS 5 +#define _BLK_REG_SHFT 13 +#define _BLK_REG_MASK (7 << _BLK_REG_SHFT) +#define BIU_BLOCK (0 << _BLK_REG_SHFT) +#define MBOX_BLOCK (1 << _BLK_REG_SHFT) +#define SXP_BLOCK (2 << _BLK_REG_SHFT) +#define RISC_BLOCK (3 << _BLK_REG_SHFT) +#define DMA_BLOCK (4 << _BLK_REG_SHFT) /* * Bus Interface Block Register Offsets */ -#define BIU_BLOCK 0x0100 + #define BIU_ID_LO BIU_BLOCK+0x0 /* R : Bus ID, Low */ +#define BIU2100_FLASH_ADDR BIU_BLOCK+0x0 #define BIU_ID_HI BIU_BLOCK+0x2 /* R : Bus ID, High */ +#define BIU2100_FLASH_DATA BIU_BLOCK+0x2 #define BIU_CONF0 BIU_BLOCK+0x4 /* R : Bus Configuration #0 */ #define BIU_CONF1 BIU_BLOCK+0x6 /* R : Bus Configuration #1 */ +#define BIU2100_CSR BIU_BLOCK+0x6 #define BIU_ICR BIU_BLOCK+0x8 /* RW : Bus Interface Ctrl */ #define BIU_ISR BIU_BLOCK+0xA /* R : Bus Interface Status */ #define BIU_SEMA BIU_BLOCK+0xC /* RW : Bus Semaphore */ #define BIU_NVRAM BIU_BLOCK+0xE /* RW : Bus NVRAM */ -#define CDMA_CONF BIU_BLOCK+0x20 /* RW*: DMA Configuration */ -#define CDMA_CONTROL BIU_BLOCK+0x22 /* RW*: DMA Control */ -#define CDMA_STATUS BIU_BLOCK+0x24 /* R : DMA Status */ -#define CDMA_FIFO_STS BIU_BLOCK+0x26 /* R : DMA FIFO Status */ -#define CDMA_COUNT BIU_BLOCK+0x28 /* RW*: DMA Transfer Count */ -#define CDMA_ADDR0 BIU_BLOCK+0x2C /* RW*: DMA Address, Word 0 */ -#define CDMA_ADDR1 BIU_BLOCK+0x2E /* RW*: DMA Address, Word 1 */ -/* these are for the 1040A cards */ -#define CDMA_ADDR2 BIU_BLOCK+0x30 /* RW*: DMA Address, Word 2 */ -#define CDMA_ADDR3 BIU_BLOCK+0x32 /* RW*: DMA Address, Word 3 */ - -#define DDMA_CONF BIU_BLOCK+0x40 /* RW*: DMA Configuration */ -#define DDMA_CONTROL BIU_BLOCK+0x42 /* RW*: DMA Control */ -#define DDMA_STATUS BIU_BLOCK+0x44 /* R : DMA Status */ -#define DDMA_FIFO_STS BIU_BLOCK+0x46 /* R : DMA FIFO Status */ -#define DDMA_COUNT_LO BIU_BLOCK+0x48 /* RW*: DMA Xfer Count, Low */ -#define DDMA_COUNT_HI BIU_BLOCK+0x4A /* RW*: DMA Xfer Count, High */ -#define DDMA_ADDR0 BIU_BLOCK+0x4C /* RW*: DMA Address, Word 0 */ -#define DDMA_ADDR1 BIU_BLOCK+0x4E /* RW*: DMA Address, Word 1 */ -/* these are for the 1040A cards */ -#define DDMA_ADDR2 BIU_BLOCK+0x50 /* RW*: DMA Address, Word 2 */ -#define DDMA_ADDR3 BIU_BLOCK+0x52 /* RW*: DMA Address, Word 3 */ - #define DFIFO_COMMAND BIU_BLOCK+0x60 /* RW : Command FIFO Port */ +#define RDMA2100_CONTROL DFIFO_COMMAND #define DFIFO_DATA BIU_BLOCK+0x62 /* RW : Data FIFO Port */ /* + * Putzed DMA register layouts. + */ +#define CDMA_CONF DMA_BLOCK+0x20 /* RW*: DMA Configuration */ +#define CDMA2100_CONTROL CDMA_CONF +#define CDMA_CONTROL DMA_BLOCK+0x22 /* RW*: DMA Control */ +#define CDMA_STATUS DMA_BLOCK+0x24 /* R : DMA Status */ +#define CDMA_FIFO_STS DMA_BLOCK+0x26 /* R : DMA FIFO Status */ +#define CDMA_COUNT DMA_BLOCK+0x28 /* RW*: DMA Transfer Count */ +#define CDMA_ADDR0 DMA_BLOCK+0x2C /* RW*: DMA Address, Word 0 */ +#define CDMA_ADDR1 DMA_BLOCK+0x2E /* RW*: DMA Address, Word 1 */ +#define CDMA_ADDR2 DMA_BLOCK+0x30 /* RW*: DMA Address, Word 2 */ +#define CDMA_ADDR3 DMA_BLOCK+0x32 /* RW*: DMA Address, Word 3 */ + +#define DDMA_CONF DMA_BLOCK+0x40 /* RW*: DMA Configuration */ +#define TDMA2100_CONTROL DDMA_CONF +#define DDMA_CONTROL DMA_BLOCK+0x42 /* RW*: DMA Control */ +#define DDMA_STATUS DMA_BLOCK+0x44 /* R : DMA Status */ +#define DDMA_FIFO_STS DMA_BLOCK+0x46 /* R : DMA FIFO Status */ +#define DDMA_COUNT_LO DMA_BLOCK+0x48 /* RW*: DMA Xfer Count, Low */ +#define DDMA_COUNT_HI DMA_BLOCK+0x4A /* RW*: DMA Xfer Count, High */ +#define DDMA_ADDR0 DMA_BLOCK+0x4C /* RW*: DMA Address, Word 0 */ +#define DDMA_ADDR1 DMA_BLOCK+0x4E /* RW*: DMA Address, Word 1 */ +/* these are for the 1040A cards */ +#define DDMA_ADDR2 DMA_BLOCK+0x50 /* RW*: DMA Address, Word 2 */ +#define DDMA_ADDR3 DMA_BLOCK+0x52 /* RW*: DMA Address, Word 3 */ + + +/* * Bus Interface Block Register Definitions */ /* BUS CONFIGURATION REGISTER #0 */ @@ -134,6 +156,22 @@ #define BIU_SBUS_CONF1_BURST8 0x0008 /* Enable 8-byte bursts */ #define BIU_PCI_CONF1_SXP 0x0008 /* SXP register select */ +#define BIU_PCI1080_CONF1_SXP 0x0100 /* SXP bank select */ +#define BIU_PCI1080_CONF1_DMA 0x0300 /* DMA bank select */ + + /* ISP2100 Bus Control/Status Register */ + +#define BIU2100_ICSR_REGBSEL 0x30 /* RW: register bank select */ +#define BIU2100_RISC_REGS (0 << 4) /* RISC Regs */ +#define BIU2100_FB_REGS (1 << 4) /* FrameBuffer Regs */ +#define BIU2100_FPM0_REGS (2 << 4) /* FPM 0 Regs */ +#define BIU2100_FPM1_REGS (3 << 4) /* FPM 1 Regs */ +#define BIU2100_PCI64 0x04 /* R: 64 Bit PCI slot */ +#define BIU2100_FLASH_ENABLE 0x02 /* RW: Enable Flash RAM */ +#define BIU2100_SOFT_RESET 0x01 +/* SOFT RESET FOR ISP2100 is same bit, but in this register, not ICR */ + + /* BUS CONTROL REGISTER */ #define BIU_ICR_ENABLE_DMA_INT 0x0020 /* Enable DMA interrupts */ #define BIU_ICR_ENABLE_CDMA_INT 0x0010 /* Enable CDMA interrupts */ @@ -142,6 +180,25 @@ #define BIU_ICR_ENABLE_ALL_INTS 0x0002 /* Global enable all inter */ #define BIU_ICR_SOFT_RESET 0x0001 /* Soft Reset of ISP */ +#define BIU2100_ICR_ENABLE_ALL_INTS 0x8000 +#define BIU2100_ICR_ENA_FPM_INT 0x0020 +#define BIU2100_ICR_ENA_FB_INT 0x0010 +#define BIU2100_ICR_ENA_RISC_INT 0x0008 +#define BIU2100_ICR_ENA_CDMA_INT 0x0004 +#define BIU2100_ICR_ENABLE_RXDMA_INT 0x0002 +#define BIU2100_ICR_ENABLE_TXDMA_INT 0x0001 +#define BIU2100_ICR_DISABLE_ALL_INTS 0x0000 + +#define ENABLE_INTS(isp) (isp->isp_type & ISP_HA_SCSI)? \ + ISP_WRITE(isp, BIU_ICR, BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS) : \ + ISP_WRITE(isp, BIU_ICR, BIU2100_ICR_ENA_RISC_INT | BIU2100_ICR_ENABLE_ALL_INTS) + +#define INTS_ENABLED(isp) ((isp->isp_type & ISP_HA_SCSI)? \ + (ISP_READ(isp, BIU_ICR) & (BIU_ICR_ENABLE_RISC_INT|BIU_ICR_ENABLE_ALL_INTS)) :\ + (ISP_READ(isp, BIU_ICR) & \ + (BIU2100_ICR_ENA_RISC_INT|BIU2100_ICR_ENABLE_ALL_INTS))) + +#define DISABLE_INTS(isp) ISP_WRITE(isp, BIU_ICR, 0) /* BUS STATUS REGISTER */ #define BIU_ISR_DMA_INT 0x0020 /* DMA interrupt pending */ @@ -150,11 +207,25 @@ #define BIU_ISR_RISC_INT 0x0004 /* Risc interrupt pending */ #define BIU_ISR_IPEND 0x0002 /* Global interrupt pending */ +#define BIU2100_ISR_INT_PENDING 0x8000 /* Global interrupt pending */ +#define BIU2100_ISR_FPM_INT 0x0020 /* FPM interrupt pending */ +#define BIU2100_ISR_FB_INT 0x0010 /* FB interrupt pending */ +#define BIU2100_ISR_RISC_INT 0x0008 /* Risc interrupt pending */ +#define BIU2100_ISR_CDMA_INT 0x0004 /* CDMA interrupt pending */ +#define BIU2100_ISR_RXDMA_INT_PENDING 0x0002 /* Global interrupt pending */ +#define BIU2100_ISR_TXDMA_INT_PENDING 0x0001 /* Global interrupt pending */ + /* BUS SEMAPHORE REGISTER */ #define BIU_SEMA_STATUS 0x0002 /* Semaphore Status Bit */ #define BIU_SEMA_LOCK 0x0001 /* Semaphore Lock Bit */ +/* NVRAM SEMAPHORE REGISTER */ +#define BIU_NVRAM_CLOCK 0x0001 +#define BIU_NVRAM_SELECT 0x0002 +#define BIU_NVRAM_DATAOUT 0x0004 +#define BIU_NVRAM_DATAIN 0x0008 +#define ISP_NVRAM_READ 6 /* COMNMAND && DATA DMA CONFIGURATION REGISTER */ #define DMA_ENABLE_SXP_DMA 0x0008 /* Enable SXP to DMA Data */ @@ -176,6 +247,13 @@ #define DMA_CNTRL_RESET_INT 0x0002 /* Clear DMA interrupt */ #define DMA_CNTRL_STROBE 0x0001 /* Start DMA transfer */ +/* + * Variants of same for 2100 + */ +#define DMA_CNTRL2100_CLEAR_CHAN 0x0004 +#define DMA_CNTRL2100_RESET_INT 0x0002 + + /* DMA STATUS REGISTER */ #define DMA_SBUS_STATUS_PIPE_MASK 0x00C0 /* DMA Pipeline status mask */ @@ -232,13 +310,14 @@ * Mailbox Block Register Offsets */ -#define MBOX_BLOCK 0x0200 #define INMAILBOX0 MBOX_BLOCK+0x0 #define INMAILBOX1 MBOX_BLOCK+0x2 #define INMAILBOX2 MBOX_BLOCK+0x4 #define INMAILBOX3 MBOX_BLOCK+0x6 #define INMAILBOX4 MBOX_BLOCK+0x8 #define INMAILBOX5 MBOX_BLOCK+0xA +#define INMAILBOX6 MBOX_BLOCK+0xC +#define INMAILBOX7 MBOX_BLOCK+0xE #define OUTMAILBOX0 MBOX_BLOCK+0x0 #define OUTMAILBOX1 MBOX_BLOCK+0x2 @@ -246,31 +325,17 @@ #define OUTMAILBOX3 MBOX_BLOCK+0x6 #define OUTMAILBOX4 MBOX_BLOCK+0x8 #define OUTMAILBOX5 MBOX_BLOCK+0xA +#define OUTMAILBOX6 MBOX_BLOCK+0xC +#define OUTMAILBOX7 MBOX_BLOCK+0xE -/* - * Mailbox Command Complete Status Codes - */ -#define MBOX_COMMAND_COMPLETE 0x4000 -#define MBOX_INVALID_COMMAND 0x4001 -#define MBOX_HOST_INTERFACE_ERROR 0x4002 -#define MBOX_TEST_FAILED 0x4003 -#define MBOX_COMMAND_ERROR 0x4005 -#define MBOX_COMMAND_PARAM_ERROR 0x4006 - -/* - * Asynchronous event status codes - */ -#define ASYNC_BUS_RESET 0x8001 -#define ASYNC_SYSTEM_ERROR 0x8002 -#define ASYNC_RQS_XFER_ERR 0x8003 -#define ASYNC_RSP_XFER_ERR 0x8004 -#define ASYNC_QWAKEUP 0x8005 -#define ASYNC_TIMEOUT_RESET 0x8006 +#define OMBOX_OFFN(n) (MBOX_BLOCK + (n * 2)) +#define NMBOX(isp) \ + (((((isp)->isp_type & ISP_HA_SCSI) >= ISP_HA_SCSI_1040A) || \ + ((isp)->isp_type & ISP_HA_FC))? 8 : 6) /* * SXP Block Register Offsets */ -#define SXP_BLOCK 0x0400 #define SXP_PART_ID SXP_BLOCK+0x0 /* R : Part ID Code */ #define SXP_CONFIG1 SXP_BLOCK+0x2 /* RW*: Configuration Reg #1 */ #define SXP_CONFIG2 SXP_BLOCK+0x4 /* RW*: Configuration Reg #2 */ @@ -441,7 +506,6 @@ /* * RISC and Host Command and Control Block Register Offsets */ -#define RISC_BLOCK 0x0800 #define RISC_ACC RISC_BLOCK+0x0 /* RW*: Accumulator */ #define RISC_R1 RISC_BLOCK+0x2 /* RW*: GP Reg R1 */ @@ -467,7 +531,10 @@ #define RISC_LCR RISC_BLOCK+0x2a /* RW*: Loop Counter */ #define RISC_PC RISC_BLOCK+0x2c /* R : Program Counter */ #define RISC_MTR RISC_BLOCK+0x2e /* RW*: Memory Timing */ +#define RISC_MTR2100 RISC_BLOCK+0x30 + #define RISC_EMB RISC_BLOCK+0x30 /* RW*: Ext Mem Boundary */ +#define DUAL_BANK 8 #define RISC_SP RISC_BLOCK+0x32 /* RW*: Stack Pointer */ #define RISC_HRL RISC_BLOCK+0x3e /* R *: Hardware Rev Level */ #define HCCR RISC_BLOCK+0x40 /* RW : Host Command & Ctrl */ @@ -486,6 +553,10 @@ #define RISC_PSR_ALU_MSB 0x0400 #define RISC_PSR_ALU_CARRY 0x0200 #define RISC_PSR_ALU_ZERO 0x0100 + +#define RISC_PSR_PCI_ULTRA 0x0080 +#define RISC_PSR_SBUS_ULTRA 0x0020 + #define RISC_PSR_DMA_INT 0x0010 #define RISC_PSR_SXP_INT 0x0008 #define RISC_PSR_HOST_INT 0x0004 @@ -508,6 +579,11 @@ #define PCI_HCCR_CMD_PARITY_ERR 0xE000 /* Generate parity error */ #define HCCR_CMD_TEST_MODE 0xF000 /* Set Test Mode */ +#define ISP2100_HCCR_PARITY_ENABLE_2 0x0400 +#define ISP2100_HCCR_PARITY_ENABLE_1 0x0200 +#define ISP2100_HCCR_PARITY_ENABLE_0 0x0100 +#define ISP2100_HCCR_PARITY 0x0001 + #define PCI_HCCR_PARITY 0x0400 /* Parity error flag */ #define PCI_HCCR_PARITY_ENABLE_1 0x0200 /* Parity enable bank 1 */ #define PCI_HCCR_PARITY_ENABLE_0 0x0100 /* Parity enable bank 0 */ @@ -517,4 +593,119 @@ #define HCCR_PAUSE 0x0020 /* R : RISC paused */ #define PCI_HCCR_BIOS 0x0001 /* W : BIOS enable */ + +/* + * Qlogic 1XXX NVRAM is an array of 128 bytes. + * + * Some portion of the front of this is for general host adapter properties + * This is followed by an array of per-target parameters, and is tailed off + * with a checksum xor byte at offset 127. For non-byte entities data is + * stored in Little Endian order. + */ + +#define ISP_NVRAM_SIZE 128 + +#define ISPBSMX(c, byte, shift, mask) \ + (((c)[(byte)] >> (shift)) & (mask)) + +#define ISP_NVRAM_VERSION(c) (c)[4] +#define ISP_NVRAM_FIFO_THRESHOLD(c) ISPBSMX(c, 5, 0, 0x03) +#define ISP_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 5, 2, 0x01) +#define ISP_NVRAM_HBA_ENABLE(c) ISPBSMX(c, 5, 3, 0x01) +#define ISP_NVRAM_INITIATOR_ID(c) ISPBSMX(c, 5, 4, 0x0f) +#define ISP_NVRAM_BUS_RESET_DELAY(c) (c)[6] +#define ISP_NVRAM_BUS_RETRY_COUNT(c) (c)[7] +#define ISP_NVRAM_BUS_RETRY_DELAY(c) (c)[8] +#define ISP_NVRAM_ASYNC_DATA_SETUP_TIME(c) ISPBSMX(c, 9, 0, 0x0f) +#define ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 4, 0x01) +#define ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 5, 0x01) +#define ISP_NVRAM_DATA_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 6, 0x01) +#define ISP_NVRAM_CMD_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 7, 0x01) +#define ISP_NVRAM_TAG_AGE_LIMIT(c) (c)[10] +#define ISP_NVRAM_LOWTRM_ENABLE(c) ISPBSMX(c, 11, 0, 0x01) +#define ISP_NVRAM_HITRM_ENABLE(c) ISPBSMX(c, 11, 1, 0x01) +#define ISP_NVRAM_PCMC_BURST_ENABLE(c) ISPBSMX(c, 11, 2, 0x01) +#define ISP_NVRAM_ENABLE_60_MHZ(c) ISPBSMX(c, 11, 3, 0x01) +#define ISP_NVRAM_SCSI_RESET_DISABLE(c) ISPBSMX(c, 11, 4, 0x01) +#define ISP_NVRAM_ENABLE_AUTO_TERM(c) ISPBSMX(c, 11, 5, 0x01) +#define ISP_NVRAM_FIFO_THRESHOLD_128(c) ISPBSMX(c, 11, 6, 0x01) +#define ISP_NVRAM_AUTO_TERM_SUPPORT(c) ISPBSMX(c, 11, 7, 0x01) +#define ISP_NVRAM_SELECTION_TIMEOUT(c) (((c)[12]) | ((c)[13] << 8)) +#define ISP_NVRAM_MAX_QUEUE_DEPTH(c) (((c)[14]) | ((c)[15] << 8)) +#define ISP_NVRAM_SCSI_BUS_SIZE(c) ISPBSMX(c, 16, 0, 0x01) +#define ISP_NVRAM_SCSI_BUS_TYPE(c) ISPBSMX(c, 16, 1, 0x01) +#define ISP_NVRAM_ADAPTER_CLK_SPEED(c) ISPBSMX(c, 16, 2, 0x01) +#define ISP_NVRAM_SOFT_TERM_SUPPORT(c) ISPBSMX(c, 16, 3, 0x01) +#define ISP_NVRAM_FLASH_ONBOARD(c) ISPBSMX(c, 16, 4, 0x01) +#define ISP_NVRAM_FAST_MTTR_ENABLE(c) ISPBSMX(c, 22, 0, 0x01) + +#define ISP_NVRAM_TARGOFF 28 +#define ISP_NVARM_TARGSIZE 6 +#define _IxT(tgt, tidx) \ + (ISP_NVRAM_TARGOFF + (ISP_NVARM_TARGSIZE * (tgt)) + (tidx)) +#define ISP_NVRAM_TGT_RENEG(c, t) ISPBSMX(c, _IxT(t, 0), 0, 0x01) +#define ISP_NVRAM_TGT_QFRZ(c, t) ISPBSMX(c, _IxT(t, 0), 1, 0x01) +#define ISP_NVRAM_TGT_ARQ(c, t) ISPBSMX(c, _IxT(t, 0), 2, 0x01) +#define ISP_NVRAM_TGT_TQING(c, t) ISPBSMX(c, _IxT(t, 0), 3, 0x01) +#define ISP_NVRAM_TGT_SYNC(c, t) ISPBSMX(c, _IxT(t, 0), 4, 0x01) +#define ISP_NVRAM_TGT_WIDE(c, t) ISPBSMX(c, _IxT(t, 0), 5, 0x01) +#define ISP_NVRAM_TGT_PARITY(c, t) ISPBSMX(c, _IxT(t, 0), 6, 0x01) +#define ISP_NVRAM_TGT_DISC(c, t) ISPBSMX(c, _IxT(t, 0), 7, 0x01) +#define ISP_NVRAM_TGT_EXEC_THROTTLE(c, t) ISPBSMX(c, _IxT(t, 1), 0, 0xff) +#define ISP_NVRAM_TGT_SYNC_PERIOD(c, t) ISPBSMX(c, _IxT(t, 2), 0, 0xff) +#define ISP_NVRAM_TGT_SYNC_OFFSET(c, t) ISPBSMX(c, _IxT(t, 3), 0, 0x0f) +#define ISP_NVRAM_TGT_DEVICE_ENABLE(c, t) ISPBSMX(c, _IxT(t, 3), 4, 0x01) +#define ISP_NVRAM_TGT_LUN_DISABLE(c, t) ISPBSMX(c, _IxT(t, 3), 5, 0x01) + +/* + * Qlogic 2XXX NVRAM is an array of 256 bytes. + * + * Some portion of the front of this is for general RISC engine parameters, + * mostly reflecting the state of the last INITIALIZE FIRMWARE mailbox command. + * + * This is followed by some general host adapter parameters, and ends with + * a checksum xor byte at offset 255. For non-byte entities data is stored + * in Little Endian order. + */ +#define ISP2100_NVRAM_SIZE 256 +/* ISP_NVRAM_VERSION is in same overall place */ +#define ISP2100_NVRAM_RISCVER(c) (c)[6] +#define ISP2100_NVRAM_OPTIONS(c) (c)[8] +#define ISP2100_NVRAM_MAXFRAMELENGTH(c) (((c)[10]) | ((c)[11] << 8)) +#define ISP2100_NVRAM_MAXIOCBALLOCATION(c) (((c)[12]) | ((c)[13] << 8)) +#define ISP2100_NVRAM_EXECUTION_THROTTLE(c) (((c)[14]) | ((c)[15] << 8)) +#define ISP2100_NVRAM_RETRY_COUNT(c) (c)[16] +#define ISP2100_NVRAM_RETRY_DELAY(c) (c)[17] + +#define ISP2100_NVRAM_NODE_NAME(c) ( \ + (((u_int64_t)(c)[18]) << 56) | \ + (((u_int64_t)(c)[19]) << 48) | \ + (((u_int64_t)(c)[20]) << 40) | \ + (((u_int64_t)(c)[21]) << 32) | \ + (((u_int64_t)(c)[22]) << 24) | \ + (((u_int64_t)(c)[23]) << 16) | \ + (((u_int64_t)(c)[24]) << 8) | \ + (((u_int64_t)(c)[25]) << 0)) +#define ISP2100_NVRAM_HARDLOOPID(c) (c)[26] + +#define ISP2100_NVRAM_HBA_OPTIONS(c) (c)[70] +#define ISP2100_NVRAM_HBA_DISABLE(c) ISPBSMX(c, 70, 0, 0x01) +#define ISP2100_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 70, 1, 0x01) +#define ISP2100_NVRAM_LUN_DISABLE(c) ISPBSMX(c, 70, 2, 0x01) +#define ISP2100_NVRAM_ENABLE_SELECT_BOOT(c) ISPBSMX(c, 70, 3, 0x01) +#define ISP2100_NVRAM_DISABLE_CODELOAD(c) ISPBSMX(c, 70, 4, 0x01) +#define ISP2100_NVRAM_SET_CACHELINESZ(c) ISPBSMX(c, 70, 5, 0x01) + +#define ISP2100_NVRAM_BOOT_NODE_NAME(c) ( \ + (((u_int64_t)(c)[72]) << 56) | \ + (((u_int64_t)(c)[73]) << 48) | \ + (((u_int64_t)(c)[74]) << 40) | \ + (((u_int64_t)(c)[75]) << 32) | \ + (((u_int64_t)(c)[76]) << 24) | \ + (((u_int64_t)(c)[77]) << 16) | \ + (((u_int64_t)(c)[78]) << 8) | \ + (((u_int64_t)(c)[79]) << 0)) + +#define ISP2100_NVRAM_BOOT_LUN(c) (c)[80] + #endif /* _ISPREG_H */ diff --git a/sys/dev/ic/ispvar.h b/sys/dev/ic/ispvar.h index 01199e01ece..ab587d5c2fa 100644 --- a/sys/dev/ic/ispvar.h +++ b/sys/dev/ic/ispvar.h @@ -1,12 +1,13 @@ -/* $NetBSD: ispvar.h,v 1.4 1997/04/05 02:48:36 mjacob Exp $ */ - +/* $OpenBSD: ispvar.h,v 1.2 1999/03/17 05:26:09 mjacob Exp $ */ +/* release_03_16_99 */ /* * Soft Definitions for for Qlogic ISP SCSI adapters. * - * Copyright (c) 1997 by Matthew Jacob + *--------------------------------------- + * Copyright (c) 1997, 1998 by Matthew Jacob * NASA/Ames Research Center * All rights reserved. - * + *--------------------------------------- * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -30,15 +31,27 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * */ #ifndef _ISPVAR_H #define _ISPVAR_H +#if defined(__NetBSD__) || defined(__OpenBSD__) #include <dev/ic/ispmbox.h> +#endif +#ifdef __FreeBSD__ +#include <dev/isp/ispmbox.h> +#endif +#ifdef __linux__ +#include "ispmbox.h" +#endif + +#define ISP_CORE_VERSION_MAJOR 1 +#define ISP_CORE_VERSION_MINOR 6 /* - * Vector for MD code to provide specific services. + * Vector for bus specific code to provide specific services. */ struct ispsoftc; struct ispmdvec { @@ -46,14 +59,16 @@ struct ispmdvec { void (*dv_wr_reg) __P((struct ispsoftc *, int, u_int16_t)); int (*dv_mbxdma) __P((struct ispsoftc *)); int (*dv_dmaset) __P((struct ispsoftc *, - struct scsi_xfer *, ispreq_t *, u_int8_t *, u_int8_t)); + ISP_SCSI_XFER_T *, ispreq_t *, u_int8_t *, u_int8_t)); void (*dv_dmaclr) - __P((struct ispsoftc *, struct scsi_xfer *, u_int32_t)); + __P((struct ispsoftc *, ISP_SCSI_XFER_T *, u_int32_t)); void (*dv_reset0) __P((struct ispsoftc *)); void (*dv_reset1) __P((struct ispsoftc *)); + void (*dv_dregs) __P((struct ispsoftc *)); const u_int16_t *dv_ispfw; /* ptr to f/w */ u_int16_t dv_fwlen; /* length of f/w */ u_int16_t dv_codeorg; /* code ORG for f/w */ + u_int16_t dv_fwrev; /* f/w revision */ /* * Initial values for conf1 register */ @@ -62,74 +77,246 @@ struct ispmdvec { }; #define MAX_TARGETS 16 -#define MAX_LUNS 8 +#ifdef ISP2100_FABRIC +#define MAX_FC_TARG 256 +#else +#define MAX_FC_TARG 126 +#endif +#define DEFAULT_LOOPID 113 + +/* queue length must be a power of two */ +#define QENTRY_LEN 64 +#define RQUEST_QUEUE_LEN MAXISPREQUEST +#define RESULT_QUEUE_LEN (MAXISPREQUEST/2) +#define ISP_QUEUE_ENTRY(q, idx) ((q) + ((idx) * QENTRY_LEN)) +#define ISP_QUEUE_SIZE(n) ((n) * QENTRY_LEN) +#define ISP_NXT_QENTRY(idx, qlen) (((idx) + 1) & ((qlen)-1)) +#define ISP_QAVAIL(in, out, qlen) \ + ((in == out)? (qlen - 1) : ((in > out)? \ + ((qlen - 1) - (in - out)) : (out - in - 1))) +/* + * SCSI Specific Host Adapter Parameters + */ + +typedef struct { + u_int isp_req_ack_active_neg : 1, + isp_data_line_active_neg: 1, + isp_cmd_dma_burst_enable: 1, + isp_data_dma_burst_enabl: 1, + isp_fifo_threshold : 3, + isp_ultramode : 1, + isp_diffmode : 1, + isp_fast_mttr : 1, + isp_initiator_id : 4, + isp_async_data_setup : 4; + u_int16_t isp_selection_timeout; + u_int16_t isp_max_queue_depth; + u_int16_t isp_clock; + u_int8_t isp_tag_aging; + u_int8_t isp_bus_reset_delay; + u_int8_t isp_retry_count; + u_int8_t isp_retry_delay; + struct { + u_int + dev_enable : 1, + dev_announced : 1, + dev_update : 1, + dev_refresh : 1, + exc_throttle : 7, + sync_offset : 4, + sync_period : 8; + u_int16_t dev_flags; /* persistent device flags */ + u_int16_t cur_dflags; /* current device flags */ + } isp_devparam[MAX_TARGETS]; +} sdparam; /* scsi device parameters */ + +/* + * Device Flags + */ +#define DPARM_DISC 0x8000 +#define DPARM_PARITY 0x4000 +#define DPARM_WIDE 0x2000 +#define DPARM_SYNC 0x1000 +#define DPARM_TQING 0x0800 +#define DPARM_ARQ 0x0400 +#define DPARM_QFRZ 0x0200 +#define DPARM_RENEG 0x0100 +#define DPARM_NARROW 0x0080 /* Possibly only available with >= 7.55 fw */ +#define DPARM_ASYNC 0x0040 /* Possibly only available with >= 7.55 fw */ +#define DPARM_DEFAULT (0xFF00 & ~DPARM_QFRZ) +#define DPARM_SAFE_DFLT (DPARM_DEFAULT & ~(DPARM_WIDE|DPARM_SYNC|DPARM_TQING)) + + +#define ISP_20M_SYNCPARMS 0x080c +#define ISP_10M_SYNCPARMS 0x0c19 +#define ISP_08M_SYNCPARMS 0x0c25 +#define ISP_05M_SYNCPARMS 0x0c32 +#define ISP_04M_SYNCPARMS 0x0c41 + +/* + * Fibre Channel Specifics + */ +typedef struct { + u_int64_t isp_wwn; /* WWN of adapter */ + u_int8_t isp_loopid; /* hard loop id */ + u_int8_t isp_alpa; /* ALPA */ + u_int8_t isp_execthrottle; + u_int8_t isp_retry_delay; + u_int8_t isp_retry_count; + u_int8_t isp_fwstate; /* ISP F/W state */ + u_int16_t isp_maxalloc; + u_int16_t isp_maxfrmlen; + u_int16_t isp_fwoptions; + /* + * Port Data Base + */ + isp_pdb_t isp_pdb[MAX_FC_TARG]; + + /* + * Scratch DMA mapped in area to fetch Port Database stuff, etc. + */ + volatile caddr_t isp_scratch; + u_int32_t isp_scdma; +} fcparam; + +#define ISP2100_SCRLEN 0x100 + +#define FW_CONFIG_WAIT 0x0000 +#define FW_WAIT_AL_PA 0x0001 +#define FW_WAIT_LOGIN 0x0002 +#define FW_READY 0x0003 +#define FW_LOSS_OF_SYNC 0x0004 +#define FW_ERROR 0x0005 +#define FW_REINIT 0x0006 +#define FW_NON_PART 0x0007 + +#ifdef ISP_TARGET_MODE +/* + * Some temporary Target Mode definitions + */ +typedef struct tmd_cmd { + u_int8_t cd_iid; /* initiator */ + u_int8_t cd_tgt; /* target */ + u_int8_t cd_lun; /* LUN for this command */ + u_int8_t cd_state; + u_int8_t cd_cdb[16]; /* command bytes */ + u_int8_t cd_sensedata[20]; + u_int16_t cd_rxid; + u_int32_t cd_datalen; + u_int32_t cd_totbytes; + void * cd_hba; +} tmd_cmd_t; -#define RQUEST_QUEUE_LEN 256 -#define RESULT_QUEUE_LEN (RQUEST_QUEUE_LEN >> 3) -#define QENTRY_LEN 64 +/* + * Async Target Mode Event Definitions + */ +#define TMD_BUS_RESET 0 +#define TMD_BDR 1 -#define ISP_QUEUE_ENTRY(q, idx) ((q) + ((idx) * QENTRY_LEN)) -#define ISP_QUEUE_SIZE(n) ((n) * QENTRY_LEN) +/* + * Immediate Notify data structure. + */ +#define NOTIFY_MSGLEN 5 +typedef struct { + u_int8_t nt_iid; /* initiator */ + u_int8_t nt_tgt; /* target */ + u_int8_t nt_lun; /* LUN for this command */ + u_int8_t nt_msg[NOTIFY_MSGLEN]; /* SCSI message byte(s) */ +} tmd_notify_t; + +#endif /* * Soft Structure per host adapter */ struct ispsoftc { - struct device isp_dev; + /* + * Platform (OS) specific data + */ + struct isposinfo isp_osinfo; + + /* + * Pointer to bus specific data + */ struct ispmdvec * isp_mdvec; -#define isp_name isp_dev.dv_xname - struct scsi_link isp_link; - u_int8_t isp_max_target; - u_int8_t isp_state; + /* - * Host Adapter Parameters, nominally stored in NVRAM + * Mostly nonvolatile state, debugging, etc.. */ - u_int16_t isp_adapter_enabled : 1, - isp_req_ack_active_neg : 1, - isp_data_line_active_neg: 1, - isp_cmd_dma_burst_enable: 1, - isp_data_dma_burst_enabl: 1, - isp_fifo_threshold : 2, - : 1, - isp_initiator_id : 4, - isp_async_data_setup : 4; - u_int16_t isp_selection_timeout; - u_int16_t isp_max_queue_depth; - u_int8_t isp_tag_aging; - u_int8_t isp_bus_reset_delay; - u_int8_t isp_retry_count; - u_int8_t isp_retry_delay; - struct { - u_int8_t dev_flags; /* Device Flags - see below */ - u_int8_t exc_throttle; - u_int8_t sync_period; - u_int8_t sync_offset : 4, - dev_enable : 1; - } isp_devparam[MAX_TARGETS]; + + u_int : 8, + isp_confopts : 8, + : 1, + isp_used : 1, + isp_dblev : 3, + isp_gotdparms : 1, + isp_dogactive : 1, + isp_bustype : 1, /* BUS Implementation */ + isp_type : 8; /* HBA Type and Revision */ + + u_int16_t isp_fwrev; /* Running F/W revision */ + u_int16_t isp_romfw_rev; /* 'ROM' F/W revision */ + void * isp_param; + + /* + * Volatile state + */ + + volatile u_int + : 19, + isp_state : 3, + isp_sendmarker : 1, /* send a marker entry */ + isp_update : 1, /* update parameters */ + isp_nactive : 9; /* how many commands active */ + /* - * Result and Request Queues. + * Result and Request Queue indices. */ + volatile u_int8_t isp_reqodx; /* index of last ISP pickup */ volatile u_int8_t isp_reqidx; /* index of next request */ volatile u_int8_t isp_residx; /* index of next result */ - volatile u_int8_t isp_sendmarker; - volatile u_int8_t isp_seqno; + volatile u_int8_t isp_seqno; /* rolling sequence # */ + /* * Sheer laziness, but it gets us around the problem * where we don't have a clean way of remembering - * which scsi_xfer is bound to which ISP queue entry. + * which transaction is bound to which ISP queue entry. * * There are other more clever ways to do this, but, * jeez, so I blow a couple of KB per host adapter... * and it *is* faster. */ - volatile struct scsi_xfer *isp_xflist[RQUEST_QUEUE_LEN]; + ISP_SCSI_XFER_T *isp_xflist[RQUEST_QUEUE_LEN]; + /* - * request/result queue + * request/result queues and dma handles for them. */ volatile caddr_t isp_rquest; volatile caddr_t isp_result; u_int32_t isp_rquest_dma; u_int32_t isp_result_dma; + +#ifdef ISP_TARGET_MODE + /* + * Vectors for handling target mode support. + * + * isp_tmd_newcmd is for feeding a newly arrived command to some + * upper layer. + * + * isp_tmd_event is for notifying some upper layer that an event has + * occurred that is not necessarily tied to any target (e.g., a SCSI + * Bus Reset). + * + * isp_tmd_notify is for notifying some upper layer that some + * event is now occurring that is either pertinent for a specific + * device or for a specific command (e.g., BDR or ABORT TAG). + * + * It is left undefined (for now) how pools of commands are managed. + */ + void (*isp_tmd_newcmd) __P((void *, tmd_cmd_t *)); + void (*isp_tmd_event) __P((void *, int)); + void (*isp_tmd_notify) __P((void *, tmd_notify_t *)); +#endif }; /* @@ -140,24 +327,40 @@ struct ispsoftc { #define ISP_INITSTATE 2 #define ISP_RUNSTATE 3 - /* - * Device Flags + * ISP Configuration Options */ -#define DPARM_DISC 0x80 -#define DPARM_PARITY 0x40 -#define DPARM_WIDE 0x20 -#define DPARM_SYNC 0x10 -#define DPARM_TQING 0x08 -#define DPARM_ARQ 0x04 -#define DPARM_QFRZ 0x02 -#define DPARM_RENEG 0x01 +#define ISP_CFG_NORELOAD 0x80 /* don't download f/w */ +#define ISP_CFG_NONVRAM 0x40 /* ignore NVRAM */ -#define DPARM_DEFAULT (0xff & ~DPARM_QFRZ) +#define ISP_FW_REV(maj, min) ((maj) << 10| (min)) +/* + * Bus (implementation) types + */ +#define ISP_BT_PCI 0 /* PCI Implementations */ +#define ISP_BT_SBUS 1 /* SBus Implementations */ + +/* + * Chip Types + */ +#define ISP_HA_SCSI 0xf +#define ISP_HA_SCSI_UNKNOWN 0x1 +#define ISP_HA_SCSI_1020 0x2 +#define ISP_HA_SCSI_1020A 0x3 +#define ISP_HA_SCSI_1040 0x4 +#define ISP_HA_SCSI_1040A 0x5 +#define ISP_HA_SCSI_1040B 0x6 +#define ISP_HA_SCSI_1080 0xe +#define ISP_HA_FC 0xf0 +#define ISP_HA_FC_2100 0x10 + +#define IS_SCSI(isp) (isp->isp_type & ISP_HA_SCSI) +#define IS_1080(isp) (isp->isp_type == ISP_HA_SCSI_1080) +#define IS_FC(isp) (isp->isp_type & ISP_HA_FC) /* - * Macros to read, write ISP registers through MD code + * Macros to read, write ISP registers through bus specific code. */ #define ISP_READ(isp, reg) \ @@ -180,7 +383,8 @@ struct ispsoftc { if ((isp)->isp_mdvec->dv_reset0) (*(isp)->isp_mdvec->dv_reset0)((isp)) #define ISP_RESET1(isp) \ if ((isp)->isp_mdvec->dv_reset1) (*(isp)->isp_mdvec->dv_reset1)((isp)) - +#define ISP_DUMPREGS(isp) \ + if ((isp)->isp_mdvec->dv_dregs) (*(isp)->isp_mdvec->dv_dregs)((isp)) #define ISP_SETBITS(isp, reg, val) \ (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), ISP_READ((isp), (reg)) | (val)) @@ -191,10 +395,10 @@ struct ispsoftc { /* * Function Prototypes */ + /* - * Reset Hardware. - * - * Only looks at sc_dev.dv_xname, sc_iot and sc_ioh fields. + * Reset Hardware. Totally. Assumes that you'll follow this with + * a call to isp_init. */ void isp_reset __P((struct ispsoftc *)); @@ -204,21 +408,65 @@ void isp_reset __P((struct ispsoftc *)); void isp_init __P((struct ispsoftc *)); /* - * Complete attachment of Hardware + * Reset the ISP and call completion for any orphaned commands. */ -void isp_attach __P((struct ispsoftc *)); +void isp_restart __P((struct ispsoftc *)); /* - * Free any associated resources prior to decommissioning. + * Interrupt Service Routine */ -void isp_uninit __P((struct ispsoftc *)); +int isp_intr __P((void *)); /* - * Interrupt Service Routine + * Command Entry Point + */ +int32_t ispscsicmd __P((ISP_SCSI_XFER_T *)); + +/* + * Platform Dependent to External to Internal Control Function + * + * For: Aborting a running command - arg is an ISP_SCSI_XFER_T * + * Resetting a Device - arg is target to reset + * Resetting a BUS - arg is ignored + * Updating parameters - arg is ignored + * + * First argument is this instance's softc pointer. + * Second argument is an index into xflist array. + * Assumes all locks must be held already. + */ +typedef enum { + ISPCTL_RESET_BUS, + ISPCTL_RESET_DEV, + ISPCTL_ABORT_CMD, + ISPCTL_UPDATE_PARAMS, + ISPCTL_FCLINK_TEST +} ispctl_t; +int isp_control __P((struct ispsoftc *, ispctl_t, void *)); + + +/* + * Platform Dependent to Internal to External Control Function + * (each platform must provide such a function) + * + * For: Announcing Target Paramter Changes (arg is target) + * + * Assumes all locks are held. */ -int isp_intr __P((void *)); +typedef enum { + ISPASYNC_NEW_TGT_PARAMS, + ISPASYNC_BUS_RESET, /* Bus Reset */ + ISPASYNC_LOOP_DOWN, /* Obvious FC only */ + ISPASYNC_LOOP_UP, /* "" */ + ISPASYNC_PDB_CHANGE_COMPLETE, /* "" */ + ISPASYNC_CHANGE_NOTIFY /* "" */ +} ispasync_t; +int isp_async __P((struct ispsoftc *, ispasync_t, void *)); +/* + * lost command routine (XXXX IN TRANSITION XXXX) + */ +void isp_lostcmd __P((struct ispsoftc *, ISP_SCSI_XFER_T *)); #endif /* _ISPVAR_H */ |