/* $NetBSD: isp.c,v 1.135 2021/12/12 13:05:14 andvar Exp $ */
/*
* Machine and OS Independent (well, as best as possible)
* code for the Qlogic ISP SCSI adapters.
*
* Copyright (C) 1997, 1998, 1999 National Aeronautics & Space Administration
* All rights reserved.
*
* Additional Copyright (C) 2000-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.
*/
/*
* Inspiration and ideas about this driver are from Erik Moe's Linux driver
* (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c). Some
* ideas dredged from the Solaris driver.
*/
isp->isp_state = ISP_NILSTATE;
if (isp->isp_dead) {
isp_shutdown(isp);
ISP_DISABLE_INTS(isp);
return;
}
/*
* Basic types (SCSI, FibreChannel and PCI or SBus)
* have been set in the MD code. We figure out more
* here. Possibly more refined types based upon PCI
* identification. Chip revision has been gathered.
*
* After we've fired this chip up, zero out the conf1 register
* for SCSI adapters and do other settings for the 2100.
*/
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 response queues.
*
* We do this now so we can use the request queue
* for dma to load firmware from.
*/
if (ISP_MBOXDMASETUP(isp) != 0) {
isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
return;
}
/*
* Put the board into PAUSE mode (so we can read the SXP registers
* or write FPM/FBM registers).
*/
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) {
case ISP_HA_FC_2100:
btype = "2100";
break;
case ISP_HA_FC_2200:
btype = "2200";
break;
case ISP_HA_FC_2300:
btype = "2300";
break;
case ISP_HA_FC_2312:
btype = "2312";
break;
case ISP_HA_FC_2322:
btype = "2322";
break;
case ISP_HA_FC_2400:
btype = "2422";
break;
case ISP_HA_FC_2500:
btype = "2532";
break;
default:
break;
}
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;
btype = "1240";
isp->isp_clock = 60;
sdp = SDPARAM(isp, 0);
sdp->isp_ultramode = 1;
sdp = SDPARAM(isp, 1);
sdp->isp_ultramode = 1;
/*
* XXX: Should probably do some bus sensing.
*/
} else if (IS_ULTRA3(isp)) {
sdparam *sdp = isp->isp_param;
isp->isp_clock = 100;
if (IS_10160(isp))
btype = "10160";
else if (IS_12160(isp))
btype = "12160";
else
btype = "<UNKLVD>";
sdp->isp_lvdmode = 1;
if (IS_DUALBUS(isp)) {
sdp++;
sdp->isp_lvdmode = 1;
}
} else if (IS_ULTRA2(isp)) {
static const char m[] = "bus %d is in %s Mode";
uint16_t l;
sdparam *sdp = SDPARAM(isp, 0);
isp->isp_clock = 100;
if (IS_1280(isp))
btype = "1280";
else if (IS_1080(isp))
btype = "1080";
else
btype = "<UNKLVD>";
l = ISP_READ(isp, SXP_PINS_DIFF) & ISP1080_MODE_MASK;
switch (l) {
case ISP1080_LVD_MODE:
sdp->isp_lvdmode = 1;
isp_prt(isp, ISP_LOGCONFIG, m, 0, "LVD");
break;
case ISP1080_HVD_MODE:
sdp->isp_diffmode = 1;
isp_prt(isp, ISP_LOGCONFIG, m, 0, "Differential");
break;
case ISP1080_SE_MODE:
sdp->isp_ultramode = 1;
isp_prt(isp, ISP_LOGCONFIG, m, 0, "Single-Ended");
break;
default:
isp_prt(isp, ISP_LOGERR,
"unknown mode on bus %d (0x%x)", 0, l);
break;
}
if (IS_DUALBUS(isp)) {
sdp = SDPARAM(isp, 1);
l = ISP_READ(isp, SXP_PINS_DIFF|SXP_BANK1_SELECT);
l &= ISP1080_MODE_MASK;
switch (l) {
case ISP1080_LVD_MODE:
sdp->isp_lvdmode = 1;
isp_prt(isp, ISP_LOGCONFIG, m, 1, "LVD");
break;
case ISP1080_HVD_MODE:
sdp->isp_diffmode = 1;
isp_prt(isp, ISP_LOGCONFIG,
m, 1, "Differential");
break;
case ISP1080_SE_MODE:
sdp->isp_ultramode = 1;
isp_prt(isp, ISP_LOGCONFIG,
m, 1, "Single-Ended");
break;
default:
isp_prt(isp, ISP_LOGERR,
"unknown mode on bus %d (0x%x)", 1, l);
break;
}
}
} else {
sdparam *sdp = SDPARAM(isp, 0);
i = ISP_READ(isp, BIU_CONF0) & BIU_CONF0_HW_MASK;
switch (i) {
default:
isp_prt(isp, ISP_LOGALL, "Unknown Chip Type 0x%x", i);
/* FALLTHROUGH */
case 1:
btype = "1020";
isp->isp_type = ISP_HA_SCSI_1020;
isp->isp_clock = 40;
break;
case 2:
/*
* Some 1020A chips are Ultra Capable, but don't
* run the clock rate up for that unless told to
* do so by the Ultra Capable bits being set.
*/
btype = "1020A";
isp->isp_type = ISP_HA_SCSI_1020A;
isp->isp_clock = 40;
break;
case 3:
btype = "1040";
isp->isp_type = ISP_HA_SCSI_1040;
isp->isp_clock = 60;
break;
case 4:
btype = "1040A";
isp->isp_type = ISP_HA_SCSI_1040A;
isp->isp_clock = 60;
break;
case 5:
btype = "1040B";
isp->isp_type = ISP_HA_SCSI_1040B;
isp->isp_clock = 60;
break;
case 6:
btype = "1040C";
isp->isp_type = ISP_HA_SCSI_1040C;
isp->isp_clock = 60;
break;
}
/*
* Now, while we're at it, gather info about ultra
* and/or differential mode.
*/
if (ISP_READ(isp, SXP_PINS_DIFF) & SXP_PINS_DIFF_MODE) {
isp_prt(isp, ISP_LOGCONFIG, "Differential Mode");
sdp->isp_diffmode = 1;
} else {
sdp->isp_diffmode = 0;
}
i = ISP_READ(isp, RISC_PSR);
if (isp->isp_bustype == ISP_BT_SBUS) {
i &= RISC_PSR_SBUS_ULTRA;
} else {
i &= RISC_PSR_PCI_ULTRA;
}
if (i != 0) {
isp_prt(isp, ISP_LOGCONFIG, "Ultra Mode Capable");
sdp->isp_ultramode = 1;
/*
* If we're in Ultra Mode, we have to be 60MHz clock-
* even for the SBus version.
*/
isp->isp_clock = 60;
} else {
sdp->isp_ultramode = 0;
/*
* Clock is known. Gronk.
*/
}
/*
* Do MD specific post initialization
*/
ISP_RESET1(isp);
/*
* Wait for everything to finish firing up.
*
* Avoid doing this on early 2312s because you can generate a PCI
* parity error (chip breakage).
*/
if (IS_2312(isp) && isp->isp_revision < 2) {
ISP_DELAY(100);
} else {
loops = MBOX_DELAY_COUNT;
while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) {
ISP_DELAY(100);
if (--loops < 0) {
ISP_RESET0(isp);
isp_prt(isp, ISP_LOGERR,
"MBOX_BUSY never cleared on reset");
return;
}
}
}
/*
* Up until this point we've done everything by just reading or
* setting registers. From this point on we rely on at least *some*
* kind of firmware running in the card.
*/
/*
* Do some sanity checking by running a NOP command.
* If it succeeds, the ROM firmware is now running.
*/
ISP_MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_NO_OP;
mbs.logval = MBLOGALL;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "NOP command failed (%x)", mbs.param[0]);
ISP_RESET0(isp);
return;
}
/*
* Download new Firmware, unless requested not to do so.
* This is made slightly trickier in some cases where the
* firmware of the ROM revision is newer than the revision
* compiled into the driver. So, where we used to compare
* versions of our f/w and the ROM f/w, now we just see
* whether we have f/w at all and whether a config flag
* has disabled our download.
*/
if ((isp->isp_mdvec->dv_ispfw == NULL) || (isp->isp_confopts & ISP_CFG_NORELOAD)) {
dodnld = 0;
}
/*
* 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 specified at offset 0x9..0xB.
*/
/*
* Give it a chance to finish starting up.
* Give the 24XX more time.
*/
if (IS_24XX(isp)) {
ISP_DELAY(500000);
/*
* Check to see if the 24XX firmware really started.
*/
if (mbs.param[1] == 0xdead) {
isp_prt(isp, ISP_LOGERR, "f/w didn't *really* start");
ISP_RESET0(isp);
return;
}
} else {
ISP_DELAY(250000);
if (IS_SCSI(isp)) {
/*
* Set CLOCK RATE, but only if asked to.
*/
if (isp->isp_clock) {
mbs.param[0] = MBOX_SET_CLOCK_RATE;
mbs.param[1] = isp->isp_clock;
mbs.logval = MBLOGNONE;
isp_mboxcmd(isp, &mbs);
/* we will try not to care if this fails */
}
}
}
/*
* Ask the chip for the current firmware version.
* This should prove that the new firmware is working.
*/
MBSINIT(&mbs, MBOX_ABOUT_FIRMWARE, MBLOGALL, 0);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
ISP_RESET0(isp);
return;
}
/*
* The SBus firmware that we are using apparently does not return
* major, minor, micro revisions in the mailbox registers, which
* is really, really, annoying.
*/
if (ISP_SBUS_SUPPORTED && isp->isp_bustype == ISP_BT_SBUS) {
if (dodnld) {
#ifdef ISP_TARGET_MODE
isp->isp_fwrev[0] = 7;
isp->isp_fwrev[1] = 55;
#else
isp->isp_fwrev[0] = 1;
isp->isp_fwrev[1] = 37;
#endif
isp->isp_fwrev[2] = 0;
}
} else {
isp->isp_fwrev[0] = mbs.param[1];
isp->isp_fwrev[1] = mbs.param[2];
isp->isp_fwrev[2] = mbs.param[3];
}
if (IS_FC(isp)) {
/*
* We do not believe firmware attributes for 2100 code less
* than 1.17.0, unless it's the firmware we specifically
* are loading.
*
* Note that all 22XX and later f/w is greater than 1.X.0.
*/
if ((ISP_FW_OLDER_THAN(isp, 1, 17, 1))) {
#ifdef USE_SMALLER_2100_FIRMWARE
isp->isp_fwattr = ISP_FW_ATTR_SCCLUN;
#else
isp->isp_fwattr = 0;
#endif
} else {
isp->isp_fwattr = mbs.param[6];
isp_prt(isp, ISP_LOGDEBUG0, "Firmware Attributes = 0x%x", mbs.param[6]);
}
} else {
#ifndef ISP_TARGET_MODE
isp->isp_fwattr = ISP_FW_ATTR_TMODE;
#else
isp->isp_fwattr = 0;
#endif
}
if (!IS_24XX(isp)) {
MBSINIT(&mbs, MBOX_GET_FIRMWARE_STATUS, MBLOGALL, 0);
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_prt(isp, ISP_LOGCONFIG, "%d max I/O command limit set", isp->isp_maxcmds);
/*
* If we don't have Multi-ID f/w loaded, we need to restrict channels to one.
* Only make this check for non-SCSI cards (I'm not sure firmware attributes
* work for them).
*/
if (IS_FC(isp) && ISP_CAP_MULTI_ID(isp) == 0 && isp->isp_nchan > 1) {
isp_prt(isp, ISP_LOGWARN, "non-MULTIID f/w loaded, only can enable 1 of %d channels", isp->isp_nchan);
isp->isp_nchan = 1;
}
for (i = 0; i < isp->isp_nchan; i++) {
isp_fw_state(isp, i);
}
if (isp->isp_dead) {
isp_shutdown(isp);
ISP_DISABLE_INTS(isp);
return;
}
isp->isp_state = ISP_RESETSTATE;
/*
* Okay- now that we have new firmware running, we now (re)set our
* notion of how many luns we support. This is somewhat tricky because
* if we haven't loaded firmware, we sometimes do not have an easy way
* of knowing how many luns we support.
*
* Expanded lun firmware gives you 32 luns for SCSI cards and
* 16384 luns for Fibre Channel cards.
*
* It turns out that even for QLogic 2100s with ROM 1.10 and above
* we do get a firmware attributes word returned in mailbox register 6.
*
* Because the lun is in a different position in the Request Queue
* Entry structure for Fibre Channel with expanded lun firmware, we
* can only support one lun (lun zero) when we don't know what kind
* of firmware we're running.
*/
if (IS_SCSI(isp)) {
if (dodnld) {
if (IS_ULTRA2(isp) || IS_ULTRA3(isp)) {
isp->isp_maxluns = 32;
} else {
isp->isp_maxluns = 8;
}
} else {
isp->isp_maxluns = 8;
}
} else {
if (ISP_CAP_SCCFW(isp)) {
isp->isp_maxluns = 16384;
} else {
isp->isp_maxluns = 16;
}
}
/*
* We get some default values established. As a side
* effect, NVRAM is read here (unless overridden by
* a configuration flag).
*/
if (do_load_defaults) {
if (IS_SCSI(isp)) {
isp_setdfltsdparm(isp);
} else {
for (i = 0; i < isp->isp_nchan; i++) {
isp_setdfltfcparm(isp, i);
}
}
}
}
/*
* Initialize Parameters of Hardware to a known state.
*
* Locks are held before coming here.
*/
/*
* If we have fast memory timing enabled, turn it on.
*/
if (sdp_chan0->isp_fast_mttr) {
ISP_WRITE(isp, RISC_MTR, 0x1313);
}
/*
* Set Retry Delay and Count.
* You set both channels at the same time.
*/
MBSINIT(&mbs, MBOX_SET_RETRY_COUNT, MBLOGALL, 0);
mbs.param[1] = sdp_chan0->isp_retry_count;
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);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
/*
* Set ASYNC DATA SETUP time. This is very important.
*/
MBSINIT(&mbs, MBOX_SET_ASYNC_DATA_SETUP_TIME, MBLOGALL, 0);
mbs.param[1] = sdp_chan0->isp_async_data_setup;
mbs.param[2] = sdp_chan1->isp_async_data_setup;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
/*
* Set ACTIVE Negation State.
*/
MBSINIT(&mbs, MBOX_SET_ACT_NEG_STATE, MBLOGNONE, 0);
mbs.param[1] =
(sdp_chan0->isp_req_ack_active_neg << 4) |
(sdp_chan0->isp_data_line_active_neg << 5);
mbs.param[2] =
(sdp_chan1->isp_req_ack_active_neg << 4) |
(sdp_chan1->isp_data_line_active_neg << 5);
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)",
sdp_chan0->isp_req_ack_active_neg,
sdp_chan0->isp_data_line_active_neg,
sdp_chan1->isp_req_ack_active_neg,
sdp_chan1->isp_data_line_active_neg);
/*
* But don't return.
*/
}
/*
* Set the Tag Aging limit
*/
MBSINIT(&mbs, MBOX_SET_TAG_AGE_LIMIT, MBLOGALL, 0);
mbs.param[1] = sdp_chan0->isp_tag_aging;
mbs.param[2] = sdp_chan1->isp_tag_aging;
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);
return;
}
/*
* Turn on LVD transitions for ULTRA2 or better and other features
*
* Now that we have 32 bit handles, don't do any fast posting
* any more. For Ultra2/Ultra3 cards, we can turn on 32 bit RIO
* operation or use fast posting. To be conservative, we'll only
* do this for Ultra3 cards now because the other cards are so
* rare for this author to find and test with.
*/
MBSINIT(&mbs, MBOX_SET_FW_FEATURES, MBLOGALL, 0);
if (IS_ULTRA2(isp))
mbs.param[1] |= FW_FEATURE_LVD_NOTIFY;
#ifdef ISP_NO_RIO
if (IS_ULTRA3(isp))
mbs.param[1] |= FW_FEATURE_FAST_POST;
#else
if (IS_ULTRA3(isp))
mbs.param[1] |= FW_FEATURE_RIO_32BIT;
#endif
if (mbs.param[1] != 0) {
uint16_t sfeat = mbs.param[1];
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGINFO,
"Enabled FW features (0x%x)", sfeat);
}
}
isp->isp_state = ISP_INITSTATE;
}
static void
isp_scsi_channel_init(ispsoftc_t *isp, int chan)
{
sdparam *sdp;
mbreg_t mbs;
int tgt;
sdp = SDPARAM(isp, chan);
/*
* Set (possibly new) Initiator ID.
*/
MBSINIT(&mbs, MBOX_SET_INIT_SCSI_ID, MBLOGALL, 0);
mbs.param[1] = (chan << 7) | sdp->isp_initiator_id;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
isp_prt(isp, ISP_LOGINFO, "Chan %d Initiator ID is %d",
chan, sdp->isp_initiator_id);
/*
* Set current per-target parameters to an initial safe minimum.
*/
for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
int lun;
uint16_t sdf;
if (sdp->isp_devparam[tgt].dev_enable == 0) {
continue;
}
#ifndef ISP_TARGET_MODE
sdf = sdp->isp_devparam[tgt].goal_flags;
sdf &= DPARM_SAFE_DFLT;
/*
* It is not quite clear when this changed over so that
* we could force narrow and async for 1000/1020 cards,
* but assume that this is only the case for loaded
* firmware.
*/
if (isp->isp_loaded_fw) {
sdf |= DPARM_NARROW | DPARM_ASYNC;
}
#else
/*
* The !$*!)$!$)* f/w uses the same index into some
* internal table to decide how to respond to negotiations,
* so if we've said "let's be safe" for ID X, and ID X
* selects *us*, the negotiations will back to 'safe'
* (as in narrow/async). What the f/w *should* do is
* use the initiator id settings to decide how to respond.
*/
sdp->isp_devparam[tgt].goal_flags = sdf = DPARM_DEFAULT;
#endif
MBSINIT(&mbs, MBOX_SET_TARGET_PARAMS, MBLOGNONE, 0);
mbs.param[1] = (chan << 15) | (tgt << 8);
mbs.param[2] = sdf;
if ((sdf & DPARM_SYNC) == 0) {
mbs.param[3] = 0;
} else {
mbs.param[3] =
(sdp->isp_devparam[tgt].goal_offset << 8) |
(sdp->isp_devparam[tgt].goal_period);
}
isp_prt(isp, ISP_LOGDEBUG0, "Initial Settings bus%d tgt%d flags 0x%x off 0x%x per 0x%x",
chan, tgt, mbs.param[2], mbs.param[3] >> 8, mbs.param[3] & 0xff);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
sdf = DPARM_SAFE_DFLT;
MBSINIT(&mbs, MBOX_SET_TARGET_PARAMS, MBLOGALL, 0);
mbs.param[1] = (tgt << 8) | (chan << 15);
mbs.param[2] = sdf;
mbs.param[3] = 0;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
continue;
}
}
/*
* We don't update any information directly from the f/w
* because we need to run at least one command to cause a
* new state to be latched up. So, we just assume that we
* converge to the values we just had set.
*
* Ensure that we don't believe tagged queuing is enabled yet.
* It turns out that sometimes the ISP just ignores our
* attempts to set parameters for devices that it hasn't
* seen yet.
*/
sdp->isp_devparam[tgt].actv_flags = sdf & ~DPARM_TQING;
for (lun = 0; lun < (int) isp->isp_maxluns; lun++) {
MBSINIT(&mbs, MBOX_SET_DEV_QUEUE_PARAMS, MBLOGALL, 0);
mbs.param[1] = (chan << 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);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
break;
}
}
}
for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
if (sdp->isp_devparam[tgt].dev_refresh) {
sdp->sendmarker = 1;
sdp->update = 1;
break;
}
}
}
/*
* We only support one channel on non-24XX cards
*/
fcp = FCPARAM(isp, 0);
if (fcp->role == ISP_ROLE_NONE) {
isp->isp_state = ISP_INITSTATE;
return;
}
/*
* Firmware Options are either retrieved from NVRAM or
* are patched elsewhere. We check them for sanity here
* and make changes based on board revision, but otherwise
* let others decide policy.
*/
/*
* If this is a 2100 < revision 5, we have to turn off FAIRNESS.
*/
if (IS_2100(isp) && isp->isp_revision < 5) {
icbp->icb_fwoptions &= ~ICBOPT_FAIRNESS;
}
/*
* We have to use FULL LOGIN even though it resets the loop too much
* because otherwise port database entries don't get updated after
* 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)) {
icbp->icb_fwoptions |= ICBOPT_FULL_LOGIN;
}
/*
* Insist on Port Database Update Async notifications
*/
icbp->icb_fwoptions |= ICBOPT_PDBCHANGE_AE;
/*
* Make sure that target role reflects into fwoptions.
*/
if (fcp->role & ISP_ROLE_TARGET) {
icbp->icb_fwoptions |= ICBOPT_TGT_ENABLE;
} else {
icbp->icb_fwoptions &= ~ICBOPT_TGT_ENABLE;
}
icbp->icb_maxfrmlen = DEFAULT_FRAMESIZE(isp);
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", DEFAULT_FRAMESIZE(isp), ICB_DFLT_FRMLEN);
icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN;
}
icbp->icb_maxalloc = fcp->isp_maxalloc;
if (icbp->icb_maxalloc < 1) {
isp_prt(isp, ISP_LOGERR, "bad maximum allocation (%d)- using 16", fcp->isp_maxalloc);
icbp->icb_maxalloc = 16;
}
icbp->icb_execthrottle = DEFAULT_EXEC_THROTTLE(isp);
if (icbp->icb_execthrottle < 1) {
isp_prt(isp, ISP_LOGERR, "bad execution throttle of %d- using %d", DEFAULT_EXEC_THROTTLE(isp), ICB_DFLT_THROTTLE);
icbp->icb_execthrottle = ICB_DFLT_THROTTLE;
}
icbp->icb_retry_delay = fcp->isp_retry_delay;
icbp->icb_retry_count = fcp->isp_retry_count;
icbp->icb_hardaddr = fcp->isp_loopid;
ownloopid = (isp->isp_confopts & ISP_CFG_OWNLOOPID) != 0;
if (icbp->icb_hardaddr >= LOCAL_LOOP_LIM) {
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.
*
* NB: for the 2300, ICBOPT_EXTENDED is required.
*/
if (IS_2100(isp)) {
/*
* We can't have Fast Posting any more- we now
* have 32 bit handles.
*/
icbp->icb_fwoptions &= ~ICBOPT_FAST_POST;
} else if (IS_2200(isp) || IS_23XX(isp)) {
icbp->icb_fwoptions |= ICBOPT_EXTENDED;
/*
* Prefer or force Point-To-Point instead Loop?
*/
switch (isp->isp_confopts & ISP_CFG_PORT_PREF) {
case ISP_CFG_NPORT:
icbp->icb_xfwoptions |= ICBXOPT_PTP_2_LOOP;
break;
case ISP_CFG_NPORT_ONLY:
icbp->icb_xfwoptions |= ICBXOPT_PTP_ONLY;
break;
case ISP_CFG_LPORT_ONLY:
icbp->icb_xfwoptions |= ICBXOPT_LOOP_ONLY;
break;
default:
icbp->icb_xfwoptions |= ICBXOPT_LOOP_2_PTP;
break;
}
if (IS_2200(isp)) {
/*
* We can't have Fast Posting any more- we now
* have 32 bit handles.
*
* RIO seemed to have too much breakage.
*
* Just opt for safety.
*/
icbp->icb_xfwoptions &= ~ICBXOPT_RIO_16BIT;
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).
*/
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) {
icbp->icb_zfwoptions |= ICBZOPT_RATE_TWOGB;
} else {
icbp->icb_zfwoptions |= ICBZOPT_RATE_AUTO;
}
if (fcp->isp_zfwoptions & ICBZOPT_50_OHM) {
icbp->icb_zfwoptions |= ICBZOPT_50_OHM;
}
}
}
/*
* For 22XX > 2.1.26 && 23XX, set some options.
*/
if (ISP_FW_NEWER_THAN(isp, 2, 26, 0)) {
MBSINIT(&mbs, MBOX_SET_FIRMWARE_OPTIONS, MBLOGALL, 0);
mbs.param[1] = IFCOPT1_DISF7SWTCH|IFCOPT1_LIPASYNC|IFCOPT1_LIPF8;
mbs.param[2] = 0;
mbs.param[3] = 0;
if (ISP_FW_NEWER_THAN(isp, 3, 16, 0)) {
mbs.param[1] |= IFCOPT1_EQFQASYNC|IFCOPT1_CTIO_RETRY;
if (fcp->role & ISP_ROLE_TARGET) {
mbs.param[3] = IFCOPT3_NOPRLI;
}
}
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;
/*
* Check to see whether all channels have *some* kind of role
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role != ISP_ROLE_NONE) {
break;
}
}
if (chan == isp->isp_nchan) {
isp_prt(isp, ISP_LOGDEBUG0, "all %d channels with role 'none'", chan);
isp->isp_state = ISP_INITSTATE;
return;
}
/*
* Now fill in information about any additional channels
*/
if (isp->isp_nchan > 1) {
isp_icb_2400_vpinfo_t vpinfo, *vdst;
vp_port_info_t pi, *pdst;
size_t amt = 0;
uint8_t *off;
vpinfo.vp_count = isp->isp_nchan - 1;
vpinfo.vp_global_options = 0;
off = fcp->isp_scratch;
off += ICB2400_VPINFO_OFF;
vdst = (isp_icb_2400_vpinfo_t *) off;
isp_put_icb_2400_vpinfo(isp, &vpinfo, vdst);
amt = ICB2400_VPINFO_OFF + sizeof (isp_icb_2400_vpinfo_t);
for (chan = 1; chan < isp->isp_nchan; chan++) {
fcparam *fcp2;
ISP_MEMZERO(&pi, sizeof (pi));
fcp2 = FCPARAM(isp, chan);
if (fcp2->role != ISP_ROLE_NONE) {
pi.vp_port_options = ICB2400_VPOPT_ENABLED;
if (fcp2->role & ISP_ROLE_INITIATOR) {
pi.vp_port_options |= ICB2400_VPOPT_INI_ENABLE;
}
if ((fcp2->role & ISP_ROLE_TARGET) == 0) {
pi.vp_port_options |= ICB2400_VPOPT_TGT_DISABLE;
}
MAKE_NODE_NAME_FROM_WWN(pi.vp_port_portname, fcp2->isp_wwpn);
MAKE_NODE_NAME_FROM_WWN(pi.vp_port_nodename, fcp2->isp_wwnn);
}
off = fcp->isp_scratch;
off += ICB2400_VPINFO_PORT_OFF(chan);
pdst = (vp_port_info_t *) off;
isp_put_vp_port_info(isp, &pi, pdst);
amt += ICB2400_VPOPT_WRITE_SIZE;
}
}
/*
* Whatever happens, we're now committed to being here.
*/
isp->isp_state = ISP_INITSTATE;
}
static void
isp_mark_portdb(ispsoftc_t *isp, int chan, int disposition)
{
fcparam *fcp = FCPARAM(isp, chan);
int i;
if (chan < 0 || chan >= isp->isp_nchan) {
isp_prt(isp, ISP_LOGWARN, "isp_mark_portdb: bad channel %d", chan);
return;
}
for (i = 0; i < MAX_FC_TARG; i++) {
if (fcp->portdb[i].target_mode) {
if (disposition < 0) {
isp_prt(isp, ISP_LOGTINFO, "isp_mark_portdb: Chan %d zeroing handle 0x" "%04x port 0x%06x", chan,
fcp->portdb[i].handle, fcp->portdb[i].portid);
ISP_MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
}
continue;
}
if (disposition == 0) {
ISP_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:
ISP_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.
*/
static int
isp_plogx(ispsoftc_t *isp, int chan, uint16_t handle, uint32_t portid, int flags, int gs)
{
mbreg_t mbs;
uint8_t q[QENTRY_LEN];
isp_plogx_t *plp;
fcparam *fcp;
uint8_t *scp;
uint32_t sst, parm1;
int rval;
const char *msg;
char buf[64];
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);
}
}
switch (mbs.param[0]) {
case MBOX_PORT_ID_USED:
isp_prt(isp, ISP_LOGDEBUG0,
"isp_port_login: 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_port_login: handle 0x%04x 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_port_login: 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_port_login: all IDs used for fabric login");
return (MBOX_ALL_IDS_USED);
default:
isp_prt(isp, ISP_LOGINFO,
"isp_port_login: error 0x%x on port login of 0x%06x@0x%0x",
mbs.param[0], portid, handle);
return (mbs.param[0]);
}
}
static int
isp_fclink_test(ispsoftc_t *isp, int chan, int usdelay)
{
mbreg_t mbs;
int count, check_for_fabric, r;
uint8_t lwfs;
int loopid;
fcparam *fcp;
fcportdb_t *lp;
isp_pdb_t pdb;
fcp = FCPARAM(isp, chan);
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d FC Link Test Entry", chan);
ISP_MARK_PORTDB(isp, chan, 1);
/*
* Wait up to N microseconds for F/W to go to a ready state.
*/
lwfs = FW_CONFIG_WAIT;
count = 0;
while (count < usdelay) {
uint64_t enano;
uint32_t wrk;
NANOTIME_T hra, hrb;
GET_NANOTIME(&hra);
isp_fw_state(isp, chan);
if (lwfs != fcp->isp_fwstate) {
isp_prt(isp, ISP_LOGCONFIG|ISP_LOGSANCFG, "Chan %d Firmware State <%s->%s>", chan, isp_fc_fw_statename((int)lwfs), isp_fc_fw_statename((int)fcp->isp_fwstate));
lwfs = fcp->isp_fwstate;
}
if (fcp->isp_fwstate == FW_READY) {
break;
}
GET_NANOTIME(&hrb);
/*
* Get the elapsed time in nanoseconds.
* Always guaranteed to be non-zero.
*/
enano = NANOTIME_SUB(&hrb, &hra);
/*
* If the elapsed time is less than 1 millisecond,
* delay a period of time up to that millisecond of
* waiting.
*
* This peculiar code is an attempt to try and avoid
* invoking uint64_t math support functions for some
* platforms where linkage is a problem.
*/
if (enano < (1000 * 1000)) {
count += 1000;
enano = (1000 * 1000) - enano;
while (enano > (uint64_t) 4000000000U) {
ISP_SLEEP(isp, 4000000);
enano -= (uint64_t) 4000000000U;
}
wrk = enano;
wrk /= 1000;
ISP_SLEEP(isp, wrk);
} else {
while (enano > (uint64_t) 4000000000U) {
count += 4000000;
enano -= (uint64_t) 4000000000U;
}
wrk = enano;
count += (wrk / 1000);
}
}
/*
* If we haven't gone to 'ready' state, return.
*/
if (fcp->isp_fwstate != FW_READY) {
isp_prt(isp, ISP_LOGSANCFG, "%s: chan %d not at FW_READY state", __func__, chan);
return (-1);
}
/*
* Get our Loop ID and Port ID.
*/
MBSINIT(&mbs, MBOX_GET_LOOP_ID, MBLOGALL, 0);
if (ISP_CAP_MULTI_ID(isp)) {
mbs.param[9] = chan;
mbs.ibits = (1 << 9);
mbs.obits = (1 << 7);
}
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return (-1);
}
if (IS_2100(isp)) {
/*
* Don't bother with fabric if we are using really old
* 2100 firmware. It's just not worth it.
*/
if (ISP_FW_NEWER_THAN(isp, 1, 15, 37)) {
check_for_fabric = 1;
} else {
check_for_fabric = 0;
}
} else if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_F_PORT) {
check_for_fabric = 1;
} else {
check_for_fabric = 0;
}
/*
* Check to make sure we got a valid loopid
* The 24XX seems to mess this up for multiple channels.
*/
if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT) {
uint8_t alpa = fcp->isp_portid;
if (alpa == 0) {
/* "Cannot Happen" */
isp_prt(isp, ISP_LOGWARN, "Zero AL_PA for Loop Topology?");
} else {
int i;
for (i = 0; alpa_map[i]; i++) {
if (alpa_map[i] == alpa) {
break;
}
}
if (alpa_map[i] && fcp->isp_loopid != i) {
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d deriving loopid %d from AL_PA map (AL_PA 0x%x) and ignoring returned value %d (AL_PA 0x%x)", chan, i, alpa_map[i], fcp->isp_loopid, alpa);
fcp->isp_loopid = i;
}
}
}
if (IS_24XX(isp)) { /* XXX SHOULDN'T THIS BE FOR 2K F/W? XXX */
loopid = NPH_FL_ID;
} else {
loopid = FL_ID;
}
if (check_for_fabric) {
r = isp_getpdb(isp, chan, loopid, &pdb, 1);
if (r && (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT)) {
isp_prt(isp, ISP_LOGWARN, "fabric topology but cannot get info about fabric controller (0x%x)", r);
fcp->isp_topo = TOPO_PTP_STUB;
}
} else {
r = -1;
}
if (r == 0) {
if (IS_2100(isp)) {
fcp->isp_topo = TOPO_FL_PORT;
}
if (pdb.portid == 0) {
/*
* Crock.
*/
fcp->isp_topo = TOPO_NL_PORT;
goto not_on_fabric;
}
/*
* 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 to make sure that they point to the correct
* entities.
*/
static int
isp_pdb_sync(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
fcportdb_t *lp;
uint16_t dbidx;
if (fcp->isp_loopstate == LOOP_READY) {
return (0);
}
/*
* Make sure we're okay for doing this right now.
*/
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);
}
switch (lp->state) {
case FC_PORTDB_STATE_PROBATIONAL:
case FC_PORTDB_STATE_DEAD:
/*
* It's up to the outer layers to clear isp_dev_map.
*/
lp->state = FC_PORTDB_STATE_NIL;
isp_async(isp, ISPASYNC_DEV_GONE, chan, lp);
if (lp->autologin == 0) {
(void) isp_plogx(isp, chan, lp->handle,
lp->portid,
PLOGX_FLG_CMD_LOGO |
PLOGX_FLG_IMPLICIT |
PLOGX_FLG_FREE_NPHDL, 0);
} else {
lp->autologin = 0;
}
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_dev_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, chan, 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, chan, 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->dev_map_idx) {
int t = lp->dev_map_idx - 1;
fcp->isp_dev_map[t] = dbidx + 1;
}
lp->state = FC_PORTDB_STATE_VALID;
isp_async(isp, ISPASYNC_DEV_STAYED, chan, lp);
if (dbidx != FL_ID) {
lp->new_roles = 0;
lp->new_portid = 0;
}
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, chan);
}
}
/*
* 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
* in isp_start.
*/
fcp->loop_seen_once = 1;
fcp->isp_loopstate = LOOP_READY;
return (0);
}
/*
* Scan local loop for devices.
*/
static int
isp_scan_loop(ispsoftc_t *isp, int chan)
{
fcportdb_t *lp, tmp;
fcparam *fcp = FCPARAM(isp, chan);
int i;
isp_pdb_t pdb;
uint16_t handle, lim = 0;
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) performed a PLOGI for us. We skip this
* step if we're a ISP_24XX in NP-IV mode.
*
* If we're a N-port connection, we treat this is a short loop (0..1).
*/
switch (fcp->isp_topo) {
case TOPO_NL_PORT:
lim = LOCAL_LOOP_LIM;
break;
case TOPO_FL_PORT:
if (IS_24XX(isp) && isp->isp_nchan > 1) {
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d Skipping Local Loop Scan", chan);
fcp->isp_loopstate = LOOP_LSCAN_DONE;
return (0);
}
lim = LOCAL_LOOP_LIM;
break;
case TOPO_N_PORT:
lim = 2;
break;
default:
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d no loop topology to scan", chan);
fcp->isp_loopstate = LOOP_LSCAN_DONE;
return (0);
}
fcp->isp_loopstate = LOOP_SCANNING_LOOP;
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d FC scan loop 0..%d", chan, lim-1);
/*
* Run through the list and get the port database info for each one.
*/
for (handle = 0; handle < lim; handle++) {
int r;
/*
* Don't scan "special" ids.
*/
if (handle >= FL_ID && handle <= SNS_ID) {
continue;
}
if (ISP_CAP_2KLOGIN(isp)) {
if (handle >= NPH_RESERVED && handle <= NPH_FL_ID) {
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)) {
uint64_t node_wwn = isp_get_wwn(isp, chan, handle, 1);
if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d FC scan loop DONE (bad)", chan);
return (-1);
}
if (node_wwn == INI_NONE) {
continue;
}
}
/*
* Get the port database entity for this index.
*/
r = isp_getpdb(isp, chan, handle, &pdb, 1);
if (r != 0) {
isp_prt(isp, ISP_LOGDEBUG1,
"Chan %d FC scan loop handle %d returned %x",
chan, handle, r);
if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
ISP_MARK_PORTDB(isp, chan, 1);
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d FC scan loop DONE (bad)", chan);
return (-1);
}
continue;
}
/*
* 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 (IS_2100(isp) && pdb.handle != handle) {
isp_prt(isp, ISP_LOGWARN,
"Chan %d cannot synchronize port database", chan);
ISP_MARK_PORTDB(isp, chan, 1);
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d FC scan loop DONE (bad)", chan);
return (-1);
}
/*
* Save the pertinent info locally.
*/
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;
/*
* 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 (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);
if (a == 0 && b == 0) {
tmp.node_wwn =
isp_get_wwn(isp, chan, handle, 1);
tmp.port_wwn =
isp_get_wwn(isp, chan, handle, 0);
if (tmp.node_wwn && tmp.port_wwn) {
isp_prt(isp, ISP_LOGINFO, "DODGED!");
goto cont;
}
}
isp_prt(isp, ISP_LOGWARN,
"Chan %d bad pdb (%1d%1d%1d) @ handle 0x%x", chan,
a, b, c, handle);
isp_dump_portdb(isp, chan);
continue;
}
cont:
/*
* Now search the entire port database
* for the same Port and Node WWN.
*/
for (i = 0; i < MAX_FC_TARG; i++) {
lp = &fcp->portdb[i];
if (lp->state == FC_PORTDB_STATE_NIL ||
lp->target_mode) {
continue;
}
if (lp->node_wwn != tmp.node_wwn) {
continue;
}
if (lp->port_wwn != tmp.port_wwn) {
continue;
}
/*
* Okay- we've found a non-nil entry that matches.
* Check to make sure it's probational or a zombie.
*/
if (lp->state != FC_PORTDB_STATE_PROBATIONAL &&
lp->state != FC_PORTDB_STATE_ZOMBIE) {
isp_prt(isp, ISP_LOGERR,
"Chan %d [%d] not probational/zombie (0x%x)",
chan, i, lp->state);
isp_dump_portdb(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d FC scan loop DONE (bad)", chan);
return (-1);
}
/*
* Mark the device as something the f/w logs into
* automatically.
*/
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|ISP_LOGDEBUG0,
"Chan %d Loop Port 0x%06x@0x%04x Pending "
"Valid", chan, 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|ISP_LOGDEBUG0,
"Chan %d Loop Port 0x%06x@0x%04x changed",
chan, tmp.portid, tmp.handle);
lp->state = FC_PORTDB_STATE_CHANGED;
lp->new_portid = tmp.portid;
lp->new_roles = tmp.roles;
break;
}
/*
* Did we find and update an old entry?
*/
if (i < MAX_FC_TARG) {
continue;
}
/*
* Ah. A new device entry. Find an empty slot
* for it and save info for later disposition.
*/
for (i = 0; i < MAX_FC_TARG; i++) {
if (fcp->portdb[i].target_mode) {
continue;
}
if (fcp->portdb[i].state == FC_PORTDB_STATE_NIL) {
break;
}
}
if (i == MAX_FC_TARG) {
isp_prt(isp, ISP_LOGERR,
"Chan %d out of portdb entries", chan);
continue;
}
lp = &fcp->portdb[i];
/*
* 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 ((ISP_FC_SCRLEN >> 1) - 16 - SNS_GID_FT_REQ_SIZE)
#define NGENT ((GIDLEN - 16) >> 2)
/*
* 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;
ISP_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;
/*
* 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 ISP_FC_SCRLEN");
}
portlim = portidx + 1;
isp_prt(isp, ISP_LOGSANCFG,
"Chan %d got %d ports back from name server", chan, portlim);
for (portidx = 0; portidx < portlim; portidx++) {
int npidx;
/*
* 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
* maybe still this one. We get some information to find out.
*
* 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.
*/
for (portidx = 0; portidx < portlim; portidx++) {
fcportdb_t *lp;
uint64_t wwnn, wwpn;
int dbidx, nr;
if (portid == 0) {
isp_prt(isp, ISP_LOGSANCFG,
"Chan %d skipping null PortID at idx %d",
chan, portidx);
continue;
}
/*
* Skip ourselves here and on other channels. If we're
* multi-id, we can't check the portids in other FCPARAM
* arenas because the resolutions here aren't synchronized.
* The best way to do this is to exclude looking at portids
* that have the same domain and area code as our own
* portid.
*/
if (ISP_CAP_MULTI_ID(isp)) {
if ((portid >> 8) == (fcp->isp_portid >> 8)) {
isp_prt(isp, ISP_LOGSANCFG,
"Chan %d skip PortID 0x%06x",
chan, portid);
continue;
}
} else if (portid == fcp->isp_portid) {
isp_prt(isp, ISP_LOGSANCFG,
"Chan %d skip ourselves on @ PortID 0x%06x",
chan, portid);
continue;
}
isp_prt(isp, ISP_LOGSANCFG,
"Chan %d Checking Fabric Port 0x%06x", chan, 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 ||
lp->target_mode) {
continue;
}
if (lp->portid == portid) {
break;
}
}
/*
* 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, chan, lp->handle, &pdb, 0);
if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
return (-1);
}
if (r != 0) {
lp->new_portid = portid;
lp->state = FC_PORTDB_STATE_DEAD;
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d Fabric Port 0x%06x is dead",
chan, 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, chan, dbidx, pdb.handle, pdb.portid,
(uint32_t) (wwnn >> 32), (uint32_t) wwnn,
(uint32_t) (wwpn >> 32), (uint32_t) wwpn,
lp->handle, portid,
(uint32_t) (lp->node_wwn >> 32),
(uint32_t) lp->node_wwn,
(uint32_t) (lp->port_wwn >> 32),
(uint32_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, chan, portid, &pdb,
&oldhandle)) {
lp->new_portid = portid;
lp->state = FC_PORTDB_STATE_DEAD;
if (fcp->isp_loopstate !=
LOOP_SCANNING_FABRIC) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
return (-1);
}
continue;
}
if (fcp->isp_loopstate !=
LOOP_SCANNING_FABRIC) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
return (-1);
}
FCPARAM(isp, 0)->isp_lasthdl = oldhandle;
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.
*/
/*
* 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.
*/
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;
}
/*
* Skip any target mode entries.
*/
if (lp->target_mode) {
continue;
}
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, "Chan %d PortID 0x%06x "
"already at %d handle %d state %d",
chan, portid, dbidx, lp->handle, lp->state);
continue;
}
/*
* We should have the index of the first free entry seen.
*/
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;
}
/*
* Otherwise, point to our new home.
*/
lp = &fcp->portdb[dbidx];
/*
* 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.
*/
if (isp_login_device(isp, chan, portid, &pdb, &oldhandle)) {
if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
return (-1);
}
continue;
}
if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_MARK_PORTDB(isp, chan, 1);
return (-1);
}
FCPARAM(isp, 0)->isp_lasthdl = oldhandle;
/*
* 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].target_mode) {
continue;
}
if (fcp->portdb[dbidx].node_wwn == wwnn &&
fcp->portdb[dbidx].port_wwn == wwpn) {
break;
}
}
if (dbidx == MAX_FC_TARG) {
ISP_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,
"Chan %d Fabric Port 0x%06x is a New Entry",
chan, portid);
continue;
}
/*
* 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.
*/
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,
"Chan %d Zombie Fabric Port 0x%06x Now Changed",
chan, portid);
lp->state = FC_PORTDB_STATE_CHANGED;
} else {
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
"Chan %d Zombie Fabric Port 0x%06x "
"Now Pending Valid", chan, portid);
lp->state = FC_PORTDB_STATE_PENDING_VALID;
}
}
/*
* Find an unused handle and try and use to login to a port.
*/
static int
isp_login_device(ispsoftc_t *isp, int chan, uint32_t portid, isp_pdb_t *p, uint16_t *ohp)
{
int lim, i, r;
uint16_t handle;
handle = isp_nxt_handle(isp, chan, *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, chan, handle, p, 0);
if (r == 0 && p->portid != portid) {
(void) isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT | PLOGX_FLG_FREE_NPHDL, 1);
} else if (r == 0) {
break;
}
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
return (-1);
}
/*
* Now try and log into the device
*/
r = isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_PLOGI, 1);
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
return (-1);
}
if (r == 0) {
*ohp = handle;
break;
} else if ((r & 0xffff) == MBOX_PORT_ID_USED) {
/*
* If we get here, then the firmware still thinks we're logged into this device, but with a different
* handle. We need to break that association. We used to try and just substitute the handle, but then
* failed to get any data via isp_getpdb (below).
*/
if (isp_plogx(isp, chan, r >> 16, portid, PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT | PLOGX_FLG_FREE_NPHDL, 1)) {
isp_prt(isp, ISP_LOGERR, "baw... logout of %x failed", r >> 16);
}
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
return (-1);
}
r = isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_PLOGI, 1);
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
return (-1);
}
if (r == 0) {
*ohp = handle;
} else {
i = lim;
}
break;
} else if ((r & 0xffff) == MBOX_LOOP_ID_USED) {
/*
* Try the next loop id.
*/
*ohp = handle;
handle = isp_nxt_handle(isp, chan, handle);
} else {
/*
* Give up.
*/
i = lim;
break;
}
}
if (i == lim) {
isp_prt(isp, ISP_LOGWARN, "Chan %d PLOGI 0x%06x failed", chan, portid);
return (-1);
}
/*
* 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
*/
r = isp_getpdb(isp, chan, handle, p, 0);
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
return (-1);
}
if (r != 0) {
isp_prt(isp, ISP_LOGERR, "Chan %d new device 0x%06x@0x%x disappeared", chan, portid, handle);
return (-1);
}
if (isp->isp_state != ISP_RUNSTATE) {
isp_prt(isp, ISP_LOGERR, "Adapter not at RUNSTATE");
XS_SETERR(xs, HBA_BOTCH);
return (CMD_COMPLETE);
}
/*
* Check command CDB length, etc.. We really are limited to 16 bytes
* for Fibre Channel, but can do up to 44 bytes in parallel SCSI,
* but probably only if we're running fairly new firmware (we'll
* let the old f/w choke on an extended command queue entry).
*/
/*
* 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 = FCPARAM(isp, XS_CHANNEL(xs));
/*
* Now see if we need to synchronize the ISP with respect to anything.
* We do dual duty here (cough) for synchronizing for busses other
* than which we got here to send a command to.
*/
reqp = (ispreq_t *) local;
ISP_MEMZERO(local, QENTRY_LEN);
if (ISP_TST_SENDMARKER(isp, XS_CHANNEL(xs))) {
if (IS_24XX(isp)) {
isp_marker_24xx_t *m = (isp_marker_24xx_t *) reqp;
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, qep);
} else {
isp_marker_t *m = (isp_marker_t *) reqp;
m->mrk_header.rqs_entry_count = 1;
m->mrk_header.rqs_entry_type = RQSTYPE_MARKER;
m->mrk_target = (XS_CHANNEL(xs) << 7); /* bus # */
m->mrk_modifier = SYNC_ALL;
isp_put_marker(isp, m, qep);
}
ISP_SYNC_REQUEST(isp);
ISP_SET_SENDMARKER(isp, XS_CHANNEL(xs), 0);
goto start_again;
}
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
*/
/* XS_SET_RESID(xs, 0); */
/*
* Fibre Channel always requires some kind of 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)) {
((ispreqt2_t *)reqp)->req_flags = XS_TAG_TYPE(xs);
} else {
/*
* If we don't know what tag to use, use HEAD OF QUEUE
* for Request Sense or Simple.
*/
if (XS_CDBP(xs)[0] == 0x3) /* REQUEST SENSE */
((ispreqt2_t *)reqp)->req_flags = REQFLAG_HTAG;
else
((ispreqt2_t *)reqp)->req_flags = REQFLAG_STAG;
}
} else {
sdparam *sdp = SDPARAM(isp, XS_CHANNEL(xs));
if ((sdp->isp_devparam[target].actv_flags & DPARM_TQING) && XS_TAG_P(xs)) {
reqp->req_flags = XS_TAG_TYPE(xs);
}
}
tptr = &reqp->req_time;
/*
* NB: we do not support long CDBs
*/
cdblen = XS_CDBLEN(xs);
if (isp_allocate_xs(isp, xs, &handle)) {
isp_prt(isp, ISP_LOGDEBUG0, "out of xflist pointers");
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 platform dependent swizzling of the request entry
* so that the Qlogic F/W understands what is being asked of it.
*
* The callee is responsible for adding all requests at this point.
*/
dmaresult = ISP_DMASETUP(isp, xs, reqp);
if (dmaresult != CMD_QUEUED) {
isp_destroy_handle(isp, handle);
/*
* dmasetup sets actual error in packet, and
* return what we were given to return.
*/
return (dmaresult);
}
isp_xs_prt(isp, xs, ISP_LOGDEBUG0, "START cmd cdb[0]=0x%x datalen %ld", XS_CDBP(xs)[0], (long) XS_XFRLEN(xs));
isp->isp_nactive++;
return (CMD_QUEUED);
}
va_start(ap, ctl);
p = va_arg(ap, isp_plcmd_t *);
va_end(ap);
if ((p->flags & PLOGX_FLG_CMD_MASK) != PLOGX_FLG_CMD_PLOGI || (p->handle != NIL_HANDLE)) {
return (isp_plogx(isp, p->channel, p->handle, p->portid, p->flags, 0));
}
do {
p->handle = isp_nxt_handle(isp, p->channel, p->handle);
r = isp_plogx(isp, p->channel, 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);
}
default:
isp_prt(isp, ISP_LOGERR, "Unknown Control Opcode 0x%x", ctl);
break;
}
return (-1);
}
/*
* Interrupt Service Routine(s).
*
* External (OS) framework has done the appropriate locking,
* and the locking will be held throughout this function.
*/
/*
* Limit our stack depth by sticking with the max likely number
* of completions on a request queue at any one time.
*/
#ifndef MAX_REQUESTQ_COMPLETIONS
#define MAX_REQUESTQ_COMPLETIONS 32
#endif
again:
optr = isp->isp_residx;
/*
* Is this a mailbox related interrupt?
* The mailbox semaphore will be nonzero if so.
*/
if (sema) {
fmbox:
if (mbox & MBOX_COMMAND_COMPLETE) {
isp->isp_intmboxc++;
if (isp->isp_mboxbsy) {
int obits = isp->isp_obits;
isp->isp_mboxtmp[0] = mbox;
for (i = 1; i < MAX_MAILBOX(isp); i++) {
if ((obits & (1 << i)) == 0) {
continue;
}
isp->isp_mboxtmp[i] = ISP_READ(isp, MBOX_OFF(i));
}
if (isp->isp_mbxwrk0) {
if (isp_mbox_continue(isp) == 0) {
return;
}
}
MBOX_NOTIFY_COMPLETE(isp);
} else {
isp_prt(isp, ISP_LOGWARN, "mailbox cmd (0x%x) with no waiters", mbox);
}
} else {
i = IS_FC(isp)? isp_parse_async_fc(isp, mbox) : isp_parse_async(isp, mbox);
if (i < 0) {
return;
}
}
if ((IS_FC(isp) && mbox != ASYNC_RIOZIO_STALL) || isp->isp_state != ISP_RUNSTATE) {
goto out;
}
}
/*
* We can't be getting this now.
*/
if (isp->isp_state != ISP_RUNSTATE) {
/*
* This seems to happen to 23XX and 24XX cards- don't know why.
*/
if (isp->isp_mboxbsy && isp->isp_lastmbxcmd == MBOX_ABOUT_FIRMWARE) {
goto fmbox;
}
isp_prt(isp, ISP_LOGINFO, "interrupt (ISR=%x SEMA=%x) when not ready", isr, sema);
/*
* Thank you very much! *Burrrp*!
*/
ISP_WRITE(isp, isp->isp_respoutrp, ISP_READ(isp, isp->isp_respinrp));
if (IS_24XX(isp)) {
ISP_DISABLE_INTS(isp);
}
goto out;
}
/*
* Get the current Response Queue Out Pointer.
*
* If we're a 2300 or 2400, we can ask what hardware what it thinks.
*/
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_LOGINFO, "isp_intr: hard optr=%x, soft optr %x", optr, isp->isp_residx);
isp->isp_residx = optr;
}
} else {
optr = isp->isp_residx;
}
/*
* You *must* read the Response Queue In Pointer
* prior to clearing the RISC interrupt.
*
* Debounce the 2300 if revision less than 2.
*/
if (IS_2100(isp) || (IS_2300(isp) && isp->isp_revision < 2)) {
i = 0;
do {
iptr = ISP_READ(isp, isp->isp_respinrp);
junk = ISP_READ(isp, isp->isp_respinrp);
} while (junk != iptr && ++i < 1000);
if (!ISP_VALID_HANDLE(isp, sp->req_handle)) {
isp_prt(isp, ISP_LOGERR, "bad request handle 0x%x (iocb type 0x%x)", sp->req_handle, etype);
ISP_MEMZERO(hp, QENTRY_LEN); /* PERF */
ISP_WRITE(isp, isp->isp_respoutrp, optr);
continue;
}
xs = isp_find_xs(isp, sp->req_handle);
if (xs == NULL) {
uint8_t ts = completion_status & 0xff;
/*
* Only whine if this isn't the expected fallout of
* aborting the command or resetting the target.
*/
if (etype != RQSTYPE_RESPONSE) {
isp_prt(isp, ISP_LOGERR, "cannot find handle 0x%x (type 0x%x)", sp->req_handle, etype);
} else if (ts != RQCS_ABORTED && ts != RQCS_RESET_OCCURRED) {
isp_prt(isp, ISP_LOGERR, "cannot find handle 0x%x (status 0x%x)", sp->req_handle, ts);
}
ISP_MEMZERO(hp, QENTRY_LEN); /* PERF */
ISP_WRITE(isp, isp->isp_respoutrp, optr);
continue;
}
if (req_status_flags & RQSTF_BUS_RESET) {
XS_SETERR(xs, HBA_BUSRESET);
ISP_SET_SENDMARKER(isp, XS_CHANNEL(xs), 1);
}
if (buddaboom) {
XS_SETERR(xs, HBA_BOTCH);
}
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.
*/
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 (req_state_flags & RQSF_GOT_STATUS) {
*XS_STSP(xs) = scsi_status & 0xff;
}
switch (etype) {
case RQSTYPE_RESPONSE:
if (resp && rlen >= 4 && resp[FCP_RSPNS_CODE_OFFSET] != 0) {
const char *ptr;
char lb[64];
const char *rnames[6] = {
"Task Management Function Done",
"Data Length Differs From Burst Length",
"Invalid FCP Cmnd",
"FCP DATA RO mismatch with FCP DATA_XFR_RDY RO",
"Task Management Function Rejected",
"Task Management Function Failed",
};
if (resp[FCP_RSPNS_CODE_OFFSET] > 5) {
ISP_SNPRINTF(lb, sizeof lb, "Unknown FCP Response Code 0x%x", resp[FCP_RSPNS_CODE_OFFSET]);
ptr = lb;
} else {
ptr = rnames[resp[FCP_RSPNS_CODE_OFFSET]];
}
isp_xs_prt(isp, xs, ISP_LOGWARN, "FCP RESPONSE, LENGTH %u: %s CDB0=0x%02x", rlen, ptr, XS_CDBP(xs)[0] & 0xff);
if (resp[FCP_RSPNS_CODE_OFFSET] != 0) {
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_SET_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 (req_status_flags & RQSTF_NEGOTIATION) {
int t = XS_TGT(xs);
sdparam *sdp = SDPARAM(isp, XS_CHANNEL(xs));
sdp->isp_devparam[t].dev_refresh = 1;
sdp->update = 1;
}
} else {
if (req_status_flags & RQSF_XFER_COMPLETE) {
XS_SET_RESID(xs, 0);
} else if (scsi_status & RQCS_RESID) {
XS_SET_RESID(xs, resid);
} else {
XS_SET_RESID(xs, 0);
}
}
if (snsp && slen) {
XS_SAVE_SENSE(xs, snsp, slen);
} else if ((req_status_flags & RQSF_GOT_STATUS) && (scsi_status & 0xff) == SCSI_CHECK && IS_FC(isp)) {
isp_prt(isp, ISP_LOGWARN, "CHECK CONDITION w/o sense data for CDB=0x%x", XS_CDBP(xs)[0] & 0xff);
isp_print_bytes(isp, "CC with no Sense", QENTRY_LEN, qe);
}
isp_prt(isp, ISP_LOGDEBUG2, "asked for %ld got raw resid %ld settled for %ld", (long) XS_XFRLEN(xs), resid, (long) XS_GET_RESID(xs));
break;
case RQSTYPE_REQUEST:
case RQSTYPE_A64:
case RQSTYPE_T2RQS:
case RQSTYPE_T3RQS:
case RQSTYPE_T7RQS:
if (!IS_24XX(isp) && (sp->req_header.rqs_flags & RQSFLAG_FULL)) {
/*
* Force Queue Full status.
*/
*XS_STSP(xs) = SCSI_QFULL;
XS_SETERR(xs, HBA_NOERROR);
} else if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BOTCH);
}
XS_SET_RESID(xs, XS_XFRLEN(xs));
break;
default:
isp_print_bytes(isp, "Unhandled Response Type", QENTRY_LEN, qe);
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BOTCH);
}
break;
}
/*
* 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);
}
isp_destroy_handle(isp, sp->req_handle);
if (((isp->isp_dblev & (ISP_LOGDEBUG1|ISP_LOGDEBUG2|ISP_LOGDEBUG3))) ||
((isp->isp_dblev & (ISP_LOGDEBUG0|ISP_LOG_CWARN) && ((!XS_NOERR(xs)) || (*XS_STSP(xs) != SCSI_GOOD))))) {
isp_prt_endcmd(isp, xs);
}
if (isp->isp_nactive > 0) {
isp->isp_nactive--;
}
complist[ndone++] = xs; /* defer completion call until later */
ISP_MEMZERO(hp, QENTRY_LEN); /* PERF */
if (ndone == MAX_REQUESTQ_COMPLETIONS) {
break;
}
}
/*
* If we looked at any commands, then it's valid to find out
* what the outpointer is. It also is a trigger to update the
* ISP's notion of what we've seen so far.
*/
if (nlooked) {
ISP_WRITE(isp, isp->isp_respoutrp, optr);
/*
* While we're at it, read the request queue out pointer.
*/
isp->isp_reqodx = ISP_READ(isp, isp->isp_rqstoutrp);
if (isp->isp_rscchiwater < ndone) {
isp->isp_rscchiwater = ndone;
}
}
/*
* Parse an ASYNC mailbox complete
*
* Return non-zero if the event has been acknowledged.
*/
static int
isp_parse_async(ispsoftc_t *isp, uint16_t mbox)
{
int acked = 0;
uint32_t h1 = 0, h2 = 0;
uint16_t chan = 0;
/*
* Pick up the channel, but not if this is a ASYNC_RIO32_2,
* where Mailboxes 6/7 have the second handle.
*/
if (mbox != ASYNC_RIO32_2) {
if (IS_DUALBUS(isp)) {
chan = ISP_READ(isp, OUTMAILBOX6);
}
}
isp_prt(isp, ISP_LOGDEBUG2, "Async Mbox 0x%x", mbox);
switch (mbox) {
case ASYNC_BUS_RESET:
ISP_SET_SENDMARKER(isp, chan, 1);
#ifdef ISP_TARGET_MODE
if (isp_target_async(isp, chan, mbox)) {
acked = 1;
}
#endif
isp_async(isp, ISPASYNC_BUS_RESET, chan);
break;
case ASYNC_SYSTEM_ERROR:
isp->isp_dead = 1;
isp->isp_state = ISP_CRASHED;
/*
* 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);
acked = 1;
break;
case ASYNC_RQS_XFER_ERR:
isp_prt(isp, ISP_LOGERR, "Request Queue Transfer Error");
break;
case ASYNC_RSP_XFER_ERR:
isp_prt(isp, ISP_LOGERR, "Response Queue Transfer Error");
break;
case ASYNC_QWAKEUP:
/*
* We've just been notified that the Queue has woken up.
* We don't need to be chatty about this- just unlatch things
* and move on.
*/
mbox = ISP_READ(isp, isp->isp_rqstoutrp);
break;
case ASYNC_TIMEOUT_RESET:
isp_prt(isp, ISP_LOGWARN, "timeout initiated SCSI bus reset of chan %d", chan);
ISP_SET_SENDMARKER(isp, chan, 1);
#ifdef ISP_TARGET_MODE
if (isp_target_async(isp, chan, mbox)) {
acked = 1;
}
#endif
break;
case ASYNC_DEVICE_RESET:
isp_prt(isp, ISP_LOGINFO, "device reset on chan %d", chan);
ISP_SET_SENDMARKER(isp, chan, 1);
#ifdef ISP_TARGET_MODE
if (isp_target_async(isp, chan, mbox)) {
acked = 1;
}
#endif
break;
case ASYNC_EXTMSG_UNDERRUN:
isp_prt(isp, ISP_LOGWARN, "extended message underrun");
break;
case ASYNC_SCAM_INT:
isp_prt(isp, ISP_LOGINFO, "SCAM interrupt");
break;
case ASYNC_HUNG_SCSI:
isp_prt(isp, ISP_LOGERR, "stalled SCSI Bus after DATA Overrun");
/* XXX: Need to issue SCSI reset at this point */
break;
case ASYNC_KILLED_BUS:
isp_prt(isp, ISP_LOGERR, "SCSI Bus reset after DATA Overrun");
break;
case ASYNC_BUS_TRANSIT:
mbox = ISP_READ(isp, OUTMAILBOX2);
switch (mbox & SXP_PINS_MODE_MASK) {
case SXP_PINS_LVD_MODE:
isp_prt(isp, ISP_LOGINFO, "Transition to LVD mode");
SDPARAM(isp, chan)->isp_diffmode = 0;
SDPARAM(isp, chan)->isp_ultramode = 0;
SDPARAM(isp, chan)->isp_lvdmode = 1;
break;
case SXP_PINS_HVD_MODE:
isp_prt(isp, ISP_LOGINFO,
"Transition to Differential mode");
SDPARAM(isp, chan)->isp_diffmode = 1;
SDPARAM(isp, chan)->isp_ultramode = 0;
SDPARAM(isp, chan)->isp_lvdmode = 0;
break;
case SXP_PINS_SE_MODE:
isp_prt(isp, ISP_LOGINFO,
"Transition to Single Ended mode");
SDPARAM(isp, chan)->isp_diffmode = 0;
SDPARAM(isp, chan)->isp_ultramode = 1;
SDPARAM(isp, chan)->isp_lvdmode = 0;
break;
default:
isp_prt(isp, ISP_LOGWARN,
"Transition to Unknown Mode 0x%x", mbox);
break;
}
/*
* XXX: Set up to renegotiate again!
*/
/* Can only be for a 1080... */
ISP_SET_SENDMARKER(isp, chan, 1);
break;
case ASYNC_CMD_CMPLT:
case ASYNC_RIO32_1:
if (!IS_ULTRA3(isp)) {
isp_prt(isp, ISP_LOGERR, "unexpected fast posting completion");
break;
}
/* FALLTHROUGH */
h1 = (ISP_READ(isp, OUTMAILBOX2) << 16) | ISP_READ(isp, OUTMAILBOX1);
break;
case ASYNC_RIO16_5:
case ASYNC_RIO16_4:
case ASYNC_RIO16_3:
case ASYNC_RIO16_2:
case ASYNC_RIO16_1:
isp_prt(isp, ISP_LOGERR, "unexpected 16 bit RIO handle");
break;
default:
isp_prt(isp, ISP_LOGWARN, "%s: unhandled async code 0x%x", __func__, mbox);
break;
}
if (h1 || h2) {
isp_prt(isp, ISP_LOGDEBUG3, "fast post/rio completion of 0x%08x", h1);
isp_fastpost_complete(isp, h1);
if (h2) {
isp_prt(isp, ISP_LOGDEBUG3, "fast post/rio completion of 0x%08x", h2);
isp_fastpost_complete(isp, h2);
if (isp->isp_fpcchiwater < 2) {
isp->isp_fpcchiwater = 2;
}
} else {
if (isp->isp_fpcchiwater < 1) {
isp->isp_fpcchiwater = 1;
}
}
} else {
isp->isp_intoasync++;
}
return (acked);
}
#define GET_24XX_BUS(isp, chan, msg) \
if (IS_24XX(isp)) { \
chan = ISP_READ(isp, OUTMAILBOX3) & 0xff; \
if (chan >= isp->isp_nchan) { \
isp_prt(isp, ISP_LOGERR, "bogus channel %u for %s at line %d", chan, msg, __LINE__); \
break; \
} \
}
static int
isp_parse_async_fc(ispsoftc_t *isp, uint16_t mbox)
{
int acked = 0;
uint16_t chan;
switch (mbox) {
case ASYNC_SYSTEM_ERROR:
isp->isp_dead = 1;
isp->isp_state = ISP_CRASHED;
FCPARAM(isp, chan)->isp_loopstate = LOOP_NIL;
FCPARAM(isp, chan)->isp_fwstate = FW_CONFIG_WAIT;
/*
* 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);
acked = 1;
break;
case ASYNC_RQS_XFER_ERR:
isp_prt(isp, ISP_LOGERR, "Request Queue Transfer Error");
break;
case ASYNC_RSP_XFER_ERR:
isp_prt(isp, ISP_LOGERR, "Response Queue Transfer Error");
break;
case ASYNC_QWAKEUP:
#ifdef ISP_TARGET_MODE
if (IS_24XX(isp)) {
isp_prt(isp, ISP_LOGERR, "ATIO Queue Transfer Error");
break;
}
#endif
isp_prt(isp, ISP_LOGERR, "%s: unexpected ASYNC_QWAKEUP code", __func__);
break;
case ASYNC_CMD_CMPLT:
isp_fastpost_complete(isp, (ISP_READ(isp, OUTMAILBOX2) << 16) | ISP_READ(isp, OUTMAILBOX1));
if (isp->isp_fpcchiwater < 1) {
isp->isp_fpcchiwater = 1;
}
break;
case ASYNC_RIOZIO_STALL:
break;
case ASYNC_CTIO_DONE:
#ifdef ISP_TARGET_MODE
if (isp_target_async(isp, (ISP_READ(isp, OUTMAILBOX2) << 16) | ISP_READ(isp, OUTMAILBOX1), mbox)) {
acked = 1;
} else {
isp->isp_fphccmplt++;
}
#else
isp_prt(isp, ISP_LOGWARN, "unexpected ASYNC CTIO done");
#endif
break;
case ASYNC_LIP_ERROR:
case ASYNC_LIP_F8:
case ASYNC_LIP_OCCURRED:
case ASYNC_PTPMODE:
/*
* These are broadcast events that have to be sent across
* all active channels.
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
int topo = fcp->isp_topo;
if (fcp->role == ISP_ROLE_NONE) {
continue;
}
fcp->isp_fwstate = FW_CONFIG_WAIT;
fcp->isp_loopstate = LOOP_LIP_RCVD;
ISP_SET_SENDMARKER(isp, chan, 1);
ISP_MARK_PORTDB(isp, chan, 1);
isp_async(isp, ISPASYNC_LIP, chan);
#ifdef ISP_TARGET_MODE
if (isp_target_async(isp, chan, mbox)) {
acked = 1;
}
#endif
/*
* We've had problems with data corruption occurring 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
* all active initiator commands as dead.
*/
if (topo == TOPO_NL_PORT || topo == TOPO_FL_PORT) {
int i, j;
for (i = j = 0; i < isp->isp_maxcmds; i++) {
XS_T *xs;
isp_hdl_t *hdp;
case ASYNC_LOOP_UP:
/*
* This is a broadcast event that has to be sent across
* all active channels.
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
case ASYNC_LOOP_DOWN:
/*
* This is a broadcast event that has to be sent across
* all active channels.
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
case ASYNC_LOOP_RESET:
/*
* This is a broadcast event that has to be sent across
* all active channels.
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
case ASYNC_PDB_CHANGED:
{
int nphdl, nlstate, reason;
/*
* We *should* get a channel out of the 24XX, but we don't seem
* to get more than a PDB CHANGED on channel 0, so turn it into
* a broadcast event.
*/
if (IS_24XX(isp)) {
nphdl = ISP_READ(isp, OUTMAILBOX1);
nlstate = ISP_READ(isp, OUTMAILBOX2);
reason = ISP_READ(isp, OUTMAILBOX3) >> 8;
} else {
nphdl = NIL_HANDLE;
nlstate = reason = 0;
}
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
case ASYNC_CONNMODE:
/*
* This only applies to 2100 amd 2200 cards
*/
if (!IS_2200(isp) && !IS_2100(isp)) {
isp_prt(isp, ISP_LOGWARN, "bad card for ASYNC_CONNMODE event");
break;
}
chan = 0;
mbox = ISP_READ(isp, OUTMAILBOX1);
ISP_MARK_PORTDB(isp, chan, 1);
switch (mbox) {
case ISP_CONN_LOOP:
isp_prt(isp, ISP_LOGINFO,
"Point-to-Point -> Loop mode");
break;
case ISP_CONN_PTP:
isp_prt(isp, ISP_LOGINFO,
"Loop -> Point-to-Point mode");
break;
case ISP_CONN_BADLIP:
isp_prt(isp, ISP_LOGWARN,
"Point-to-Point -> Loop mode (BAD LIP)");
break;
case ISP_CONN_FATAL:
isp->isp_dead = 1;
isp->isp_state = ISP_CRASHED;
isp_prt(isp, ISP_LOGERR, "FATAL CONNECTION ERROR");
isp_async(isp, ISPASYNC_FW_CRASH);
return (-1);
case ISP_CONN_LOOPBACK:
isp_prt(isp, ISP_LOGWARN,
"Looped Back in Point-to-Point mode");
break;
default:
isp_prt(isp, ISP_LOGWARN,
"Unknown connection mode (0x%x)", mbox);
break;
}
isp_async(isp, ISPASYNC_CHANGE_NOTIFY, chan, ISPASYNC_CHANGE_OTHER);
FCPARAM(isp, chan)->sendmarker = 1;
FCPARAM(isp, chan)->isp_fwstate = FW_CONFIG_WAIT;
FCPARAM(isp, chan)->isp_loopstate = LOOP_LIP_RCVD;
break;
case ASYNC_RCV_ERR:
if (IS_24XX(isp)) {
isp_prt(isp, ISP_LOGWARN, "Receive Error");
} else {
isp_prt(isp, ISP_LOGWARN, "unexpected ASYNC_RCV_ERR");
}
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;
}
if (mbox != ASYNC_CTIO_DONE && mbox != ASYNC_CMD_CMPLT) {
isp->isp_intoasync++;
}
return (acked);
}
/*
* Handle other response entries. A pointer to the request queue output
* index is here in case we want to eat several entries at once, although
* this is not used currently.
*/
static int
isp_handle_other_response(ispsoftc_t *isp, int type, isphdr_t *hp, uint32_t *optrp)
{
switch (type) {
case RQSTYPE_STATUS_CONT:
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:
case RQSTYPE_ENABLE_LUN:
case RQSTYPE_MODIFY_LUN:
case RQSTYPE_NOTIFY:
case RQSTYPE_NOTIFY_ACK:
case RQSTYPE_CTIO1:
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)) {
return (1);
}
#endif
/* FALLTHROUGH */
case RQSTYPE_RPT_ID_ACQ:
if (IS_24XX(isp)) {
isp_ridacq_t rid;
isp_get_ridacq(isp, (isp_ridacq_t *)hp, &rid);
if (rid.ridacq_format == 0) {
}
return (1);
}
/* FALLTHROUGH */
case RQSTYPE_REQUEST:
default:
ISP_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));
return (0);
}
}
static void
isp_parse_status(ispsoftc_t *isp, ispstatusreq_t *sp, XS_T *xs, long *rp)
{
switch (sp->req_completion_status & 0xff) {
case RQCS_COMPLETE:
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_NOERROR);
}
return;
case RQCS_INCOMPLETE:
if ((sp->req_state_flags & RQSF_GOT_TARGET) == 0) {
isp_xs_prt(isp, xs, ISP_LOGDEBUG1, "Selection Timeout");
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_SELTIMEOUT);
*rp = XS_XFRLEN(xs);
}
return;
}
isp_xs_prt(isp, xs, ISP_LOGERR, "Command Incomplete, state 0x%x", sp->req_state_flags);
break;
case RQCS_TIMEOUT:
isp_xs_prt(isp, xs, ISP_LOGWARN, "Command timed out");
/*
* XXX: Check to see if we logged out of the device.
*/
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_CMDTIMEOUT);
}
return;
case RQCS_DATA_OVERRUN:
XS_SET_RESID(xs, sp->req_resid);
isp_xs_prt(isp, xs, ISP_LOGERR, "data overrun (%ld)", (long) XS_GET_RESID(xs));
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_DATAOVR);
}
return;
case RQCS_COMMAND_OVERRUN:
isp_xs_prt(isp, xs, ISP_LOGERR, "command overrun");
break;
case RQCS_STATUS_OVERRUN:
isp_xs_prt(isp, xs, ISP_LOGERR, "status overrun");
break;
case RQCS_BAD_MESSAGE:
isp_xs_prt(isp, xs, ISP_LOGERR, "msg not COMMAND COMPLETE after status");
break;
case RQCS_NO_MESSAGE_OUT:
isp_xs_prt(isp, xs, ISP_LOGERR, "No MESSAGE OUT phase after selection");
break;
case RQCS_EXT_ID_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "EXTENDED IDENTIFY failed");
break;
case RQCS_IDE_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "INITIATOR DETECTED ERROR rejected");
break;
case RQCS_ABORT_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "ABORT OPERATION rejected");
break;
case RQCS_REJECT_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "MESSAGE REJECT rejected");
break;
case RQCS_NOP_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "NOP rejected");
break;
case RQCS_PARITY_ERROR_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "MESSAGE PARITY ERROR rejected");
break;
case RQCS_DEVICE_RESET_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGWARN, "BUS DEVICE RESET rejected");
break;
case RQCS_ID_MSG_FAILED:
isp_xs_prt(isp, xs, ISP_LOGERR, "IDENTIFY rejected");
break;
case RQCS_UNEXP_BUS_FREE:
isp_xs_prt(isp, xs, ISP_LOGERR, "Unexpected Bus Free");
break;
case RQCS_DATA_UNDERRUN:
{
if (IS_FC(isp)) {
int ru_marked = (sp->req_scsi_status & RQCS_RU) != 0;
if (!ru_marked || sp->req_resid > XS_XFRLEN(xs)) {
isp_xs_prt(isp, xs, ISP_LOGWARN, bun, XS_XFRLEN(xs), sp->req_resid, (ru_marked)? "marked" : "not marked");
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BOTCH);
}
return;
}
}
XS_SET_RESID(xs, sp->req_resid);
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_NOERROR);
}
return;
}
case RQCS_XACT_ERR1:
isp_xs_prt(isp, xs, ISP_LOGERR, "HBA attempted queued transaction with disconnect not set");
break;
case RQCS_XACT_ERR2:
isp_xs_prt(isp, xs, ISP_LOGERR, "HBA attempted queued transaction to target routine %d", XS_LUN(xs));
break;
case RQCS_XACT_ERR3:
isp_xs_prt(isp, xs, ISP_LOGERR, "HBA attempted queued cmd when queueing disabled");
break;
case RQCS_BAD_ENTRY:
isp_prt(isp, ISP_LOGERR, "Invalid IOCB entry type detected");
break;
case RQCS_QUEUE_FULL:
isp_xs_prt(isp, xs, ISP_LOGDEBUG0, "internal queues full status 0x%x", *XS_STSP(xs));
/*
* If QFULL or some other status byte is set, then this
* isn't an error, per se.
*
* Unfortunately, some QLogic f/w writers have, in
* some cases, omitted to *set* status to QFULL.
*
case RQCS_LVD_BUSERR:
isp_xs_prt(isp, xs, ISP_LOGERR, "Bad LVD condition");
break;
case RQCS_PORT_UNAVAILABLE:
/*
* No such port on the loop. Moral equivalent of SELTIMEO
*/
case RQCS_PORT_LOGGED_OUT:
{
const char *reason;
uint8_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, 0)->isp_topo == TOPO_NL_PORT ||
FCPARAM(isp, 0)->isp_topo == TOPO_FL_PORT) {
mbreg_t mbs;
MBSINIT(&mbs, MBOX_INIT_LIP, MBLOGALL, 0);
if (ISP_CAP_2KLOGIN(isp)) {
mbs.ibits = (1 << 10);
}
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);
}
}
static void
isp_parse_status_24xx(ispsoftc_t *isp, isp24xx_statusreq_t *sp, XS_T *xs, long *rp)
{
int ru_marked, sv_marked;
int chan = XS_CHANNEL(xs);
switch (sp->req_completion_status) {
case RQCS_COMPLETE:
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_NOERROR);
}
return;
case RQCS_DMA_ERROR:
isp_xs_prt(isp, xs, ISP_LOGERR, "DMA error");
break;
case RQCS_TRANSPORT_ERROR:
isp_xs_prt(isp, xs, ISP_LOGERR, "Transport Error");
break;
case RQCS_RESET_OCCURRED:
isp_xs_prt(isp, xs, ISP_LOGWARN, "reset destroyed command");
FCPARAM(isp, chan)->sendmarker = 1;
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BUSRESET);
}
return;
case RQCS_ABORTED:
isp_xs_prt(isp, xs, ISP_LOGERR, "Command Aborted");
FCPARAM(isp, chan)->sendmarker = 1;
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_ABORTED);
}
return;
case RQCS_TIMEOUT:
isp_xs_prt(isp, xs, ISP_LOGWARN, "Command Timed Out");
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_CMDTIMEOUT);
}
return;
case RQCS_DATA_OVERRUN:
XS_SET_RESID(xs, sp->req_resid);
isp_xs_prt(isp, xs, ISP_LOGERR, "Data Overrun");
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_DATAOVR);
}
return;
case RQCS_24XX_DRE: /* data reassembly error */
isp_prt(isp, ISP_LOGERR,
"Chan %d data reassembly error for target %d",
chan, 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, "Chan %d target %d sent ABTS",
chan, 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;
/*
* We can get an underrun w/o things being marked
* if we got a non-zero status.
*/
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_xs_prt(isp, xs, ISP_LOGWARN, bun, XS_XFRLEN(xs), sp->req_resid, (ru_marked)? "marked" : "not marked");
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BOTCH);
}
return;
}
XS_SET_RESID(xs, sp->req_resid);
isp_xs_prt(isp, xs, ISP_LOGDEBUG0, "Data Underrun (%d) for command 0x%x", sp->req_resid, XS_CDBP(xs)[0] & 0xff);
if (XS_NOERR(xs)) {
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;
uint8_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, "Chan %d port %s for target %d",
chan, reason, XS_TGT(xs));
/*
* There is no MBOX_INIT_LIP for the 24XX.
*/
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_SELTIMEOUT);
}
return;
}
case RQCS_PORT_CHANGED:
isp_prt(isp, ISP_LOGWARN,
"port changed for target %d chan %d", XS_TGT(xs), chan);
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_SELTIMEOUT);
}
return;
case RQCS_24XX_ENOMEM: /* f/w resource unavailable */
isp_prt(isp, ISP_LOGWARN,
"f/w resource unavailable for target %d chan %d",
XS_TGT(xs), chan);
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 for "
"chan %d", XS_TGT(xs), chan);
if (XS_NOERR(xs)) {
*XS_STSP(xs) = SCSI_BUSY;
XS_SETERR(xs, HBA_TGTBSY);
}
return;
default:
isp_prt(isp, ISP_LOGERR,
"Unknown Completion Status 0x%x on chan %d",
sp->req_completion_status, chan);
break;
}
if (XS_NOERR(xs)) {
XS_SETERR(xs, HBA_BOTCH);
}
}
if (fph == 0) {
return;
}
xs = isp_find_xs(isp, fph);
if (xs == NULL) {
isp_prt(isp, ISP_LOGWARN,
"Command for fast post handle 0x%x not found", fph);
return;
}
isp_destroy_handle(isp, fph);
/*
* Since we don't have a result queue entry item,
* we must believe that SCSI status is zero and
* that all data transferred.
*/
XS_SET_RESID(xs, 0);
*XS_STSP(xs) = SCSI_GOOD;
if (XS_XFRLEN(xs)) {
ISP_DMAFREE(isp, xs, fph);
}
if (isp->isp_nactive) {
isp->isp_nactive--;
}
isp->isp_fphccmplt++;
isp_done(xs);
}
/*
* We assume that we can't overwrite a previous command.
*/
isp->isp_obits = obits;
isp->isp_mboxbsy = 1;
/*
* Set Host Interrupt condition so that RISC will pick up mailbox regs.
*/
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, mbp);
/*
* Did the command time out?
*/
if (mbp->param[0] == MBOX_TIMEOUT) {
isp->isp_mboxbsy = 0;
MBOX_RELEASE(isp);
goto out;
}
static void
isp_spi_update(ispsoftc_t *isp, int chan)
{
int tgt;
mbreg_t mbs;
sdparam *sdp;
if (IS_FC(isp)) {
/*
* There are no 'per-bus' settings for Fibre Channel.
*/
return;
}
sdp = SDPARAM(isp, chan);
sdp->update = 0;
for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
uint16_t flags, period, offset;
int get;
if (sdp->isp_devparam[tgt].dev_enable == 0) {
sdp->isp_devparam[tgt].dev_update = 0;
sdp->isp_devparam[tgt].dev_refresh = 0;
isp_prt(isp, ISP_LOGDEBUG0, "skipping target %d bus %d update", tgt, chan);
continue;
}
/*
* If the goal is to update the status of the device,
* take what's in goal_flags and try and set the device
* toward that. Otherwise, if we're just refreshing the
* current device state, get the current parameters.
*/
MBSINIT(&mbs, 0, MBLOGALL, 0);
/*
* Refresh overrides set
*/
if (sdp->isp_devparam[tgt].dev_refresh) {
mbs.param[0] = MBOX_GET_TARGET_PARAMS;
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;
/*
* Insist that PARITY must be enabled
* if SYNC or WIDE is enabled.
*/
if ((mbs.param[2] & (DPARM_SYNC|DPARM_WIDE)) != 0) {
mbs.param[2] |= DPARM_PARITY;
}
if (mbs.param[2] & DPARM_SYNC) {
mbs.param[3] =
(sdp->isp_devparam[tgt].goal_offset << 8) |
(sdp->isp_devparam[tgt].goal_period);
}
/*
* A command completion later that has
* RQSTF_NEGOTIATION set can cause
* the dev_refresh/announce cycle also.
*
* Note: It is really important to update our current
* flags with at least the state of TAG capabilities-
* otherwise we might try and send a tagged command
* when we have it all turned off. So change it here
* to say that current already matches goal.
*/
sdp->isp_devparam[tgt].actv_flags &= ~DPARM_TQING;
sdp->isp_devparam[tgt].actv_flags |=
(sdp->isp_devparam[tgt].goal_flags & DPARM_TQING);
isp_prt(isp, ISP_LOGDEBUG0, "bus %d set tgt %d flags 0x%x off 0x%x period 0x%x",
chan, tgt, mbs.param[2], mbs.param[3] >> 8, mbs.param[3] & 0xff);
get = 0;
} else {
continue;
}
mbs.param[1] = (chan << 15) | (tgt << 8);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
continue;
}
if (get == 0) {
sdp->sendmarker = 1;
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;
isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, chan, tgt);
}
}
/*
* The trick here is to establish a default for the default (honk!)
* state (goal_flags). Then try and get the current status from
* the card to fill in the current state. We don't, in fact, set
* the default to the SAFE default state- that's not the goal state.
*/
for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
uint8_t off, per;
sdp->isp_devparam[tgt].actv_offset = 0;
sdp->isp_devparam[tgt].actv_period = 0;
sdp->isp_devparam[tgt].actv_flags = 0;
/*
* We default to Wide/Fast for versions less than a 1040
* (unless it's SBus).
*/
if (IS_ULTRA3(isp)) {
off = ISP_80M_SYNCPARMS >> 8;
per = ISP_80M_SYNCPARMS & 0xff;
} else if (IS_ULTRA2(isp)) {
off = ISP_40M_SYNCPARMS >> 8;
per = ISP_40M_SYNCPARMS & 0xff;
} else if (IS_1240(isp)) {
off = ISP_20M_SYNCPARMS >> 8;
per = ISP_20M_SYNCPARMS & 0xff;
} else if ((isp->isp_bustype == ISP_BT_SBUS &&
isp->isp_type < ISP_HA_SCSI_1020A) ||
(isp->isp_bustype == ISP_BT_PCI &&
isp->isp_type < ISP_HA_SCSI_1040) ||
(isp->isp_clock && isp->isp_clock < 60) ||
(sdp->isp_ultramode == 0)) {
off = ISP_10M_SYNCPARMS >> 8;
per = ISP_10M_SYNCPARMS & 0xff;
} else {
off = ISP_20M_SYNCPARMS_1040 >> 8;
per = ISP_20M_SYNCPARMS_1040 & 0xff;
}
sdp->isp_devparam[tgt].goal_offset =
sdp->isp_devparam[tgt].nvrm_offset = off;
sdp->isp_devparam[tgt].goal_period =
sdp->isp_devparam[tgt].nvrm_period = per;
}
/*
* If we're a dual bus card, just copy the data over
*/
if (sdp1) {
*sdp1 = *sdp;
sdp1->isp_initiator_id = DEFAULT_IID(isp, 1);
}
/*
* If we've not been told to avoid reading NVRAM, try and read it.
* If we're successful reading it, we can then return because NVRAM
* will tell us what the desired settings are. Otherwise, we establish
* some reasonable 'fake' nvram and goal defaults.
*/
if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) {
mbreg_t mbs;
if (IS_24XX(isp)) {
fcp->isp_fwoptions |= ICB2400_OPT1_FAIRNESS;
fcp->isp_fwoptions |= ICB2400_OPT1_HARD_ADDRESS;
if (isp->isp_confopts & ISP_CFG_FULL_DUPLEX) {
fcp->isp_fwoptions |= ICB2400_OPT1_FULL_DUPLEX;
}
fcp->isp_fwoptions |= ICB2400_OPT1_BOTH_WWNS;
} else {
fcp->isp_fwoptions |= ICBOPT_FAIRNESS;
fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE;
fcp->isp_fwoptions |= ICBOPT_HARD_ADDRESS;
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, chan);
if (j == 0) {
break;
}
}
if (j) {
isp->isp_confopts |= ISP_CFG_NONVRAM;
}
}
/*
* 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.
*/
void
isp_reinit(ispsoftc_t *isp, int do_load_defaults)
{
int i;
/*
* There is NVRAM storage for both Port and Node entities-
* but the Node entity appears to be unused on all the cards
* 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
* 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.
*/
wwn = ISP2100_NVRAM_PORT_NAME(nvram_data);
if (wwn) {
isp_prt(isp, ISP_LOGCONFIG, "NVRAM Port WWN 0x%08x%08x",
(uint32_t) (wwn >> 32), (uint32_t) (wwn));
if ((wwn >> 60) == 0) {
wwn |= (((uint64_t) 2)<< 60);
}
}
fcp->isp_wwpn_nvram = wwn;
if (IS_2200(isp) || IS_23XX(isp)) {
wwn = ISP2100_NVRAM_NODE_NAME(nvram_data);
if (wwn) {
isp_prt(isp, ISP_LOGCONFIG, "NVRAM Node WWN 0x%08x%08x",
(uint32_t) (wwn >> 32),
(uint32_t) (wwn));
if ((wwn >> 60) == 0) {
wwn |= (((uint64_t) 2)<< 60);
}
} else {
wwn = fcp->isp_wwpn_nvram & ~((uint64_t) 0xfff << 48);
}
} else {
wwn &= ~((uint64_t) 0xfff << 48);
}
fcp->isp_wwnn_nvram = wwn;