diff options
Diffstat (limited to 'sys/dev/ic/isp.c')
-rw-r--r-- | sys/dev/ic/isp.c | 5707 |
1 files changed, 3656 insertions, 2051 deletions
diff --git a/sys/dev/ic/isp.c b/sys/dev/ic/isp.c index 8ccfe47c05f..2d0f10fdd18 100644 --- a/sys/dev/ic/isp.c +++ b/sys/dev/ic/isp.c @@ -1,33 +1,35 @@ -/* $OpenBSD: isp.c,v 1.45 2009/06/03 05:11:09 krw Exp $ */ +/* $OpenBSD: isp.c,v 1.46 2009/06/24 11:00:53 krw Exp $ */ /* $FreeBSD: src/sys/dev/isp/isp.c,v 1.150 2008/12/15 21:42:38 marius Exp $*/ +/*- + * Copyright (c) 1997-2007 by Matthew Jacob + * 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, 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. + * + * THIS SOFTWARE IS PROVIDED BY 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 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. + */ + /* * Machine and OS Independent (well, as best as possible) - * code for the QLogic ISP SCSI adapters. - * - * Copyright (c) 1997, 1998, 1999, 2000, 2001 by Matthew Jacob - * Feral Software - * 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. 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. + * code for the Qlogic ISP SCSI and FC-SCSI adapters. */ /* @@ -39,11 +41,14 @@ /* * Include header file appropriate for platform we're building on. */ - #ifdef __NetBSD__ +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD$"); #include <dev/ic/isp_netbsd.h> #endif #ifdef __FreeBSD__ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/sys/dev/isp/isp.c,v 1.149 2007/07/10 07:55:04 mjacob Exp $"); #include <dev/isp/isp_freebsd.h> #endif #ifdef __OpenBSD__ @@ -61,30 +66,16 @@ */ #define MBOX_DELAY_COUNT 1000000 / 100 +#define ISP_MARK_PORTDB(a, b) \ + isp_prt(isp, ISP_LOGSANCFG, "line %d: markportdb", __LINE__); \ + isp_mark_portdb(a, b) /* * Local static data */ -static const char portshift[] = - "Target %d Loop ID 0x%x (Port 0x%x) => Loop 0x%x (Port 0x%x)"; -static const char portdup[] = - "Target %d duplicates Target %d- killing off both"; -static const char retained[] = - "Retaining Loop ID 0x%x for Target %d (Port 0x%x)"; -static const char lretained[] = - "Retained login of Target %d (Loop ID 0x%x) Port 0x%x"; -static const char plogout[] = - "Logging out Target %d at Loop ID 0x%x (Port 0x%x)"; -static const char plogierr[] = - "Command Error in PLOGI for Port 0x%x (0x%x)"; -static const char nopdb[] = - "Could not get PDB for Device @ Port 0x%x"; -static const char pdbmfail1[] = - "PDB Loop ID info for Device @ Port 0x%x does not match up (0x%x)"; -static const char pdbmfail2[] = - "PDB Port info for Device @ Port 0x%x does not match up (0x%x)"; -static const char ldumped[] = - "Target %d (Loop ID 0x%x) Port 0x%x dumped after login info mismatch"; +static const char fconf[] = + "PortDB[%d] changed:\n current =(0x%x@0x%06x 0x%08x%08x 0x%08x%08x)\n" + " database=(0x%x@0x%06x 0x%08x%08x 0x%08x%08x)"; static const char notresp[] = "Not RESPONSE in RESPONSE Queue (type 0x%x) @ idx %d (next %d) nlooked %d"; static const char xact1[] = @@ -96,11 +87,11 @@ static const char xact3[] = static const char pskip[] = "SCSI phase skipped for target %d.%d.%d"; static const char topology[] = - "Loop ID %d, AL_PA 0x%x, Port ID 0x%x, Loop State 0x%x, Topology '%s'"; -static const char swrej[] = - "Fabric Nameserver rejected %s (Reason=0x%x Expl=0x%x) for Port ID 0x%x"; + "HBA PortID 0x%06x N-Port Handle %d, Connection Topology '%s'"; +static const char ourwwn[] = + "HBA WWNN 0x%08x%08x HBA WWPN 0x%08x%08x"; static const char finmsg[] = - "(%d.%d.%d): FIN dl%d resid %lu STS 0x%x SKEY %c XS_ERR=0x%x"; + "%d.%d.%d: FIN dl%d resid %d STS 0x%x SKEY %c XS_ERR=0x%x"; static const char sc0[] = "%s CHAN %d FTHRSH %d IID %d RESETD %d RETRYC %d RETRYD %d ASD 0x%x"; static const char sc1[] = @@ -114,40 +105,54 @@ static const char bun[] = /* * Local function prototypes. */ -static int isp_parse_async(struct ispsoftc *, u_int16_t); -static int isp_handle_other_response(struct ispsoftc *, int, isphdr_t *, - u_int16_t *); -static void -isp_parse_status(struct ispsoftc *, ispstatusreq_t *, XS_T *); -static void isp_fastpost_complete(struct ispsoftc *, u_int16_t); -static int isp_mbox_continue(struct ispsoftc *); -static void isp_scsi_init(struct ispsoftc *); -static void isp_scsi_channel_init(struct ispsoftc *, int); -static void isp_fibre_init(struct ispsoftc *); -static void isp_mark_getpdb_all(struct ispsoftc *); -static int isp_getmap(struct ispsoftc *, fcpos_map_t *); -static int isp_getpdb(struct ispsoftc *, int, isp_pdb_t *); -static u_int64_t isp_get_portname(struct ispsoftc *, int, int); -static int isp_fclink_test(struct ispsoftc *, int); -static char *isp2100_fw_statename(int); -static int isp_pdb_sync(struct ispsoftc *); -static int isp_scan_loop(struct ispsoftc *); -static int isp_fabric_mbox_cmd(struct ispsoftc *, mbreg_t *); -static int isp_scan_fabric(struct ispsoftc *, int); -static void isp_register_fc4_type(struct ispsoftc *); -static void isp_fw_state(struct ispsoftc *); -static void isp_mboxcmd_qnw(struct ispsoftc *, mbreg_t *, int); -static void isp_mboxcmd(struct ispsoftc *, mbreg_t *, int); - -static void isp_update(struct ispsoftc *); -static void isp_update_bus(struct ispsoftc *, int); -static void isp_setdfltparm(struct ispsoftc *, int); -static int isp_read_nvram(struct ispsoftc *); -static void isp_rdnvram_word(struct ispsoftc *, int, u_int16_t *); -static void isp_parse_nvram_1020(struct ispsoftc *, u_int8_t *); -static void isp_parse_nvram_1080(struct ispsoftc *, int, u_int8_t *); -static void isp_parse_nvram_12160(struct ispsoftc *, int, u_int8_t *); -static void isp_parse_nvram_2100(struct ispsoftc *, u_int8_t *); +int isp_parse_async(struct ispsoftc *, u_int16_t); +int isp_handle_other_response(struct ispsoftc *, int, isphdr_t *, + u_int32_t *); +void +isp_parse_status(struct ispsoftc *, ispstatusreq_t *, XS_T *, long *); +void +isp_parse_status_24xx(struct ispsoftc *, isp24xx_statusreq_t *, XS_T *, long *); +void isp_fastpost_complete(struct ispsoftc *, u_int16_t); +int isp_mbox_continue(struct ispsoftc *); +void isp_scsi_init(struct ispsoftc *); +void isp_scsi_channel_init(struct ispsoftc *, int); +void isp_fibre_init(struct ispsoftc *); +void isp_fibre_init_2400(struct ispsoftc *); +void isp_mark_portdb(struct ispsoftc *, int); +int isp_plogx(struct ispsoftc *, u_int16_t, u_int32_t, int, int); +int isp_port_login(struct ispsoftc *, u_int16_t, u_int32_t); +int isp_port_logout(struct ispsoftc *, u_int16_t, u_int32_t); +int isp_getpdb(struct ispsoftc *, u_int16_t, isp_pdb_t *, int); +u_int64_t isp_get_portname(struct ispsoftc *, int, int); +int isp_fclink_test(struct ispsoftc *, int); +const char *ispfc_fw_statename(int); +int isp_pdb_sync(struct ispsoftc *); +int isp_scan_loop(struct ispsoftc *); +int isp_gid_ft_sns(struct ispsoftc *); +int isp_gid_ft_ct_passthru(struct ispsoftc *); +int isp_scan_fabric(struct ispsoftc *); +int isp_login_device(struct ispsoftc *, u_int32_t, isp_pdb_t *, u_int16_t *); +int isp_register_fc4_type(struct ispsoftc *); +int isp_register_fc4_type_24xx(struct ispsoftc *); +u_int16_t isp_nxt_handle(struct ispsoftc *, u_int16_t); +void isp_fw_state(struct ispsoftc *); +void isp_mboxcmd_qnw(struct ispsoftc *, mbreg_t *, int); +void isp_mboxcmd(struct ispsoftc *, mbreg_t *); + +void isp_update(struct ispsoftc *); +void isp_update_bus(struct ispsoftc *, int); +void isp_setdfltparm(struct ispsoftc *, int); +void isp_setdfltfcparm(struct ispsoftc *); +int isp_read_nvram(struct ispsoftc *); +int isp_read_nvram_2400(struct ispsoftc *); +void isp_rdnvram_word(struct ispsoftc *, int, u_int16_t *); +void isp_rd_2400_nvram(struct ispsoftc *, u_int32_t, u_int32_t *); +void isp_parse_nvram_1020(struct ispsoftc *, u_int8_t *); +void isp_parse_nvram_1080(struct ispsoftc *, int, u_int8_t *); +void isp_parse_nvram_12160(struct ispsoftc *, int, u_int8_t *); +void isp_fix_nvram_wwns(struct ispsoftc *); +void isp_parse_nvram_2100(struct ispsoftc *, u_int8_t *); +void isp_parse_nvram_2400(struct ispsoftc *, u_int8_t *); /* * Reset Hardware. @@ -161,9 +166,10 @@ void isp_reset(struct ispsoftc *isp) { mbreg_t mbs; - u_int16_t code_org; + u_int32_t code_org, val; int loops, i, dodnld = 1; - char *btype = "????"; + static const char *btype = "????"; + static const char dcrc[] = "Downloaded RISC Code Checksum Failure"; isp->isp_state = ISP_NILSTATE; @@ -203,10 +209,16 @@ isp_reset(struct ispsoftc *isp) /* * Just in case it was paused... */ - ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, + HCCR_2400_CMD_RELEASE); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + } MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_ABOUT_FIRMWARE; - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { isp->isp_romfw_rev[0] = mbs.param[1]; isp->isp_romfw_rev[1] = mbs.param[2]; @@ -216,13 +228,47 @@ isp_reset(struct ispsoftc *isp) isp->isp_touched = 1; } - DISABLE_INTS(isp); + ISP_DISABLE_INTS(isp); + + /* + * Pick an initial maxcmds value which will be used + * to allocate xflist pointer space. It may be changed + * later by the firmware. + */ + if (IS_24XX(isp)) { + isp->isp_maxcmds = 4096; + } else if (IS_2322(isp)) { + isp->isp_maxcmds = 2048; + } else if (IS_23XX(isp) || IS_2200(isp)) { + isp->isp_maxcmds = 1024; + } else { + isp->isp_maxcmds = 512; + } + + /* + * Set up DMA for the request and result queues. + * + * We do this now so we can use the request queue + * for a dma + */ + if (ISP_MBOXDMASETUP(isp) != 0) { + isp_prt(isp, ISP_LOGERR, "Cannot setup DMA"); + return; + } + /* * Set up default request/response queue in-pointer/out-pointer * register indices. */ - if (IS_23XX(isp)) { + if (IS_24XX(isp)) { + isp->isp_rqstinrp = BIU2400_REQINP; + isp->isp_rqstoutrp = BIU2400_REQOUTP; + isp->isp_respinrp = BIU2400_RSPINP; + isp->isp_respoutrp = BIU2400_RSPOUTP; + isp->isp_atioinrp = BIU2400_ATIO_RSPINP; + isp->isp_atiooutrp = BIU2400_ATIO_REQINP; + } else if (IS_23XX(isp)) { isp->isp_rqstinrp = BIU_REQINP; isp->isp_rqstoutrp = BIU_REQOUTP; isp->isp_respinrp = BIU_RSPINP; @@ -238,7 +284,13 @@ isp_reset(struct ispsoftc *isp) * Put the board into PAUSE mode (so we can read the SXP registers * or write FPM/FBM registers). */ - ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_HOST_INT); + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_PAUSE); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + } if (IS_FC(isp)) { switch (isp->isp_type) { @@ -254,17 +306,27 @@ isp_reset(struct ispsoftc *isp) case ISP_HA_FC_2312: btype = "2312"; break; + case ISP_HA_FC_2322: + btype = "2322"; + break; + case ISP_HA_FC_2400: + btype = "2422"; + break; default: break; } - /* - * While we're paused, reset the FPM module and FBM fifos. - */ - ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS); - ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET); - ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS); - ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL); - ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS); + + if (!IS_24XX(isp)) { + /* + * While we're paused, reset the FPM module and FBM + * fifos. + */ + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS); + ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS); + ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS); + } } else if (IS_1240(isp)) { sdparam *sdp = isp->isp_param; btype = "1240"; @@ -447,8 +509,6 @@ isp_reset(struct ispsoftc *isp) */ ISP_RESET0(isp); -again: - /* * Hit the chip over the head with hammer, * and give the ISP a chance to recover. @@ -470,6 +530,44 @@ again: DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT); + } else if (IS_24XX(isp)) { + /* + * Stop DMA and wait for it to stop. + */ + ISP_WRITE(isp, BIU2400_CSR, BIU2400_DMA_STOP|(3 << 4)); + for (val = loops = 0; loops < 30000; loops++) { + USEC_DELAY(10); + val = ISP_READ(isp, BIU2400_CSR); + if ((val & BIU2400_DMA_ACTIVE) == 0) { + break; + } + } + if (val & BIU2400_DMA_ACTIVE) { + ISP_RESET0(isp); + isp_prt(isp, ISP_LOGERR, "DMA Failed to Stop on Reset"); + return; + } + /* + * Hold it in SOFT_RESET and STOP state for 100us. + */ + ISP_WRITE(isp, BIU2400_CSR, + BIU2400_SOFT_RESET|BIU2400_DMA_STOP|(3 << 4)); + USEC_DELAY(100); + for (loops = 0; loops < 10000; loops++) { + USEC_DELAY(5); + val = ISP_READ(isp, OUTMAILBOX0); + } + for (val = loops = 0; loops < 500000; loops ++) { + val = ISP_READ(isp, BIU2400_CSR); + if ((val & BIU2400_SOFT_RESET) == 0) { + break; + } + } + if (val & BIU2400_SOFT_RESET) { + ISP_RESET0(isp); + isp_prt(isp, ISP_LOGERR, "Failed to come out of reset"); + return; + } } else { ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET); /* @@ -494,8 +592,13 @@ again: loops = MBOX_DELAY_COUNT; for (;;) { if (IS_SCSI(isp)) { - if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET)) + if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET)) { break; + } + } else if (IS_24XX(isp)) { + if (ISP_READ(isp, OUTMAILBOX0) == 0) { + break; + } } else { if (!(ISP_READ(isp, BIU2100_CSR) & BIU2100_SOFT_RESET)) break; @@ -503,6 +606,7 @@ again: USEC_DELAY(100); if (--loops < 0) { ISP_DUMPREGS(isp, "chip reset timed out"); + ISP_RESET0(isp); return; } } @@ -514,24 +618,41 @@ again: if (IS_SCSI(isp)) { ISP_WRITE(isp, BIU_CONF1, 0); - } else { + } else if (!IS_24XX(isp)) { ISP_WRITE(isp, BIU2100_CSR, 0); } /* * Reset RISC Processor */ - ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); - USEC_DELAY(100); - /* Clear semaphore register (just to be sure) */ - ISP_WRITE(isp, BIU_SEMA, 0); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RESET); + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RELEASE); + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RESET); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); + USEC_DELAY(100); + ISP_WRITE(isp, BIU_SEMA, 0); + } + /* - * Establish some initial burst rate stuff. - * (only for the 1XX0 boards). This really should - * be done later after fetching from NVRAM. + * Post-RISC Reset stuff. */ - if (IS_SCSI(isp)) { + if (IS_24XX(isp)) { + for (val = loops = 0; loops < 5000000; loops++) { + USEC_DELAY(5); + val = ISP_READ(isp, OUTMAILBOX0); + if (val == 0) { + break; + } + } + if (val != 0) { + ISP_RESET0(isp); + isp_prt(isp, ISP_LOGERR, "reset didn't clear"); + return; + } + } else if (IS_SCSI(isp)) { u_int16_t tmp = isp->isp_mdvec->dv_conf1; /* * Busted FIFO. Turn off all but burst enables. @@ -544,30 +665,36 @@ again: 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); + if (SDPARAM(isp)->isp_ptisp) { + if (SDPARAM(isp)->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); } - /* - * PTI specific register - */ - ISP_WRITE(isp, RISC_EMB, DUAL_BANK) -#else - ISP_WRITE(isp, RISC_MTR, 0x1212); -#endif + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); } else { ISP_WRITE(isp, RISC_MTR2100, 0x1212); if (IS_2200(isp) || IS_23XX(isp)) { ISP_WRITE(isp, HCCR, HCCR_2X00_DISABLE_PARITY_PAUSE); } + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); } - ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); /* release paused processor */ + ISP_WRITE(isp, isp->isp_rqstinrp, 0); + ISP_WRITE(isp, isp->isp_rqstoutrp, 0); + ISP_WRITE(isp, isp->isp_respinrp, 0); + ISP_WRITE(isp, isp->isp_respoutrp, 0); + /* * Do MD specific post initialization @@ -580,13 +707,14 @@ again: * Avoid doing this on the 2312 because you can generate a PCI * parity error (chip breakage). */ - if (IS_23XX(isp)) { - USEC_DELAY(5); + if (IS_2312(isp)) { + USEC_DELAY(100); } else { loops = MBOX_DELAY_COUNT; while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) { USEC_DELAY(100); if (--loops < 0) { + ISP_RESET0(isp); isp_prt(isp, ISP_LOGERR, "MBOX_BUSY never cleared on reset"); return; @@ -605,12 +733,14 @@ again: */ MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_NO_OP; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + ISP_RESET0(isp); return; } - if (IS_SCSI(isp)) { + if (IS_SCSI(isp) || IS_24XX(isp)) { MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_MAILBOX_REG_TEST; mbs.param[1] = 0xdead; @@ -618,13 +748,18 @@ again: mbs.param[3] = 0xffff; mbs.param[4] = 0x1111; mbs.param[5] = 0xa5a5; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.param[6] = 0x0000; + mbs.param[7] = 0x0000; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + ISP_RESET0(isp); return; } if (mbs.param[1] != 0xdead || mbs.param[2] != 0xbeef || mbs.param[3] != 0xffff || mbs.param[4] != 0x1111 || mbs.param[5] != 0xa5a5) { + ISP_RESET0(isp); isp_prt(isp, ISP_LOGERR, "Register Test Failed (0x%x 0x%x 0x%x 0x%x 0x%x)", mbs.param[1], mbs.param[2], mbs.param[3], @@ -648,26 +783,193 @@ again: dodnld = 0; } - if (IS_23XX(isp)) + if (IS_24XX(isp)) { + code_org = ISP_CODE_ORG_2400; + } else if (IS_23XX(isp)) { code_org = ISP_CODE_ORG_2300; - else + } else { code_org = ISP_CODE_ORG; + } + + if (dodnld && IS_24XX(isp)) { + const u_int32_t *ptr = isp->isp_mdvec->dv_ispfw; + + /* + * NB: Whatever you do do, do *not* issue the VERIFY FIRMWARE + * NB: command to the 2400 while loading new firmware. This + * NB: causes the new f/w to start and immediately crash back + * NB: to the ROM. + */ + + /* + * Keep loading until we run out of f/w. + */ + code_org = ptr[2]; /* 1st load address is our start addr */ + + for (;;) { + u_int32_t la, wi, wl; + + isp_prt(isp, ISP_LOGDEBUG0, + "load 0x%x words of code at load address 0x%x", + ptr[3], ptr[2]); + + wi = 0; + la = ptr[2]; + wl = ptr[3]; + + while (wi < ptr[3]) { + u_int32_t *cp; + u_int32_t nw; + + nw = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) >> 2; + if (nw > wl) { + nw = wl; + } + cp = isp->isp_rquest; + for (i = 0; i < nw; i++) { + ISP_IOXPUT_32(isp, ptr[wi++], &cp[i]); + wl--; + } + MEMORYBARRIER(isp, SYNC_REQUEST, + 0, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp))); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_LOAD_RISC_RAM; + mbs.param[1] = la; + mbs.param[2] = DMA_WD1(isp->isp_rquest_dma); + mbs.param[3] = DMA_WD0(isp->isp_rquest_dma); + mbs.param[4] = nw >> 16; + mbs.param[5] = nw; + mbs.param[6] = DMA_WD3(isp->isp_rquest_dma); + mbs.param[7] = DMA_WD2(isp->isp_rquest_dma); + mbs.param[8] = la >> 16; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "F/W Risc Ram Load Failed"); + ISP_RESET0(isp); + return; + } + la += nw; + } + + if (ptr[1] == 0) { + break; + } + ptr += ptr[3]; + } + isp->isp_loaded_fw = 1; + } else if (dodnld && IS_23XX(isp)) { + const u_int16_t *ptr = isp->isp_mdvec->dv_ispfw; + u_int16_t wi, wl, segno; + u_int32_t la; + + la = code_org; + segno = 0; + + for (;;) { + u_int32_t nxtaddr; + + isp_prt(isp, ISP_LOGDEBUG0, + "load 0x%x words of code at load address 0x%x", + ptr[3], la); + + wi = 0; + wl = ptr[3]; + + while (wi < ptr[3]) { + u_int16_t *cp; + u_int32_t nw; + + nw = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) >> 1; + if (nw > wl) { + nw = wl; + } + if (nw > (1 << 15)) { + nw = 1 << 15; + } + cp = isp->isp_rquest; + for (i = 0; i < nw; i++) { + ISP_IOXPUT_16(isp, ptr[wi++], &cp[i]); + wl--; + } + MEMORYBARRIER(isp, SYNC_REQUEST, + 0, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp))); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_LOAD_RISC_RAM; + mbs.param[1] = la; + mbs.param[2] = DMA_WD1(isp->isp_rquest_dma); + mbs.param[3] = DMA_WD0(isp->isp_rquest_dma); + mbs.param[4] = nw; + mbs.param[6] = DMA_WD3(isp->isp_rquest_dma); + mbs.param[7] = DMA_WD2(isp->isp_rquest_dma); + mbs.param[8] = la >> 16; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "F/W Risc Ram Load Failed"); + ISP_RESET0(isp); + return; + } + la += nw; + } + + if (!IS_2322(isp)) { + /* + * Verify that it downloaded correctly. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_VERIFY_CHECKSUM; + mbs.param[1] = code_org; + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, dcrc); + ISP_RESET0(isp); + return; + } + break; + } + + if (++segno == 3) { + break; + } + + /* + * If we're a 2322, the firmware actually comes in + * three chunks. We loaded the first at the code_org + * address. The other two chunks, which follow right + * after each other in memory here, get loaded at + * addresses specfied at offset 0x9..0xB. + */ - if (dodnld) { - isp->isp_mbxworkp = (void *) &isp->isp_mdvec->dv_ispfw[1]; - isp->isp_mbxwrk0 = isp->isp_mdvec->dv_ispfw[3] - 1; + nxtaddr = ptr[3]; + ptr = &ptr[nxtaddr]; + la = ptr[5] | ((ptr[4] & 0x3f) << 16); + } + isp->isp_loaded_fw = 1; + } else if (dodnld) { + union { + const u_int16_t *cp; + u_int16_t *np; + } u; + u.cp = isp->isp_mdvec->dv_ispfw; + isp->isp_mbxworkp = &u.np[1]; + isp->isp_mbxwrk0 = u.np[3] - 1; isp->isp_mbxwrk1 = code_org + 1; MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_WRITE_RAM_WORD; mbs.param[1] = code_org; - mbs.param[2] = isp->isp_mdvec->dv_ispfw[0]; - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.param[2] = u.np[0]; + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGERR, "F/W download failed at word %d", isp->isp_mbxwrk1 - code_org); - dodnld = 0; - goto again; + ISP_RESET0(isp); + return; } /* * Verify that it downloaded correctly. @@ -675,9 +977,11 @@ again: MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_VERIFY_CHECKSUM; mbs.param[1] = code_org; - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - isp_prt(isp, ISP_LOGERR, "Ram Checksum Failure"); + isp_prt(isp, ISP_LOGERR, dcrc); + ISP_RESET0(isp); return; } isp->isp_loaded_fw = 1; @@ -695,13 +999,40 @@ again: MEMZERO(&mbs, sizeof (mbs)); + mbs.timeout = 1000000; mbs.param[0] = MBOX_EXEC_FIRMWARE; - mbs.param[1] = code_org; - isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (IS_24XX(isp)) { + mbs.param[1] = code_org >> 16; + mbs.param[2] = code_org; + if (isp->isp_loaded_fw) { + mbs.param[3] = 0; + } else { + mbs.param[3] = 1; + } + } else if (IS_2322(isp)) { + mbs.param[1] = code_org; + if (isp->isp_loaded_fw) { + mbs.param[2] = 0; + } else { + mbs.param[2] = 1; + } + } else { + mbs.param[1] = code_org; + } + + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (IS_2322(isp) || IS_24XX(isp)) { + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + ISP_RESET0(isp); + return; + } + } + /* - * Give it a chance to start. + * Give it a chance to finish starting up. */ - USEC_DELAY(500); + USEC_DELAY(250000); if (IS_SCSI(isp)) { /* @@ -711,15 +1042,24 @@ again: MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_SET_CLOCK_RATE; mbs.param[1] = isp->isp_clock; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); /* we will try not to care if this fails */ } } MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_ABOUT_FIRMWARE; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + ISP_RESET0(isp); + return; + } + + if (IS_24XX(isp) && mbs.param[1] == 0xdead) { + isp_prt(isp, ISP_LOGERR, "f/w didn't *really* start"); + ISP_RESET0(isp); return; } @@ -744,7 +1084,8 @@ again: isp->isp_fwrev[1] = mbs.param[2]; isp->isp_fwrev[2] = mbs.param[3]; } - isp_prt(isp, ISP_LOGCONFIG, + + isp_prt(isp, ISP_LOGALL, "Board Type %s, Chip Revision 0x%x, %s F/W Revision %d.%d.%d", btype, isp->isp_revision, dodnld? "loaded" : "resident", isp->isp_fwrev[0], isp->isp_fwrev[1], isp->isp_fwrev[2]); @@ -755,9 +1096,9 @@ again: * than 1.17.0, unless it's the firmware we specifically * are loading. * - * Note that all 22XX and 23XX f/w is greater than 1.X.0. + * Note that all 22XX and later f/w is greater than 1.X.0. */ - if (!(ISP_FW_NEWER_THAN(isp, 1, 17, 0))) { + if ((ISP_FW_OLDER_THAN(isp, 1, 17, 1))) { #ifdef USE_SMALLER_2100_FIRMWARE FCPARAM(isp)->isp_fwattr = ISP_FW_ATTR_SCCLUN; #else @@ -768,9 +1109,27 @@ again: isp_prt(isp, ISP_LOGDEBUG0, "Firmware Attributes = 0x%x", mbs.param[6]); } - if (ISP_READ(isp, BIU2100_CSR) & BIU2100_PCI64) { - isp_prt(isp, ISP_LOGCONFIG, - "Installed in 64-Bit PCI slot"); + FCPARAM(isp)->isp_2klogin = 0; + FCPARAM(isp)->isp_sccfw = 0; + FCPARAM(isp)->isp_tmode = 0; + if (IS_24XX(isp)) { + FCPARAM(isp)->isp_2klogin = 1; + FCPARAM(isp)->isp_sccfw = 1; + FCPARAM(isp)->isp_tmode = 1; + } else { + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + FCPARAM(isp)->isp_sccfw = 1; + } + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_2KLOGINS) { + FCPARAM(isp)->isp_2klogin = 1; + FCPARAM(isp)->isp_sccfw = 1; + } + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_TMODE) { + FCPARAM(isp)->isp_tmode = 1; + } + } + if (FCPARAM(isp)->isp_2klogin) { + isp_prt(isp, ISP_LOGCONFIG, "2K Logins Supported"); } } @@ -781,24 +1140,23 @@ again: isp->isp_romfw_rev[2]); } - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_GET_FIRMWARE_STATUS; - isp_mboxcmd(isp, &mbs, MBLOGALL); - if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { - return; + if (!IS_24XX(isp)) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_FIRMWARE_STATUS; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + ISP_RESET0(isp); + return; + } + if (isp->isp_maxcmds >= mbs.param[2]) { + isp->isp_maxcmds = mbs.param[2]; + } } - isp->isp_maxcmds = mbs.param[2]; - isp_prt(isp, ISP_LOGINFO, - "%d max I/O commands supported", mbs.param[2]); + isp_prt(isp, ISP_LOGCONFIG, + "%d max I/O command limit set", isp->isp_maxcmds); isp_fw_state(isp); - /* - * Set up DMA for the request and result mailboxes. - */ - if (ISP_MBOXDMASETUP(isp) != 0) { - isp_prt(isp, ISP_LOGERR, "Cannot setup DMA"); - return; - } isp->isp_state = ISP_RESETSTATE; /* @@ -829,12 +1187,24 @@ again: isp->isp_maxluns = 8; } } else { - if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + if (FCPARAM(isp)->isp_sccfw) { isp->isp_maxluns = 16384; } else { isp->isp_maxluns = 16; } } + /* + * Must do this first to get defaults established. + */ + if (IS_SCSI(isp)) { + isp_setdfltparm(isp, 0); + if (IS_DUALBUS(isp)) { + isp_setdfltparm(isp, 1); + } + } else { + isp_setdfltfcparm(isp); + } + } /* @@ -846,21 +1216,27 @@ again: void isp_init(struct ispsoftc *isp) { - /* - * Must do this first to get defaults established. - */ - isp_setdfltparm(isp, 0); - if (IS_DUALBUS(isp)) { - isp_setdfltparm(isp, 1); - } if (IS_FC(isp)) { - isp_fibre_init(isp); + /* + * Do this *before* initializing the firmware. + */ + ISP_MARK_PORTDB(isp, 0); + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + + if (isp->isp_role != ISP_ROLE_NONE) { + if (IS_24XX(isp)) { + isp_fibre_init_2400(isp); + } else { + isp_fibre_init(isp); + } + } } else { isp_scsi_init(isp); } } -static void +void isp_scsi_init(struct ispsoftc *isp) { sdparam *sdp_chan0, *sdp_chan1; @@ -898,8 +1274,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[2] = sdp_chan0->isp_retry_delay; mbs.param[6] = sdp_chan1->isp_retry_count; mbs.param[7] = sdp_chan1->isp_retry_delay; - - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -911,7 +1287,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME; mbs.param[1] = sdp_chan0->isp_async_data_setup; mbs.param[2] = sdp_chan1->isp_async_data_setup; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -927,8 +1304,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[2] = (sdp_chan1->isp_req_ack_active_neg << 4) | (sdp_chan1->isp_data_line_active_neg << 5); - - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGERR, "failed to set active negation state (%d,%d), (%d,%d)", @@ -948,7 +1325,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT; mbs.param[1] = sdp_chan0->isp_tag_aging; mbs.param[2] = sdp_chan1->isp_tag_aging; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGERR, "failed to set tag age limit (%d,%d)", sdp_chan0->isp_tag_aging, sdp_chan1->isp_tag_aging); @@ -962,7 +1340,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[0] = MBOX_SET_SELECT_TIMEOUT; mbs.param[1] = sdp_chan0->isp_selection_timeout; mbs.param[2] = sdp_chan1->isp_selection_timeout; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -985,7 +1364,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[4] = 0; mbs.param[6] = DMA_WD3(isp->isp_result_dma); mbs.param[7] = DMA_WD2(isp->isp_result_dma); - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -999,7 +1379,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[5] = 0; mbs.param[6] = DMA_WD3(isp->isp_result_dma); mbs.param[7] = DMA_WD2(isp->isp_result_dma); - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -1011,7 +1392,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[2] = DMA_WD1(isp->isp_result_dma); mbs.param[3] = DMA_WD0(isp->isp_result_dma); mbs.param[4] = 0; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -1023,7 +1405,8 @@ isp_scsi_init(struct ispsoftc *isp) mbs.param[2] = DMA_WD1(isp->isp_rquest_dma); mbs.param[3] = DMA_WD0(isp->isp_rquest_dma); mbs.param[5] = 0; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -1048,14 +1431,13 @@ isp_scsi_init(struct ispsoftc *isp) if (IS_ULTRA2(isp) || IS_1240(isp)) mbs.param[1] |= FW_FEATURE_RIO_16BIT; #else -#ifndef ISP_NO_FASTPOST if (IS_ULTRA2(isp) || IS_1240(isp)) mbs.param[1] |= FW_FEATURE_FAST_POST; #endif -#endif if (mbs.param[1] != 0) { u_int16_t sfeat = mbs.param[1]; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGINFO, "Enabled FW features (0x%x)", sfeat); @@ -1068,7 +1450,7 @@ isp_scsi_init(struct ispsoftc *isp) isp->isp_state = ISP_INITSTATE; } -static void +void isp_scsi_channel_init(struct ispsoftc *isp, int channel) { sdparam *sdp; @@ -1084,7 +1466,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel) MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_SET_INIT_SCSI_ID; mbs.param[1] = (channel << 7) | sdp->isp_initiator_id; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return; } @@ -1140,7 +1523,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel) "Initial Settings bus%d tgt%d flags 0x%x off 0x%x per 0x%x", channel, tgt, mbs.param[2], mbs.param[3] >> 8, mbs.param[3] & 0xff); - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { sdf = DPARM_SAFE_DFLT; MEMZERO(&mbs, sizeof (mbs)); @@ -1148,7 +1532,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel) mbs.param[1] = (tgt << 8) | (channel << 15); mbs.param[2] = sdf; mbs.param[3] = 0; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { continue; } @@ -1172,7 +1557,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel) mbs.param[1] = (channel << 15) | (tgt << 8) | lun; mbs.param[2] = sdp->isp_max_queue_depth; mbs.param[3] = sdp->isp_devparam[tgt].exc_throttle; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { break; } @@ -1189,37 +1575,21 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel) /* * Fibre Channel specific initialization. - * - * Locks are held before coming here. */ -static void +void isp_fibre_init(struct ispsoftc *isp) { fcparam *fcp; isp_icb_t local, *icbp = &local; mbreg_t mbs; - int loopid; + int ownloopid; u_int64_t nwwn, pwwn; fcp = isp->isp_param; - /* - * Do this *before* initializing the firmware. - */ - isp_mark_getpdb_all(isp); - fcp->isp_fwstate = FW_CONFIG_WAIT; - fcp->isp_loopstate = LOOP_NIL; - - /* - * If we have no role (neither target nor initiator), return. - */ - if (isp->isp_role == ISP_ROLE_NONE) { - return; - } - - loopid = fcp->isp_loopid; MEMZERO(icbp, sizeof (*icbp)); icbp->icb_version = ICB_VERSION1; + icbp->icb_fwoptions = fcp->isp_fwoptions; /* * Firmware Options are either retrieved from NVRAM or @@ -1231,8 +1601,8 @@ isp_fibre_init(struct ispsoftc *isp) /* * If this is a 2100 < revision 5, we have to turn off FAIRNESS. */ - if ((isp->isp_type == ISP_HA_FC_2100) && isp->isp_revision < 5) { - fcp->isp_fwoptions &= ~ICBOPT_FAIRNESS; + if (IS_2100(isp) && isp->isp_revision < 5) { + icbp->icb_fwoptions &= ~ICBOPT_FAIRNESS; } /* @@ -1241,27 +1611,29 @@ isp_fibre_init(struct ispsoftc *isp) * a LIP- this is a known f/w bug for 2100 f/w less than 1.17.0. */ if (!ISP_FW_NEWER_THAN(isp, 1, 17, 0)) { - fcp->isp_fwoptions |= ICBOPT_FULL_LOGIN; + icbp->icb_fwoptions |= ICBOPT_FULL_LOGIN; } /* * Insist on Port Database Update Async notifications */ - fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE; + icbp->icb_fwoptions |= ICBOPT_PDBCHANGE_AE; /* * Make sure that target role reflects into fwoptions. */ if (isp->isp_role & ISP_ROLE_TARGET) { - fcp->isp_fwoptions |= ICBOPT_TGT_ENABLE; + icbp->icb_fwoptions |= ICBOPT_TGT_ENABLE; } else { - fcp->isp_fwoptions &= ~ICBOPT_TGT_ENABLE; + icbp->icb_fwoptions &= ~ICBOPT_TGT_ENABLE; + } + + if (isp->isp_role & ISP_ROLE_INITIATOR) { + icbp->icb_fwoptions &= ~ICBOPT_INI_DISABLE; + } else { + icbp->icb_fwoptions |= ICBOPT_INI_DISABLE; } - /* - * Propagate all of this into the ICB structure. - */ - 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) { @@ -1285,7 +1657,21 @@ isp_fibre_init(struct ispsoftc *isp) } icbp->icb_retry_delay = fcp->isp_retry_delay; icbp->icb_retry_count = fcp->isp_retry_count; - icbp->icb_hardaddr = loopid; + icbp->icb_hardaddr = fcp->isp_loopid; + ownloopid = (isp->isp_confopts & ISP_CFG_OWNLOOPID) != 0; + if (icbp->icb_hardaddr > 125) { + icbp->icb_hardaddr = 0; + ownloopid = 0; + } + + /* + * Our life seems so much better with 2200s and later with + * the latest f/w if we set Hard Address. + */ + if (ownloopid || ISP_FW_NEWER_THAN(isp, 2, 2, 5)) { + icbp->icb_fwoptions |= ICBOPT_HARD_ADDRESS; + } + /* * Right now we just set extended options to prefer point-to-point * over loop based upon some soft config options. @@ -1311,26 +1697,21 @@ isp_fibre_init(struct ispsoftc *isp) icbp->icb_xfwoptions |= ICBXOPT_LOOP_2_PTP; break; } - if (IS_23XX(isp)) { + if (IS_2200(isp)) { + icbp->icb_fwoptions &= ~ICBOPT_FAST_POST; + } else { /* * QLogic recommends that FAST Posting be turned * off for 23XX cards and instead allow the HBA * to write response queue entries and interrupt * after a delay (ZIO). - * - * If we set ZIO, it will disable fast posting, - * so we don't need to clear it in fwoptions. - */ - icbp->icb_xfwoptions |= ICBXOPT_ZIO; -#if 0 - /* - * Values, in 100us increments. The default - * is 2 (200us) if a value 0 (default) is - * selected. */ - icbp->icb_idelaytimer = 2; -#endif - + icbp->icb_fwoptions &= ~ICBOPT_FAST_POST; + if ((fcp->isp_xfwoptions & ICBXOPT_TIMER_MASK) == + ICBXOPT_ZIO) { + icbp->icb_xfwoptions |= ICBXOPT_ZIO; + icbp->icb_idelaytimer = 10; + } if (isp->isp_confopts & ISP_CFG_ONEGB) { icbp->icb_zfwoptions |= ICBZOPT_RATE_ONEGB; } else if (isp->isp_confopts & ISP_CFG_TWOGB) { @@ -1338,32 +1719,15 @@ isp_fibre_init(struct ispsoftc *isp) } else { icbp->icb_zfwoptions |= ICBZOPT_RATE_AUTO; } + if (fcp->isp_zfwoptions & ICBZOPT_50_OHM) { + icbp->icb_zfwoptions |= ICBZOPT_50_OHM; + } } } -#ifndef ISP_NO_RIO_FC - /* - * RIO seems to be enabled in 2100s for fw >= 1.17.0. - * - * I've had some questionable problems with RIO on 2200. - * More specifically, on a 2204 I had problems with RIO - * on a Linux system where I was dropping commands right - * and left. It's not clear to me what the actual problem - * was. - * - * 23XX Cards do not support RIO. Instead they support ZIO. - */ -#if 0 - if (!IS_23XX(isp) && ISP_FW_NEWER_THAN(isp, 1, 17, 0)) { - icbp->icb_xfwoptions |= ICBXOPT_RIO_16BIT; - icbp->icb_racctimer = 4; - icbp->icb_idelaytimer = 8; - } -#endif -#endif /* - * For 22XX > 2.1.26 && 23XX, set someoptions. + * For 22XX > 2.1.26 && 23XX, set some options. * XXX: Probably okay for newer 2100 f/w too. */ if (ISP_FW_NEWER_THAN(isp, 2, 26, 0)) { @@ -1377,16 +1741,14 @@ isp_fibre_init(struct ispsoftc *isp) mbs.param[1] = 0xb; mbs.param[2] = 0; mbs.param[3] = 0; - isp_mboxcmd(isp, &mbs, MBLOGALL); - } - icbp->icb_logintime = 30; /* 30 second login timeout */ - - if (IS_23XX(isp)) { - ISP_WRITE(isp, isp->isp_rqstinrp, 0); - ISP_WRITE(isp, isp->isp_rqstoutrp, 0); - ISP_WRITE(isp, isp->isp_respinrp, 0); - ISP_WRITE(isp, isp->isp_respoutrp, 0); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } } + icbp->icb_logintime = ICB_LOGIN_TOV; + icbp->icb_lunetimeout = ICB_LUN_ENABLE_TOV; nwwn = ISP_NODEWWN(isp); pwwn = ISP_PORTWWN(isp); @@ -1400,12 +1762,25 @@ isp_fibre_init(struct ispsoftc *isp) ((u_int32_t) (nwwn & 0xffffffff)), ((u_int32_t) (pwwn >> 32)), ((u_int32_t) (pwwn & 0xffffffff))); + } else if (pwwn) { + icbp->icb_fwoptions &= ~ICBOPT_BOTH_WWNS; + MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn); + isp_prt(isp, ISP_LOGDEBUG1, + "Setting ICB Port 0x%08x%08x", + ((u_int32_t) (pwwn >> 32)), + ((u_int32_t) (pwwn & 0xffffffff))); } else { - isp_prt(isp, ISP_LOGDEBUG1, "Not using any WWNs"); - icbp->icb_fwoptions &= ~(ICBOPT_BOTH_WWNS|ICBOPT_FULL_LOGIN); + isp_prt(isp, ISP_LOGERR, "No valid WWNs to use"); + return; } icbp->icb_rqstqlen = RQUEST_QUEUE_LEN(isp); + if (icbp->icb_rqstqlen < 1) { + isp_prt(isp, ISP_LOGERR, "bad request queue length"); + } icbp->icb_rsltqlen = RESULT_QUEUE_LEN(isp); + if (icbp->icb_rsltqlen < 1) { + isp_prt(isp, ISP_LOGERR, "bad result queue length"); + } icbp->icb_rqstaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_rquest_dma); icbp->icb_rqstaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_rquest_dma); icbp->icb_rqstaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_rquest_dma); @@ -1414,6 +1789,7 @@ isp_fibre_init(struct ispsoftc *isp) icbp->icb_respaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_result_dma); icbp->icb_respaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_result_dma); icbp->icb_respaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_result_dma); + isp_prt(isp, ISP_LOGDEBUG0, "isp_fibre_init: fwopt 0x%x xfwopt 0x%x zfwopt 0x%x", icbp->icb_fwoptions, icbp->icb_xfwoptions, icbp->icb_zfwoptions); @@ -1430,17 +1806,21 @@ isp_fibre_init(struct ispsoftc *isp) mbs.param[3] = DMA_WD0(fcp->isp_scdma); mbs.param[6] = DMA_WD3(fcp->isp_scdma); mbs.param[7] = DMA_WD2(fcp->isp_scdma); + mbs.logval = MBLOGALL; + mbs.timeout = 30 * 1000000; isp_prt(isp, ISP_LOGDEBUG0, "INIT F/W from %p (%08x%08x)", - fcp->isp_scratch, (uint32_t) ((uint64_t)fcp->isp_scdma >> 32), - (uint32_t) fcp->isp_scdma); - isp_mboxcmd(isp, &mbs, MBLOGALL); + fcp->isp_scratch, (u_int32_t) ((u_int64_t)fcp->isp_scdma >> 32), + (u_int32_t) fcp->isp_scdma); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (*icbp)); + isp_mboxcmd(isp, &mbs); FC_SCRATCH_RELEASE(isp); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_print_bytes(isp, "isp_fibre_init", sizeof (*icbp), icbp); return; } - isp->isp_reqidx = isp->isp_reqodx = 0; + isp->isp_reqidx = 0; + isp->isp_reqodx = 0; isp->isp_residx = 0; - isp->isp_sendmarker = 1; /* * Whatever happens, we're now committed to being here. @@ -1448,84 +1828,615 @@ isp_fibre_init(struct ispsoftc *isp) isp->isp_state = ISP_INITSTATE; } -/* - * Fibre Channel Support- get the port database for the id. - * - * Locks are held before coming here. Return 0 if success, - * else failure. - */ - -static int -isp_getmap(struct ispsoftc *isp, fcpos_map_t *map) +void +isp_fibre_init_2400(struct ispsoftc *isp) { - fcparam *fcp = (fcparam *) isp->isp_param; + fcparam *fcp; + isp_icb_2400_t local, *icbp = &local; mbreg_t mbs; + int ownloopid; + u_int64_t nwwn, pwwn; + + fcp = isp->isp_param; + + /* + * Turn on LIP F8 async event (1) + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_FIRMWARE_OPTIONS; + mbs.param[1] = 1; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + /* + * XXX: This should be applied to icb- not fwoptions + */ + if (isp->isp_role & ISP_ROLE_TARGET) { + fcp->isp_fwoptions |= ICB2400_OPT1_TGT_ENABLE; + } else { + fcp->isp_fwoptions &= ~ICB2400_OPT1_TGT_ENABLE; + } + + if (isp->isp_role & ISP_ROLE_INITIATOR) { + fcp->isp_fwoptions &= ~ICB2400_OPT1_INI_DISABLE; + } else { + fcp->isp_fwoptions |= ICB2400_OPT1_INI_DISABLE; + } + + MEMZERO(icbp, sizeof (*icbp)); + icbp->icb_version = ICB_VERSION1; + icbp->icb_maxfrmlen = fcp->isp_maxfrmlen; + if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || + icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) { + isp_prt(isp, ISP_LOGERR, + "bad frame length (%d) from NVRAM- using %d", + fcp->isp_maxfrmlen, ICB_DFLT_FRMLEN); + icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN; + } + + icbp->icb_execthrottle = fcp->isp_execthrottle; + if (icbp->icb_execthrottle < 1) { + isp_prt(isp, ISP_LOGERR, + "bad execution throttle of %d- using 16", + fcp->isp_execthrottle); + icbp->icb_execthrottle = ICB_DFLT_THROTTLE; + } + + if (isp->isp_role & ISP_ROLE_TARGET) { + /* + * Get current resource count + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_RESOURCE_COUNT; + mbs.obits = 0x4cf; + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + icbp->icb_xchgcnt = mbs.param[3]; + } - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_GET_FC_AL_POSITION_MAP; + icbp->icb_fwoptions1 = fcp->isp_fwoptions; + + icbp->icb_hardaddr = fcp->isp_loopid; + ownloopid = (isp->isp_confopts & ISP_CFG_OWNLOOPID) != 0; + if (icbp->icb_hardaddr > 125) { + icbp->icb_hardaddr = 0; + ownloopid = 0; + } + if (ownloopid) { + icbp->icb_fwoptions1 |= ICB2400_OPT1_HARD_ADDRESS; + } + + icbp->icb_fwoptions2 = fcp->isp_xfwoptions; + switch(isp->isp_confopts & ISP_CFG_PORT_PREF) { + case ISP_CFG_NPORT: + icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK; + icbp->icb_fwoptions2 |= ICB2400_OPT2_PTP_2_LOOP; + break; + case ISP_CFG_NPORT_ONLY: + icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK; + icbp->icb_fwoptions2 |= ICB2400_OPT2_PTP_ONLY; + break; + case ISP_CFG_LPORT_ONLY: + icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK; + icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_ONLY; + break; + default: + icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK; + icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_2_PTP; + break; + } + + switch (icbp->icb_fwoptions2 & ICB2400_OPT2_TIMER_MASK) { + case ICB2400_OPT2_ZIO: + case ICB2400_OPT2_ZIO1: + icbp->icb_idelaytimer = 0; + break; + case 0: + break; + default: + isp_prt(isp, ISP_LOGWARN, "bad value %x in fwopt2 timer field", + icbp->icb_fwoptions2 & ICB2400_OPT2_TIMER_MASK); + icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TIMER_MASK; + break; + } + + icbp->icb_fwoptions3 = fcp->isp_zfwoptions; + icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_AUTO; + if (isp->isp_confopts & ISP_CFG_ONEGB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_ONEGB; + } else if (isp->isp_confopts & ISP_CFG_TWOGB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_TWOGB; + } else if (isp->isp_confopts & ISP_CFG_FOURGB) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_FOURGB; + } else { + icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_AUTO; + } + + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) { + icbp->icb_fwoptions3 |= ICB2400_OPT3_SOFTID; + } + icbp->icb_logintime = ICB_LOGIN_TOV; + + nwwn = ISP_NODEWWN(isp); + pwwn = ISP_PORTWWN(isp); + + if (nwwn && pwwn) { + icbp->icb_fwoptions1 |= ICB2400_OPT1_BOTH_WWNS; + MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, nwwn); + MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn); + isp_prt(isp, ISP_LOGDEBUG1, + "Setting ICB Node 0x%08x%08x Port 0x%08x%08x", + ((u_int32_t) (nwwn >> 32)), + ((u_int32_t) (nwwn & 0xffffffff)), + ((u_int32_t) (pwwn >> 32)), + ((u_int32_t) (pwwn & 0xffffffff))); + } else if (pwwn) { + icbp->icb_fwoptions1 &= ~ICB2400_OPT1_BOTH_WWNS; + MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn); + isp_prt(isp, ISP_LOGDEBUG1, + "Setting ICB Port 0x%08x%08x", + ((u_int32_t) (pwwn >> 32)), + ((u_int32_t) (pwwn & 0xffffffff))); + } else { + isp_prt(isp, ISP_LOGERR, "No valid WWNs to use"); + return; + } + icbp->icb_retry_count = fcp->isp_retry_count; + + icbp->icb_rqstqlen = RQUEST_QUEUE_LEN(isp); + if (icbp->icb_rqstqlen < 8) { + isp_prt(isp, ISP_LOGERR, "bad request queue length %d", + icbp->icb_rqstqlen); + return; + } + icbp->icb_rsltqlen = RESULT_QUEUE_LEN(isp); + if (icbp->icb_rsltqlen < 8) { + isp_prt(isp, ISP_LOGERR, "bad result queue length %d", + icbp->icb_rsltqlen); + return; + } + icbp->icb_rqstaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_rquest_dma); + + icbp->icb_respaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_result_dma); + +#ifdef ISP_TARGET_MODE + if (isp->isp_role & ISP_ROLE_TARGET) { + icbp->icb_atioqlen = RESULT_QUEUE_LEN(isp); + if (icbp->icb_atioqlen < 8) { + isp_prt(isp, ISP_LOGERR, "bad ATIO queue length %d", + icbp->icb_atioqlen); + return; + } + icbp->icb_atioqaddr[RQRSP_ADDR0015] = + DMA_WD0(isp->isp_atioq_dma); + icbp->icb_atioqaddr[RQRSP_ADDR1631] = + DMA_WD1(isp->isp_atioq_dma); + icbp->icb_atioqaddr[RQRSP_ADDR3247] = + DMA_WD2(isp->isp_atioq_dma); + icbp->icb_atioqaddr[RQRSP_ADDR4863] = + DMA_WD3(isp->isp_atioq_dma); + isp_prt(isp, ISP_LOGDEBUG0, + "isp_fibre_init_2400: atioq %04x%04x%04x%04x", + DMA_WD3(isp->isp_atioq_dma), DMA_WD2(isp->isp_atioq_dma), + DMA_WD1(isp->isp_atioq_dma), DMA_WD0(isp->isp_atioq_dma)); + } +#endif + + isp_prt(isp, ISP_LOGDEBUG0, + "isp_fibre_init_2400: fwopt1 0x%x fwopt2 0x%x fwopt3 0x%x", + icbp->icb_fwoptions1, icbp->icb_fwoptions2, icbp->icb_fwoptions3); + + isp_prt(isp, ISP_LOGDEBUG0, + "isp_fibre_init_2400: rqst %04x%04x%04x%04x rsp %04x%04x%04x%04x", + DMA_WD3(isp->isp_rquest_dma), DMA_WD2(isp->isp_rquest_dma), + DMA_WD1(isp->isp_rquest_dma), DMA_WD0(isp->isp_rquest_dma), + DMA_WD3(isp->isp_result_dma), DMA_WD2(isp->isp_result_dma), + DMA_WD1(isp->isp_result_dma), DMA_WD0(isp->isp_result_dma)); + + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "isp_fibre_init_2400", sizeof (*icbp), + icbp); + } + FC_SCRATCH_ACQUIRE(isp); + isp_put_icb_2400(isp, icbp, fcp->isp_scratch); + + + /* + * Init the firmware + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_FIRMWARE; mbs.param[2] = DMA_WD1(fcp->isp_scdma); mbs.param[3] = DMA_WD0(fcp->isp_scdma); - FC_SCRATCH_ACQUIRE(isp); - isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); - if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - MEMCPY(map, fcp->isp_scratch, sizeof (fcpos_map_t)); - map->fwmap = mbs.param[1] != 0; - FC_SCRATCH_RELEASE(isp); - return (0); - } + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + mbs.logval = MBLOGALL; + mbs.timeout = 30 * 1000000; + isp_prt(isp, ISP_LOGDEBUG0, "INIT F/W from %04x%04x%04x%04x", + DMA_WD3(fcp->isp_scdma), DMA_WD2(fcp->isp_scdma), + DMA_WD1(fcp->isp_scdma), DMA_WD0(fcp->isp_scdma)); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (*icbp)); + isp_mboxcmd(isp, &mbs); FC_SCRATCH_RELEASE(isp); - return (-1); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_reqidx = 0; + isp->isp_reqodx = 0; + isp->isp_residx = 0; + + /* + * Whatever happens, we're now committed to being here. + */ + isp->isp_state = ISP_INITSTATE; } -static void -isp_mark_getpdb_all(struct ispsoftc *isp) +void +isp_mark_portdb(struct ispsoftc *isp, int onprobation) { fcparam *fcp = (fcparam *) isp->isp_param; int i; + for (i = 0; i < MAX_FC_TARG; i++) { - fcp->portdb[i].valid = fcp->portdb[i].fabric_dev = 0; + if (onprobation == 0) { + MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t)); + } else { + switch (fcp->portdb[i].state) { + case FC_PORTDB_STATE_CHANGED: + case FC_PORTDB_STATE_PENDING_VALID: + case FC_PORTDB_STATE_VALID: + case FC_PORTDB_STATE_PROBATIONAL: + fcp->portdb[i].state = + FC_PORTDB_STATE_PROBATIONAL; + break; + case FC_PORTDB_STATE_ZOMBIE: + break; + case FC_PORTDB_STATE_NIL: + default: + MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t)); + fcp->portdb[i].state = + FC_PORTDB_STATE_NIL; + break; + } + } + } +} + +/* + * Perform an IOCB PLOGI or LOGO via EXECUTE IOCB A64 for 24XX cards + * or via FABRIC LOGIN/FABRIC LOGOUT for other cards. + */ +int +isp_plogx(struct ispsoftc *isp, u_int16_t handle, u_int32_t portid, int flags, int gs) +{ + mbreg_t mbs; + u_int8_t q[QENTRY_LEN]; + isp_plogx_t *plp; + u_int8_t *scp; + u_int32_t sst, parm1; + int rval; + + if (!IS_24XX(isp)) { + int action = flags & PLOGX_FLG_CMD_MASK; + if (action == PLOGX_FLG_CMD_PLOGI) { + return (isp_port_login(isp, handle, portid)); + } else if (action == PLOGX_FLG_CMD_LOGO) { + return (isp_port_logout(isp, handle, portid)); + } else { + return (MBOX_INVALID_COMMAND); + } + } + + MEMZERO(q, QENTRY_LEN); + plp = (isp_plogx_t *) q; + plp->plogx_header.rqs_entry_count = 1; + plp->plogx_header.rqs_entry_type = RQSTYPE_LOGIN; + plp->plogx_handle = 0xffffffff; + plp->plogx_nphdl = handle; + plp->plogx_portlo = portid; + plp->plogx_rspsz_porthi = (portid >> 16) & 0xff; + plp->plogx_flags = flags; + + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "IOCB LOGX", QENTRY_LEN, plp); + } + + if (gs == 0) { + FC_SCRATCH_ACQUIRE(isp); + } + scp = FCPARAM(isp)->isp_scratch; + isp_put_plogx(isp, plp, (isp_plogx_t *) scp); + + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64; + mbs.param[1] = QENTRY_LEN; + mbs.param[2] = DMA_WD1(FCPARAM(isp)->isp_scdma); + mbs.param[3] = DMA_WD0(FCPARAM(isp)->isp_scdma); + mbs.param[6] = DMA_WD3(FCPARAM(isp)->isp_scdma); + mbs.param[7] = DMA_WD2(FCPARAM(isp)->isp_scdma); + mbs.timeout = 500000; + mbs.logval = MBLOGALL; + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, QENTRY_LEN); + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + rval = mbs.param[0]; + goto out; + } + MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN); + scp += QENTRY_LEN; + isp_get_plogx(isp, (isp_plogx_t *) scp, plp); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "IOCB LOGX response", QENTRY_LEN, plp); + } + + if (plp->plogx_status == PLOGX_STATUS_OK) { + rval = 0; + goto out; + } else if (plp->plogx_status != PLOGX_STATUS_IOCBERR) { + isp_prt(isp, ISP_LOGWARN, "status 0x%x on port login IOCB", + plp->plogx_status); + rval = -1; + goto out; + } + + sst = plp->plogx_ioparm[0].lo16 | (plp->plogx_ioparm[0].hi16 << 16); + parm1 = plp->plogx_ioparm[1].lo16 | (plp->plogx_ioparm[1].hi16 << 16); + + rval = -1; + + switch (sst) { + case PLOGX_IOCBERR_NOLINK: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no link"); + break; + case PLOGX_IOCBERR_NOIOCB: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no IOCB buffer"); + break; + case PLOGX_IOCBERR_NOXGHG: + isp_prt(isp, ISP_LOGERR, + "PLOGX failed- no Exchange Control Block"); + break; + case PLOGX_IOCBERR_FAILED: + isp_prt(isp, ISP_LOGERR, + "PLOGX(0x%x) of Port 0x%06x failed: reason 0x%x (last LOGIN" + " state 0x%x)", flags, portid, parm1 & 0xff, + (parm1 >> 8) & 0xff); + break; + case PLOGX_IOCBERR_NOFABRIC: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no fabric"); + break; + case PLOGX_IOCBERR_NOTREADY: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- f/w not ready"); + break; + case PLOGX_IOCBERR_NOLOGIN: + isp_prt(isp, ISP_LOGERR, + "PLOGX failed- not logged in (last LOGIN state 0x%x)", + parm1); + rval = MBOX_NOT_LOGGED_IN; + break; + case PLOGX_IOCBERR_REJECT: + isp_prt(isp, ISP_LOGERR, "PLOGX failed: LS_RJT = 0x%x", parm1); + break; + case PLOGX_IOCBERR_NOPCB: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no PCB allocated"); + break; + case PLOGX_IOCBERR_EINVAL: + isp_prt(isp, ISP_LOGERR, + "PLOGX failed: invalid parameter at offset 0x%x", parm1); + break; + case PLOGX_IOCBERR_PORTUSED: + isp_prt(isp, ISP_LOGDEBUG0, + "portid 0x%x already logged in with N-port handle 0x%x", + portid, parm1); + rval = MBOX_PORT_ID_USED | (handle << 16); + break; + case PLOGX_IOCBERR_HNDLUSED: + isp_prt(isp, ISP_LOGDEBUG0, + "N-port handle 0x%x already used for portid 0x%x", + handle, parm1); + rval = MBOX_LOOP_ID_USED; + break; + case PLOGX_IOCBERR_NOHANDLE: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no handle allocated"); + break; + case PLOGX_IOCBERR_NOFLOGI: + isp_prt(isp, ISP_LOGERR, "PLOGX failed- no FLOGI_ACC"); + break; + default: + isp_prt(isp, ISP_LOGERR, "status %x from %x", plp->plogx_status, + flags); + rval = -1; + break; } +out: + if (gs == 0) { + FC_SCRATCH_RELEASE(isp); + } + return (rval); } -static int -isp_getpdb(struct ispsoftc *isp, int id, isp_pdb_t *pdbp) +int +isp_port_login(struct ispsoftc *isp, u_int16_t handle, u_int32_t portid) +{ + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_FABRIC_LOGIN; + if (FCPARAM(isp)->isp_2klogin) { + mbs.param[1] = handle; + mbs.ibits = (1 << 10); + } else { + mbs.param[1] = handle << 8; + } + mbs.param[2] = portid >> 16; + mbs.param[3] = portid; + mbs.logval = MBLOGNONE; + mbs.timeout = 500000; + isp_mboxcmd(isp, &mbs); + + switch (mbs.param[0]) { + case MBOX_PORT_ID_USED: + isp_prt(isp, ISP_LOGDEBUG0, + "isp_plogi_old: portid 0x%06x already logged in as %u", + portid, mbs.param[1]); + return (MBOX_PORT_ID_USED | (mbs.param[1] << 16)); + + case MBOX_LOOP_ID_USED: + isp_prt(isp, ISP_LOGDEBUG0, + "isp_plogi_old: handle %u in use for port id 0x%02xXXXX", + handle, mbs.param[1] & 0xff); + return (MBOX_LOOP_ID_USED); + + case MBOX_COMMAND_COMPLETE: + return (0); + + case MBOX_COMMAND_ERROR: + isp_prt(isp, ISP_LOGINFO, + "isp_plogi_old: error 0x%x in PLOGI to port 0x%06x", + mbs.param[1], portid); + return (MBOX_COMMAND_ERROR); + + case MBOX_ALL_IDS_USED: + isp_prt(isp, ISP_LOGINFO, + "isp_plogi_old: all IDs used for fabric login"); + return (MBOX_ALL_IDS_USED); + + default: + isp_prt(isp, ISP_LOGINFO, + "isp_plogi_old: error 0x%x on port login of 0x%06x@0x%0x", + mbs.param[0], portid, handle); + return (mbs.param[0]); + } +} + +int +isp_port_logout(struct ispsoftc *isp, u_int16_t handle, u_int32_t portid) +{ + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_FABRIC_LOGOUT; + if (FCPARAM(isp)->isp_2klogin) { + mbs.param[1] = handle; + mbs.ibits = (1 << 10); + } else { + mbs.param[1] = handle << 8; + } + mbs.logval = MBLOGNONE; + mbs.timeout = 100000; + isp_mboxcmd(isp, &mbs); + return (mbs.param[0] == MBOX_COMMAND_COMPLETE? 0 : mbs.param[0]); +} + +int +isp_getpdb(struct ispsoftc *isp, u_int16_t id, isp_pdb_t *pdb, int dolock) { fcparam *fcp = (fcparam *) isp->isp_param; mbreg_t mbs; + union { + isp_pdb_21xx_t fred; + isp_pdb_24xx_t bill; + } un; - MEMZERO(&mbs, sizeof (mbs)); + MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_PORT_DB; - mbs.param[1] = id << 8; + if (IS_24XX(isp)) { + mbs.ibits = 0x3ff; + mbs.param[1] = id; + } else if (FCPARAM(isp)->isp_2klogin) { + mbs.param[1] = id; + mbs.ibits = (1 << 10); + } else { + mbs.param[1] = id << 8; + } mbs.param[2] = DMA_WD1(fcp->isp_scdma); mbs.param[3] = DMA_WD0(fcp->isp_scdma); mbs.param[6] = DMA_WD3(fcp->isp_scdma); mbs.param[7] = DMA_WD2(fcp->isp_scdma); - FC_SCRATCH_ACQUIRE(isp); - isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); - if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - isp_get_pdb(isp, (isp_pdb_t *)fcp->isp_scratch, pdbp); + mbs.timeout = 250000; + mbs.logval = MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR; + if (dolock) { + FC_SCRATCH_ACQUIRE(isp); + } + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (un)); + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + if (dolock) { + FC_SCRATCH_RELEASE(isp); + } + return (-1); + } + if (IS_24XX(isp)) { + isp_get_pdb_24xx(isp, fcp->isp_scratch, &un.bill); + pdb->handle = un.bill.pdb_handle; + pdb->s3_role = un.bill.pdb_prli_svc3; + pdb->portid = BITS2WORD_24XX(un.bill.pdb_portid_bits); + MEMCPY(pdb->portname, un.bill.pdb_portname, 8); + MEMCPY(pdb->nodename, un.bill.pdb_nodename, 8); + } else { + isp_get_pdb_21xx(isp, fcp->isp_scratch, &un.fred); + pdb->handle = un.fred.pdb_loopid; + pdb->s3_role = un.fred.pdb_prli_svc3; + pdb->portid = BITS2WORD(un.fred.pdb_portid_bits); + MEMCPY(pdb->portname, un.fred.pdb_portname, 8); + MEMCPY(pdb->nodename, un.fred.pdb_nodename, 8); + } + if (dolock) { FC_SCRATCH_RELEASE(isp); - return (0); } - FC_SCRATCH_RELEASE(isp); - return (-1); + return (0); } -static u_int64_t +u_int64_t isp_get_portname(struct ispsoftc *isp, int loopid, int nodename) { - u_int64_t wwn = 0; + u_int64_t wwn = (u_int64_t) -1; mbreg_t mbs; - MEMZERO(&mbs, sizeof (mbs)); + MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_PORT_NAME; - mbs.param[1] = loopid << 8; - if (nodename) - mbs.param[1] |= 1; - isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); - if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + if (FCPARAM(isp)->isp_2klogin || IS_24XX(isp)) { + mbs.param[1] = loopid; + mbs.ibits = (1 << 10); + if (nodename) { + mbs.param[10] = 1; + } + } else { + mbs.param[1] = loopid << 8; + if (nodename) { + mbs.param[1] |= 1; + } + } + mbs.logval = MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return (wwn); + } + if (IS_24XX(isp)) { wwn = - (((u_int64_t)(mbs.param[2] & 0xff)) << 56) | + (((u_int64_t)(mbs.param[2] >> 8)) << 56) | + (((u_int64_t)(mbs.param[2] & 0xff)) << 48) | + (((u_int64_t)(mbs.param[3] >> 8)) << 40) | + (((u_int64_t)(mbs.param[3] & 0xff)) << 32) | + (((u_int64_t)(mbs.param[6] >> 8)) << 24) | + (((u_int64_t)(mbs.param[6] & 0xff)) << 16) | + (((u_int64_t)(mbs.param[7] >> 8)) << 8) | + (((u_int64_t)(mbs.param[7] & 0xff))); + } else { + wwn = + (((u_int64_t)(mbs.param[2] & 0xff)) << 56) | (((u_int64_t)(mbs.param[2] >> 8)) << 48) | (((u_int64_t)(mbs.param[3] & 0xff)) << 40) | (((u_int64_t)(mbs.param[3] >> 8)) << 32) | @@ -1538,13 +2449,13 @@ isp_get_portname(struct ispsoftc *isp, int loopid, int nodename) } /* - * Make sure we have good FC link and know our Loop ID. + * Make sure we have good FC link. */ -static int +int isp_fclink_test(struct ispsoftc *isp, int usdelay) { - static char *toponames[] = { + static const char *toponames[] = { "Private Loop", "FL Port", "N-Port to N-Port", @@ -1554,15 +2465,15 @@ isp_fclink_test(struct ispsoftc *isp, int usdelay) mbreg_t mbs; int count, check_for_fabric; u_int8_t lwfs; + int loopid; fcparam *fcp; - struct lportdb *lp; + fcportdb_t *lp; isp_pdb_t pdb; fcp = isp->isp_param; - /* - * XXX: Here is where we would start a 'loop dead' timeout - */ + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "FC Link Test Entry"); + ISP_MARK_PORTDB(isp, 1); /* * Wait up to N microseconds for F/W to go to a ready state. @@ -1577,12 +2488,14 @@ isp_fclink_test(struct ispsoftc *isp, int usdelay) GET_NANOTIME(&hra); isp_fw_state(isp); if (lwfs != fcp->isp_fwstate) { - isp_prt(isp, ISP_LOGINFO, "Firmware State <%s->%s>", - isp2100_fw_statename((int)lwfs), - isp2100_fw_statename((int)fcp->isp_fwstate)); + isp_prt(isp, ISP_LOGCONFIG|ISP_LOGSANCFG, + "Firmware State <%s->%s>", + ispfc_fw_statename((int)lwfs), + ispfc_fw_statename((int)fcp->isp_fwstate)); lwfs = fcp->isp_fwstate; } - if (fcp->isp_fwstate == FW_READY) { + if (fcp->isp_fwstate == FW_READY && + fcp->isp_loopstate >= LOOP_PDB_RCVD) { break; } GET_NANOTIME(&hrb); @@ -1631,36 +2544,38 @@ isp_fclink_test(struct ispsoftc *isp, int usdelay) * If we haven't gone to 'ready' state, return. */ if (fcp->isp_fwstate != FW_READY) { + isp_prt(isp, ISP_LOGSANCFG, + "isp_fclink_test: not at FW_READY state"); return (-1); } /* - * Get our Loop ID (if possible). We really need to have it. + * Get our Loop ID and Port ID. */ MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_LOOP_ID; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { return (-1); } - fcp->isp_loopid = mbs.param[1]; - if (IS_2200(isp) || IS_23XX(isp)) { + + if (FCPARAM(isp)->isp_2klogin) { + fcp->isp_loopid = mbs.param[1]; + } else { + fcp->isp_loopid = mbs.param[1] & 0xff; + } + + if (IS_2100(isp)) { + fcp->isp_topo = TOPO_NL_PORT; + } else { int topo = (int) mbs.param[6]; - if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB) + if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB) { topo = TOPO_PTP_STUB; + } fcp->isp_topo = topo; - } else { - fcp->isp_topo = TOPO_NL_PORT; } - fcp->isp_portid = fcp->isp_alpa = mbs.param[2] & 0xff; - - /* - * Check to see if we're on a fabric by trying to see if we - * can talk to the fabric name server. This can be a bit - * tricky because if we're a 2100, we should check always - * (in case we're connected to a server doing aliasing). - */ - fcp->isp_onfabric = 0; + fcp->isp_portid = mbs.param[2] | (mbs.param[3] << 16); if (IS_2100(isp)) { /* @@ -1675,122 +2590,92 @@ isp_fclink_test(struct ispsoftc *isp, int usdelay) } else if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_F_PORT) { check_for_fabric = 1; - } else + } else { check_for_fabric = 0; + } - if (check_for_fabric && isp_getpdb(isp, FL_PORT_ID, &pdb) == 0) { - int loopid = FL_PORT_ID; + if (IS_24XX(isp)) { + loopid = NPH_FL_ID; + } else { + loopid = FL_ID; + } + + if (check_for_fabric && isp_getpdb(isp, loopid, &pdb, 1) == 0) { + int r; if (IS_2100(isp)) { fcp->isp_topo = TOPO_FL_PORT; } - - if (BITS2WORD(pdb.pdb_portid_bits) == 0) { + if (pdb.portid == 0) { /* * Crock. */ fcp->isp_topo = TOPO_NL_PORT; goto not_on_fabric; } - fcp->isp_portid = mbs.param[2] | ((int) mbs.param[3] << 16); /* * Save the Fabric controller's port database entry. */ - lp = &fcp->portdb[loopid]; - lp->node_wwn = - (((u_int64_t)pdb.pdb_nodename[0]) << 56) | - (((u_int64_t)pdb.pdb_nodename[1]) << 48) | - (((u_int64_t)pdb.pdb_nodename[2]) << 40) | - (((u_int64_t)pdb.pdb_nodename[3]) << 32) | - (((u_int64_t)pdb.pdb_nodename[4]) << 24) | - (((u_int64_t)pdb.pdb_nodename[5]) << 16) | - (((u_int64_t)pdb.pdb_nodename[6]) << 8) | - (((u_int64_t)pdb.pdb_nodename[7])); - lp->port_wwn = - (((u_int64_t)pdb.pdb_portname[0]) << 56) | - (((u_int64_t)pdb.pdb_portname[1]) << 48) | - (((u_int64_t)pdb.pdb_portname[2]) << 40) | - (((u_int64_t)pdb.pdb_portname[3]) << 32) | - (((u_int64_t)pdb.pdb_portname[4]) << 24) | - (((u_int64_t)pdb.pdb_portname[5]) << 16) | - (((u_int64_t)pdb.pdb_portname[6]) << 8) | - (((u_int64_t)pdb.pdb_portname[7])); - lp->roles = - (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; - lp->portid = BITS2WORD(pdb.pdb_portid_bits); - lp->loopid = pdb.pdb_loopid; - lp->loggedin = lp->valid = 1; - fcp->isp_onfabric = 1; - (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); - isp_register_fc4_type(isp); + lp = &fcp->portdb[FL_ID]; + lp->state = FC_PORTDB_STATE_PENDING_VALID; + MAKE_WWN_FROM_NODE_NAME(lp->node_wwn, pdb.nodename); + MAKE_WWN_FROM_NODE_NAME(lp->port_wwn, pdb.portname); + lp->roles = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + lp->portid = pdb.portid; + lp->handle = pdb.handle; + lp->new_portid = lp->portid; + lp->new_roles = lp->roles; + if (IS_24XX(isp)) { + r = isp_register_fc4_type_24xx(isp); + } else { + r = isp_register_fc4_type(isp); + } + if (r) { + isp_prt(isp, ISP_LOGSANCFG, + "isp_fclink_test: register fc4 type failed"); + return (-1); + } } else { not_on_fabric: - fcp->isp_onfabric = 0; - fcp->portdb[FL_PORT_ID].valid = 0; + fcp->portdb[FL_ID].state = FC_PORTDB_STATE_NIL; } fcp->isp_gbspeed = 1; - if (IS_23XX(isp)) { - MEMZERO(&mbs, sizeof (mbs)); + if (IS_23XX(isp) || IS_24XX(isp)) { + MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_SET_DATA_RATE; mbs.param[1] = MBGSD_GET_RATE; /* mbs.param[2] undefined if we're just getting rate */ - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + mbs.timeout = 3000000; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - if (mbs.param[1] == MBGSD_TWOGB) { + if (mbs.param[1] == MBGSD_FOURGB) { + isp_prt(isp, ISP_LOGINFO, "4Gb link speed/s"); + fcp->isp_gbspeed = 4; + } if (mbs.param[1] == MBGSD_TWOGB) { isp_prt(isp, ISP_LOGINFO, "2Gb link speed/s"); fcp->isp_gbspeed = 2; } } } - isp_prt(isp, ISP_LOGCONFIG, topology, fcp->isp_loopid, fcp->isp_alpa, - fcp->isp_portid, fcp->isp_loopstate, toponames[fcp->isp_topo]); - /* - * Announce ourselves, too. This involves synthesizing an entry. + * Announce ourselves, too. */ - if (fcp->isp_iid_set == 0) { - fcp->isp_iid_set = 1; - fcp->isp_iid = fcp->isp_loopid; - lp = &fcp->portdb[fcp->isp_iid]; - } else { - lp = &fcp->portdb[fcp->isp_iid]; - if (fcp->isp_portid != lp->portid || - fcp->isp_loopid != lp->loopid || - fcp->isp_nodewwn != ISP_NODEWWN(isp) || - fcp->isp_portwwn != ISP_PORTWWN(isp)) { - lp->valid = 0; - count = fcp->isp_iid; - (void) isp_async(isp, ISPASYNC_PROMENADE, &count); - } - } - lp->loopid = fcp->isp_loopid; - lp->portid = fcp->isp_portid; - lp->node_wwn = ISP_NODEWWN(isp); - lp->port_wwn = ISP_PORTWWN(isp); - switch (isp->isp_role) { - case ISP_ROLE_NONE: - lp->roles = 0; - break; - case ISP_ROLE_TARGET: - lp->roles = SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT; - break; - case ISP_ROLE_INITIATOR: - lp->roles = SVC3_INI_ROLE >> SVC3_ROLE_SHIFT; - break; - case ISP_ROLE_BOTH: - lp->roles = (SVC3_INI_ROLE|SVC3_TGT_ROLE) >> SVC3_ROLE_SHIFT; - break; - } - lp->loggedin = lp->valid = 1; - count = fcp->isp_iid; - (void) isp_async(isp, ISPASYNC_PROMENADE, &count); + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGCONFIG, topology, fcp->isp_portid, + fcp->isp_loopid, toponames[fcp->isp_topo]); + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGCONFIG, ourwwn, + (u_int32_t) (ISP_NODEWWN(isp) >> 32), + (u_int32_t) ISP_NODEWWN(isp), + (u_int32_t) (ISP_PORTWWN(isp) >> 32), + (u_int32_t) ISP_PORTWWN(isp)); + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "FC Link Test Complete"); return (0); } -static char * -isp2100_fw_statename(int state) +const char * +ispfc_fw_statename(int state) { switch(state) { case FW_CONFIG_WAIT: return "Config Wait"; @@ -1806,17 +2691,35 @@ isp2100_fw_statename(int state) } /* - * Synchronize our soft copy of the port database with what the f/w thinks - * (with a view toward possibly for a specific target....) + * Complete the synchronization of our Port Database. + * + * At this point, we've scanned the local loop (if any) and the fabric + * and performed fabric logins on all new devices. + * + * Our task here is to go through our port database and remove any entities + * that are still marked probational (issuing PLOGO for ones which we had + * PLOGI'd into) or are dead. + * + * Our task here is to also check policy to decide whether devices which + * have *changed* in some way should still be kept active. For example, + * if a device has just changed PortID, we can either elect to treat it + * as an old device or as a newly arrived device (and notify the outer + * layer appropriately). + * + * We also do initiator map target id assignment here for new initiator + * devices and refresh old ones ot make sure that they point to the corret + * entities. */ - -static int +int isp_pdb_sync(struct ispsoftc *isp) { - struct lportdb *lp; fcparam *fcp = isp->isp_param; - isp_pdb_t pdb; - int loopid, base, lim; + fcportdb_t *lp; + u_int16_t dbidx; + + if (fcp->isp_loopstate == LOOP_READY) { + return (0); + } /* * Make sure we're okay for doing this right now. @@ -1824,289 +2727,127 @@ isp_pdb_sync(struct ispsoftc *isp) if (fcp->isp_loopstate != LOOP_PDB_RCVD && fcp->isp_loopstate != LOOP_FSCAN_DONE && fcp->isp_loopstate != LOOP_LSCAN_DONE) { + isp_prt(isp, ISP_LOGWARN, "isp_pdb_sync: bad loopstate %d", + fcp->isp_loopstate); return (-1); } - if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT || + if (fcp->isp_topo == TOPO_FL_PORT || + fcp->isp_topo == TOPO_NL_PORT || fcp->isp_topo == TOPO_N_PORT) { if (fcp->isp_loopstate < LOOP_LSCAN_DONE) { if (isp_scan_loop(isp) != 0) { + isp_prt(isp, ISP_LOGWARN, + "isp_pdb_sync: isp_scan_loop failed"); return (-1); } } } - fcp->isp_loopstate = LOOP_SYNCING_PDB; - - /* - * If we get this far, we've settled our differences with the f/w - * (for local loop device) and we can say that the loop state is ready. - */ - - if (fcp->isp_topo == TOPO_NL_PORT) { - fcp->loop_seen_once = 1; - fcp->isp_loopstate = LOOP_READY; - return (0); - } - /* - * Find all Fabric Entities that didn't make it from one scan to the - * next and let the world know they went away. Scan the whole database. - */ - for (lp = &fcp->portdb[0]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) { - if (lp->was_fabric_dev && lp->fabric_dev == 0) { - loopid = lp - fcp->portdb; - lp->valid = 0; /* should already be set */ - (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); - MEMZERO((void *) lp, sizeof (*lp)); - continue; + if (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT) { + if (fcp->isp_loopstate < LOOP_FSCAN_DONE) { + if (isp_scan_fabric(isp) != 0) { + isp_prt(isp, ISP_LOGWARN, + "isp_pdb_sync: isp_scan_fabric failed"); + return (-1); + } } - lp->was_fabric_dev = lp->fabric_dev; } - if (fcp->isp_topo == TOPO_FL_PORT) - base = FC_SNS_ID+1; - else - base = 0; - - if (fcp->isp_topo == TOPO_N_PORT) - lim = 1; - else - lim = MAX_FC_TARG; - - /* - * Now log in any fabric devices that the outer layer has - * left for us to see. This seems the most sane policy - * for the moment. - */ - for (lp = &fcp->portdb[base]; lp < &fcp->portdb[lim]; lp++) { - u_int32_t portid; - mbreg_t mbs; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Synchronizing PDBs"); - loopid = lp - fcp->portdb; - if (loopid >= FL_PORT_ID && loopid <= FC_SNS_ID) { - continue; - } + fcp->isp_loopstate = LOOP_SYNCING_PDB; - /* - * Anything here? - */ - if (lp->port_wwn == 0) { - continue; - } + for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { + lp = &fcp->portdb[dbidx]; - /* - * Don't try to log into yourself. - */ - if ((portid = lp->portid) == fcp->isp_portid) { + if (lp->state == FC_PORTDB_STATE_NIL) { continue; } - - /* - * If we'd been logged in- see if we still are and we haven't - * changed. If so, no need to log ourselves out, etc.. - * - * Unfortunately, our charming QLogic f/w has decided to - * return a valid port database entry for a fabric device - * that has, in fact, gone away. And it hangs trying to - * log it out. - */ - if (lp->loggedin && lp->force_logout == 0 && - isp_getpdb(isp, lp->loopid, &pdb) == 0) { - int nrole; - u_int64_t nwwnn, nwwpn; - nwwnn = - (((u_int64_t)pdb.pdb_nodename[0]) << 56) | - (((u_int64_t)pdb.pdb_nodename[1]) << 48) | - (((u_int64_t)pdb.pdb_nodename[2]) << 40) | - (((u_int64_t)pdb.pdb_nodename[3]) << 32) | - (((u_int64_t)pdb.pdb_nodename[4]) << 24) | - (((u_int64_t)pdb.pdb_nodename[5]) << 16) | - (((u_int64_t)pdb.pdb_nodename[6]) << 8) | - (((u_int64_t)pdb.pdb_nodename[7])); - nwwpn = - (((u_int64_t)pdb.pdb_portname[0]) << 56) | - (((u_int64_t)pdb.pdb_portname[1]) << 48) | - (((u_int64_t)pdb.pdb_portname[2]) << 40) | - (((u_int64_t)pdb.pdb_portname[3]) << 32) | - (((u_int64_t)pdb.pdb_portname[4]) << 24) | - (((u_int64_t)pdb.pdb_portname[5]) << 16) | - (((u_int64_t)pdb.pdb_portname[6]) << 8) | - (((u_int64_t)pdb.pdb_portname[7])); - nrole = (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> - SVC3_ROLE_SHIFT; - if (pdb.pdb_loopid == lp->loopid && lp->portid == - (u_int32_t) BITS2WORD(pdb.pdb_portid_bits) && - nwwnn == lp->node_wwn && nwwpn == lp->port_wwn && - lp->roles == nrole && lp->force_logout == 0) { - lp->loggedin = lp->valid = 1; - isp_prt(isp, ISP_LOGCONFIG, lretained, - (int) (lp - fcp->portdb), - (int) lp->loopid, lp->portid); - continue; + if (lp->state == FC_PORTDB_STATE_VALID) { + if (dbidx != FL_ID) { + isp_prt(isp, + ISP_LOGERR, "portdb idx %d already valid", + dbidx); } + continue; } - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_SYNCING_PDB) { - return (-1); - } - - /* - * Force a logout if we were logged in. - */ - if (lp->loggedin) { - if (lp->force_logout || - isp_getpdb(isp, lp->loopid, &pdb) == 0) { - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_FABRIC_LOGOUT; - mbs.param[1] = lp->loopid << 8; - isp_mboxcmd(isp, &mbs, MBLOGNONE); - isp_prt(isp, ISP_LOGINFO, plogout, - (int) (lp - fcp->portdb), lp->loopid, - lp->portid); + switch (lp->state) { + case FC_PORTDB_STATE_PROBATIONAL: + case FC_PORTDB_STATE_DEAD: + /* + * It's up to the outer layers to clear isp_ini_map. + */ + lp->state = FC_PORTDB_STATE_NIL; + isp_async(isp, ISPASYNC_DEV_GONE, lp); + if (lp->autologin == 0) { + (void) isp_plogx(isp, lp->handle, lp->portid, + PLOGX_FLG_CMD_LOGO | + PLOGX_FLG_IMPLICIT | + PLOGX_FLG_FREE_NPHDL, 0); + } else { + lp->autologin = 0; } - lp->force_logout = lp->loggedin = 0; - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_SYNCING_PDB) { - return (-1); + lp->new_roles = 0; + lp->new_portid = 0; + /* + * Note that we might come out of this with our state + * set to FC_PORTDB_STATE_ZOMBIE. + */ + break; + case FC_PORTDB_STATE_NEW: + /* + * It's up to the outer layers to assign a virtual + * target id in isp_ini_map (if any). + */ + lp->portid = lp->new_portid; + lp->roles = lp->new_roles; + lp->state = FC_PORTDB_STATE_VALID; + isp_async(isp, ISPASYNC_DEV_ARRIVED, lp); + lp->new_roles = 0; + lp->new_portid = 0; + lp->reserved = 0; + lp->new_reserved = 0; + break; + case FC_PORTDB_STATE_CHANGED: +/* + * XXXX FIX THIS + */ + lp->state = FC_PORTDB_STATE_VALID; + isp_async(isp, ISPASYNC_DEV_CHANGED, lp); + lp->new_roles = 0; + lp->new_portid = 0; + lp->reserved = 0; + lp->new_reserved = 0; + break; + case FC_PORTDB_STATE_PENDING_VALID: + lp->portid = lp->new_portid; + lp->roles = lp->new_roles; + if (lp->ini_map_idx) { + int t = lp->ini_map_idx - 1; + fcp->isp_ini_map[t] = dbidx + 1; } - } - - /* - * And log in.... - */ - loopid = lp - fcp->portdb; - lp->loopid = FL_PORT_ID; - do { - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_FABRIC_LOGIN; - mbs.param[1] = loopid << 8; - mbs.param[2] = portid >> 16; - mbs.param[3] = portid & 0xffff; - isp_mboxcmd(isp, &mbs, MBLOGALL & ~(MBOX_LOOP_ID_USED | - MBOX_PORT_ID_USED | MBOX_COMMAND_ERROR)); - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_SYNCING_PDB) { - return (-1); + lp->state = FC_PORTDB_STATE_VALID; + isp_async(isp, ISPASYNC_DEV_STAYED, lp); + if (dbidx != FL_ID) { + lp->new_roles = 0; + lp->new_portid = 0; } - switch (mbs.param[0]) { - case MBOX_LOOP_ID_USED: - /* - * Try the next available loop id. - */ - loopid++; - break; - case MBOX_PORT_ID_USED: - /* - * This port is already logged in. - * Snaffle the loop id it's using if it's - * nonzero, otherwise we're hosed. - */ - if (mbs.param[1] != 0) { - loopid = mbs.param[1]; - isp_prt(isp, ISP_LOGINFO, retained, - loopid, (int) (lp - fcp->portdb), - lp->portid); - } else { - loopid = MAX_FC_TARG; - break; - } - /* FALLTHROUGH */ - case MBOX_COMMAND_COMPLETE: - lp->loggedin = 1; - lp->loopid = loopid; - break; - case MBOX_COMMAND_ERROR: - isp_prt(isp, ISP_LOGINFO, plogierr, - portid, mbs.param[1]); - /* FALLTHROUGH */ - case MBOX_ALL_IDS_USED: /* We're outta IDs */ - default: - loopid = MAX_FC_TARG; - break; - } - } while (lp->loopid == FL_PORT_ID && loopid < MAX_FC_TARG); - - /* - * If we get here and we haven't set a Loop ID, - * we failed to log into this device. - */ - - if (lp->loopid == FL_PORT_ID) { - lp->loopid = 0; - continue; - } - - /* - * Make sure we can get the appropriate port information. - */ - if (isp_getpdb(isp, lp->loopid, &pdb) != 0) { - isp_prt(isp, ISP_LOGWARN, nopdb, lp->portid); - goto dump_em; - } - - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_SYNCING_PDB) { - return (-1); - } - - if (pdb.pdb_loopid != lp->loopid) { - isp_prt(isp, ISP_LOGWARN, pdbmfail1, - lp->portid, pdb.pdb_loopid); - goto dump_em; - } - - if (lp->portid != (u_int32_t) BITS2WORD(pdb.pdb_portid_bits)) { - isp_prt(isp, ISP_LOGWARN, pdbmfail2, - lp->portid, BITS2WORD(pdb.pdb_portid_bits)); - goto dump_em; - } - - lp->roles = - (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; - lp->node_wwn = - (((u_int64_t)pdb.pdb_nodename[0]) << 56) | - (((u_int64_t)pdb.pdb_nodename[1]) << 48) | - (((u_int64_t)pdb.pdb_nodename[2]) << 40) | - (((u_int64_t)pdb.pdb_nodename[3]) << 32) | - (((u_int64_t)pdb.pdb_nodename[4]) << 24) | - (((u_int64_t)pdb.pdb_nodename[5]) << 16) | - (((u_int64_t)pdb.pdb_nodename[6]) << 8) | - (((u_int64_t)pdb.pdb_nodename[7])); - lp->port_wwn = - (((u_int64_t)pdb.pdb_portname[0]) << 56) | - (((u_int64_t)pdb.pdb_portname[1]) << 48) | - (((u_int64_t)pdb.pdb_portname[2]) << 40) | - (((u_int64_t)pdb.pdb_portname[3]) << 32) | - (((u_int64_t)pdb.pdb_portname[4]) << 24) | - (((u_int64_t)pdb.pdb_portname[5]) << 16) | - (((u_int64_t)pdb.pdb_portname[6]) << 8) | - (((u_int64_t)pdb.pdb_portname[7])); - /* - * Check to make sure this all makes sense. - */ - if (lp->node_wwn && lp->port_wwn) { - lp->valid = 1; - loopid = lp - fcp->portdb; - (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); - continue; - } -dump_em: - lp->valid = 0; - isp_prt(isp, ISP_LOGINFO, - ldumped, loopid, lp->loopid, lp->portid); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_FABRIC_LOGOUT; - mbs.param[1] = lp->loopid << 8; - mbs.param[2] = 0; - mbs.param[3] = 0; - isp_mboxcmd(isp, &mbs, MBLOGNONE); - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_SYNCING_PDB) { - return (-1); + lp->reserved = 0; + lp->new_reserved = 0; + break; + case FC_PORTDB_STATE_ZOMBIE: + break; + default: + isp_prt(isp, ISP_LOGWARN, + "isp_scan_loop: state %d for idx %d", + lp->state, dbidx); + isp_dump_portdb(isp); } } + /* * If we get here, we've for sure seen not only a valid loop * but know what is or isn't on it, so mark this for usage @@ -2117,799 +2858,969 @@ dump_em: return (0); } -static int +/* + * Scan local loop for devices. + */ +int isp_scan_loop(struct ispsoftc *isp) { - struct lportdb *lp; + fcportdb_t *lp, tmp; fcparam *fcp = isp->isp_param; + int i; isp_pdb_t pdb; - int loopid, lim, hival; + u_int16_t handle, lim = 0; + if (fcp->isp_fwstate < FW_READY || + fcp->isp_loopstate < LOOP_PDB_RCVD) { + return (-1); + } + + if (fcp->isp_loopstate > LOOP_SCANNING_LOOP) { + return (0); + } + + /* + * Check our connection topology. + * + * If we're a public or private loop, we scan 0..125 as handle values. + * The firmware has (typically) peformed a PLOGI for us. + * + * If we're a N-port connection, we treat this is a short loop (0..1). + * + * If we're in target mode, we can all possible handles to see who + * might have logged into us. + */ switch (fcp->isp_topo) { case TOPO_NL_PORT: - hival = FL_PORT_ID; + case TOPO_FL_PORT: + lim = LOCAL_LOOP_LIM; break; case TOPO_N_PORT: - hival = 2; - break; - case TOPO_FL_PORT: - hival = FC_PORT_ID; + lim = 2; break; default: + isp_prt(isp, ISP_LOGDEBUG0, "no loop topology to scan"); fcp->isp_loopstate = LOOP_LSCAN_DONE; return (0); } + fcp->isp_loopstate = LOOP_SCANNING_LOOP; - /* - * make sure the temp port database is clean... - */ - MEMZERO((void *)fcp->tport, sizeof (fcp->tport)); + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "FC scan loop 0..%d", lim-1); + /* - * Run through the local loop ports and get port database info - * for each loop ID. - * - * There's a somewhat unexplained situation where the f/w passes back - * the wrong database entity- if that happens, just restart (up to - * FL_PORT_ID times). + * Run through the list and get the port database info for each one. */ - for (lim = loopid = 0; loopid < hival; loopid++) { - lp = &fcp->tport[loopid]; - + for (handle = 0; handle < lim; handle++) { /* - * Don't even try for ourselves... + * But don't even try for ourselves... */ - if (loopid == fcp->isp_loopid) + if (handle == fcp->isp_loopid) { continue; + } - lp->node_wwn = isp_get_portname(isp, loopid, 1); - if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) - return (-1); - if (lp->node_wwn == 0) - continue; - lp->port_wwn = isp_get_portname(isp, loopid, 0); - if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) - return (-1); - if (lp->port_wwn == 0) { - lp->node_wwn = 0; - continue; + /* + * In older cards with older f/w GET_PORT_DATABASE has been + * known to hang. This trick gets around that problem. + */ + if (IS_2100(isp) || IS_2200(isp)) { + u_int64_t node_wwn = isp_get_portname(isp, handle, 1); + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + return (-1); + } + if (node_wwn == 0) { + continue; + } } /* - * Get an entry.... + * Get the port database entity for this index. */ - if (isp_getpdb(isp, loopid, &pdb) != 0) { - if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) + if (isp_getpdb(isp, handle, &pdb, 1) != 0) { + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + ISP_MARK_PORTDB(isp, 1); return (-1); + } continue; } + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + ISP_MARK_PORTDB(isp, 1); return (-1); } /* - * If the returned database element doesn't match what we - * asked for, restart the process entirely (up to a point...). + * On *very* old 2100 firmware we would end up sometimes + * with the firmware returning the port database entry + * for something else. We used to restart this, but + * now we just punt. */ - if (pdb.pdb_loopid != loopid) { - loopid = 0; - if (lim++ < hival) { - continue; - } + if (IS_2100(isp) && pdb.handle != handle) { isp_prt(isp, ISP_LOGWARN, "giving up on synchronizing the port database"); + ISP_MARK_PORTDB(isp, 1); return (-1); } /* * Save the pertinent info locally. */ - lp->node_wwn = - (((u_int64_t)pdb.pdb_nodename[0]) << 56) | - (((u_int64_t)pdb.pdb_nodename[1]) << 48) | - (((u_int64_t)pdb.pdb_nodename[2]) << 40) | - (((u_int64_t)pdb.pdb_nodename[3]) << 32) | - (((u_int64_t)pdb.pdb_nodename[4]) << 24) | - (((u_int64_t)pdb.pdb_nodename[5]) << 16) | - (((u_int64_t)pdb.pdb_nodename[6]) << 8) | - (((u_int64_t)pdb.pdb_nodename[7])); - lp->port_wwn = - (((u_int64_t)pdb.pdb_portname[0]) << 56) | - (((u_int64_t)pdb.pdb_portname[1]) << 48) | - (((u_int64_t)pdb.pdb_portname[2]) << 40) | - (((u_int64_t)pdb.pdb_portname[3]) << 32) | - (((u_int64_t)pdb.pdb_portname[4]) << 24) | - (((u_int64_t)pdb.pdb_portname[5]) << 16) | - (((u_int64_t)pdb.pdb_portname[6]) << 8) | - (((u_int64_t)pdb.pdb_portname[7])); - lp->roles = - (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; - lp->portid = BITS2WORD(pdb.pdb_portid_bits); - lp->loopid = pdb.pdb_loopid; - } - - /* - * Mark all of the permanent local loop database entries as invalid - * (except our own entry). - */ - for (loopid = 0; loopid < hival; loopid++) { - if (loopid == fcp->isp_iid) { - fcp->portdb[loopid].valid = 1; - fcp->portdb[loopid].loopid = fcp->isp_loopid; - continue; - } - fcp->portdb[loopid].valid = 0; - } - - /* - * Now merge our local copy of the port database into our saved copy. - * Notify the outer layers of new devices arriving. - */ - for (loopid = 0; loopid < hival; loopid++) { - int i; - - /* - * If we don't have a non-zero Port WWN, we're not here. - */ - if (fcp->tport[loopid].port_wwn == 0) { - continue; - } + MAKE_WWN_FROM_NODE_NAME(tmp.node_wwn, pdb.nodename); + MAKE_WWN_FROM_NODE_NAME(tmp.port_wwn, pdb.portname); + tmp.roles = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + tmp.portid = pdb.portid; + tmp.handle = pdb.handle; /* - * Skip ourselves. + * Check to make sure it's still a valid entry. The 24XX seems + * to return a portid but not a WWPN/WWNN or role for devices + * which shift on a loop. */ - if (loopid == fcp->isp_iid) { + if (tmp.node_wwn == 0 || tmp.port_wwn == 0 || tmp.portid == 0) { + int a, b, c; + a = (tmp.node_wwn == 0); + b = (tmp.port_wwn == 0); + c = (tmp.portid == 0); + isp_prt(isp, ISP_LOGWARN, + "bad pdb (%1d%1d%1d) @ handle 0x%x", a, b, c, + handle); + isp_dump_portdb(isp); continue; } /* - * For the purposes of deciding whether this is the - * 'same' device or not, we only search for an identical - * Port WWN. Node WWNs may or may not be the same as - * the Port WWN, and there may be multiple different - * Port WWNs with the same Node WWN. It would be chaos - * to have multiple identical Port WWNs, so we don't - * allow that. + * Now search the entire port database + * for the same Port and Node WWN. */ - - for (i = 0; i < hival; i++) { - int j; - if (fcp->portdb[i].port_wwn == 0) + for (i = 0; i < MAX_FC_TARG; i++) { + lp = &fcp->portdb[i]; + if (lp->state == FC_PORTDB_STATE_NIL) { continue; - if (fcp->portdb[i].port_wwn != - fcp->tport[loopid].port_wwn) + } + if (lp->node_wwn != tmp.node_wwn) { + continue; + } + if (lp->port_wwn != tmp.port_wwn) { continue; + } + /* - * We found this WWN elsewhere- it's changed - * loopids then. We don't change its actual - * position in our cached port database- we - * just change the actual loop ID we'd use. + * Okay- we've found a non-nil entry that matches. + * Check to make sure it's probational or a zombie. */ - if (fcp->portdb[i].loopid != loopid) { - isp_prt(isp, ISP_LOGINFO, portshift, i, - fcp->portdb[i].loopid, - fcp->portdb[i].portid, loopid, - fcp->tport[loopid].portid); + if (lp->state != FC_PORTDB_STATE_PROBATIONAL && + lp->state != FC_PORTDB_STATE_ZOMBIE) { + isp_prt(isp, ISP_LOGERR, + "[%d] not probational/zombie (0x%x)", + i, lp->state); + isp_dump_portdb(isp); + ISP_MARK_PORTDB(isp, 1); + return (-1); } - fcp->portdb[i].portid = fcp->tport[loopid].portid; - fcp->portdb[i].loopid = loopid; - fcp->portdb[i].valid = 1; - fcp->portdb[i].roles = fcp->tport[loopid].roles; /* - * Now make sure this Port WWN doesn't exist elsewhere - * in the port database. + * Mark the device as something the f/w logs into + * automatically. */ - for (j = i+1; j < hival; j++) { - if (fcp->portdb[i].port_wwn != - fcp->portdb[j].port_wwn) { - continue; - } - isp_prt(isp, ISP_LOGWARN, portdup, j, i); - /* - * Invalidate the 'old' *and* 'new' ones. - * This is really harsh and not quite right, - * but if this happens, we really don't know - * who is what at this point. - */ - fcp->portdb[i].valid = 0; - fcp->portdb[j].valid = 0; + lp->autologin = 1; + + /* + * Check to make see if really still the same + * device. If it is, we mark it pending valid. + */ + if (lp->portid == tmp.portid && + lp->handle == tmp.handle && + lp->roles == tmp.roles) { + lp->new_portid = tmp.portid; + lp->new_roles = tmp.roles; + lp->state = FC_PORTDB_STATE_PENDING_VALID; + isp_prt(isp, ISP_LOGSANCFG, + "Loop Port 0x%02x@0x%x Pending Valid", + tmp.portid, tmp.handle); + break; } + + /* + * We can wipe out the old handle value + * here because it's no longer valid. + */ + lp->handle = tmp.handle; + + /* + * Claim that this has changed and let somebody else + * decide what to do. + */ + isp_prt(isp, ISP_LOGSANCFG, + "Loop Port 0x%02x@0x%x changed", + tmp.portid, tmp.handle); + lp->state = FC_PORTDB_STATE_CHANGED; + lp->new_portid = tmp.portid; + lp->new_roles = tmp.roles; break; } /* - * If we didn't traverse the entire port database, - * then we found (and remapped) an existing entry. - * No need to notify anyone- go for the next one. + * Did we find and update an old entry? */ - if (i < hival) { - isp_prt(isp, ISP_LOGINFO, retained, - fcp->portdb[i].loopid, i, fcp->portdb[i].portid); + if (i < MAX_FC_TARG) { continue; } /* - * We've not found this Port WWN anywhere. It's a new entry. - * See if we can leave it where it is (with target == loopid). + * Ah. A new device entry. Find an empty slot + * for it and save info for later disposition. */ - if (fcp->portdb[loopid].port_wwn != 0) { - for (lim = 0; lim < hival; lim++) { - if (fcp->portdb[lim].port_wwn == 0) - break; - } - /* "Cannot Happen" */ - if (lim == hival) { - isp_prt(isp, ISP_LOGWARN, "Remap Overflow"); - continue; + for (i = 0; i < MAX_FC_TARG; i++) { + if (fcp->portdb[i].state == FC_PORTDB_STATE_NIL) { + break; } - i = lim; - } else { - i = loopid; } - - /* - * NB: The actual loopid we use here is loopid- we may - * in fact be at a completely different index (target). - */ - fcp->portdb[i].loopid = loopid; - fcp->portdb[i].port_wwn = fcp->tport[loopid].port_wwn; - fcp->portdb[i].node_wwn = fcp->tport[loopid].node_wwn; - fcp->portdb[i].roles = fcp->tport[loopid].roles; - fcp->portdb[i].portid = fcp->tport[loopid].portid; - fcp->portdb[i].valid = 1; - - /* - * Tell the outside world we've arrived. - */ - (void) isp_async(isp, ISPASYNC_PROMENADE, &i); - } - - /* - * Now find all previously used targets that are now invalid and - * notify the outer layers that they're gone. - */ - for (lp = &fcp->portdb[0]; lp < &fcp->portdb[hival]; lp++) { - if (lp->valid || lp->port_wwn == 0) { + if (i == MAX_FC_TARG) { + isp_prt(isp, ISP_LOGERR, "out of portdb entries"); continue; } + lp = &fcp->portdb[i]; - /* - * Tell the outside world we've gone - * away and erase our pdb entry. - * - */ - loopid = lp - fcp->portdb; - (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); - MEMZERO((void *) lp, sizeof (*lp)); + MEMZERO(lp, sizeof (fcportdb_t)); + lp->autologin = 1; + lp->state = FC_PORTDB_STATE_NEW; + lp->new_portid = tmp.portid; + lp->new_roles = tmp.roles; + lp->handle = tmp.handle; + lp->port_wwn = tmp.port_wwn; + lp->node_wwn = tmp.node_wwn; + isp_prt(isp, ISP_LOGSANCFG, + "Loop Port 0x%02x@0x%x is New Entry", + tmp.portid, tmp.handle); } fcp->isp_loopstate = LOOP_LSCAN_DONE; return (0); } +/* + * Scan the fabric for devices and add them to our port database. + * + * Use the GID_FT command to get all Port IDs for FC4 SCSI devices it knows. + * + * For 2100-23XX cards, we can use the SNS mailbox command to pass simple + * name server commands to the switch management server via the QLogic f/w. + * + * For the 24XX card, we have to use CT-Pass through run via the Execute IOCB + * mailbox command. + * + * The net result is to leave the list of Port IDs setting untranslated in + * offset IGPOFF of the FC scratch area, whereupon we'll canonicalize it to + * host order at OGPOFF. + */ + +/* + * Take less than half of our scratch area to store Port IDs + */ +#define GIDLEN ((ISP2100_SCRLEN >> 1) - 16 - SNS_GID_FT_REQ_SIZE) +#define NGENT ((GIDLEN - 16) >> 2) + +#define IGPOFF (2 * QENTRY_LEN) +#define OGPOFF (ISP2100_SCRLEN >> 1) +#define ZTXOFF (ISP2100_SCRLEN - (1 * QENTRY_LEN)) +#define CTXOFF (ISP2100_SCRLEN - (2 * QENTRY_LEN)) +#define XTXOFF (ISP2100_SCRLEN - (3 * QENTRY_LEN)) -static int -isp_fabric_mbox_cmd(struct ispsoftc *isp, mbreg_t *mbp) +int +isp_gid_ft_sns(struct ispsoftc *isp) { - /* the caller sets up the mailbox */ - isp_mboxcmd(isp, mbp, MBLOGNONE); - if (mbp->param[0] != MBOX_COMMAND_COMPLETE) { - if (FCPARAM(isp)->isp_loopstate == LOOP_SCANNING_FABRIC) { - FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; - } - if (mbp->param[0] == MBOX_COMMAND_ERROR) { - char tbuf[16]; - char *m; - switch (mbp->param[1]) { - case 1: - m = "No Loop"; - break; - case 2: - m = "Failed to allocate IOCB buffer"; - break; - case 3: - m = "Failed to allocate XCB buffer"; - break; - case 4: - m = "timeout or transmit failed"; - break; - case 5: - m = "no fabric loop"; - break; - case 6: - m = "remote device not a target"; - break; - default: - SNPRINTF(tbuf, sizeof tbuf, "%x", - mbp->param[1]); - m = tbuf; - break; - } - isp_prt(isp, ISP_LOGERR, "SNS Failed- %s", m); - } - return (-1); - } + union { + sns_gid_ft_req_t _x; + u_int8_t _y[SNS_GID_FT_REQ_SIZE]; + } un; + fcparam *fcp = FCPARAM(isp); + sns_gid_ft_req_t *rq = &un._x; + mbreg_t mbs; - if (FCPARAM(isp)->isp_fwstate != FW_READY || - FCPARAM(isp)->isp_loopstate < LOOP_SCANNING_FABRIC) { - return (-1); + isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GID_FT) via SNS"); + + MEMZERO(rq, SNS_GID_FT_REQ_SIZE); + rq->snscb_rblen = GIDLEN >> 1; + rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma + IGPOFF); + rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma + IGPOFF); + rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma + IGPOFF); + rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma + IGPOFF); + rq->snscb_sblen = 6; + rq->snscb_cmd = SNS_GID_FT; + rq->snscb_mword_div_2 = NGENT; + rq->snscb_fc4_type = FC4_SCSI; + + isp_put_gid_ft_request(isp, rq, fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GID_FT_REQ_SIZE); + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GID_FT_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + mbs.logval = MBLOGALL; + mbs.timeout = 10000000; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + if (mbs.param[0] == MBOX_INVALID_COMMAND) { + return (1); + } else { + return (-1); + } } - return(0); + return (0); } -#ifdef ISP_USE_GA_NXT -static int -isp_scan_fabric(struct ispsoftc *isp, int ftype) +int +isp_gid_ft_ct_passthru(struct ispsoftc *isp) { - fcparam *fcp = isp->isp_param; - u_int32_t portid, first_portid, last_portid; - int hicap, last_port_same; - - if (fcp->isp_onfabric == 0) { - fcp->isp_loopstate = LOOP_FSCAN_DONE; - return (0); + mbreg_t mbs; + fcparam *fcp = FCPARAM(isp); + union { + isp_ct_pt_t plocal; + ct_hdr_t clocal; + u_int8_t q[QENTRY_LEN]; + } un; + isp_ct_pt_t *pt; + ct_hdr_t *ct; + u_int32_t *rp; + u_int8_t *scp = fcp->isp_scratch; + + isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GID_FT) via CT"); + + if (!IS_24XX(isp)) { + return (1); } - FC_SCRATCH_ACQUIRE(isp); - /* - * Since Port IDs are 24 bits, we can check against having seen - * anything yet with this value. + * Build a Passthrough IOCB in memory. */ - last_port_same = 0; - last_portid = 0xffffffff; /* not a port */ - first_portid = portid = fcp->isp_portid; - fcp->isp_loopstate = LOOP_SCANNING_FABRIC; - - for (hicap = 0; hicap < GA_NXT_MAX; hicap++) { - mbreg_t mbs; - sns_screq_t *rq; - sns_ga_nxt_rsp_t *rs0, *rs1; - struct lportdb lcl; - u_int8_t sc[SNS_GA_NXT_RESP_SIZE]; - - rq = (sns_screq_t *)sc; - MEMZERO((void *) rq, SNS_GA_NXT_REQ_SIZE); - rq->snscb_rblen = SNS_GA_NXT_RESP_SIZE >> 1; - rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+0x100); - rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+0x100); - rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+0x100); - rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+0x100); - rq->snscb_sblen = 6; - rq->snscb_data[0] = SNS_GA_NXT; - rq->snscb_data[4] = portid & 0xffff; - rq->snscb_data[5] = (portid >> 16) & 0xff; - isp_put_sns_request(isp, rq, (sns_screq_t *) fcp->isp_scratch); - MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GA_NXT_REQ_SIZE); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_SEND_SNS; - mbs.param[1] = SNS_GA_NXT_REQ_SIZE >> 1; - mbs.param[2] = DMA_WD1(fcp->isp_scdma); - mbs.param[3] = DMA_WD0(fcp->isp_scdma); - /* - * Leave 4 and 5 alone - */ - mbs.param[6] = DMA_WD3(fcp->isp_scdma); - mbs.param[7] = DMA_WD2(fcp->isp_scdma); - if (isp_fabric_mbox_cmd(isp, &mbs)) { - if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { - fcp->isp_loopstate = LOOP_PDB_RCVD; - } - FC_SCRATCH_RELEASE(isp); - return (-1); - } - MEMORYBARRIER(isp, SYNC_SFORCPU, 0x100, SNS_GA_NXT_RESP_SIZE); - rs1 = (sns_ga_nxt_rsp_t *) sc; - rs0 = (sns_ga_nxt_rsp_t *) ((u_int8_t *)fcp->isp_scratch+0x100); - isp_get_ga_nxt_response(isp, rs0, rs1); - if (rs1->snscb_cthdr.ct_response != FS_ACC) { - int level; - if (rs1->snscb_cthdr.ct_reason == 9 && - rs1->snscb_cthdr.ct_explanation == 7) - level = ISP_LOGDEBUG0; - else - level = ISP_LOGWARN; - isp_prt(isp, level, swrej, "GA_NXT", - rs1->snscb_cthdr.ct_reason, - rs1->snscb_cthdr.ct_explanation, portid); - FC_SCRATCH_RELEASE(isp); - fcp->isp_loopstate = LOOP_FSCAN_DONE; - return (0); - } - portid = - (((u_int32_t) rs1->snscb_port_id[0]) << 16) | - (((u_int32_t) rs1->snscb_port_id[1]) << 8) | - (((u_int32_t) rs1->snscb_port_id[2])); + pt = &un.plocal; + MEMZERO(un.q, QENTRY_LEN); + pt->ctp_header.rqs_entry_count = 1; + pt->ctp_header.rqs_entry_type = RQSTYPE_CT_PASSTHRU; + pt->ctp_handle = 0xffffffff; + pt->ctp_nphdl = NPH_SNS_ID; + pt->ctp_cmd_cnt = 1; + pt->ctp_time = 30; + pt->ctp_rsp_cnt = 1; + pt->ctp_rsp_bcnt = GIDLEN; + pt->ctp_cmd_bcnt = sizeof (*ct) + sizeof (u_int32_t); + pt->ctp_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma+XTXOFF); + pt->ctp_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma+XTXOFF); + pt->ctp_dataseg[0].ds_count = sizeof (*ct) + sizeof (u_int32_t); + pt->ctp_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma+IGPOFF); + pt->ctp_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma+IGPOFF); + pt->ctp_dataseg[1].ds_count = GIDLEN; + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "ct IOCB", QENTRY_LEN, pt); + } + isp_put_ct_pt(isp, pt, (isp_ct_pt_t *) &scp[CTXOFF]); - /* - * XXX: We should check to make sure that this entry - * XXX: supports the type(s) we are interested in. - */ - /* - * Okay, we now have information about a fabric object. - * If it is the type we're interested in, tell the outer layers - * about it. The outer layer needs to know: Port ID, WWNN, - * WWPN, FC4 type, and port type. - * - * The lportdb structure is adequate for this. - */ - MEMZERO(&lcl, sizeof (lcl)); - lcl.port_type = rs1->snscb_port_type; - lcl.fc4_type = ftype; - lcl.portid = portid; - lcl.node_wwn = - (((u_int64_t)rs1->snscb_nodename[0]) << 56) | - (((u_int64_t)rs1->snscb_nodename[1]) << 48) | - (((u_int64_t)rs1->snscb_nodename[2]) << 40) | - (((u_int64_t)rs1->snscb_nodename[3]) << 32) | - (((u_int64_t)rs1->snscb_nodename[4]) << 24) | - (((u_int64_t)rs1->snscb_nodename[5]) << 16) | - (((u_int64_t)rs1->snscb_nodename[6]) << 8) | - (((u_int64_t)rs1->snscb_nodename[7])); - lcl.port_wwn = - (((u_int64_t)rs1->snscb_portname[0]) << 56) | - (((u_int64_t)rs1->snscb_portname[1]) << 48) | - (((u_int64_t)rs1->snscb_portname[2]) << 40) | - (((u_int64_t)rs1->snscb_portname[3]) << 32) | - (((u_int64_t)rs1->snscb_portname[4]) << 24) | - (((u_int64_t)rs1->snscb_portname[5]) << 16) | - (((u_int64_t)rs1->snscb_portname[6]) << 8) | - (((u_int64_t)rs1->snscb_portname[7])); + /* + * Build the CT header and command in memory. + * + * Note that the CT header has to end up as Big Endian format in memory. + */ + ct = &un.clocal; + MEMZERO(ct, sizeof (*ct)); + ct->ct_revision = CT_REVISION; + ct->ct_fcs_type = CT_FC_TYPE_FC; + ct->ct_fcs_subtype = CT_FC_SUBTYPE_NS; + ct->ct_cmd_resp = SNS_GID_FT; + ct->ct_bcnt_resid = (GIDLEN - 16) >> 2; + + isp_put_ct_hdr(isp, ct, (ct_hdr_t *) &scp[XTXOFF]); + rp = (u_int32_t *) &scp[XTXOFF+sizeof (*ct)]; + ISP_IOZPUT_32(isp, FC4_SCSI, rp); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "CT HDR + payload after put", + sizeof (*ct) + sizeof (u_int32_t), &scp[XTXOFF]); + } + MEMZERO(&scp[ZTXOFF], QENTRY_LEN); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64; + mbs.param[1] = QENTRY_LEN; + mbs.param[2] = DMA_WD1(fcp->isp_scdma + CTXOFF); + mbs.param[3] = DMA_WD0(fcp->isp_scdma + CTXOFF); + mbs.param[6] = DMA_WD3(fcp->isp_scdma + CTXOFF); + mbs.param[7] = DMA_WD2(fcp->isp_scdma + CTXOFF); + mbs.timeout = 500000; + mbs.logval = MBLOGALL; + MEMORYBARRIER(isp, SYNC_SFORDEV, XTXOFF, 2 * QENTRY_LEN); + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, ZTXOFF, QENTRY_LEN); + pt = &un.plocal; + isp_get_ct_pt(isp, (isp_ct_pt_t *) &scp[ZTXOFF], pt); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "IOCB response", QENTRY_LEN, pt); + } - /* - * Does this fabric object support the type we want? - * If not, skip it. - */ - if (rs1->snscb_fc4_types[ftype >> 5] & (1 << (ftype & 0x1f))) { - if (first_portid == portid) { - lcl.last_fabric_dev = 1; - } else { - lcl.last_fabric_dev = 0; - } - (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl); - } else { - isp_prt(isp, ISP_LOGDEBUG0, - "PortID 0x%x doesn't support FC4 type 0x%x", - portid, ftype); - } - if (first_portid == portid) { - fcp->isp_loopstate = LOOP_FSCAN_DONE; - FC_SCRATCH_RELEASE(isp); - return (0); - } - if (portid == last_portid) { - if (last_port_same++ > 20) { - isp_prt(isp, ISP_LOGWARN, - "tangled fabric database detected"); - break; - } - } else { - last_port_same = 0 ; - last_portid = portid; - } + if (pt->ctp_status && pt->ctp_status != RQCS_DATA_UNDERRUN) { + isp_prt(isp, ISP_LOGWARN, "CT Passthrough returned 0x%x", + pt->ctp_status); + return (-1); } - FC_SCRATCH_RELEASE(isp); - if (hicap >= GA_NXT_MAX) { - isp_prt(isp, ISP_LOGWARN, "fabric too big (> %d)", GA_NXT_MAX); + MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN + 16); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "CT response", GIDLEN+16, &scp[IGPOFF]); } - fcp->isp_loopstate = LOOP_FSCAN_DONE; return (0); } -#else -#define GIDLEN ((ISP2100_SCRLEN >> 1) + 16) -#define NGENT ((GIDLEN - 16) >> 2) - -#define IGPOFF (ISP2100_SCRLEN - GIDLEN) -#define GXOFF (256) -static int -isp_scan_fabric(struct ispsoftc *isp, int ftype) +int +isp_scan_fabric(struct ispsoftc *isp) { fcparam *fcp = FCPARAM(isp); - mbreg_t mbs; - int i; - sns_gid_ft_req_t *rq; + u_int32_t portid; + u_int16_t handle, oldhandle; + int portidx, portlim, r; sns_gid_ft_rsp_t *rs0, *rs1; - if (fcp->isp_onfabric == 0) { + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "FC Scan Fabric"); + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate < LOOP_LSCAN_DONE) { + return (-1); + } + if (fcp->isp_loopstate > LOOP_SCANNING_FABRIC) { + return (0); + } + if (fcp->isp_topo != TOPO_FL_PORT && fcp->isp_topo != TOPO_F_PORT) { fcp->isp_loopstate = LOOP_FSCAN_DONE; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "FC Scan Fabric Done (no fabric)"); return (0); } FC_SCRATCH_ACQUIRE(isp); fcp->isp_loopstate = LOOP_SCANNING_FABRIC; - rq = (sns_gid_ft_req_t *)fcp->tport; - MEMZERO((void *) rq, SNS_GID_FT_REQ_SIZE); - rq->snscb_rblen = GIDLEN >> 1; - rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+IGPOFF); - rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+IGPOFF); - rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+IGPOFF); - rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+IGPOFF); - rq->snscb_sblen = 6; - rq->snscb_cmd = SNS_GID_FT; - rq->snscb_mword_div_2 = NGENT; - rq->snscb_fc4_type = ftype; - isp_put_gid_ft_request(isp, rq, (sns_gid_ft_req_t *) fcp->isp_scratch); - MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GID_FT_REQ_SIZE); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_SEND_SNS; - mbs.param[1] = SNS_GID_FT_REQ_SIZE >> 1; - mbs.param[2] = DMA_WD1(fcp->isp_scdma); - mbs.param[3] = DMA_WD0(fcp->isp_scdma); + if (IS_24XX(isp)) { + r = isp_gid_ft_ct_passthru(isp); + } else { + r = isp_gid_ft_sns(isp); + } - /* - * Leave 4 and 5 alone - */ - mbs.param[6] = DMA_WD3(fcp->isp_scdma); - mbs.param[7] = DMA_WD2(fcp->isp_scdma); - if (isp_fabric_mbox_cmd(isp, &mbs)) { - if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { - fcp->isp_loopstate = LOOP_PDB_RCVD; - } + if (r > 0) { + fcp->isp_loopstate = LOOP_FSCAN_DONE; FC_SCRATCH_RELEASE(isp); - return (-1); + return (0); + } else if (r < 0) { + fcp->isp_loopstate = LOOP_PDB_RCVD; /* try again */ + FC_SCRATCH_RELEASE(isp); + return (0); } if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { FC_SCRATCH_RELEASE(isp); return (-1); } + MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN); - rs1 = (sns_gid_ft_rsp_t *) fcp->tport; rs0 = (sns_gid_ft_rsp_t *) ((u_int8_t *)fcp->isp_scratch+IGPOFF); + rs1 = (sns_gid_ft_rsp_t *) ((u_int8_t *)fcp->isp_scratch+OGPOFF); isp_get_gid_ft_response(isp, rs0, rs1, NGENT); - if (rs1->snscb_cthdr.ct_response != FS_ACC) { + if (rs1->snscb_cthdr.ct_cmd_resp != LS_ACC) { int level; if (rs1->snscb_cthdr.ct_reason == 9 && - rs1->snscb_cthdr.ct_explanation == 7) - level = ISP_LOGDEBUG0; - else + rs1->snscb_cthdr.ct_explanation == 7) { + level = ISP_LOGSANCFG|ISP_LOGDEBUG0; + } else { level = ISP_LOGWARN; - isp_prt(isp, level, swrej, "GID_FT", - rs1->snscb_cthdr.ct_reason, - rs1->snscb_cthdr.ct_explanation, 0); + } + isp_prt(isp, level, "Fabric Nameserver rejected GID_FT " + "(Reason=0x%x Expl=0x%x)", rs1->snscb_cthdr.ct_reason, + rs1->snscb_cthdr.ct_explanation); FC_SCRATCH_RELEASE(isp); fcp->isp_loopstate = LOOP_FSCAN_DONE; return (0); } + /* - * Okay, we now have a list of Port IDs for this class of device. - * Go through the list and for each one get the WWPN/WWNN for it - * and tell the outer layers about it. The outer layer needs to - * know: Port ID, WWNN, WWPN, FC4 type, and (possibly) port type. + * If we get this far, we certainly still have the fabric controller. + */ + fcp->portdb[FL_ID].state = FC_PORTDB_STATE_PENDING_VALID; + + /* + * Prime the handle we will start using. + */ + oldhandle = NIL_HANDLE; + + /* + * Okay, we now have a list of Port IDs for all FC4 SCSI devices + * that the Fabric Name server knows about. Go through the list + * and remove duplicate port ids. + */ + + portlim = 0; + portidx = 0; + for (portidx = 0; portidx < NGENT-1; portidx++) { + if (rs1->snscb_ports[portidx].control & 0x80) { + break; + } + } + + /* + * If we're not at the last entry, our list wasn't big enough. + */ + if ((rs1->snscb_ports[portidx].control & 0x80) == 0) { + isp_prt(isp, ISP_LOGWARN, + "fabric too big for scratch area: increase ISP2100_SCRLEN"); + } + portlim = portidx + 1; + isp_prt(isp, ISP_LOGSANCFG, + "got %d ports back from name server", portlim); + + for (portidx = 0; portidx < portlim; portidx++) { + int npidx; + + portid = + ((rs1->snscb_ports[portidx].portid[0]) << 16) | + ((rs1->snscb_ports[portidx].portid[1]) << 8) | + ((rs1->snscb_ports[portidx].portid[2])); + + for (npidx = portidx + 1; npidx < portlim; npidx++) { + u_int32_t new_portid = + ((rs1->snscb_ports[npidx].portid[0]) << 16) | + ((rs1->snscb_ports[npidx].portid[1]) << 8) | + ((rs1->snscb_ports[npidx].portid[2])); + if (new_portid == portid) { + break; + } + } + + if (npidx < portlim) { + rs1->snscb_ports[npidx].portid[0] = 0; + rs1->snscb_ports[npidx].portid[1] = 0; + rs1->snscb_ports[npidx].portid[2] = 0; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "removing duplicate PortID 0x%x entry from list", + portid); + } + } + + /* + * Okay, we now have a list of Port IDs for all FC4 SCSI devices + * that the Fabric Name server knows about. + * + * For each entry on this list go through our port database looking + * for probational entries- if we find one, then an old entry is + * is maybe still this one. We get some information to find out. * - * The lportdb structure is adequate for this. + * Otherwise, it's a new fabric device, and we log into it + * (unconditionally). After searching the entire database + * again to make sure that we never ever ever ever have more + * than one entry that has the same PortID or the same + * WWNN/WWPN duple, we enter the device into our database. */ - i = -1; - do { - sns_gxn_id_req_t grqbuf, *gq = &grqbuf; - sns_gxn_id_rsp_t *gs0, grsbuf, *gs1 = &grsbuf; - struct lportdb lcl; -#if 0 - sns_gff_id_rsp_t *fs0, ffsbuf, *fs1 = &ffsbuf; -#endif - i++; - MEMZERO(&lcl, sizeof (lcl)); - lcl.fc4_type = ftype; - lcl.portid = - (((u_int32_t) rs1->snscb_ports[i].portid[0]) << 16) | - (((u_int32_t) rs1->snscb_ports[i].portid[1]) << 8) | - (((u_int32_t) rs1->snscb_ports[i].portid[2])); - - MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); - gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1; - gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); - gq->snscb_sblen = 6; - gq->snscb_cmd = SNS_GPN_ID; - gq->snscb_portid = lcl.portid; - isp_put_gxn_id_request(isp, gq, - (sns_gxn_id_req_t *) fcp->isp_scratch); - MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_SEND_SNS; - mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; - mbs.param[2] = DMA_WD1(fcp->isp_scdma); - mbs.param[3] = DMA_WD0(fcp->isp_scdma); + for (portidx = 0; portidx < portlim; portidx++) { + fcportdb_t *lp; + isp_pdb_t pdb; + u_int64_t wwnn, wwpn; + int dbidx, nr; + + portid = + ((rs1->snscb_ports[portidx].portid[0]) << 16) | + ((rs1->snscb_ports[portidx].portid[1]) << 8) | + ((rs1->snscb_ports[portidx].portid[2])); + + if (portid == 0) { + isp_prt(isp, ISP_LOGSANCFG, + "skipping null PortID at idx %d", portidx); + continue; + } + /* - * Leave 4 and 5 alone + * Skip ourselves... */ - mbs.param[6] = DMA_WD3(fcp->isp_scdma); - mbs.param[7] = DMA_WD2(fcp->isp_scdma); - if (isp_fabric_mbox_cmd(isp, &mbs)) { - if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { - fcp->isp_loopstate = LOOP_PDB_RCVD; - } - FC_SCRATCH_RELEASE(isp); - return (-1); + if (portid == fcp->isp_portid) { + isp_prt(isp, ISP_LOGSANCFG, + "skip ourselves @ PortID 0x%06x", portid); + continue; } - if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { - FC_SCRATCH_RELEASE(isp); - return (-1); + isp_prt(isp, ISP_LOGSANCFG, + "Checking Fabric Port 0x%06x", portid); + + /* + * We now search our Port Database for any + * probational entries with this PortID. We don't + * look for zombies here- only probational + * entries (we've already logged out of zombies). + */ + for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { + lp = &fcp->portdb[dbidx]; + + if (lp->state != FC_PORTDB_STATE_PROBATIONAL) { + continue; + } + if (lp->portid == portid) { + break; + } } - MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE); - gs0 = (sns_gxn_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF); - isp_get_gxn_id_response(isp, gs0, gs1); - if (gs1->snscb_cthdr.ct_response != FS_ACC) { - isp_prt(isp, ISP_LOGWARN, swrej, "GPN_ID", - gs1->snscb_cthdr.ct_reason, - gs1->snscb_cthdr.ct_explanation, lcl.portid); + + /* + * We found a probational entry with this Port ID. + */ + if (dbidx < MAX_FC_TARG) { + int handle_changed = 0; + + lp = &fcp->portdb[dbidx]; + + /* + * See if we're still logged into it. + * + * If we aren't, mark it as a dead device and + * leave the new portid in the database entry + * for somebody further along to decide what to + * do (policy choice). + * + * If we are, check to see if it's the same + * device still (it should be). If for some + * reason it isn't, mark it as a changed device + * and leave the new portid and role in the + * database entry for somebody further along to + * decide what to do (policy choice). + * + */ + + r = isp_getpdb(isp, lp->handle, &pdb, 0); if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { FC_SCRATCH_RELEASE(isp); + ISP_MARK_PORTDB(isp, 1); return (-1); } + if (r != 0) { + lp->new_portid = portid; + lp->state = FC_PORTDB_STATE_DEAD; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "Fabric Port 0x%06x considered dead", + portid); + continue; + } + + + /* + * Check to make sure that handle, portid, WWPN and + * WWNN agree. If they don't, then the association + * between this PortID and the stated handle has been + * broken by the firmware. + */ + MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename); + MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname); + if (pdb.handle != lp->handle || + pdb.portid != portid || + wwpn != lp->port_wwn || + wwnn != lp->node_wwn) { + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + fconf, dbidx, pdb.handle, pdb.portid, + (u_int32_t) (wwnn >> 32), (u_int32_t) wwnn, + (u_int32_t) (wwpn >> 32), (u_int32_t) wwpn, + lp->handle, portid, + (u_int32_t) (lp->node_wwn >> 32), + (u_int32_t) lp->node_wwn, + (u_int32_t) (lp->port_wwn >> 32), + (u_int32_t) lp->port_wwn); + /* + * Try to re-login to this device using a + * new handle. If that fails, mark it dead. + * + * isp_login_device will check for handle and + * portid consistency after re-login. + * + */ + if (isp_login_device(isp, portid, &pdb, + &oldhandle)) { + lp->new_portid = portid; + lp->state = FC_PORTDB_STATE_DEAD; + if (fcp->isp_loopstate != + LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + ISP_MARK_PORTDB(isp, 1); + return (-1); + } + continue; + } + MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename); + MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname); + if (wwpn != lp->port_wwn || + wwnn != lp->node_wwn) { + isp_prt(isp, ISP_LOGWARN, "changed WWN" + " after relogin"); + lp->new_portid = portid; + lp->state = FC_PORTDB_STATE_DEAD; + continue; + } + + lp->handle = pdb.handle; + handle_changed++; + } + + nr = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + + /* + * Check to see whether the portid and roles have + * stayed the same. If they have stayed the same, + * we believe that this is the same device and it + * hasn't become disconnected and reconnected, so + * mark it as pending valid. + * + * If they aren't the same, mark the device as a + * changed device and save the new port id and role + * and let somebody else decide. + */ + + lp->new_portid = portid; + lp->new_roles = nr; + if (pdb.portid != lp->portid || nr != lp->roles || + handle_changed) { + isp_prt(isp, ISP_LOGSANCFG, + "Fabric Port 0x%06x changed", portid); + lp->state = FC_PORTDB_STATE_CHANGED; + } else { + isp_prt(isp, ISP_LOGSANCFG, + "Fabric Port 0x%06x Now Pending Valid", + portid); + lp->state = FC_PORTDB_STATE_PENDING_VALID; + } continue; } - lcl.port_wwn = - (((u_int64_t)gs1->snscb_wwn[0]) << 56) | - (((u_int64_t)gs1->snscb_wwn[1]) << 48) | - (((u_int64_t)gs1->snscb_wwn[2]) << 40) | - (((u_int64_t)gs1->snscb_wwn[3]) << 32) | - (((u_int64_t)gs1->snscb_wwn[4]) << 24) | - (((u_int64_t)gs1->snscb_wwn[5]) << 16) | - (((u_int64_t)gs1->snscb_wwn[6]) << 8) | - (((u_int64_t)gs1->snscb_wwn[7])); - - MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); - gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1; - gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); - gq->snscb_sblen = 6; - gq->snscb_cmd = SNS_GNN_ID; - gq->snscb_portid = lcl.portid; - isp_put_gxn_id_request(isp, gq, - (sns_gxn_id_req_t *) fcp->isp_scratch); - MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_SEND_SNS; - mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; - mbs.param[2] = DMA_WD1(fcp->isp_scdma); - mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* - * Leave 4 and 5 alone + * Ah- a new entry. Search the database again for all non-NIL + * entries to make sure we never ever make a new database entry + * with the same port id. While we're at it, mark where the + * last free entry was. */ - mbs.param[6] = DMA_WD3(fcp->isp_scdma); - mbs.param[7] = DMA_WD2(fcp->isp_scdma); - if (isp_fabric_mbox_cmd(isp, &mbs)) { - if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { - fcp->isp_loopstate = LOOP_PDB_RCVD; + + dbidx = MAX_FC_TARG; + for (lp = fcp->portdb; lp < &fcp->portdb[MAX_FC_TARG]; lp++) { + if (lp >= &fcp->portdb[FL_ID] && + lp <= &fcp->portdb[SNS_ID]) { + continue; } - FC_SCRATCH_RELEASE(isp); - return (-1); - } - if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { - FC_SCRATCH_RELEASE(isp); - return (-1); - } - MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE); - gs0 = (sns_gxn_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF); - isp_get_gxn_id_response(isp, gs0, gs1); - if (gs1->snscb_cthdr.ct_response != FS_ACC) { - isp_prt(isp, ISP_LOGWARN, swrej, "GNN_ID", - gs1->snscb_cthdr.ct_reason, - gs1->snscb_cthdr.ct_explanation, lcl.portid); - if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { - FC_SCRATCH_RELEASE(isp); - return (-1); + if (lp->state == FC_PORTDB_STATE_NIL) { + if (dbidx == MAX_FC_TARG) { + dbidx = lp - fcp->portdb; + } + continue; + } + if (lp->state == FC_PORTDB_STATE_ZOMBIE) { + continue; } + if (lp->portid == portid) { + break; + } + } + + if (lp < &fcp->portdb[MAX_FC_TARG]) { + isp_prt(isp, ISP_LOGWARN, + "PortID 0x%06x already at %d handle %d state %d", + portid, dbidx, lp->handle, lp->state); continue; } - lcl.node_wwn = - (((u_int64_t)gs1->snscb_wwn[0]) << 56) | - (((u_int64_t)gs1->snscb_wwn[1]) << 48) | - (((u_int64_t)gs1->snscb_wwn[2]) << 40) | - (((u_int64_t)gs1->snscb_wwn[3]) << 32) | - (((u_int64_t)gs1->snscb_wwn[4]) << 24) | - (((u_int64_t)gs1->snscb_wwn[5]) << 16) | - (((u_int64_t)gs1->snscb_wwn[6]) << 8) | - (((u_int64_t)gs1->snscb_wwn[7])); /* - * The QLogic f/w is bouncing this with a parameter error. + * We should have the index of the first free entry seen. */ -#if 0 + if (dbidx == MAX_FC_TARG) { + isp_prt(isp, ISP_LOGERR, + "port database too small to login PortID 0x%06x" + "- increase MAX_FC_TARG", portid); + continue; + } + /* - * Try and get FC4 Features (FC-GS-3 only). - * We can use the sns_gxn_id_req_t for this request. + * Otherwise, point to our new home. */ - MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); - gq->snscb_rblen = SNS_GFF_ID_RESP_SIZE >> 1; - gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); - gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); - gq->snscb_sblen = 6; - gq->snscb_cmd = SNS_GFF_ID; - gq->snscb_portid = lcl.portid; - isp_put_gxn_id_request(isp, gq, - (sns_gxn_id_req_t *) fcp->isp_scratch); - MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_SEND_SNS; - mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; - mbs.param[2] = DMA_WD1(fcp->isp_scdma); - mbs.param[3] = DMA_WD0(fcp->isp_scdma); + lp = &fcp->portdb[dbidx]; + /* - * Leave 4 and 5 alone + * Try to see if we are logged into this device, + * and maybe log into it. + * + * isp_login_device will check for handle and + * portid consistency after login. */ - mbs.param[6] = DMA_WD3(fcp->isp_scdma); - mbs.param[7] = DMA_WD2(fcp->isp_scdma); - if (isp_fabric_mbox_cmd(isp, &mbs)) { - if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { - fcp->isp_loopstate = LOOP_PDB_RCVD; - } - FC_SCRATCH_RELEASE(isp); - return (-1); - } - if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { - FC_SCRATCH_RELEASE(isp); - return (-1); - } - MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GFF_ID_RESP_SIZE); - fs0 = (sns_gff_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF); - isp_get_gff_id_response(isp, fs0, fs1); - if (fs1->snscb_cthdr.ct_response != FS_ACC) { - isp_prt(isp, /* ISP_LOGDEBUG0 */ ISP_LOGWARN, - swrej, "GFF_ID", - fs1->snscb_cthdr.ct_reason, - fs1->snscb_cthdr.ct_explanation, lcl.portid); + if (isp_login_device(isp, portid, &pdb, &oldhandle)) { if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { FC_SCRATCH_RELEASE(isp); + ISP_MARK_PORTDB(isp, 1); return (-1); } - } else { - int index = (ftype >> 3); - int bshft = (ftype & 0x7) * 4; - int fc4_fval = - (fs1->snscb_fc4_features[index] >> bshft) & 0xf; - if (fc4_fval & 0x1) { - lcl.roles |= - (SVC3_INI_ROLE >> SVC3_ROLE_SHIFT); + continue; + } + + handle = pdb.handle; + MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename); + MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname); + nr = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + + /* + * And go through the database *one* more time to make sure + * that we do not make more than one entry that has the same + * WWNN/WWPN duple + */ + for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { + if (dbidx >= FL_ID && dbidx <= SNS_ID) { + continue; + } + if (fcp->portdb[dbidx].state == FC_PORTDB_STATE_NIL) { + continue; } - if (fc4_fval & 0x2) { - lcl.roles |= - (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT); + if (fcp->portdb[dbidx].node_wwn == wwnn && + fcp->portdb[dbidx].port_wwn == wwpn) { + break; } } -#endif + + if (dbidx == MAX_FC_TARG) { + MEMZERO(lp, sizeof (fcportdb_t)); + lp->handle = handle; + lp->node_wwn = wwnn; + lp->port_wwn = wwpn; + lp->new_portid = portid; + lp->new_roles = nr; + lp->state = FC_PORTDB_STATE_NEW; + isp_prt(isp, ISP_LOGSANCFG, + "Fabric Port 0x%06x is New Entry", portid); + continue; + } + + if (fcp->portdb[dbidx].state != FC_PORTDB_STATE_ZOMBIE) { + isp_prt(isp, ISP_LOGWARN, + "PortID 0x%x 0x%08x%08x/0x%08x%08x %ld already at " + "idx %d, state 0x%x", portid, + (u_int32_t) (wwnn >> 32), (u_int32_t) wwnn, + (u_int32_t) (wwpn >> 32), (u_int32_t) wwpn, + (long) (lp - fcp->portdb), dbidx, + fcp->portdb[dbidx].state); + continue; + } /* - * If we really want to know what kind of port type this is, - * we have to run another CT command. Otherwise, we'll leave - * it as undefined. - * - lcl.port_type = 0; + * We found a zombie entry that matches us. + * Revive it. We know that WWN and WWPN + * are the same. For fabric devices, we + * don't care that handle is different + * as we assign that. If role or portid + * are different, it maybe a changed device. */ - if (rs1->snscb_ports[i].control & 0x80) { - lcl.last_fabric_dev = 1; + lp = &fcp->portdb[dbidx]; + lp->handle = handle; + lp->new_portid = portid; + lp->new_roles = nr; + if (lp->portid != portid || lp->roles != nr) { + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "Zombie Fabric Port 0x%06x Now Changed", portid); + lp->state = FC_PORTDB_STATE_CHANGED; } else { - lcl.last_fabric_dev = 0; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "Zombie Fabric Port 0x%06x Now Pending Valid", + portid); + lp->state = FC_PORTDB_STATE_PENDING_VALID; } - (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl); + } - } while ((rs1->snscb_ports[i].control & 0x80) == 0 && i < NGENT-1); + FC_SCRATCH_RELEASE(isp); + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + ISP_MARK_PORTDB(isp, 1); + return (-1); + } + fcp->isp_loopstate = LOOP_FSCAN_DONE; + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "FC Scan Fabric Done"); + return (0); +} + +/* + * Find an unused handle and try and use to login to a port. + */ +int +isp_login_device(struct ispsoftc *isp, u_int32_t portid, isp_pdb_t *p, u_int16_t *ohp) +{ + int lim, i, r; + u_int16_t handle; + + if (FCPARAM(isp)->isp_2klogin) { + lim = NPH_MAX_2K; + } else { + lim = NPH_MAX; + } + + handle = isp_nxt_handle(isp, *ohp); + for (i = 0; i < lim; i++) { + /* + * See if we're still logged into something with + * this handle and that something agrees with this + * port id. + */ + r = isp_getpdb(isp, handle, p, 0); + if (r == 0 && p->portid != portid) { + (void) isp_plogx(isp, handle, portid, + PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT, 1); + } else if (r == 0) { + break; + } + if (FCPARAM(isp)->isp_loopstate != LOOP_SCANNING_FABRIC) { + return (-1); + } + /* + * Now try and log into the device + */ + r = isp_plogx(isp, handle, portid, PLOGX_FLG_CMD_PLOGI, 1); + if (FCPARAM(isp)->isp_loopstate != LOOP_SCANNING_FABRIC) { + return (-1); + } + if (r == 0) { + *ohp = handle; + break; + } else if ((r & 0xffff) == MBOX_PORT_ID_USED) { + handle = r >> 16; + break; + } else if (r != MBOX_LOOP_ID_USED) { + i = lim; + break; + } else { + *ohp = handle; + handle = isp_nxt_handle(isp, *ohp); + } + } + + if (i == lim) { + isp_prt(isp, ISP_LOGWARN, "PLOGI 0x%06x failed", portid); + return (-1); + } /* - * If we're not at the last entry, our list isn't big enough. + * If we successfully logged into it, get the PDB for it + * so we can crosscheck that it is still what we think it + * is and that we also have the role it plays */ - if ((rs1->snscb_ports[i].control & 0x80) == 0) { - isp_prt(isp, ISP_LOGWARN, "fabric too big for scratch area"); + r = isp_getpdb(isp, handle, p, 0); + if (FCPARAM(isp)->isp_loopstate != LOOP_SCANNING_FABRIC) { + return (-1); + } + if (r != 0) { + isp_prt(isp, ISP_LOGERR, "new device 0x%06x@0x%x disappeared", + portid, handle); + return (-1); } - FC_SCRATCH_RELEASE(isp); - fcp->isp_loopstate = LOOP_FSCAN_DONE; + if (p->handle != handle || p->portid != portid) { + isp_prt(isp, ISP_LOGERR, + "new device 0x%06x@0x%x changed (0x%06x@0x%0x)", + portid, handle, p->portid, p->handle); + return (-1); + } return (0); } -#endif -static void +int isp_register_fc4_type(struct ispsoftc *isp) { fcparam *fcp = isp->isp_param; @@ -2928,9 +3839,6 @@ isp_register_fc4_type(struct ispsoftc *isp) reqp->snscb_data[4] = fcp->isp_portid & 0xffff; reqp->snscb_data[5] = (fcp->isp_portid >> 16) & 0xff; reqp->snscb_data[6] = (1 << FC4_SCSI); -#if 0 - reqp->snscb_data[6] |= (1 << FC4_IP); /* ISO/IEC 8802-2 LLC/SNAP */ -#endif FC_SCRATCH_ACQUIRE(isp); isp_put_sns_request(isp, reqp, (sns_screq_t *) fcp->isp_scratch); MEMZERO(&mbs, sizeof (mbs)); @@ -2938,18 +3846,167 @@ isp_register_fc4_type(struct ispsoftc *isp) mbs.param[1] = SNS_RFT_ID_REQ_SIZE >> 1; mbs.param[2] = DMA_WD1(fcp->isp_scdma); mbs.param[3] = DMA_WD0(fcp->isp_scdma); - /* - * Leave 4 and 5 alone - */ mbs.param[6] = DMA_WD3(fcp->isp_scdma); mbs.param[7] = DMA_WD2(fcp->isp_scdma); - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + mbs.timeout = 10000000; + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_RFT_ID_REQ_SIZE); + isp_mboxcmd(isp, &mbs); FC_SCRATCH_RELEASE(isp); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - isp_prt(isp, ISP_LOGDEBUG0, "Register FC4 types succeeded"); + return (0); + } else { + return (-1); } } +int +isp_register_fc4_type_24xx(struct ispsoftc *isp) +{ + mbreg_t mbs; + fcparam *fcp = FCPARAM(isp); + union { + isp_ct_pt_t plocal; + rft_id_t clocal; + u_int8_t q[QENTRY_LEN]; + } un; + isp_ct_pt_t *pt; + ct_hdr_t *ct; + rft_id_t *rp; + u_int8_t *scp = fcp->isp_scratch; + + FC_SCRATCH_ACQUIRE(isp); + /* + * Build a Passthrough IOCB in memory. + */ + MEMZERO(un.q, QENTRY_LEN); + pt = &un.plocal; + pt->ctp_header.rqs_entry_count = 1; + pt->ctp_header.rqs_entry_type = RQSTYPE_CT_PASSTHRU; + pt->ctp_handle = 0xffffffff; + pt->ctp_nphdl = NPH_SNS_ID; + pt->ctp_cmd_cnt = 1; + pt->ctp_time = 1; + pt->ctp_rsp_cnt = 1; + pt->ctp_rsp_bcnt = sizeof (ct_hdr_t); + pt->ctp_cmd_bcnt = sizeof (rft_id_t); + pt->ctp_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma+XTXOFF); + pt->ctp_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma+XTXOFF); + pt->ctp_dataseg[0].ds_count = sizeof (rft_id_t); + pt->ctp_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma+IGPOFF); + pt->ctp_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma+IGPOFF); + pt->ctp_dataseg[1].ds_count = sizeof (ct_hdr_t); + isp_put_ct_pt(isp, pt, (isp_ct_pt_t *) &scp[CTXOFF]); + + /* + * Build the CT header and command in memory. + * + * Note that the CT header has to end up as Big Endian format in memory. + */ + MEMZERO(&un.clocal, sizeof (un.clocal)); + ct = &un.clocal.rftid_hdr; + ct->ct_revision = CT_REVISION; + ct->ct_fcs_type = CT_FC_TYPE_FC; + ct->ct_fcs_subtype = CT_FC_SUBTYPE_NS; + ct->ct_cmd_resp = SNS_RFT_ID; + ct->ct_bcnt_resid = (sizeof (rft_id_t) - sizeof (ct_hdr_t)) >> 2; + rp = &un.clocal; + rp->rftid_portid[0] = fcp->isp_portid >> 16; + rp->rftid_portid[1] = fcp->isp_portid >> 8; + rp->rftid_portid[2] = fcp->isp_portid; + rp->rftid_fc4types[FC4_SCSI >> 5] = 1 << (FC4_SCSI & 0x1f); + isp_put_rft_id(isp, rp, (rft_id_t *) &scp[XTXOFF]); + + MEMZERO(&scp[ZTXOFF], sizeof (ct_hdr_t)); + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64; + mbs.param[1] = QENTRY_LEN; + mbs.param[2] = DMA_WD1(fcp->isp_scdma + CTXOFF); + mbs.param[3] = DMA_WD0(fcp->isp_scdma + CTXOFF); + mbs.param[6] = DMA_WD3(fcp->isp_scdma + CTXOFF); + mbs.param[7] = DMA_WD2(fcp->isp_scdma + CTXOFF); + mbs.timeout = 500000; + mbs.logval = MBLOGALL; + MEMORYBARRIER(isp, SYNC_SFORDEV, XTXOFF, 2 * QENTRY_LEN); + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, ZTXOFF, QENTRY_LEN); + pt = &un.plocal; + isp_get_ct_pt(isp, (isp_ct_pt_t *) &scp[ZTXOFF], pt); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, "IOCB response", QENTRY_LEN, pt); + } + if (pt->ctp_status) { + FC_SCRATCH_RELEASE(isp); + isp_prt(isp, ISP_LOGWARN, "CT Passthrough returned 0x%x", + pt->ctp_status); + return (-1); + } + + isp_get_ct_hdr(isp, (ct_hdr_t *) &scp[IGPOFF], ct); + FC_SCRATCH_RELEASE(isp); + + if (ct->ct_cmd_resp == LS_RJT) { + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "Register FC4 Type rejected"); + return (-1); + } else if (ct->ct_cmd_resp == LS_ACC) { + isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, + "Register FC4 Type accepted"); + return(0); + } else { + isp_prt(isp, ISP_LOGWARN, + "Register FC4 Type: 0x%x", ct->ct_cmd_resp); + return (-1); + } +} + +u_int16_t +isp_nxt_handle(struct ispsoftc *isp, u_int16_t handle) +{ + int i; + if (handle == NIL_HANDLE) { + if (FCPARAM(isp)->isp_topo == TOPO_F_PORT) { + handle = 0; + } else { + handle = SNS_ID+1; + } + } else { + handle += 1; + if (handle >= FL_ID && handle <= SNS_ID) { + handle = SNS_ID+1; + } + if (handle >= NPH_RESERVED && handle <= NPH_FL_ID) { + handle = NPH_FL_ID+1; + } + if (FCPARAM(isp)->isp_2klogin) { + if (handle == NPH_MAX_2K) { + handle = 0; + } + } else { + if (handle == NPH_MAX) { + handle = 0; + } + } + } + if (handle == FCPARAM(isp)->isp_loopid) { + return (isp_nxt_handle(isp, handle)); + } + for (i = 0; i < MAX_FC_TARG; i++) { + if (FCPARAM(isp)->portdb[i].state == FC_PORTDB_STATE_NIL) { + continue; + } + if (FCPARAM(isp)->portdb[i].handle == handle) { + return (isp_nxt_handle(isp, handle)); + } + } + return (handle); +} + /* * Start a command. Locking is assumed done in the caller. */ @@ -2958,10 +4015,12 @@ int isp_start(XS_T *xs) { struct ispsoftc *isp; - u_int16_t nxti, optr, handle; + u_int32_t nxti, optr, handle; u_int8_t local[QENTRY_LEN]; ispreq_t *reqp, *qep; - int target, i; + void *cdbp; + u_int16_t *tptr; + int target, i, hdlidx = 0; XS_INITERR(xs); isp = XS_ISP(xs); @@ -3000,201 +4059,54 @@ isp_start(XS_T *xs) } /* - * Check to see whether we have good firmware state still or - * need to refresh our port database for this target. + * Translate the target to device handle as appropriate, checking + * for correct device state as well. */ target = XS_TGT(xs); if (IS_FC(isp)) { fcparam *fcp = isp->isp_param; - struct lportdb *lp; -#ifdef HANDLE_LOOPSTATE_IN_OUTER_LAYERS - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_READY) { - return (CMD_RQLATER); - } - - /* - * If we're not on a Fabric, we can't have a target - * above FL_PORT_ID-1. - * - * If we're on a fabric and *not* connected as an F-port, - * we can't have a target less than FC_SNS_ID+1. This - * keeps us from having to sort out the difference between - * local public loop devices and those which we might get - * from a switch's database. - */ - if (fcp->isp_onfabric == 0) { - if (target >= FL_PORT_ID) { - XS_SETERR(xs, HBA_SELTIMEOUT); - return (CMD_COMPLETE); - } - } else { - if (target >= FL_PORT_ID && target <= FC_SNS_ID) { - XS_SETERR(xs, HBA_SELTIMEOUT); - return (CMD_COMPLETE); - } - /* - * We used to exclude having local loop ports - * at the same time that we have fabric ports. - * That is, we used to exclude having ports - * at < FL_PORT_ID if we're FL-port. - * - * That's wrong. The only thing that could be - * dicey is if the switch you're connected to - * has these local loop ports appear on the - * fabric and we somehow attach them twice. - */ - } -#else - /* - * Check for f/w being in ready state. If the f/w - * isn't in ready state, then we don't know our - * loop ID and the f/w hasn't completed logging - * into all targets on the loop. If this is the - * case, then bounce the command. We pretend this is - * a SELECTION TIMEOUT error if we've never gone to - * FW_READY state at all- in this case we may not - * be hooked to a loop at all and we shouldn't hang - * the machine for this. Otherwise, defer this command - * until later. - */ - if (fcp->isp_fwstate != FW_READY) { - /* - * Give ourselves at most a 250ms delay. - */ - if (isp_fclink_test(isp, 250000)) { - XS_SETERR(xs, HBA_SELTIMEOUT); - if (fcp->loop_seen_once) { - return (CMD_RQLATER); - } else { - return (CMD_COMPLETE); - } - } - } /* - * If we're not on a Fabric, we can't have a target - * above FL_PORT_ID-1. - * - * If we're on a fabric and *not* connected as an F-port, - * we can't have a target less than FC_SNS_ID+1. This - * keeps us from having to sort out the difference between - * local public loop devices and those which we might get - * from a switch's database. + * Try again later. */ - if (fcp->isp_onfabric == 0) { - if (target >= FL_PORT_ID) { - XS_SETERR(xs, HBA_SELTIMEOUT); - return (CMD_COMPLETE); - } - } else { - if (target >= FL_PORT_ID && target <= FC_SNS_ID) { - XS_SETERR(xs, HBA_SELTIMEOUT); - return (CMD_COMPLETE); - } - if (fcp->isp_topo != TOPO_F_PORT && - target < FL_PORT_ID) { - XS_SETERR(xs, HBA_SELTIMEOUT); - return (CMD_COMPLETE); - } - } - - /* - * If our loop state is such that we haven't yet received - * a "Port Database Changed" notification (after a LIP or - * a Loop Reset or firmware initialization), then defer - * sending commands for a little while, but only if we've - * seen a valid loop at one point (otherwise we can get - * stuck at initialization time). - */ - if (fcp->isp_loopstate < LOOP_PDB_RCVD) { - XS_SETERR(xs, HBA_SELTIMEOUT); - if (fcp->loop_seen_once) { - return (CMD_RQLATER); - } else { - return (CMD_COMPLETE); - } - } - - /* - * If we're in the middle of loop or fabric scanning - * or merging the port databases, retry this command later. - */ - if (fcp->isp_loopstate == LOOP_SCANNING_FABRIC || - fcp->isp_loopstate == LOOP_SCANNING_LOOP || - fcp->isp_loopstate == LOOP_SYNCING_PDB) { + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_READY) { return (CMD_RQLATER); } - /* - * If our loop state is now such that we've just now - * received a Port Database Change notification, then - * we have to go off and (re)scan the fabric. We back - * out and try again later if this doesn't work. - */ - if (fcp->isp_loopstate == LOOP_PDB_RCVD && fcp->isp_onfabric) { - if (isp_scan_fabric(isp, FC4_SCSI)) { - return (CMD_RQLATER); - } - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate < LOOP_FSCAN_DONE) { - return (CMD_RQLATER); - } - } - - /* - * If our loop state is now such that we've just now - * received a Port Database Change notification, then - * we have to go off and (re)synchronize our port - * database. - */ - if (fcp->isp_loopstate < LOOP_READY) { - if (isp_pdb_sync(isp)) { - return (CMD_RQLATER); - } - if (fcp->isp_fwstate != FW_READY || - fcp->isp_loopstate != LOOP_READY) { - return (CMD_RQLATER); - } + if (XS_TGT(xs) >= MAX_FC_TARG) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); } - /* - * XXX: Here's were we would cancel any loop_dead flag - * XXX: also cancel in dead_loop timeout that's running - */ -#endif - - /* - * Now check whether we should even think about pursuing this. - */ - lp = &fcp->portdb[target]; - if (lp->valid == 0) { + hdlidx = fcp->isp_ini_map[XS_TGT(xs)] - 1; + isp_prt(isp, ISP_LOGDEBUG1, "XS_TGT(xs)=%d- hdlidx value %d", + XS_TGT(xs), hdlidx); + if (hdlidx < 0 || hdlidx >= MAX_FC_TARG) { XS_SETERR(xs, HBA_SELTIMEOUT); return (CMD_COMPLETE); } - if ((lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) == 0) { - isp_prt(isp, ISP_LOGDEBUG2, - "Target %d does not have target service", target); + if (fcp->portdb[hdlidx].state == FC_PORTDB_STATE_ZOMBIE) { + return (CMD_RQLATER); + } + if (fcp->portdb[hdlidx].state != FC_PORTDB_STATE_VALID) { XS_SETERR(xs, HBA_SELTIMEOUT); return (CMD_COMPLETE); } - /* - * Now turn target into what the actual Loop ID is. - */ - target = lp->loopid; - xs->sc_link->node_wwn = lp->node_wwn; - xs->sc_link->port_wwn = lp->port_wwn; - + target = fcp->portdb[hdlidx].handle; + xs->sc_link->node_wwn = fcp->portdb[hdlidx].node_wwn; + xs->sc_link->port_wwn = fcp->portdb[hdlidx].port_wwn; } /* - * Next check to see if any HBA or Device - * parameters need to be updated. + * Next check to see if any HBA or Device parameters need to be updated. */ if (isp->isp_update != 0) { isp_update(isp); } + start_again: + if (isp_getrqentry(isp, &nxti, &optr, (void *)&qep)) { isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow"); XS_SETERR(xs, HBA_BOTCH); @@ -3208,34 +4120,40 @@ isp_start(XS_T *xs) */ reqp = (ispreq_t *) local; if (isp->isp_sendmarker) { - u_int8_t n = (IS_DUALBUS(isp)? 2: 1); - /* - * Check ports to send markers for... - */ - for (i = 0; i < n; i++) { - if ((isp->isp_sendmarker & (1 << i)) == 0) { - continue; - } - MEMZERO((void *) reqp, QENTRY_LEN); - reqp->req_header.rqs_entry_count = 1; - reqp->req_header.rqs_entry_type = RQSTYPE_MARKER; - reqp->req_modifier = SYNC_ALL; - reqp->req_target = i << 7; /* insert bus number */ - isp_put_request(isp, reqp, qep); + if (IS_24XX(isp)) { + isp_marker_24xx_t *m = (isp_marker_24xx_t *) qep; + MEMZERO(m, QENTRY_LEN); + m->mrk_header.rqs_entry_count = 1; + m->mrk_header.rqs_entry_type = RQSTYPE_MARKER; + m->mrk_modifier = SYNC_ALL; + isp_put_marker_24xx(isp, m, (isp_marker_24xx_t *)qep); ISP_ADD_REQUEST(isp, nxti); - isp->isp_sendmarker &= ~(1 << i); - if (isp_getrqentry(isp, &nxti, &optr, (void *) &qep)) { - isp_prt(isp, ISP_LOGDEBUG0, - "Request Queue Overflow+"); - XS_SETERR(xs, HBA_BOTCH); - return (CMD_EAGAIN); + isp->isp_sendmarker = 0; + goto start_again; + } else { + for (i = 0; i < (IS_DUALBUS(isp)? 2: 1); i++) { + isp_marker_t *m = (isp_marker_t *) qep; + if ((isp->isp_sendmarker & (1 << i)) == 0) { + continue; + } + MEMZERO(m, QENTRY_LEN); + m->mrk_header.rqs_entry_count = 1; + m->mrk_header.rqs_entry_type = RQSTYPE_MARKER; + m->mrk_target = (i << 7); /* bus # */ + m->mrk_modifier = SYNC_ALL; + isp_put_marker(isp, m, (isp_marker_t *) qep); + ISP_ADD_REQUEST(isp, nxti); + isp->isp_sendmarker &= ~(1 << i); + goto start_again; } } } MEMZERO((void *)reqp, QENTRY_LEN); reqp->req_header.rqs_entry_count = 1; - if (IS_FC(isp)) { + if (IS_24XX(isp)) { + reqp->req_header.rqs_entry_type = RQSTYPE_T7RQS; + } else if (IS_FC(isp)) { reqp->req_header.rqs_entry_type = RQSTYPE_T2RQS; } else { if (XS_CDBLEN(xs) > 12) @@ -3245,7 +4163,26 @@ isp_start(XS_T *xs) } /* reqp->req_header.rqs_flags = 0; */ /* reqp->req_header.rqs_seqno = 0; */ - if (IS_FC(isp)) { + if (IS_24XX(isp)) { + int ttype; + if (XS_TAG_P(xs)) { + ttype = XS_TAG_TYPE(xs); + } else { + if (XS_CDBP(xs)[0] == 0x3) { + ttype = REQFLAG_HTAG; + } else { + ttype = REQFLAG_STAG; + } + } + if (ttype == REQFLAG_OTAG) { + ttype = FCP_CMND_TASK_ATTR_ORDERED; + } else if (ttype == REQFLAG_HTAG) { + ttype = FCP_CMND_TASK_ATTR_HEAD; + } else { + ttype = FCP_CMND_TASK_ATTR_SIMPLE; + } + ((ispreqt7_t *)reqp)->req_task_attribute = ttype; + } else if (IS_FC(isp)) { /* * See comment in isp_intr */ @@ -3253,7 +4190,7 @@ isp_start(XS_T *xs) /* * Fibre Channel always requires some kind of tag. - * The QLogic drivers seem be happy not to use a tag, + * The Qlogic drivers seem be happy not to use a tag, * but this breaks for some devices (IBM drives). */ if (XS_TAG_P(xs)) { @@ -3276,21 +4213,45 @@ isp_start(XS_T *xs) reqp->req_flags = XS_TAG_TYPE(xs); } } - reqp->req_target = target | (XS_CHANNEL(xs) << 7); + cdbp = reqp->req_cdb; + tptr = &reqp->req_time; + if (IS_SCSI(isp)) { + reqp->req_target = target | (XS_CHANNEL(xs) << 7); reqp->req_lun_trn = XS_LUN(xs); reqp->req_cdblen = XS_CDBLEN(xs); + } else if (IS_24XX(isp)) { + fcportdb_t *lp; + + lp = &FCPARAM(isp)->portdb[hdlidx]; + ((ispreqt7_t *)reqp)->req_nphdl = target; + ((ispreqt7_t *)reqp)->req_tidlo = lp->portid; + ((ispreqt7_t *)reqp)->req_tidhi = lp->portid >> 16; + if (XS_LUN(xs) > 256) { + ((ispreqt7_t *)reqp)->req_lun[0] = XS_LUN(xs) >> 8; + ((ispreqt7_t *)reqp)->req_lun[0] |= 0x40; + } + ((ispreqt7_t *)reqp)->req_lun[1] = XS_LUN(xs); + cdbp = ((ispreqt7_t *)reqp)->req_cdb; + tptr = &((ispreqt7_t *)reqp)->req_time; + } else if (FCPARAM(isp)->isp_2klogin) { + ((ispreqt2e_t *)reqp)->req_target = target; + ((ispreqt2e_t *)reqp)->req_scclun = XS_LUN(xs); + } else if (FCPARAM(isp)->isp_sccfw) { + ((ispreqt2_t *)reqp)->req_target = target; + ((ispreqt2_t *)reqp)->req_scclun = XS_LUN(xs); } else { - if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) - ((ispreqt2_t *)reqp)->req_scclun = XS_LUN(xs); - else - ((ispreqt2_t *)reqp)->req_lun_trn = XS_LUN(xs); + ((ispreqt2_t *)reqp)->req_target = target; + ((ispreqt2_t *)reqp)->req_lun_trn = XS_LUN(xs); } - MEMCPY(reqp->req_cdb, XS_CDBP(xs), XS_CDBLEN(xs)); + MEMCPY(cdbp, XS_CDBP(xs), XS_CDBLEN(xs)); - reqp->req_time = XS_TIME(xs) / 1000; - if (reqp->req_time == 0 && XS_TIME(xs)) { - reqp->req_time = 1; + *tptr = XS_TIME(xs) / 1000; + if (*tptr == 0 && XS_TIME(xs)) { + *tptr = 1; + } + if (IS_24XX(isp) && *tptr > 0x1999) { + *tptr = 0x1999; } if (isp_save_xs(isp, xs, &handle)) { @@ -3298,11 +4259,12 @@ isp_start(XS_T *xs) XS_SETERR(xs, HBA_BOTCH); return (CMD_EAGAIN); } + /* Whew. Thankfully the same for type 7 requests */ reqp->req_handle = handle; /* * Set up DMA and/or do any bus swizzling of the request entry - * so that the QLogic F/W understands what is being asked of it. + * so that the Qlogic F/W understands what is being asked of it. */ i = ISP_DMASETUP(isp, xs, reqp, &nxti, optr); if (i != CMD_QUEUED) { @@ -3314,7 +4276,7 @@ isp_start(XS_T *xs) return (i); } XS_SETERR(xs, HBA_NOERROR); - isp_prt(isp, ISP_LOGDEBUG2, + isp_prt(isp, ISP_LOGDEBUG0, "START cmd for %d.%d.%d cmd 0x%x datalen %ld", XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), XS_CDBP(xs)[0], (long) XS_XFRLEN(xs)); @@ -3334,7 +4296,9 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) XS_T *xs; mbreg_t mbs; int bus, tgt; - u_int16_t handle; + u_int32_t handle; + + MEMZERO(&mbs, sizeof (mbs)); switch (ctl) { default: @@ -3345,22 +4309,26 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) /* * Issue a bus reset. */ - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_BUS_RESET; - if (IS_SCSI(isp)) { - mbs.param[1] = - ((sdparam *) isp->isp_param)->isp_bus_reset_delay; - if (mbs.param[1] < 2) + if (IS_24XX(isp)) { + isp_prt(isp, ISP_LOGWARN, "RESET BUS NOT IMPLEMENTED"); + break; + } else if (IS_FC(isp)) { + mbs.param[1] = 10; + bus = 0; + } else { + mbs.param[1] = SDPARAM(isp)->isp_bus_reset_delay; + if (mbs.param[1] < 2) { mbs.param[1] = 2; + } bus = *((int *) arg); - if (IS_DUALBUS(isp)) + if (IS_DUALBUS(isp)) { mbs.param[2] = bus; - } else { - mbs.param[1] = 10; - bus = 0; + } } + mbs.param[0] = MBOX_BUS_RESET; isp->isp_sendmarker |= (1 << bus); - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { break; } @@ -3370,12 +4338,25 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) case ISPCTL_RESET_DEV: tgt = (*((int *) arg)) & 0xffff; - bus = (*((int *) arg)) >> 16; - MEMZERO(&mbs, sizeof (mbs)); + if (IS_24XX(isp)) { + isp_prt(isp, ISP_LOGWARN, "RESET DEV NOT IMPLEMENTED"); + break; + } else if (IS_FC(isp)) { + if (FCPARAM(isp)->isp_2klogin) { + mbs.param[1] = tgt; + mbs.ibits = (1 << 10); + } else { + mbs.param[1] = (tgt << 8); + } + bus = 0; + } else { + bus = (*((int *) arg)) >> 16; + mbs.param[1] = (bus << 15) | (tgt << 8); + } mbs.param[0] = MBOX_ABORT_TARGET; - mbs.param[1] = (tgt << 8) | (bus << 15); mbs.param[2] = 3; /* 'delay', in seconds */ - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { break; } @@ -3387,38 +4368,39 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) case ISPCTL_ABORT_CMD: xs = (XS_T *) arg; tgt = XS_TGT(xs); + handle = isp_find_handle(isp, xs); if (handle == 0) { isp_prt(isp, ISP_LOGWARN, "cannot find handle for command to abort"); break; } - bus = XS_CHANNEL(xs); - MEMZERO(&mbs, sizeof (mbs)); - mbs.param[0] = MBOX_ABORT; - if (IS_FC(isp)) { - if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { - mbs.param[1] = tgt << 8; - mbs.param[4] = 0; - mbs.param[5] = 0; + if (IS_24XX(isp)) { + isp_prt(isp, ISP_LOGWARN, "ABORT CMD NOT IMPLEMENTED"); + break; + } else if (IS_FC(isp)) { + if (FCPARAM(isp)->isp_sccfw) { + if (FCPARAM(isp)->isp_2klogin) { + mbs.param[1] = tgt; + } else { + mbs.param[1] = tgt << 8; + } mbs.param[6] = XS_LUN(xs); } else { mbs.param[1] = tgt << 8 | XS_LUN(xs); } } else { - mbs.param[1] = - (bus << 15) | (XS_TGT(xs) << 8) | XS_LUN(xs); + bus = XS_CHANNEL(xs); + mbs.param[1] = (bus << 15) | (tgt << 8) | XS_LUN(xs); } + mbs.param[0] = MBOX_ABORT; mbs.param[2] = handle; - isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_ERROR); - if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { - return (0); + mbs.logval = MBLOGALL & ~MBOX_COMMAND_ERROR; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + break; } - /* - * XXX: Look for command in the REQUEST QUEUE. That is, - * XXX: It hasn't been picked up by firmware yet. - */ - break; + return (0); case ISPCTL_UPDATE_PARAMS: @@ -3428,7 +4410,10 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) case ISPCTL_FCLINK_TEST: if (IS_FC(isp)) { - int usdelay = (arg)? *((int *) arg) : 250000; + int usdelay = *((int *) arg); + if (usdelay == 0) { + usdelay = 250000; + } return (isp_fclink_test(isp, usdelay)); } break; @@ -3436,8 +4421,7 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) case ISPCTL_SCAN_FABRIC: if (IS_FC(isp)) { - int ftype = (arg)? *((int *) arg) : FC4_SCSI; - return (isp_scan_fabric(isp, ftype)); + return (isp_scan_fabric(isp)); } break; @@ -3457,28 +4441,64 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) case ISPCTL_SEND_LIP: - if (IS_FC(isp)) { - MEMZERO(&mbs, sizeof (mbs)); + if (IS_FC(isp) && !IS_24XX(isp)) { mbs.param[0] = MBOX_INIT_LIP; - isp_mboxcmd(isp, &mbs, MBLOGALL); + if (FCPARAM(isp)->isp_2klogin) { + mbs.ibits = (1 << 10); + } + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { return (0); } } break; - case ISPCTL_GET_POSMAP: - + case ISPCTL_GET_PDB: if (IS_FC(isp) && arg) { - return (isp_getmap(isp, arg)); + int id = *((int *)arg); + isp_pdb_t *pdb = arg; + return (isp_getpdb(isp, id, pdb, 1)); } break; + case ISPCTL_GET_PORTNAME: + { + u_int64_t *wwnp = arg; + int loopid = *wwnp; + *wwnp = isp_get_portname(isp, loopid, 0); + if (*wwnp == (u_int64_t) -1) { + break; + } else { + return (0); + } + } case ISPCTL_RUN_MBOXCMD: - isp_mboxcmd(isp, arg, MBLOGALL); + isp_mboxcmd(isp, arg); return(0); + case ISPCTL_PLOGX: + { + isp_plcmd_t *p = arg; + int r; + + if ((p->flags & PLOGX_FLG_CMD_MASK) != PLOGX_FLG_CMD_PLOGI || + (p->handle != NIL_HANDLE)) { + return (isp_plogx(isp, p->handle, p->portid, + p->flags, 0)); + } + do { + p->handle = isp_nxt_handle(isp, p->handle); + r = isp_plogx(isp, p->handle, p->portid, p->flags, 0); + if ((r & 0xffff) == MBOX_PORT_ID_USED) { + p->handle = r >> 16; + r = 0; + break; + } + } while ((r & 0xffff) == MBOX_LOOP_ID_USED); + return (r); + } #ifdef ISP_TARGET_MODE case ISPCTL_TOGGLE_TMODE: { @@ -3489,11 +4509,11 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) */ if (IS_SCSI(isp)) { int param = *(int *)arg; - MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_ENABLE_TARGET_MODE; mbs.param[1] = param & 0xffff; mbs.param[2] = param >> 16; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { break; } @@ -3517,17 +4537,18 @@ isp_control(struct ispsoftc *isp, ispctl_t ctl, void *arg) * of completions on a request queue at any one time. */ #ifndef MAX_REQUESTQ_COMPLETIONS -#define MAX_REQUESTQ_COMPLETIONS 64 +#define MAX_REQUESTQ_COMPLETIONS 32 #endif void -isp_intr(struct ispsoftc *isp, u_int16_t isr, u_int16_t sema, u_int16_t mbox) +isp_intr(struct ispsoftc *isp, u_int32_t isr, u_int16_t sema, u_int16_t mbox) { XS_T *complist[MAX_REQUESTQ_COMPLETIONS], *xs; - u_int16_t iptr, optr, junk; + u_int32_t iptr, optr, junk; int i, nlooked = 0, ndone = 0; again: + optr = isp->isp_residx; /* * Is this a mailbox related interrupt? * The mailbox semaphore will be nonzero if so. @@ -3538,7 +4559,7 @@ again: if (isp->isp_mboxbsy) { int obits = isp->isp_obits; isp->isp_mboxtmp[0] = mbox; - for (i = 1; i < MAX_MAILBOX; i++) { + for (i = 1; i < MAX_MAILBOX(isp); i++) { if ((obits & (1 << i)) == 0) { continue; } @@ -3553,17 +4574,14 @@ again: MBOX_NOTIFY_COMPLETE(isp); } else { isp_prt(isp, ISP_LOGWARN, - "Mbox Command Async (0x%x) with no waiters", - mbox); + "mailbox cmd (0x%x) with no waiters", mbox); } } else if (isp_parse_async(isp, mbox) < 0) { return; } if ((IS_FC(isp) && mbox != ASYNC_RIO_RESP) || isp->isp_state != ISP_RUNSTATE) { - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); - ISP_WRITE(isp, BIU_SEMA, 0); - return; + goto out; } } @@ -3571,32 +4589,73 @@ again: * We can't be getting this now. */ if (isp->isp_state != ISP_RUNSTATE) { - isp_prt(isp, ISP_LOGWARN, + isp_prt(isp, ISP_LOGINFO, "interrupt (ISR=%x SEMA=%x) when not ready", isr, sema); /* * Thank you very much! *Burrrp*! */ - WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, - READ_RESPONSE_QUEUE_IN_POINTER(isp)); + ISP_WRITE(isp, isp->isp_respoutrp, + ISP_READ(isp, isp->isp_respinrp)); + if (IS_24XX(isp)) { + ISP_DISABLE_INTS(isp); + } + goto out; + } - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); - ISP_WRITE(isp, BIU_SEMA, 0); - return; +#ifdef ISP_TARGET_MODE + /* + * Check for ATIO Queue entries. + */ + if (isp->isp_rspbsy == 0 && (isp->isp_role & ISP_ROLE_TARGET) && + IS_24XX(isp)) { + iptr = ISP_READ(isp, isp->isp_atioinrp); + optr = ISP_READ(isp, isp->isp_atiooutrp); + + isp->isp_rspbsy = 1; + while (optr != iptr) { + u_int8_t qe[QENTRY_LEN]; + isphdr_t *hp; + u_int32_t oop; + void *addr; + + oop = optr; + MEMORYBARRIER(isp, SYNC_ATIOQ, oop, QENTRY_LEN); + addr = ISP_QUEUE_ENTRY(isp->isp_atioq, oop); + isp_get_hdr(isp, addr, (isphdr_t *)qe); + hp = (isphdr_t *)qe; + switch (hp->rqs_entry_type) { + case RQSTYPE_NOTIFY: + case RQSTYPE_ATIO: + (void) isp_target_notify(isp, addr, &oop); + break; + default: + isp_print_qentry(isp, "?ATIOQ entry?", + oop, addr); + break; + } + optr = ISP_NXT_QENTRY(oop, RESULT_QUEUE_LEN(isp)); + ISP_WRITE(isp, isp->isp_atiooutrp, optr); + } + isp->isp_rspbsy = 0; + optr = isp->isp_residx; } +#endif /* * Get the current Response Queue Out Pointer. * - * If we're a 2300, we can ask what hardware what it thinks. + * If we're a 2300 or 2400, we can ask what hardware what it thinks. */ - if (IS_23XX(isp)) { + if (IS_23XX(isp) || IS_24XX(isp)) { optr = ISP_READ(isp, isp->isp_respoutrp); /* * Debug: to be taken out eventually */ if (isp->isp_residx != optr) { - isp_prt(isp, ISP_LOGWARN, "optr %x soft optr %x", + isp_prt(isp, ISP_LOGINFO, + "isp_intr: hard optr=%x, soft optr %x", optr, isp->isp_residx); + isp->isp_residx = optr; } } else { optr = isp->isp_residx; @@ -3611,19 +4670,18 @@ again: if (IS_2100(isp) || (IS_2300(isp) && isp->isp_revision < 2)) { i = 0; do { - iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); - junk = READ_RESPONSE_QUEUE_IN_POINTER(isp); + iptr = ISP_READ(isp, isp->isp_respinrp); + junk = ISP_READ(isp, isp->isp_respinrp); } while (junk != iptr && ++i < 1000); if (iptr != junk) { - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); isp_prt(isp, ISP_LOGWARN, "Response Queue Out Pointer Unstable (%x, %x)", iptr, junk); - return; + goto out; } } else { - iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); + iptr = ISP_READ(isp, isp->isp_respinrp); } isp->isp_resodx = iptr; @@ -3637,15 +4695,17 @@ again: * make sure the old interrupt went away (to avoid 'ringing' * effects), but that didn't stop this from occurring. */ - if (IS_23XX(isp)) { + if (IS_24XX(isp)) { + junk = 0; + } else if (IS_23XX(isp)) { USEC_DELAY(100); - iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); + iptr = ISP_READ(isp, isp->isp_respinrp); junk = ISP_READ(isp, BIU_R2HSTSLO); } else { junk = ISP_READ(isp, BIU_ISR); } if (optr == iptr) { - if (IS_23XX(isp)) { + if (IS_23XX(isp) || IS_24XX(isp)) { ; } else { sema = ISP_READ(isp, BIU_SEMA); @@ -3661,42 +4721,75 @@ again: } } isp->isp_resodx = iptr; - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); - ISP_WRITE(isp, BIU_SEMA, 0); + if (isp->isp_rspbsy) { - return; + goto out; } isp->isp_rspbsy = 1; - while (optr != iptr) { - ispstatusreq_t local, *sp = &local; + u_int8_t qe[QENTRY_LEN]; + ispstatusreq_t *sp = (ispstatusreq_t *) qe; isphdr_t *hp; - int type; + int buddaboom, etype, scsi_status, completion_status; + int req_status_flags, req_state_flags; + u_int8_t *snsp, *resp; + u_int32_t rlen, slen; + long resid; u_int16_t oop; - int buddaboom = 0; hp = (isphdr_t *) ISP_QUEUE_ENTRY(isp->isp_result, optr); oop = optr; optr = ISP_NXT_QENTRY(optr, RESULT_QUEUE_LEN(isp)); nlooked++; + read_again: + buddaboom = req_status_flags = req_state_flags = 0; + resid = 0L; + /* * Synchronize our view of this response queue entry. */ MEMORYBARRIER(isp, SYNC_RESULT, oop, QENTRY_LEN); - - type = isp_get_response_type(isp, hp); - - if (type == RQSTYPE_RESPONSE) { + isp_get_hdr(isp, hp, &sp->req_header); + etype = sp->req_header.rqs_entry_type; + + if (IS_24XX(isp) && etype == RQSTYPE_RESPONSE) { + isp24xx_statusreq_t *sp2 = (isp24xx_statusreq_t *)qe; + isp_get_24xx_response(isp, + (isp24xx_statusreq_t *)hp, sp2); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, + "Response Queue Entry", QENTRY_LEN, sp2); + } + scsi_status = sp2->req_scsi_status; + completion_status = sp2->req_completion_status; + req_state_flags = 0; + resid = sp2->req_resid; + } else if (etype == RQSTYPE_RESPONSE) { isp_get_response(isp, (ispstatusreq_t *) hp, sp); - } else if (type == RQSTYPE_RIO2) { - isp_rio2_t rio; - isp_get_rio2(isp, (isp_rio2_t *) hp, &rio); - for (i = 0; i < rio.req_header.rqs_seqno; i++) { - isp_fastpost_complete(isp, rio.req_handles[i]); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, + "Response Queue Entry", QENTRY_LEN, sp); + } + scsi_status = sp->req_scsi_status; + completion_status = sp->req_completion_status; + req_status_flags = sp->req_status_flags; + req_state_flags = sp->req_state_flags; + resid = sp->req_resid; + } else if (etype == RQSTYPE_RIO2) { + isp_rio2_t *rio = (isp_rio2_t *)qe; + isp_get_rio2(isp, (isp_rio2_t *) hp, rio); + if (isp->isp_dblev & ISP_LOGDEBUG1) { + isp_print_bytes(isp, + "Response Queue Entry", QENTRY_LEN, rio); + } + for (i = 0; i < rio->req_header.rqs_seqno; i++) { + isp_fastpost_complete(isp, rio->req_handles[i]); + } + if (isp->isp_fpcchiwater < rio->req_header.rqs_seqno) { + isp->isp_fpcchiwater = + rio->req_header.rqs_seqno; } - if (isp->isp_fpcchiwater < rio.req_header.rqs_seqno) - isp->isp_fpcchiwater = rio.req_header.rqs_seqno; MEMZERO(hp, QENTRY_LEN); /* PERF */ continue; } else { @@ -3705,7 +4798,12 @@ again: * may have updated the response queue pointers for * us, so we reload our goal index. */ - if (isp_handle_other_response(isp, type, hp, &optr)) { + int r; + r = isp_handle_other_response(isp, etype, hp, &optr); + if (r < 0) { + goto read_again; + } + if (r > 0) { iptr = isp->isp_resodx; MEMZERO(hp, QENTRY_LEN); /* PERF */ continue; @@ -3716,34 +4814,33 @@ again: * we don't know how to deal with the rest of the * response. */ - isp_get_response(isp, (ispstatusreq_t *) hp, sp); /* * It really has to be a bounced request just copied * from the request queue to the response queue. If * not, something bad has happened. */ - if (sp->req_header.rqs_entry_type != RQSTYPE_REQUEST) { + if (etype != RQSTYPE_REQUEST) { isp_prt(isp, ISP_LOGERR, notresp, - sp->req_header.rqs_entry_type, oop, optr, - nlooked); - if (isp->isp_dblev & ISP_LOGDEBUG0) { - isp_print_bytes(isp, "Queue Entry", - QENTRY_LEN, sp); - } + etype, oop, optr, nlooked); + isp_print_bytes(isp, + "Request Queue Entry", QENTRY_LEN, sp); MEMZERO(hp, QENTRY_LEN); /* PERF */ continue; } buddaboom = 1; + scsi_status = sp->req_scsi_status; + completion_status = sp->req_completion_status; + req_status_flags = sp->req_status_flags; + req_state_flags = sp->req_state_flags; + resid = sp->req_resid; } - if (sp->req_header.rqs_flags & 0xf) { -#define _RQS_OFLAGS \ - ~(RQSFLAG_CONTINUATION|RQSFLAG_FULL|RQSFLAG_BADHEADER|RQSFLAG_BADPACKET) + if (sp->req_header.rqs_flags & RQSFLAG_MASK) { if (sp->req_header.rqs_flags & RQSFLAG_CONTINUATION) { isp_prt(isp, ISP_LOGWARN, "continuation segment"); - WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + ISP_WRITE(isp, isp->isp_respoutrp, optr); continue; } if (sp->req_header.rqs_flags & RQSFLAG_FULL) { @@ -3754,53 +4851,47 @@ again: */ } if (sp->req_header.rqs_flags & RQSFLAG_BADHEADER) { - isp_prt(isp, ISP_LOGERR, "bad header flag"); + isp_print_bytes(isp, "bad header flag", + QENTRY_LEN, sp); buddaboom++; } if (sp->req_header.rqs_flags & RQSFLAG_BADPACKET) { - isp_prt(isp, ISP_LOGERR, "bad request packet"); + isp_print_bytes(isp, "bad request packet", + QENTRY_LEN, sp); buddaboom++; } - if (sp->req_header.rqs_flags & _RQS_OFLAGS) { - isp_prt(isp, ISP_LOGERR, - "unknown flags (0x%x) in response", - sp->req_header.rqs_flags); - buddaboom++; - } -#undef _RQS_OFLAGS } + if (sp->req_handle > isp->isp_maxcmds || sp->req_handle < 1) { - MEMZERO(hp, QENTRY_LEN); /* PERF */ isp_prt(isp, ISP_LOGERR, - "bad request handle %d (type 0x%x, flags 0x%x)", - sp->req_handle, sp->req_header.rqs_entry_type, - sp->req_header.rqs_flags); - WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + "bad request handle %d (type 0x%x)", + sp->req_handle, etype); + MEMZERO(hp, QENTRY_LEN); /* PERF */ + ISP_WRITE(isp, isp->isp_respoutrp, optr); continue; } xs = isp_find_xs(isp, sp->req_handle); if (xs == NULL) { - u_int8_t ts = sp->req_completion_status & 0xff; - MEMZERO(hp, QENTRY_LEN); /* PERF */ + u_int8_t ts = completion_status & 0xff; /* * Only whine if this isn't the expected fallout of * aborting the command. */ - if (sp->req_header.rqs_entry_type != RQSTYPE_RESPONSE) { + if (etype != RQSTYPE_RESPONSE) { isp_prt(isp, ISP_LOGERR, "cannot find handle 0x%x (type 0x%x)", - sp->req_handle, - sp->req_header.rqs_entry_type); + sp->req_handle, etype); } else if (ts != RQCS_ABORTED) { isp_prt(isp, ISP_LOGERR, "cannot find handle 0x%x (status 0x%x)", sp->req_handle, ts); } - WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + MEMZERO(hp, QENTRY_LEN); /* PERF */ + ISP_WRITE(isp, isp->isp_respoutrp, optr); continue; } isp_destroy_handle(isp, sp->req_handle); - if (sp->req_status_flags & RQSTF_BUS_RESET) { + if (req_status_flags & RQSTF_BUS_RESET) { XS_SETERR(xs, HBA_BUSRESET); isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); } @@ -3808,39 +4899,71 @@ again: XS_SETERR(xs, HBA_BOTCH); } - if (IS_FC(isp) && (sp->req_scsi_status & RQCS_SV)) { + resp = NULL; + rlen = 0; + snsp = NULL; + slen = 0; + if (IS_24XX(isp) && (scsi_status & (RQCS_RV|RQCS_SV)) != 0) { + resp = ((isp24xx_statusreq_t *)sp)->req_rsp_sense; + rlen = ((isp24xx_statusreq_t *)sp)->req_response_len; + } else if (IS_FC(isp) && (scsi_status & RQCS_RV) != 0) { + resp = sp->req_response; + rlen = sp->req_response_len; + } + if (IS_FC(isp) && (scsi_status & RQCS_SV) != 0) { /* * Fibre Channel F/W doesn't say we got status * if there's Sense Data instead. I guess they * think it goes w/o saying. */ - sp->req_state_flags |= RQSF_GOT_STATUS; + req_state_flags |= RQSF_GOT_STATUS|RQSF_GOT_SENSE; + if (IS_24XX(isp)) { + snsp = + ((isp24xx_statusreq_t *)sp)->req_rsp_sense; + snsp += rlen; + slen = + ((isp24xx_statusreq_t *)sp)->req_sense_len; + } else { + snsp = sp->req_sense_data; + slen = sp->req_sense_len; + } + } else if (IS_SCSI(isp) && (req_state_flags & RQSF_GOT_SENSE)) { + snsp = sp->req_sense_data; + slen = sp->req_sense_len; } - if (sp->req_state_flags & RQSF_GOT_STATUS) { - *XS_STSP(xs) = sp->req_scsi_status & 0xff; + if (req_state_flags & RQSF_GOT_STATUS) { + *XS_STSP(xs) = scsi_status & 0xff; } - switch (sp->req_header.rqs_entry_type) { + switch (etype) { case RQSTYPE_RESPONSE: XS_SET_STATE_STAT(isp, xs, sp); - isp_parse_status(isp, sp, xs); + if (resp && rlen >= 4 && + resp[FCP_RSPNS_CODE_OFFSET] != 0) { + isp_prt(isp, ISP_LOGWARN, + "%d.%d.%d FCP RESPONSE: 0x%x", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), + resp[FCP_RSPNS_CODE_OFFSET]); + XS_SETERR(xs, HBA_BOTCH); + } + if (IS_24XX(isp)) { + isp_parse_status_24xx(isp, + (isp24xx_statusreq_t *)sp, xs, &resid); + } else { + isp_parse_status(isp, (void *)sp, xs, &resid); + } if ((XS_NOERR(xs) || XS_ERR(xs) == HBA_NOERROR) && (*XS_STSP(xs) == SCSI_BUSY)) { XS_SETERR(xs, HBA_TGTBSY); } if (IS_SCSI(isp)) { - XS_RESID(xs) = sp->req_resid; - if ((sp->req_state_flags & RQSF_GOT_STATUS) && - (*XS_STSP(xs) == SCSI_CHECK) && - (sp->req_state_flags & RQSF_GOT_SENSE)) { - XS_SAVE_SENSE(xs, sp); - } + XS_RESID(xs) = resid; /* * 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) { + if (req_status_flags & RQSTF_NEGOTIATION) { int t = XS_TGT(xs); sdparam *sdp = isp->isp_param; sdp += XS_CHANNEL(xs); @@ -3849,26 +4972,26 @@ again: (1 << XS_CHANNEL(xs)); } } else { - if (sp->req_status_flags & RQSF_XFER_COMPLETE) { + if (req_status_flags & RQSF_XFER_COMPLETE) { XS_RESID(xs) = 0; - } else if (sp->req_scsi_status & RQCS_RESID) { - XS_RESID(xs) = sp->req_resid; + } else if (scsi_status & RQCS_RESID) { + XS_RESID(xs) = resid; } else { XS_RESID(xs) = 0; } - if ((sp->req_state_flags & RQSF_GOT_STATUS) && - (*XS_STSP(xs) == SCSI_CHECK) && - (sp->req_scsi_status & RQCS_SV)) { - XS_SAVE_SENSE(xs, sp); - /* solely for the benefit of debug */ - sp->req_state_flags |= RQSF_GOT_SENSE; - } + } + if (snsp && slen) { + XS_SAVE_SENSE(xs, snsp, slen); } isp_prt(isp, ISP_LOGDEBUG2, - "asked for %ld got resid %ld", (long) XS_XFRLEN(xs), - (long) sp->req_resid); + "asked for %ld got raw resid %ld settled for %ld", + (long) XS_XFRLEN(xs), resid, (long) XS_RESID(xs)); break; case RQSTYPE_REQUEST: + case RQSTYPE_A64: + case RQSTYPE_T2RQS: + case RQSTYPE_T3RQS: + case RQSTYPE_T7RQS: if (sp->req_header.rqs_flags & RQSFLAG_FULL) { /* * Force Queue Full status. @@ -3879,16 +5002,19 @@ again: /* * ???? */ + XS_SETERR(xs, HBA_BOTCH); isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Entry bounced back"); - XS_SETERR(xs, HBA_BOTCH); + if ((isp->isp_dblev & ISP_LOGDEBUG1) == 0) { + isp_print_bytes(isp, "Bounced Request", + QENTRY_LEN, qe); + } } XS_RESID(xs) = XS_XFRLEN(xs); break; default: - isp_prt(isp, ISP_LOGWARN, - "unhandled response queue type 0x%x", - sp->req_header.rqs_entry_type); + isp_print_bytes(isp, "Unhandled Response Type", + QENTRY_LEN, qe); if (XS_NOERR(xs)) { XS_SETERR(xs, HBA_BOTCH); } @@ -3896,17 +5022,18 @@ again: } /* - * Free any dma resources. As a side effect, this may - * also do any cache flushing necessary for data coherence. */ + * Free any DMA resources. As a side effect, this may + * also do any cache flushing necessary for data coherence. + */ if (XS_XFRLEN(xs)) { ISP_DMAFREE(isp, xs, sp->req_handle); } if (((isp->isp_dblev & (ISP_LOGDEBUG2|ISP_LOGDEBUG3))) || - ((isp->isp_dblev & ISP_LOGDEBUG1) && ((!XS_NOERR(xs)) || + ((isp->isp_dblev & ISP_LOGDEBUG0) && ((!XS_NOERR(xs)) || (*XS_STSP(xs) != SCSI_GOOD)))) { char skey; - if (sp->req_state_flags & RQSF_GOT_SENSE) { + if (req_state_flags & RQSF_GOT_SENSE) { skey = XS_SNSKEY(xs) & 0xf; if (skey < 10) skey += '0'; @@ -3918,8 +5045,7 @@ again: skey = '.'; } isp_prt(isp, ISP_LOGALL, finmsg, XS_CHANNEL(xs), - XS_TGT(xs), XS_LUN(xs), XS_XFRLEN(xs), - (ulong)XS_RESID(xs), + XS_TGT(xs), XS_LUN(xs), XS_XFRLEN(xs), XS_RESID(xs), *XS_STSP(xs), skey, XS_ERR(xs)); } @@ -3938,13 +5064,23 @@ again: * ISP's notion of what we've seen so far. */ if (nlooked) { - WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + ISP_WRITE(isp, isp->isp_respoutrp, optr); /* - * While we're at it, read the request queue out pointer. + * While we're at it, read the requst queue out pointer. */ - isp->isp_reqodx = READ_REQUEST_QUEUE_OUT_POINTER(isp); - if (isp->isp_rscchiwater < ndone) + isp->isp_reqodx = ISP_READ(isp, isp->isp_rqstoutrp); + if (isp->isp_rscchiwater < ndone) { isp->isp_rscchiwater = ndone; + } + } + +out: + + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RISC_INT); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); } isp->isp_residx = optr; @@ -3962,7 +5098,7 @@ again: * Support routines. */ -static int +int isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) { int rval = 0; @@ -3979,26 +5115,32 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) case ASYNC_BUS_RESET: isp->isp_sendmarker |= (1 << bus); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif isp_async(isp, ISPASYNC_BUS_RESET, &bus); break; case ASYNC_SYSTEM_ERROR: -#ifdef ISP_FW_CRASH_DUMP + isp->isp_state = ISP_CRASHED; + if (IS_FC(isp)) { + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + } /* - * If we have crash dumps enabled, it's up to the handler - * for isp_async to reinit stuff and restart the firmware - * after performing the crash dump. The reason we do things - * this way is that we may need to activate a kernel thread - * to do all the crash dump goop. + * Were we waiting for a mailbox command to complete? + * If so, it's dead, so wake up the waiter. + */ + if (isp->isp_mboxbsy) { + isp->isp_obits = 1; + isp->isp_mboxtmp[0] = MBOX_HOST_INTERFACE_ERROR; + MBOX_NOTIFY_COMPLETE(isp); + } + /* + * It's up to the handler for isp_async to reinit stuff and + * restart the firmware */ isp_async(isp, ISPASYNC_FW_CRASH, NULL); -#else - isp_async(isp, ISPASYNC_FW_CRASH, NULL); - isp_reinit(isp); - isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); -#endif rval = -1; break; @@ -4016,7 +5158,7 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) * We don't need to be chatty about this- just unlatch things * and move on. */ - mbox = READ_REQUEST_QUEUE_OUT_POINTER(isp); + mbox = ISP_READ(isp, isp->isp_rqstoutrp); break; case ASYNC_TIMEOUT_RESET: @@ -4024,8 +5166,9 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) "timeout initiated SCSI bus reset of bus %d", bus); isp->isp_sendmarker |= (1 << bus); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif break; @@ -4033,8 +5176,9 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) isp_prt(isp, ISP_LOGINFO, "device reset on bus %d", bus); isp->isp_sendmarker |= (1 << bus); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif break; @@ -4125,29 +5269,33 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) int handle = (ISP_READ(isp, OUTMAILBOX2) << 16) | (ISP_READ(isp, OUTMAILBOX1)); - if (isp_target_async(isp, handle, mbox)) + if (isp_target_async(isp, handle, mbox)) { rval = -1; + } else { + /* count it as a fast posting intr */ + isp->isp_fphccmplt++; + } #else isp_prt(isp, ISP_LOGINFO, "Fast Posting CTIO done"); -#endif isp->isp_fphccmplt++; /* count it as a fast posting intr */ +#endif break; } + case ASYNC_LIP_ERROR: case ASYNC_LIP_F8: case ASYNC_LIP_OCCURRED: - FCPARAM(isp)->isp_lipseq = - ISP_READ(isp, OUTMAILBOX1); FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; isp->isp_sendmarker = 1; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_LIP, NULL); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif /* - * We've had problems with data corruption occurring on + * We've had problems with data corruption occuring on * commands that complete (with no apparent error) after * we receive a LIP. This has been observed mostly on * Local Loop topologies. To be safe, let's just mark @@ -4175,11 +5323,12 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) isp->isp_sendmarker = 1; FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_LOOP_UP, NULL); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif break; @@ -4187,11 +5336,12 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) isp->isp_sendmarker = 1; FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_NIL; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_LOOP_DOWN, NULL); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif break; @@ -4199,50 +5349,49 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) isp->isp_sendmarker = 1; FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_NIL; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_LOOP_RESET, NULL); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif break; case ASYNC_PDB_CHANGED: isp->isp_sendmarker = 1; FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_PDB); break; case ASYNC_CHANGE_NOTIFY: - /* - * Not correct, but it will force us to rescan the loop. - */ - FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; - isp_mark_getpdb_all(isp); + if (FCPARAM(isp)->isp_topo == TOPO_F_PORT) { + FCPARAM(isp)->isp_loopstate = LOOP_LSCAN_DONE; + } else { + FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; + } + ISP_MARK_PORTDB(isp, 1); isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_SNS); break; case ASYNC_PTPMODE: - if (FCPARAM(isp)->isp_onfabric) - FCPARAM(isp)->isp_topo = TOPO_F_PORT; - else - FCPARAM(isp)->isp_topo = TOPO_N_PORT; - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); isp->isp_sendmarker = 1; FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER); #ifdef ISP_TARGET_MODE - if (isp_target_async(isp, bus, mbox)) + if (isp_target_async(isp, bus, mbox)) { rval = -1; + } #endif isp_prt(isp, ISP_LOGINFO, "Point-to-Point mode"); break; case ASYNC_CONNMODE: mbox = ISP_READ(isp, OUTMAILBOX1); - isp_mark_getpdb_all(isp); + ISP_MARK_PORTDB(isp, 1); switch (mbox) { case ISP_CONN_LOOP: isp_prt(isp, ISP_LOGINFO, @@ -4258,13 +5407,7 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) break; case ISP_CONN_FATAL: isp_prt(isp, ISP_LOGERR, "FATAL CONNECTION ERROR"); -#ifdef ISP_FW_CRASH_DUMP isp_async(isp, ISPASYNC_FW_CRASH, NULL); -#else - isp_async(isp, ISPASYNC_FW_CRASH, NULL); - isp_reinit(isp); - isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); -#endif return (-1); case ISP_CONN_LOOPBACK: isp_prt(isp, ISP_LOGWARN, @@ -4281,6 +5424,15 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; break; + case ASYNC_RJT_SENT: /* same as ASYNC_QFULL_SENT */ + if (IS_24XX(isp)) { + isp_prt(isp, ISP_LOGTDEBUG0, "LS_RJT sent"); + break; + } else if (IS_2200(isp)) { + isp_prt(isp, ISP_LOGTDEBUG0, "QFULL sent"); + break; + } + /* FALLTHROUGH */ default: isp_prt(isp, ISP_LOGWARN, "Unknown Async Code 0x%x", mbox); break; @@ -4288,9 +5440,9 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) if (bus & 0x100) { int i, nh; - u_int16_t handles[5]; + u_int16_t handles[16]; - for (nh = 0, i = 1; i < MAX_MAILBOX; i++) { + for (nh = 0, i = 1; i < MAX_MAILBOX(isp); i++) { if ((bus & (1 << i)) == 0) { continue; } @@ -4301,8 +5453,9 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) isp_prt(isp, ISP_LOGDEBUG3, "fast post completion of %u", handles[i]); } - if (isp->isp_fpcchiwater < nh) + if (isp->isp_fpcchiwater < nh) { isp->isp_fpcchiwater = nh; + } } else { isp->isp_intoasync++; } @@ -4315,13 +5468,16 @@ isp_parse_async(struct ispsoftc *isp, u_int16_t mbox) * this is not used currently. */ -static int +int isp_handle_other_response(struct ispsoftc *isp, int type, - isphdr_t *hp, u_int16_t *optrp) + isphdr_t *hp, u_int32_t *optrp) { switch (type) { case RQSTYPE_STATUS_CONT: - isp_prt(isp, ISP_LOGINFO, "Ignored Continuation Response"); + isp_prt(isp, ISP_LOGDEBUG0, "Ignored Continuation Response"); + return (1); + case RQSTYPE_MARKER: + isp_prt(isp, ISP_LOGDEBUG0, "Marker Response"); return (1); case RQSTYPE_ATIO: case RQSTYPE_CTIO: @@ -4333,6 +5489,9 @@ isp_handle_other_response(struct ispsoftc *isp, int type, case RQSTYPE_ATIO2: case RQSTYPE_CTIO2: case RQSTYPE_CTIO3: + case RQSTYPE_CTIO7: + case RQSTYPE_ABTS_RCVD: + case RQSTYPE_ABTS_RSP: isp->isp_rsltccmplt++; /* count as a response completion */ #ifdef ISP_TARGET_MODE if (isp_target_notify(isp, (ispstatusreq_t *) hp, optrp)) { @@ -4342,17 +5501,31 @@ isp_handle_other_response(struct ispsoftc *isp, int type, /* FALLTHROUGH */ case RQSTYPE_REQUEST: default: - if (isp_async(isp, ISPASYNC_UNHANDLED_RESPONSE, hp)) { - return (1); + USEC_DELAY(100); + if (type != isp_get_response_type(isp, hp)) { + /* + * This is questionable- we're just papering over + * something we've seen on SMP linux in target + * mode- we don't really know what's happening + * here that causes us to think we've gotten + * an entry, but that either the entry isn't + * filled out yet or our CPU read data is stale. + */ + isp_prt(isp, ISP_LOGINFO, + "unstable type in response queue"); + return (-1); } isp_prt(isp, ISP_LOGWARN, "Unhandled Response Type 0x%x", isp_get_response_type(isp, hp)); + if (isp_async(isp, ISPASYNC_UNHANDLED_RESPONSE, hp)) { + return (1); + } return (0); } } -static void -isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) +void +isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs, long *rp) { switch (sp->req_completion_status & 0xff) { case RQCS_COMPLETE: @@ -4368,6 +5541,7 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); if (XS_NOERR(xs)) { XS_SETERR(xs, HBA_SELTIMEOUT); + *rp = XS_XFRLEN(xs); } return; } @@ -4380,6 +5554,7 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) case RQCS_DMA_ERROR: isp_prt(isp, ISP_LOGERR, "DMA error for command on %d.%d.%d", XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + *rp = XS_XFRLEN(xs); break; case RQCS_TRANSPORT_ERROR: @@ -4435,6 +5610,7 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) isp_prt(isp, ISP_LOGERR, "%s", buf); isp_prt(isp, ISP_LOGERR, "transport error for %d.%d.%d:\n%s", XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), buf); + *rp = XS_XFRLEN(xs); break; } case RQCS_RESET_OCCURRED: @@ -4445,6 +5621,7 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) if (XS_NOERR(xs)) { XS_SETERR(xs, HBA_BUSRESET); } + *rp = XS_XFRLEN(xs); return; case RQCS_ABORTED: @@ -4460,15 +5637,8 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) isp_prt(isp, ISP_LOGWARN, "command timed out for %d.%d.%d", XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); /* - * Check to see if we logged out the device. + * XXX: Check to see if we logged out of the device. */ - if (IS_FC(isp)) { - if ((sp->req_completion_status & RQSTF_LOGOUT) && - FCPARAM(isp)->portdb[XS_TGT(xs)].valid && - FCPARAM(isp)->portdb[XS_TGT(xs)].fabric_dev) { - FCPARAM(isp)->portdb[XS_TGT(xs)].relogin = 1; - } - } if (XS_NOERR(xs)) { XS_SETERR(xs, HBA_CMDTIMEOUT); } @@ -4675,40 +5845,214 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) * No such port on the loop. Moral equivalent of SELTIMEO */ case RQCS_PORT_LOGGED_OUT: + { + const char *reason; + u_int8_t sts = sp->req_completion_status & 0xff; + /* * It was there (maybe)- treat as a selection timeout. */ - if ((sp->req_completion_status & 0xff) == RQCS_PORT_UNAVAILABLE) - isp_prt(isp, ISP_LOGINFO, - "port unavailable for target %d", XS_TGT(xs)); - else - isp_prt(isp, ISP_LOGINFO, - "port logout for target %d", XS_TGT(xs)); + if (sts == RQCS_PORT_UNAVAILABLE) { + reason = "unavailable"; + } else { + reason = "logout"; + } + + isp_prt(isp, ISP_LOGINFO, "port %s for target %d", + reason, XS_TGT(xs)); + /* * If we're on a local loop, force a LIP (which is overkill) * to force a re-login of this unit. If we're on fabric, - * then we'll have to relogin as a matter of course. + * then we'll have to log in again as a matter of course. */ if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT || FCPARAM(isp)->isp_topo == TOPO_FL_PORT) { mbreg_t mbs; MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_INIT_LIP; + if (FCPARAM(isp)->isp_2klogin) { + mbs.ibits = (1 << 10); + } + mbs.logval = MBLOGALL; isp_mboxcmd_qnw(isp, &mbs, 1); } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + } + case RQCS_PORT_CHANGED: + isp_prt(isp, ISP_LOGWARN, + "port changed for target %d", XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + + case RQCS_PORT_BUSY: + isp_prt(isp, ISP_LOGWARN, + "port busy for target %d", XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_TGTBSY); + } + return; + + default: + isp_prt(isp, ISP_LOGERR, "Unknown Completion Status 0x%x", + sp->req_completion_status); + break; + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } +} + +void +isp_parse_status_24xx(struct ispsoftc *isp, isp24xx_statusreq_t *sp, + XS_T *xs, long *rp) +{ + int ru_marked, sv_marked; + switch (sp->req_completion_status) { + case RQCS_COMPLETE: + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_NOERROR); + } + return; + + case RQCS_DMA_ERROR: + isp_prt(isp, ISP_LOGERR, "DMA error for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + case RQCS_TRANSPORT_ERROR: + isp_prt(isp, ISP_LOGERR, "transport error for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_RESET_OCCURRED: + isp_prt(isp, ISP_LOGWARN, + "bus reset destroyed command for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BUSRESET); + } + return; + + case RQCS_ABORTED: + isp_prt(isp, ISP_LOGERR, "command aborted for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_ABORTED); + } + return; + + case RQCS_TIMEOUT: + isp_prt(isp, ISP_LOGWARN, "command timed out for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_CMDTIMEOUT); + } + return; + + case RQCS_DATA_OVERRUN: + XS_RESID(xs) = sp->req_resid; + isp_prt(isp, ISP_LOGERR, + "data overrun for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_DATAOVR); + } + return; + + case RQCS_24XX_DRE: /* data reassembly error */ + isp_prt(isp, ISP_LOGERR, "data reassembly error for target %d", + XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_ABORTED); + } + *rp = XS_XFRLEN(xs); + return; + + case RQCS_24XX_TABORT: /* aborted by target */ + isp_prt(isp, ISP_LOGERR, "target %d sent ABTS", + XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_ABORTED); + } + return; + + case RQCS_DATA_UNDERRUN: + ru_marked = (sp->req_scsi_status & RQCS_RU) != 0; /* - * Probably overkill. + * We can get an underrun w/o things being marked + * if we got a non-zero status. */ - isp->isp_sendmarker = 1; - FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; - isp_mark_getpdb_all(isp); - isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER); + sv_marked = (sp->req_scsi_status & (RQCS_SV|RQCS_RV)) != 0; + if ((ru_marked == 0 && sv_marked == 0) || + (sp->req_resid > XS_XFRLEN(xs))) { + isp_prt(isp, ISP_LOGWARN, bun, XS_TGT(xs), + XS_LUN(xs), XS_XFRLEN(xs), sp->req_resid, + (ru_marked)? "marked" : "not marked"); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } + return; + } + XS_RESID(xs) = sp->req_resid; + isp_prt(isp, ISP_LOGDEBUG0, + "%d.%d.%d data underrun (%d) for command 0x%x", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), + sp->req_resid, XS_CDBP(xs)[0] & 0xff); if (XS_NOERR(xs)) { - XS_SETERR(xs, HBA_SELTIMEOUT); + XS_SETERR(xs, HBA_NOERROR); } return; + case RQCS_PORT_UNAVAILABLE: + /* + * No such port on the loop. Moral equivalent of SELTIMEO + */ + case RQCS_PORT_LOGGED_OUT: + { + const char *reason; + u_int8_t sts = sp->req_completion_status & 0xff; + + /* + * It was there (maybe)- treat as a selection timeout. + */ + if (sts == RQCS_PORT_UNAVAILABLE) { + reason = "unavailable"; + } else { + reason = "logout"; + } + + isp_prt(isp, ISP_LOGINFO, "port %s for target %d", + reason, XS_TGT(xs)); + + /* + * If we're on a local loop, force a LIP (which is overkill) + * to force a re-login of this unit. If we're on fabric, + * then we'll have to log in again as a matter of course. + */ + if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT || + FCPARAM(isp)->isp_topo == TOPO_FL_PORT) { + mbreg_t mbs; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_LIP; + if (FCPARAM(isp)->isp_2klogin) { + mbs.ibits = (1 << 10); + } + mbs.logval = MBLOGALL; + isp_mboxcmd_qnw(isp, &mbs, 1); + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + } case RQCS_PORT_CHANGED: isp_prt(isp, ISP_LOGWARN, "port changed for target %d", XS_TGT(xs)); @@ -4717,10 +6061,22 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) } return; - case RQCS_PORT_BUSY: + + case RQCS_24XX_ENOMEM: /* f/w resource unavailable */ isp_prt(isp, ISP_LOGWARN, - "port busy for target %d", XS_TGT(xs)); + "f/w resource unavailable for target %d", XS_TGT(xs)); + if (XS_NOERR(xs)) { + *XS_STSP(xs) = SCSI_BUSY; + XS_SETERR(xs, HBA_TGTBSY); + } + return; + + case RQCS_24XX_TMO: /* task management overrun */ + isp_prt(isp, ISP_LOGWARN, + "command for target %d overlapped task management", + XS_TGT(xs)); if (XS_NOERR(xs)) { + *XS_STSP(xs) = SCSI_BUSY; XS_SETERR(xs, HBA_TGTBSY); } return; @@ -4735,7 +6091,7 @@ isp_parse_status(struct ispsoftc *isp, ispstatusreq_t *sp, XS_T *xs) } } -static void +void isp_fastpost_complete(struct ispsoftc *isp, u_int16_t fph) { XS_T *xs; @@ -4745,7 +6101,7 @@ isp_fastpost_complete(struct ispsoftc *isp, u_int16_t fph) } xs = isp_find_xs(isp, fph); if (xs == NULL) { - isp_prt(isp, ISP_LOGWARN, + isp_prt(isp, ISP_LOGDEBUG1, "Command for fast post handle 0x%x not found", fph); return; } @@ -4768,15 +6124,17 @@ isp_fastpost_complete(struct ispsoftc *isp, u_int16_t fph) isp_done(xs); } -static int +int isp_mbox_continue(struct ispsoftc *isp) { mbreg_t mbs; u_int16_t *ptr; + u_int32_t offset; switch (isp->isp_lastmbxcmd) { case MBOX_WRITE_RAM_WORD: case MBOX_READ_RAM_WORD: + case MBOX_WRITE_RAM_WORD_EXTENDED: case MBOX_READ_RAM_WORD_EXTENDED: break; default: @@ -4787,12 +6145,15 @@ isp_mbox_continue(struct ispsoftc *isp) return (-1); } - /* * Clear the previous interrupt. */ - ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); - ISP_WRITE(isp, BIU_SEMA, 0); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RISC_INT); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + } /* * Continue with next word. @@ -4801,27 +6162,46 @@ isp_mbox_continue(struct ispsoftc *isp) ptr = isp->isp_mbxworkp; switch (isp->isp_lastmbxcmd) { case MBOX_WRITE_RAM_WORD: - mbs.param[2] = *ptr++; - mbs.param[1] = isp->isp_mbxwrk1++; + mbs.param[1] = isp->isp_mbxwrk1++;; + mbs.param[2] = *ptr++;; break; case MBOX_READ_RAM_WORD: - case MBOX_READ_RAM_WORD_EXTENDED: *ptr++ = isp->isp_mboxtmp[2]; mbs.param[1] = isp->isp_mbxwrk1++; break; + case MBOX_WRITE_RAM_WORD_EXTENDED: + offset = isp->isp_mbxwrk1; + offset |= isp->isp_mbxwrk8 << 16; + + mbs.param[2] = *ptr++;; + mbs.param[1] = offset; + mbs.param[8] = offset >> 16; + isp->isp_mbxwrk1 = ++offset; + isp->isp_mbxwrk8 = offset >> 16; + break; + case MBOX_READ_RAM_WORD_EXTENDED: + offset = isp->isp_mbxwrk1; + offset |= isp->isp_mbxwrk8 << 16; + + *ptr++ = isp->isp_mboxtmp[2]; + mbs.param[1] = offset; + mbs.param[8] = offset >> 16; + isp->isp_mbxwrk1 = ++offset; + isp->isp_mbxwrk8 = offset >> 16; + break; } isp->isp_mbxworkp = ptr; + isp->isp_mbxwrk0--; mbs.param[0] = isp->isp_lastmbxcmd; - isp->isp_mbxwrk0 -= 1; + mbs.logval = MBLOGALL; isp_mboxcmd_qnw(isp, &mbs, 0); return (0); } - -#define HIBYT(x) ((x) >> 0x8) -#define LOBYT(x) ((x) & 0xff) -#define ISPOPMAP(a, b) (((a) << 8) | (b)) -static const u_int16_t mbpscsi[] = { +#define HIWRD(x) ((x) >> 16) +#define LOWRD(x) ((x) & 0xffff) +#define ISPOPMAP(a, b) (((a) << 16) | (b)) +static const u_int32_t mbpscsi[] = { ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */ ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */ ISPOPMAP(0x03, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ @@ -4829,7 +6209,7 @@ static const u_int16_t mbpscsi[] = { ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */ ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */ ISPOPMAP(0x3f, 0x3f), /* 0x06: MBOX_MAILBOX_REG_TEST */ - ISPOPMAP(0x03, 0x07), /* 0x07: MBOX_VERIFY_CHECKSUM */ + ISPOPMAP(0x07, 0x07), /* 0x07: MBOX_VERIFY_CHECKSUM */ ISPOPMAP(0x01, 0x0f), /* 0x08: MBOX_ABOUT_FIRMWARE */ ISPOPMAP(0x00, 0x00), /* 0x09: */ ISPOPMAP(0x00, 0x00), /* 0x0a: */ @@ -4906,7 +6286,7 @@ static const u_int16_t mbpscsi[] = { ISPOPMAP(0xdf, 0xdf), /* 0x51: DUMP RAM A64 */ ISPOPMAP(0xdf, 0xff), /* 0x52: INITIALIZE REQUEST QUEUE A64 */ ISPOPMAP(0xef, 0xff), /* 0x53: INITIALIZE RESPONSE QUEUE A64 */ - ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUTE IOCB A64 */ + ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUCUTE COMMAND IOCB A64 */ ISPOPMAP(0x07, 0x01), /* 0x55: ENABLE TARGET MODE */ ISPOPMAP(0x03, 0x0f), /* 0x56: GET TARGET STATUS */ ISPOPMAP(0x00, 0x00), /* 0x57: */ @@ -4922,8 +6302,8 @@ static const u_int16_t mbpscsi[] = { #define ISP_STRIPPED #endif -#ifndef ISP_STRIPPED -static char *scsi_mbcmd_names[] = { +#ifndef ISP_STRIPPED +static const char *scsi_mbcmd_names[] = { "NO-OP", "LOAD RAM", "EXEC FIRMWARE", @@ -5021,23 +6401,23 @@ static char *scsi_mbcmd_names[] = { }; #endif -static const u_int16_t mbpfc[] = { +static const u_int32_t mbpfc[] = { ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */ ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */ - ISPOPMAP(0x03, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ + ISPOPMAP(0x0f, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ ISPOPMAP(0xdf, 0x01), /* 0x03: MBOX_DUMP_RAM */ ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */ ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */ ISPOPMAP(0xff, 0xff), /* 0x06: MBOX_MAILBOX_REG_TEST */ - ISPOPMAP(0x03, 0x05), /* 0x07: MBOX_VERIFY_CHECKSUM */ + ISPOPMAP(0x03, 0x07), /* 0x07: MBOX_VERIFY_CHECKSUM */ ISPOPMAP(0x01, 0x4f), /* 0x08: MBOX_ABOUT_FIRMWARE */ - ISPOPMAP(0xdf, 0x01), /* 0x09: LOAD RAM */ + ISPOPMAP(0xdf, 0x01), /* 0x09: MBOX_LOAD_RISC_RAM_2100 */ ISPOPMAP(0xdf, 0x01), /* 0x0a: DUMP RAM */ - ISPOPMAP(0x00, 0x00), /* 0x0b: */ + ISPOPMAP(0x1ff, 0x01), /* 0x0b: MBOX_LOAD_RISC_RAM */ ISPOPMAP(0x00, 0x00), /* 0x0c: */ - ISPOPMAP(0x00, 0x00), /* 0x0d: */ + ISPOPMAP(0x10f, 0x01), /* 0x0d: MBOX_WRITE_RAM_WORD_EXTENDED */ ISPOPMAP(0x01, 0x05), /* 0x0e: MBOX_CHECK_FIRMWARE */ - ISPOPMAP(0x03, 0x07), /* 0x0f: MBOX_READ_RAM_WORD_EXTENDED(1) */ + ISPOPMAP(0x10f, 0x05), /* 0x0f: MBOX_READ_RAM_WORD_EXTENDED */ ISPOPMAP(0x1f, 0x11), /* 0x10: MBOX_INIT_REQ_QUEUE */ ISPOPMAP(0x2f, 0x21), /* 0x11: MBOX_INIT_RES_QUEUE */ ISPOPMAP(0x0f, 0x01), /* 0x12: MBOX_EXECUTE_IOCB */ @@ -5088,8 +6468,8 @@ static const u_int16_t mbpfc[] = { ISPOPMAP(0x00, 0x00), /* 0x3f: */ ISPOPMAP(0x03, 0x01), /* 0x40: MBOX_LOOP_PORT_BYPASS */ ISPOPMAP(0x03, 0x01), /* 0x41: MBOX_LOOP_PORT_ENABLE */ - ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_GET_RESOURCE_COUNTS */ - ISPOPMAP(0x01, 0x01), /* 0x43: MBOX_REQUEST_NON_PARTICIPATING_MODE */ + ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_GET_RESOURCE_COUNT */ + ISPOPMAP(0x01, 0x01), /* 0x43: MBOX_REQUEST_OFFLINE_MODE */ ISPOPMAP(0x00, 0x00), /* 0x44: */ ISPOPMAP(0x00, 0x00), /* 0x45: */ ISPOPMAP(0x00, 0x00), /* 0x46: */ @@ -5118,7 +6498,7 @@ static const u_int16_t mbpfc[] = { ISPOPMAP(0x07, 0x03), /* 0x5d: MBOX_GET_SET_DATA_RATE */ ISPOPMAP(0x00, 0x00), /* 0x5e: */ ISPOPMAP(0x00, 0x00), /* 0x5f: */ - ISPOPMAP(0xfd, 0x31), /* 0x60: MBOX_INIT_FIRMWARE */ + ISPOPMAP(0xcd, 0x01), /* 0x60: MBOX_INIT_FIRMWARE */ ISPOPMAP(0x00, 0x00), /* 0x61: */ ISPOPMAP(0x01, 0x01), /* 0x62: MBOX_INIT_LIP */ ISPOPMAP(0xcd, 0x03), /* 0x63: MBOX_GET_FC_AL_POSITION_MAP */ @@ -5148,18 +6528,19 @@ static const u_int16_t mbpfc[] = { ISPOPMAP(0x00, 0x00), /* 0x7b: */ ISPOPMAP(0x4f, 0x03), /* 0x7c: Get ID List */ ISPOPMAP(0xcf, 0x01), /* 0x7d: SEND LFA */ - ISPOPMAP(0x07, 0x01) /* 0x7e: Lun RESET */ + ISPOPMAP(0x0f, 0x01) /* 0x7e: LUN RESET */ }; /* * Footnotes * * (1): this sets bits 21..16 in mailbox register #8, which we nominally * do not access at this time in the core driver. The caller is - * responsible for setting this register first (Gross!). + * responsible for setting this register first (Gross!). The assumption + * is that we won't overflow. */ -#ifndef ISP_STRIPPED -static char *fc_mbcmd_names[] = { +#ifndef ISP_STRIPPED +static const char *fc_mbcmd_names[] = { "NO-OP", "LOAD RAM", "EXEC FIRMWARE", @@ -5171,7 +6552,7 @@ static char *fc_mbcmd_names[] = { "ABOUT FIRMWARE", "LOAD RAM", "DUMP RAM", - NULL, + "WRITE RAM WORD EXTENDED", NULL, "READ RAM WORD EXTENDED", "CHECK FIRMWARE", @@ -5226,12 +6607,12 @@ static char *fc_mbcmd_names[] = { NULL, "LOOP PORT BYPASS", "LOOP PORT ENABLE", - "GET RESOURCE COUNTS", + "GET RESOURCE COUNT", "REQUEST NON PARTICIPATING MODE", NULL, NULL, NULL, - "GET PORT DATABASE,, ENHANCED", + "GET PORT DATABASE ENHANCED", NULL, NULL, NULL, @@ -5290,26 +6671,24 @@ static char *fc_mbcmd_names[] = { }; #endif -static void +void isp_mboxcmd_qnw(struct ispsoftc *isp, mbreg_t *mbp, int nodelay) { - unsigned int lim, ibits, obits, box, opcode; - const u_int16_t *mcp; + unsigned int ibits, obits, box, opcode; + const u_int32_t *mcp; if (IS_FC(isp)) { mcp = mbpfc; - lim = (sizeof (mbpfc) / sizeof (mbpfc[0])); } else { mcp = mbpscsi; - lim = (sizeof (mbpscsi) / sizeof (mbpscsi[0])); } opcode = mbp->param[0]; - ibits = HIBYT(mcp[opcode]) & NMBOX_BMASK(isp); - obits = LOBYT(mcp[opcode]) & NMBOX_BMASK(isp); - for (box = 0; box < MAX_MAILBOX; box++) { + ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp); + obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp); + ibits |= mbp->ibits; + obits |= mbp->obits; + for (box = 0; box < MAX_MAILBOX(isp); box++) { if (ibits & (1 << box)) { - isp_prt(isp, ISP_LOGDEBUG1, "IN mbox %d = 0x%x", box, - mbp->param[box]); ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]); } if (nodelay == 0) { @@ -5321,7 +6700,11 @@ isp_mboxcmd_qnw(struct ispsoftc *isp, mbreg_t *mbp, int nodelay) isp->isp_obits = obits; isp->isp_mboxbsy = 1; } - ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_SET_HOST_INT); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + } /* * Oddly enough, if we're not delaying for an answer, * delay a bit to give the f/w a chance to pick up the @@ -5332,12 +6715,13 @@ isp_mboxcmd_qnw(struct ispsoftc *isp, mbreg_t *mbp, int nodelay) } } -static void -isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) +void +isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp) { - char *cname, *xname, tname[16], mname[16]; + const char *cname, *xname; + char tname[16], mname[16]; unsigned int lim, ibits, obits, box, opcode; - const u_int16_t *mcp; + const u_int32_t *mcp; if (IS_FC(isp)) { mcp = mbpfc; @@ -5353,8 +6737,14 @@ isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) return; } - ibits = HIBYT(mcp[opcode]) & NMBOX_BMASK(isp); - obits = LOBYT(mcp[opcode]) & NMBOX_BMASK(isp); + ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp); + obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp); + + /* + * Pick up any additional bits that the caller might have set. + */ + ibits |= mbp->ibits; + obits |= mbp->obits; if (ibits == 0 && obits == 0) { mbp->param[0] = MBOX_COMMAND_PARAM_ERROR; @@ -5365,10 +6755,15 @@ isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) /* * Get exclusive usage of mailbox registers. */ - MBOX_ACQUIRE(isp); + if (MBOX_ACQUIRE(isp)) { + mbp->param[0] = MBOX_REGS_BUSY; + goto out; + } - for (box = 0; box < MAX_MAILBOX; box++) { + for (box = 0; box < MAX_MAILBOX(isp); box++) { if (ibits & (1 << box)) { + isp_prt(isp, ISP_LOGDEBUG1, "IN mbox %d = 0x%04x", box, + mbp->param[box]); ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]); } isp->isp_mboxtmp[box] = mbp->param[box] = 0; @@ -5385,39 +6780,43 @@ isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) /* * Set Host Interrupt condition so that RISC will pick up mailbox regs. */ - ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + if (IS_24XX(isp)) { + ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_SET_HOST_INT); + } else { + ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + } /* * While we haven't finished the command, spin our wheels here. */ - MBOX_WAIT_COMPLETE(isp); + MBOX_WAIT_COMPLETE(isp, mbp); - if (isp->isp_mboxbsy) { - /* - * Command timed out. - */ - isp->isp_mboxbsy = 0; + /* + * Did the command time out? + */ + if (mbp->param[0] == MBOX_TIMEOUT) { MBOX_RELEASE(isp); - return; + goto out; } /* * Copy back output registers. */ - for (box = 0; box < MAX_MAILBOX; box++) { + for (box = 0; box < MAX_MAILBOX(isp); box++) { if (obits & (1 << box)) { mbp->param[box] = isp->isp_mboxtmp[box]; - isp_prt(isp, ISP_LOGDEBUG1, "OUT mbox %d = 0x%x", box, + isp_prt(isp, ISP_LOGDEBUG1, "OUT mbox %d = 0x%04x", box, mbp->param[box]); } } MBOX_RELEASE(isp); - - if (logmask == 0 || opcode == MBOX_EXEC_FIRMWARE) { + out: + isp->isp_mboxbsy = 0; + if (mbp->logval == 0 || opcode == MBOX_EXEC_FIRMWARE) { return; } -#ifdef ISP_STRIPPED +#ifdef ISP_STRIPPED cname = NULL; #else cname = (IS_FC(isp))? fc_mbcmd_names[opcode] : scsi_mbcmd_names[opcode]; @@ -5435,38 +6834,49 @@ isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) case MBOX_COMMAND_COMPLETE: break; case MBOX_INVALID_COMMAND: - if (logmask & MBLOGMASK(MBOX_COMMAND_COMPLETE)) + if (mbp->logval & MBLOGMASK(MBOX_COMMAND_COMPLETE)) { xname = "INVALID COMMAND"; + } break; case MBOX_HOST_INTERFACE_ERROR: - if (logmask & MBLOGMASK(MBOX_HOST_INTERFACE_ERROR)) + if (mbp->logval & MBLOGMASK(MBOX_HOST_INTERFACE_ERROR)) { xname = "HOST INTERFACE ERROR"; + } break; case MBOX_TEST_FAILED: - if (logmask & MBLOGMASK(MBOX_TEST_FAILED)) + if (mbp->logval & MBLOGMASK(MBOX_TEST_FAILED)) { xname = "TEST FAILED"; + } break; case MBOX_COMMAND_ERROR: - if (logmask & MBLOGMASK(MBOX_COMMAND_ERROR)) + if (mbp->logval & MBLOGMASK(MBOX_COMMAND_ERROR)) { xname = "COMMAND ERROR"; + } break; case MBOX_COMMAND_PARAM_ERROR: - if (logmask & MBLOGMASK(MBOX_COMMAND_PARAM_ERROR)) + if (mbp->logval & MBLOGMASK(MBOX_COMMAND_PARAM_ERROR)) { xname = "COMMAND PARAMETER ERROR"; + } break; case MBOX_LOOP_ID_USED: - if (logmask & MBLOGMASK(MBOX_LOOP_ID_USED)) + if (mbp->logval & MBLOGMASK(MBOX_LOOP_ID_USED)) { xname = "LOOP ID ALREADY IN USE"; + } break; case MBOX_PORT_ID_USED: - if (logmask & MBLOGMASK(MBOX_PORT_ID_USED)) + if (mbp->logval & MBLOGMASK(MBOX_PORT_ID_USED)) { xname = "PORT ID ALREADY IN USE"; + } break; case MBOX_ALL_IDS_USED: - if (logmask & MBLOGMASK(MBOX_ALL_IDS_USED)) + if (mbp->logval & MBLOGMASK(MBOX_ALL_IDS_USED)) { xname = "ALL LOOP IDS IN USE"; + } + break; + case MBOX_REGS_BUSY: + xname = "REGISTERS BUSY"; break; - case 0: /* special case */ + case MBOX_TIMEOUT: xname = "TIMEOUT"; break; default: @@ -5474,12 +6884,13 @@ isp_mboxcmd(struct ispsoftc *isp, mbreg_t *mbp, int logmask) xname = mname; break; } - if (xname) + if (xname) { isp_prt(isp, ISP_LOGALL, "Mailbox Command '%s' failed (%s)", cname, xname); + } } -static void +void isp_fw_state(struct ispsoftc *isp) { if (IS_FC(isp)) { @@ -5488,14 +6899,15 @@ isp_fw_state(struct ispsoftc *isp) MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_FW_STATE; - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { fcp->isp_fwstate = mbs.param[1]; } } } -static void +void isp_update(struct ispsoftc *isp) { int bus, upmask; @@ -5508,7 +6920,7 @@ isp_update(struct ispsoftc *isp) } } -static void +void isp_update_bus(struct ispsoftc *isp, int bus) { int tgt; @@ -5550,17 +6962,16 @@ isp_update_bus(struct ispsoftc *isp, int bus) */ if (sdp->isp_devparam[tgt].dev_refresh) { mbs.param[0] = MBOX_GET_TARGET_PARAMS; - sdp->isp_devparam[tgt].dev_refresh = 0; get = 1; } else if (sdp->isp_devparam[tgt].dev_update) { mbs.param[0] = MBOX_SET_TARGET_PARAMS; + /* * Make sure goal_flags has "Renegotiate on Error" * on and "Freeze Queue on Error" off. */ sdp->isp_devparam[tgt].goal_flags |= DPARM_RENEG; sdp->isp_devparam[tgt].goal_flags &= ~DPARM_QFRZ; - mbs.param[2] = sdp->isp_devparam[tgt].goal_flags; /* @@ -5594,26 +7005,31 @@ isp_update_bus(struct ispsoftc *isp, int bus) "bus %d set tgt %d flags 0x%x off 0x%x period 0x%x", bus, tgt, mbs.param[2], mbs.param[3] >> 8, mbs.param[3] & 0xff); - sdp->isp_devparam[tgt].dev_update = 0; - sdp->isp_devparam[tgt].dev_refresh = 1; get = 0; } else { continue; } mbs.param[1] = (bus << 15) | (tgt << 8); - isp_mboxcmd(isp, &mbs, MBLOGALL); + mbs.logval = MBLOGALL; + isp_mboxcmd(isp, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + continue; + } if (get == 0) { isp->isp_sendmarker |= (1 << bus); - continue; + sdp->isp_devparam[tgt].dev_update = 0; + sdp->isp_devparam[tgt].dev_refresh = 1; + } else { + sdp->isp_devparam[tgt].dev_refresh = 0; + flags = mbs.param[2]; + period = mbs.param[3] & 0xff; + offset = mbs.param[3] >> 8; + sdp->isp_devparam[tgt].actv_flags = flags; + sdp->isp_devparam[tgt].actv_period = period; + sdp->isp_devparam[tgt].actv_offset = offset; + get = (bus << 16) | tgt; + (void) isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, &get); } - flags = mbs.param[2]; - period = mbs.param[3] & 0xff; - offset = mbs.param[3] >> 8; - sdp->isp_devparam[tgt].actv_flags = flags; - sdp->isp_devparam[tgt].actv_period = period; - sdp->isp_devparam[tgt].actv_offset = offset; - get = (bus << 16) | tgt; - (void) isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, &get); } for (tgt = 0; tgt < MAX_TARGETS; tgt++) { @@ -5625,100 +7041,16 @@ isp_update_bus(struct ispsoftc *isp, int bus) } } -#ifndef DEFAULT_FRAMESIZE -#define DEFAULT_FRAMESIZE(isp) ICB_DFLT_FRMLEN -#endif #ifndef DEFAULT_EXEC_THROTTLE #define DEFAULT_EXEC_THROTTLE(isp) ISP_EXEC_THROTTLE #endif -static void +void isp_setdfltparm(struct ispsoftc *isp, int channel) { int tgt; sdparam *sdp; - if (IS_FC(isp)) { - fcparam *fcp = (fcparam *) isp->isp_param; - int nvfail; - - fcp += channel; - if (fcp->isp_gotdparms) { - return; - } - fcp->isp_gotdparms = 1; - fcp->isp_maxfrmlen = DEFAULT_FRAMESIZE(isp); - fcp->isp_maxalloc = ICB_DFLT_ALLOC; - fcp->isp_execthrottle = DEFAULT_EXEC_THROTTLE(isp); - fcp->isp_retry_delay = ICB_DFLT_RDELAY; - fcp->isp_retry_count = ICB_DFLT_RCOUNT; - /* Platform specific.... */ - fcp->isp_loopid = DEFAULT_LOOPID(isp); - fcp->isp_nodewwn = DEFAULT_NODEWWN(isp); - fcp->isp_portwwn = DEFAULT_PORTWWN(isp); - fcp->isp_fwoptions = 0; - fcp->isp_fwoptions |= 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 - if (isp->isp_confopts & ISP_CFG_FULL_DUPLEX) - fcp->isp_fwoptions |= ICBOPT_FULL_DUPLEX; - - /* - * Make sure this is turned off now until we get - * extended options from NVRAM - */ - fcp->isp_fwoptions &= ~ICBOPT_EXTENDED; - - /* - * Now try and read NVRAM unless told to not do so. - * This will set fcparam's isp_nodewwn && isp_portwwn. - */ - if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) { - nvfail = isp_read_nvram(isp); - if (nvfail) - isp->isp_confopts |= ISP_CFG_NONVRAM; - } else { - nvfail = 1; - } - /* - * Set node && port to override platform set defaults - * unless the nvram read failed (or none was done), - * or the platform code wants to use what had been - * set in the defaults. - */ - if (nvfail) { - isp->isp_confopts |= ISP_CFG_OWNWWPN|ISP_CFG_OWNWWNN; - } - if (isp->isp_confopts & ISP_CFG_OWNWWNN) { - isp_prt(isp, ISP_LOGCONFIG, "Using Node WWN 0x%08x%08x", - (u_int32_t) (DEFAULT_NODEWWN(isp) >> 32), - (u_int32_t) (DEFAULT_NODEWWN(isp) & 0xffffffff)); - ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp); - } else { - /* - * We always start out with values derived - * from NVRAM or our platform default. - */ - ISP_NODEWWN(isp) = fcp->isp_nodewwn; - } - if (isp->isp_confopts & ISP_CFG_OWNWWPN) { - isp_prt(isp, ISP_LOGCONFIG, "Using Port WWN 0x%08x%08x", - (u_int32_t) (DEFAULT_PORTWWN(isp) >> 32), - (u_int32_t) (DEFAULT_PORTWWN(isp) & 0xffffffff)); - ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp); - } else { - /* - * We always start out with values derived - * from NVRAM or our platform default. - */ - ISP_PORTWWN(isp) = fcp->isp_portwwn; - } - return; - } - sdp = (sdparam *) isp->isp_param; sdp += channel; @@ -5729,7 +7061,7 @@ isp_setdfltparm(struct ispsoftc *isp, int channel) return; } sdp->isp_gotdparms = 1; - + sdp->isp_bad_nvram = 0; /* * Establish some default parameters. */ @@ -5769,6 +7101,7 @@ isp_setdfltparm(struct ispsoftc *isp, int channel) if (isp_read_nvram(isp) == 0) { return; } + sdp->isp_bad_nvram = 1; } /* @@ -5779,7 +7112,8 @@ isp_setdfltparm(struct ispsoftc *isp, int channel) MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_GET_ACT_NEG_STATE; - isp_mboxcmd(isp, &mbs, MBLOGNONE); + mbs.logval = MBLOGNONE; + isp_mboxcmd(isp, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { sdp->isp_req_ack_active_neg = 1; sdp->isp_data_line_active_neg = 1; @@ -5817,7 +7151,7 @@ isp_setdfltparm(struct ispsoftc *isp, int channel) /* * We default to Wide/Fast for versions less than a 1040 - * (unless its SBus). + * (unless it's SBus). */ if (IS_ULTRA3(isp)) { off = ISP_80M_SYNCPARMS >> 8; @@ -5852,20 +7186,125 @@ isp_setdfltparm(struct ispsoftc *isp, int channel) } } +#ifndef DEFAULT_FRAMESIZE +#define DEFAULT_FRAMESIZE(isp) ICB_DFLT_FRMLEN +#endif +void +isp_setdfltfcparm(struct ispsoftc *isp) +{ + fcparam *fcp = FCPARAM(isp); + + if (fcp->isp_gotdparms) { + return; + } + fcp->isp_gotdparms = 1; + fcp->isp_bad_nvram = 0; + fcp->isp_maxfrmlen = DEFAULT_FRAMESIZE(isp); + fcp->isp_maxalloc = ICB_DFLT_ALLOC; + fcp->isp_execthrottle = DEFAULT_EXEC_THROTTLE(isp); + fcp->isp_retry_delay = ICB_DFLT_RDELAY; + fcp->isp_retry_count = ICB_DFLT_RCOUNT; + /* Platform specific.... */ + fcp->isp_loopid = DEFAULT_LOOPID(isp); + fcp->isp_wwnn_nvram = DEFAULT_NODEWWN(isp); + fcp->isp_wwpn_nvram = DEFAULT_PORTWWN(isp); + fcp->isp_fwoptions = 0; + fcp->isp_fwoptions |= ICBOPT_FAIRNESS; + fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE; + fcp->isp_fwoptions |= ICBOPT_HARD_ADDRESS; + fcp->isp_fwoptions |= ICBOPT_FAST_POST; + if (isp->isp_confopts & ISP_CFG_FULL_DUPLEX) { + fcp->isp_fwoptions |= ICBOPT_FULL_DUPLEX; + } + + /* + * Make sure this is turned off now until we get + * extended options from NVRAM + */ + fcp->isp_fwoptions &= ~ICBOPT_EXTENDED; + + /* + * Now try and read NVRAM unless told to not do so. + * This will set fcparam's isp_wwnn_nvram && isp_wwpn_nvram. + */ + if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) { + int i, j = 0; + /* + * Give a couple of tries at reading NVRAM. + */ + for (i = 0; i < 2; i++) { + j = isp_read_nvram(isp); + if (j == 0) { + break; + } + } + if (j) { + fcp->isp_bad_nvram = 1; + isp->isp_confopts |= ISP_CFG_NONVRAM; + isp->isp_confopts |= ISP_CFG_OWNWWPN; + isp->isp_confopts |= ISP_CFG_OWNWWNN; + } + } else { + isp->isp_confopts |= ISP_CFG_OWNWWPN|ISP_CFG_OWNWWNN; + } + + /* + * Set node && port to override platform set defaults + * unless the nvram read failed (or none was done), + * or the platform code wants to use what had been + * set in the defaults. + */ + if (isp->isp_confopts & ISP_CFG_OWNWWNN) { + isp_prt(isp, ISP_LOGCONFIG, "Using Node WWN 0x%08x%08x", + (u_int32_t) (DEFAULT_NODEWWN(isp) >> 32), + (u_int32_t) (DEFAULT_NODEWWN(isp) & 0xffffffff)); + ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp); + } else { + /* + * We always start out with values derived + * from NVRAM or our platform default. + */ + ISP_NODEWWN(isp) = fcp->isp_wwnn_nvram; + if (fcp->isp_wwnn_nvram == 0) { + isp_prt(isp, ISP_LOGCONFIG, + "bad WWNN- using default"); + ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp); + } + } + if (isp->isp_confopts & ISP_CFG_OWNWWPN) { + isp_prt(isp, ISP_LOGCONFIG, "Using Port WWN 0x%08x%08x", + (u_int32_t) (DEFAULT_PORTWWN(isp) >> 32), + (u_int32_t) (DEFAULT_PORTWWN(isp) & 0xffffffff)); + ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp); + } else { + /* + * We always start out with values derived + * from NVRAM or our platform default. + */ + ISP_PORTWWN(isp) = fcp->isp_wwpn_nvram; + if (fcp->isp_wwpn_nvram == 0) { + isp_prt(isp, ISP_LOGCONFIG, + "bad WWPN- using default"); + ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp); + } + } +} + /* * Re-initialize the ISP and complete all orphaned commands * with a 'botched' notice. The reset/init routines should * not disturb an already active list of commands. - * - * Locks held prior to coming here. */ void isp_reinit(struct ispsoftc *isp) { XS_T *xs; - u_int16_t handle; + u_int32_t tmp; + if (IS_FC(isp)) { + ISP_MARK_PORTDB(isp, 0); + } isp_reset(isp); if (isp->isp_state != ISP_RESETSTATE) { isp_prt(isp, ISP_LOGERR, "isp_reinit cannot reset card"); @@ -5877,15 +7316,36 @@ isp_reinit(struct ispsoftc *isp) if (isp->isp_state != ISP_RUNSTATE) { isp_prt(isp, ISP_LOGERR, "isp_reinit cannot restart card"); + ISP_DISABLE_INTS(isp); } - } + } else { + ISP_DISABLE_INTS(isp); + if (IS_FC(isp)) { + /* + * If we're in ISP_ROLE_NONE, turn off the lasers. + */ + if (!IS_24XX(isp)) { + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS); + ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS); + ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS); + } + } + } isp->isp_nactive = 0; - for (handle = 1; (int) handle <= isp->isp_maxcmds; handle++) { - xs = isp_find_xs(isp, handle); + for (tmp = 0; tmp < isp->isp_maxcmds; tmp++) { + u_int32_t handle; + + xs = isp->isp_xflist[tmp]; if (xs == NULL) { continue; } + handle = isp_find_handle(isp, xs); + if (handle == 0) { + continue; + } isp_destroy_handle(isp, handle); if (XS_XFRLEN(xs)) { ISP_DMAFREE(isp, xs, handle); @@ -5896,15 +7356,18 @@ isp_reinit(struct ispsoftc *isp) XS_SETERR(xs, HBA_BUSRESET); isp_done(xs); } +#ifdef ISP_TARGET_MODE + MEMZERO(isp->isp_tgtlist, isp->isp_maxcmds * sizeof (void **)); +#endif } /* * NVRAM Routines */ -static int +int isp_read_nvram(struct ispsoftc *isp) { - int i, amt; + int i, amt, retval; u_int8_t csum, minversion; union { u_int8_t _x[ISP2100_NVRAM_SIZE]; @@ -5913,7 +7376,9 @@ isp_read_nvram(struct ispsoftc *isp) #define nvram_data _n._x #define nvram_words _n._s - if (IS_FC(isp)) { + if (IS_24XX(isp)) { + return (isp_read_nvram_2400(isp)); + } else if (IS_FC(isp)) { amt = ISP2100_NVRAM_SIZE; minversion = 1; } else if (IS_ULTRA2(isp)) { @@ -5924,13 +7389,10 @@ isp_read_nvram(struct ispsoftc *isp) 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++) { + for (i = 0; i < amt>>1; 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) { @@ -5938,22 +7400,24 @@ isp_read_nvram(struct ispsoftc *isp) isp_prt(isp, ISP_LOGDEBUG0, "%x %x %x", nvram_data[0], nvram_data[1], nvram_data[2]); } - return (-1); - } - for (i = 2; i < amt>>1; i++) { - isp_rdnvram_word(isp, i, &nvram_words[i]); + retval = -1; + goto out; } + for (csum = 0, i = 0; i < amt; i++) { csum += nvram_data[i]; } if (csum != 0) { isp_prt(isp, ISP_LOGWARN, "invalid NVRAM checksum"); - return (-1); + retval = -1; + goto out; } + if (ISP_NVRAM_VERSION(nvram_data) < minversion) { isp_prt(isp, ISP_LOGWARN, "version %d NVRAM not understood", ISP_NVRAM_VERSION(nvram_data)); - return (-1); + retval = -1; + goto out; } if (IS_ULTRA3(isp)) { @@ -5970,24 +7434,66 @@ isp_read_nvram(struct ispsoftc *isp) } else { isp_parse_nvram_2100(isp, nvram_data); } - return (0); + retval = 0; +out: + return (retval); #undef nvram_data #undef nvram_words } -static void +int +isp_read_nvram_2400(struct ispsoftc *isp) +{ + u_int8_t *nvram_data = FCPARAM(isp)->isp_scratch; + int retval = 0; + u_int32_t addr, csum, lwrds, *dptr; + + if (isp->isp_port) { + addr = ISP2400_NVRAM_PORT1_ADDR; + } else { + addr = ISP2400_NVRAM_PORT0_ADDR; + } + + dptr = (u_int32_t *) nvram_data; + for (lwrds = 0; lwrds < ISP2400_NVRAM_SIZE >> 2; lwrds++) { + isp_rd_2400_nvram(isp, addr++, dptr++); + } + if (nvram_data[0] != 'I' || nvram_data[1] != 'S' || + nvram_data[2] != 'P') { + isp_prt(isp, ISP_LOGWARN, "invalid NVRAM header (%x %x %x)", + nvram_data[0], nvram_data[1], nvram_data[2]); + retval = -1; + goto out; + } + dptr = (u_int32_t *) nvram_data; + for (csum = 0, lwrds = 0; lwrds < ISP2400_NVRAM_SIZE >> 2; lwrds++) { + u_int32_t tmp; + ISP_IOXGET_32(isp, &dptr[lwrds], tmp); + csum += tmp; + } + if (csum != 0) { + isp_prt(isp, ISP_LOGWARN, "invalid NVRAM checksum"); + retval = -1; + goto out; + } + isp_parse_nvram_2400(isp, nvram_data); +out: + return (retval); +} + +void isp_rdnvram_word(struct ispsoftc *isp, int wo, u_int16_t *rp) { int i, cbits; - u_int16_t bit, rqst; + u_int16_t bit, rqst, junk; ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); - USEC_DELAY(2); + USEC_DELAY(10); ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); - USEC_DELAY(2); + USEC_DELAY(10); if (IS_FC(isp)) { - wo &= ((ISP2100_NVRAM_SIZE >> 1) - 1); + wo &= ((ISP2100_NVRAM_SIZE >> 1) - 1); if (IS_2312(isp) && isp->isp_port) { wo += 128; } @@ -6013,11 +7519,14 @@ isp_rdnvram_word(struct ispsoftc *isp, int wo, u_int16_t *rp) bit = BIU_NVRAM_SELECT; } ISP_WRITE(isp, BIU_NVRAM, bit); - USEC_DELAY(2); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ ISP_WRITE(isp, BIU_NVRAM, bit | BIU_NVRAM_CLOCK); - USEC_DELAY(2); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ ISP_WRITE(isp, BIU_NVRAM, bit); - USEC_DELAY(2); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ } /* * Now read the result back in (bits come back in MSB format). @@ -6027,21 +7536,46 @@ isp_rdnvram_word(struct ispsoftc *isp, int wo, u_int16_t *rp) u_int16_t rv; *rp <<= 1; ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); - USEC_DELAY(2); + USEC_DELAY(10); rv = ISP_READ(isp, BIU_NVRAM); if (rv & BIU_NVRAM_DATAIN) { *rp |= 1; } - USEC_DELAY(2); + USEC_DELAY(10); ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); - USEC_DELAY(2); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ } ISP_WRITE(isp, BIU_NVRAM, 0); - USEC_DELAY(2); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ ISP_SWIZZLE_NVRAM_WORD(isp, rp); } -static void +void +isp_rd_2400_nvram(struct ispsoftc *isp, u_int32_t addr, u_int32_t *rp) +{ + int loops = 0; + const u_int32_t base = 0x7ffe0000; + u_int32_t tmp = 0; + + ISP_WRITE(isp, BIU2400_FLASH_ADDR, base | addr); + for (loops = 0; loops < 5000; loops++) { + USEC_DELAY(10); + tmp = ISP_READ(isp, BIU2400_FLASH_ADDR); + if ((tmp & (1U << 31)) != 0) { + break; + } + } + if (tmp & (1U << 31)) { + *rp = ISP_READ(isp, BIU2400_FLASH_DATA); + ISP_SWIZZLE_NVRAM_LONG(isp, rp); + } else { + *rp = 0xffffffff; + } +} + +void isp_parse_nvram_1020(struct ispsoftc *isp, u_int8_t *nvram_data) { sdparam *sdp = (sdparam *) isp->isp_param; @@ -6167,7 +7701,7 @@ isp_parse_nvram_1020(struct ispsoftc *isp, u_int8_t *nvram_data) } } -static void +void isp_parse_nvram_1080(struct ispsoftc *isp, int bus, u_int8_t *nvram_data) { sdparam *sdp = (sdparam *) isp->isp_param; @@ -6259,7 +7793,7 @@ isp_parse_nvram_1080(struct ispsoftc *isp, int bus, u_int8_t *nvram_data) } } -static void +void isp_parse_nvram_12160(struct ispsoftc *isp, int bus, u_int8_t *nvram_data) { sdparam *sdp = (sdparam *) isp->isp_param; @@ -6350,10 +7884,42 @@ isp_parse_nvram_12160(struct ispsoftc *isp, int bus, u_int8_t *nvram_data) } } -static void +void +isp_fix_nvram_wwns(struct ispsoftc *isp) +{ + fcparam *fcp = FCPARAM(isp); + + /* + * Make sure we have both Node and Port as non-zero values. + */ + if (fcp->isp_wwnn_nvram != 0 && fcp->isp_wwpn_nvram == 0) { + fcp->isp_wwpn_nvram = fcp->isp_wwnn_nvram; + } else if (fcp->isp_wwnn_nvram == 0 && fcp->isp_wwpn_nvram != 0) { + fcp->isp_wwnn_nvram = fcp->isp_wwpn_nvram; + } + + /* + * Make the Node and Port values sane if they're NAA == 2. + * This means to clear bits 48..56 for the Node WWN and + * make sure that there's some non-zero value in 48..56 + * for the Port WWN. + */ + if (fcp->isp_wwnn_nvram && fcp->isp_wwpn_nvram) { + if ((fcp->isp_wwnn_nvram & (((u_int64_t) 0xfff) << 48)) != 0 && + (fcp->isp_wwnn_nvram >> 60) == 2) { + fcp->isp_wwnn_nvram &= ~((u_int64_t) 0xfff << 48); + } + if ((fcp->isp_wwpn_nvram & (((u_int64_t) 0xfff) << 48)) == 0 && + (fcp->isp_wwpn_nvram >> 60) == 2) { + fcp->isp_wwpn_nvram |= ((u_int64_t) 1 << 56); + } + } +} + +void isp_parse_nvram_2100(struct ispsoftc *isp, u_int8_t *nvram_data) { - fcparam *fcp = (fcparam *) isp->isp_param; + fcparam *fcp = FCPARAM(isp); u_int64_t wwn; /* @@ -6362,7 +7928,7 @@ isp_parse_nvram_2100(struct ispsoftc *isp, u_int8_t *nvram_data) * I can find. However, we should account for this being set * at some point in the future. * - * QLogic WWNs have an NAA of 2, but usually nothing shows up in + * Qlogic WWNs have an NAA of 2, but usually nothing shows up in * bits 48..60. In the case of the 2202, it appears that they do * use bit 48 to distinguish between the two instances on the card. * The 2204, which I've never seen, *probably* extends this method. @@ -6375,9 +7941,9 @@ isp_parse_nvram_2100(struct ispsoftc *isp, u_int8_t *nvram_data) wwn |= (((u_int64_t) 2)<< 60); } } - fcp->isp_portwwn = wwn; + fcp->isp_wwpn_nvram = wwn; if (IS_2200(isp) || IS_23XX(isp)) { - wwn = ISP2200_NVRAM_NODE_NAME(nvram_data); + wwn = ISP2100_NVRAM_NODE_NAME(nvram_data); if (wwn) { isp_prt(isp, ISP_LOGCONFIG, "NVRAM Node WWN 0x%08x%08x", (u_int32_t) (wwn >> 32), @@ -6389,70 +7955,113 @@ isp_parse_nvram_2100(struct ispsoftc *isp, u_int8_t *nvram_data) } else { wwn &= ~((u_int64_t) 0xfff << 48); } - fcp->isp_nodewwn = wwn; + fcp->isp_wwnn_nvram = wwn; - /* - * Make sure we have both Node and Port as non-zero values. - */ - if (fcp->isp_nodewwn != 0 && fcp->isp_portwwn == 0) { - fcp->isp_portwwn = fcp->isp_nodewwn; - } else if (fcp->isp_nodewwn == 0 && fcp->isp_portwwn != 0) { - fcp->isp_nodewwn = fcp->isp_portwwn; + isp_fix_nvram_wwns(isp); + + fcp->isp_maxalloc = ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data); + if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0) { + 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); + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) { + fcp->isp_loopid = ISP2100_NVRAM_HARDLOOPID(nvram_data); + } + if ((isp->isp_confopts & ISP_CFG_OWNEXCTHROTTLE) == 0) { + fcp->isp_execthrottle = + ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data); + } + fcp->isp_fwoptions = ISP2100_NVRAM_OPTIONS(nvram_data); + isp_prt(isp, ISP_LOGDEBUG0, + "NVRAM 0x%08x%08x 0x%08x%08x maxalloc %d maxframelen %d", + (u_int32_t) (fcp->isp_wwnn_nvram >> 32), (u_int32_t) fcp->isp_wwnn_nvram, + (u_int32_t) (fcp->isp_wwpn_nvram >> 32), (u_int32_t) fcp->isp_wwpn_nvram, + ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data), + ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data)); + isp_prt(isp, ISP_LOGDEBUG0, + "execthrottle %d fwoptions 0x%x hardloop %d tov %d", + ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data), + ISP2100_NVRAM_OPTIONS(nvram_data), + ISP2100_NVRAM_HARDLOOPID(nvram_data), + ISP2100_NVRAM_TOV(nvram_data)); + fcp->isp_xfwoptions = ISP2100_XFW_OPTIONS(nvram_data); + fcp->isp_zfwoptions = ISP2100_ZFW_OPTIONS(nvram_data); + isp_prt(isp, ISP_LOGDEBUG0, + "xfwoptions 0x%x zfw options 0x%x", + ISP2100_XFW_OPTIONS(nvram_data), ISP2100_ZFW_OPTIONS(nvram_data)); +} - /* - * Make the Node and Port values sane if they're NAA == 2. - * This means to clear bits 48..56 for the Node WWN and - * make sure that there's some non-zero value in 48..56 - * for the Port WWN. - */ - if (fcp->isp_nodewwn && fcp->isp_portwwn) { - if ((fcp->isp_nodewwn & (((u_int64_t) 0xfff) << 48)) != 0 && - (fcp->isp_nodewwn >> 60) == 2) { - fcp->isp_nodewwn &= ~((u_int64_t) 0xfff << 48); +void +isp_parse_nvram_2400(struct ispsoftc *isp, u_int8_t *nvram_data) +{ + fcparam *fcp = FCPARAM(isp); + u_int64_t wwn; + + isp_prt(isp, ISP_LOGDEBUG0, + "NVRAM 0x%08x%08x 0x%08x%08x exchg_cnt %d maxframelen %d", + (u_int32_t) (ISP2400_NVRAM_NODE_NAME(nvram_data) >> 32), + (u_int32_t) (ISP2400_NVRAM_NODE_NAME(nvram_data)), + (u_int32_t) (ISP2400_NVRAM_PORT_NAME(nvram_data) >> 32), + (u_int32_t) (ISP2400_NVRAM_PORT_NAME(nvram_data)), + ISP2400_NVRAM_EXCHANGE_COUNT(nvram_data), + ISP2400_NVRAM_MAXFRAMELENGTH(nvram_data)); + isp_prt(isp, ISP_LOGDEBUG0, + "NVRAM execthr %d loopid %d fwopt1 0x%x fwopt2 0x%x fwopt3 0x%x", + ISP2400_NVRAM_EXECUTION_THROTTLE(nvram_data), + ISP2400_NVRAM_HARDLOOPID(nvram_data), + ISP2400_NVRAM_FIRMWARE_OPTIONS1(nvram_data), + ISP2400_NVRAM_FIRMWARE_OPTIONS2(nvram_data), + ISP2400_NVRAM_FIRMWARE_OPTIONS3(nvram_data)); + + wwn = ISP2400_NVRAM_PORT_NAME(nvram_data); + if (wwn) { + if ((wwn >> 60) != 2 && (wwn >> 60) != 5) { + wwn = 0; } - if ((fcp->isp_portwwn & (((u_int64_t) 0xfff) << 48)) == 0 && - (fcp->isp_portwwn >> 60) == 2) { - fcp->isp_portwwn |= ((u_int64_t) 1 << 56); + } + fcp->isp_wwpn_nvram = wwn; + + wwn = ISP2400_NVRAM_NODE_NAME(nvram_data); + if (wwn) { + if ((wwn >> 60) != 2 && (wwn >> 60) != 5) { + wwn = 0; } } + fcp->isp_wwnn_nvram = wwn; - isp_prt(isp, ISP_LOGDEBUG0, - "NVRAM: maxfrmlen %d execthrottle %d fwoptions 0x%x loopid %x", - ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data), - ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data), - ISP2100_NVRAM_OPTIONS(nvram_data), - ISP2100_NVRAM_HARDLOOPID(nvram_data)); - - fcp->isp_maxalloc = - ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data); - if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0) - 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); - if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) - fcp->isp_loopid = - ISP2100_NVRAM_HARDLOOPID(nvram_data); - if ((isp->isp_confopts & ISP_CFG_OWNEXCTHROTTLE) == 0) + isp_fix_nvram_wwns(isp); + + if (ISP2400_NVRAM_EXCHANGE_COUNT(nvram_data)) { + fcp->isp_maxalloc = ISP2400_NVRAM_EXCHANGE_COUNT(nvram_data); + } + if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0) { + fcp->isp_maxfrmlen = ISP2400_NVRAM_MAXFRAMELENGTH(nvram_data); + } + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) { + fcp->isp_loopid = ISP2400_NVRAM_HARDLOOPID(nvram_data); + } + if ((isp->isp_confopts & ISP_CFG_OWNEXCTHROTTLE) == 0) { fcp->isp_execthrottle = - ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data); - fcp->isp_fwoptions = ISP2100_NVRAM_OPTIONS(nvram_data); + ISP2400_NVRAM_EXECUTION_THROTTLE(nvram_data); + } + fcp->isp_fwoptions = ISP2400_NVRAM_FIRMWARE_OPTIONS1(nvram_data); + fcp->isp_xfwoptions = ISP2400_NVRAM_FIRMWARE_OPTIONS2(nvram_data); + fcp->isp_zfwoptions = ISP2400_NVRAM_FIRMWARE_OPTIONS3(nvram_data); } #ifdef ISP_FW_CRASH_DUMP -static void isp2200_fw_dump(struct ispsoftc *); -static void isp2300_fw_dump(struct ispsoftc *); +void isp2200_fw_dump(struct ispsoftc *); +void isp2300_fw_dump(struct ispsoftc *); -static void +void isp2200_fw_dump(struct ispsoftc *isp) { int i, j; mbreg_t mbs; u_int16_t *ptr; + MEMZERO(&mbs, sizeof (mbs)); ptr = FCPARAM(isp)->isp_dump_data; if (ptr == NULL) { isp_prt(isp, ISP_LOGERR, @@ -6571,8 +8180,7 @@ isp2200_fw_dump(struct ispsoftc *isp) break; } } - ENABLE_INTS(isp); - MEMZERO(&mbs, sizeof (mbs)); + ISP_ENABLE_INTS(isp); mbs.param[0] = MBOX_READ_RAM_WORD; mbs.param[1] = 0x1000; isp->isp_mbxworkp = (void *) ptr; @@ -6591,13 +8199,14 @@ isp2200_fw_dump(struct ispsoftc *isp) (void) isp_async(isp, ISPASYNC_FW_DUMPED, 0); } -static void +void isp2300_fw_dump(struct ispsoftc *isp) { int i, j; mbreg_t mbs; u_int16_t *ptr; + MEMZERO(&mbs, sizeof (mbs)); ptr = FCPARAM(isp)->isp_dump_data; if (ptr == NULL) { isp_prt(isp, ISP_LOGERR, @@ -6714,7 +8323,7 @@ isp2300_fw_dump(struct ispsoftc *isp) isp_prt(isp, ISP_LOGERR, "Board Would Not Reset"); return; } - ENABLE_INTS(isp); + ISP_ENABLE_INTS(isp); MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_READ_RAM_WORD; mbs.param[1] = 0x800; @@ -6729,20 +8338,13 @@ isp2300_fw_dump(struct ispsoftc *isp) } ptr = isp->isp_mbxworkp; /* finish fetch of final word */ *ptr++ = isp->isp_mboxtmp[2]; - - /* - * We don't have access to mailbox registers 8.. onward - * in our 'common' device model- so we have to set it - * here and hope it stays the same! - */ - ISP_WRITE(isp, PCI_MBOX_REGS2300_OFF + (8 << 1), 0x1); - MEMZERO(&mbs, sizeof (mbs)); mbs.param[0] = MBOX_READ_RAM_WORD_EXTENDED; - mbs.param[1] = 0; + mbs.param[8] = 1; isp->isp_mbxworkp = (void *) ptr; isp->isp_mbxwrk0 = 0xffff; /* continuation count */ isp->isp_mbxwrk1 = 0x1; /* next SRAM address */ + isp->isp_mbxwrk8 = 0x1; isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGWARN, @@ -6763,5 +8365,8 @@ isp_fw_dump(struct ispsoftc *isp) isp2200_fw_dump(isp); else if (IS_23XX(isp)) isp2300_fw_dump(isp); + else if (IS_24XX(isp)) + isp_prt(isp, ISP_LOGERR, "24XX dump method undefined"); + } #endif |