#ifdef __NetBSD__
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <dev/ic/isp_netbsd.h>
#endif
#ifdef __FreeBSD__
#include <sys/param.h>
#include <sys/cdefs.h>
#include <sys/firmware.h>
#include <dev/isp/isp_freebsd.h>
#endif
#ifdef __OpenBSD__
#include <dev/ic/isp_openbsd.h>
#endif
#ifdef __linux__
#include "isp_linux.h"
#endif
#ifdef __svr4__
#include "isp_solaris.h"
#endif
static const char notresp[] = "Unknown IOCB in RESPONSE Queue (type 0x%x) @ idx %d (next %d)";
static const char bun[] = "bad underrun (count %d, resid %d, status %s)";
static const char lipd[] = "Chan %d LIP destroyed %d active commands";
static const char sacq[] = "unable to acquire scratch area";
static const uint8_t alpa_map[] = {
0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda,
0xd9, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce,
0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc7, 0xc6, 0xc5,
0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5, 0xb4, 0xb3,
0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b,
0x98, 0x97, 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81,
0x80, 0x7c, 0x7a, 0x79, 0x76, 0x75, 0x74, 0x73,
0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69,
0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c,
0x4b, 0x4a, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c,
0x3a, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31,
0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x27, 0x26,
0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
};
static int isp_handle_control(ispsoftc_t *, isphdr_t *);
static void isp_handle_rpt_id_acq(ispsoftc_t *, isphdr_t *);
static void isp_parse_status_24xx(ispsoftc_t *, isp24xx_statusreq_t *, XS_T *);
static void isp_clear_portdb(ispsoftc_t *, int);
static void isp_mark_portdb(ispsoftc_t *, int);
static int isp_plogx(ispsoftc_t *, int, uint16_t, uint32_t, int);
static int isp_getpdb(ispsoftc_t *, int, uint16_t, isp_pdb_t *);
static int isp_gethandles(ispsoftc_t *, int, uint16_t *, int *, int);
static void isp_dump_chip_portdb(ispsoftc_t *, int);
static uint64_t isp_get_wwn(ispsoftc_t *, int, int, int);
static int isp_fclink_test(ispsoftc_t *, int, int);
static int isp_pdb_sync(ispsoftc_t *, int);
static int isp_scan_loop(ispsoftc_t *, int);
static int isp_gid_pt(ispsoftc_t *, int);
static int isp_scan_fabric(ispsoftc_t *, int);
static int isp_login_device(ispsoftc_t *, int, uint32_t, isp_pdb_t *, uint16_t *);
static int isp_register_fc4_type(ispsoftc_t *, int);
static int isp_register_fc4_features_24xx(ispsoftc_t *, int);
static int isp_register_port_name_24xx(ispsoftc_t *, int);
static int isp_register_node_name_24xx(ispsoftc_t *, int);
static uint16_t isp_next_handle(ispsoftc_t *, uint16_t *);
static int isp_fw_state(ispsoftc_t *, int);
static void isp_mboxcmd(ispsoftc_t *, mbreg_t *);
static void isp_get_flash_addrs(ispsoftc_t *);
static void isp_setdfltfcparm(ispsoftc_t *, int);
static int isp_read_flash_dword(ispsoftc_t *, uint32_t, uint32_t *);
static int isp_read_flash_data(ispsoftc_t *, uint32_t *, uint32_t, uint32_t);
static void isp_rd_2xxx_flash(ispsoftc_t *, uint32_t, uint32_t *);
static int isp_read_flthdr_2xxx(ispsoftc_t *);
static void isp_parse_flthdr_2xxx(ispsoftc_t *, uint8_t *);
static int isp_read_flt_2xxx(ispsoftc_t *);
static int isp_parse_flt_2xxx(ispsoftc_t *, uint8_t *);
static int isp_read_nvram(ispsoftc_t *);
static void isp_parse_nvram_2400(ispsoftc_t *, uint8_t *);
static void isp_print_image(ispsoftc_t *, char *, struct isp_image_status *);
static bool isp_check_aux_image_status_signature(struct isp_image_status *);
static bool isp_check_image_status_signature(struct isp_image_status *);
static unsigned long isp_image_status_checksum(struct isp_image_status *);
static void isp_component_status(struct active_regions *, struct isp_image_status *);
static int isp_compare_image_generation(ispsoftc_t *, struct isp_image_status *, struct isp_image_status *);
static void isp_get_aux_images(ispsoftc_t *, struct active_regions *);
static void isp_get_active_image(ispsoftc_t *, struct active_regions *);
static bool isp_risc_firmware_invalid(ispsoftc_t *, uint32_t *);
static int isp_load_ram(ispsoftc_t *, uint32_t *, uint32_t, uint32_t);
static int isp_load_risc_flash(ispsoftc_t *, uint32_t *, uint32_t);
static int isp_load_risc(ispsoftc_t *, uint32_t *);
static void
isp_change_fw_state(ispsoftc_t *isp, int chan, int state)
{
fcparam *fcp = FCPARAM(isp, chan);
if (fcp->isp_fwstate == state)
return;
isp_prt(isp, ISP_LOGCONFIG|ISP_LOG_SANCFG,
"Chan %d Firmware state <%s->%s>", chan,
isp_fc_fw_statename(fcp->isp_fwstate), isp_fc_fw_statename(state));
fcp->isp_fwstate = state;
}
static void
isp_get_flash_addrs(ispsoftc_t *isp)
{
fcparam *fcp = FCPARAM(isp, 0);
int r = 0;
if (IS_28XX(isp)) {
fcp->flash_data_addr = ISP28XX_BASE_ADDR;
fcp->flt_region_flt = ISP28XX_FLT_ADDR;
} else if (IS_26XX(isp)) {
fcp->flash_data_addr = ISP27XX_BASE_ADDR;
fcp->flt_region_flt = ISP27XX_FLT_ADDR;
} else if (IS_25XX(isp)) {
fcp->flash_data_addr = ISP25XX_BASE_ADDR;
fcp->flt_region_flt = ISP25XX_FLT_ADDR;
} else {
fcp->flash_data_addr = ISP24XX_BASE_ADDR;
fcp->flt_region_flt = ISP24XX_FLT_ADDR;
}
fcp->flt_length = 0;
r = isp_read_flthdr_2xxx(isp);
if (r == 0) {
isp_read_flt_2xxx(isp);
} else {
if (IS_28XX(isp)) {
fcp->flt_region_nvram = 0x300000;
} else if (IS_26XX(isp)) {
fcp->flash_data_addr = 0x7fe7c000;
fcp->flt_region_nvram = 0;
} else if (IS_25XX(isp)) {
fcp->flt_region_nvram = 0x48000;
} else {
fcp->flash_data_addr = 0x7ffe0000;
fcp->flt_region_nvram = 0;
}
fcp->flt_region_nvram += ISP2400_NVRAM_PORT_ADDR(isp->isp_port);
}
}
void
isp_reset(ispsoftc_t *isp, int do_load_defaults)
{
mbreg_t mbs;
char *buf;
uint16_t fwt;
uint32_t code_org, val;
int loaded_fw, loops, i, dodnld = 1;
const char *btype = "????";
static const char dcrc[] = "Downloaded RISC Code Checksum Failure";
isp->isp_state = ISP_NILSTATE;
ISP_DISABLE_INTS(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);
switch (isp->isp_type) {
case ISP_HA_FC_2400:
btype = "2422";
break;
case ISP_HA_FC_2500:
btype = "2532";
break;
case ISP_HA_FC_2600:
btype = "2600";
break;
case ISP_HA_FC_2700:
btype = "2700";
break;
case ISP_HA_FC_2800:
btype = "2800";
break;
default:
break;
}
ISP_WRITE(isp, BIU2400_CSR, BIU2400_DMA_STOP|(3 << 4));
for (loops = 0; loops < 100000; loops++) {
ISP_DELAY(10);
val = ISP_READ(isp, BIU2400_CSR);
if ((val & BIU2400_DMA_ACTIVE) == 0) {
break;
}
}
if (val & BIU2400_DMA_ACTIVE)
isp_prt(isp, ISP_LOGERR, "DMA Failed to Stop on Reset");
ISP_WRITE(isp, BIU2400_CSR, BIU2400_SOFT_RESET|BIU2400_DMA_STOP|(3 << 4));
ISP_DELAY(100);
for (loops = 0; loops < 10000; loops++) {
ISP_DELAY(5);
val = ISP_READ(isp, OUTMAILBOX0);
if (val != 0x4)
break;
}
switch (val) {
case 0x0:
break;
case 0x4:
isp_prt(isp, ISP_LOGERR, "The ROM code is busy after 50ms.");
return;
case 0xf:
isp_prt(isp, ISP_LOGERR, "Board configuration error.");
return;
default:
isp_prt(isp, ISP_LOGERR, "Unknown RISC Status Code 0x%x.", val);
return;
}
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);
for (loops = 0; loops < 10000; loops++) {
ISP_DELAY(5);
val = ISP_READ(isp, OUTMAILBOX0);
if (val != 0x4)
break;
}
switch (val) {
case 0x0:
break;
case 0x4:
isp_prt(isp, ISP_LOGERR, "The ROM code is busy after 50ms.");
return;
case 0xf:
isp_prt(isp, ISP_LOGERR, "Board configuration error.");
return;
default:
isp_prt(isp, ISP_LOGERR, "Unknown RISC Status Code 0x%x.", val);
return;
}
isp->isp_reqidx = isp->isp_reqodx = 0;
isp->isp_resodx = 0;
isp->isp_atioodx = 0;
ISP_WRITE(isp, BIU2400_REQINP, 0);
ISP_WRITE(isp, BIU2400_REQOUTP, 0);
ISP_WRITE(isp, BIU2400_RSPINP, 0);
ISP_WRITE(isp, BIU2400_RSPOUTP, 0);
if (!IS_26XX(isp)) {
ISP_WRITE(isp, BIU2400_PRI_REQINP, 0);
ISP_WRITE(isp, BIU2400_PRI_REQOUTP, 0);
}
ISP_WRITE(isp, BIU2400_ATIO_RSPINP, 0);
ISP_WRITE(isp, BIU2400_ATIO_RSPOUTP, 0);
MBSINIT(&mbs, MBOX_NO_OP, MBLOGALL, 0);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "NOP command failed (%x)", mbs.param[0]);
return;
}
{
static const uint16_t patterns[MAX_MAILBOX] = {
0x0000, 0xdead, 0xbeef, 0xffff,
0xa5a5, 0x5a5a, 0x7f7f, 0x7ff7,
0x3421, 0xabcd, 0xdcba, 0xfeef,
0xbead, 0xdebe, 0x2222, 0x3333,
0x5555, 0x6666, 0x7777, 0xaaaa,
0xffff, 0xdddd, 0x9999, 0x1fbc,
0x6666, 0x6677, 0x1122, 0x33ff,
0x0000, 0x0001, 0x1000, 0x1010,
};
int nmbox = ISP_NMBOX(isp);
MBSINIT(&mbs, MBOX_MAILBOX_REG_TEST, MBLOGALL, 0);
for (i = 1; i < nmbox; i++) {
mbs.param[i] = patterns[i];
}
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
for (i = 1; i < nmbox; i++) {
if (mbs.param[i] != patterns[i]) {
isp_prt(isp, ISP_LOGERR, "Register Test Failed at Register %d: should have 0x%04x but got 0x%04x", i, patterns[i], mbs.param[i]);
return;
}
}
}
if (ISP_MBOXDMASETUP(isp) != 0) {
isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
return;
}
fcparam *fcp = FCPARAM(isp, 0);
isp_get_flash_addrs(isp);
snprintf(fcp->fw_version_ispfw, sizeof(fcp->fw_version_ispfw),
"not loaded");
snprintf(fcp->fw_version_flash, sizeof(fcp->fw_version_flash),
"not loaded");
snprintf(fcp->fw_version_run, sizeof(fcp->fw_version_run),
"not loaded");
if (!(isp->isp_confopts & ISP_CFG_NORELOAD)) {
char fwname[32];
snprintf(fwname, sizeof(fwname), "isp_%04x", isp->isp_did);
isp->isp_osinfo.ispfw = firmware_get(fwname);
if (isp->isp_osinfo.ispfw != NULL) {
isp->isp_mdvec->dv_ispfw = isp->isp_osinfo.ispfw->data;
const uint32_t *ispfwptr = isp->isp_mdvec->dv_ispfw;
for (i = 0; i < 4; i++)
fcp->fw_ispfwrev[i] = ispfwptr[4 + i];
isp_prt(isp, ISP_LOGCONFIG,
"Loaded ispfw(4) firmware %s", fwname);
snprintf(fcp->fw_version_ispfw,
sizeof(fcp->fw_version_ispfw),
"%u.%u.%u", fcp->fw_ispfwrev[0],
fcp->fw_ispfwrev[1], fcp->fw_ispfwrev[2]);
isp_prt(isp, ISP_LOGCONFIG,
"Firmware revision (ispfw) %u.%u.%u (%x).",
fcp->fw_ispfwrev[0], fcp->fw_ispfwrev[1],
fcp->fw_ispfwrev[2], fcp->fw_ispfwrev[3]);
} else {
isp_prt(isp, ISP_LOGDEBUG0,
"Unable to load ispfw(4) firmware %s", fwname);
}
}
loaded_fw = 0;
dodnld = 0;
if (IS_27XX(isp)) {
switch (isp_load_risc(isp, 0)) {
case ISP_ABORTED:
dodnld = 1;
break;
case ISP_SUCCESS:
loaded_fw = 1;
break;
default:
if (isp->isp_osinfo.ispfw != NULL)
dodnld = 1;
break;
}
} else {
if (isp->isp_osinfo.ispfw != NULL)
dodnld = 1;
}
code_org = ISP_CODE_ORG_2400;
if (dodnld) {
const uint32_t *ptr = isp->isp_mdvec->dv_ispfw;
uint32_t la, wi, wl;
code_org = ptr[2];
for (;;) {
isp_prt(isp, ISP_LOGDEBUG2,
"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]) {
uint32_t *cp;
uint32_t nw;
nw = min(wl, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) / 4);
cp = isp->isp_rquest;
for (i = 0; i < nw; i++)
ISP_IOXPUT_32(isp, ptr[wi + i], &cp[i]);
if (isp_load_ram(isp, cp, la, nw) != 0) {
isp_prt(isp, ISP_LOGERR,
"Failed to load firmware fragment.");
return;
}
la += nw;
wi += nw;
wl -= nw;
}
if (ptr[1] == 0) {
break;
}
ptr += ptr[3];
}
loaded_fw = 1;
if (isp->isp_osinfo.ispfw != NULL)
firmware_put(isp->isp_osinfo.ispfw, FIRMWARE_UNLOAD);
} else {
isp_prt(isp, ISP_LOGCONFIG,
"Skipping ispfw(4) firmware download");
}
if (loaded_fw) {
MBSINIT(&mbs, MBOX_VERIFY_CHECKSUM, MBLOGNONE, 0);
mbs.param[1] = code_org >> 16;
mbs.param[2] = code_org;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "%s: 0x%x", dcrc,
(mbs.param[2] << 16 | mbs.param[1]));
return;
}
} else if (IS_26XX(isp)) {
isp_prt(isp, ISP_LOGCONFIG,
"Instruct RISC to load firmware from flash by itself");
MBSINIT(&mbs, MBOX_LOAD_FLASH_FIRMWARE, MBLOGALL, 5000000);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "Flash F/W load failed");
return;
}
}
MBSINIT(&mbs, MBOX_EXEC_FIRMWARE, MBLOGALL, 5000000);
mbs.param[1] = code_org >> 16;
mbs.param[2] = code_org;
if (!IS_26XX(isp))
mbs.param[3] = loaded_fw ? 0 : 1;
mbs.param[4] = 0;
if (IS_27XX(isp))
mbs.param[4] |= 0x08;
mbs.param[11] = 0;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE)
return;
fcp->fw_ability_mask = (mbs.param[3] << 16) | mbs.param[2];
isp_prt(isp, ISP_LOGDEBUG0, "Firmware ability mask: 0x%x",
fcp->fw_ability_mask);
if (IS_26XX(isp)) {
fcp->max_supported_speed = mbs.param[2] & (0x1 | 0x2);
isp_prt(isp, ISP_LOGINFO, "Maximum supported speed: %s",
fcp->max_supported_speed == 0 ? "16Gbit/s" :
fcp->max_supported_speed == 1 ? "32Gbit/s" :
fcp->max_supported_speed == 2 ? "64Gbit/s" : "unknown");
}
if (IS_28XX(isp) && (mbs.param[5] & 0x400)) {
isp_prt(isp, ISP_LOGINFO,
"HW supports EDIF (Encryption of data in flight)");
}
MBSINIT(&mbs, MBOX_ABOUT_FIRMWARE, MBLOGALL, 5000000);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
isp->isp_fwrev[0] = mbs.param[1];
isp->isp_fwrev[1] = mbs.param[2];
isp->isp_fwrev[2] = mbs.param[3];
isp->isp_fwattr = mbs.param[6];
isp->isp_fwattr_h = mbs.param[15];
if (isp->isp_fwattr & ISP_FW_ATTR_EXTNDED) {
isp->isp_fwattr_ext[0] = mbs.param[16];
isp->isp_fwattr_ext[1] = mbs.param[17];
}
isp_prt(isp, ISP_LOGCONFIG, "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]);
snprintf(fcp->fw_version_run, sizeof(fcp->fw_version_run),
"%u.%u.%u", isp->isp_fwrev[0], isp->isp_fwrev[1],
isp->isp_fwrev[2]);
if (!dodnld && !IS_26XX(isp))
snprintf(fcp->fw_version_flash, sizeof(fcp->fw_version_flash),
"%s", fcp->fw_version_run);
fwt = isp->isp_fwattr;
buf = FCPARAM(isp, 0)->isp_scanscratch;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN, "FW Attributes Lower:");
if (fwt & ISP_FW_ATTR_CLASS2) {
fwt ^= ISP_FW_ATTR_CLASS2;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s Class2", buf);
}
if (fwt & ISP_FW_ATTR_IP) {
fwt ^= ISP_FW_ATTR_IP;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s IP", buf);
}
if (fwt & ISP_FW_ATTR_MULTIID) {
fwt ^= ISP_FW_ATTR_MULTIID;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s MultiID", buf);
}
if (fwt & ISP_FW_ATTR_SB2) {
fwt ^= ISP_FW_ATTR_SB2;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s SB2", buf);
}
if (fwt & ISP_FW_ATTR_T10CRC) {
fwt ^= ISP_FW_ATTR_T10CRC;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s T10CRC", buf);
}
if (fwt & ISP_FW_ATTR_VI) {
fwt ^= ISP_FW_ATTR_VI;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s VI", buf);
}
if (fwt & ISP_FW_ATTR_MQ) {
fwt ^= ISP_FW_ATTR_MQ;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s MQ", buf);
}
if (fwt & ISP_FW_ATTR_MSIX) {
fwt ^= ISP_FW_ATTR_MSIX;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s MSIX", buf);
}
if (fwt & ISP_FW_ATTR_FCOE) {
fwt ^= ISP_FW_ATTR_FCOE;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s FCOE", buf);
}
if (fwt & ISP_FW_ATTR_VP0) {
fwt ^= ISP_FW_ATTR_VP0;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s VP0_Decoupling", buf);
}
if (fwt & ISP_FW_ATTR_EXPFW) {
fwt ^= ISP_FW_ATTR_EXPFW;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s (Experimental)", buf);
}
if (fwt & ISP_FW_ATTR_HOTFW) {
fwt ^= ISP_FW_ATTR_HOTFW;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s HotFW", buf);
}
fwt &= ~ISP_FW_ATTR_EXTNDED;
if (fwt) {
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf),
"%s (unknown 0x%04x)", buf, fwt);
}
isp_prt(isp, ISP_LOGCONFIG, "%s", buf);
fwt = isp->isp_fwattr_h;
buf = FCPARAM(isp, 0)->isp_scanscratch;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN, "FW Attributes Upper:");
if (fwt & ISP_FW_ATTR_H_EXTVP) {
fwt ^= ISP_FW_ATTR_H_EXTVP;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s ExtVP", buf);
}
if (fwt & ISP_FW_ATTR_H_VN2VN) {
fwt ^= ISP_FW_ATTR_H_VN2VN;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s VN2VN", buf);
}
if (fwt & ISP_FW_ATTR_H_EXMOFF) {
fwt ^= ISP_FW_ATTR_H_EXMOFF;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s EXMOFF", buf);
}
if (fwt & ISP_FW_ATTR_H_NPMOFF) {
fwt ^= ISP_FW_ATTR_H_NPMOFF;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s NPMOFF", buf);
}
if (fwt & ISP_FW_ATTR_H_DIFCHOP) {
fwt ^= ISP_FW_ATTR_H_DIFCHOP;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s DIFCHOP", buf);
}
if (fwt & ISP_FW_ATTR_H_SRIOV) {
fwt ^= ISP_FW_ATTR_H_SRIOV;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s SRIOV", buf);
}
if (fwt & ISP_FW_ATTR_H_NVME) {
fwt ^= ISP_FW_ATTR_H_NVME;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s NVMe", buf);
}
if (fwt & ISP_FW_ATTR_H_NVME_UP) {
fwt ^= ISP_FW_ATTR_H_NVME_UP;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s NVMe(updated)", buf);
}
if (fwt & (ISP_FW_ATTR_H_NVME_FB)) {
fwt ^= (ISP_FW_ATTR_H_NVME_FB);
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s NVMe(first burst)", buf);
}
if (fwt) {
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf),
"%s (unknown 0x%04x)", buf, fwt);
}
isp_prt(isp, ISP_LOGCONFIG, "%s", buf);
fwt = isp->isp_fwattr_ext[0];
buf = FCPARAM(isp, 0)->isp_scanscratch;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN, "FW Ext. Attributes Lower:");
if (fwt & ISP_FW_ATTR_E0_ASICTMP) {
fwt ^= ISP_FW_ATTR_E0_ASICTMP;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s ASICTMP", buf);
}
if (fwt & ISP_FW_ATTR_E0_ATIOMQ) {
fwt ^= ISP_FW_ATTR_E0_ATIOMQ;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s ATIOMQ", buf);
}
if (fwt & ISP_FW_ATTR_E0_EDIF) {
fwt ^= ISP_FW_ATTR_E0_EDIF;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s EDIF", buf);
}
if (fwt & ISP_FW_ATTR_E0_SCM) {
fwt ^= ISP_FW_ATTR_E0_SCM;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s SCM", buf);
}
if (fwt & ISP_FW_ATTR_E0_NVME2) {
fwt ^= ISP_FW_ATTR_E0_NVME2;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf), "%s NVMe-2", buf);
}
if (fwt) {
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf),
"%s (unknown 0x%04x)", buf, fwt);
}
isp_prt(isp, ISP_LOGCONFIG, "%s", buf);
fwt = isp->isp_fwattr_ext[1];
buf = FCPARAM(isp, 0)->isp_scanscratch;
ISP_SNPRINTF(buf, ISP_FC_SCRLEN, "FW Ext. Attributes Upper:");
if (fwt) {
ISP_SNPRINTF(buf, ISP_FC_SCRLEN - strlen(buf),
"%s (unknown 0x%04x)", buf, fwt);
}
isp_prt(isp, ISP_LOGCONFIG, "%s", buf);
MBSINIT(&mbs, MBOX_GET_RESOURCE_COUNT, MBLOGALL, 0);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE)
return;
isp->isp_maxcmds = MIN(mbs.param[3], ISP_HANDLE_MAX - ISP_HANDLE_RESERVE);
isp_prt(isp, ISP_LOGCONFIG, "%d max I/O command limit set", isp->isp_maxcmds);
if (isp->isp_nchan > 1) {
if (!ISP_CAP_MULTI_ID(isp)) {
isp_prt(isp, ISP_LOGWARN, "non-MULTIID f/w loaded, "
"only can enable 1 of %d channels", isp->isp_nchan);
isp->isp_nchan = 1;
} else if (!ISP_CAP_VP0(isp)) {
isp_prt(isp, ISP_LOGWARN, "We can not use MULTIID "
"feature properly without VP0_Decoupling");
isp->isp_nchan = 1;
}
}
if (ISP_MBOXDMASETUP(isp) != 0) {
isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
return;
}
if (ISP_IRQSETUP(isp) != 0) {
isp_prt(isp, ISP_LOGERR, "Cannot setup IRQ");
return;
}
ISP_ENABLE_INTS(isp);
for (i = 0; i < isp->isp_nchan; i++)
isp_change_fw_state(isp, i, FW_CONFIG_WAIT);
isp->isp_state = ISP_RESETSTATE;
if (do_load_defaults) {
for (i = 0; i < isp->isp_nchan; i++)
isp_setdfltfcparm(isp, i);
}
}
static int
isp_stop(ispsoftc_t *isp)
{
mbreg_t mbs;
isp->isp_state = ISP_NILSTATE;
MBSINIT(&mbs, MBOX_STOP_FIRMWARE, MBLOGALL, 500000);
mbs.param[1] = 0;
mbs.param[2] = 0;
mbs.param[3] = 0;
mbs.param[4] = 0;
mbs.param[5] = 0;
mbs.param[6] = 0;
mbs.param[7] = 0;
mbs.param[8] = 0;
isp_mboxcmd(isp, &mbs);
return (mbs.param[0] == MBOX_COMMAND_COMPLETE ? 0 : mbs.param[0]);
}
void
isp_shutdown(ispsoftc_t *isp)
{
if (isp->isp_state >= ISP_RESETSTATE)
isp_stop(isp);
ISP_DISABLE_INTS(isp);
ISP_WRITE(isp, BIU2400_ICR, 0);
ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_PAUSE);
}
void
isp_init(ispsoftc_t *isp)
{
fcparam *fcp;
isp_icb_2400_t local, *icbp = &local;
mbreg_t mbs;
int chan;
int ownloopid = 0;
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_LOG_WARN1, "all %d channels with role 'none'", chan);
return;
}
isp->isp_state = ISP_INITSTATE;
fcp = FCPARAM(isp, 0);
MBSINIT(&mbs, MBOX_SET_FIRMWARE_OPTIONS, MBLOGALL, 0);
mbs.param[1] = 1;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
ISP_MEMZERO(icbp, sizeof (*icbp));
icbp->icb_fwoptions1 = fcp->isp_fwoptions;
icbp->icb_fwoptions2 = fcp->isp_xfwoptions;
icbp->icb_fwoptions3 = fcp->isp_zfwoptions;
if (isp->isp_nchan > 1 && ISP_CAP_VP0(isp)) {
icbp->icb_fwoptions1 &= ~ICB2400_OPT1_INI_DISABLE;
icbp->icb_fwoptions1 |= ICB2400_OPT1_TGT_ENABLE;
} else {
if (fcp->role & ISP_ROLE_TARGET)
icbp->icb_fwoptions1 |= ICB2400_OPT1_TGT_ENABLE;
else
icbp->icb_fwoptions1 &= ~ICB2400_OPT1_TGT_ENABLE;
if (fcp->role & ISP_ROLE_INITIATOR)
icbp->icb_fwoptions1 &= ~ICB2400_OPT1_INI_DISABLE;
else
icbp->icb_fwoptions1 |= ICB2400_OPT1_INI_DISABLE;
}
icbp->icb_version = ICB_VERSION1;
icbp->icb_maxfrmlen = DEFAULT_FRAMESIZE(isp);
if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) {
icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN;
if (IS_28XX(isp))
icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN_28XX;
isp_prt(isp, ISP_LOGERR,
"bad frame length (%d) from NVRAM - using %d",
DEFAULT_FRAMESIZE(isp), icbp->icb_maxfrmlen);
}
if (!IS_26XX(isp))
icbp->icb_execthrottle = 0xffff;
#ifdef ISP_TARGET_MODE
if (icbp->icb_fwoptions1 & ICB2400_OPT1_TGT_ENABLE) {
if ((icbp->icb_fwoptions1 & ICB2400_OPT1_INI_DISABLE) == 0)
icbp->icb_xchgcnt = MIN(isp->isp_maxcmds / 2, ATPDPSIZE);
else
icbp->icb_xchgcnt = isp->isp_maxcmds;
}
#endif
ownloopid = (isp->isp_confopts & ISP_CFG_OWNLOOPID) != 0;
icbp->icb_hardaddr = fcp->isp_loopid;
if (icbp->icb_hardaddr >= LOCAL_LOOP_LIM) {
icbp->icb_hardaddr = 0;
ownloopid = 0;
}
if (ownloopid)
icbp->icb_fwoptions1 |= ICB2400_OPT1_HARD_ADDRESS;
if (isp->isp_confopts & ISP_CFG_NOFCTAPE) {
icbp->icb_fwoptions2 &= ~ICB2400_OPT2_FCTAPE;
}
if (isp->isp_confopts & ISP_CFG_FCTAPE) {
icbp->icb_fwoptions2 |= ICB2400_OPT2_FCTAPE;
}
for (chan = 0; chan < isp->isp_nchan; chan++) {
if (icbp->icb_fwoptions2 & ICB2400_OPT2_FCTAPE)
FCPARAM(isp, chan)->fctape_enabled = 1;
else
FCPARAM(isp, chan)->fctape_enabled = 0;
}
switch (isp->isp_confopts & ISP_CFG_PORT_PREF) {
case ISP_CFG_LPORT_ONLY:
icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_ONLY;
break;
case ISP_CFG_NPORT_ONLY:
icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
icbp->icb_fwoptions2 |= ICB2400_OPT2_PTP_ONLY;
break;
case ISP_CFG_NPORT:
case ISP_CFG_LPORT:
icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_2_PTP;
break;
default:
switch (icbp->icb_fwoptions2 & ICB2400_OPT2_TOPO_MASK) {
case ICB2400_OPT2_LOOP_ONLY:
case ICB2400_OPT2_PTP_ONLY:
case ICB2400_OPT2_LOOP_2_PTP:
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;
}
if (IS_26XX(isp)) {
icbp->icb_fwoptions2 |= ICB2400_OPT2_ENA_IHR;
icbp->icb_fwoptions2 |= ICB2400_OPT2_ENA_IHA;
}
if ((icbp->icb_fwoptions3 & ICB2400_OPT3_RSPSZ_MASK) == 0) {
icbp->icb_fwoptions3 |= ICB2400_OPT3_RSPSZ_24;
}
if (isp->isp_confopts & ISP_CFG_1GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_1GB;
} else if (isp->isp_confopts & ISP_CFG_2GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_2GB;
} else if (isp->isp_confopts & ISP_CFG_4GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_4GB;
} else if (isp->isp_confopts & ISP_CFG_8GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_8GB;
} else if (isp->isp_confopts & ISP_CFG_16GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_16GB;
} else if (isp->isp_confopts & ISP_CFG_32GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_32GB;
} else if (isp->isp_confopts & ISP_CFG_64GB) {
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_64GB;
} else {
switch (icbp->icb_fwoptions3 & ICB2400_OPT3_RATE_MASK) {
case ICB2400_OPT3_RATE_4GB:
case ICB2400_OPT3_RATE_8GB:
case ICB2400_OPT3_RATE_16GB:
case ICB2400_OPT3_RATE_32GB:
case ICB2400_OPT3_RATE_64GB:
case ICB2400_OPT3_RATE_AUTO:
break;
case ICB2400_OPT3_RATE_2GB:
if (isp->isp_type <= ISP_HA_FC_2500)
break;
case ICB2400_OPT3_RATE_1GB:
if (isp->isp_type <= ISP_HA_FC_2400)
break;
default:
icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_MASK;
icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_AUTO;
break;
}
}
if (ownloopid == 0) {
icbp->icb_fwoptions3 |= ICB2400_OPT3_SOFTID;
}
icbp->icb_logintime = ICB_LOGIN_TOV;
if (fcp->isp_wwnn && fcp->isp_wwpn) {
icbp->icb_fwoptions1 |= ICB2400_OPT1_BOTH_WWNS;
MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, fcp->isp_wwnn);
isp_prt(isp, ISP_LOGDEBUG1, "Setting ICB Node 0x%08x%08x Port 0x%08x%08x", ((uint32_t) (fcp->isp_wwnn >> 32)), ((uint32_t) (fcp->isp_wwnn)),
((uint32_t) (fcp->isp_wwpn >> 32)), ((uint32_t) (fcp->isp_wwpn)));
} else if (fcp->isp_wwpn) {
icbp->icb_fwoptions1 &= ~ICB2400_OPT1_BOTH_WWNS;
MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
isp_prt(isp, ISP_LOGDEBUG1, "Setting ICB Node to be same as Port 0x%08x%08x", ((uint32_t) (fcp->isp_wwpn >> 32)), ((uint32_t) (fcp->isp_wwpn)));
} else {
isp_prt(isp, ISP_LOGERR, "No valid WWNs to use");
return;
}
icbp->icb_rspnsin = isp->isp_resodx;
icbp->icb_rqstout = isp->isp_reqidx;
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
icbp->icb_atio_in = isp->isp_atioodx;
icbp->icb_atioqlen = ATIO_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_init: 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
if (ISP_CAP_MSIX(isp) && isp->isp_nirq >= 2) {
icbp->icb_msixresp = 1;
if (IS_26XX(isp) && isp->isp_nirq >= 3)
icbp->icb_msixatio = 2;
}
isp_prt(isp, ISP_LOGDEBUG0, "isp_init: fwopt1 0x%x fwopt2 0x%x fwopt3 0x%x", icbp->icb_fwoptions1, icbp->icb_fwoptions2, icbp->icb_fwoptions3);
isp_prt(isp, ISP_LOGDEBUG0, "isp_init: 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 (FC_SCRATCH_ACQUIRE(isp, 0)) {
isp_prt(isp, ISP_LOGERR, sacq);
return;
}
ISP_MEMZERO(fcp->isp_scratch, ISP_FC_SCRLEN);
isp_put_icb_2400(isp, icbp, fcp->isp_scratch);
if (isp->isp_dblev & ISP_LOGDEBUG1) {
isp_print_bytes(isp, "isp_init",
sizeof (*icbp), fcp->isp_scratch);
}
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_global_options = ICB2400_VPGOPT_GEN_RIDA;
if (ISP_CAP_VP0(isp)) {
vpinfo.vp_global_options |= ICB2400_VPGOPT_VP0_DECOUPLE;
vpinfo.vp_count = isp->isp_nchan;
chan = 0;
} else {
vpinfo.vp_count = isp->isp_nchan - 1;
chan = 1;
}
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 < 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 |
ICB2400_VPOPT_ENA_SNSLOGIN;
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;
if (fcp2->isp_loopid < LOCAL_LOOP_LIM) {
pi.vp_port_loopid = fcp2->isp_loopid;
if (isp->isp_confopts & ISP_CFG_OWNLOOPID)
pi.vp_port_options |= ICB2400_VPOPT_HARD_ADDRESS;
}
}
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;
if (ISP_CAP_VP0(isp))
off += ICB2400_VPINFO_PORT_OFF(chan);
else
off += ICB2400_VPINFO_PORT_OFF(chan - 1);
pdst = (vp_port_info_t *) off;
isp_put_vp_port_info(isp, &pi, pdst);
amt += ICB2400_VPOPT_WRITE_SIZE;
}
if (isp->isp_dblev & ISP_LOGDEBUG1) {
isp_print_bytes(isp, "isp_init",
amt - ICB2400_VPINFO_OFF,
(char *)fcp->isp_scratch + ICB2400_VPINFO_OFF);
}
}
MBSINIT(&mbs, 0, MBLOGALL, 30000000);
if (isp->isp_nchan > 1) {
mbs.param[0] = MBOX_INIT_FIRMWARE_MULTI_ID;
} else {
mbs.param[0] = MBOX_INIT_FIRMWARE;
}
mbs.param[1] = 0;
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);
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), 0);
isp_mboxcmd(isp, &mbs);
FC_SCRATCH_RELEASE(isp, 0);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return;
}
isp->isp_state = ISP_RUNSTATE;
}
static int
isp_fc_enable_vp(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
vp_modify_t vp;
int retval;
ISP_MEMZERO(&vp, sizeof(vp));
vp.vp_mod_hdr.rqs_entry_type = RQSTYPE_VP_MODIFY;
vp.vp_mod_hdr.rqs_entry_count = 1;
vp.vp_mod_cnt = 1;
vp.vp_mod_idx0 = chan;
vp.vp_mod_cmd = VP_MODIFY_ENA;
vp.vp_mod_ports[0].options = ICB2400_VPOPT_ENABLED |
ICB2400_VPOPT_ENA_SNSLOGIN;
if (fcp->role & ISP_ROLE_INITIATOR)
vp.vp_mod_ports[0].options |= ICB2400_VPOPT_INI_ENABLE;
if ((fcp->role & ISP_ROLE_TARGET) == 0)
vp.vp_mod_ports[0].options |= ICB2400_VPOPT_TGT_DISABLE;
if (fcp->isp_loopid < LOCAL_LOOP_LIM) {
vp.vp_mod_ports[0].loopid = fcp->isp_loopid;
if (isp->isp_confopts & ISP_CFG_OWNLOOPID)
vp.vp_mod_ports[0].options |= ICB2400_VPOPT_HARD_ADDRESS;
}
MAKE_NODE_NAME_FROM_WWN(vp.vp_mod_ports[0].wwpn, fcp->isp_wwpn);
MAKE_NODE_NAME_FROM_WWN(vp.vp_mod_ports[0].wwnn, fcp->isp_wwnn);
retval = isp_exec_entry_queue(isp, &vp, &vp, 5);
if (retval != 0) {
isp_prt(isp, ISP_LOGERR, "%s: VP_MODIFY of chan %d error %d",
__func__, chan, retval);
return (retval);
}
if (vp.vp_mod_hdr.rqs_flags != 0 || vp.vp_mod_status != VP_STS_OK) {
isp_prt(isp, ISP_LOGERR,
"%s: VP_MODIFY of Chan %d failed with flags %x status %d",
__func__, chan, vp.vp_mod_hdr.rqs_flags, vp.vp_mod_status);
return (EIO);
}
return (0);
}
static int
isp_fc_disable_vp(ispsoftc_t *isp, int chan)
{
vp_ctrl_info_t vp;
int retval;
ISP_MEMZERO(&vp, sizeof(vp));
vp.vp_ctrl_hdr.rqs_entry_type = RQSTYPE_VP_CTRL;
vp.vp_ctrl_hdr.rqs_entry_count = 1;
if (ISP_CAP_VP0(isp)) {
vp.vp_ctrl_status = 1;
} else {
vp.vp_ctrl_status = 0;
chan--;
}
vp.vp_ctrl_command = VP_CTRL_CMD_DISABLE_VP_LOGO_ALL;
vp.vp_ctrl_vp_count = 1;
vp.vp_ctrl_idmap[chan / 16] |= (1 << chan % 16);
retval = isp_exec_entry_queue(isp, &vp, &vp, 5);
if (retval != 0) {
isp_prt(isp, ISP_LOGERR, "%s: VP_CTRL of chan %d error %d",
__func__, chan, retval);
return (retval);
}
if (vp.vp_ctrl_hdr.rqs_flags != 0 || vp.vp_ctrl_status != 0) {
isp_prt(isp, ISP_LOGERR,
"%s: VP_CTRL of Chan %d failed with flags %x status %d %d",
__func__, chan, vp.vp_ctrl_hdr.rqs_flags,
vp.vp_ctrl_status, vp.vp_ctrl_index_fail);
return (EIO);
}
return (0);
}
static int
isp_fc_change_role(ispsoftc_t *isp, int chan, int new_role)
{
fcparam *fcp = FCPARAM(isp, chan);
int i, was, res = 0;
if (chan >= isp->isp_nchan) {
isp_prt(isp, ISP_LOGWARN, "%s: bad channel %d", __func__, chan);
return (ENXIO);
}
if (fcp->role == new_role)
return (0);
for (was = 0, i = 0; i < isp->isp_nchan; i++) {
if (FCPARAM(isp, i)->role != ISP_ROLE_NONE)
was++;
}
if (was == 0 || (was == 1 && fcp->role != ISP_ROLE_NONE)) {
fcp->role = new_role;
return (isp_reinit(isp, 0));
}
if (fcp->role != ISP_ROLE_NONE) {
res = isp_fc_disable_vp(isp, chan);
isp_clear_portdb(isp, chan);
}
fcp->role = new_role;
if (fcp->role != ISP_ROLE_NONE)
res = isp_fc_enable_vp(isp, chan);
return (res);
}
static void
isp_clear_portdb(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
fcportdb_t *lp;
int i;
for (i = 0; i < MAX_FC_TARG; i++) {
lp = &fcp->portdb[i];
switch (lp->state) {
case FC_PORTDB_STATE_DEAD:
case FC_PORTDB_STATE_CHANGED:
case FC_PORTDB_STATE_VALID:
lp->state = FC_PORTDB_STATE_NIL;
isp_async(isp, ISPASYNC_DEV_GONE, chan, lp);
break;
case FC_PORTDB_STATE_NIL:
case FC_PORTDB_STATE_NEW:
lp->state = FC_PORTDB_STATE_NIL;
break;
case FC_PORTDB_STATE_ZOMBIE:
break;
default:
panic("Don't know how to clear state %d\n", lp->state);
}
}
}
static void
isp_mark_portdb(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
fcportdb_t *lp;
int i;
for (i = 0; i < MAX_FC_TARG; i++) {
lp = &fcp->portdb[i];
if (lp->state == FC_PORTDB_STATE_NIL)
continue;
if (lp->portid >= DOMAIN_CONTROLLER_BASE &&
lp->portid <= DOMAIN_CONTROLLER_END)
continue;
fcp->portdb[i].probational = 1;
}
}
static int
isp_plogx(ispsoftc_t *isp, int chan, uint16_t handle, uint32_t portid, int flags)
{
isp_plogx_t pl;
uint32_t sst, parm1;
int retval, lev;
const char *msg;
char buf[64];
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d PLOGX %s PortID 0x%06x nphdl 0x%x",
chan, (flags & PLOGX_FLG_CMD_MASK) == PLOGX_FLG_CMD_PLOGI ?
"Login":"Logout", portid, handle);
ISP_MEMZERO(&pl, sizeof(pl));
pl.plogx_header.rqs_entry_count = 1;
pl.plogx_header.rqs_entry_type = RQSTYPE_LOGIN;
pl.plogx_nphdl = handle;
pl.plogx_vphdl = chan;
pl.plogx_portlo = portid;
pl.plogx_rspsz_porthi = (portid >> 16) & 0xff;
pl.plogx_flags = flags;
retval = isp_exec_entry_queue(isp, &pl, &pl, 3 * ICB_LOGIN_TOV);
if (retval != 0) {
isp_prt(isp, ISP_LOGERR, "%s: PLOGX of chan %d error %d",
__func__, chan, retval);
return (retval);
}
if (pl.plogx_status == PLOGX_STATUS_OK) {
return (0);
} else if (pl.plogx_status != PLOGX_STATUS_IOCBERR) {
isp_prt(isp, ISP_LOGWARN,
"status 0x%x on port login IOCB channel %d",
pl.plogx_status, chan);
return (-1);
}
sst = pl.plogx_ioparm[0].lo16 | (pl.plogx_ioparm[0].hi16 << 16);
parm1 = pl.plogx_ioparm[1].lo16 | (pl.plogx_ioparm[1].hi16 << 16);
retval = -1;
lev = ISP_LOGERR;
msg = NULL;
switch (sst) {
case PLOGX_IOCBERR_NOLINK:
msg = "no link";
break;
case PLOGX_IOCBERR_NOIOCB:
msg = "no IOCB buffer";
break;
case PLOGX_IOCBERR_NOXGHG:
msg = "no Exchange Control Block";
break;
case PLOGX_IOCBERR_FAILED:
ISP_SNPRINTF(buf, sizeof (buf), "reason 0x%x (last LOGIN state 0x%x)", parm1 & 0xff, (parm1 >> 8) & 0xff);
msg = buf;
break;
case PLOGX_IOCBERR_NOFABRIC:
msg = "no fabric";
break;
case PLOGX_IOCBERR_NOTREADY:
msg = "firmware not ready";
break;
case PLOGX_IOCBERR_NOLOGIN:
ISP_SNPRINTF(buf, sizeof (buf), "not logged in (last state 0x%x)", parm1);
msg = buf;
retval = MBOX_NOT_LOGGED_IN;
break;
case PLOGX_IOCBERR_REJECT:
ISP_SNPRINTF(buf, sizeof (buf), "LS_RJT = 0x%x", parm1);
msg = buf;
break;
case PLOGX_IOCBERR_NOPCB:
msg = "no PCB allocated";
break;
case PLOGX_IOCBERR_EINVAL:
ISP_SNPRINTF(buf, sizeof (buf), "invalid parameter at offset 0x%x", parm1);
msg = buf;
break;
case PLOGX_IOCBERR_PORTUSED:
lev = ISP_LOG_SANCFG|ISP_LOG_WARN1;
ISP_SNPRINTF(buf, sizeof (buf), "already logged in with N-Port handle 0x%x", parm1);
msg = buf;
retval = MBOX_PORT_ID_USED | (parm1 << 16);
break;
case PLOGX_IOCBERR_HNDLUSED:
lev = ISP_LOG_SANCFG|ISP_LOG_WARN1;
ISP_SNPRINTF(buf, sizeof (buf), "handle already used for PortID 0x%06x", parm1);
msg = buf;
retval = MBOX_LOOP_ID_USED;
break;
case PLOGX_IOCBERR_NOHANDLE:
msg = "no handle allocated";
break;
case PLOGX_IOCBERR_NOFLOGI:
msg = "no FLOGI_ACC";
break;
default:
ISP_SNPRINTF(buf, sizeof (buf), "status %x from %x", pl.plogx_status, flags);
msg = buf;
break;
}
if (msg) {
isp_prt(isp, lev, "Chan %d PLOGX PortID 0x%06x to N-Port handle 0x%x: %s",
chan, portid, handle, msg);
}
return (retval);
}
static int
isp_getpdb(ispsoftc_t *isp, int chan, uint16_t id, isp_pdb_t *pdb)
{
mbreg_t mbs;
union {
isp_pdb_24xx_t bill;
} un;
MBSINIT(&mbs, MBOX_GET_PORT_DB,
MBLOGALL & ~MBLOGMASK(MBOX_COMMAND_PARAM_ERROR), 250000);
mbs.ibits = (1 << 9)|(1 << 10);
mbs.param[1] = id;
mbs.param[2] = DMA_WD1(isp->isp_iocb_dma);
mbs.param[3] = DMA_WD0(isp->isp_iocb_dma);
mbs.param[6] = DMA_WD3(isp->isp_iocb_dma);
mbs.param[7] = DMA_WD2(isp->isp_iocb_dma);
mbs.param[9] = chan;
MEMORYBARRIER(isp, SYNC_IFORDEV, 0, sizeof(un), chan);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE)
return (mbs.param[0] | (mbs.param[1] << 16));
MEMORYBARRIER(isp, SYNC_IFORCPU, 0, sizeof(un), chan);
isp_get_pdb_24xx(isp, isp->isp_iocb, &un.bill);
pdb->handle = un.bill.pdb_handle;
pdb->prli_word0 = un.bill.pdb_prli_svc0;
pdb->prli_word3 = un.bill.pdb_prli_svc3;
pdb->portid = BITS2WORD_24XX(un.bill.pdb_portid_bits);
ISP_MEMCPY(pdb->portname, un.bill.pdb_portname, 8);
ISP_MEMCPY(pdb->nodename, un.bill.pdb_nodename, 8);
isp_prt(isp, ISP_LOGDEBUG0,
"Chan %d handle 0x%x Port 0x%06x flags 0x%x curstate %x laststate %x",
chan, id, pdb->portid, un.bill.pdb_flags,
un.bill.pdb_curstate, un.bill.pdb_laststate);
if (((un.bill.pdb_curstate & PDB2400_STATE_FCP_MASK) <
PDB2400_STATE_PLOGI_DONE)
|| ((un.bill.pdb_curstate & PDB2400_STATE_FCP_MASK) >
PDB2400_STATE_LOGGED_IN)) {
mbs.param[0] = MBOX_NOT_LOGGED_IN;
return (mbs.param[0]);
}
return (0);
}
static int
isp_gethandles(ispsoftc_t *isp, int chan, uint16_t *handles, int *num, int loop)
{
fcparam *fcp = FCPARAM(isp, chan);
mbreg_t mbs;
isp_pnhle_24xx_t el4, *elp4;
int i, j;
uint32_t p;
MBSINIT(&mbs, MBOX_GET_ID_LIST, MBLOGALL, 250000);
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.param[8] = ISP_FC_SCRLEN;
mbs.param[9] = chan;
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
MEMORYBARRIER(isp, SYNC_SFORDEV, 0, ISP_FC_SCRLEN, chan);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
FC_SCRATCH_RELEASE(isp, chan);
return (mbs.param[0] | (mbs.param[1] << 16));
}
MEMORYBARRIER(isp, SYNC_SFORCPU, 0, ISP_FC_SCRLEN, chan);
elp4 = fcp->isp_scratch;
for (i = 0, j = 0; i < mbs.param[1] && j < *num; i++) {
isp_get_pnhle_24xx(isp, &elp4[i], &el4);
p = el4.pnhle_port_id_lo | (el4.pnhle_port_id_hi << 16);
if (loop && (p >> 8) != (fcp->isp_portid >> 8))
continue;
handles[j++] = el4.pnhle_handle;
}
*num = j;
FC_SCRATCH_RELEASE(isp, chan);
return (0);
}
static void
isp_dump_chip_portdb(ispsoftc_t *isp, int chan)
{
isp_pdb_t pdb;
uint16_t nphdl;
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOGINFO, "Chan %d chip port dump", chan);
for (nphdl = 0; nphdl != NPH_MAX_2K; nphdl++) {
if (isp_getpdb(isp, chan, nphdl, &pdb)) {
continue;
}
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOGINFO, "Chan %d Handle 0x%04x "
"PortID 0x%06x WWPN 0x%02x%02x%02x%02x%02x%02x%02x%02x",
chan, nphdl, pdb.portid, pdb.portname[0], pdb.portname[1],
pdb.portname[2], pdb.portname[3], pdb.portname[4],
pdb.portname[5], pdb.portname[6], pdb.portname[7]);
}
}
static uint64_t
isp_get_wwn(ispsoftc_t *isp, int chan, int nphdl, int nodename)
{
uint64_t wwn = INI_NONE;
mbreg_t mbs;
MBSINIT(&mbs, MBOX_GET_PORT_NAME,
MBLOGALL & ~MBLOGMASK(MBOX_COMMAND_PARAM_ERROR), 500000);
mbs.param[1] = nphdl;
if (nodename)
mbs.param[10] = 1;
mbs.param[9] = chan;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return (wwn);
}
wwn = (((uint64_t)(mbs.param[2] >> 8)) << 56) |
(((uint64_t)(mbs.param[2] & 0xff))<< 48) |
(((uint64_t)(mbs.param[3] >> 8)) << 40) |
(((uint64_t)(mbs.param[3] & 0xff))<< 32) |
(((uint64_t)(mbs.param[6] >> 8)) << 24) |
(((uint64_t)(mbs.param[6] & 0xff))<< 16) |
(((uint64_t)(mbs.param[7] >> 8)) << 8) |
(((uint64_t)(mbs.param[7] & 0xff)));
return (wwn);
}
static int
isp_fclink_test(ispsoftc_t *isp, int chan, int usdelay)
{
mbreg_t mbs;
int i, r, topo;
fcparam *fcp;
isp_pdb_t pdb;
NANOTIME_T hra, hrb;
fcp = FCPARAM(isp, chan);
if (fcp->isp_loopstate < LOOP_HAVE_LINK)
return (-1);
if (fcp->isp_loopstate >= LOOP_LTEST_DONE)
return (0);
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC link test", chan);
GET_NANOTIME(&hra);
while (1) {
isp_change_fw_state(isp, chan, isp_fw_state(isp, chan));
if (fcp->isp_fwstate == FW_READY) {
break;
}
if (fcp->isp_loopstate < LOOP_HAVE_LINK)
goto abort;
GET_NANOTIME(&hrb);
if ((NANOTIME_SUB(&hrb, &hra) / 1000 + 1000 >= usdelay))
break;
ISP_SLEEP(isp, 1000);
}
if (fcp->isp_fwstate != FW_READY) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Firmware is not ready (%s)",
chan, isp_fc_fw_statename(fcp->isp_fwstate));
return (-1);
}
MBSINIT(&mbs, MBOX_GET_LOOP_ID, MBLOGALL, 0);
mbs.param[9] = chan;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
return (-1);
}
topo = (int) mbs.param[6];
if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB)
topo = TOPO_PTP_STUB;
fcp->isp_topo = topo;
fcp->isp_portid = mbs.param[2] | (mbs.param[3] << 16);
if (!TOPO_IS_FABRIC(fcp->isp_topo)) {
fcp->isp_loopid = mbs.param[1] & 0xff;
} else if (fcp->isp_topo != TOPO_F_PORT) {
uint8_t alpa = fcp->isp_portid;
for (i = 0; alpa_map[i]; i++) {
if (alpa_map[i] == alpa)
break;
}
if (alpa_map[i])
fcp->isp_loopid = i;
}
#if 0
fcp->isp_loopstate = LOOP_HAVE_ADDR;
#endif
fcp->isp_loopstate = LOOP_TESTING_LINK;
if (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT) {
r = isp_getpdb(isp, chan, NPH_FL_ID, &pdb);
if (r != 0 || pdb.portid == 0) {
isp_prt(isp, ISP_LOGWARN,
"fabric topology, but cannot get info about fabric controller (0x%x)", r);
fcp->isp_topo = TOPO_PTP_STUB;
goto not_on_fabric;
}
fcp->isp_fabric_params = mbs.param[7];
fcp->isp_sns_hdl = NPH_SNS_ID;
r = isp_register_fc4_type(isp, chan);
if (fcp->isp_loopstate < LOOP_TESTING_LINK)
goto abort;
if (r != 0)
goto not_on_fabric;
r = isp_register_fc4_features_24xx(isp, chan);
if (fcp->isp_loopstate < LOOP_TESTING_LINK)
goto abort;
if (r != 0)
goto not_on_fabric;
r = isp_register_port_name_24xx(isp, chan);
if (fcp->isp_loopstate < LOOP_TESTING_LINK)
goto abort;
if (r != 0)
goto not_on_fabric;
isp_register_node_name_24xx(isp, chan);
if (fcp->isp_loopstate < LOOP_TESTING_LINK)
goto abort;
}
not_on_fabric:
fcp->isp_gbspeed = 1;
MBSINIT(&mbs, MBOX_GET_SET_DATA_RATE, MBLOGALL, 3000000);
mbs.param[1] = MBGSD_GET_RATE;
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
if (mbs.param[1] == MBGSD_10GB)
fcp->isp_gbspeed = 10;
else if (mbs.param[1] == MBGSD_64GB)
fcp->isp_gbspeed = 64;
else if (mbs.param[1] == MBGSD_32GB)
fcp->isp_gbspeed = 32;
else if (mbs.param[1] == MBGSD_16GB)
fcp->isp_gbspeed = 16;
else if (mbs.param[1] == MBGSD_8GB)
fcp->isp_gbspeed = 8;
else if (mbs.param[1] == MBGSD_4GB)
fcp->isp_gbspeed = 4;
else if (mbs.param[1] == MBGSD_2GB)
fcp->isp_gbspeed = 2;
else if (mbs.param[1] == MBGSD_1GB)
fcp->isp_gbspeed = 1;
}
if (fcp->isp_loopstate < LOOP_TESTING_LINK) {
abort:
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC link test aborted", chan);
return (1);
}
fcp->isp_loopstate = LOOP_LTEST_DONE;
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOGCONFIG,
"Chan %d WWPN %016jx WWNN %016jx",
chan, (uintmax_t)fcp->isp_wwpn, (uintmax_t)fcp->isp_wwnn);
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOGCONFIG,
"Chan %d %dGb %s PortID 0x%06x LoopID 0x%02x",
chan, fcp->isp_gbspeed, isp_fc_toponame(fcp), fcp->isp_portid,
fcp->isp_loopid);
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC link test done", chan);
return (0);
}
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_FSCAN_DONE)
return (-1);
if (fcp->isp_loopstate >= LOOP_READY)
return (0);
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC PDB sync", chan);
fcp->isp_loopstate = LOOP_SYNCING_PDB;
for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
lp = &fcp->portdb[dbidx];
if (lp->state == FC_PORTDB_STATE_NIL)
continue;
if (lp->probational && lp->state != FC_PORTDB_STATE_ZOMBIE)
lp->state = FC_PORTDB_STATE_DEAD;
switch (lp->state) {
case FC_PORTDB_STATE_DEAD:
lp->state = FC_PORTDB_STATE_NIL;
isp_async(isp, ISPASYNC_DEV_GONE, chan, lp);
if ((lp->portid & 0xffff00) != 0) {
(void) isp_plogx(isp, chan, lp->handle,
lp->portid,
PLOGX_FLG_CMD_LOGO |
PLOGX_FLG_IMPLICIT |
PLOGX_FLG_FREE_NPHDL);
}
break;
case FC_PORTDB_STATE_NEW:
lp->state = FC_PORTDB_STATE_VALID;
isp_async(isp, ISPASYNC_DEV_ARRIVED, chan, lp);
break;
case FC_PORTDB_STATE_CHANGED:
lp->state = FC_PORTDB_STATE_VALID;
isp_async(isp, ISPASYNC_DEV_CHANGED, chan, lp);
lp->portid = lp->new_portid;
lp->prli_word0 = lp->new_prli_word0;
lp->prli_word3 = lp->new_prli_word3;
break;
case FC_PORTDB_STATE_VALID:
isp_async(isp, ISPASYNC_DEV_STAYED, chan, lp);
break;
case FC_PORTDB_STATE_ZOMBIE:
break;
default:
isp_prt(isp, ISP_LOGWARN,
"isp_pdb_sync: state %d for idx %d",
lp->state, dbidx);
isp_dump_portdb(isp, chan);
}
}
if (fcp->isp_loopstate < LOOP_SYNCING_PDB) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC PDB sync aborted", chan);
return (1);
}
fcp->isp_loopstate = LOOP_READY;
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC PDB sync done", chan);
return (0);
}
static void
isp_pdb_add_update(ispsoftc_t *isp, int chan, isp_pdb_t *pdb)
{
fcportdb_t *lp;
uint64_t wwnn, wwpn;
MAKE_WWN_FROM_NODE_NAME(wwnn, pdb->nodename);
MAKE_WWN_FROM_NODE_NAME(wwpn, pdb->portname);
if (isp_find_pdb_by_wwpn(isp, chan, wwpn, &lp)) {
if (!lp->probational) {
isp_prt(isp, ISP_LOGERR,
"Chan %d Port 0x%06x@0x%04x [%d] is not probational (0x%x)",
chan, lp->portid, lp->handle,
FC_PORTDB_TGT(isp, chan, lp), lp->state);
isp_dump_portdb(isp, chan);
return;
}
lp->probational = 0;
lp->node_wwn = wwnn;
if (lp->portid == pdb->portid &&
lp->handle == pdb->handle &&
lp->prli_word3 == pdb->prli_word3 &&
((pdb->prli_word0 & PRLI_WD0_EST_IMAGE_PAIR) ==
(lp->prli_word0 & PRLI_WD0_EST_IMAGE_PAIR))) {
if (lp->state != FC_PORTDB_STATE_NEW)
lp->state = FC_PORTDB_STATE_VALID;
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x@0x%04x is valid",
chan, pdb->portid, pdb->handle);
return;
}
lp->state = FC_PORTDB_STATE_CHANGED;
lp->handle = pdb->handle;
lp->new_portid = pdb->portid;
lp->new_prli_word0 = pdb->prli_word0;
lp->new_prli_word3 = pdb->prli_word3;
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x@0x%04x is changed",
chan, pdb->portid, pdb->handle);
return;
}
if (!isp_find_pdb_empty(isp, chan, &lp)) {
isp_prt(isp, ISP_LOGERR, "Chan %d out of portdb entries", chan);
return;
}
ISP_MEMZERO(lp, sizeof (fcportdb_t));
lp->probational = 0;
lp->state = FC_PORTDB_STATE_NEW;
lp->portid = lp->new_portid = pdb->portid;
lp->prli_word0 = lp->new_prli_word0 = pdb->prli_word0;
lp->prli_word3 = lp->new_prli_word3 = pdb->prli_word3;
lp->handle = pdb->handle;
lp->port_wwn = wwpn;
lp->node_wwn = wwnn;
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d Port 0x%06x@0x%04x is new",
chan, pdb->portid, pdb->handle);
}
static int
isp_scan_loop(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
int idx, lim, r;
isp_pdb_t pdb;
uint16_t *handles;
uint16_t handle;
if (fcp->isp_loopstate < LOOP_LTEST_DONE)
return (-1);
if (fcp->isp_loopstate >= LOOP_LSCAN_DONE)
return (0);
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC loop scan", chan);
fcp->isp_loopstate = LOOP_SCANNING_LOOP;
if (TOPO_IS_FABRIC(fcp->isp_topo)) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC loop scan done (no loop)", chan);
fcp->isp_loopstate = LOOP_LSCAN_DONE;
return (0);
}
handles = (uint16_t *)fcp->isp_scanscratch;
lim = ISP_FC_SCRLEN / 2;
r = isp_gethandles(isp, chan, handles, &lim, 1);
if (r != 0) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Getting list of handles failed with %x", chan, r);
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC loop scan done (bad)", chan);
return (-1);
}
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d Got %d handles",
chan, lim);
isp_mark_portdb(isp, chan);
for (idx = 0; idx < lim; idx++) {
handle = handles[idx];
if (handle >= NPH_RESERVED)
continue;
r = isp_getpdb(isp, chan, handle, &pdb);
if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
abort:
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC loop scan aborted", chan);
return (1);
}
if (r != 0) {
isp_prt(isp, ISP_LOGDEBUG1,
"Chan %d FC Scan Loop handle %d returned %x",
chan, handle, r);
continue;
}
isp_pdb_add_update(isp, chan, &pdb);
}
if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
goto abort;
fcp->isp_loopstate = LOOP_LSCAN_DONE;
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC loop scan done", chan);
return (0);
}
static int
isp_ct_passthru(ispsoftc_t *isp, int chan, uint32_t cmd_bcnt, uint32_t rsp_bcnt)
{
fcparam *fcp = FCPARAM(isp, chan);
isp_ct_pt_t pt;
int retval;
if (isp->isp_dblev & ISP_LOGDEBUG1)
isp_print_bytes(isp, "CT request", cmd_bcnt, fcp->isp_scratch);
ISP_MEMZERO(&pt, sizeof(pt));
pt.ctp_header.rqs_entry_count = 1;
pt.ctp_header.rqs_entry_type = RQSTYPE_CT_PASSTHRU;
pt.ctp_nphdl = fcp->isp_sns_hdl;
pt.ctp_cmd_cnt = 1;
pt.ctp_vpidx = ISP_GET_VPIDX(isp, chan);
pt.ctp_time = 10;
pt.ctp_rsp_cnt = 1;
pt.ctp_rsp_bcnt = rsp_bcnt;
pt.ctp_cmd_bcnt = cmd_bcnt;
pt.ctp_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma);
pt.ctp_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma);
pt.ctp_dataseg[0].ds_count = cmd_bcnt;
pt.ctp_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma);
pt.ctp_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma);
pt.ctp_dataseg[1].ds_count = rsp_bcnt;
retval = isp_exec_entry_queue(isp, &pt, &pt, 2 * pt.ctp_time);
if (retval != 0) {
isp_prt(isp, ISP_LOGERR, "%s: CTP of chan %d error %d",
__func__, chan, retval);
return (retval);
}
if (pt.ctp_status && pt.ctp_status != RQCS_DATA_UNDERRUN) {
isp_prt(isp, ISP_LOGWARN,
"Chan %d CT pass-through returned 0x%x",
chan, pt.ctp_status);
return (-1);
}
if (isp->isp_dblev & ISP_LOGDEBUG1)
isp_print_bytes(isp, "CT response", rsp_bcnt, fcp->isp_scratch);
return (0);
}
#define GIDLEN ISP_FC_SCRLEN
#define NGENT ((GIDLEN - 16) >> 2)
static int
isp_gid_pt(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t ct;
uint8_t *scp = fcp->isp_scratch;
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d requesting GID_PT", chan);
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
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_PT;
ct.ct_bcnt_resid = (GIDLEN - 16) >> 2;
isp_put_ct_hdr(isp, &ct, (ct_hdr_t *)scp);
scp[sizeof(ct)] = 0x7f;
scp[sizeof(ct)+1] = 0;
scp[sizeof(ct)+2] = 0;
scp[sizeof(ct)+3] = 0;
if (isp_ct_passthru(isp, chan, sizeof(ct) + sizeof(uint32_t), GIDLEN)) {
FC_SCRATCH_RELEASE(isp, chan);
return (-1);
}
isp_get_gid_xx_response(isp, (sns_gid_xx_rsp_t *)scp,
(sns_gid_xx_rsp_t *)fcp->isp_scanscratch, NGENT);
FC_SCRATCH_RELEASE(isp, chan);
return (0);
}
static int
isp_gff_id(ispsoftc_t *isp, int chan, uint32_t portid)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t ct;
uint32_t *rp;
uint8_t *scp = fcp->isp_scratch;
sns_gff_id_rsp_t rsp;
int i, res = -1;
if (!fcp->isp_use_gff_id)
return (res);
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d requesting GFF_ID", chan);
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (res);
}
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_GFF_ID;
ct.ct_bcnt_resid = (SNS_GFF_ID_RESP_SIZE - sizeof(ct)) / 4;
isp_put_ct_hdr(isp, &ct, (ct_hdr_t *)scp);
rp = (uint32_t *) &scp[sizeof(ct)];
ISP_IOZPUT_32(isp, portid, rp);
if (isp_ct_passthru(isp, chan, sizeof(ct) + sizeof(uint32_t),
SNS_GFF_ID_RESP_SIZE)) {
FC_SCRATCH_RELEASE(isp, chan);
return (res);
}
isp_get_gff_id_response(isp, (sns_gff_id_rsp_t *)scp, &rsp);
if (rsp.snscb_cthdr.ct_cmd_resp == LS_ACC) {
for (i = 0; i < 32; i++) {
if (rsp.snscb_fc4_features[i] != 0) {
res = 0;
break;
}
}
if (((rsp.snscb_fc4_features[FC4_SCSI / 8] >>
((FC4_SCSI % 8) * 4)) & 0x01) != 0)
res = 1;
if (((ISP_SWAP32(isp, rsp.snscb_fc4_features[FC4_SCSI / 8]) >>
((FC4_SCSI % 8) * 4)) & 0x01) != 0)
res = 1;
}
FC_SCRATCH_RELEASE(isp, chan);
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d GFF_ID result is %d", chan, res);
return (res);
}
static int
isp_gft_id(ispsoftc_t *isp, int chan, uint32_t portid)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t ct;
uint32_t *rp;
uint8_t *scp = fcp->isp_scratch;
sns_gft_id_rsp_t rsp;
int i, res = -1;
if (!fcp->isp_use_gft_id)
return (res);
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d requesting GFT_ID", chan);
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (res);
}
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_GFT_ID;
ct.ct_bcnt_resid = (SNS_GFT_ID_RESP_SIZE - sizeof(ct)) / 4;
isp_put_ct_hdr(isp, &ct, (ct_hdr_t *)scp);
rp = (uint32_t *) &scp[sizeof(ct)];
ISP_IOZPUT_32(isp, portid, rp);
if (isp_ct_passthru(isp, chan, sizeof(ct) + sizeof(uint32_t),
SNS_GFT_ID_RESP_SIZE)) {
FC_SCRATCH_RELEASE(isp, chan);
return (res);
}
isp_get_gft_id_response(isp, (sns_gft_id_rsp_t *)scp, &rsp);
if (rsp.snscb_cthdr.ct_cmd_resp == LS_ACC) {
for (i = 0; i < 8; i++) {
if (rsp.snscb_fc4_types[i] != 0) {
res = 0;
break;
}
}
if (((rsp.snscb_fc4_types[FC4_SCSI / 32] >>
(FC4_SCSI % 32)) & 0x01) != 0)
res = 1;
}
FC_SCRATCH_RELEASE(isp, chan);
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d GFT_ID result is %d", chan, res);
return (res);
}
static int
isp_scan_fabric(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
fcportdb_t *lp;
uint32_t portid;
isp_pdb_t pdb;
int portidx, portlim, r;
sns_gid_xx_rsp_t *rs;
if (fcp->isp_loopstate < LOOP_LSCAN_DONE)
return (-1);
if (fcp->isp_loopstate >= LOOP_FSCAN_DONE)
return (0);
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC fabric scan", chan);
fcp->isp_loopstate = LOOP_SCANNING_FABRIC;
if (!TOPO_IS_FABRIC(fcp->isp_topo)) {
fcp->isp_loopstate = LOOP_FSCAN_DONE;
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC fabric scan done (no fabric)", chan);
return (0);
}
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC) {
abort:
FC_SCRATCH_RELEASE(isp, chan);
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC fabric scan aborted", chan);
return (1);
}
r = isp_getpdb(isp, chan, NPH_FL_ID, &pdb);
if ((r & 0xffff) == MBOX_NOT_LOGGED_IN) {
isp_dump_chip_portdb(isp, chan);
}
if (r) {
fcp->isp_loopstate = LOOP_LTEST_DONE;
fail:
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d FC fabric scan done (bad)", chan);
return (-1);
}
r = isp_gid_pt(isp, chan);
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC)
goto abort;
if (r > 0) {
fcp->isp_loopstate = LOOP_FSCAN_DONE;
return (-1);
} else if (r < 0) {
fcp->isp_loopstate = LOOP_LTEST_DONE;
return (-1);
}
rs = (sns_gid_xx_rsp_t *) fcp->isp_scanscratch;
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC)
goto abort;
if (rs->snscb_cthdr.ct_cmd_resp != LS_ACC) {
int level;
if (rs->snscb_cthdr.ct_reason == 9 &&
(rs->snscb_cthdr.ct_explanation == 0x07 ||
rs->snscb_cthdr.ct_explanation == 0x0a)) {
level = ISP_LOG_SANCFG;
} else {
level = ISP_LOGWARN;
}
isp_prt(isp, level, "Chan %d Fabric Nameserver rejected GID_PT"
" (Reason=0x%x Expl=0x%x)", chan,
rs->snscb_cthdr.ct_reason,
rs->snscb_cthdr.ct_explanation);
fcp->isp_loopstate = LOOP_FSCAN_DONE;
return (-1);
}
for (portidx = 0; portidx < NGENT-1; portidx++) {
if (rs->snscb_ports[portidx].control & 0x80)
break;
}
if ((rs->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_LOG_SANCFG,
"Chan %d Got %d ports back from name server", chan, portlim);
for (portidx = 0; portidx < portlim; portidx++) {
int npidx;
portid =
((rs->snscb_ports[portidx].portid[0]) << 16) |
((rs->snscb_ports[portidx].portid[1]) << 8) |
((rs->snscb_ports[portidx].portid[2]));
for (npidx = portidx + 1; npidx < portlim; npidx++) {
uint32_t new_portid =
((rs->snscb_ports[npidx].portid[0]) << 16) |
((rs->snscb_ports[npidx].portid[1]) << 8) |
((rs->snscb_ports[npidx].portid[2]));
if (new_portid == portid) {
break;
}
}
if (npidx < portlim) {
rs->snscb_ports[npidx].portid[0] = 0;
rs->snscb_ports[npidx].portid[1] = 0;
rs->snscb_ports[npidx].portid[2] = 0;
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d removing duplicate PortID 0x%06x entry from list", chan, portid);
}
}
isp_mark_portdb(isp, chan);
for (portidx = 0; portidx < portlim; portidx++) {
portid = ((rs->snscb_ports[portidx].portid[0]) << 16) |
((rs->snscb_ports[portidx].portid[1]) << 8) |
((rs->snscb_ports[portidx].portid[2]));
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Checking fabric port 0x%06x", chan, portid);
if (portid == 0) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port at idx %d is zero",
chan, portidx);
continue;
}
if (portid == fcp->isp_portid) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x is our", chan, portid);
continue;
}
if (isp_find_pdb_by_portid(isp, chan, portid, &lp)) {
if (!lp->probational) {
isp_prt(isp, ISP_LOGERR,
"Chan %d Port 0x%06x@0x%04x [%d] is not probational (0x%x)",
chan, lp->portid, lp->handle,
FC_PORTDB_TGT(isp, chan, lp), lp->state);
isp_dump_portdb(isp, chan);
goto fail;
}
if (lp->state == FC_PORTDB_STATE_ZOMBIE)
goto relogin;
r = isp_getpdb(isp, chan, lp->handle, &pdb);
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC)
goto abort;
if (r != 0) {
lp->state = FC_PORTDB_STATE_DEAD;
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x handle 0x%x is dead (%d)",
chan, portid, lp->handle, r);
goto relogin;
}
isp_pdb_add_update(isp, chan, &pdb);
continue;
}
relogin:
if ((fcp->role & ISP_ROLE_INITIATOR) == 0) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x is not logged in", chan, portid);
continue;
}
r = isp_gff_id(isp, chan, portid);
if (r == 0) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x is not an FCP target", chan, portid);
continue;
}
if (r < 0)
r = isp_gft_id(isp, chan, portid);
if (r == 0) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Port 0x%06x is not FCP", chan, portid);
continue;
}
if (isp_login_device(isp, chan, portid, &pdb,
&FCPARAM(isp, 0)->isp_lasthdl)) {
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC)
goto abort;
continue;
}
isp_pdb_add_update(isp, chan, &pdb);
}
if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC)
goto abort;
fcp->isp_loopstate = LOOP_FSCAN_DONE;
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d FC fabric scan done", chan);
return (0);
}
static int
isp_login_device(ispsoftc_t *isp, int chan, uint32_t portid, isp_pdb_t *p, uint16_t *ohp)
{
int i, r;
uint16_t handle;
handle = isp_next_handle(isp, ohp);
for (i = 0; i < NPH_MAX_2K; i++) {
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC)
return (-1);
r = isp_getpdb(isp, chan, handle, p);
if (r == 0) {
if (p->portid != portid) {
handle = isp_next_handle(isp, ohp);
continue;
}
break;
}
if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC)
return (-1);
r = isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_PLOGI);
if (r == 0) {
break;
} else if ((r & 0xffff) == MBOX_PORT_ID_USED) {
if (isp_plogx(isp, chan, r >> 16, portid, PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT | PLOGX_FLG_FREE_NPHDL)) {
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);
if (r != 0)
i = NPH_MAX_2K;
break;
} else if ((r & 0xffff) == MBOX_LOOP_ID_USED) {
handle = isp_next_handle(isp, ohp);
} else {
i = NPH_MAX_2K;
break;
}
}
if (i == NPH_MAX_2K) {
isp_prt(isp, ISP_LOGWARN, "Chan %d PLOGI 0x%06x failed", chan, portid);
return (-1);
}
r = isp_getpdb(isp, chan, handle, p);
if (r != 0) {
isp_prt(isp, ISP_LOGERR, "Chan %d new device 0x%06x@0x%x disappeared", chan, portid, handle);
return (-1);
}
if (p->handle != handle || p->portid != portid) {
isp_prt(isp, ISP_LOGERR, "Chan %d new device 0x%06x@0x%x changed (0x%06x@0x%0x)",
chan, portid, handle, p->portid, p->handle);
return (-1);
}
return (0);
}
static int
isp_register_fc4_type(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
rft_id_t rp;
ct_hdr_t *ct = &rp.rftid_hdr;
uint8_t *scp = fcp->isp_scratch;
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
ISP_MEMZERO(&rp, sizeof(rp));
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.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);
if (isp_ct_passthru(isp, chan, sizeof(rft_id_t), sizeof(ct_hdr_t))) {
FC_SCRATCH_RELEASE(isp, chan);
return (-1);
}
isp_get_ct_hdr(isp, (ct_hdr_t *) scp, ct);
FC_SCRATCH_RELEASE(isp, chan);
if (ct->ct_cmd_resp == LS_RJT) {
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOG_WARN1, "Chan %d Register FC4 Type rejected", chan);
return (-1);
} else if (ct->ct_cmd_resp == LS_ACC) {
isp_prt(isp, ISP_LOG_SANCFG, "Chan %d Register FC4 Type accepted", chan);
} else {
isp_prt(isp, ISP_LOGWARN, "Chan %d Register FC4 Type: 0x%x", chan, ct->ct_cmd_resp);
return (-1);
}
return (0);
}
static int
isp_register_fc4_features_24xx(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t *ct;
rff_id_t rp;
uint8_t *scp = fcp->isp_scratch;
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
ISP_MEMZERO(&rp, sizeof(rp));
ct = &rp.rffid_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_RFF_ID;
ct->ct_bcnt_resid = (sizeof (rff_id_t) - sizeof (ct_hdr_t)) >> 2;
rp.rffid_portid[0] = fcp->isp_portid >> 16;
rp.rffid_portid[1] = fcp->isp_portid >> 8;
rp.rffid_portid[2] = fcp->isp_portid;
rp.rffid_fc4features = 0;
if (fcp->role & ISP_ROLE_TARGET)
rp.rffid_fc4features |= 1;
if (fcp->role & ISP_ROLE_INITIATOR)
rp.rffid_fc4features |= 2;
rp.rffid_fc4type = FC4_SCSI;
isp_put_rff_id(isp, &rp, (rff_id_t *)scp);
if (isp->isp_dblev & ISP_LOGDEBUG1)
isp_print_bytes(isp, "CT request", sizeof(rft_id_t), scp);
if (isp_ct_passthru(isp, chan, sizeof(rft_id_t), sizeof(ct_hdr_t))) {
FC_SCRATCH_RELEASE(isp, chan);
return (-1);
}
isp_get_ct_hdr(isp, (ct_hdr_t *) scp, ct);
FC_SCRATCH_RELEASE(isp, chan);
if (ct->ct_cmd_resp == LS_RJT) {
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOG_WARN1,
"Chan %d Register FC4 Features rejected", chan);
return (-1);
} else if (ct->ct_cmd_resp == LS_ACC) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Register FC4 Features accepted", chan);
} else {
isp_prt(isp, ISP_LOGWARN,
"Chan %d Register FC4 Features: 0x%x", chan, ct->ct_cmd_resp);
return (-1);
}
return (0);
}
static int
isp_register_port_name_24xx(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t *ct;
rspn_id_t rp;
uint8_t *scp = fcp->isp_scratch;
int len;
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
ISP_MEMZERO(&rp, sizeof(rp));
ct = &rp.rspnid_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_RSPN_ID;
rp.rspnid_portid[0] = fcp->isp_portid >> 16;
rp.rspnid_portid[1] = fcp->isp_portid >> 8;
rp.rspnid_portid[2] = fcp->isp_portid;
rp.rspnid_length = 0;
len = offsetof(rspn_id_t, rspnid_name);
mtx_lock(&prison0.pr_mtx);
rp.rspnid_length += sprintf(&scp[len + rp.rspnid_length],
"%s", prison0.pr_hostname[0] ? prison0.pr_hostname : "FreeBSD");
mtx_unlock(&prison0.pr_mtx);
rp.rspnid_length += sprintf(&scp[len + rp.rspnid_length],
":%s", device_get_nameunit(isp->isp_dev));
if (chan != 0) {
rp.rspnid_length += sprintf(&scp[len + rp.rspnid_length],
"/%d", chan);
}
len += rp.rspnid_length;
ct->ct_bcnt_resid = (len - sizeof(ct_hdr_t)) >> 2;
isp_put_rspn_id(isp, &rp, (rspn_id_t *)scp);
if (isp_ct_passthru(isp, chan, len, sizeof(ct_hdr_t))) {
FC_SCRATCH_RELEASE(isp, chan);
return (-1);
}
isp_get_ct_hdr(isp, (ct_hdr_t *) scp, ct);
FC_SCRATCH_RELEASE(isp, chan);
if (ct->ct_cmd_resp == LS_RJT) {
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOG_WARN1,
"Chan %d Register Symbolic Port Name rejected", chan);
return (-1);
} else if (ct->ct_cmd_resp == LS_ACC) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Register Symbolic Port Name accepted", chan);
} else {
isp_prt(isp, ISP_LOGWARN,
"Chan %d Register Symbolic Port Name: 0x%x", chan, ct->ct_cmd_resp);
return (-1);
}
return (0);
}
static int
isp_register_node_name_24xx(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
ct_hdr_t *ct;
rsnn_nn_t rp;
uint8_t *scp = fcp->isp_scratch;
int len;
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
isp_prt(isp, ISP_LOGERR, sacq);
return (-1);
}
ISP_MEMZERO(&rp, sizeof(rp));
ct = &rp.rsnnnn_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_RSNN_NN;
MAKE_NODE_NAME_FROM_WWN(rp.rsnnnn_nodename, fcp->isp_wwnn);
rp.rsnnnn_length = 0;
len = offsetof(rsnn_nn_t, rsnnnn_name);
mtx_lock(&prison0.pr_mtx);
rp.rsnnnn_length += sprintf(&scp[len + rp.rsnnnn_length],
"%s", prison0.pr_hostname[0] ? prison0.pr_hostname : "FreeBSD");
mtx_unlock(&prison0.pr_mtx);
len += rp.rsnnnn_length;
ct->ct_bcnt_resid = (len - sizeof(ct_hdr_t)) >> 2;
isp_put_rsnn_nn(isp, &rp, (rsnn_nn_t *)scp);
if (isp_ct_passthru(isp, chan, len, sizeof(ct_hdr_t))) {
FC_SCRATCH_RELEASE(isp, chan);
return (-1);
}
isp_get_ct_hdr(isp, (ct_hdr_t *) scp, ct);
FC_SCRATCH_RELEASE(isp, chan);
if (ct->ct_cmd_resp == LS_RJT) {
isp_prt(isp, ISP_LOG_SANCFG|ISP_LOG_WARN1,
"Chan %d Register Symbolic Node Name rejected", chan);
return (-1);
} else if (ct->ct_cmd_resp == LS_ACC) {
isp_prt(isp, ISP_LOG_SANCFG,
"Chan %d Register Symbolic Node Name accepted", chan);
} else {
isp_prt(isp, ISP_LOGWARN,
"Chan %d Register Symbolic Node Name: 0x%x", chan, ct->ct_cmd_resp);
return (-1);
}
return (0);
}
static uint16_t
isp_next_handle(ispsoftc_t *isp, uint16_t *ohp)
{
fcparam *fcp;
int i, chan, wrap;
uint16_t handle;
handle = *ohp;
wrap = 0;
next:
if (handle == NIL_HANDLE) {
handle = 0;
} else {
handle++;
if (handle > NPH_RESERVED - 1) {
if (++wrap >= 2) {
isp_prt(isp, ISP_LOGERR, "Out of port handles!");
return (NIL_HANDLE);
}
handle = 0;
}
}
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
for (i = 0; i < MAX_FC_TARG; i++) {
if (fcp->portdb[i].state != FC_PORTDB_STATE_NIL &&
fcp->portdb[i].handle == handle)
goto next;
}
}
*ohp = handle;
return (handle);
}
int
isp_start(XS_T *xs)
{
ispsoftc_t *isp;
fcparam *fcp;
uint32_t cdblen;
ispreqt7_t local, *reqp = &local;
void *qep;
fcportdb_t *lp;
int target, dmaresult;
XS_INITERR(xs);
isp = XS_ISP(xs);
if (XS_CDBLEN(xs) > 16 || XS_CDBLEN(xs) == 0) {
isp_prt(isp, ISP_LOGERR, "unsupported cdb length (%d, CDB[0]=0x%x)", XS_CDBLEN(xs), XS_CDBP(xs)[0] & 0xff);
XS_SETERR(xs, HBA_REQINVAL);
return (CMD_COMPLETE);
}
target = XS_TGT(xs);
fcp = FCPARAM(isp, XS_CHANNEL(xs));
if ((fcp->role & ISP_ROLE_INITIATOR) == 0) {
isp_prt(isp, ISP_LOG_WARN1,
"%d.%d.%jx I am not an initiator",
XS_CHANNEL(xs), target, (uintmax_t)XS_LUN(xs));
XS_SETERR(xs, HBA_SELTIMEOUT);
return (CMD_COMPLETE);
}
if (isp->isp_state != ISP_RUNSTATE) {
isp_prt(isp, ISP_LOGERR, "Adapter not at RUNSTATE");
XS_SETERR(xs, HBA_BOTCH);
return (CMD_COMPLETE);
}
isp_prt(isp, ISP_LOGDEBUG2, "XS_TGT(xs)=%d", target);
lp = &fcp->portdb[target];
if (target < 0 || target >= MAX_FC_TARG ||
lp->is_target == 0) {
XS_SETERR(xs, HBA_SELTIMEOUT);
return (CMD_COMPLETE);
}
if (fcp->isp_loopstate != LOOP_READY) {
isp_prt(isp, ISP_LOGDEBUG1,
"%d.%d.%jx loop is not ready",
XS_CHANNEL(xs), target, (uintmax_t)XS_LUN(xs));
return (CMD_RQLATER);
}
if (lp->state == FC_PORTDB_STATE_ZOMBIE) {
isp_prt(isp, ISP_LOGDEBUG1,
"%d.%d.%jx target zombie",
XS_CHANNEL(xs), target, (uintmax_t)XS_LUN(xs));
return (CMD_RQLATER);
}
if (lp->state != FC_PORTDB_STATE_VALID) {
isp_prt(isp, ISP_LOGDEBUG1,
"%d.%d.%jx bad db port state 0x%x",
XS_CHANNEL(xs), target, (uintmax_t)XS_LUN(xs), lp->state);
XS_SETERR(xs, HBA_SELTIMEOUT);
return (CMD_COMPLETE);
}
start_again:
qep = isp_getrqentry(isp);
if (qep == NULL) {
isp_prt(isp, ISP_LOG_WARN1, "Request Queue Overflow");
XS_SETERR(xs, HBA_BOTCH);
return (CMD_EAGAIN);
}
XS_SETERR(xs, HBA_NOERROR);
ISP_MEMZERO(reqp, QENTRY_LEN);
if (ISP_TST_SENDMARKER(isp, XS_CHANNEL(xs))) {
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;
m->mrk_vphdl = XS_CHANNEL(xs);
isp_put_marker_24xx(isp, m, qep);
ISP_SYNC_REQUEST(isp);
ISP_SET_SENDMARKER(isp, XS_CHANNEL(xs), 0);
goto start_again;
}
cdblen = XS_CDBLEN(xs);
if (cdblen > sizeof (reqp->req_cdb)) {
isp_prt(isp, ISP_LOGERR, "Command Length %u too long for this chip", cdblen);
XS_SETERR(xs, HBA_REQINVAL);
return (CMD_COMPLETE);
}
reqp->req_header.rqs_entry_type = RQSTYPE_T7RQS;
reqp->req_header.rqs_entry_count = 1;
reqp->req_nphdl = lp->handle;
reqp->req_time = XS_TIME(xs);
be64enc(reqp->req_lun, CAM_EXTLUN_BYTE_SWIZZLE(XS_LUN(xs)));
if (XS_XFRIN(xs))
reqp->req_alen_datadir = FCP_CMND_DATA_READ;
else if (XS_XFROUT(xs))
reqp->req_alen_datadir = FCP_CMND_DATA_WRITE;
if (XS_TAG_P(xs))
reqp->req_task_attribute = XS_TAG_TYPE(xs);
else
reqp->req_task_attribute = FCP_CMND_TASK_ATTR_SIMPLE;
reqp->req_task_attribute |= (XS_PRIORITY(xs) << FCP_CMND_PRIO_SHIFT) &
FCP_CMND_PRIO_MASK;
if (FCPARAM(isp, XS_CHANNEL(xs))->fctape_enabled && (lp->prli_word3 & PRLI_WD3_RETRY)) {
if (FCP_NEXT_CRN(isp, &reqp->req_crn, xs)) {
isp_prt(isp, ISP_LOG_WARN1,
"%d.%d.%jx cannot generate next CRN",
XS_CHANNEL(xs), target, (uintmax_t)XS_LUN(xs));
XS_SETERR(xs, HBA_BOTCH);
return (CMD_EAGAIN);
}
}
ISP_MEMCPY(reqp->req_cdb, XS_CDBP(xs), cdblen);
reqp->req_dl = XS_XFRLEN(xs);
reqp->req_tidlo = lp->portid;
reqp->req_tidhi = lp->portid >> 16;
reqp->req_vpidx = ISP_GET_VPIDX(isp, XS_CHANNEL(xs));
reqp->req_handle = isp_allocate_handle(isp, xs, ISP_HANDLE_INITIATOR);
if (reqp->req_handle == 0) {
isp_prt(isp, ISP_LOG_WARN1, "out of xflist pointers");
XS_SETERR(xs, HBA_BOTCH);
return (CMD_EAGAIN);
}
dmaresult = ISP_DMASETUP(isp, xs, reqp);
if (dmaresult != 0) {
isp_destroy_handle(isp, reqp->req_handle);
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));
return (0);
}
int
isp_control(ispsoftc_t *isp, ispctl_t ctl, ...)
{
fcparam *fcp;
fcportdb_t *lp;
XS_T *xs;
mbreg_t *mbr;
int chan, tgt;
uint32_t handle;
va_list ap;
uint8_t local[QENTRY_LEN];
switch (ctl) {
case ISPCTL_RESET_BUS:
isp_prt(isp, ISP_LOGERR, "BUS RESET NOT IMPLEMENTED");
break;
case ISPCTL_RESET_DEV:
{
isp24xx_tmf_t *tmf;
isp24xx_statusreq_t *sp;
va_start(ap, ctl);
chan = va_arg(ap, int);
tgt = va_arg(ap, int);
va_end(ap);
fcp = FCPARAM(isp, chan);
if (tgt < 0 || tgt >= MAX_FC_TARG) {
isp_prt(isp, ISP_LOGWARN, "Chan %d trying to reset bad target %d", chan, tgt);
break;
}
lp = &fcp->portdb[tgt];
if (lp->is_target == 0 || lp->state != FC_PORTDB_STATE_VALID) {
isp_prt(isp, ISP_LOGWARN, "Chan %d abort of no longer valid target %d", chan, tgt);
break;
}
tmf = (isp24xx_tmf_t *) local;
ISP_MEMZERO(tmf, QENTRY_LEN);
tmf->tmf_header.rqs_entry_type = RQSTYPE_TSK_MGMT;
tmf->tmf_header.rqs_entry_count = 1;
tmf->tmf_nphdl = lp->handle;
tmf->tmf_delay = 2;
tmf->tmf_timeout = 4;
tmf->tmf_flags = ISP24XX_TMF_TARGET_RESET;
tmf->tmf_tidlo = lp->portid;
tmf->tmf_tidhi = lp->portid >> 16;
tmf->tmf_vpidx = ISP_GET_VPIDX(isp, chan);
fcp->sendmarker = 1;
isp_prt(isp, ISP_LOGALL, "Chan %d Reset N-Port Handle 0x%04x @ Port 0x%06x", chan, lp->handle, lp->portid);
sp = (isp24xx_statusreq_t *) local;
if (isp_exec_entry_mbox(isp, tmf, sp, 2 * tmf->tmf_timeout))
break;
if (sp->req_completion_status == 0)
return (0);
isp_prt(isp, ISP_LOGWARN, "Chan %d reset of target %d returned 0x%x", chan, tgt, sp->req_completion_status);
break;
}
case ISPCTL_ABORT_CMD:
{
isp24xx_abrt_t *ab = (isp24xx_abrt_t *)&local;
va_start(ap, ctl);
xs = va_arg(ap, XS_T *);
va_end(ap);
tgt = XS_TGT(xs);
chan = XS_CHANNEL(xs);
handle = isp_find_handle(isp, xs);
if (handle == 0) {
isp_prt(isp, ISP_LOGWARN, "cannot find handle for command to abort");
break;
}
fcp = FCPARAM(isp, chan);
if (tgt < 0 || tgt >= MAX_FC_TARG) {
isp_prt(isp, ISP_LOGWARN, "Chan %d trying to abort bad target %d", chan, tgt);
break;
}
lp = &fcp->portdb[tgt];
if (lp->is_target == 0 || lp->state != FC_PORTDB_STATE_VALID) {
isp_prt(isp, ISP_LOGWARN, "Chan %d abort of no longer valid target %d", chan, tgt);
break;
}
isp_prt(isp, ISP_LOGALL, "Chan %d Abort Cmd for N-Port 0x%04x @ Port 0x%06x", chan, lp->handle, lp->portid);
ISP_MEMZERO(ab, QENTRY_LEN);
ab->abrt_header.rqs_entry_type = RQSTYPE_ABORT_IO;
ab->abrt_header.rqs_entry_count = 1;
ab->abrt_handle = lp->handle;
ab->abrt_cmd_handle = handle;
ab->abrt_tidlo = lp->portid;
ab->abrt_tidhi = lp->portid >> 16;
ab->abrt_vpidx = ISP_GET_VPIDX(isp, chan);
if (isp_exec_entry_mbox(isp, ab, ab, 5))
break;
if (ab->abrt_nphdl == ISP24XX_ABRT_OKAY)
return (0);
isp_prt(isp, ISP_LOGWARN, "Chan %d handle %d abort returned 0x%x", chan, tgt, ab->abrt_nphdl);
break;
}
case ISPCTL_FCLINK_TEST:
{
int usdelay;
va_start(ap, ctl);
chan = va_arg(ap, int);
usdelay = va_arg(ap, int);
va_end(ap);
if (usdelay == 0)
usdelay = 250000;
return (isp_fclink_test(isp, chan, usdelay));
}
case ISPCTL_SCAN_FABRIC:
va_start(ap, ctl);
chan = va_arg(ap, int);
va_end(ap);
return (isp_scan_fabric(isp, chan));
case ISPCTL_SCAN_LOOP:
va_start(ap, ctl);
chan = va_arg(ap, int);
va_end(ap);
return (isp_scan_loop(isp, chan));
case ISPCTL_PDB_SYNC:
va_start(ap, ctl);
chan = va_arg(ap, int);
va_end(ap);
return (isp_pdb_sync(isp, chan));
case ISPCTL_SEND_LIP:
break;
case ISPCTL_GET_PDB:
{
isp_pdb_t *pdb;
va_start(ap, ctl);
chan = va_arg(ap, int);
tgt = va_arg(ap, int);
pdb = va_arg(ap, isp_pdb_t *);
va_end(ap);
return (isp_getpdb(isp, chan, tgt, pdb));
}
case ISPCTL_GET_NAMES:
{
uint64_t *wwnn, *wwnp;
va_start(ap, ctl);
chan = va_arg(ap, int);
tgt = va_arg(ap, int);
wwnn = va_arg(ap, uint64_t *);
wwnp = va_arg(ap, uint64_t *);
va_end(ap);
if (wwnn == NULL && wwnp == NULL) {
break;
}
if (wwnn) {
*wwnn = isp_get_wwn(isp, chan, tgt, 1);
if (*wwnn == INI_NONE) {
break;
}
}
if (wwnp) {
*wwnp = isp_get_wwn(isp, chan, tgt, 0);
if (*wwnp == INI_NONE) {
break;
}
}
return (0);
}
case ISPCTL_RUN_MBOXCMD:
{
va_start(ap, ctl);
mbr = va_arg(ap, mbreg_t *);
va_end(ap);
isp_mboxcmd(isp, mbr);
return (0);
}
case ISPCTL_PLOGX:
{
isp_plcmd_t *p;
int r;
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));
}
do {
isp_next_handle(isp, &p->handle);
r = isp_plogx(isp, p->channel, p->handle, p->portid, p->flags);
if ((r & 0xffff) == MBOX_PORT_ID_USED) {
p->handle = r >> 16;
r = 0;
break;
}
} while ((r & 0xffff) == MBOX_LOOP_ID_USED);
return (r);
}
case ISPCTL_CHANGE_ROLE:
{
int role;
va_start(ap, ctl);
chan = va_arg(ap, int);
role = va_arg(ap, int);
va_end(ap);
return (isp_fc_change_role(isp, chan, role));
}
default:
isp_prt(isp, ISP_LOGERR, "Unknown Control Opcode 0x%x", ctl);
break;
}
return (-1);
}
#ifdef ISP_TARGET_MODE
void
isp_intr_atioq(ispsoftc_t *isp)
{
void *addr;
uint32_t iptr, optr, oop;
iptr = ISP_READ(isp, BIU2400_ATIO_RSPINP);
optr = isp->isp_atioodx;
while (optr != iptr) {
oop = optr;
MEMORYBARRIER(isp, SYNC_ATIOQ, oop, QENTRY_LEN, -1);
addr = ISP_QUEUE_ENTRY(isp->isp_atioq, oop);
switch (((isphdr_t *)addr)->rqs_entry_type) {
case RQSTYPE_NOTIFY:
case RQSTYPE_ATIO:
case RQSTYPE_NOTIFY_ACK:
case RQSTYPE_ABTS_RCVD:
(void) isp_target_notify(isp, addr, &oop,
ATIO_QUEUE_LEN(isp));
break;
case RQSTYPE_RPT_ID_ACQ:
default:
isp_print_qentry(isp, "?ATIOQ entry?", oop, addr);
break;
}
optr = ISP_NXT_QENTRY(oop, ATIO_QUEUE_LEN(isp));
}
if (isp->isp_atioodx != optr) {
ISP_WRITE(isp, BIU2400_ATIO_RSPOUTP, optr);
isp->isp_atioodx = optr;
}
}
#endif
void
isp_intr_mbox(ispsoftc_t *isp, uint16_t mbox0)
{
int i, obits;
if (!isp->isp_mboxbsy) {
isp_prt(isp, ISP_LOGWARN, "mailbox 0x%x with no waiters", mbox0);
return;
}
obits = isp->isp_obits;
isp->isp_mboxtmp[0] = mbox0;
for (i = 1; i < ISP_NMBOX(isp); i++) {
if ((obits & (1 << i)) == 0)
continue;
isp->isp_mboxtmp[i] = ISP_READ(isp, MBOX_OFF(i));
}
isp->isp_mboxbsy = 0;
}
void
isp_intr_respq(ispsoftc_t *isp)
{
XS_T *xs, *cont_xs;
uint8_t qe[QENTRY_LEN];
isp24xx_statusreq_t *sp = (isp24xx_statusreq_t *)qe;
ispstatus_cont_t *scp = (ispstatus_cont_t *)qe;
isphdr_t *hp;
uint8_t *resp, *snsp, etype;
uint16_t scsi_status;
uint32_t iptr, cont = 0, cptr, optr, rlen, slen, totslen;
#ifdef ISP_TARGET_MODE
uint32_t sptr;
#endif
if (isp->isp_state != ISP_RUNSTATE) {
isp_prt(isp, ISP_LOGINFO, "respq interrupt when not ready");
return;
}
iptr = ISP_READ(isp, BIU2400_RSPINP);
optr = isp->isp_resodx;
while (optr != iptr) {
cptr = optr;
#ifdef ISP_TARGET_MODE
sptr = optr;
#endif
hp = (isphdr_t *) ISP_QUEUE_ENTRY(isp->isp_result, cptr);
optr = ISP_NXT_QENTRY(optr, RESULT_QUEUE_LEN(isp));
MEMORYBARRIER(isp, SYNC_RESULT, cptr, QENTRY_LEN, -1);
if (isp->isp_dblev & ISP_LOGDEBUG1)
isp_print_qentry(isp, "Response Queue Entry", cptr, hp);
isp_get_hdr(isp, hp, &sp->req_header);
if (sp->req_header.rqs_flags & RQSFLAG_BADTYPE) {
isp_print_qentry(isp, "invalid entry type", cptr, hp);
continue;
}
if (sp->req_header.rqs_flags & RQSFLAG_BADPARAM) {
isp_print_qentry(isp, "invalid entry parameter", cptr, hp);
continue;
}
if (sp->req_header.rqs_flags & RQSFLAG_BADCOUNT) {
isp_print_qentry(isp, "invalid entry count", cptr, hp);
continue;
}
if (sp->req_header.rqs_flags & RQSFLAG_BADORDER) {
isp_print_qentry(isp, "invalid entry order", cptr, hp);
continue;
}
etype = sp->req_header.rqs_entry_type;
if (cont > 0 && etype != RQSTYPE_STATUS_CONT) {
cont = 0;
isp_done(cont_xs);
}
if (isp_handle_control(isp, hp)) {
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
}
switch (etype) {
case RQSTYPE_RESPONSE:
isp_get_24xx_response(isp, (isp24xx_statusreq_t *)hp, sp);
break;
case RQSTYPE_MARKER:
isp_prt(isp, ISP_LOG_WARN1, "Marker Response");
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
case RQSTYPE_STATUS_CONT:
isp_get_cont_response(isp, (ispstatus_cont_t *)hp, scp);
if (cont > 0) {
slen = min(cont, sizeof(scp->req_sense_data));
XS_SENSE_APPEND(cont_xs, scp->req_sense_data, slen);
cont -= slen;
if (cont == 0) {
isp_done(cont_xs);
} else {
isp_prt(isp, ISP_LOGDEBUG0|ISP_LOG_CWARN,
"Expecting Status Continuations for %u bytes",
cont);
}
} else {
isp_prt(isp, ISP_LOG_WARN1, "Ignored Continuation Response");
}
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
#ifdef ISP_TARGET_MODE
case RQSTYPE_NOTIFY_ACK:
case RQSTYPE_CTIO7:
case RQSTYPE_ABTS_RCVD:
case RQSTYPE_ABTS_RSP:
isp_target_notify(isp, hp, &cptr, RESULT_QUEUE_LEN(isp));
while (sptr != cptr) {
ISP_MEMZERO(hp, QENTRY_LEN);
sptr = ISP_NXT_QENTRY(sptr, RESULT_QUEUE_LEN(isp));
hp = (isphdr_t *)ISP_QUEUE_ENTRY(isp->isp_result, sptr);
}
ISP_MEMZERO(hp, QENTRY_LEN);
optr = ISP_NXT_QENTRY(cptr, RESULT_QUEUE_LEN(isp));
continue;
#endif
case RQSTYPE_RPT_ID_ACQ:
isp_handle_rpt_id_acq(isp, hp);
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
default:
isp_prt(isp, ISP_LOGERR, notresp, etype, cptr, optr);
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
}
xs = isp_find_xs(isp, sp->req_handle);
if (xs == NULL) {
if (sp->req_completion_status != RQCS_ABORTED &&
sp->req_completion_status != RQCS_RESET_OCCURRED)
isp_prt(isp, ISP_LOGERR, "cannot find handle 0x%x (status 0x%x)",
sp->req_handle, sp->req_completion_status);
ISP_MEMZERO(hp, QENTRY_LEN);
continue;
}
resp = snsp = sp->req_rsp_sense;
rlen = slen = totslen = 0;
scsi_status = sp->req_scsi_status;
if (scsi_status & RQCS_RV) {
rlen = sp->req_response_len;
snsp += rlen;
}
if (scsi_status & RQCS_SV) {
totslen = sp->req_sense_len;
slen = MIN(totslen, sizeof(sp->req_rsp_sense) - rlen);
}
*XS_STSP(xs) = scsi_status & 0xff;
if (scsi_status & RQCS_RESID)
XS_SET_RESID(xs, sp->req_fcp_residual);
else
XS_SET_RESID(xs, 0);
if (rlen >= 4 && resp[FCP_RSPNS_CODE_OFFSET] != 0) {
const char *ptr;
char lb[64];
const char *rnames[10] = {
"Task Management function complete",
"FCP_DATA length different than FCP_BURST_LEN",
"FCP_CMND fields invalid",
"FCP_DATA parameter mismatch with FCP_DATA_RO",
"Task Management function rejected",
"Task Management function failed",
NULL,
NULL,
"Task Management function succeeded",
"Task Management function incorrect logical unit number",
};
uint8_t code = resp[FCP_RSPNS_CODE_OFFSET];
if (code >= nitems(rnames) || rnames[code] == NULL) {
ISP_SNPRINTF(lb, sizeof(lb),
"Unknown FCP Response Code 0x%x", code);
ptr = lb;
} else {
ptr = rnames[code];
}
isp_xs_prt(isp, xs, ISP_LOGWARN,
"FCP RESPONSE, LENGTH %u: %s CDB0=0x%02x",
rlen, ptr, XS_CDBP(xs)[0] & 0xff);
if (code != FCP_RSPNS_TMF_DONE &&
code != FCP_RSPNS_TMF_SUCCEEDED)
XS_SETERR(xs, HBA_BOTCH);
}
isp_parse_status_24xx(isp, sp, xs);
if (slen > 0) {
XS_SAVE_SENSE(xs, snsp, slen);
if (totslen > slen) {
cont = totslen - slen;
cont_xs = xs;
isp_prt(isp, ISP_LOGDEBUG0|ISP_LOG_CWARN,
"Expecting Status Continuations for %u bytes",
cont);
}
}
ISP_DMAFREE(isp, xs);
isp_destroy_handle(isp, sp->req_handle);
ISP_MEMZERO(hp, QENTRY_LEN);
if (cont == 0)
isp_done(xs);
}
if (cont > 0)
isp_done(cont_xs);
if (optr != isp->isp_resodx) {
ISP_WRITE(isp, BIU2400_RSPOUTP, optr);
isp->isp_resodx = optr;
}
}
void
isp_intr_async(ispsoftc_t *isp, uint16_t mbox)
{
fcparam *fcp;
uint16_t chan;
isp_prt(isp, ISP_LOGDEBUG2, "Async Mbox 0x%x", mbox);
switch (mbox) {
case ASYNC_SYSTEM_ERROR:
isp->isp_state = ISP_CRASHED;
for (chan = 0; chan < isp->isp_nchan; chan++) {
FCPARAM(isp, chan)->isp_loopstate = LOOP_NIL;
isp_change_fw_state(isp, chan, FW_CONFIG_WAIT);
}
if (isp->isp_mboxbsy) {
isp->isp_obits = 1;
isp->isp_mboxtmp[0] = MBOX_HOST_INTERFACE_ERROR;
isp->isp_mboxbsy = 0;
}
isp_async(isp, ISPASYNC_FW_CRASH);
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_ATIO_XFER_ERR:
isp_prt(isp, ISP_LOGERR, "ATIO Queue Transfer Error");
break;
case ASYNC_LIP_OCCURRED:
case ASYNC_LIP_NOS_OLS_RECV:
case ASYNC_LIP_ERROR:
case ASYNC_PTPMODE:
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
int topo = fcp->isp_topo;
if (fcp->role == ISP_ROLE_NONE)
continue;
if (fcp->isp_loopstate > LOOP_HAVE_LINK)
fcp->isp_loopstate = LOOP_HAVE_LINK;
ISP_SET_SENDMARKER(isp, chan, 1);
isp_async(isp, ISPASYNC_LIP, chan);
#ifdef ISP_TARGET_MODE
isp_target_async(isp, chan, mbox);
#endif
if (topo == TOPO_NL_PORT || topo == TOPO_FL_PORT) {
int i, j;
for (i = j = 0; i < ISP_HANDLE_NUM(isp); i++) {
XS_T *xs;
isp_hdl_t *hdp;
hdp = &isp->isp_xflist[i];
if (ISP_H2HT(hdp->handle) != ISP_HANDLE_INITIATOR) {
continue;
}
xs = hdp->cmd;
if (XS_CHANNEL(xs) != chan) {
continue;
}
j++;
isp_prt(isp, ISP_LOG_WARN1,
"%d.%d.%jx bus reset set at %s:%u",
XS_CHANNEL(xs), XS_TGT(xs),
(uintmax_t)XS_LUN(xs),
__func__, __LINE__);
XS_SETERR(xs, HBA_BUSRESET);
}
if (j) {
isp_prt(isp, ISP_LOGERR, lipd, chan, j);
}
}
}
break;
case ASYNC_LOOP_UP:
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
fcp->isp_linkstate = 1;
if (fcp->isp_loopstate < LOOP_HAVE_LINK)
fcp->isp_loopstate = LOOP_HAVE_LINK;
ISP_SET_SENDMARKER(isp, chan, 1);
isp_async(isp, ISPASYNC_LOOP_UP, chan);
#ifdef ISP_TARGET_MODE
isp_target_async(isp, chan, mbox);
#endif
}
break;
case ASYNC_LOOP_DOWN:
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
ISP_SET_SENDMARKER(isp, chan, 1);
fcp->isp_linkstate = 0;
fcp->isp_loopstate = LOOP_NIL;
isp_async(isp, ISPASYNC_LOOP_DOWN, chan);
#ifdef ISP_TARGET_MODE
isp_target_async(isp, chan, mbox);
#endif
}
break;
case ASYNC_LOOP_RESET:
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
ISP_SET_SENDMARKER(isp, chan, 1);
if (fcp->isp_loopstate > LOOP_HAVE_LINK)
fcp->isp_loopstate = LOOP_HAVE_LINK;
isp_async(isp, ISPASYNC_LOOP_RESET, chan);
#ifdef ISP_TARGET_MODE
isp_target_async(isp, chan, mbox);
#endif
}
break;
case ASYNC_PDB_CHANGED:
{
int echan, nphdl, nlstate, reason;
nphdl = ISP_READ(isp, OUTMAILBOX1);
nlstate = ISP_READ(isp, OUTMAILBOX2);
reason = ISP_READ(isp, OUTMAILBOX3) >> 8;
if (ISP_CAP_MULTI_ID(isp)) {
chan = ISP_READ(isp, OUTMAILBOX3) & 0xff;
if (chan == 0xff || nphdl == NIL_HANDLE) {
chan = 0;
echan = isp->isp_nchan - 1;
} else if (chan >= isp->isp_nchan) {
break;
} else {
echan = chan;
}
} else {
chan = echan = 0;
}
for (; chan <= echan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
if (fcp->isp_loopstate > LOOP_LTEST_DONE) {
if (nphdl != NIL_HANDLE &&
nphdl == fcp->isp_login_hdl &&
reason == PDB24XX_AE_OPN_2)
continue;
fcp->isp_loopstate = LOOP_LTEST_DONE;
} else if (fcp->isp_loopstate < LOOP_HAVE_LINK)
fcp->isp_loopstate = LOOP_HAVE_LINK;
isp_async(isp, ISPASYNC_CHANGE_NOTIFY, chan,
ISPASYNC_CHANGE_PDB, nphdl, nlstate, reason);
}
break;
}
case ASYNC_CHANGE_NOTIFY:
{
int portid;
portid = ((ISP_READ(isp, OUTMAILBOX1) & 0xff) << 16) |
ISP_READ(isp, OUTMAILBOX2);
if (ISP_CAP_MULTI_ID(isp)) {
chan = ISP_READ(isp, OUTMAILBOX3) & 0xff;
if (chan >= isp->isp_nchan)
break;
} else {
chan = 0;
}
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
break;
if (fcp->isp_loopstate > LOOP_LTEST_DONE)
fcp->isp_loopstate = LOOP_LTEST_DONE;
else if (fcp->isp_loopstate < LOOP_HAVE_LINK)
fcp->isp_loopstate = LOOP_HAVE_LINK;
isp_async(isp, ISPASYNC_CHANGE_NOTIFY, chan,
ISPASYNC_CHANGE_SNS, portid);
break;
}
case ASYNC_ERR_LOGGING_DISABLED:
isp_prt(isp, ISP_LOGWARN, "Error logging disabled (reason 0x%x)",
ISP_READ(isp, OUTMAILBOX1));
break;
case ASYNC_P2P_INIT_ERR:
isp_prt(isp, ISP_LOGWARN, "P2P init error (reason 0x%x)",
ISP_READ(isp, OUTMAILBOX1));
break;
case ASYNC_RCV_ERR:
isp_prt(isp, ISP_LOGWARN, "Receive Error");
break;
case ASYNC_RJT_SENT:
isp_prt(isp, ISP_LOGTDEBUG0, "LS_RJT sent");
break;
case ASYNC_FW_RESTART_COMPLETE:
isp_prt(isp, ISP_LOGDEBUG0, "FW restart complete");
break;
case ASYNC_TEMPERATURE_ALERT:
isp_prt(isp, ISP_LOGERR, "Temperature alert (subcode 0x%x)",
ISP_READ(isp, OUTMAILBOX1));
break;
case ASYNC_INTER_DRIVER_COMP:
isp_prt(isp, ISP_LOGDEBUG0, "Inter-driver communication complete");
break;
case ASYNC_INTER_DRIVER_NOTIFY:
isp_prt(isp, ISP_LOGDEBUG0, "Inter-driver communication notification");
break;
case ASYNC_INTER_DRIVER_TIME_EXT:
isp_prt(isp, ISP_LOGDEBUG0, "Inter-driver communication time extended");
break;
case ASYNC_TRANSCEIVER_INSERTION:
isp_prt(isp, ISP_LOGDEBUG0, "Transceiver insertion (0x%x)",
ISP_READ(isp, OUTMAILBOX1));
break;
case ASYNC_TRANSCEIVER_REMOVAL:
isp_prt(isp, ISP_LOGDEBUG0, "Transceiver removal");
break;
case ASYNC_NIC_FW_STATE_CHANGE:
isp_prt(isp, ISP_LOGDEBUG0, "NIC Firmware State Change");
break;
case ASYNC_AUTOLOAD_FW_COMPLETE:
isp_prt(isp, ISP_LOGDEBUG0, "Autoload FW init complete");
break;
case ASYNC_AUTOLOAD_FW_FAILURE:
isp_prt(isp, ISP_LOGERR, "Autoload FW init failure");
break;
default:
isp_prt(isp, ISP_LOGWARN, "Unknown Async Code 0x%x", mbox);
break;
}
}
static int
isp_handle_control(ispsoftc_t *isp, isphdr_t *hp)
{
uint32_t hdl;
void *ptr;
switch (hp->rqs_entry_type) {
case RQSTYPE_RESPONSE:
case RQSTYPE_MARKER:
case RQSTYPE_NOTIFY_ACK:
case RQSTYPE_CTIO7:
case RQSTYPE_TSK_MGMT:
case RQSTYPE_CT_PASSTHRU:
case RQSTYPE_VP_MODIFY:
case RQSTYPE_VP_CTRL:
case RQSTYPE_ABORT_IO:
case RQSTYPE_MBOX:
case RQSTYPE_LOGIN:
case RQSTYPE_ELS_PASSTHRU:
ISP_IOXGET_32(isp, (uint32_t *)(hp + 1), hdl);
if (ISP_H2HT(hdl) != ISP_HANDLE_CTRL)
break;
ptr = isp_find_xs(isp, hdl);
if (ptr != NULL) {
isp_destroy_handle(isp, hdl);
memcpy(ptr, hp, QENTRY_LEN);
wakeup(ptr);
}
return (1);
}
return (0);
}
static void
isp_handle_rpt_id_acq(ispsoftc_t *isp, isphdr_t *hp)
{
fcparam *fcp;
isp_ridacq_t rid;
int chan, c;
uint32_t portid;
isp_get_ridacq(isp, (isp_ridacq_t *)hp, &rid);
portid = (uint32_t)rid.ridacq_vp_port_hi << 16 |
rid.ridacq_vp_port_lo;
if (rid.ridacq_format == 0) {
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if (fcp->role == ISP_ROLE_NONE)
continue;
c = (chan == 0) ? 127 : (chan - 1);
if (rid.ridacq_map[c / 16] & (1 << (c % 16)) ||
chan == 0) {
fcp->isp_loopstate = LOOP_HAVE_LINK;
isp_async(isp, ISPASYNC_CHANGE_NOTIFY,
chan, ISPASYNC_CHANGE_OTHER);
} else {
fcp->isp_loopstate = LOOP_NIL;
isp_async(isp, ISPASYNC_LOOP_DOWN,
chan);
}
}
} else {
fcp = FCPARAM(isp, rid.ridacq_vp_index);
if (rid.ridacq_vp_status == RIDACQ_STS_COMPLETE ||
rid.ridacq_vp_status == RIDACQ_STS_CHANGED) {
fcp->isp_topo = (rid.ridacq_map[0] >> 9) & 0x7;
fcp->isp_portid = portid;
fcp->isp_loopstate = LOOP_HAVE_ADDR;
isp_async(isp, ISPASYNC_CHANGE_NOTIFY,
rid.ridacq_vp_index, ISPASYNC_CHANGE_OTHER);
} else {
fcp->isp_loopstate = LOOP_NIL;
isp_async(isp, ISPASYNC_LOOP_DOWN,
rid.ridacq_vp_index);
}
}
}
static void
isp_parse_status_24xx(ispsoftc_t *isp, isp24xx_statusreq_t *sp, XS_T *xs)
{
int ru_marked, sv_marked;
int chan = XS_CHANNEL(xs);
switch (sp->req_completion_status) {
case RQCS_COMPLETE:
return;
case RQCS_DMA_ERROR:
isp_xs_prt(isp, xs, ISP_LOGERR, "DMA error");
if (XS_NOERR(xs))
XS_SETERR(xs, HBA_BOTCH);
break;
case RQCS_TRANSPORT_ERROR:
isp_xs_prt(isp, xs, ISP_LOGERR, "Transport Error");
if (XS_NOERR(xs))
XS_SETERR(xs, HBA_BOTCH);
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_DRE:
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_BOTCH);
return;
case RQCS_TABORT:
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;
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_LOG_WARN1, "Data Underrun (%d) for command 0x%x", sp->req_resid, XS_CDBP(xs)[0] & 0xff);
return;
case RQCS_PORT_UNAVAILABLE:
case RQCS_PORT_LOGGED_OUT:
{
const char *reason;
uint8_t sts = sp->req_completion_status & 0xff;
fcparam *fcp = FCPARAM(isp, XS_CHANNEL(xs));
fcportdb_t *lp;
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));
if (XS_NOERR(xs)) {
lp = &fcp->portdb[XS_TGT(xs)];
if (lp->state == FC_PORTDB_STATE_ZOMBIE) {
*XS_STSP(xs) = SCSI_BUSY;
XS_SETERR(xs, HBA_TGTBSY);
} else
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_STSP(xs) = SCSI_BUSY;
XS_SETERR(xs, HBA_TGTBSY);
}
return;
case RQCS_ENOMEM:
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_TMO:
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);
}
#define ISP_FC_IBITS(op) ((mbpfc[((op)<<3) + 0] << 24) | (mbpfc[((op)<<3) + 1] << 16) | (mbpfc[((op)<<3) + 2] << 8) | (mbpfc[((op)<<3) + 3]))
#define ISP_FC_OBITS(op) ((mbpfc[((op)<<3) + 4] << 24) | (mbpfc[((op)<<3) + 5] << 16) | (mbpfc[((op)<<3) + 6] << 8) | (mbpfc[((op)<<3) + 7]))
#define ISP_FC_OPMAP(in0, out0) 0, 0, 0, in0, 0, 0, 0, out0
#define ISP_FC_OPMAP_HALF(in1, in0, out1, out0) 0, 0, in1, in0, 0, 0, out1, out0
#define ISP_FC_OPMAP_FULL(in3, in2, in1, in0, out3, out2, out1, out0) in3, in2, in1, in0, out3, out2, out1, out0
static const uint32_t mbpfc[] = {
ISP_FC_OPMAP(0x01, 0x01),
ISP_FC_OPMAP(0x1f, 0x01),
ISP_FC_OPMAP_HALF(0x07, 0xff, 0x00, 0x1f),
ISP_FC_OPMAP(0x01, 0x07),
ISP_FC_OPMAP(0x07, 0x07),
ISP_FC_OPMAP(0x03, 0x07),
ISP_FC_OPMAP_FULL(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
ISP_FC_OPMAP(0x07, 0x07),
ISP_FC_OPMAP_FULL(0x0, 0x0, 0x0, 0x01, 0x0, 0x3, 0x80, 0x7f),
ISP_FC_OPMAP(0xdf, 0x01),
ISP_FC_OPMAP(0xdf, 0x01),
ISP_FC_OPMAP_HALF(0x1, 0xff, 0x0, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP_HALF(0x1, 0x0f, 0x0, 0x01),
ISP_FC_OPMAP(0x01, 0x05),
ISP_FC_OPMAP_HALF(0x1, 0x03, 0x0, 0x0d),
ISP_FC_OPMAP(0x1f, 0x11),
ISP_FC_OPMAP(0x2f, 0x21),
ISP_FC_OPMAP(0x0f, 0x01),
ISP_FC_OPMAP(0x03, 0x03),
ISP_FC_OPMAP_HALF(0x1, 0xff, 0x0, 0x03),
ISP_FC_OPMAP(0x4f, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP(0x03, 0x03),
ISP_FC_OPMAP(0x07, 0x05),
ISP_FC_OPMAP(0x07, 0x05),
ISP_FC_OPMAP(0x07, 0x05),
ISP_FC_OPMAP(0x07, 0x05),
ISP_FC_OPMAP(0x07, 0x03),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x01, 0x07),
ISP_FC_OPMAP_HALF(0x2, 0x01, 0x7e, 0xcf),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x03, 0x4b),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x01, 0x03),
ISP_FC_OPMAP(0x03, 0x07),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x4b, 0x4b),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x0f, 0x01),
ISP_FC_OPMAP(0x0f, 0x07),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x03, 0x01),
ISP_FC_OPMAP(0x03, 0x01),
ISP_FC_OPMAP_HALF(0x0, 0x01, 0x1f, 0xcf),
ISP_FC_OPMAP(0x01, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0xcf, 0x03),
ISP_FC_OPMAP(0xcf, 0x0f),
ISP_FC_OPMAP(0xcd, 0x01),
ISP_FC_OPMAP_HALF(0x2, 0xcd, 0x0, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0xcf, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x03, 0x01),
ISP_FC_OPMAP(0xcf, 0x01),
ISP_FC_OPMAP(0x07, 0x1f),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0xcf, 0x0f),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x01, 0x01),
ISP_FC_OPMAP(0xcd, 0x03),
ISP_FC_OPMAP(0xcf, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP_HALF(0x00, 0x01, 0x0f, 0x1f),
ISP_FC_OPMAP_HALF(0x6, 0x03, 0x0, 0xcf),
ISP_FC_OPMAP(0xcf, 0x01),
ISP_FC_OPMAP(0x0f, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0xcf, 0x03),
ISP_FC_OPMAP(0x0f, 0x07),
ISP_FC_OPMAP_HALF(0x02, 0x03, 0x00, 0x03),
ISP_FC_OPMAP(0x03, 0x03),
ISP_FC_OPMAP(0x0f, 0x0f),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x07, 0x01),
ISP_FC_OPMAP_HALF(0x03, 0xcf, 0x00, 0x07),
ISP_FC_OPMAP(0x4f, 0x01),
ISP_FC_OPMAP(0xcd, 0x01),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP(0x00, 0x00),
ISP_FC_OPMAP_HALF(0x03, 0x4f, 0x00, 0x07),
ISP_FC_OPMAP(0xcf, 0x01),
ISP_FC_OPMAP(0x0f, 0x01)
};
#define MAX_FC_OPCODE 0x7e
static const char *fc_mbcmd_names[] = {
"NO-OP",
"LOAD RAM",
"EXEC FIRMWARE",
"LOAD FLASH FIRMWARE",
"WRITE RAM WORD",
"READ RAM WORD",
"MAILBOX REG TEST",
"VERIFY CHECKSUM",
"ABOUT FIRMWARE",
"LOAD RAM (2100)",
"DUMP RAM (2100)",
"LOAD RISC RAM",
"DUMP RISC RAM",
"WRITE RAM WORD EXTENDED",
"CHECK FIRMWARE",
"READ RAM WORD EXTENDED",
"INIT REQUEST QUEUE",
"INIT RESULT QUEUE",
"EXECUTE IOCB",
"WAKE UP",
"STOP FIRMWARE",
"ABORT",
"ABORT DEVICE",
"ABORT TARGET",
"BUS RESET",
"STOP QUEUE",
"START QUEUE",
"SINGLE STEP QUEUE",
"ABORT QUEUE",
"GET DEV QUEUE STATUS",
NULL,
"GET FIRMWARE STATUS",
"GET LOOP ID",
NULL,
"GET TIMEOUT PARAMS",
NULL,
NULL,
NULL,
NULL,
NULL,
"GET FIRMWARE OPTIONS",
"GET PORT QUEUE PARAMS",
"GENERATE SYSTEM ERROR",
NULL,
NULL,
NULL,
NULL,
NULL,
"WRITE SFP",
"READ SFP",
"SET TIMEOUT PARAMS",
NULL,
NULL,
NULL,
NULL,
NULL,
"SET FIRMWARE OPTIONS",
"SET PORT QUEUE PARAMS",
NULL,
"SET FC LED CONF",
NULL,
"RESTART NIC FIRMWARE",
"ACCESS CONTROL",
NULL,
"LOOP PORT BYPASS",
"LOOP PORT ENABLE",
"GET RESOURCE COUNT",
"REQUEST NON PARTICIPATING MODE",
"DIAGNOSTIC ECHO TEST",
"DIAGNOSTIC LOOPBACK",
NULL,
"GET PORT DATABASE ENHANCED",
"INIT FIRMWARE MULTI ID",
"GET VP DATABASE",
"GET VP DATABASE ENTRY",
NULL,
NULL,
NULL,
NULL,
NULL,
"GET FCF LIST",
"GET DCBX PARAMETERS",
NULL,
"HOST MEMORY COPY",
"EXECUTE IOCB A64",
NULL,
NULL,
"SEND RNID",
NULL,
"SET PARAMETERS",
"GET PARAMETERS",
"DRIVER HEARTBEAT",
"FIRMWARE HEARTBEAT",
"GET/SET DATA RATE",
"SEND RNFT",
NULL,
"INIT FIRMWARE",
"GET INIT CONTROL BLOCK",
"INIT LIP",
"GET FC-AL POSITION MAP",
"GET PORT DATABASE",
"CLEAR ACA",
"TARGET RESET",
"CLEAR TASK SET",
"ABORT TASK SET",
"GET FW STATE",
"GET PORT NAME",
"GET LINK STATUS",
"INIT LIP RESET",
"GET LINK STATS & PRIVATE DATA CNTS",
"SEND SNS",
"FABRIC LOGIN",
"SEND CHANGE REQUEST",
"FABRIC LOGOUT",
"INIT LIP LOGIN",
NULL,
"LOGIN LOOP PORT",
"GET PORT/NODE NAME LIST",
"SET VENDOR ID",
"INITIALIZE IP MAILBOX",
NULL,
NULL,
"GET XGMAC STATS",
NULL,
"GET ID LIST",
"SEND LFA",
"LUN RESET"
};
static void
isp_mboxcmd(ispsoftc_t *isp, mbreg_t *mbp)
{
const char *cname, *xname, *sname;
char tname[16], mname[16];
unsigned int ibits, obits, box, opcode, t, to;
opcode = mbp->param[0];
if (opcode > MAX_FC_OPCODE) {
mbp->param[0] = MBOX_INVALID_COMMAND;
isp_prt(isp, ISP_LOGERR, "Unknown Command 0x%x", opcode);
return;
}
cname = fc_mbcmd_names[opcode];
ibits = ISP_FC_IBITS(opcode);
obits = ISP_FC_OBITS(opcode);
if (cname == NULL) {
cname = tname;
ISP_SNPRINTF(tname, sizeof(tname), "opcode %x", opcode);
}
isp_prt(isp, ISP_LOGDEBUG3, "Mailbox Command '%s'", cname);
ibits |= mbp->ibits;
obits |= mbp->obits;
ibits &= mbp->ibitm;
obits &= mbp->obitm;
if (ibits == 0 && obits == 0) {
mbp->param[0] = MBOX_COMMAND_PARAM_ERROR;
isp_prt(isp, ISP_LOGERR, "no parameters for 0x%x", opcode);
return;
}
for (box = 0; box < ISP_NMBOX(isp); box++) {
if (ibits & (1 << box)) {
isp_prt(isp, ISP_LOGDEBUG3, "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;
}
isp->isp_obits = obits;
isp->isp_mboxbsy = 1;
ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_SET_HOST_INT);
to = (mbp->timeout == 0) ? MBCMD_DEFAULT_TIMEOUT : mbp->timeout;
for (t = 0; t < to; t += 100) {
if (!isp->isp_mboxbsy)
break;
ISP_RUN_ISR(isp);
if (!isp->isp_mboxbsy)
break;
ISP_DELAY(100);
}
if (isp->isp_mboxbsy) {
isp->isp_mboxbsy = 0;
isp_prt(isp, ISP_LOGWARN, "Mailbox Command (0x%x) Timeout (%uus) (%s:%d)",
opcode, to, mbp->func, mbp->lineno);
mbp->param[0] = MBOX_TIMEOUT;
goto out;
}
for (box = 0; box < ISP_NMBOX(isp); box++) {
if (obits & (1 << box)) {
mbp->param[box] = isp->isp_mboxtmp[box];
isp_prt(isp, ISP_LOGDEBUG3, "OUT mbox %d = 0x%04x", box,
mbp->param[box]);
}
}
out:
if (mbp->logval == 0 || mbp->param[0] == MBOX_COMMAND_COMPLETE)
return;
if ((mbp->param[0] & 0xbfe0) == 0 &&
(mbp->logval & MBLOGMASK(mbp->param[0])) == 0)
return;
xname = NULL;
sname = "";
switch (mbp->param[0]) {
case MBOX_INVALID_COMMAND:
xname = "INVALID COMMAND";
break;
case MBOX_HOST_INTERFACE_ERROR:
xname = "HOST INTERFACE ERROR";
break;
case MBOX_TEST_FAILED:
xname = "TEST FAILED";
break;
case MBOX_COMMAND_ERROR:
xname = "COMMAND ERROR";
ISP_SNPRINTF(mname, sizeof(mname), " subcode 0x%x",
mbp->param[1]);
sname = mname;
break;
case MBOX_COMMAND_PARAM_ERROR:
xname = "COMMAND PARAMETER ERROR";
break;
case MBOX_PORT_ID_USED:
xname = "PORT ID ALREADY IN USE";
break;
case MBOX_LOOP_ID_USED:
xname = "LOOP ID ALREADY IN USE";
break;
case MBOX_ALL_IDS_USED:
xname = "ALL LOOP IDS IN USE";
break;
case MBOX_NOT_LOGGED_IN:
xname = "NOT LOGGED IN";
break;
case MBOX_LINK_DOWN_ERROR:
xname = "LINK DOWN ERROR";
break;
case MBOX_LOOPBACK_ERROR:
xname = "LOOPBACK ERROR";
break;
case MBOX_CHECKSUM_ERROR:
xname = "CHECKSUM ERROR";
break;
case MBOX_INVALID_PRODUCT_KEY:
xname = "INVALID PRODUCT KEY";
break;
case MBOX_REGS_BUSY:
xname = "REGISTERS BUSY";
break;
case MBOX_TIMEOUT:
xname = "TIMEOUT";
break;
default:
ISP_SNPRINTF(mname, sizeof(mname), "error 0x%x", mbp->param[0]);
xname = mname;
break;
}
if (xname) {
isp_prt(isp, ISP_LOGALL, "Mailbox Command '%s' failed (%s%s)",
cname, xname, sname);
}
}
static int
isp_fw_state(ispsoftc_t *isp, int chan)
{
mbreg_t mbs;
MBSINIT(&mbs, MBOX_GET_FW_STATE, MBLOGALL, 0);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] == MBOX_COMMAND_COMPLETE)
return (mbs.param[1]);
return (FW_ERROR);
}
static void
isp_setdfltfcparm(ispsoftc_t *isp, int chan)
{
fcparam *fcp = FCPARAM(isp, chan);
fcp->role = DEFAULT_ROLE(isp, chan);
fcp->isp_retry_delay = ICB_DFLT_RDELAY;
fcp->isp_retry_count = ICB_DFLT_RCOUNT;
fcp->isp_loopid = DEFAULT_LOOPID(isp, chan);
fcp->isp_wwnn_nvram = DEFAULT_NODEWWN(isp, chan);
fcp->isp_wwpn_nvram = DEFAULT_PORTWWN(isp, chan);
fcp->isp_fwoptions = 0;
fcp->isp_xfwoptions = 0;
fcp->isp_zfwoptions = 0;
fcp->isp_lasthdl = NIL_HANDLE;
fcp->isp_login_hdl = NIL_HANDLE;
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;
fcp->isp_xfwoptions |= ICB2400_OPT2_LOOP_2_PTP;
fcp->isp_zfwoptions |= ICB2400_OPT3_RATE_AUTO;
if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) {
int i, j = 0;
for (i = 0; i < 2; i++) {
j = isp_read_nvram(isp);
if (j == 0) {
break;
}
}
if (j) {
isp->isp_confopts |= ISP_CFG_NONVRAM;
}
}
fcp->isp_wwnn = ACTIVE_NODEWWN(isp, chan);
fcp->isp_wwpn = ACTIVE_PORTWWN(isp, chan);
isp_prt(isp, ISP_LOGCONFIG, "Chan %d 0x%08x%08x/0x%08x%08x Role %s",
chan, (uint32_t) (fcp->isp_wwnn >> 32), (uint32_t) (fcp->isp_wwnn),
(uint32_t) (fcp->isp_wwpn >> 32), (uint32_t) (fcp->isp_wwpn),
isp_class3_roles[fcp->role]);
}
int
isp_reinit(ispsoftc_t *isp, int do_load_defaults)
{
int i, res = 0;
if (isp->isp_state > ISP_RESETSTATE)
isp_stop(isp);
if (isp->isp_state != ISP_RESETSTATE)
isp_reset(isp, do_load_defaults);
if (isp->isp_state != ISP_RESETSTATE) {
res = EIO;
isp_prt(isp, ISP_LOGERR, "%s: cannot reset card", __func__);
goto cleanup;
}
isp_init(isp);
if (isp->isp_state > ISP_RESETSTATE &&
isp->isp_state != ISP_RUNSTATE) {
res = EIO;
isp_prt(isp, ISP_LOGERR, "%s: cannot init card", __func__);
ISP_DISABLE_INTS(isp);
}
cleanup:
isp_clear_commands(isp);
for (i = 0; i < isp->isp_nchan; i++)
isp_clear_portdb(isp, i);
return (res);
}
static inline uint32_t
flash_data_addr(ispsoftc_t *isp, uint32_t faddr)
{
fcparam *fcp = FCPARAM(isp, 0);
return (fcp->flash_data_addr + faddr);
}
static int
isp_read_flash_dword(ispsoftc_t *isp, uint32_t addr, uint32_t *data)
{
int loops = 0;
ISP_WRITE(isp, BIU2400_FLASH_ADDR, addr & ~0x80000000);
for (loops = 0; loops < 30000; loops++) {
if (ISP_READ(isp, BIU2400_FLASH_ADDR & 0x80000000)) {
*data = ISP_READ(isp, BIU2400_FLASH_DATA);
return (ISP_SUCCESS);
}
ISP_DELAY(10);
}
isp_prt(isp, ISP_LOGERR,
"Flash read dword at 0x%x timeout.", addr);
*data = 0xffffffff;
return (ISP_FUNCTION_TIMEOUT);
}
static int
isp_read_flash_data(ispsoftc_t *isp, uint32_t *dwptr, uint32_t faddr, uint32_t dwords)
{
int loops = 0;
int rval = ISP_SUCCESS;
faddr = flash_data_addr(isp, faddr);
for (loops = 0; loops < dwords; loops++, faddr++, dwptr++) {
rval = isp_read_flash_dword(isp, faddr, dwptr);
if (rval != ISP_SUCCESS)
break;
*dwptr = htole32(*dwptr);
}
return (rval);
}
static void
isp_rd_2xxx_flash(ispsoftc_t *isp, uint32_t addr, uint32_t *rp)
{
fcparam *fcp = FCPARAM(isp, 0);
int loops = 0;
uint32_t base = fcp->flash_data_addr;
ISP_WRITE(isp, BIU2400_FLASH_ADDR, (base + addr) & ~0x80000000);
for (loops = 0; loops < 30000; loops++) {
ISP_DELAY(10);
if (ISP_READ(isp, BIU2400_FLASH_ADDR & 0x80000000)) {
*rp = ISP_READ(isp, BIU2400_FLASH_DATA);
ISP_SWIZZLE_NVRAM_LONG(isp, rp);
return;
}
}
isp_prt(isp, ISP_LOGERR,
"Flash read dword at 0x%x timeout.", (base + addr));
*rp = 0xffffffff;
}
static int
isp_read_flthdr_2xxx(ispsoftc_t *isp)
{
fcparam *fcp = FCPARAM(isp, 0);
int retval = 0;
uint32_t addr, lwrds, *dptr;
uint16_t csum;
uint8_t flthdr_data[FLT_HEADER_SIZE];
addr = fcp->flt_region_flt;
dptr = (uint32_t *) flthdr_data;
isp_prt(isp, ISP_LOGDEBUG0, "FLTL[DEF]: 0x%x", addr);
for (lwrds = 0; lwrds < FLT_HEADER_SIZE >> 2; lwrds++) {
isp_rd_2xxx_flash(isp, addr++, dptr++);
}
dptr = (uint32_t *) flthdr_data;
for (csum = 0, lwrds = 0; lwrds < FLT_HEADER_SIZE >> 4; lwrds++) {
uint16_t tmp;
ISP_IOXGET_16(isp, &dptr[lwrds], tmp);
csum += tmp;
}
if (csum != 0) {
retval = -1;
goto out;
}
isp_parse_flthdr_2xxx(isp, flthdr_data);
out:
return (retval);
}
static void
isp_parse_flthdr_2xxx(ispsoftc_t *isp, uint8_t *flthdr_data)
{
fcparam *fcp = FCPARAM(isp, 0);
uint16_t ver, csum;
ver = le16toh((uint16_t) (ISP2XXX_FLT_VERSION(flthdr_data)));
fcp->flt_length = le16toh((uint16_t) (ISP2XXX_FLT_LENGTH(flthdr_data)));
csum = le16toh((uint16_t) (ISP2XXX_FLT_CSUM(flthdr_data)));
if ((fcp->flt_length == 0) ||
(fcp->flt_length > (FLT_HEADER_SIZE + FLT_REGIONS_SIZE))) {
isp_prt(isp, ISP_LOGERR,
"FLT[DEF]: Invalid length=0x%x(%d)",
fcp->flt_length, fcp->flt_length);
}
isp_prt(isp, ISP_LOGDEBUG0,
"FLT[DEF]: version=0x%x length=0x%x(%d) checksum=0x%x",
ver, fcp->flt_length, fcp->flt_length, csum);
}
static int
isp_read_flt_2xxx(ispsoftc_t *isp)
{
fcparam *fcp = FCPARAM(isp, 0);
int retval = 0;
int len = fcp->flt_length - FLT_HEADER_SIZE;
uint32_t addr, lwrds, *dptr;
uint8_t flt_data[len];
fcp->flt_region_entries = len / FLT_REGION_SIZE;
addr = fcp->flt_region_flt + (FLT_HEADER_SIZE >> 2);
dptr = (uint32_t *) flt_data;
isp_prt(isp, ISP_LOGDEBUG0, "FLT[DEF]: regions=%d",
fcp->flt_region_entries);
for (lwrds = 0; lwrds < len >> 2; lwrds++) {
isp_rd_2xxx_flash(isp, addr++, dptr++);
}
retval = isp_parse_flt_2xxx(isp, flt_data);
return (retval);
}
static int
isp_parse_flt_2xxx(ispsoftc_t *isp, uint8_t *flt_data)
{
fcparam *fcp = FCPARAM(isp, 0);
int count;
struct flt_region region[fcp->flt_region_entries];
for (count = 0; count < fcp->flt_region_entries; count++) {
region[count].code =
le16toh((uint16_t) (ISP2XXX_FLT_REG_CODE(flt_data, count)));
region[count].attribute =
(uint8_t) (ISP2XXX_FLT_REG_ATTR(flt_data, count));
region[count].reserved =
(uint8_t) (ISP2XXX_FLT_REG_RES(flt_data, count));
region[count].size =
le32toh((uint32_t) (ISP2XXX_FLT_REG_SIZE(flt_data, count)) >> 2);
region[count].start =
le32toh((uint32_t) (ISP2XXX_FLT_REG_START(flt_data, count)) >> 2);
region[count].end =
le32toh((uint32_t) (ISP2XXX_FLT_REG_END(flt_data, count)) >> 2);
isp_prt(isp, ISP_LOGDEBUG0,
"FLT[0x%x]: start=0x%x end=0x%x size=0x%x attribute=0x%x",
region[count].code, region[count].start, region[count].end,
region[count].size, region[count].attribute);
switch (region[count].code) {
case FLT_REG_FW:
fcp->flt_region_fw = region[count].start;
break;
case FLT_REG_BOOT_CODE:
fcp->flt_region_boot = region[count].start;
break;
case FLT_REG_VPD_0:
fcp->flt_region_vpd_nvram = region[count].start;
if (isp->isp_port == 0)
fcp->flt_region_vpd = region[count].start;
break;
case FLT_REG_VPD_1:
if (isp->isp_port == 1)
fcp->flt_region_vpd = region[count].start;
break;
case FLT_REG_VPD_2:
if (!IS_27XX(isp))
break;
if (isp->isp_port == 2)
fcp->flt_region_vpd = region[count].start;
break;
case FLT_REG_VPD_3:
if (!IS_27XX(isp))
break;
if (isp->isp_port == 3)
fcp->flt_region_vpd = region[count].start;
break;
case FLT_REG_NVRAM_0:
if (isp->isp_port == 0)
fcp->flt_region_nvram = region[count].start;
break;
case FLT_REG_NVRAM_1:
if (isp->isp_port == 1)
fcp->flt_region_nvram = region[count].start;
break;
case FLT_REG_NVRAM_2:
if (!IS_27XX(isp))
break;
if (isp->isp_port == 2)
fcp->flt_region_nvram = region[count].start;
break;
case FLT_REG_NVRAM_3:
if (!IS_27XX(isp))
break;
if (isp->isp_port == 3)
fcp->flt_region_nvram = region[count].start;
break;
case FLT_REG_FDT:
fcp->flt_region_fdt = region[count].start;
break;
case FLT_REG_FLT:
fcp->flt_region_flt = region[count].start;
break;
case FLT_REG_NPIV_CONF_0:
if (isp->isp_port == 0)
fcp->flt_region_npiv_conf = region[count].start;
break;
case FLT_REG_NPIV_CONF_1:
if (isp->isp_port == 1)
fcp->flt_region_npiv_conf = region[count].start;
break;
case FLT_REG_GOLD_FW:
fcp->flt_region_gold_fw = region[count].start;
break;
case FLT_REG_FCP_PRIO_0:
if (isp->isp_port == 0)
fcp->flt_region_fcp_prio = region[count].start;
break;
case FLT_REG_FCP_PRIO_1:
if (isp->isp_port == 1)
fcp->flt_region_fcp_prio = region[count].start;
break;
case FLT_REG_IMG_PRI_27XX:
if (IS_27XX(isp))
fcp->flt_region_img_status_pri = region[count].start;
break;
case FLT_REG_IMG_SEC_27XX:
if (IS_27XX(isp))
fcp->flt_region_img_status_sec = region[count].start;
break;
case FLT_REG_FW_SEC_27XX:
if (IS_27XX(isp))
fcp->flt_region_fw_sec = region[count].start;
break;
case FLT_REG_BOOTLOAD_SEC_27XX:
if (IS_27XX(isp))
fcp->flt_region_boot_sec = region[count].start;
break;
case FLT_REG_AUX_IMG_PRI_28XX:
if (IS_27XX(isp))
fcp->flt_region_aux_img_status_pri = region[count].start;
break;
case FLT_REG_AUX_IMG_SEC_28XX:
if (IS_27XX(isp))
fcp->flt_region_aux_img_status_sec = region[count].start;
break;
case FLT_REG_NVRAM_SEC_28XX_0:
if (IS_27XX(isp))
if (isp->isp_port == 0)
fcp->flt_region_nvram_sec = region[count].start;
break;
case FLT_REG_NVRAM_SEC_28XX_1:
if (IS_27XX(isp))
if (isp->isp_port == 1)
fcp->flt_region_nvram_sec = region[count].start;
break;
case FLT_REG_NVRAM_SEC_28XX_2:
if (IS_27XX(isp))
if (isp->isp_port == 2)
fcp->flt_region_nvram_sec = region[count].start;
break;
case FLT_REG_NVRAM_SEC_28XX_3:
if (IS_27XX(isp))
if (isp->isp_port == 3)
fcp->flt_region_nvram_sec = region[count].start;
break;
case FLT_REG_VPD_SEC_27XX_0:
case FLT_REG_VPD_SEC_28XX_0:
if (IS_27XX(isp)) {
fcp->flt_region_vpd_nvram_sec = region[count].start;
if (isp->isp_port == 0)
fcp->flt_region_vpd_sec = region[count].start;
}
break;
case FLT_REG_VPD_SEC_27XX_1:
case FLT_REG_VPD_SEC_28XX_1:
if (IS_27XX(isp))
if (isp->isp_port == 1)
fcp->flt_region_vpd_sec = region[count].start;
break;
case FLT_REG_VPD_SEC_27XX_2:
case FLT_REG_VPD_SEC_28XX_2:
if (IS_27XX(isp))
if (isp->isp_port == 2)
fcp->flt_region_vpd_sec = region[count].start;
break;
case FLT_REG_VPD_SEC_27XX_3:
case FLT_REG_VPD_SEC_28XX_3:
if (IS_27XX(isp))
if (isp->isp_port == 3)
fcp->flt_region_vpd_sec = region[count].start;
break;
}
}
isp_prt(isp, ISP_LOGCONFIG,
"FLT[FLT]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x "
"fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x",
fcp->flt_region_boot, fcp->flt_region_fw, fcp->flt_region_vpd_nvram,
fcp->flt_region_vpd, fcp->flt_region_nvram, fcp->flt_region_fdt,
fcp->flt_region_flt, fcp->flt_region_npiv_conf,
fcp->flt_region_fcp_prio);
return (0);
}
static void
isp_print_image(ispsoftc_t *isp, char *name, struct isp_image_status *image_status)
{
isp_prt(isp, ISP_LOGDEBUG0,
"%s %s: mask=0x%02x gen=0x%04x ver=%u.%u map=0x%01x sum=0x%08x sig=0x%08x",
name, "status",
image_status->image_status_mask,
le16toh(image_status->generation),
image_status->ver_major,
image_status->ver_minor,
image_status->bitmap,
le32toh(image_status->checksum),
le32toh(image_status->signature));
}
static bool
isp_check_aux_image_status_signature(struct isp_image_status *image_status)
{
unsigned long signature = le32toh(image_status->signature);
return (signature != ISP28XX_AUX_IMG_STATUS_SIGN);
}
static bool
isp_check_image_status_signature(struct isp_image_status *image_status)
{
unsigned long signature = le32toh(image_status->signature);
return ((signature != ISP27XX_IMG_STATUS_SIGN) &&
(signature != ISP28XX_IMG_STATUS_SIGN));
}
static unsigned long
isp_image_status_checksum(struct isp_image_status *image_status)
{
uint32_t *p = (uint32_t *)image_status;
unsigned int n = sizeof(*image_status) / sizeof(*p);
uint32_t sum = 0;
for ( ; n--; p++)
sum += le32toh(*((uint32_t *)(p)));
return (sum);
}
static inline unsigned int
isp_component_bitmask(struct isp_image_status *aux, unsigned int bitmask)
{
return (aux->bitmap & bitmask ?
ISP27XX_SECONDARY_IMAGE : ISP27XX_PRIMARY_IMAGE);
}
static void
isp_component_status(struct active_regions *active_regions, struct isp_image_status *aux)
{
active_regions->aux.board_config =
isp_component_bitmask(aux, ISP28XX_AUX_IMG_BOARD_CONFIG);
active_regions->aux.vpd_nvram =
isp_component_bitmask(aux, ISP28XX_AUX_IMG_VPD_NVRAM);
active_regions->aux.npiv_config_0_1 =
isp_component_bitmask(aux, ISP28XX_AUX_IMG_NPIV_CONFIG_0_1);
active_regions->aux.npiv_config_2_3 =
isp_component_bitmask(aux, ISP28XX_AUX_IMG_NPIV_CONFIG_2_3);
active_regions->aux.nvme_params =
isp_component_bitmask(aux, ISP28XX_AUX_IMG_NVME_PARAMS);
}
static int
isp_compare_image_generation(ispsoftc_t *isp,
struct isp_image_status *pri_image_status,
struct isp_image_status *sec_image_status)
{
int16_t delta =
le16toh(pri_image_status->generation) -
le16toh(sec_image_status->generation);
isp_prt(isp, ISP_LOGDEBUG0, "generation delta = %d", delta);
return (delta);
}
static void
isp_get_aux_images(ispsoftc_t *isp, struct active_regions *active_regions)
{
fcparam *fcp = FCPARAM(isp, 0);
struct isp_image_status pri_aux_image_status, sec_aux_image_status;
bool valid_pri_image = false, valid_sec_image = false;
bool active_pri_image = false, active_sec_image = false;
if (!fcp->flt_region_aux_img_status_pri) {
isp_prt(isp, ISP_LOGWARN,
"Primary aux image not addressed");
goto check_sec_image;
}
isp_read_flash_data(isp, (uint32_t *)&pri_aux_image_status,
fcp->flt_region_aux_img_status_pri,
sizeof(pri_aux_image_status) >> 2);
isp_print_image(isp, "Primary aux image", &pri_aux_image_status);
if (isp_check_aux_image_status_signature(&pri_aux_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Primary aux image signature (0x%x) not valid",
le32toh(pri_aux_image_status.signature));
goto check_sec_image;
}
if (isp_image_status_checksum(&pri_aux_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Primary aux image checksum failed");
goto check_sec_image;
}
valid_pri_image = true;
if (pri_aux_image_status.image_status_mask & 1) {
isp_prt(isp, ISP_LOGCONFIG,
"Primary aux image is active");
active_pri_image = true;
}
check_sec_image:
if (!fcp->flt_region_aux_img_status_sec) {
isp_prt(isp, ISP_LOGWARN,
"Secondary aux image not addressed");
goto check_valid_image;
}
isp_read_flash_data(isp, (uint32_t *)&sec_aux_image_status,
fcp->flt_region_aux_img_status_sec,
sizeof(sec_aux_image_status) >> 2);
isp_print_image(isp, "Secondary aux image", &sec_aux_image_status);
if (isp_check_aux_image_status_signature(&sec_aux_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Secondary aux image signature (0x%x) not valid",
le32toh(sec_aux_image_status.signature));
goto check_valid_image;
}
if (isp_image_status_checksum(&sec_aux_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Secondary aux image checksum failed");
goto check_valid_image;
}
valid_sec_image = true;
if (sec_aux_image_status.image_status_mask & 1) {
isp_prt(isp, ISP_LOGCONFIG,
"Secondary aux image is active");
active_sec_image = true;
}
check_valid_image:
if (valid_pri_image && active_pri_image &&
valid_sec_image && active_sec_image) {
if (isp_compare_image_generation(isp, &pri_aux_image_status,
&sec_aux_image_status) >= 0) {
isp_component_status(active_regions,
&pri_aux_image_status);
} else {
isp_component_status(active_regions,
&sec_aux_image_status);
}
} else if (valid_pri_image && active_pri_image) {
isp_component_status(active_regions, &pri_aux_image_status);
} else if (valid_sec_image && active_sec_image) {
isp_component_status(active_regions, &sec_aux_image_status);
}
isp_prt(isp, ISP_LOGDEBUG0,
"aux images active: BCFG=%u VPD/NVR=%u NPIV0/1=%u NPIV2/3=%u, NVME=%u",
active_regions->aux.board_config,
active_regions->aux.vpd_nvram,
active_regions->aux.npiv_config_0_1,
active_regions->aux.npiv_config_2_3,
active_regions->aux.nvme_params);
}
static void
isp_get_active_image(ispsoftc_t *isp, struct active_regions * active_regions)
{
fcparam *fcp = FCPARAM(isp, 0);
struct isp_image_status pri_image_status, sec_image_status;
bool valid_pri_image = false, valid_sec_image = false;
bool active_pri_image = false, active_sec_image = false;
if (!fcp->flt_region_img_status_pri) {
isp_prt(isp, ISP_LOGWARN,
"Primary image not addressed");
goto check_sec_image;
}
if (isp_read_flash_data(isp, (uint32_t *) &pri_image_status,
fcp->flt_region_img_status_pri, sizeof(pri_image_status) >> 2) !=
ISP_SUCCESS)
goto check_sec_image;
isp_print_image(isp, "Primary image", &pri_image_status);
if (isp_check_image_status_signature(&pri_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Primary image signature (0x%x) not valid",
le32toh(pri_image_status.signature));
goto check_sec_image;
}
if (isp_image_status_checksum(&pri_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Primary image checksum failed");
goto check_sec_image;
}
valid_pri_image = true;
if (pri_image_status.image_status_mask & 1) {
isp_prt(isp, ISP_LOGCONFIG,
"Primary image is active");
active_pri_image = true;
}
check_sec_image:
if (!fcp->flt_region_img_status_sec) {
isp_prt(isp, ISP_LOGWARN,
"Secondary image not addressed");
return;
}
if (isp_read_flash_data(isp, (uint32_t *) &sec_image_status,
fcp->flt_region_img_status_sec, sizeof(sec_image_status) >> 2) !=
ISP_SUCCESS)
return;
isp_print_image(isp, "Secondary image", &sec_image_status);
if (isp_check_image_status_signature(&sec_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Secondary image signature (0x%x) not valid",
le32toh(sec_image_status.signature));
}
if (isp_image_status_checksum(&sec_image_status)) {
isp_prt(isp, ISP_LOGERR,
"Secondary image checksum failed");
goto check_valid_image;
}
valid_sec_image = true;
if (sec_image_status.image_status_mask & 1) {
isp_prt(isp, ISP_LOGCONFIG,
"Secondary image is active");
active_sec_image = true;
}
check_valid_image:
if (valid_pri_image && active_pri_image)
active_regions->global = ISP27XX_PRIMARY_IMAGE;
if (valid_sec_image && active_sec_image) {
if (!active_regions->global ||
isp_compare_image_generation(isp,
&pri_image_status, &sec_image_status) < 0) {
active_regions->global = ISP27XX_SECONDARY_IMAGE;
}
}
isp_prt(isp, ISP_LOGDEBUG0, "active image %s (%u)",
active_regions->global == ISP27XX_DEFAULT_IMAGE ?
"default (boot/fw)" :
active_regions->global == ISP27XX_PRIMARY_IMAGE ?
"primary" :
active_regions->global == ISP27XX_SECONDARY_IMAGE ?
"secondary" : "invalid",
active_regions->global);
}
static bool isp_risc_firmware_invalid(ispsoftc_t *isp, uint32_t *dword)
{
return ((dword[4] | dword[5] | dword[6] | dword[7]) == 0 ||
(~dword[4] | ~dword[5] | ~dword[6] | ~dword[7]) == 0);
}
static int
isp_load_ram(ispsoftc_t *isp, uint32_t *data, uint32_t risc_addr,
uint32_t risc_code_size)
{
mbreg_t mbs;
int rval = ISP_SUCCESS;
MEMORYBARRIER(isp, SYNC_REQUEST, 0, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)), -1);
MBSINIT(&mbs, MBOX_LOAD_RISC_RAM, MBLOGALL, 0);
mbs.param[1] = risc_addr;
mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
mbs.param[4] = risc_code_size >> 16;
mbs.param[5] = risc_code_size;
mbs.param[6] = DMA_WD3(isp->isp_rquest_dma);
mbs.param[7] = DMA_WD2(isp->isp_rquest_dma);
mbs.param[8] = risc_addr >> 16;
isp_prt(isp, ISP_LOGDEBUG0,
"LOAD RISC RAM %u (0x%x) words at load address 0x%x",
risc_code_size, risc_code_size, risc_addr);
isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "F/W download failed");
rval = ISP_FUNCTION_FAILED;
}
return (rval);
}
static int
isp_load_risc_flash(ispsoftc_t *isp, uint32_t *srisc_addr, uint32_t faddr)
{
fcparam *fcp = FCPARAM(isp, 0);
int rval = ISP_SUCCESS;
unsigned int segments, fragment;
unsigned long i;
unsigned int j;
unsigned long dlen;
uint32_t *dcode;
uint32_t risc_addr, risc_size = 0;
isp_prt(isp, ISP_LOGDEBUG0,
"Accessing flash firmware at 0x%x.", faddr);
dcode = isp->isp_rquest;
isp_read_flash_data(isp, dcode, faddr, 8);
if (isp_risc_firmware_invalid(isp, dcode)) {
snprintf(fcp->fw_version_flash, sizeof(fcp->fw_version_flash),
"invalid");
isp_prt(isp, ISP_LOGERR,
"Unable to verify the integrity of flash firmware image.");
isp_prt(isp, ISP_LOGERR,
"Firmware data: 0x%08x 0x%08x 0x%08x 0x%08x.",
dcode[0], dcode[1], dcode[2], dcode[3]);
return (ISP_FUNCTION_FAILED);
} else {
for (i = 0; i < 4; i++)
fcp->fw_flashrev[i] = be32toh(dcode[4 + i]);
snprintf(fcp->fw_version_flash, sizeof(fcp->fw_version_flash),
"%u.%u.%u", fcp->fw_flashrev[0], fcp->fw_flashrev[1],
fcp->fw_flashrev[2]);
isp_prt(isp, ISP_LOGCONFIG,
"Firmware revision (flash) %u.%u.%u (%x).",
fcp->fw_flashrev[0], fcp->fw_flashrev[1],
fcp->fw_flashrev[2], fcp->fw_flashrev[3]);
if (isp->isp_osinfo.ispfw != NULL) {
int ispfw_newer = 0;
if (ISP_FW_NEWER_THANX(fcp->fw_ispfwrev, fcp->fw_flashrev)) {
ispfw_newer = 1;
}
if (isp->isp_confopts & ISP_CFG_FWLOAD_FORCE) {
isp_prt(isp, ISP_LOGCONFIG,
"Loading RISC with %s ispfw(4) firmware %s",
(ispfw_newer == 0) ? "older" : "newer",
"because fwload_force is set");
return (ISP_ABORTED);
}
if (ispfw_newer != 0) {
isp_prt(isp, ISP_LOGCONFIG,
"Loading RISC with newer ispfw(4) firmware");
return (ISP_ABORTED);
}
isp_prt(isp, ISP_LOGCONFIG,
"Loading RISC with newer flash firmware");
}
}
dcode = isp->isp_rquest;
segments = ISP_RISC_CODE_SEGMENTS;
for (j = 0; j < segments; j++) {
isp_prt(isp, ISP_LOGDEBUG0, "Loading segment %u", j);
isp_read_flash_data(isp, dcode, faddr, 10);
risc_addr = be32toh(dcode[2]);
risc_size = be32toh(dcode[3]);
dlen = min(risc_size, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) / 4);
for (fragment = 0; risc_size; fragment++) {
if (dlen > risc_size)
dlen = risc_size;
isp_prt(isp, ISP_LOGDEBUG0,
"Loading fragment %u: 0x%x <- 0x%x (0x%lx dwords)",
fragment, risc_addr, faddr, dlen);
isp_read_flash_data(isp, dcode, faddr, dlen);
for (i = 0; i < dlen; i++) {
dcode[i] = bswap32(dcode[i]);
}
rval = isp_load_ram(isp, dcode, risc_addr, dlen);
if (rval) {
isp_prt(isp, ISP_LOGERR,
"Failed to load firmware fragment %u.",
fragment);
return (ISP_FUNCTION_FAILED);
}
faddr += dlen;
risc_addr += dlen;
risc_size -= dlen;
}
}
return (rval);
}
static int
isp_load_risc(ispsoftc_t *isp, uint32_t *srisc_addr)
{
fcparam *fcp = FCPARAM(isp, 0);
int rval = ISP_SUCCESS;
struct active_regions active_regions = { };
if (!IS_27XX(isp))
goto try_primary_fw;
isp_get_active_image(isp, &active_regions);
if (active_regions.global != ISP27XX_SECONDARY_IMAGE)
goto try_primary_fw;
isp_prt(isp, ISP_LOGCONFIG,
"Loading secondary firmware image.");
rval = isp_load_risc_flash(isp, srisc_addr, fcp->flt_region_fw_sec);
return (rval);
try_primary_fw:
isp_prt(isp, ISP_LOGCONFIG,
"Loading primary firmware image.");
rval = isp_load_risc_flash(isp, srisc_addr, fcp->flt_region_fw);
return (rval);
}
static int
isp_read_nvram(ispsoftc_t *isp)
{
fcparam *fcp = FCPARAM(isp, 0);
int retval = 0;
uint32_t addr, csum, lwrds, *dptr;
uint8_t nvram_data[ISP2400_NVRAM_SIZE];
struct active_regions active_regions = { };
if (IS_27XX(isp))
isp_get_aux_images(isp, &active_regions);
addr = fcp->flt_region_nvram;
if (IS_28XX(isp)) {
if (active_regions.aux.vpd_nvram == ISP27XX_SECONDARY_IMAGE)
addr = fcp->flt_region_nvram_sec;
isp_prt(isp, ISP_LOGCONFIG, "Loading %s NVRAM image",
active_regions.aux.vpd_nvram == ISP27XX_PRIMARY_IMAGE ?
"primary" : "secondary");
}
dptr = (uint32_t *) nvram_data;
for (lwrds = 0; lwrds < ISP2400_NVRAM_SIZE >> 2; lwrds++) {
isp_rd_2xxx_flash(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 = (uint32_t *) nvram_data;
for (csum = 0, lwrds = 0; lwrds < ISP2400_NVRAM_SIZE >> 2; lwrds++) {
uint32_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);
}
static void
isp_parse_nvram_2400(ispsoftc_t *isp, uint8_t *nvram_data)
{
fcparam *fcp = FCPARAM(isp, 0);
uint64_t wwn;
isp_prt(isp, ISP_LOGDEBUG0,
"NVRAM 0x%08x%08x 0x%08x%08x maxframelen %d",
(uint32_t) (ISP2400_NVRAM_NODE_NAME(nvram_data) >> 32),
(uint32_t) (ISP2400_NVRAM_NODE_NAME(nvram_data)),
(uint32_t) (ISP2400_NVRAM_PORT_NAME(nvram_data) >> 32),
(uint32_t) (ISP2400_NVRAM_PORT_NAME(nvram_data)),
ISP2400_NVRAM_MAXFRAMELENGTH(nvram_data));
isp_prt(isp, ISP_LOGDEBUG0,
"NVRAM loopid %d fwopt1 0x%x fwopt2 0x%x fwopt3 0x%x",
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);
fcp->isp_wwpn_nvram = wwn;
wwn = ISP2400_NVRAM_NODE_NAME(nvram_data);
if (wwn) {
if ((wwn >> 60) != 2 && (wwn >> 60) != 5) {
wwn = 0;
}
}
if (wwn == 0 && (fcp->isp_wwpn_nvram >> 60) == 2) {
wwn = fcp->isp_wwpn_nvram;
wwn &= ~((uint64_t) 0xfff << 48);
}
fcp->isp_wwnn_nvram = wwn;
if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0) {
DEFAULT_FRAMESIZE(isp) =
ISP2400_NVRAM_MAXFRAMELENGTH(nvram_data);
}
if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) {
fcp->isp_loopid = ISP2400_NVRAM_HARDLOOPID(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);
}