/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at * http://www.opensource.org/licenses/cddl1.txt. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004-2012 Emulex. All rights reserved. * Use is subject to license terms. * Copyright 2020 RackTop Systems, Inc. */ #include /* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */ EMLXS_MSG_DEF(EMLXS_SLI3_C); static void emlxs_sli3_issue_iocb(emlxs_hba_t *hba, RING *rp, IOCBQ *iocbq); static void emlxs_sli3_handle_link_event(emlxs_hba_t *hba); static void emlxs_sli3_handle_ring_event(emlxs_hba_t *hba, int32_t ring_no, uint32_t ha_copy); #ifdef SFCT_SUPPORT static uint32_t emlxs_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); #endif /* SFCT_SUPPORT */ static uint32_t emlxs_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_disable_traffic_cop = 1; static int emlxs_sli3_map_hdw(emlxs_hba_t *hba); static void emlxs_sli3_unmap_hdw(emlxs_hba_t *hba); static int32_t emlxs_sli3_online(emlxs_hba_t *hba); static void emlxs_sli3_offline(emlxs_hba_t *hba, uint32_t reset_requested); static uint32_t emlxs_sli3_hba_reset(emlxs_hba_t *hba, uint32_t restart, uint32_t skip_post, uint32_t quiesce); static void emlxs_sli3_hba_kill(emlxs_hba_t *hba); static void emlxs_sli3_hba_kill4quiesce(emlxs_hba_t *hba); static uint32_t emlxs_sli3_hba_init(emlxs_hba_t *hba); static uint32_t emlxs_sli2_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_sli3_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_sli2_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_sli3_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp); static void emlxs_sli3_issue_iocb_cmd(emlxs_hba_t *hba, CHANNEL *rp, IOCBQ *iocb_cmd); static uint32_t emlxs_sli3_issue_mbox_cmd(emlxs_hba_t *hba, MAILBOXQ *mbq, int32_t flg, uint32_t tmo); #ifdef SFCT_SUPPORT static uint32_t emlxs_sli3_prep_fct_iocb(emlxs_port_t *port, emlxs_buf_t *cmd_sbp, int channel); #endif /* SFCT_SUPPORT */ static uint32_t emlxs_sli3_prep_fcp_iocb(emlxs_port_t *port, emlxs_buf_t *sbp, int ring); static uint32_t emlxs_sli3_prep_ip_iocb(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_sli3_prep_els_iocb(emlxs_port_t *port, emlxs_buf_t *sbp); static uint32_t emlxs_sli3_prep_ct_iocb(emlxs_port_t *port, emlxs_buf_t *sbp); static void emlxs_sli3_poll_intr(emlxs_hba_t *hba); static int32_t emlxs_sli3_intx_intr(char *arg); #ifdef MSI_SUPPORT static uint32_t emlxs_sli3_msi_intr(char *arg1, char *arg2); #endif /* MSI_SUPPORT */ static void emlxs_sli3_enable_intr(emlxs_hba_t *hba); static void emlxs_sli3_disable_intr(emlxs_hba_t *hba, uint32_t att); static void emlxs_handle_ff_error(emlxs_hba_t *hba); static uint32_t emlxs_handle_mb_event(emlxs_hba_t *hba); static void emlxs_sli3_timer_check_mbox(emlxs_hba_t *hba); static uint32_t emlxs_mb_config_port(emlxs_hba_t *hba, MAILBOXQ *mbq, uint32_t sli_mode, uint32_t hbainit); static void emlxs_enable_latt(emlxs_hba_t *hba); static uint32_t emlxs_check_attention(emlxs_hba_t *hba); static uint32_t emlxs_get_attention(emlxs_hba_t *hba, int32_t msgid); static void emlxs_proc_attention(emlxs_hba_t *hba, uint32_t ha_copy); /* static int emlxs_handle_rcv_seq(emlxs_hba_t *hba, */ /* CHANNEL *cp, IOCBQ *iocbq); */ /* static void emlxs_update_HBQ_index(emlxs_hba_t *hba, */ /* uint32_t hbq_id); */ /* static void emlxs_hbq_free_all(emlxs_hba_t *hba, */ /* uint32_t hbq_id); */ static uint32_t emlxs_hbq_setup(emlxs_hba_t *hba, uint32_t hbq_id); static void emlxs_sli3_timer(emlxs_hba_t *hba); static void emlxs_sli3_poll_erratt(emlxs_hba_t *hba); static uint32_t emlxs_sli3_reg_did(emlxs_port_t *port, uint32_t did, SERV_PARM *param, emlxs_buf_t *sbp, fc_unsol_buf_t *ubp, IOCBQ *iocbq); static uint32_t emlxs_sli3_unreg_node(emlxs_port_t *port, NODELIST *node, emlxs_buf_t *sbp, fc_unsol_buf_t *ubp, IOCBQ *iocbq); /* Define SLI3 API functions */ emlxs_sli_api_t emlxs_sli3_api = { emlxs_sli3_map_hdw, emlxs_sli3_unmap_hdw, emlxs_sli3_online, emlxs_sli3_offline, emlxs_sli3_hba_reset, emlxs_sli3_hba_kill, emlxs_sli3_issue_iocb_cmd, emlxs_sli3_issue_mbox_cmd, #ifdef SFCT_SUPPORT emlxs_sli3_prep_fct_iocb, #else NULL, #endif /* SFCT_SUPPORT */ emlxs_sli3_prep_fcp_iocb, emlxs_sli3_prep_ip_iocb, emlxs_sli3_prep_els_iocb, emlxs_sli3_prep_ct_iocb, emlxs_sli3_poll_intr, emlxs_sli3_intx_intr, emlxs_sli3_msi_intr, emlxs_sli3_disable_intr, emlxs_sli3_timer, emlxs_sli3_poll_erratt, emlxs_sli3_reg_did, emlxs_sli3_unreg_node }; /* * emlxs_sli3_online() * * This routine will start initialization of the SLI2/3 HBA. */ static int32_t emlxs_sli3_online(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; emlxs_config_t *cfg; emlxs_vpd_t *vpd; MAILBOX *mb = NULL; MAILBOXQ *mbq = NULL; RING *rp; CHANNEL *cp; MATCHMAP *mp = NULL; MATCHMAP *mp1 = NULL; uint8_t *inptr; uint8_t *outptr; uint32_t status; uint16_t i; uint32_t j; uint32_t read_rev_reset; uint32_t key = 0; uint32_t fw_check; uint32_t kern_update = 0; uint32_t rval = 0; uint32_t offset; uint8_t vpd_data[DMP_VPD_SIZE]; uint32_t MaxRbusSize; uint32_t MaxIbusSize; uint32_t sli_mode; uint32_t sli_mode_mask; cfg = &CFG; vpd = &VPD; MaxRbusSize = 0; MaxIbusSize = 0; read_rev_reset = 0; hba->chan_count = MAX_RINGS; if (hba->bus_type == SBUS_FC) { (void) READ_SBUS_CSR_REG(hba, FC_SHS_REG(hba)); } /* Set the fw_check flag */ fw_check = cfg[CFG_FW_CHECK].current; if ((fw_check & 0x04) || (hba->fw_flag & FW_UPDATE_KERNEL)) { kern_update = 1; } hba->mbox_queue_flag = 0; hba->sli.sli3.hc_copy = 0; hba->fc_edtov = FF_DEF_EDTOV; hba->fc_ratov = FF_DEF_RATOV; hba->fc_altov = FF_DEF_ALTOV; hba->fc_arbtov = FF_DEF_ARBTOV; /* * Get a buffer which will be used repeatedly for mailbox commands */ mbq = (MAILBOXQ *) kmem_zalloc((sizeof (MAILBOXQ)), KM_SLEEP); mb = (MAILBOX *)mbq; /* Initialize sli mode based on configuration parameter */ switch (cfg[CFG_SLI_MODE].current) { case 2: /* SLI2 mode */ sli_mode = EMLXS_HBA_SLI2_MODE; sli_mode_mask = EMLXS_SLI2_MASK; break; case 3: /* SLI3 mode */ sli_mode = EMLXS_HBA_SLI3_MODE; sli_mode_mask = EMLXS_SLI3_MASK; break; case 0: /* Best available */ case 1: /* Best available */ default: if (hba->model_info.sli_mask & EMLXS_SLI3_MASK) { sli_mode = EMLXS_HBA_SLI3_MODE; sli_mode_mask = EMLXS_SLI3_MASK; } else if (hba->model_info.sli_mask & EMLXS_SLI2_MASK) { sli_mode = EMLXS_HBA_SLI2_MODE; sli_mode_mask = EMLXS_SLI2_MASK; } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "No SLI mode available."); rval = EIO; goto failed; } break; } /* SBUS adapters only available in SLI2 */ if (hba->bus_type == SBUS_FC) { sli_mode = EMLXS_HBA_SLI2_MODE; sli_mode_mask = EMLXS_SLI2_MASK; } reset: /* Reset & Initialize the adapter */ if (emlxs_sli3_hba_init(hba)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to init hba."); rval = EIO; goto failed; } #ifdef FMA_SUPPORT /* Access handle validation */ if ((emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle) != DDI_FM_OK) || (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle) != DDI_FM_OK) || (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.csr_acc_handle) != DDI_FM_OK)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_access_handle_msg, NULL); rval = EIO; goto failed; } #endif /* FMA_SUPPORT */ /* Check for PEGASUS (This is a special case) */ /* We need to check for dual channel adapter */ if (hba->model_info.vendor_id == PCI_VENDOR_ID_EMULEX && hba->model_info.device_id == PCI_DEVICE_ID_PEGASUS) { /* Try to determine if this is a DC adapter */ if (emlxs_get_max_sram(hba, &MaxRbusSize, &MaxIbusSize) == 0) { if (MaxRbusSize == REDUCED_SRAM_CFG) { /* LP9802DC */ for (i = 1; i < emlxs_pci_model_count; i++) { if (emlxs_pci_model[i].id == LP9802DC) { bcopy(&emlxs_pci_model[i], &hba->model_info, sizeof (emlxs_model_t)); break; } } } else if (hba->model_info.id != LP9802) { /* LP9802 */ for (i = 1; i < emlxs_pci_model_count; i++) { if (emlxs_pci_model[i].id == LP9802) { bcopy(&emlxs_pci_model[i], &hba->model_info, sizeof (emlxs_model_t)); break; } } } } } /* * Setup and issue mailbox READ REV command */ vpd->opFwRev = 0; vpd->postKernRev = 0; vpd->sli1FwRev = 0; vpd->sli2FwRev = 0; vpd->sli3FwRev = 0; vpd->sli4FwRev = 0; vpd->postKernName[0] = 0; vpd->opFwName[0] = 0; vpd->sli1FwName[0] = 0; vpd->sli2FwName[0] = 0; vpd->sli3FwName[0] = 0; vpd->sli4FwName[0] = 0; vpd->opFwLabel[0] = 0; vpd->sli1FwLabel[0] = 0; vpd->sli2FwLabel[0] = 0; vpd->sli3FwLabel[0] = 0; vpd->sli4FwLabel[0] = 0; /* Sanity check */ if (hba->model_info.sli_mask & EMLXS_SLI4_MASK) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Adapter / SLI mode mismatch mask:x%x", hba->model_info.sli_mask); rval = EIO; goto failed; } EMLXS_STATE_CHANGE(hba, FC_INIT_REV); emlxs_mb_read_rev(hba, mbq, 0); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to read rev. Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } if (mb->un.varRdRev.rr == 0) { /* Old firmware */ if (read_rev_reset == 0) { read_rev_reset = 1; goto reset; } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Outdated firmware detected."); } vpd->rBit = 0; } else { if (mb->un.varRdRev.un.b.ProgType != FUNC_FIRMWARE) { if (read_rev_reset == 0) { read_rev_reset = 1; goto reset; } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Non-operational firmware detected. " "type=%x", mb->un.varRdRev.un.b.ProgType); } } vpd->rBit = 1; vpd->sli1FwRev = mb->un.varRdRev.sliFwRev1; bcopy((char *)mb->un.varRdRev.sliFwName1, vpd->sli1FwLabel, 16); vpd->sli2FwRev = mb->un.varRdRev.sliFwRev2; bcopy((char *)mb->un.varRdRev.sliFwName2, vpd->sli2FwLabel, 16); /* * Lets try to read the SLI3 version * Setup and issue mailbox READ REV(v3) command */ EMLXS_STATE_CHANGE(hba, FC_INIT_REV); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_read_rev(hba, mbq, 1); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to read rev (v3). Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } if (mb->un.varRdRev.rf3) { /* * vpd->sli2FwRev = mb->un.varRdRev.sliFwRev1; * Not needed */ vpd->sli3FwRev = mb->un.varRdRev.sliFwRev2; bcopy((char *)mb->un.varRdRev.sliFwName2, vpd->sli3FwLabel, 16); } } if ((sli_mode == EMLXS_HBA_SLI3_MODE) && (vpd->sli3FwRev == 0)) { if (vpd->sli2FwRev) { sli_mode = EMLXS_HBA_SLI2_MODE; sli_mode_mask = EMLXS_SLI2_MASK; } else { sli_mode = 0; sli_mode_mask = 0; } } else if ((sli_mode == EMLXS_HBA_SLI2_MODE) && (vpd->sli2FwRev == 0)) { if (vpd->sli3FwRev) { sli_mode = EMLXS_HBA_SLI3_MODE; sli_mode_mask = EMLXS_SLI3_MASK; } else { sli_mode = 0; sli_mode_mask = 0; } } if (!(hba->model_info.sli_mask & sli_mode_mask)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Firmware not available. sli-mode=%d", cfg[CFG_SLI_MODE].current); rval = EIO; goto failed; } /* Save information as VPD data */ vpd->postKernRev = mb->un.varRdRev.postKernRev; vpd->opFwRev = mb->un.varRdRev.opFwRev; bcopy((char *)mb->un.varRdRev.opFwName, vpd->opFwLabel, 16); vpd->biuRev = mb->un.varRdRev.biuRev; vpd->smRev = mb->un.varRdRev.smRev; vpd->smFwRev = mb->un.varRdRev.un.smFwRev; vpd->endecRev = mb->un.varRdRev.endecRev; vpd->fcphHigh = mb->un.varRdRev.fcphHigh; vpd->fcphLow = mb->un.varRdRev.fcphLow; vpd->feaLevelHigh = mb->un.varRdRev.feaLevelHigh; vpd->feaLevelLow = mb->un.varRdRev.feaLevelLow; /* Decode FW names */ emlxs_decode_version(vpd->postKernRev, vpd->postKernName, sizeof (vpd->postKernName)); emlxs_decode_version(vpd->opFwRev, vpd->opFwName, sizeof (vpd->opFwName)); emlxs_decode_version(vpd->sli1FwRev, vpd->sli1FwName, sizeof (vpd->sli1FwName)); emlxs_decode_version(vpd->sli2FwRev, vpd->sli2FwName, sizeof (vpd->sli2FwName)); emlxs_decode_version(vpd->sli3FwRev, vpd->sli3FwName, sizeof (vpd->sli3FwName)); emlxs_decode_version(vpd->sli4FwRev, vpd->sli4FwName, sizeof (vpd->sli4FwName)); /* Decode FW labels */ emlxs_decode_label(vpd->opFwLabel, vpd->opFwLabel, 1, sizeof (vpd->opFwLabel)); emlxs_decode_label(vpd->sli1FwLabel, vpd->sli1FwLabel, 1, sizeof (vpd->sli1FwLabel)); emlxs_decode_label(vpd->sli2FwLabel, vpd->sli2FwLabel, 1, sizeof (vpd->sli2FwLabel)); emlxs_decode_label(vpd->sli3FwLabel, vpd->sli3FwLabel, 1, sizeof (vpd->sli3FwLabel)); emlxs_decode_label(vpd->sli4FwLabel, vpd->sli4FwLabel, 1, sizeof (vpd->sli4FwLabel)); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); key = emlxs_get_key(hba, mbq); /* Get adapter VPD information */ offset = 0; bzero(vpd_data, sizeof (vpd_data)); vpd->port_index = (uint32_t)-1; while (offset < DMP_VPD_SIZE) { /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_dump_vpd(hba, mbq, offset); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { /* * Let it go through even if failed. * Not all adapter's have VPD info and thus will * fail here. This is not a problem */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "No VPD found. offset=%x status=%x", offset, mb->mbxStatus); break; } else { if (mb->un.varDmp.ra == 1) { uint32_t *lp1, *lp2; uint32_t bsize; uint32_t wsize; /* * mb->un.varDmp.word_cnt is actually byte * count for the dump reply */ bsize = mb->un.varDmp.word_cnt; /* Stop if no data was received */ if (bsize == 0) { break; } /* Check limit on byte size */ bsize = (bsize > (sizeof (vpd_data) - offset)) ? (sizeof (vpd_data) - offset) : bsize; /* * Convert size from bytes to words with * minimum of 1 word */ wsize = (bsize > 4) ? (bsize >> 2) : 1; /* * Transfer data into vpd_data buffer one * word at a time */ lp1 = (uint32_t *)&mb->un.varDmp.resp_offset; lp2 = (uint32_t *)&vpd_data[offset]; for (i = 0; i < wsize; i++) { status = *lp1++; *lp2++ = BE_SWAP32(status); } /* Increment total byte count saved */ offset += (wsize << 2); /* * Stop if less than a full transfer was * received */ if (wsize < DMP_VPD_DUMP_WCOUNT) { break; } } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "No VPD acknowledgment. offset=%x", offset); break; } } } if (vpd_data[0]) { (void) emlxs_parse_vpd(hba, (uint8_t *)vpd_data, offset); /* * If there is a VPD part number, and it does not * match the current default HBA model info, * replace the default data with an entry that * does match. * * After emlxs_parse_vpd model holds the VPD value * for V2 and part_num hold the value for PN. These * 2 values are NOT necessarily the same. */ rval = 0; if ((vpd->model[0] != 0) && (strcmp(&vpd->model[0], hba->model_info.model) != 0)) { /* First scan for a V2 match */ for (i = 1; i < emlxs_pci_model_count; i++) { if (strcmp(&vpd->model[0], emlxs_pci_model[i].model) == 0) { bcopy(&emlxs_pci_model[i], &hba->model_info, sizeof (emlxs_model_t)); rval = 1; break; } } } if (!rval && (vpd->part_num[0] != 0) && (strcmp(&vpd->part_num[0], hba->model_info.model) != 0)) { /* Next scan for a PN match */ for (i = 1; i < emlxs_pci_model_count; i++) { if (strcmp(&vpd->part_num[0], emlxs_pci_model[i].model) == 0) { bcopy(&emlxs_pci_model[i], &hba->model_info, sizeof (emlxs_model_t)); break; } } } /* * Now lets update hba->model_info with the real * VPD data, if any. */ /* * Replace the default model description with vpd data */ if (vpd->model_desc[0] != 0) { (void) strncpy(hba->model_info.model_desc, vpd->model_desc, (sizeof (hba->model_info.model_desc)-1)); } /* Replace the default model with vpd data */ if (vpd->model[0] != 0) { (void) strncpy(hba->model_info.model, vpd->model, (sizeof (hba->model_info.model)-1)); } /* Replace the default program types with vpd data */ if (vpd->prog_types[0] != 0) { emlxs_parse_prog_types(hba, vpd->prog_types); } } /* * Since the adapter model may have changed with the vpd data * lets double check if adapter is not supported */ if (hba->model_info.flags & EMLXS_NOT_SUPPORTED) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unsupported adapter found. " "Id:%d Vendor id:0x%x Device id:0x%x SSDID:0x%x " "Model:%s", hba->model_info.id, hba->model_info.vendor_id, hba->model_info.device_id, hba->model_info.ssdid, hba->model_info.model); rval = EIO; goto failed; } /* Read the adapter's wakeup parms */ (void) emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 1); emlxs_decode_version(hba->wakeup_parms.u0.boot_bios_wd[0], vpd->boot_version, sizeof (vpd->boot_version)); /* Get fcode version property */ emlxs_get_fcode_version(hba); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Firmware: kern=%08x stub=%08x sli1=%08x", vpd->postKernRev, vpd->opFwRev, vpd->sli1FwRev); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Firmware: sli2=%08x sli3=%08x sli4=%08x fl=%x", vpd->sli2FwRev, vpd->sli3FwRev, vpd->sli4FwRev, vpd->feaLevelHigh); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "BIOS: boot=%s fcode=%s", vpd->boot_version, vpd->fcode_version); /* * If firmware checking is enabled and the adapter model indicates * a firmware image, then perform firmware version check */ hba->fw_flag = 0; hba->fw_timer = 0; if (((fw_check & 0x1) && (hba->model_info.flags & EMLXS_ORACLE_BRANDED) && hba->model_info.fwid) || ((fw_check & 0x2) && hba->model_info.fwid)) { emlxs_firmware_t *fw; /* Find firmware image indicated by adapter model */ fw = NULL; for (i = 0; i < emlxs_fw_count; i++) { if (emlxs_fw_table[i].id == hba->model_info.fwid) { fw = &emlxs_fw_table[i]; break; } } /* * If the image was found, then verify current firmware * versions of adapter */ if (fw) { if (!kern_update && ((fw->kern && (vpd->postKernRev != fw->kern)) || (fw->stub && (vpd->opFwRev != fw->stub)))) { hba->fw_flag |= FW_UPDATE_NEEDED; } else if ((fw->kern && (vpd->postKernRev != fw->kern)) || (fw->stub && (vpd->opFwRev != fw->stub)) || (fw->sli1 && (vpd->sli1FwRev != fw->sli1)) || (fw->sli2 && (vpd->sli2FwRev != fw->sli2)) || (fw->sli3 && (vpd->sli3FwRev != fw->sli3)) || (fw->sli4 && (vpd->sli4FwRev != fw->sli4))) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Firmware update needed. " "Updating. id=%d fw=%d", hba->model_info.id, hba->model_info.fwid); #ifdef MODFW_SUPPORT /* * Load the firmware image now * If MODFW_SUPPORT is not defined, the * firmware image will already be defined * in the emlxs_fw_table */ emlxs_fw_load(hba, fw); #endif /* MODFW_SUPPORT */ if (fw->image && fw->size) { uint32_t rc; rc = emlxs_fw_download(hba, (char *)fw->image, fw->size, 0); if ((rc != FC_SUCCESS) && (rc != EMLXS_REBOOT_REQUIRED)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Firmware update failed."); hba->fw_flag |= FW_UPDATE_NEEDED; } #ifdef MODFW_SUPPORT /* * Unload the firmware image from * kernel memory */ emlxs_fw_unload(hba, fw); #endif /* MODFW_SUPPORT */ fw_check = 0; goto reset; } hba->fw_flag |= FW_UPDATE_NEEDED; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Firmware image unavailable."); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Firmware update not needed."); } } else { /* This should not happen */ /* * This means either the adapter database is not * correct or a firmware image is missing from the * compile */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Firmware image unavailable. id=%d fw=%d", hba->model_info.id, hba->model_info.fwid); } } /* * Add our interrupt routine to kernel's interrupt chain & enable it * If MSI is enabled this will cause Solaris to program the MSI address * and data registers in PCI config space */ if (EMLXS_INTR_ADD(hba) != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to add interrupt(s)."); rval = EIO; goto failed; } EMLXS_STATE_CHANGE(hba, FC_INIT_CFGPORT); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); (void) emlxs_mb_config_port(hba, mbq, sli_mode, key); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to configure port. " "Mailbox cmd=%x status=%x slimode=%d key=%x", mb->mbxCommand, mb->mbxStatus, sli_mode, key); for (sli_mode--; sli_mode > 0; sli_mode--) { /* Check if sli_mode is supported by this adapter */ if (hba->model_info.sli_mask & EMLXS_SLI_MASK(sli_mode)) { sli_mode_mask = EMLXS_SLI_MASK(sli_mode); break; } } if (sli_mode) { fw_check = 0; goto reset; } hba->flag &= ~FC_SLIM2_MODE; rval = EIO; goto failed; } /* Check if SLI3 mode was achieved */ if (mb->un.varCfgPort.rMA && (mb->un.varCfgPort.sli_mode == EMLXS_HBA_SLI3_MODE)) { if (mb->un.varCfgPort.vpi_max > 1) { hba->flag |= FC_NPIV_ENABLED; if (hba->model_info.chip >= EMLXS_SATURN_CHIP) { hba->vpi_max = min(mb->un.varCfgPort.vpi_max, MAX_VPORTS - 1); } else { hba->vpi_max = min(mb->un.varCfgPort.vpi_max, MAX_VPORTS_LIMITED - 1); } } #if (EMLXS_MODREV >= EMLXS_MODREV5) hba->fca_tran->fca_num_npivports = (cfg[CFG_NPIV_ENABLE].current) ? hba->vpi_max : 0; #endif /* >= EMLXS_MODREV5 */ if (mb->un.varCfgPort.gerbm && mb->un.varCfgPort.max_hbq) { hba->flag |= FC_HBQ_ENABLED; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "SLI3 mode: flag=%x vpi_max=%d", hba->flag, hba->vpi_max); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "SLI2 mode: flag=%x", hba->flag); sli_mode = EMLXS_HBA_SLI2_MODE; sli_mode_mask = EMLXS_SLI2_MASK; hba->sli_mode = sli_mode; #if (EMLXS_MODREV >= EMLXS_MODREV5) hba->fca_tran->fca_num_npivports = 0; #endif /* >= EMLXS_MODREV5 */ } /* Get and save the current firmware version (based on sli_mode) */ emlxs_decode_firmware_rev(hba, vpd); emlxs_pcix_mxr_update(hba, 0); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_read_config(hba, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to read configuration. Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } /* Save the link speed capabilities */ vpd->link_speed = (uint16_t)mb->un.varRdConfig.lmt; emlxs_process_link_speed(hba); /* Set the max node count */ if (cfg[CFG_NUM_NODES].current > 0) { hba->max_nodes = min(cfg[CFG_NUM_NODES].current, mb->un.varRdConfig.max_rpi); } else { hba->max_nodes = mb->un.varRdConfig.max_rpi; } /* Set the io throttle */ hba->io_throttle = mb->un.varRdConfig.max_xri - IO_THROTTLE_RESERVE; /* Set max_iotag */ if (cfg[CFG_NUM_IOTAGS].current) { hba->max_iotag = (uint16_t)cfg[CFG_NUM_IOTAGS].current; } else { hba->max_iotag = mb->un.varRdConfig.max_xri; } /* Set out-of-range iotag base */ hba->fc_oor_iotag = hba->max_iotag; /* * Allocate some memory for buffers */ if (emlxs_mem_alloc_buffer(hba) == 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to allocate memory buffers."); EMLXS_STATE_CHANGE(hba, FC_ERROR); return (ENOMEM); } /* * Setup and issue mailbox RUN BIU DIAG command Setup test buffers */ if (((mp = (MATCHMAP *)emlxs_mem_get(hba, MEM_BUF)) == 0) || ((mp1 = (MATCHMAP *)emlxs_mem_get(hba, MEM_BUF)) == 0)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to allocate diag buffers."); rval = ENOMEM; goto failed; } bcopy((caddr_t)&emlxs_diag_pattern[0], (caddr_t)mp->virt, MEM_ELSBUF_SIZE); EMLXS_MPDATA_SYNC(mp->dma_handle, 0, MEM_ELSBUF_SIZE, DDI_DMA_SYNC_FORDEV); bzero(mp1->virt, MEM_ELSBUF_SIZE); EMLXS_MPDATA_SYNC(mp1->dma_handle, 0, MEM_ELSBUF_SIZE, DDI_DMA_SYNC_FORDEV); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); (void) emlxs_mb_run_biu_diag(hba, mbq, mp->phys, mp1->phys); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to run BIU diag. Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } EMLXS_MPDATA_SYNC(mp1->dma_handle, 0, MEM_ELSBUF_SIZE, DDI_DMA_SYNC_FORKERNEL); #ifdef FMA_SUPPORT if (mp->dma_handle) { if (emlxs_fm_check_dma_handle(hba, mp->dma_handle) != DDI_FM_OK) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_dma_handle_msg, "sli3_online: hdl=%p", mp->dma_handle); rval = EIO; goto failed; } } if (mp1->dma_handle) { if (emlxs_fm_check_dma_handle(hba, mp1->dma_handle) != DDI_FM_OK) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_dma_handle_msg, "sli3_online: hdl=%p", mp1->dma_handle); rval = EIO; goto failed; } } #endif /* FMA_SUPPORT */ outptr = mp->virt; inptr = mp1->virt; for (i = 0; i < MEM_ELSBUF_SIZE; i++) { if (*outptr++ != *inptr++) { outptr--; inptr--; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "BIU diagnostic failed. " "offset %x value %x should be %x.", i, (uint32_t)*inptr, (uint32_t)*outptr); rval = EIO; goto failed; } } /* Free the buffers since we were polling */ emlxs_mem_put(hba, MEM_BUF, (void *)mp); mp = NULL; emlxs_mem_put(hba, MEM_BUF, (void *)mp1); mp1 = NULL; hba->channel_fcp = FC_FCP_RING; hba->channel_els = FC_ELS_RING; hba->channel_ip = FC_IP_RING; hba->channel_ct = FC_CT_RING; hba->sli.sli3.ring_count = MAX_RINGS; hba->channel_tx_count = 0; hba->io_count = 0; hba->fc_iotag = 1; for (i = 0; i < hba->chan_count; i++) { cp = &hba->chan[i]; /* 1 to 1 mapping between ring and channel */ cp->iopath = (void *)&hba->sli.sli3.ring[i]; cp->hba = hba; cp->channelno = i; } /* * Setup and issue mailbox CONFIGURE RING command */ for (i = 0; i < (uint32_t)hba->sli.sli3.ring_count; i++) { /* * Initialize cmd/rsp ring pointers */ rp = &hba->sli.sli3.ring[i]; /* 1 to 1 mapping between ring and channel */ rp->channelp = &hba->chan[i]; rp->hba = hba; rp->ringno = (uint8_t)i; rp->fc_cmdidx = 0; rp->fc_rspidx = 0; EMLXS_STATE_CHANGE(hba, FC_INIT_CFGRING); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_config_ring(hba, i, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to configure ring. " "Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } } /* * Setup link timers */ EMLXS_STATE_CHANGE(hba, FC_INIT_INITLINK); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_config_link(hba, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to configure link. Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } #ifdef MAX_RRDY_SUPPORT /* Set MAX_RRDY if one is provided */ if (cfg[CFG_MAX_RRDY].current) { /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_set_var(hba, (MAILBOX *)mbq, 0x00060412, cfg[CFG_MAX_RRDY].current); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "MAX_RRDY: Unable to set. status=%x " \ "value=%d", mb->mbxStatus, cfg[CFG_MAX_RRDY].current); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "MAX_RRDY: %d", cfg[CFG_MAX_RRDY].current); } } #endif /* MAX_RRDY_SUPPORT */ /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); /* * We need to get login parameters for NID */ (void) emlxs_mb_read_sparam(hba, mbq); mp = (MATCHMAP *)mbq->bp; if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to read parameters. Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } /* Free the buffer since we were polling */ emlxs_mem_put(hba, MEM_BUF, (void *)mp); mp = NULL; /* If no serial number in VPD data, then use the WWPN */ if (vpd->serial_num[0] == 0) { outptr = (uint8_t *)&hba->wwpn.IEEE[0]; for (i = 0; i < 12; i++) { status = *outptr++; j = ((status & 0xf0) >> 4); if (j <= 9) { vpd->serial_num[i] = (char)((uint8_t)'0' + (uint8_t)j); } else { vpd->serial_num[i] = (char)((uint8_t)'A' + (uint8_t)(j - 10)); } i++; j = (status & 0xf); if (j <= 9) { vpd->serial_num[i] = (char)((uint8_t)'0' + (uint8_t)j); } else { vpd->serial_num[i] = (char)((uint8_t)'A' + (uint8_t)(j - 10)); } } /* * Set port number and port index to zero * The WWN's are unique to each port and therefore port_num * must equal zero. This effects the hba_fru_details structure * in fca_bind_port() */ vpd->port_num[0] = 0; vpd->port_index = 0; } /* * Make first attempt to set a port index * Check if this is a multifunction adapter */ if ((vpd->port_index == (uint32_t)-1) && (hba->model_info.chip >= EMLXS_THOR_CHIP)) { char *buffer; int32_t i; /* * The port address looks like this: * 1 - for port index 0 * 1,1 - for port index 1 * 1,2 - for port index 2 */ buffer = ddi_get_name_addr(hba->dip); if (buffer) { vpd->port_index = 0; /* Reverse scan for a comma */ for (i = strlen(buffer) - 1; i > 0; i--) { if (buffer[i] == ',') { /* Comma found - set index now */ vpd->port_index = emlxs_strtol(&buffer[i + 1], 10); break; } } } } /* Make final attempt to set a port index */ if (vpd->port_index == (uint32_t)-1) { dev_info_t *p_dip; dev_info_t *c_dip; p_dip = ddi_get_parent(hba->dip); c_dip = ddi_get_child(p_dip); vpd->port_index = 0; while (c_dip && (hba->dip != c_dip)) { c_dip = ddi_get_next_sibling(c_dip); vpd->port_index++; } } if (vpd->port_num[0] == 0) { if (hba->model_info.channels == EMLXS_MULTI_CHANNEL) { (void) snprintf(vpd->port_num, (sizeof (vpd->port_num)-1), "%d", vpd->port_index); } } if (vpd->id[0] == 0) { (void) strncpy(vpd->id, hba->model_info.model_desc, (sizeof (vpd->id)-1)); } if (vpd->manufacturer[0] == 0) { (void) strncpy(vpd->manufacturer, hba->model_info.manufacturer, (sizeof (vpd->manufacturer)-1)); } if (vpd->part_num[0] == 0) { (void) strncpy(vpd->part_num, hba->model_info.model, (sizeof (vpd->part_num)-1)); } if (vpd->model_desc[0] == 0) { (void) strncpy(vpd->model_desc, hba->model_info.model_desc, (sizeof (vpd->model_desc)-1)); } if (vpd->model[0] == 0) { (void) strncpy(vpd->model, hba->model_info.model, (sizeof (vpd->model)-1)); } if (vpd->prog_types[0] == 0) { emlxs_build_prog_types(hba, vpd); } /* Create the symbolic names */ (void) snprintf(hba->snn, (sizeof (hba->snn)-1), "%s %s FV%s DV%s %s", hba->model_info.manufacturer, hba->model_info.model, hba->vpd.fw_version, emlxs_version, (char *)utsname.nodename); (void) snprintf(hba->spn, (sizeof (hba->spn)-1), "%s PPN-%01x%01x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", hba->model_info.manufacturer, hba->wwpn.nameType, hba->wwpn.IEEEextMsn, hba->wwpn.IEEEextLsb, hba->wwpn.IEEE[0], hba->wwpn.IEEE[1], hba->wwpn.IEEE[2], hba->wwpn.IEEE[3], hba->wwpn.IEEE[4], hba->wwpn.IEEE[5]); if (cfg[CFG_NETWORK_ON].current) { if ((hba->sparam.portName.nameType != NAME_IEEE) || (hba->sparam.portName.IEEEextMsn != 0) || (hba->sparam.portName.IEEEextLsb != 0)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "WWPN doesn't conform to IP profile: " "nameType=%x. Disabling networking.", hba->sparam.portName.nameType); cfg[CFG_NETWORK_ON].current = 0; } } if (cfg[CFG_NETWORK_ON].current) { /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); /* Issue CONFIG FARP */ emlxs_mb_config_farp(hba, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { /* * Let it go through even if failed. */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg, "Unable to configure FARP. " "Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); } } #ifdef MSI_SUPPORT /* Configure MSI map if required */ if (hba->intr_count > 1) { if (hba->intr_type == DDI_INTR_TYPE_MSIX) { /* always start from 0 */ hba->last_msiid = 0; } /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_config_msix(hba, mbq, hba->intr_map, hba->intr_count); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) == MBX_SUCCESS) { goto msi_configured; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Unable to config MSIX. Mailbox cmd=0x%x status=0x%x", mb->mbxCommand, mb->mbxStatus); /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_mb_config_msi(hba, mbq, hba->intr_map, hba->intr_count); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) == MBX_SUCCESS) { goto msi_configured; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Unable to config MSI. Mailbox cmd=0x%x status=0x%x", mb->mbxCommand, mb->mbxStatus); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Attempting single interrupt mode..."); /* First cleanup old interrupts */ (void) emlxs_msi_remove(hba); (void) emlxs_msi_uninit(hba); status = emlxs_msi_init(hba, 1); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to initialize interrupt. status=%d", status); rval = EIO; goto failed; } /* * Reset adapter - The adapter needs to be reset because * the bus cannot handle the MSI change without handshaking * with the adapter again */ (void) emlxs_mem_free_buffer(hba); fw_check = 0; goto reset; } msi_configured: if ((hba->intr_count >= 1) && (hba->sli_mode == EMLXS_HBA_SLI3_MODE)) { /* intr_count is a sequence of msi id */ /* Setup msi2chan[msi_id] */ for (i = 0; i < hba->intr_count; i ++) { hba->msi2chan[i] = i; if (i >= hba->chan_count) hba->msi2chan[i] = (i - hba->chan_count); } } #endif /* MSI_SUPPORT */ /* * We always disable the firmware traffic cop feature */ if (emlxs_disable_traffic_cop) { /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); emlxs_disable_tc(hba, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to disable traffic cop. " "Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); rval = EIO; goto failed; } } /* Reuse mbq from previous mbox */ bzero(mbq, sizeof (MAILBOXQ)); /* Register for async events */ emlxs_mb_async_event(hba, mbq); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Async events disabled. Mailbox status=%x", mb->mbxStatus); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Async events enabled."); hba->flag |= FC_ASYNC_EVENTS; } EMLXS_STATE_CHANGE(hba, FC_LINK_DOWN); emlxs_sli3_enable_intr(hba); if (hba->flag & FC_HBQ_ENABLED) { if (port->flag & EMLXS_TGT_ENABLED) { if (emlxs_hbq_setup(hba, EMLXS_FCT_HBQ_ID)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to setup FCT HBQ."); rval = ENOMEM; #ifdef SFCT_SUPPORT /* Check if we can fall back to just */ /* initiator mode */ if ((hba->pm_state == EMLXS_PM_IN_ATTACH) && (port->flag & EMLXS_INI_ENABLED) && (cfg[CFG_DTM_ENABLE].current == 1) && (cfg[CFG_TARGET_MODE].current == 0)) { cfg[CFG_DTM_ENABLE].current = 0; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Disabling dynamic target mode. " "Enabling initiator mode only."); /* This will trigger the driver to */ /* reattach */ rval = EAGAIN; } #endif /* SFCT_SUPPORT */ goto failed; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "FCT Ring: Posted %d buffers.", MEM_FCTBUF_COUNT); } if (cfg[CFG_NETWORK_ON].current) { if (emlxs_hbq_setup(hba, EMLXS_IP_HBQ_ID)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to setup IP HBQ."); rval = ENOMEM; goto failed; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "IP Ring: Posted %d buffers.", MEM_IPBUF_COUNT); } if (emlxs_hbq_setup(hba, EMLXS_ELS_HBQ_ID)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to setup ELS HBQ."); rval = ENOMEM; goto failed; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "ELS Ring: Posted %d buffers.", MEM_ELSBUF_COUNT); if (emlxs_hbq_setup(hba, EMLXS_CT_HBQ_ID)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to setup CT HBQ."); rval = ENOMEM; goto failed; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "CT Ring: Posted %d buffers.", MEM_CTBUF_COUNT); } else { if (port->flag & EMLXS_TGT_ENABLED) { /* Post the FCT unsol buffers */ rp = &hba->sli.sli3.ring[FC_FCT_RING]; for (j = 0; j < MEM_FCTBUF_COUNT; j += 2) { (void) emlxs_post_buffer(hba, rp, 2); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "FCP Ring: Posted %d buffers.", MEM_FCTBUF_COUNT); } if (cfg[CFG_NETWORK_ON].current) { /* Post the IP unsol buffers */ rp = &hba->sli.sli3.ring[FC_IP_RING]; for (j = 0; j < MEM_IPBUF_COUNT; j += 2) { (void) emlxs_post_buffer(hba, rp, 2); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "IP Ring: Posted %d buffers.", MEM_IPBUF_COUNT); } /* Post the ELS unsol buffers */ rp = &hba->sli.sli3.ring[FC_ELS_RING]; for (j = 0; j < MEM_ELSBUF_COUNT; j += 2) { (void) emlxs_post_buffer(hba, rp, 2); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "ELS Ring: Posted %d buffers.", MEM_ELSBUF_COUNT); /* Post the CT unsol buffers */ rp = &hba->sli.sli3.ring[FC_CT_RING]; for (j = 0; j < MEM_CTBUF_COUNT; j += 2) { (void) emlxs_post_buffer(hba, rp, 2); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "CT Ring: Posted %d buffers.", MEM_CTBUF_COUNT); } (void) kmem_free((uint8_t *)mbq, sizeof (MAILBOXQ)); /* Check persist-linkdown */ if (cfg[CFG_PERSIST_LINKDOWN].current) { EMLXS_STATE_CHANGE(hba, FC_LINK_DOWN_PERSIST); return (0); } #ifdef SFCT_SUPPORT if ((port->mode == MODE_TARGET) && !(port->fct_flags & FCT_STATE_PORT_ONLINE)) { emlxs_enable_latt(hba); return (0); } #endif /* SFCT_SUPPORT */ /* * Setup and issue mailbox INITIALIZE LINK command * At this point, the interrupt will be generated by the HW */ mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX); if (mbq == NULL) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to allocate mailbox buffer."); rval = EIO; goto failed; } mb = (MAILBOX *)mbq; emlxs_mb_init_link(hba, mbq, cfg[CFG_TOPOLOGY].current, cfg[CFG_LINK_SPEED].current); rval = emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_NOWAIT, 0); if ((rval != MBX_SUCCESS) && (rval != MBX_BUSY)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Unable to initialize link. " \ "Mailbox cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); mbq = NULL; rval = EIO; goto failed; } /* * Enable link attention interrupt */ emlxs_enable_latt(hba); /* Wait for link to come up */ i = cfg[CFG_LINKUP_DELAY].current; while (i && (hba->state < FC_LINK_UP)) { /* Check for hardware error */ if (hba->state == FC_ERROR) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "Adapter error."); mbq = NULL; rval = EIO; goto failed; } BUSYWAIT_MS(1000); i--; } /* * The leadvile driver will now handle the FLOGI at the driver level */ return (0); failed: EMLXS_STATE_CHANGE(hba, FC_ERROR); if (hba->intr_flags & EMLXS_MSI_ADDED) { (void) EMLXS_INTR_REMOVE(hba); } if (mp) { emlxs_mem_put(hba, MEM_BUF, (void *)mp); mp = NULL; } if (mp1) { emlxs_mem_put(hba, MEM_BUF, (void *)mp1); mp1 = NULL; } (void) emlxs_mem_free_buffer(hba); if (mbq) { (void) kmem_free((uint8_t *)mbq, sizeof (MAILBOXQ)); mbq = NULL; mb = NULL; } if (rval == 0) { rval = EIO; } return (rval); } /* emlxs_sli3_online() */ /*ARGSUSED*/ static void emlxs_sli3_offline(emlxs_hba_t *hba, uint32_t reset_requested) { /* Reverse emlxs_sli3_online */ /* Kill the adapter */ emlxs_sli3_hba_kill(hba); /* Free driver shared memory */ (void) emlxs_mem_free_buffer(hba); } /* emlxs_sli3_offline() */ static int emlxs_sli3_map_hdw(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; dev_info_t *dip; ddi_device_acc_attr_t dev_attr; int status; dip = (dev_info_t *)hba->dip; dev_attr = emlxs_dev_acc_attr; if (hba->bus_type == SBUS_FC) { if (hba->sli.sli3.slim_acc_handle == 0) { status = ddi_regs_map_setup(dip, SBUS_DFLY_SLIM_RINDEX, (caddr_t *)&hba->sli.sli3.slim_addr, 0, 0, &dev_attr, &hba->sli.sli3.slim_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(SBUS) ddi_regs_map_setup SLIM failed. " "status=%x", status); goto failed; } } if (hba->sli.sli3.csr_acc_handle == 0) { status = ddi_regs_map_setup(dip, SBUS_DFLY_CSR_RINDEX, (caddr_t *)&hba->sli.sli3.csr_addr, 0, 0, &dev_attr, &hba->sli.sli3.csr_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(SBUS) ddi_regs_map_setup DFLY CSR " "failed. status=%x", status); goto failed; } } if (hba->sli.sli3.sbus_flash_acc_handle == 0) { status = ddi_regs_map_setup(dip, SBUS_FLASH_RDWR, (caddr_t *)&hba->sli.sli3.sbus_flash_addr, 0, 0, &dev_attr, &hba->sli.sli3.sbus_flash_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(SBUS) ddi_regs_map_setup Fcode Flash " "failed. status=%x", status); goto failed; } } if (hba->sli.sli3.sbus_core_acc_handle == 0) { status = ddi_regs_map_setup(dip, SBUS_TITAN_CORE_RINDEX, (caddr_t *)&hba->sli.sli3.sbus_core_addr, 0, 0, &dev_attr, &hba->sli.sli3.sbus_core_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(SBUS) ddi_regs_map_setup TITAN CORE " "failed. status=%x", status); goto failed; } } if (hba->sli.sli3.sbus_csr_handle == 0) { status = ddi_regs_map_setup(dip, SBUS_TITAN_CSR_RINDEX, (caddr_t *)&hba->sli.sli3.sbus_csr_addr, 0, 0, &dev_attr, &hba->sli.sli3.sbus_csr_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(SBUS) ddi_regs_map_setup TITAN CSR " "failed. status=%x", status); goto failed; } } } else { /* ****** PCI ****** */ if (hba->sli.sli3.slim_acc_handle == 0) { status = ddi_regs_map_setup(dip, PCI_SLIM_RINDEX, (caddr_t *)&hba->sli.sli3.slim_addr, 0, 0, &dev_attr, &hba->sli.sli3.slim_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "(PCI) ddi_regs_map_setup SLIM failed. " "stat=%d mem=%p attr=%p hdl=%p", status, &hba->sli.sli3.slim_addr, &dev_attr, &hba->sli.sli3.slim_acc_handle); goto failed; } } /* * Map in control registers, using memory-mapped version of * the registers rather than the I/O space-mapped registers. */ if (hba->sli.sli3.csr_acc_handle == 0) { status = ddi_regs_map_setup(dip, PCI_CSR_RINDEX, (caddr_t *)&hba->sli.sli3.csr_addr, 0, 0, &dev_attr, &hba->sli.sli3.csr_acc_handle); if (status != DDI_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "ddi_regs_map_setup CSR failed. status=%x", status); goto failed; } } } if (hba->sli.sli3.slim2.virt == 0) { MBUF_INFO *buf_info; MBUF_INFO bufinfo; buf_info = &bufinfo; bzero(buf_info, sizeof (MBUF_INFO)); buf_info->size = SLI_SLIM2_SIZE; buf_info->flags = FC_MBUF_DMA | FC_MBUF_SNGLSG; buf_info->align = ddi_ptob(dip, 1L); (void) emlxs_mem_alloc(hba, buf_info); if (buf_info->virt == NULL) { goto failed; } hba->sli.sli3.slim2.virt = buf_info->virt; hba->sli.sli3.slim2.phys = buf_info->phys; hba->sli.sli3.slim2.size = SLI_SLIM2_SIZE; hba->sli.sli3.slim2.data_handle = buf_info->data_handle; hba->sli.sli3.slim2.dma_handle = buf_info->dma_handle; bzero((char *)hba->sli.sli3.slim2.virt, SLI_SLIM2_SIZE); } /* offset from beginning of register space */ hba->sli.sli3.ha_reg_addr = (uint32_t *)(hba->sli.sli3.csr_addr + (sizeof (uint32_t) * HA_REG_OFFSET)); hba->sli.sli3.ca_reg_addr = (uint32_t *)(hba->sli.sli3.csr_addr + (sizeof (uint32_t) * CA_REG_OFFSET)); hba->sli.sli3.hs_reg_addr = (uint32_t *)(hba->sli.sli3.csr_addr + (sizeof (uint32_t) * HS_REG_OFFSET)); hba->sli.sli3.hc_reg_addr = (uint32_t *)(hba->sli.sli3.csr_addr + (sizeof (uint32_t) * HC_REG_OFFSET)); hba->sli.sli3.bc_reg_addr = (uint32_t *)(hba->sli.sli3.csr_addr + (sizeof (uint32_t) * BC_REG_OFFSET)); if (hba->bus_type == SBUS_FC) { /* offset from beginning of register space */ /* for TITAN registers */ hba->sli.sli3.shc_reg_addr = (uint32_t *)(hba->sli.sli3.sbus_csr_addr + (sizeof (uint32_t) * SBUS_CTRL_REG_OFFSET)); hba->sli.sli3.shs_reg_addr = (uint32_t *)(hba->sli.sli3.sbus_csr_addr + (sizeof (uint32_t) * SBUS_STAT_REG_OFFSET)); hba->sli.sli3.shu_reg_addr = (uint32_t *)(hba->sli.sli3.sbus_csr_addr + (sizeof (uint32_t) * SBUS_UPDATE_REG_OFFSET)); } hba->chan_count = MAX_RINGS; return (0); failed: emlxs_sli3_unmap_hdw(hba); return (ENOMEM); } /* emlxs_sli3_map_hdw() */ static void emlxs_sli3_unmap_hdw(emlxs_hba_t *hba) { MBUF_INFO bufinfo; MBUF_INFO *buf_info = &bufinfo; if (hba->sli.sli3.csr_acc_handle) { ddi_regs_map_free(&hba->sli.sli3.csr_acc_handle); hba->sli.sli3.csr_acc_handle = 0; } if (hba->sli.sli3.slim_acc_handle) { ddi_regs_map_free(&hba->sli.sli3.slim_acc_handle); hba->sli.sli3.slim_acc_handle = 0; } if (hba->sli.sli3.sbus_flash_acc_handle) { ddi_regs_map_free(&hba->sli.sli3.sbus_flash_acc_handle); hba->sli.sli3.sbus_flash_acc_handle = 0; } if (hba->sli.sli3.sbus_core_acc_handle) { ddi_regs_map_free(&hba->sli.sli3.sbus_core_acc_handle); hba->sli.sli3.sbus_core_acc_handle = 0; } if (hba->sli.sli3.sbus_csr_handle) { ddi_regs_map_free(&hba->sli.sli3.sbus_csr_handle); hba->sli.sli3.sbus_csr_handle = 0; } if (hba->sli.sli3.slim2.virt) { bzero(buf_info, sizeof (MBUF_INFO)); if (hba->sli.sli3.slim2.phys) { buf_info->phys = hba->sli.sli3.slim2.phys; buf_info->data_handle = hba->sli.sli3.slim2.data_handle; buf_info->dma_handle = hba->sli.sli3.slim2.dma_handle; buf_info->flags = FC_MBUF_DMA; } buf_info->virt = hba->sli.sli3.slim2.virt; buf_info->size = hba->sli.sli3.slim2.size; emlxs_mem_free(hba, buf_info); hba->sli.sli3.slim2.virt = NULL; } return; } /* emlxs_sli3_unmap_hdw() */ static uint32_t emlxs_sli3_hba_init(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; emlxs_port_t *vport; emlxs_config_t *cfg; uint16_t i; VPIobj_t *vpip; cfg = &CFG; i = 0; /* Restart the adapter */ if (emlxs_sli3_hba_reset(hba, 1, 0, 0)) { return (1); } hba->channel_fcp = FC_FCP_RING; hba->channel_els = FC_ELS_RING; hba->channel_ip = FC_IP_RING; hba->channel_ct = FC_CT_RING; hba->chan_count = MAX_RINGS; hba->sli.sli3.ring_count = MAX_RINGS; /* * WARNING: There is a max of 6 ring masks allowed */ /* RING 0 - FCP */ if (port->flag & EMLXS_TGT_ENABLED) { hba->sli.sli3.ring_masks[FC_FCP_RING] = 1; hba->sli.sli3.ring_rval[i] = FC_FCP_CMND; hba->sli.sli3.ring_rmask[i] = 0; hba->sli.sli3.ring_tval[i] = FC_TYPE_SCSI_FCP; hba->sli.sli3.ring_tmask[i++] = 0xFF; } else { hba->sli.sli3.ring_masks[FC_FCP_RING] = 0; } hba->sli.sli3.ring[FC_FCP_RING].fc_numCiocb = SLIM_IOCB_CMD_R0_ENTRIES; hba->sli.sli3.ring[FC_FCP_RING].fc_numRiocb = SLIM_IOCB_RSP_R0_ENTRIES; /* RING 1 - IP */ if (cfg[CFG_NETWORK_ON].current) { hba->sli.sli3.ring_masks[FC_IP_RING] = 1; hba->sli.sli3.ring_rval[i] = FC_UNSOL_DATA; /* Unsol Data */ hba->sli.sli3.ring_rmask[i] = 0xFF; hba->sli.sli3.ring_tval[i] = FC_TYPE_IS8802_SNAP; /* LLC/SNAP */ hba->sli.sli3.ring_tmask[i++] = 0xFF; } else { hba->sli.sli3.ring_masks[FC_IP_RING] = 0; } hba->sli.sli3.ring[FC_IP_RING].fc_numCiocb = SLIM_IOCB_CMD_R1_ENTRIES; hba->sli.sli3.ring[FC_IP_RING].fc_numRiocb = SLIM_IOCB_RSP_R1_ENTRIES; /* RING 2 - ELS */ hba->sli.sli3.ring_masks[FC_ELS_RING] = 1; hba->sli.sli3.ring_rval[i] = FC_ELS_REQ; /* ELS request/rsp */ hba->sli.sli3.ring_rmask[i] = 0xFE; hba->sli.sli3.ring_tval[i] = FC_TYPE_EXTENDED_LS; /* ELS */ hba->sli.sli3.ring_tmask[i++] = 0xFF; hba->sli.sli3.ring[FC_ELS_RING].fc_numCiocb = SLIM_IOCB_CMD_R2_ENTRIES; hba->sli.sli3.ring[FC_ELS_RING].fc_numRiocb = SLIM_IOCB_RSP_R2_ENTRIES; /* RING 3 - CT */ hba->sli.sli3.ring_masks[FC_CT_RING] = 1; hba->sli.sli3.ring_rval[i] = FC_UNSOL_CTL; /* CT request/rsp */ hba->sli.sli3.ring_rmask[i] = 0xFE; hba->sli.sli3.ring_tval[i] = FC_TYPE_FC_SERVICES; /* CT */ hba->sli.sli3.ring_tmask[i++] = 0xFF; hba->sli.sli3.ring[FC_CT_RING].fc_numCiocb = SLIM_IOCB_CMD_R3_ENTRIES; hba->sli.sli3.ring[FC_CT_RING].fc_numRiocb = SLIM_IOCB_RSP_R3_ENTRIES; if (i > 6) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_attach_failed_msg, "hba_init: Too many ring masks defined. cnt=%d", i); return (1); } /* Initialize all the port objects */ hba->vpi_max = 0; for (i = 0; i < MAX_VPORTS; i++) { vport = &VPORT(i); vport->hba = hba; vport->vpi = i; vpip = &vport->VPIobj; vpip->index = i; vpip->VPI = i; vpip->port = vport; vpip->state = VPI_STATE_OFFLINE; vport->vpip = vpip; } /* * Initialize the max_node count to a default value if needed * This determines how many node objects we preallocate in the pool * The actual max_nodes will be set later based on adapter info */ if (hba->max_nodes == 0) { if (cfg[CFG_NUM_NODES].current > 0) { hba->max_nodes = cfg[CFG_NUM_NODES].current; } else if (hba->model_info.chip >= EMLXS_SATURN_CHIP) { hba->max_nodes = 4096; } else { hba->max_nodes = 512; } } return (0); } /* emlxs_sli3_hba_init() */ /* * 0: quiesce indicates the call is not from quiesce routine. * 1: quiesce indicates the call is from quiesce routine. */ static uint32_t emlxs_sli3_hba_reset(emlxs_hba_t *hba, uint32_t restart, uint32_t skip_post, uint32_t quiesce) { emlxs_port_t *port = &PPORT; MAILBOX swpmb; MAILBOX *mb; uint32_t *word0; uint16_t cfg_value; uint32_t status = 0; uint32_t status1; uint32_t status2; uint32_t i; uint32_t ready; emlxs_port_t *vport; RING *rp; emlxs_config_t *cfg = &CFG; if (!cfg[CFG_RESET_ENABLE].current) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_reset_failed_msg, "Adapter reset disabled."); EMLXS_STATE_CHANGE(hba, FC_ERROR); return (1); } /* Kill the adapter first */ if (quiesce == 0) { emlxs_sli3_hba_kill(hba); } else { emlxs_sli3_hba_kill4quiesce(hba); } if (restart) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Restarting."); EMLXS_STATE_CHANGE(hba, FC_INIT_START); ready = (HS_FFRDY | HS_MBRDY); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Resetting."); EMLXS_STATE_CHANGE(hba, FC_WARM_START); ready = HS_MBRDY; } hba->flag &= ~(FC_SLIM2_MODE | FC_HARDWARE_ERROR); mb = FC_SLIM1_MAILBOX(hba); word0 = (uint32_t *)&swpmb; reset: i = 0; /* Save reset time */ HBASTATS.ResetTime = hba->timer_tics; if (restart) { /* First put restart command in mailbox */ *word0 = 0; swpmb.mbxCommand = MBX_RESTART; swpmb.mbxHc = 1; WRITE_SLIM_ADDR(hba, ((volatile uint32_t *)mb), *word0); /* Only skip post after emlxs_sli3_online is completed */ if (skip_post) { WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb) + 1), 1); } else { WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb) + 1), 0); } } /* * Turn off SERR, PERR in PCI cmd register */ cfg_value = ddi_get16(hba->pci_acc_handle, (uint16_t *)(hba->pci_addr + PCI_COMMAND_REGISTER)); ddi_put16(hba->pci_acc_handle, (uint16_t *)(hba->pci_addr + PCI_COMMAND_REGISTER), (uint16_t)(cfg_value & ~(CMD_PARITY_CHK | CMD_SERR_ENBL))); hba->sli.sli3.hc_copy = HC_INITFF; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); /* Wait 1 msec before restoring PCI config */ BUSYWAIT_MS(1); /* Restore PCI cmd register */ ddi_put16(hba->pci_acc_handle, (uint16_t *)(hba->pci_addr + PCI_COMMAND_REGISTER), (uint16_t)cfg_value); /* Wait 3 seconds before checking */ BUSYWAIT_MS(3000); i += 3; /* Wait for reset completion */ while (i < 30) { /* Check status register to see what current state is */ status = READ_CSR_REG(hba, FC_HS_REG(hba)); /* Check to see if any errors occurred during init */ if (status & HS_FFERM) { status1 = READ_SLIM_ADDR(hba, ((volatile uint8_t *) hba->sli.sli3.slim_addr + 0xa8)); status2 = READ_SLIM_ADDR(hba, ((volatile uint8_t *) hba->sli.sli3.slim_addr + 0xac)); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_reset_failed_msg, "HS_FFERM: status=0x%x status1=0x%x status2=0x%x", status, status1, status2); EMLXS_STATE_CHANGE(hba, FC_ERROR); return (1); } if ((status & ready) == ready) { /* Reset Done !! */ goto done; } /* * Check every 1 second for 15 seconds, then reset board * again (w/post), then check every 1 second for 15 * seconds. */ BUSYWAIT_MS(1000); i++; /* Reset again (w/post) at 15 seconds */ if (i == 15) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Reset failed. Retrying..."); goto reset; } } #ifdef FMA_SUPPORT reset_fail: #endif /* FMA_SUPPORT */ /* Timeout occurred */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_reset_failed_msg, "Timeout: status=0x%x", status); EMLXS_STATE_CHANGE(hba, FC_ERROR); /* Log a dump event */ emlxs_log_dump_event(port, NULL, 0); return (1); done: /* Initialize hc_copy */ hba->sli.sli3.hc_copy = READ_CSR_REG(hba, FC_HC_REG(hba)); #ifdef FMA_SUPPORT /* Access handle validation */ if ((emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle) != DDI_FM_OK) || (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle) != DDI_FM_OK) || (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.csr_acc_handle) != DDI_FM_OK)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_access_handle_msg, NULL); goto reset_fail; } #endif /* FMA_SUPPORT */ /* Reset the hba structure */ hba->flag &= FC_RESET_MASK; hba->channel_tx_count = 0; hba->io_count = 0; hba->iodone_count = 0; hba->topology = 0; hba->linkspeed = 0; hba->heartbeat_active = 0; hba->discovery_timer = 0; hba->linkup_timer = 0; hba->loopback_tics = 0; /* Reset the ring objects */ for (i = 0; i < MAX_RINGS; i++) { rp = &hba->sli.sli3.ring[i]; rp->fc_mpon = 0; rp->fc_mpoff = 0; } /* Reset the port objects */ for (i = 0; i < MAX_VPORTS; i++) { vport = &VPORT(i); vport->flag &= EMLXS_PORT_RESET_MASK; vport->did = 0; vport->prev_did = 0; vport->lip_type = 0; bzero(&vport->fabric_sparam, sizeof (SERV_PARM)); bzero(&vport->prev_fabric_sparam, sizeof (SERV_PARM)); bzero((caddr_t)&vport->node_base, sizeof (NODELIST)); vport->node_base.nlp_Rpi = 0; vport->node_base.nlp_DID = 0xffffff; vport->node_base.nlp_list_next = NULL; vport->node_base.nlp_list_prev = NULL; vport->node_base.nlp_active = 1; vport->node_count = 0; if (vport->ub_count < EMLXS_UB_TOKEN_OFFSET) { vport->ub_count = EMLXS_UB_TOKEN_OFFSET; } } return (0); } /* emlxs_sli3_hba_reset */ #define BPL_CMD 0 #define BPL_RESP 1 #define BPL_DATA 2 static ULP_BDE64 * emlxs_pkt_to_bpl(fc_packet_t *pkt, ULP_BDE64 *bpl, uint32_t bpl_type) { ddi_dma_cookie_t *cp; uint_t i; int32_t size; uint_t cookie_cnt; uint8_t bdeFlags; #if (EMLXS_MODREV >= EMLXS_MODREV3) switch (bpl_type) { case BPL_CMD: cp = pkt->pkt_cmd_cookie; cookie_cnt = pkt->pkt_cmd_cookie_cnt; size = (int32_t)pkt->pkt_cmdlen; bdeFlags = 0; break; case BPL_RESP: cp = pkt->pkt_resp_cookie; cookie_cnt = pkt->pkt_resp_cookie_cnt; size = (int32_t)pkt->pkt_rsplen; bdeFlags = BUFF_USE_RCV; break; case BPL_DATA: cp = pkt->pkt_data_cookie; cookie_cnt = pkt->pkt_data_cookie_cnt; size = (int32_t)pkt->pkt_datalen; bdeFlags = (pkt->pkt_tran_type == FC_PKT_FCP_READ) ? BUFF_USE_RCV : 0; break; default: return (NULL); } #else switch (bpl_type) { case BPL_CMD: cp = &pkt->pkt_cmd_cookie; cookie_cnt = 1; size = (int32_t)pkt->pkt_cmdlen; bdeFlags = 0; break; case BPL_RESP: cp = &pkt->pkt_resp_cookie; cookie_cnt = 1; size = (int32_t)pkt->pkt_rsplen; bdeFlags = BUFF_USE_RCV; break; case BPL_DATA: cp = &pkt->pkt_data_cookie; cookie_cnt = 1; size = (int32_t)pkt->pkt_datalen; bdeFlags = (pkt->pkt_tran_type == FC_PKT_FCP_READ) ? BUFF_USE_RCV : 0; break; default: return (NULL); } #endif /* >= EMLXS_MODREV3 */ for (i = 0; i < cookie_cnt && size > 0; i++, cp++) { bpl->addrHigh = BE_SWAP32(PADDR_HI(cp->dmac_laddress)); bpl->addrLow = BE_SWAP32(PADDR_LO(cp->dmac_laddress)); bpl->tus.f.bdeSize = MIN(size, cp->dmac_size); bpl->tus.f.bdeFlags = bdeFlags; bpl->tus.w = BE_SWAP32(bpl->tus.w); bpl++; size -= cp->dmac_size; } return (bpl); } /* emlxs_pkt_to_bpl */ static uint32_t emlxs_sli2_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; fc_packet_t *pkt; MATCHMAP *bmp; ULP_BDE64 *bpl; uint64_t bp; IOCB *iocb; IOCBQ *iocbq; CHANNEL *cp; uint32_t data_cookie_cnt; uint32_t channelno; cp = sbp->channel; iocb = (IOCB *) & sbp->iocbq; pkt = PRIV2PKT(sbp); if (hba->sli.sli3.bpl_table) { bmp = hba->sli.sli3.bpl_table[sbp->iotag]; } else { bmp = (MATCHMAP *)emlxs_mem_get(hba, MEM_BPL); } if (!bmp) { return (1); } sbp->bmp = bmp; bpl = (ULP_BDE64 *)bmp->virt; bp = bmp->phys; #if (EMLXS_MODREV >= EMLXS_MODREV3) data_cookie_cnt = pkt->pkt_data_cookie_cnt; #else data_cookie_cnt = 1; #endif /* >= EMLXS_MODREV3 */ iocbq = &sbp->iocbq; channelno = (iocbq->flag & IOCB_FCP_CMD)? FC_FCP_RING:cp->channelno; switch (channelno) { case FC_FCP_RING: /* CMD payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_CMD); if (! bpl) { return (1); } /* Check if response & data payloads are needed */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { break; } /* RSP payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_RESP); if (! bpl) { return (1); } /* Check if data payload is needed */ if ((pkt->pkt_datalen == 0) || (data_cookie_cnt == 0)) { break; } /* DATA payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_DATA); if (! bpl) { return (1); } break; case FC_IP_RING: /* CMD payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_CMD); if (! bpl) { return (1); } break; case FC_ELS_RING: /* CMD payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_CMD); if (! bpl) { return (1); } /* Check if response payload is needed */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { break; } /* RSP payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_RESP); if (! bpl) { return (1); } break; case FC_CT_RING: /* CMD payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_CMD); if (! bpl) { return (1); } /* Check if response payload is needed */ if ((pkt->pkt_tran_type == FC_PKT_OUTBOUND) && (pkt->pkt_cmd_fhdr.type != EMLXS_MENLO_TYPE)) { break; } /* RSP payload */ bpl = emlxs_pkt_to_bpl(pkt, bpl, BPL_RESP); if (! bpl) { return (1); } break; } iocb->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL; iocb->un.genreq64.bdl.addrHigh = PADDR_HI(bp); iocb->un.genreq64.bdl.addrLow = PADDR_LO(bp); iocb->un.genreq64.bdl.bdeSize = (uint32_t)(((uintptr_t)bpl - (uintptr_t)bmp->virt) & 0xFFFFFFFF); iocb->ULPBDECOUNT = 1; iocb->ULPLE = 1; return (0); } /* emlxs_sli2_bde_setup */ static uint32_t emlxs_sli3_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { ddi_dma_cookie_t *cp_cmd; ddi_dma_cookie_t *cp_resp; ddi_dma_cookie_t *cp_data; fc_packet_t *pkt; ULP_BDE64 *bde; int data_cookie_cnt; uint32_t i; uint32_t channelno; IOCB *iocb; IOCBQ *iocbq; CHANNEL *cp; pkt = PRIV2PKT(sbp); #if (EMLXS_MODREV >= EMLXS_MODREV3) if ((pkt->pkt_cmd_cookie_cnt > 1) || (pkt->pkt_resp_cookie_cnt > 1) || ((pkt->pkt_cmd_cookie_cnt + pkt->pkt_resp_cookie_cnt + pkt->pkt_data_cookie_cnt) > SLI3_MAX_BDE)) { i = emlxs_sli2_bde_setup(port, sbp); return (i); } cp_cmd = pkt->pkt_cmd_cookie; cp_resp = pkt->pkt_resp_cookie; cp_data = pkt->pkt_data_cookie; data_cookie_cnt = pkt->pkt_data_cookie_cnt; #else cp_cmd = &pkt->pkt_cmd_cookie; cp_resp = &pkt->pkt_resp_cookie; cp_data = &pkt->pkt_data_cookie; data_cookie_cnt = 1; #endif /* >= EMLXS_MODREV3 */ cp = sbp->channel; iocbq = &sbp->iocbq; iocb = (IOCB *)iocbq; iocb->unsli3.ext_iocb.ebde_count = 0; channelno = (iocbq->flag & IOCB_FCP_CMD)? FC_FCP_RING:cp->channelno; switch (channelno) { case FC_FCP_RING: /* CMD payload */ iocb->un.fcpi64.bdl.addrHigh = PADDR_HI(cp_cmd->dmac_laddress); iocb->un.fcpi64.bdl.addrLow = PADDR_LO(cp_cmd->dmac_laddress); iocb->un.fcpi64.bdl.bdeSize = pkt->pkt_cmdlen; iocb->un.fcpi64.bdl.bdeFlags = 0; /* Check if a response & data payload are needed */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { break; } /* RSP payload */ iocb->unsli3.ext_iocb.ebde1.addrHigh = PADDR_HI(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.addrLow = PADDR_LO(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.tus.f.bdeSize = pkt->pkt_rsplen; iocb->unsli3.ext_iocb.ebde1.tus.f.bdeFlags = 0; iocb->unsli3.ext_iocb.ebde_count = 1; /* Check if a data payload is needed */ if ((pkt->pkt_datalen == 0) || (data_cookie_cnt == 0)) { break; } /* DATA payload */ bde = (ULP_BDE64 *)&iocb->unsli3.ext_iocb.ebde2; for (i = 0; i < data_cookie_cnt; i++) { bde->addrHigh = PADDR_HI(cp_data->dmac_laddress); bde->addrLow = PADDR_LO(cp_data->dmac_laddress); bde->tus.f.bdeSize = cp_data->dmac_size; bde->tus.f.bdeFlags = 0; cp_data++; bde++; } iocb->unsli3.ext_iocb.ebde_count += data_cookie_cnt; break; case FC_IP_RING: /* CMD payload */ iocb->un.xseq64.bdl.addrHigh = PADDR_HI(cp_cmd->dmac_laddress); iocb->un.xseq64.bdl.addrLow = PADDR_LO(cp_cmd->dmac_laddress); iocb->un.xseq64.bdl.bdeSize = pkt->pkt_cmdlen; iocb->un.xseq64.bdl.bdeFlags = 0; break; case FC_ELS_RING: /* CMD payload */ iocb->un.elsreq64.bdl.addrHigh = PADDR_HI(cp_cmd->dmac_laddress); iocb->un.elsreq64.bdl.addrLow = PADDR_LO(cp_cmd->dmac_laddress); iocb->un.elsreq64.bdl.bdeSize = pkt->pkt_cmdlen; iocb->un.elsreq64.bdl.bdeFlags = 0; /* Check if a response payload is needed */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { break; } /* RSP payload */ iocb->unsli3.ext_iocb.ebde1.addrHigh = PADDR_HI(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.addrLow = PADDR_LO(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.tus.f.bdeSize = pkt->pkt_rsplen; iocb->unsli3.ext_iocb.ebde1.tus.f.bdeFlags = BUFF_USE_RCV; iocb->unsli3.ext_iocb.ebde_count = 1; break; case FC_CT_RING: /* CMD payload */ iocb->un.genreq64.bdl.addrHigh = PADDR_HI(cp_cmd->dmac_laddress); iocb->un.genreq64.bdl.addrLow = PADDR_LO(cp_cmd->dmac_laddress); iocb->un.genreq64.bdl.bdeSize = pkt->pkt_cmdlen; iocb->un.genreq64.bdl.bdeFlags = 0; /* Check if a response payload is needed */ if ((pkt->pkt_tran_type == FC_PKT_OUTBOUND) && (pkt->pkt_cmd_fhdr.type != EMLXS_MENLO_TYPE)) { break; } /* RSP payload */ iocb->unsli3.ext_iocb.ebde1.addrHigh = PADDR_HI(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.addrLow = PADDR_LO(cp_resp->dmac_laddress); iocb->unsli3.ext_iocb.ebde1.tus.f.bdeSize = pkt->pkt_rsplen; iocb->unsli3.ext_iocb.ebde1.tus.f.bdeFlags = BUFF_USE_RCV; iocb->unsli3.ext_iocb.ebde_count = 1; break; } iocb->ULPBDECOUNT = 0; iocb->ULPLE = 0; return (0); } /* emlxs_sli3_bde_setup */ /* Only used for FCP Data xfers */ #ifdef SFCT_SUPPORT /*ARGSUSED*/ static uint32_t emlxs_sli2_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; scsi_task_t *fct_task; MATCHMAP *bmp; ULP_BDE64 *bpl; uint64_t bp; uint8_t bdeFlags; IOCB *iocb; uint32_t size; MATCHMAP *mp; iocb = (IOCB *)&sbp->iocbq.iocb; sbp->bmp = NULL; if (!sbp->fct_buf) { iocb->un.fcpt64.bdl.addrHigh = 0; iocb->un.fcpt64.bdl.addrLow = 0; iocb->un.fcpt64.bdl.bdeSize = 0; iocb->un.fcpt64.bdl.bdeFlags = 0; iocb->un.fcpt64.fcpt_Offset = 0; iocb->un.fcpt64.fcpt_Length = 0; iocb->ULPBDECOUNT = 0; iocb->ULPLE = 1; return (0); } if (hba->sli.sli3.bpl_table) { bmp = hba->sli.sli3.bpl_table[sbp->iotag]; } else { bmp = (MATCHMAP *)emlxs_mem_get(hba, MEM_BPL); } if (!bmp) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fct_error_msg, "fct_sli2_bde_setup: Unable to BPL buffer. iotag=%d", sbp->iotag); iocb->un.fcpt64.bdl.addrHigh = 0; iocb->un.fcpt64.bdl.addrLow = 0; iocb->un.fcpt64.bdl.bdeSize = 0; iocb->un.fcpt64.bdl.bdeFlags = 0; iocb->un.fcpt64.fcpt_Offset = 0; iocb->un.fcpt64.fcpt_Length = 0; iocb->ULPBDECOUNT = 0; iocb->ULPLE = 1; return (1); } bpl = (ULP_BDE64 *)bmp->virt; bp = bmp->phys; fct_task = (scsi_task_t *)sbp->fct_cmd->cmd_specific; size = sbp->fct_buf->db_data_size; mp = (MATCHMAP *)sbp->fct_buf->db_port_private; bdeFlags = (fct_task->task_flags & TF_WRITE_DATA) ? BUFF_USE_RCV : 0; /* Init the buffer list */ bpl->addrHigh = BE_SWAP32(PADDR_HI(mp->phys)); bpl->addrLow = BE_SWAP32(PADDR_LO(mp->phys)); bpl->tus.f.bdeSize = size; bpl->tus.f.bdeFlags = bdeFlags; bpl->tus.w = BE_SWAP32(bpl->tus.w); /* Init the IOCB */ iocb->un.fcpt64.bdl.addrHigh = PADDR_HI(bp); iocb->un.fcpt64.bdl.addrLow = PADDR_LO(bp); iocb->un.fcpt64.bdl.bdeSize = sizeof (ULP_BDE64); iocb->un.fcpt64.bdl.bdeFlags = BUFF_TYPE_BDL; iocb->un.fcpt64.fcpt_Length = (fct_task->task_flags & TF_WRITE_DATA) ? size : 0; iocb->un.fcpt64.fcpt_Offset = 0; iocb->ULPBDECOUNT = 1; iocb->ULPLE = 1; sbp->bmp = bmp; return (0); } /* emlxs_sli2_fct_bde_setup */ #endif /* SFCT_SUPPORT */ #ifdef SFCT_SUPPORT /*ARGSUSED*/ static uint32_t emlxs_sli3_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { scsi_task_t *fct_task; IOCB *iocb; MATCHMAP *mp; uint32_t bdeFlags; uint32_t size; iocb = (IOCB *)&sbp->iocbq; if (!sbp->fct_buf) { iocb->un.fcpt64.bdl.addrHigh = 0; iocb->un.fcpt64.bdl.addrLow = 0; iocb->un.fcpt64.bdl.bdeSize = 0; iocb->un.fcpt64.bdl.bdeFlags = 0; iocb->un.fcpt64.fcpt_Offset = 0; iocb->un.fcpt64.fcpt_Length = 0; iocb->ULPBDECOUNT = 0; iocb->ULPLE = 0; iocb->unsli3.ext_iocb.ebde_count = 0; return (0); } fct_task = (scsi_task_t *)sbp->fct_cmd->cmd_specific; size = sbp->fct_buf->db_data_size; mp = (MATCHMAP *)sbp->fct_buf->db_port_private; bdeFlags = (fct_task->task_flags & TF_WRITE_DATA) ? BUFF_USE_RCV : 0; /* Init first BDE */ iocb->un.fcpt64.bdl.addrHigh = PADDR_HI(mp->phys); iocb->un.fcpt64.bdl.addrLow = PADDR_LO(mp->phys); iocb->un.fcpt64.bdl.bdeSize = size; iocb->un.fcpt64.bdl.bdeFlags = bdeFlags; iocb->unsli3.ext_iocb.ebde_count = 0; iocb->un.fcpt64.fcpt_Length = (fct_task->task_flags & TF_WRITE_DATA) ? size : 0; iocb->un.fcpt64.fcpt_Offset = 0; iocb->ULPBDECOUNT = 0; iocb->ULPLE = 0; return (0); } /* emlxs_sli3_fct_bde_setup */ #endif /* SFCT_SUPPORT */ static void emlxs_sli3_issue_iocb_cmd(emlxs_hba_t *hba, CHANNEL *cp, IOCBQ *iocbq) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ PGP *pgp; emlxs_buf_t *sbp; SLIM2 *slim2p = (SLIM2 *)hba->sli.sli3.slim2.virt; RING *rp; uint32_t nextIdx; uint32_t status; void *ioa2; off_t offset; uint32_t count = 0; uint32_t flag; uint32_t channelno; int32_t throttle; #ifdef NODE_THROTTLE_SUPPORT int32_t node_throttle; NODELIST *marked_node = NULL; #endif /* NODE_THROTTLE_SUPPORT */ channelno = cp->channelno; rp = (RING *)cp->iopath; throttle = 0; /* Check if FCP ring and adapter is not ready */ /* We may use any ring for FCP_CMD */ if (iocbq && (iocbq->flag & IOCB_FCP_CMD) && (hba->state != FC_READY)) { if (!(iocbq->flag & IOCB_SPECIAL) || !iocbq->port || (((emlxs_port_t *)iocbq->port)->mode == MODE_INITIATOR)) { emlxs_tx_put(iocbq, 1); return; } } /* Attempt to acquire CMD_RING lock */ if (mutex_tryenter(&EMLXS_CMD_RING_LOCK(channelno)) == 0) { /* Queue it for later */ if (iocbq) { if ((hba->io_count - hba->channel_tx_count) > 10) { emlxs_tx_put(iocbq, 1); return; } else { /* * EMLXS_MSGF(EMLXS_CONTEXT, * &emlxs_ring_watchdog_msg, * "%s host=%d port=%d cnt=%d,%d RACE * CONDITION3 DETECTED.", * emlxs_ring_xlate(channelno), * rp->fc_cmdidx, rp->fc_port_cmdidx, * hba->channel_tx_count, * hba->io_count); */ mutex_enter(&EMLXS_CMD_RING_LOCK(channelno)); } } else { return; } } /* CMD_RING_LOCK acquired */ /* Throttle check only applies to non special iocb */ if (iocbq && (!(iocbq->flag & IOCB_SPECIAL))) { /* Check if HBA is full */ throttle = hba->io_throttle - hba->io_active; if (throttle <= 0) { /* Hitting adapter throttle limit */ /* Queue it for later */ if (iocbq) { emlxs_tx_put(iocbq, 1); } goto busy; } } /* Read adapter's get index */ pgp = (PGP *) &((SLIM2 *)hba->sli.sli3.slim2.virt)->mbx.us.s2.port[channelno]; offset = (off_t)((uint64_t)((unsigned long)&(pgp->cmdGetInx)) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORKERNEL); rp->fc_port_cmdidx = BE_SWAP32(pgp->cmdGetInx); /* Calculate the next put index */ nextIdx = (rp->fc_cmdidx + 1 >= rp->fc_numCiocb) ? 0 : rp->fc_cmdidx + 1; /* Check if ring is full */ if (nextIdx == rp->fc_port_cmdidx) { /* Try one more time */ EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORKERNEL); rp->fc_port_cmdidx = BE_SWAP32(pgp->cmdGetInx); if (nextIdx == rp->fc_port_cmdidx) { /* Queue it for later */ if (iocbq) { emlxs_tx_put(iocbq, 1); } goto busy; } } /* * We have a command ring slot available * Make sure we have an iocb to send */ if (iocbq) { mutex_enter(&EMLXS_TX_CHANNEL_LOCK); /* Check if the ring already has iocb's waiting */ if (cp->nodeq.q_first != NULL) { /* Put the current iocbq on the tx queue */ emlxs_tx_put(iocbq, 0); /* * Attempt to replace it with the next iocbq * in the tx queue */ iocbq = emlxs_tx_get(cp, 0); } mutex_exit(&EMLXS_TX_CHANNEL_LOCK); } else { /* Try to get the next iocb on the tx queue */ iocbq = emlxs_tx_get(cp, 1); } sendit: count = 0; /* Process each iocbq */ while (iocbq) { sbp = iocbq->sbp; #ifdef NODE_THROTTLE_SUPPORT if (sbp && sbp->node && sbp->node->io_throttle) { node_throttle = sbp->node->io_throttle - sbp->node->io_active; if (node_throttle <= 0) { /* Node is busy */ /* Queue this iocb and get next iocb from */ /* channel */ if (!marked_node) { marked_node = sbp->node; } mutex_enter(&EMLXS_TX_CHANNEL_LOCK); emlxs_tx_put(iocbq, 0); if (cp->nodeq.q_first == marked_node) { mutex_exit(&EMLXS_TX_CHANNEL_LOCK); goto busy; } iocbq = emlxs_tx_get(cp, 0); mutex_exit(&EMLXS_TX_CHANNEL_LOCK); continue; } } marked_node = 0; #endif /* NODE_THROTTLE_SUPPORT */ if (sbp && (sbp->pkt_flags & PACKET_DELAY_REQUIRED)) { /* * Update adapter if needed, since we are about to * delay here */ if (count) { count = 0; /* Update the adapter's cmd put index */ if (hba->bus_type == SBUS_FC) { slim2p->mbx.us.s2.host[channelno]. cmdPutInx = BE_SWAP32(rp->fc_cmdidx); /* DMA sync the index for the adapter */ offset = (off_t) ((uint64_t) ((unsigned long)&(slim2p->mbx.us. s2.host[channelno].cmdPutInx)) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2. dma_handle, offset, 4, DDI_DMA_SYNC_FORDEV); } else { ioa2 = (void *) ((char *)hba->sli.sli3.slim_addr + hba->sli.sli3.hgp_ring_offset + ((channelno * 2) * sizeof (uint32_t))); WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, rp->fc_cmdidx); } status = (CA_R0ATT << (channelno * 4)); WRITE_CSR_REG(hba, FC_CA_REG(hba), (volatile uint32_t)status); } /* Perform delay */ if ((channelno == FC_ELS_RING) && !(iocbq->flag & IOCB_FCP_CMD)) { drv_usecwait(100000); } else { drv_usecwait(20000); } } /* * At this point, we have a command ring slot available * and an iocb to send */ flag = iocbq->flag; /* Send the iocb */ emlxs_sli3_issue_iocb(hba, rp, iocbq); /* * After this, the sbp / iocb should not be * accessed in the xmit path. */ count++; if (iocbq && (!(flag & IOCB_SPECIAL))) { /* Check if HBA is full */ throttle = hba->io_throttle - hba->io_active; if (throttle <= 0) { goto busy; } } /* Calculate the next put index */ nextIdx = (rp->fc_cmdidx + 1 >= rp->fc_numCiocb) ? 0 : rp->fc_cmdidx + 1; /* Check if ring is full */ if (nextIdx == rp->fc_port_cmdidx) { /* Try one more time */ EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORKERNEL); rp->fc_port_cmdidx = BE_SWAP32(pgp->cmdGetInx); if (nextIdx == rp->fc_port_cmdidx) { goto busy; } } /* Get the next iocb from the tx queue if there is one */ iocbq = emlxs_tx_get(cp, 1); } if (count) { /* Update the adapter's cmd put index */ if (hba->bus_type == SBUS_FC) { slim2p->mbx.us.s2.host[channelno]. cmdPutInx = BE_SWAP32(rp->fc_cmdidx); /* DMA sync the index for the adapter */ offset = (off_t) ((uint64_t)((unsigned long)&(slim2p->mbx.us.s2. host[channelno].cmdPutInx)) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORDEV); } else { ioa2 = (void *)((char *)hba->sli.sli3.slim_addr + hba->sli.sli3.hgp_ring_offset + ((channelno * 2) * sizeof (uint32_t))); WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, rp->fc_cmdidx); } status = (CA_R0ATT << (channelno * 4)); WRITE_CSR_REG(hba, FC_CA_REG(hba), (volatile uint32_t)status); /* Check tx queue one more time before releasing */ if ((iocbq = emlxs_tx_get(cp, 1))) { /* * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_ring_watchdog_msg, * "%s host=%d port=%d RACE CONDITION1 * DETECTED.", emlxs_ring_xlate(channelno), * rp->fc_cmdidx, rp->fc_port_cmdidx); */ goto sendit; } } #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_CMD_RING_LOCK(channelno)); return; busy: /* * Set ring to SET R0CE_REQ in Chip Att register. * Chip will tell us when an entry is freed. */ if (count) { /* Update the adapter's cmd put index */ if (hba->bus_type == SBUS_FC) { slim2p->mbx.us.s2.host[channelno].cmdPutInx = BE_SWAP32(rp->fc_cmdidx); /* DMA sync the index for the adapter */ offset = (off_t) ((uint64_t)((unsigned long)&(slim2p->mbx.us.s2. host[channelno].cmdPutInx)) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORDEV); } else { ioa2 = (void *)((char *)hba->sli.sli3.slim_addr + hba->sli.sli3.hgp_ring_offset + ((channelno * 2) * sizeof (uint32_t))); WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, rp->fc_cmdidx); } } status = ((CA_R0ATT | CA_R0CE_REQ) << (channelno * 4)); WRITE_CSR_REG(hba, FC_CA_REG(hba), (volatile uint32_t)status); if (throttle <= 0) { HBASTATS.IocbThrottled++; } else { HBASTATS.IocbRingFull[channelno]++; } #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_CMD_RING_LOCK(channelno)); return; } /* emlxs_sli3_issue_iocb_cmd() */ /* MBX_NOWAIT - returns MBX_BUSY or MBX_SUCCESS or MBX_HARDWARE_ERROR */ /* MBX_WAIT - returns MBX_TIMEOUT or mailbox_status */ /* MBX_SLEEP - returns MBX_TIMEOUT or mailbox_status */ /* MBX_POLL - returns MBX_TIMEOUT or mailbox_status */ static uint32_t emlxs_sli3_issue_mbox_cmd(emlxs_hba_t *hba, MAILBOXQ *mbq, int32_t flag, uint32_t tmo) { emlxs_port_t *port; SLIM2 *slim2p = (SLIM2 *)hba->sli.sli3.slim2.virt; MAILBOX *mbox; MAILBOX *mb; uint32_t *word0; volatile uint32_t ldata; off_t offset; MATCHMAP *mbox_bp; uint32_t tmo_local; MAILBOX swpmb; if (!mbq->port) { mbq->port = &PPORT; } port = (emlxs_port_t *)mbq->port; mb = (MAILBOX *)mbq; word0 = (uint32_t *)&swpmb; mb->mbxStatus = MBX_SUCCESS; /* Check for minimum timeouts */ switch (mb->mbxCommand) { /* Mailbox commands that erase/write flash */ case MBX_DOWN_LOAD: case MBX_UPDATE_CFG: case MBX_LOAD_AREA: case MBX_LOAD_EXP_ROM: case MBX_WRITE_NV: case MBX_FLASH_WR_ULA: case MBX_DEL_LD_ENTRY: case MBX_LOAD_SM: if (tmo < 300) { tmo = 300; } break; default: if (tmo < 30) { tmo = 30; } break; } /* Convert tmo seconds to 10 millisecond tics */ tmo_local = tmo * 100; /* Adjust wait flag */ if (flag != MBX_NOWAIT) { /* If interrupt is enabled, use sleep, otherwise poll */ if (hba->sli.sli3.hc_copy & HC_MBINT_ENA) { flag = MBX_SLEEP; } else { flag = MBX_POLL; } } mutex_enter(&EMLXS_PORT_LOCK); /* Check for hardware error */ if (hba->flag & FC_HARDWARE_ERROR) { mb->mbxStatus = (hba->flag & FC_OVERTEMP_EVENT) ? MBX_OVERTEMP_ERROR : MBX_HARDWARE_ERROR; mutex_exit(&EMLXS_PORT_LOCK); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Hardware error reported. %s failed. status=%x mb=%p", emlxs_mb_cmd_xlate(mb->mbxCommand), mb->mbxStatus, mb); return (MBX_HARDWARE_ERROR); } if (hba->mbox_queue_flag) { /* If we are not polling, then queue it for later */ if (flag == MBX_NOWAIT) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Busy. %s: mb=%p NoWait.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); emlxs_mb_put(hba, mbq); HBASTATS.MboxBusy++; mutex_exit(&EMLXS_PORT_LOCK); return (MBX_BUSY); } while (hba->mbox_queue_flag) { mutex_exit(&EMLXS_PORT_LOCK); if (tmo_local-- == 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_event_msg, "Timeout. %s: mb=%p tmo=%d Waiting.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, tmo); /* Non-lethalStatus mailbox timeout */ /* Does not indicate a hardware error */ mb->mbxStatus = MBX_TIMEOUT; return (MBX_TIMEOUT); } BUSYWAIT_MS(10); mutex_enter(&EMLXS_PORT_LOCK); /* Check for hardware error */ if (hba->flag & FC_HARDWARE_ERROR) { mb->mbxStatus = (hba->flag & FC_OVERTEMP_EVENT) ? MBX_OVERTEMP_ERROR : MBX_HARDWARE_ERROR; mutex_exit(&EMLXS_PORT_LOCK); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Hardware error reported. %s failed. " "status=%x mb=%p", emlxs_mb_cmd_xlate(mb->mbxCommand), mb->mbxStatus, mb); return (MBX_HARDWARE_ERROR); } } } /* Initialize mailbox area */ emlxs_mb_init(hba, mbq, flag, tmo); switch (flag) { case MBX_NOWAIT: if (mb->mbxCommand != MBX_HEARTBEAT) { if (mb->mbxCommand != MBX_DOWN_LOAD && mb->mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Sending. %s: mb=%p NoWait.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); } } break; case MBX_SLEEP: if (mb->mbxCommand != MBX_DOWN_LOAD && mb->mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Sending. %s: mb=%p Sleep.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); } break; case MBX_POLL: if (mb->mbxCommand != MBX_DOWN_LOAD && mb->mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Sending. %s: mb=%p Polled.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); } break; } mb->mbxOwner = OWN_CHIP; /* Clear the attention bit */ WRITE_CSR_REG(hba, FC_HA_REG(hba), HA_MBATT); if (hba->flag & FC_SLIM2_MODE) { /* First copy command data */ mbox = FC_SLIM2_MAILBOX(hba); offset = (off_t)((uint64_t)((unsigned long)mbox) - (uint64_t)((unsigned long)slim2p)); #ifdef MBOX_EXT_SUPPORT if (mbq->extbuf) { uint32_t *mbox_ext = (uint32_t *)((uint8_t *)mbox + MBOX_EXTENSION_OFFSET); off_t offset_ext = offset + MBOX_EXTENSION_OFFSET; BE_SWAP32_BCOPY((uint8_t *)mbq->extbuf, (uint8_t *)mbox_ext, mbq->extsize); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset_ext, mbq->extsize, DDI_DMA_SYNC_FORDEV); } #endif /* MBOX_EXT_SUPPORT */ BE_SWAP32_BCOPY((uint8_t *)mb, (uint8_t *)mbox, MAILBOX_CMD_BSIZE); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, MAILBOX_CMD_BSIZE, DDI_DMA_SYNC_FORDEV); } else { /* SLIM 1 */ mbox = FC_SLIM1_MAILBOX(hba); #ifdef MBOX_EXT_SUPPORT if (mbq->extbuf) { uint32_t *mbox_ext = (uint32_t *)((uint8_t *)mbox + MBOX_EXTENSION_OFFSET); WRITE_SLIM_COPY(hba, (uint32_t *)mbq->extbuf, mbox_ext, (mbq->extsize / 4)); } #endif /* MBOX_EXT_SUPPORT */ /* First copy command data */ WRITE_SLIM_COPY(hba, &mb->un.varWords, &mbox->un.varWords, (MAILBOX_CMD_WSIZE - 1)); /* copy over last word, with mbxOwner set */ ldata = *((volatile uint32_t *)mb); WRITE_SLIM_ADDR(hba, ((volatile uint32_t *)mbox), ldata); } /* Interrupt board to do it right away */ WRITE_CSR_REG(hba, FC_CA_REG(hba), CA_MBATT); mutex_exit(&EMLXS_PORT_LOCK); #ifdef FMA_SUPPORT /* Access handle validation */ if ((emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle) != DDI_FM_OK) || (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.csr_acc_handle) != DDI_FM_OK)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_access_handle_msg, NULL); return (MBX_HARDWARE_ERROR); } #endif /* FMA_SUPPORT */ switch (flag) { case MBX_NOWAIT: return (MBX_SUCCESS); case MBX_SLEEP: /* Wait for completion */ /* The driver clock is timing the mailbox. */ /* emlxs_mb_fini() will be called externally. */ mutex_enter(&EMLXS_MBOX_LOCK); while (!(mbq->flag & MBQ_COMPLETED)) { cv_wait(&EMLXS_MBOX_CV, &EMLXS_MBOX_LOCK); } mutex_exit(&EMLXS_MBOX_LOCK); if (mb->mbxStatus == MBX_TIMEOUT) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_event_msg, "Timeout. %s: mb=%p tmo=%d. Sleep.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, tmo); } else { if (mb->mbxCommand != MBX_DOWN_LOAD && mb->mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Completed. %s: mb=%p status=%x Sleep.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, mb->mbxStatus); } } break; case MBX_POLL: /* Convert tmo seconds to 500 usec tics */ tmo_local = tmo * 2000; /* Get first word of mailbox */ if (hba->flag & FC_SLIM2_MODE) { mbox = FC_SLIM2_MAILBOX(hba); offset = (off_t)((uint64_t)((unsigned long)mbox) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL); *word0 = *((volatile uint32_t *)mbox); *word0 = BE_SWAP32(*word0); } else { mbox = FC_SLIM1_MAILBOX(hba); *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mbox)); } /* Wait for command to complete */ while ((swpmb.mbxOwner == OWN_CHIP) && !(mbq->flag & MBQ_COMPLETED)) { if (!hba->timer_id && (tmo_local-- == 0)) { /* self time */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, "%s: mb=%p tmo=%d Polled.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, tmo); hba->flag |= FC_MBOX_TIMEOUT; EMLXS_STATE_CHANGE(hba, FC_ERROR); emlxs_mb_fini(hba, NULL, MBX_TIMEOUT); break; } BUSYWAIT_US(500); /* Get first word of mailbox */ if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC( hba->sli.sli3.slim2.dma_handle, offset, sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL); *word0 = *((volatile uint32_t *)mbox); *word0 = BE_SWAP32(*word0); } else { *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mbox)); } } /* while */ if (mb->mbxStatus == MBX_TIMEOUT) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_event_msg, "Timeout. %s: mb=%p tmo=%d. Polled.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, tmo); break; } /* Check for config port command */ if ((swpmb.mbxCommand == MBX_CONFIG_PORT) && (swpmb.mbxStatus == MBX_SUCCESS)) { /* Setup host mbox for cmpl */ mbox = FC_SLIM2_MAILBOX(hba); offset = (off_t)((uint64_t)((unsigned long)mbox) - (uint64_t)((unsigned long)slim2p)); hba->flag |= FC_SLIM2_MODE; } /* copy results back to user */ if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, MAILBOX_CMD_BSIZE, DDI_DMA_SYNC_FORKERNEL); BE_SWAP32_BCOPY((uint8_t *)mbox, (uint8_t *)mb, MAILBOX_CMD_BSIZE); } else { READ_SLIM_COPY(hba, (uint32_t *)mb, (uint32_t *)mbox, MAILBOX_CMD_WSIZE); } #ifdef MBOX_EXT_SUPPORT if (mbq->extbuf) { uint32_t *mbox_ext = (uint32_t *)((uint8_t *)mbox + MBOX_EXTENSION_OFFSET); off_t offset_ext = offset + MBOX_EXTENSION_OFFSET; if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC( hba->sli.sli3.slim2.dma_handle, offset_ext, mbq->extsize, DDI_DMA_SYNC_FORKERNEL); BE_SWAP32_BCOPY((uint8_t *)mbox_ext, (uint8_t *)mbq->extbuf, mbq->extsize); } else { READ_SLIM_COPY(hba, (uint32_t *)mbq->extbuf, mbox_ext, (mbq->extsize / 4)); } } #endif /* MBOX_EXT_SUPPORT */ /* Sync the memory buffer */ if (mbq->bp) { mbox_bp = (MATCHMAP *)mbq->bp; EMLXS_MPDATA_SYNC(mbox_bp->dma_handle, 0, mbox_bp->size, DDI_DMA_SYNC_FORKERNEL); } if (mb->mbxCommand != MBX_DOWN_LOAD && mb->mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Completed. %s: mb=%p status=%x Polled.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, mb->mbxStatus); } /* Process the result */ if (!(mbq->flag & MBQ_PASSTHRU)) { if (mbq->mbox_cmpl) { (void) (mbq->mbox_cmpl)(hba, mbq); } } /* Clear the attention bit */ WRITE_CSR_REG(hba, FC_HA_REG(hba), HA_MBATT); /* Clean up the mailbox area */ emlxs_mb_fini(hba, NULL, mb->mbxStatus); break; } /* switch (flag) */ return (mb->mbxStatus); } /* emlxs_sli3_issue_mbox_cmd() */ #ifdef SFCT_SUPPORT /*ARGSUSED*/ static uint32_t emlxs_sli3_prep_fct_iocb(emlxs_port_t *port, emlxs_buf_t *cmd_sbp, int channel) { emlxs_hba_t *hba = HBA; emlxs_config_t *cfg = &CFG; fct_cmd_t *fct_cmd; stmf_data_buf_t *dbuf; scsi_task_t *fct_task; fc_packet_t *pkt; uint32_t did; IOCBQ *iocbq; IOCB *iocb; uint32_t timeout; uint32_t iotag; emlxs_node_t *ndlp; CHANNEL *cp; ddi_dma_cookie_t *cp_cmd; pkt = PRIV2PKT(cmd_sbp); cp = (CHANNEL *)cmd_sbp->channel; iocbq = &cmd_sbp->iocbq; iocb = &iocbq->iocb; /* Get the iotag by registering the packet */ iotag = emlxs_register_pkt(cp, cmd_sbp); if (!iotag) { /* No more command slots available, retry later */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to allocate iotag. did=0x%x", cmd_sbp->did); return (IOERR_NO_RESOURCES); } /* Point of no return */ if (iocb->ULPCOMMAND == CMD_ABORT_XRI_CX) { ndlp = cmd_sbp->node; cp->ulpSendCmd++; /* Initalize iocbq */ iocbq->port = (void *)port; iocbq->node = (void *)ndlp; iocbq->channel = (void *)cp; /* * Don't give the abort priority, we want the IOCB * we are aborting to be processed first. */ iocbq->flag |= IOCB_SPECIAL; iocb->ULPCONTEXT = pkt->pkt_cmd_fhdr.rx_id; iocb->ULPIOTAG = (uint16_t)iotag; iocb->ULPLE = 1; iocb->ULPCLASS = cmd_sbp->class; iocb->ULPOWNER = OWN_CHIP; if (hba->state >= FC_LINK_UP) { /* Create the abort IOCB */ iocb->un.acxri.abortType = ABORT_TYPE_ABTS; iocb->ULPCOMMAND = CMD_ABORT_XRI_CX; } else { /* Create the close IOCB */ iocb->ULPCOMMAND = CMD_CLOSE_XRI_CX; } iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); /* Set the pkt timer */ cmd_sbp->ticks = hba->timer_tics + pkt->pkt_timeout + ((pkt->pkt_timeout > 0xff) ? 0 : 10); return (IOERR_SUCCESS); } else if (iocb->ULPCOMMAND == CMD_FCP_TRSP64_CX) { ndlp = cmd_sbp->node; cp->ulpSendCmd++; /* Initalize iocbq */ iocbq->port = (void *)port; iocbq->node = (void *)ndlp; iocbq->channel = (void *)cp; #if (EMLXS_MODREV >= EMLXS_MODREV3) cp_cmd = pkt->pkt_cmd_cookie; #else cp_cmd = &pkt->pkt_cmd_cookie; #endif /* >= EMLXS_MODREV3 */ iocb->un.fcpt64.bdl.addrHigh = PADDR_HI(cp_cmd->dmac_laddress); iocb->un.fcpt64.bdl.addrLow = PADDR_LO(cp_cmd->dmac_laddress); iocb->un.fcpt64.bdl.bdeSize = pkt->pkt_cmdlen; iocb->un.fcpt64.bdl.bdeFlags = 0; if (hba->sli_mode < 3) { iocb->ULPBDECOUNT = 1; iocb->ULPLE = 1; } else { /* SLI3 */ iocb->ULPBDECOUNT = 0; iocb->ULPLE = 0; iocb->unsli3.ext_iocb.ebde_count = 0; } /* Initalize iocb */ iocb->ULPCONTEXT = (uint16_t)pkt->pkt_cmd_fhdr.rx_id; iocb->ULPIOTAG = (uint16_t)iotag; iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); iocb->ULPOWNER = OWN_CHIP; iocb->ULPCLASS = cmd_sbp->class; iocb->ULPCOMMAND = CMD_FCP_TRSP64_CX; /* Set the pkt timer */ cmd_sbp->ticks = hba->timer_tics + pkt->pkt_timeout + ((pkt->pkt_timeout > 0xff) ? 0 : 10); if (pkt->pkt_cmdlen) { EMLXS_MPDATA_SYNC(pkt->pkt_cmd_dma, 0, pkt->pkt_cmdlen, DDI_DMA_SYNC_FORDEV); } return (IOERR_SUCCESS); } dbuf = cmd_sbp->fct_buf; fct_cmd = cmd_sbp->fct_cmd; fct_task = (scsi_task_t *)fct_cmd->cmd_specific; ndlp = *(emlxs_node_t **)fct_cmd->cmd_rp->rp_fca_private; did = fct_cmd->cmd_rportid; iocbq->channel = (void *)cmd_sbp->channel; if (emlxs_fct_bde_setup(port, cmd_sbp)) { /* Unregister the packet */ (void) emlxs_unregister_pkt(cmd_sbp->channel, iotag, 0); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to setup buffer list. did=%x", did); return (IOERR_INTERNAL_ERROR); } if (cfg[CFG_TIMEOUT_ENABLE].current) { timeout = ((2 * hba->fc_ratov) < 60) ? 60 : (2 * hba->fc_ratov); } else { timeout = 0x80000000; } cmd_sbp->ticks = hba->timer_tics + timeout + ((timeout > 0xff) ? 0 : 10); /* Initalize iocbq */ iocbq->port = (void *)port; iocbq->node = (void *)ndlp; /* Initalize iocb */ iocb->ULPCONTEXT = (uint16_t)fct_cmd->cmd_rxid; iocb->ULPIOTAG = (uint16_t)iotag; iocb->ULPRSVDBYTE = ((timeout > 0xff) ? 0 : timeout); iocb->ULPOWNER = OWN_CHIP; iocb->ULPCLASS = cmd_sbp->class; iocb->ULPPU = 1; /* Wd4 is relative offset */ iocb->un.fcpt64.fcpt_Offset = dbuf->db_relative_offset; if (fct_task->task_flags & TF_WRITE_DATA) { iocb->ULPCOMMAND = CMD_FCP_TRECEIVE64_CX; } else { /* TF_READ_DATA */ iocb->ULPCOMMAND = CMD_FCP_TSEND64_CX; if ((hba->sli_mode == EMLXS_HBA_SLI3_MODE) && (dbuf->db_data_size >= fct_task->task_expected_xfer_length)) { iocb->ULPCT = 0x1; /* enable auto-rsp AP feature */ } } return (IOERR_SUCCESS); } /* emlxs_sli3_prep_fct_iocb() */ #endif /* SFCT_SUPPORT */ /* ARGSUSED */ static uint32_t emlxs_sli3_prep_fcp_iocb(emlxs_port_t *port, emlxs_buf_t *sbp, int channel) { emlxs_hba_t *hba = HBA; fc_packet_t *pkt; CHANNEL *cp; IOCBQ *iocbq; IOCB *iocb; NODELIST *ndlp; uint16_t iotag; uint32_t did; pkt = PRIV2PKT(sbp); did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id); cp = &hba->chan[FC_FCP_RING]; iocbq = &sbp->iocbq; iocb = &iocbq->iocb; /* Find target node object */ ndlp = (NODELIST *)iocbq->node; /* Get the iotag by registering the packet */ iotag = emlxs_register_pkt(cp, sbp); if (!iotag) { /* * No more command slots available, retry later */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to allocate iotag: did=0x%x", did); return (FC_TRAN_BUSY); } /* Initalize iocbq */ iocbq->port = (void *) port; iocbq->channel = (void *) cp; /* Indicate this is a FCP cmd */ iocbq->flag |= IOCB_FCP_CMD; if (emlxs_bde_setup(port, sbp)) { /* Unregister the packet */ (void) emlxs_unregister_pkt(cp, iotag, 0); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to setup buffer list. did=%x", did); return (FC_TRAN_BUSY); } /* Point of no return */ /* Initalize iocb */ iocb->ULPCONTEXT = ndlp->nlp_Rpi; iocb->ULPIOTAG = iotag; iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); iocb->ULPOWNER = OWN_CHIP; switch (FC_TRAN_CLASS(pkt->pkt_tran_flags)) { case FC_TRAN_CLASS1: iocb->ULPCLASS = CLASS1; break; case FC_TRAN_CLASS2: iocb->ULPCLASS = CLASS2; /* iocb->ULPCLASS = CLASS3; */ break; case FC_TRAN_CLASS3: default: iocb->ULPCLASS = CLASS3; break; } /* if device is FCP-2 device, set the following bit */ /* that says to run the FC-TAPE protocol. */ if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { iocb->ULPFCP2RCVY = 1; } if (pkt->pkt_datalen == 0) { iocb->ULPCOMMAND = CMD_FCP_ICMND64_CR; } else if (pkt->pkt_tran_type == FC_PKT_FCP_READ) { iocb->ULPCOMMAND = CMD_FCP_IREAD64_CR; iocb->ULPPU = PARM_XFER_CHECK; iocb->un.fcpi64.fcpi_parm = pkt->pkt_datalen; } else { iocb->ULPCOMMAND = CMD_FCP_IWRITE64_CR; } return (FC_SUCCESS); } /* emlxs_sli3_prep_fcp_iocb() */ static uint32_t emlxs_sli3_prep_ip_iocb(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; fc_packet_t *pkt; IOCBQ *iocbq; IOCB *iocb; CHANNEL *cp; NODELIST *ndlp; uint16_t iotag; uint32_t did; pkt = PRIV2PKT(sbp); cp = &hba->chan[FC_IP_RING]; did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id); iocbq = &sbp->iocbq; iocb = &iocbq->iocb; ndlp = (NODELIST *)iocbq->node; /* Get the iotag by registering the packet */ iotag = emlxs_register_pkt(cp, sbp); if (!iotag) { /* * No more command slots available, retry later */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to allocate iotag: did=0x%x", did); return (FC_TRAN_BUSY); } /* Initalize iocbq */ iocbq->port = (void *) port; iocbq->channel = (void *) cp; if (emlxs_bde_setup(port, sbp)) { /* Unregister the packet */ (void) emlxs_unregister_pkt(cp, iotag, 0); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to setup buffer list. did=%x", did); return (FC_TRAN_BUSY); } /* Point of no return */ /* Initalize iocb */ iocb->un.xseq64.w5.hcsw.Fctl = 0; if (pkt->pkt_cmd_fhdr.f_ctl & F_CTL_FIRST_SEQ) { iocb->un.xseq64.w5.hcsw.Fctl |= FSEQ; } if (pkt->pkt_cmd_fhdr.f_ctl & F_CTL_SEQ_INITIATIVE) { iocb->un.xseq64.w5.hcsw.Fctl |= SI; } /* network headers */ iocb->un.xseq64.w5.hcsw.Dfctl = pkt->pkt_cmd_fhdr.df_ctl; iocb->un.xseq64.w5.hcsw.Rctl = pkt->pkt_cmd_fhdr.r_ctl; iocb->un.xseq64.w5.hcsw.Type = pkt->pkt_cmd_fhdr.type; iocb->ULPIOTAG = iotag; iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); iocb->ULPOWNER = OWN_CHIP; if (pkt->pkt_tran_type == FC_PKT_BROADCAST) { HBASTATS.IpBcastIssued++; iocb->ULPCOMMAND = CMD_XMIT_BCAST64_CN; iocb->ULPCONTEXT = 0; if (hba->sli_mode == EMLXS_HBA_SLI3_MODE) { if (hba->topology != TOPOLOGY_LOOP) { iocb->ULPCT = 0x1; } iocb->ULPCONTEXT = port->vpi; } } else { HBASTATS.IpSeqIssued++; iocb->ULPCOMMAND = CMD_XMIT_SEQUENCE64_CX; iocb->ULPCONTEXT = ndlp->nlp_Xri; } switch (FC_TRAN_CLASS(pkt->pkt_tran_flags)) { case FC_TRAN_CLASS1: iocb->ULPCLASS = CLASS1; break; case FC_TRAN_CLASS2: iocb->ULPCLASS = CLASS2; break; case FC_TRAN_CLASS3: default: iocb->ULPCLASS = CLASS3; break; } return (FC_SUCCESS); } /* emlxs_sli3_prep_ip_iocb() */ static uint32_t emlxs_sli3_prep_els_iocb(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; fc_packet_t *pkt; IOCBQ *iocbq; IOCB *iocb; CHANNEL *cp; uint16_t iotag; uint32_t did; uint32_t cmd; pkt = PRIV2PKT(sbp); cp = &hba->chan[FC_ELS_RING]; did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id); iocbq = &sbp->iocbq; iocb = &iocbq->iocb; /* Get the iotag by registering the packet */ iotag = emlxs_register_pkt(cp, sbp); if (!iotag) { /* * No more command slots available, retry later */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to allocate iotag. did=0x%x", did); return (FC_TRAN_BUSY); } /* Initalize iocbq */ iocbq->port = (void *) port; iocbq->channel = (void *) cp; if (emlxs_bde_setup(port, sbp)) { /* Unregister the packet */ (void) emlxs_unregister_pkt(cp, iotag, 0); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to setup buffer list. did=%x", did); return (FC_TRAN_BUSY); } /* Point of no return */ /* Initalize iocb */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { /* ELS Response */ iocb->ULPCONTEXT = (volatile uint16_t) pkt->pkt_cmd_fhdr.rx_id; iocb->ULPCOMMAND = CMD_XMIT_ELS_RSP64_CX; } else { /* ELS Request */ iocb->un.elsreq64.remoteID = (did == BCAST_DID) ? 0 : did; iocb->ULPCONTEXT = (did == BCAST_DID) ? pkt->pkt_cmd_fhdr.seq_id : 0; iocb->ULPCOMMAND = CMD_ELS_REQUEST64_CR; if (hba->sli_mode == EMLXS_HBA_SLI3_MODE) { if (hba->topology != TOPOLOGY_LOOP) { cmd = *((uint32_t *)pkt->pkt_cmd); cmd &= ELS_CMD_MASK; if ((cmd == ELS_CMD_FLOGI) || (cmd == ELS_CMD_FDISC)) { iocb->ULPCT = 0x2; } else { iocb->ULPCT = 0x1; } } iocb->ULPCONTEXT = port->vpi; } } iocb->ULPIOTAG = iotag; iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); iocb->ULPOWNER = OWN_CHIP; switch (FC_TRAN_CLASS(pkt->pkt_tran_flags)) { case FC_TRAN_CLASS1: iocb->ULPCLASS = CLASS1; break; case FC_TRAN_CLASS2: iocb->ULPCLASS = CLASS2; break; case FC_TRAN_CLASS3: default: iocb->ULPCLASS = CLASS3; break; } sbp->class = iocb->ULPCLASS; return (FC_SUCCESS); } /* emlxs_sli3_prep_els_iocb() */ static uint32_t emlxs_sli3_prep_ct_iocb(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; fc_packet_t *pkt; IOCBQ *iocbq; IOCB *iocb; CHANNEL *cp; NODELIST *ndlp; uint16_t iotag; uint32_t did; pkt = PRIV2PKT(sbp); did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id); cp = &hba->chan[FC_CT_RING]; iocbq = &sbp->iocbq; iocb = &iocbq->iocb; ndlp = (NODELIST *)iocbq->node; /* Get the iotag by registering the packet */ iotag = emlxs_register_pkt(cp, sbp); if (!iotag) { /* * No more command slots available, retry later */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to allocate iotag. did=0x%x", did); return (FC_TRAN_BUSY); } if (emlxs_bde_setup(port, sbp)) { /* Unregister the packet */ (void) emlxs_unregister_pkt(cp, iotag, 0); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_trans_msg, "Adapter Busy. Unable to setup buffer list. did=%x", did); return (FC_TRAN_BUSY); } /* Point of no return */ /* Initalize iocbq */ iocbq->port = (void *) port; iocbq->channel = (void *) cp; /* Fill in rest of iocb */ iocb->un.genreq64.w5.hcsw.Fctl = LA; if (pkt->pkt_cmd_fhdr.f_ctl & F_CTL_LAST_SEQ) { iocb->un.genreq64.w5.hcsw.Fctl |= LSEQ; } if (pkt->pkt_cmd_fhdr.f_ctl & F_CTL_SEQ_INITIATIVE) { iocb->un.genreq64.w5.hcsw.Fctl |= SI; } /* Initalize iocb */ if (pkt->pkt_tran_type == FC_PKT_OUTBOUND) { /* CT Response */ iocb->ULPCOMMAND = CMD_XMIT_SEQUENCE64_CX; iocb->un.genreq64.w5.hcsw.Dfctl = pkt->pkt_cmd_fhdr.df_ctl; iocb->ULPCONTEXT = pkt->pkt_cmd_fhdr.rx_id; } else { /* CT Request */ iocb->ULPCOMMAND = CMD_GEN_REQUEST64_CR; iocb->un.genreq64.w5.hcsw.Dfctl = 0; iocb->ULPCONTEXT = ndlp->nlp_Rpi; } iocb->un.genreq64.w5.hcsw.Rctl = pkt->pkt_cmd_fhdr.r_ctl; iocb->un.genreq64.w5.hcsw.Type = pkt->pkt_cmd_fhdr.type; iocb->ULPIOTAG = iotag; iocb->ULPRSVDBYTE = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout); iocb->ULPOWNER = OWN_CHIP; switch (FC_TRAN_CLASS(pkt->pkt_tran_flags)) { case FC_TRAN_CLASS1: iocb->ULPCLASS = CLASS1; break; case FC_TRAN_CLASS2: iocb->ULPCLASS = CLASS2; break; case FC_TRAN_CLASS3: default: iocb->ULPCLASS = CLASS3; break; } return (FC_SUCCESS); } /* emlxs_sli3_prep_ct_iocb() */ #ifdef SFCT_SUPPORT static uint32_t emlxs_fct_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { emlxs_hba_t *hba = HBA; uint32_t rval; if (sbp->fct_buf->db_sglist_length != 1) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fct_error_msg, "fct_bde_setup: Only 1 sglist entry supported: %d", sbp->fct_buf->db_sglist_length); return (1); } if (hba->sli_mode < EMLXS_HBA_SLI3_MODE) { rval = emlxs_sli2_fct_bde_setup(port, sbp); } else { rval = emlxs_sli3_fct_bde_setup(port, sbp); } return (rval); } /* emlxs_fct_bde_setup() */ #endif /* SFCT_SUPPORT */ static uint32_t emlxs_bde_setup(emlxs_port_t *port, emlxs_buf_t *sbp) { uint32_t rval; emlxs_hba_t *hba = HBA; if (hba->sli_mode < EMLXS_HBA_SLI3_MODE) { rval = emlxs_sli2_bde_setup(port, sbp); } else { rval = emlxs_sli3_bde_setup(port, sbp); } return (rval); } /* emlxs_bde_setup() */ static void emlxs_sli3_poll_intr(emlxs_hba_t *hba) { uint32_t ha_copy; /* Check attention bits once and process if required */ ha_copy = emlxs_check_attention(hba); if (ha_copy == 0) { return; } mutex_enter(&EMLXS_PORT_LOCK); ha_copy = emlxs_get_attention(hba, -1); mutex_exit(&EMLXS_PORT_LOCK); emlxs_proc_attention(hba, ha_copy); return; } /* emlxs_sli3_poll_intr() */ #ifdef MSI_SUPPORT static uint32_t emlxs_sli3_msi_intr(char *arg1, char *arg2) { emlxs_hba_t *hba = (emlxs_hba_t *)arg1; #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ uint16_t msgid; uint32_t hc_copy; uint32_t ha_copy; uint32_t restore = 0; /* * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, * "sli3_msi_intr: arg1=%p arg2=%p", arg1, arg2); */ /* Check for legacy interrupt handling */ if (hba->intr_type == DDI_INTR_TYPE_FIXED) { mutex_enter(&EMLXS_PORT_LOCK); if (hba->flag & FC_OFFLINE_MODE) { mutex_exit(&EMLXS_PORT_LOCK); if (hba->bus_type == SBUS_FC) { return (DDI_INTR_CLAIMED); } else { return (DDI_INTR_UNCLAIMED); } } /* Get host attention bits */ ha_copy = emlxs_get_attention(hba, -1); if (ha_copy == 0) { if (hba->intr_unclaimed) { mutex_exit(&EMLXS_PORT_LOCK); return (DDI_INTR_UNCLAIMED); } hba->intr_unclaimed = 1; } else { hba->intr_unclaimed = 0; } mutex_exit(&EMLXS_PORT_LOCK); /* Process the interrupt */ emlxs_proc_attention(hba, ha_copy); return (DDI_INTR_CLAIMED); } /* DDI_INTR_TYPE_MSI */ /* DDI_INTR_TYPE_MSIX */ /* Get MSI message id */ msgid = (uint16_t)((unsigned long)arg2); /* Validate the message id */ if (msgid >= hba->intr_count) { msgid = 0; } mutex_enter(&EMLXS_INTR_LOCK(msgid)); mutex_enter(&EMLXS_PORT_LOCK); /* Check if adapter is offline */ if (hba->flag & FC_OFFLINE_MODE) { mutex_exit(&EMLXS_PORT_LOCK); mutex_exit(&EMLXS_INTR_LOCK(msgid)); /* Always claim an MSI interrupt */ return (DDI_INTR_CLAIMED); } /* Disable interrupts associated with this msgid */ if (msgid == 0 && (hba->model_info.chip == EMLXS_ZEPHYR_CHIP)) { hc_copy = hba->sli.sli3.hc_copy & ~hba->intr_mask; WRITE_CSR_REG(hba, FC_HC_REG(hba), hc_copy); restore = 1; } /* Get host attention bits */ ha_copy = emlxs_get_attention(hba, msgid); mutex_exit(&EMLXS_PORT_LOCK); /* Process the interrupt */ emlxs_proc_attention(hba, ha_copy); /* Restore interrupts */ if (restore) { mutex_enter(&EMLXS_PORT_LOCK); WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_PORT_LOCK); } mutex_exit(&EMLXS_INTR_LOCK(msgid)); return (DDI_INTR_CLAIMED); } /* emlxs_sli3_msi_intr() */ #endif /* MSI_SUPPORT */ static int emlxs_sli3_intx_intr(char *arg) { emlxs_hba_t *hba = (emlxs_hba_t *)arg; uint32_t ha_copy = 0; mutex_enter(&EMLXS_PORT_LOCK); if (hba->flag & FC_OFFLINE_MODE) { mutex_exit(&EMLXS_PORT_LOCK); if (hba->bus_type == SBUS_FC) { return (DDI_INTR_CLAIMED); } else { return (DDI_INTR_UNCLAIMED); } } /* Get host attention bits */ ha_copy = emlxs_get_attention(hba, -1); if (ha_copy == 0) { if (hba->intr_unclaimed) { mutex_exit(&EMLXS_PORT_LOCK); return (DDI_INTR_UNCLAIMED); } hba->intr_unclaimed = 1; } else { hba->intr_unclaimed = 0; } mutex_exit(&EMLXS_PORT_LOCK); /* Process the interrupt */ emlxs_proc_attention(hba, ha_copy); return (DDI_INTR_CLAIMED); } /* emlxs_sli3_intx_intr() */ /* EMLXS_PORT_LOCK must be held when call this routine */ static uint32_t emlxs_get_attention(emlxs_hba_t *hba, int32_t msgid) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ uint32_t ha_copy = 0; uint32_t ha_copy2; uint32_t mask = hba->sli.sli3.hc_copy; #ifdef MSI_SUPPORT read_ha_register: /* Check for default MSI interrupt */ if (msgid == 0) { /* Read host attention register to determine interrupt source */ ha_copy2 = READ_CSR_REG(hba, FC_HA_REG(hba)); /* Filter out MSI non-default attention bits */ ha_copy2 &= ~(hba->intr_cond); } /* Check for polled or fixed type interrupt */ else if (msgid == -1) { /* Read host attention register to determine interrupt source */ ha_copy2 = READ_CSR_REG(hba, FC_HA_REG(hba)); } /* Otherwise, assume a mapped MSI interrupt */ else { /* Convert MSI msgid to mapped attention bits */ ha_copy2 = hba->intr_map[msgid]; } #else /* !MSI_SUPPORT */ /* Read host attention register to determine interrupt source */ ha_copy2 = READ_CSR_REG(hba, FC_HA_REG(hba)); #endif /* MSI_SUPPORT */ /* Check if Hardware error interrupt is enabled */ if ((ha_copy2 & HA_ERATT) && !(mask & HC_ERINT_ENA)) { ha_copy2 &= ~HA_ERATT; } /* Check if link interrupt is enabled */ if ((ha_copy2 & HA_LATT) && !(mask & HC_LAINT_ENA)) { ha_copy2 &= ~HA_LATT; } /* Check if Mailbox interrupt is enabled */ if ((ha_copy2 & HA_MBATT) && !(mask & HC_MBINT_ENA)) { ha_copy2 &= ~HA_MBATT; } /* Check if ring0 interrupt is enabled */ if ((ha_copy2 & HA_R0ATT) && !(mask & HC_R0INT_ENA)) { ha_copy2 &= ~HA_R0ATT; } /* Check if ring1 interrupt is enabled */ if ((ha_copy2 & HA_R1ATT) && !(mask & HC_R1INT_ENA)) { ha_copy2 &= ~HA_R1ATT; } /* Check if ring2 interrupt is enabled */ if ((ha_copy2 & HA_R2ATT) && !(mask & HC_R2INT_ENA)) { ha_copy2 &= ~HA_R2ATT; } /* Check if ring3 interrupt is enabled */ if ((ha_copy2 & HA_R3ATT) && !(mask & HC_R3INT_ENA)) { ha_copy2 &= ~HA_R3ATT; } /* Accumulate attention bits */ ha_copy |= ha_copy2; /* Clear attentions except for error, link, and autoclear(MSIX) */ ha_copy2 &= ~(HA_ERATT | HA_LATT); /* | hba->intr_autoClear */ if (ha_copy2) { WRITE_CSR_REG(hba, FC_HA_REG(hba), ha_copy2); } #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ return (ha_copy); } /* emlxs_get_attention() */ static void emlxs_proc_attention(emlxs_hba_t *hba, uint32_t ha_copy) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ /* ha_copy should be pre-filtered */ /* * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, * "proc_attention: ha_copy=%x", ha_copy); */ if (hba->state < FC_WARM_START) { return; } if (!ha_copy) { return; } if (hba->bus_type == SBUS_FC) { (void) READ_SBUS_CSR_REG(hba, FC_SHS_REG(hba)); } /* Adapter error */ if (ha_copy & HA_ERATT) { HBASTATS.IntrEvent[6]++; emlxs_handle_ff_error(hba); return; } /* Mailbox interrupt */ if (ha_copy & HA_MBATT) { HBASTATS.IntrEvent[5]++; (void) emlxs_handle_mb_event(hba); } /* Link Attention interrupt */ if (ha_copy & HA_LATT) { HBASTATS.IntrEvent[4]++; emlxs_sli3_handle_link_event(hba); } /* event on ring 0 - FCP Ring */ if (ha_copy & HA_R0ATT) { HBASTATS.IntrEvent[0]++; emlxs_sli3_handle_ring_event(hba, 0, ha_copy); } /* event on ring 1 - IP Ring */ if (ha_copy & HA_R1ATT) { HBASTATS.IntrEvent[1]++; emlxs_sli3_handle_ring_event(hba, 1, ha_copy); } /* event on ring 2 - ELS Ring */ if (ha_copy & HA_R2ATT) { HBASTATS.IntrEvent[2]++; emlxs_sli3_handle_ring_event(hba, 2, ha_copy); } /* event on ring 3 - CT Ring */ if (ha_copy & HA_R3ATT) { HBASTATS.IntrEvent[3]++; emlxs_sli3_handle_ring_event(hba, 3, ha_copy); } if (hba->bus_type == SBUS_FC) { WRITE_SBUS_CSR_REG(hba, FC_SHS_REG(hba), SBUS_STAT_IP); } /* Set heartbeat flag to show activity */ hba->heartbeat_flag = 1; #ifdef FMA_SUPPORT if (hba->bus_type == SBUS_FC) { /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.sbus_csr_handle); } #endif /* FMA_SUPPORT */ return; } /* emlxs_proc_attention() */ /* * emlxs_handle_ff_error() * * Description: Processes a FireFly error * Runs at Interrupt level */ static void emlxs_handle_ff_error(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; uint32_t status; uint32_t status1; uint32_t status2; int i = 0; /* do what needs to be done, get error from STATUS REGISTER */ status = READ_CSR_REG(hba, FC_HS_REG(hba)); /* Clear Chip error bit */ WRITE_CSR_REG(hba, FC_HA_REG(hba), HA_ERATT); /* If HS_FFER1 is set, then wait until the HS_FFER1 bit clears */ if (status & HS_FFER1) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_hardware_error_msg, "HS_FFER1 received"); EMLXS_STATE_CHANGE(hba, FC_ERROR); (void) emlxs_offline(hba, 1); while ((status & HS_FFER1) && (i < 300)) { status = READ_CSR_REG(hba, FC_HS_REG(hba)); BUSYWAIT_MS(1000); i++; } } if (i == 300) { /* 5 minutes is up, shutdown HBA */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_hardware_error_msg, "HS_FFER1 clear timeout"); EMLXS_STATE_CHANGE(hba, FC_ERROR); emlxs_thread_spawn(hba, emlxs_shutdown_thread, NULL, NULL); goto done; } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_hardware_error_msg, "HS_FFER1 cleared"); if (status & HS_OVERTEMP) { status1 = READ_SLIM_ADDR(hba, ((volatile uint8_t *)hba->sli.sli3.slim_addr + 0xb0)); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_hardware_error_msg, "Maximum adapter temperature exceeded (%d °C).", status1); hba->temperature = status1; hba->flag |= FC_OVERTEMP_EVENT; EMLXS_STATE_CHANGE(hba, FC_ERROR); emlxs_thread_spawn(hba, emlxs_shutdown_thread, NULL, NULL); } else { status1 = READ_SLIM_ADDR(hba, ((volatile uint8_t *)hba->sli.sli3.slim_addr + 0xa8)); status2 = READ_SLIM_ADDR(hba, ((volatile uint8_t *)hba->sli.sli3.slim_addr + 0xac)); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_hardware_error_msg, "Host Error Attention: " "status=0x%x status1=0x%x status2=0x%x", status, status1, status2); EMLXS_STATE_CHANGE(hba, FC_ERROR); if (status & HS_FFER6) { emlxs_thread_spawn(hba, emlxs_restart_thread, NULL, NULL); } else { emlxs_thread_spawn(hba, emlxs_shutdown_thread, NULL, NULL); } } done: #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ return; } /* emlxs_handle_ff_error() */ /* * emlxs_sli3_handle_link_event() * * Description: Process a Link Attention. */ static void emlxs_sli3_handle_link_event(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; MAILBOXQ *mbq; int rc; HBASTATS.LinkEvent++; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_event_msg, "event=%x", HBASTATS.LinkEvent); /* Make sure link is declared down */ emlxs_linkdown(hba); /* Get a buffer which will be used for mailbox commands */ if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))) { /* Get link attention message */ if (emlxs_mb_read_la(hba, mbq) == 0) { rc = emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_NOWAIT, 0); if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) { emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); } mutex_enter(&EMLXS_PORT_LOCK); /* * Clear Link Attention in HA REG */ WRITE_CSR_REG(hba, FC_HA_REG(hba), HA_LATT); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_PORT_LOCK); } else { emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); } } } /* emlxs_sli3_handle_link_event() */ /* * emlxs_sli3_handle_ring_event() * * Description: Process a Ring Attention. */ static void emlxs_sli3_handle_ring_event(emlxs_hba_t *hba, int32_t ring_no, uint32_t ha_copy) { emlxs_port_t *port = &PPORT; SLIM2 *slim2p = (SLIM2 *)hba->sli.sli3.slim2.virt; CHANNEL *cp; RING *rp; IOCB *entry; IOCBQ *iocbq; IOCBQ local_iocbq; PGP *pgp; uint32_t count; volatile uint32_t chipatt; void *ioa2; uint32_t reg; uint32_t channel_no; off_t offset; IOCBQ *rsp_head = NULL; IOCBQ *rsp_tail = NULL; emlxs_buf_t *sbp = NULL; count = 0; rp = &hba->sli.sli3.ring[ring_no]; cp = rp->channelp; channel_no = cp->channelno; /* * Isolate this ring's host attention bits * This makes all ring attention bits equal * to Ring0 attention bits */ reg = (ha_copy >> (ring_no * 4)) & 0x0f; /* * Gather iocb entries off response ring. * Ensure entry is owned by the host. */ pgp = (PGP *)&slim2p->mbx.us.s2.port[ring_no]; offset = (off_t)((uint64_t)((unsigned long)&(pgp->rspPutInx)) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORKERNEL); rp->fc_port_rspidx = BE_SWAP32(pgp->rspPutInx); /* While ring is not empty */ while (rp->fc_rspidx != rp->fc_port_rspidx) { HBASTATS.IocbReceived[channel_no]++; /* Get the next response ring iocb */ entry = (IOCB *)(((char *)rp->fc_rspringaddr + (rp->fc_rspidx * hba->sli.sli3.iocb_rsp_size))); /* DMA sync the response ring iocb for the adapter */ offset = (off_t)((uint64_t)((unsigned long)entry) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, hba->sli.sli3.iocb_rsp_size, DDI_DMA_SYNC_FORKERNEL); count++; /* Copy word6 and word7 to local iocb for now */ iocbq = &local_iocbq; BE_SWAP32_BCOPY((uint8_t *)entry + (sizeof (uint32_t) * 6), (uint8_t *)iocbq + (sizeof (uint32_t) * 6), (sizeof (uint32_t) * 2)); /* when LE is not set, entire Command has not been received */ if (!iocbq->iocb.ULPLE) { /* This should never happen */ EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_ring_error_msg, "ulpLE is not set. " "ring=%d iotag=%d cmd=%x status=%x", channel_no, iocbq->iocb.ULPIOTAG, iocbq->iocb.ULPCOMMAND, iocbq->iocb.ULPSTATUS); goto next; } sbp = NULL; switch (iocbq->iocb.ULPCOMMAND) { #ifdef SFCT_SUPPORT case CMD_CLOSE_XRI_CX: case CMD_CLOSE_XRI_CN: case CMD_ABORT_XRI_CX: if (port->mode == MODE_TARGET) { sbp = emlxs_unregister_pkt(cp, iocbq->iocb.ULPIOTAG, 0); } break; #endif /* SFCT_SUPPORT */ /* Ring 0 registered commands */ case CMD_FCP_ICMND_CR: case CMD_FCP_ICMND_CX: case CMD_FCP_IREAD_CR: case CMD_FCP_IREAD_CX: case CMD_FCP_IWRITE_CR: case CMD_FCP_IWRITE_CX: case CMD_FCP_ICMND64_CR: case CMD_FCP_ICMND64_CX: case CMD_FCP_IREAD64_CR: case CMD_FCP_IREAD64_CX: case CMD_FCP_IWRITE64_CR: case CMD_FCP_IWRITE64_CX: #ifdef SFCT_SUPPORT case CMD_FCP_TSEND_CX: case CMD_FCP_TSEND64_CX: case CMD_FCP_TRECEIVE_CX: case CMD_FCP_TRECEIVE64_CX: case CMD_FCP_TRSP_CX: case CMD_FCP_TRSP64_CX: #endif /* SFCT_SUPPORT */ /* Ring 1 registered commands */ case CMD_XMIT_BCAST_CN: case CMD_XMIT_BCAST_CX: case CMD_XMIT_SEQUENCE_CX: case CMD_XMIT_SEQUENCE_CR: case CMD_XMIT_BCAST64_CN: case CMD_XMIT_BCAST64_CX: case CMD_XMIT_SEQUENCE64_CX: case CMD_XMIT_SEQUENCE64_CR: case CMD_CREATE_XRI_CR: case CMD_CREATE_XRI_CX: /* Ring 2 registered commands */ case CMD_ELS_REQUEST_CR: case CMD_ELS_REQUEST_CX: case CMD_XMIT_ELS_RSP_CX: case CMD_ELS_REQUEST64_CR: case CMD_ELS_REQUEST64_CX: case CMD_XMIT_ELS_RSP64_CX: /* Ring 3 registered commands */ case CMD_GEN_REQUEST64_CR: case CMD_GEN_REQUEST64_CX: sbp = emlxs_unregister_pkt(cp, iocbq->iocb.ULPIOTAG, 0); break; } /* If packet is stale, then drop it. */ if (sbp == STALE_PACKET) { cp->hbaCmplCmd_sbp++; /* Copy entry to the local iocbq */ BE_SWAP32_BCOPY((uint8_t *)entry, (uint8_t *)iocbq, hba->sli.sli3.iocb_rsp_size); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_iocb_stale_msg, "channelno=%d iocb=%p cmd=%x status=%x " "error=%x iotag=%d context=%x info=%x", channel_no, iocbq, (uint8_t)iocbq->iocb.ULPCOMMAND, iocbq->iocb.ULPSTATUS, (uint8_t)iocbq->iocb.un.grsp.perr.statLocalError, (uint16_t)iocbq->iocb.ULPIOTAG, (uint16_t)iocbq->iocb.ULPCONTEXT, (uint8_t)iocbq->iocb.ULPRSVDBYTE); goto next; } /* * If a packet was found, then queue the packet's * iocb for deferred processing */ else if (sbp) { #ifdef SFCT_SUPPORT fct_cmd_t *fct_cmd; emlxs_buf_t *cmd_sbp; fct_cmd = sbp->fct_cmd; if (fct_cmd) { cmd_sbp = (emlxs_buf_t *)fct_cmd->cmd_fca_private; mutex_enter(&cmd_sbp->fct_mtx); EMLXS_FCT_STATE_CHG(fct_cmd, cmd_sbp, EMLXS_FCT_IOCB_COMPLETE); mutex_exit(&cmd_sbp->fct_mtx); } #endif /* SFCT_SUPPORT */ cp->hbaCmplCmd_sbp++; atomic_dec_32(&hba->io_active); #ifdef NODE_THROTTLE_SUPPORT if (sbp->node) { atomic_dec_32(&sbp->node->io_active); } #endif /* NODE_THROTTLE_SUPPORT */ /* Copy entry to sbp's iocbq */ iocbq = &sbp->iocbq; BE_SWAP32_BCOPY((uint8_t *)entry, (uint8_t *)iocbq, hba->sli.sli3.iocb_rsp_size); iocbq->next = NULL; /* * If this is NOT a polled command completion * or a driver allocated pkt, then defer pkt * completion. */ if (!(sbp->pkt_flags & (PACKET_POLLED | PACKET_ALLOCATED))) { /* Add the IOCB to the local list */ if (!rsp_head) { rsp_head = iocbq; } else { rsp_tail->next = iocbq; } rsp_tail = iocbq; goto next; } } else { cp->hbaCmplCmd++; /* Copy entry to the local iocbq */ BE_SWAP32_BCOPY((uint8_t *)entry, (uint8_t *)iocbq, hba->sli.sli3.iocb_rsp_size); iocbq->next = NULL; iocbq->bp = NULL; iocbq->port = &PPORT; iocbq->channel = cp; iocbq->node = NULL; iocbq->sbp = NULL; iocbq->flag = 0; } /* process the channel event now */ emlxs_proc_channel_event(hba, cp, iocbq); next: /* Increment the driver's local response get index */ if (++rp->fc_rspidx >= rp->fc_numRiocb) { rp->fc_rspidx = 0; } } /* while (TRUE) */ if (rsp_head) { mutex_enter(&cp->rsp_lock); if (cp->rsp_head == NULL) { cp->rsp_head = rsp_head; cp->rsp_tail = rsp_tail; } else { cp->rsp_tail->next = rsp_head; cp->rsp_tail = rsp_tail; } mutex_exit(&cp->rsp_lock); emlxs_thread_trigger2(&cp->intr_thread, emlxs_proc_channel, cp); } /* Check if at least one response entry was processed */ if (count) { /* Update response get index for the adapter */ if (hba->bus_type == SBUS_FC) { slim2p->mbx.us.s2.host[channel_no].rspGetInx = BE_SWAP32(rp->fc_rspidx); /* DMA sync the index for the adapter */ offset = (off_t) ((uint64_t)((unsigned long)&(slim2p->mbx.us.s2. host[channel_no].rspGetInx)) - (uint64_t)((unsigned long)slim2p)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, 4, DDI_DMA_SYNC_FORDEV); } else { ioa2 = (void *)((char *)hba->sli.sli3.slim_addr + hba->sli.sli3.hgp_ring_offset + (((channel_no * 2) + 1) * sizeof (uint32_t))); WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, rp->fc_rspidx); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); #endif /* FMA_SUPPORT */ } if (reg & HA_R0RE_REQ) { /* HBASTATS.chipRingFree++; */ mutex_enter(&EMLXS_PORT_LOCK); /* Tell the adapter we serviced the ring */ chipatt = ((CA_R0ATT | CA_R0RE_RSP) << (channel_no * 4)); WRITE_CSR_REG(hba, FC_CA_REG(hba), chipatt); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_PORT_LOCK); } } if ((reg & HA_R0CE_RSP) || hba->channel_tx_count) { /* HBASTATS.hostRingFree++; */ /* Cmd ring may be available. Try sending more iocbs */ emlxs_sli3_issue_iocb_cmd(hba, cp, 0); } /* HBASTATS.ringEvent++; */ return; } /* emlxs_sli3_handle_ring_event() */ extern int emlxs_handle_rcv_seq(emlxs_hba_t *hba, CHANNEL *cp, IOCBQ *iocbq) { emlxs_port_t *port = &PPORT; IOCB *iocb; RING *rp; MATCHMAP *mp = NULL; uint64_t bdeAddr; uint32_t vpi = 0; uint32_t channelno; uint32_t size = 0; uint32_t *RcvError; uint32_t *RcvDropped; uint32_t *UbPosted; emlxs_msg_t *dropped_msg; char error_str[64]; uint32_t buf_type; uint32_t *word; channelno = cp->channelno; rp = &hba->sli.sli3.ring[channelno]; iocb = &iocbq->iocb; word = (uint32_t *)iocb; switch (channelno) { #ifdef SFCT_SUPPORT case FC_FCT_RING: HBASTATS.FctRingEvent++; RcvError = &HBASTATS.FctRingError; RcvDropped = &HBASTATS.FctRingDropped; UbPosted = &HBASTATS.FctUbPosted; dropped_msg = &emlxs_fct_detail_msg; buf_type = MEM_FCTBUF; break; #endif /* SFCT_SUPPORT */ case FC_IP_RING: HBASTATS.IpRcvEvent++; RcvError = &HBASTATS.IpDropped; RcvDropped = &HBASTATS.IpDropped; UbPosted = &HBASTATS.IpUbPosted; dropped_msg = &emlxs_unsol_ip_dropped_msg; buf_type = MEM_IPBUF; break; case FC_ELS_RING: HBASTATS.ElsRcvEvent++; RcvError = &HBASTATS.ElsRcvError; RcvDropped = &HBASTATS.ElsRcvDropped; UbPosted = &HBASTATS.ElsUbPosted; dropped_msg = &emlxs_unsol_els_dropped_msg; buf_type = MEM_ELSBUF; break; case FC_CT_RING: HBASTATS.CtRcvEvent++; RcvError = &HBASTATS.CtRcvError; RcvDropped = &HBASTATS.CtRcvDropped; UbPosted = &HBASTATS.CtUbPosted; dropped_msg = &emlxs_unsol_ct_dropped_msg; buf_type = MEM_CTBUF; break; default: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_iocb_invalid_msg, "channel=%d cmd=%x %s %x %x %x %x", channelno, iocb->ULPCOMMAND, emlxs_state_xlate(iocb->ULPSTATUS), word[4], word[5], word[6], word[7]); return (1); } if (iocb->ULPSTATUS) { if ((iocb->ULPSTATUS == IOSTAT_LOCAL_REJECT) && (iocb->un.grsp.perr.statLocalError == IOERR_RCV_BUFFER_TIMEOUT)) { (void) strlcpy(error_str, "Out of posted buffers:", sizeof (error_str)); iocb->ULPBDECOUNT = 0; } else if ((iocb->ULPSTATUS == IOSTAT_LOCAL_REJECT) && (iocb->un.grsp.perr.statLocalError == IOERR_RCV_BUFFER_WAITING)) { (void) strlcpy(error_str, "Buffer waiting:", sizeof (error_str)); iocb->ULPBDECOUNT = 0; goto done; } else if (iocb->ULPSTATUS == IOSTAT_NEED_BUFF_ENTRY) { (void) strlcpy(error_str, "Need Buffer Entry:", sizeof (error_str)); iocb->ULPBDECOUNT = 0; goto done; } else { (void) strlcpy(error_str, "General error:", sizeof (error_str)); } goto failed; } if (hba->flag & FC_HBQ_ENABLED) { HBQ_INIT_t *hbq; HBQE_t *hbqE; uint32_t hbqe_tag; uint32_t hbq_id; (*UbPosted)--; hbqE = (HBQE_t *)iocb; hbq_id = hbqE->unt.ext.HBQ_tag; hbqe_tag = hbqE->unt.ext.HBQE_tag; hbq = &hba->sli.sli3.hbq_table[hbq_id]; if (hbqe_tag >= hbq->HBQ_numEntries) { (void) snprintf(error_str, sizeof (error_str), "Invalid HBQE iotag=%d:", hbqe_tag); goto dropped; } mp = hba->sli.sli3.hbq_table[hbq_id].HBQ_PostBufs[hbqe_tag]; size = iocb->unsli3.ext_rcv.seq_len; } else { bdeAddr = PADDR(iocb->un.cont64[0].addrHigh, iocb->un.cont64[0].addrLow); /* Check for invalid buffer */ if (iocb->un.cont64[0].tus.f.bdeFlags & BUFF_TYPE_INVALID) { (void) strlcpy(error_str, "Invalid buffer:", sizeof (error_str)); goto dropped; } mp = emlxs_mem_get_vaddr(hba, rp, bdeAddr); size = iocb->un.rcvseq64.rcvBde.tus.f.bdeSize; } if (!mp) { (void) strlcpy(error_str, "Buffer not mapped:", sizeof (error_str)); goto dropped; } #ifdef FMA_SUPPORT if (mp->dma_handle) { if (emlxs_fm_check_dma_handle(hba, mp->dma_handle) != DDI_FM_OK) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_dma_handle_msg, "handle_rcv_seq: hdl=%p", mp->dma_handle); goto dropped; } } #endif /* FMA_SUPPORT */ if (!size) { (void) strlcpy(error_str, "Buffer empty:", sizeof (error_str)); goto dropped; } /* To avoid we drop the broadcast packets */ if (channelno != FC_IP_RING) { /* Get virtual port */ if (hba->flag & FC_NPIV_ENABLED) { vpi = iocb->unsli3.ext_rcv.vpi; if (vpi >= hba->vpi_max) { (void) snprintf(error_str, sizeof (error_str), "Invalid VPI=%d:", vpi); goto dropped; } port = &VPORT(vpi); } } /* Process request */ switch (channelno) { case FC_FCT_RING: if (port->mode == MODE_INITIATOR) { (void) strlcpy(error_str, "Target mode disabled:", sizeof (error_str)); goto dropped; #ifdef SFCT_SUPPORT } else if (port->mode == MODE_TARGET) { (void) emlxs_fct_handle_unsol_req(port, cp, iocbq, mp, size); #endif /* SFCT_SUPPORT */ } else { (void) snprintf(error_str, sizeof (error_str), "Invalid mode=%x:", port->mode); goto dropped; } break; case FC_IP_RING: if (port->mode == MODE_INITIATOR) { (void) emlxs_ip_handle_unsol_req(port, cp, iocbq, mp, size); #ifdef SFCT_SUPPORT } else if (port->mode == MODE_TARGET) { (void) strlcpy(error_str, "Initiator mode disabled:", sizeof (error_str)); goto dropped; #endif /* SFCT_SUPPORT */ } else { (void) snprintf(error_str, sizeof (error_str), "Invalid mode=%x:", port->mode); goto dropped; } break; case FC_ELS_RING: if (port->mode == MODE_INITIATOR) { (void) emlxs_els_handle_unsol_req(port, cp, iocbq, mp, size); #ifdef SFCT_SUPPORT } else if (port->mode == MODE_TARGET) { (void) emlxs_fct_handle_unsol_els(port, cp, iocbq, mp, size); #endif /* SFCT_SUPPORT */ } else { (void) snprintf(error_str, sizeof (error_str), "Invalid mode=%x:", port->mode); goto dropped; } break; case FC_CT_RING: (void) emlxs_ct_handle_unsol_req(port, cp, iocbq, mp, size); break; } goto done; dropped: (*RcvDropped)++; EMLXS_MSGF(EMLXS_CONTEXT, dropped_msg, "%s: cmd=%x %s %x %x %x %x", error_str, iocb->ULPCOMMAND, emlxs_state_xlate(iocb->ULPSTATUS), word[4], word[5], word[6], word[7]); if (channelno == FC_FCT_RING) { uint32_t sid; if (hba->sli_mode == EMLXS_HBA_SLI3_MODE) { emlxs_node_t *ndlp; ndlp = emlxs_node_find_rpi(port, iocb->ULPIOTAG); if (! ndlp) { goto done; } sid = ndlp->nlp_DID; } else { sid = iocb->un.ulpWord[4] & 0xFFFFFF; } emlxs_send_logo(port, sid); } goto done; failed: (*RcvError)++; EMLXS_MSGF(EMLXS_CONTEXT, dropped_msg, "%s: cmd=%x %s %x %x %x %x hba:%x %x", error_str, iocb->ULPCOMMAND, emlxs_state_xlate(iocb->ULPSTATUS), word[4], word[5], word[6], word[7], hba->state, hba->flag); done: if (hba->flag & FC_HBQ_ENABLED) { if (iocb->ULPBDECOUNT) { HBQE_t *hbqE; uint32_t hbq_id; hbqE = (HBQE_t *)iocb; hbq_id = hbqE->unt.ext.HBQ_tag; emlxs_update_HBQ_index(hba, hbq_id); } } else { if (mp) { emlxs_mem_put(hba, buf_type, (void *)mp); } if (iocb->ULPBDECOUNT) { (void) emlxs_post_buffer(hba, rp, 1); } } return (0); } /* emlxs_handle_rcv_seq() */ /* EMLXS_CMD_RING_LOCK must be held when calling this function */ static void emlxs_sli3_issue_iocb(emlxs_hba_t *hba, RING *rp, IOCBQ *iocbq) { emlxs_port_t *port; IOCB *icmd; IOCB *iocb; emlxs_buf_t *sbp; off_t offset; uint32_t ringno; ringno = rp->ringno; sbp = iocbq->sbp; icmd = &iocbq->iocb; port = iocbq->port; HBASTATS.IocbIssued[ringno]++; /* Check for ULP pkt request */ if (sbp) { mutex_enter(&sbp->mtx); if (sbp->node == NULL) { /* Set node to base node by default */ iocbq->node = (void *)&port->node_base; sbp->node = (void *)&port->node_base; } sbp->pkt_flags |= PACKET_IN_CHIPQ; mutex_exit(&sbp->mtx); atomic_inc_32(&hba->io_active); #ifdef NODE_THROTTLE_SUPPORT if (sbp->node) { atomic_inc_32(&sbp->node->io_active); } #endif /* NODE_THROTTLE_SUPPORT */ #ifdef SFCT_SUPPORT #ifdef FCT_IO_TRACE if (sbp->fct_cmd) { emlxs_fct_io_trace(port, sbp->fct_cmd, EMLXS_FCT_IOCB_ISSUED); emlxs_fct_io_trace(port, sbp->fct_cmd, icmd->ULPCOMMAND); } #endif /* FCT_IO_TRACE */ #endif /* SFCT_SUPPORT */ rp->channelp->hbaSendCmd_sbp++; iocbq->channel = rp->channelp; } else { rp->channelp->hbaSendCmd++; } /* get the next available command ring iocb */ iocb = (IOCB *)(((char *)rp->fc_cmdringaddr + (rp->fc_cmdidx * hba->sli.sli3.iocb_cmd_size))); /* Copy the local iocb to the command ring iocb */ BE_SWAP32_BCOPY((uint8_t *)icmd, (uint8_t *)iocb, hba->sli.sli3.iocb_cmd_size); /* DMA sync the command ring iocb for the adapter */ offset = (off_t)((uint64_t)((unsigned long)iocb) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, hba->sli.sli3.iocb_cmd_size, DDI_DMA_SYNC_FORDEV); /* * After this, the sbp / iocb should not be * accessed in the xmit path. */ /* Free the local iocb if there is no sbp tracking it */ if (!sbp) { emlxs_mem_put(hba, MEM_IOCB, (void *)iocbq); } /* update local ring index to next available ring index */ rp->fc_cmdidx = (rp->fc_cmdidx + 1 >= rp->fc_numCiocb) ? 0 : rp->fc_cmdidx + 1; return; } /* emlxs_sli3_issue_iocb() */ static void emlxs_sli3_hba_kill(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; MAILBOX swpmb; MAILBOX *mb2; MAILBOX *mb1; uint32_t *word0; uint32_t j; uint32_t interlock_failed; uint32_t ha_copy; uint32_t value; off_t offset; uint32_t size; /* Perform adapter interlock to kill adapter */ interlock_failed = 0; mutex_enter(&EMLXS_PORT_LOCK); if (hba->flag & FC_INTERLOCKED) { EMLXS_STATE_CHANGE_LOCKED(hba, FC_KILLED); mutex_exit(&EMLXS_PORT_LOCK); return; } j = 0; while (j++ < 10000) { if (hba->mbox_queue_flag == 0) { break; } mutex_exit(&EMLXS_PORT_LOCK); BUSYWAIT_US(100); mutex_enter(&EMLXS_PORT_LOCK); } if (hba->mbox_queue_flag != 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock failed. Mailbox busy."); mutex_exit(&EMLXS_PORT_LOCK); return; } hba->flag |= FC_INTERLOCKED; hba->mbox_queue_flag = 1; /* Disable all host interrupts */ hba->sli.sli3.hc_copy = 0; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); WRITE_CSR_REG(hba, FC_HA_REG(hba), 0xffffffff); mb2 = FC_SLIM2_MAILBOX(hba); mb1 = FC_SLIM1_MAILBOX(hba); word0 = (uint32_t *)&swpmb; if (!(hba->flag & FC_SLIM2_MODE)) { goto mode_B; } mode_A: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Attempting SLIM2 Interlock..."); interlock_A: value = 0x55555555; *word0 = 0; swpmb.mbxCommand = MBX_KILL_BOARD; swpmb.mbxOwner = OWN_CHIP; /* Write value to SLIM */ WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1), value); WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb1)), *word0); /* Send Kill board request */ mb2->un.varWords[0] = value; mb2->mbxCommand = MBX_KILL_BOARD; mb2->mbxOwner = OWN_CHIP; /* Sync the memory */ offset = (off_t)((uint64_t)((unsigned long)mb2) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); size = (sizeof (uint32_t) * 2); BE_SWAP32_BCOPY((uint8_t *)mb2, (uint8_t *)mb2, size); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, size, DDI_DMA_SYNC_FORDEV); /* interrupt board to do it right away */ WRITE_CSR_REG(hba, FC_CA_REG(hba), CA_MBATT); /* First wait for command acceptence */ j = 0; while (j++ < 1000) { value = READ_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1)); if (value == 0xAAAAAAAA) { break; } BUSYWAIT_US(50); } if (value == 0xAAAAAAAA) { /* Now wait for mailbox ownership to clear */ while (j++ < 10000) { *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mb1)); if (swpmb.mbxOwner == 0) { break; } BUSYWAIT_US(50); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock succeeded."); goto done; } /* Interlock failed !!! */ interlock_failed = 1; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock failed."); mode_B: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Attempting SLIM1 Interlock..."); interlock_B: value = 0x55555555; *word0 = 0; swpmb.mbxCommand = MBX_KILL_BOARD; swpmb.mbxOwner = OWN_CHIP; /* Write KILL BOARD to mailbox */ WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1), value); WRITE_SLIM_ADDR(hba, ((volatile uint32_t *)mb1), *word0); /* interrupt board to do it right away */ WRITE_CSR_REG(hba, FC_CA_REG(hba), CA_MBATT); /* First wait for command acceptence */ j = 0; while (j++ < 1000) { value = READ_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1)); if (value == 0xAAAAAAAA) { break; } BUSYWAIT_US(50); } if (value == 0xAAAAAAAA) { /* Now wait for mailbox ownership to clear */ while (j++ < 10000) { *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mb1)); if (swpmb.mbxOwner == 0) { break; } BUSYWAIT_US(50); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock succeeded."); goto done; } /* Interlock failed !!! */ /* If this is the first time then try again */ if (interlock_failed == 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock failed. Retrying..."); /* Try again */ interlock_failed = 1; goto interlock_B; } /* * Now check for error attention to indicate the board has * been kiilled */ j = 0; while (j++ < 10000) { ha_copy = READ_CSR_REG(hba, FC_HA_REG(hba)); if (ha_copy & HA_ERATT) { break; } BUSYWAIT_US(50); } if (ha_copy & HA_ERATT) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock failed. Board killed."); } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "Interlock failed. Board not killed."); } done: hba->mbox_queue_flag = 0; EMLXS_STATE_CHANGE_LOCKED(hba, FC_KILLED); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_PORT_LOCK); return; } /* emlxs_sli3_hba_kill() */ static void emlxs_sli3_hba_kill4quiesce(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; MAILBOX swpmb; MAILBOX *mb2; MAILBOX *mb1; uint32_t *word0; off_t offset; uint32_t j; uint32_t value; uint32_t size; /* Disable all host interrupts */ hba->sli.sli3.hc_copy = 0; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); WRITE_CSR_REG(hba, FC_HA_REG(hba), 0xffffffff); mb2 = FC_SLIM2_MAILBOX(hba); mb1 = FC_SLIM1_MAILBOX(hba); word0 = (uint32_t *)&swpmb; value = 0x55555555; *word0 = 0; swpmb.mbxCommand = MBX_KILL_BOARD; swpmb.mbxOwner = OWN_CHIP; /* Write value to SLIM */ WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1), value); WRITE_SLIM_ADDR(hba, (((volatile uint32_t *)mb1)), *word0); /* Send Kill board request */ mb2->un.varWords[0] = value; mb2->mbxCommand = MBX_KILL_BOARD; mb2->mbxOwner = OWN_CHIP; /* Sync the memory */ offset = (off_t)((uint64_t)((unsigned long)mb2) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); size = (sizeof (uint32_t) * 2); BE_SWAP32_BCOPY((uint8_t *)mb2, (uint8_t *)mb2, size); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, size, DDI_DMA_SYNC_FORDEV); /* interrupt board to do it right away */ WRITE_CSR_REG(hba, FC_CA_REG(hba), CA_MBATT); /* First wait for command acceptence */ j = 0; while (j++ < 1000) { value = READ_SLIM_ADDR(hba, (((volatile uint32_t *)mb1) + 1)); if (value == 0xAAAAAAAA) { break; } BUSYWAIT_US(50); } if (value == 0xAAAAAAAA) { /* Now wait for mailbox ownership to clear */ while (j++ < 10000) { *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mb1)); if (swpmb.mbxOwner == 0) { break; } BUSYWAIT_US(50); } goto done; } done: EMLXS_STATE_CHANGE_LOCKED(hba, FC_KILLED); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ return; } /* emlxs_sli3_hba_kill4quiesce */ /* * emlxs_handle_mb_event * * Description: Process a Mailbox Attention. * Called from host_interrupt to process MBATT * * Returns: * */ static uint32_t emlxs_handle_mb_event(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; MAILBOX *mb; MAILBOX swpmb; MAILBOX *mbox; MAILBOXQ *mbq = NULL; uint32_t *word0; MATCHMAP *mbox_bp; off_t offset; uint32_t i; int rc; word0 = (uint32_t *)&swpmb; mutex_enter(&EMLXS_PORT_LOCK); switch (hba->mbox_queue_flag) { case 0: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_stray_mbox_intr_msg, "No mailbox active."); mutex_exit(&EMLXS_PORT_LOCK); return (0); case MBX_POLL: /* Mark mailbox complete, this should wake up any polling */ /* threads. This can happen if interrupts are enabled while */ /* a polled mailbox command is outstanding. If we don't set */ /* MBQ_COMPLETED here, the polling thread may wait until */ /* timeout error occurs */ mutex_enter(&EMLXS_MBOX_LOCK); mbq = (MAILBOXQ *)hba->mbox_mbq; if (mbq) { port = (emlxs_port_t *)mbq->port; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Mailbox event. Completing Polled command."); mbq->flag |= MBQ_COMPLETED; } mutex_exit(&EMLXS_MBOX_LOCK); mutex_exit(&EMLXS_PORT_LOCK); return (0); case MBX_SLEEP: case MBX_NOWAIT: /* Check mbox_timer, it acts as a service flag too */ /* The first to service the mbox queue will clear the timer */ if (hba->mbox_timer) { hba->mbox_timer = 0; mutex_enter(&EMLXS_MBOX_LOCK); mbq = (MAILBOXQ *)hba->mbox_mbq; mutex_exit(&EMLXS_MBOX_LOCK); } if (!mbq) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Mailbox event. No service required."); mutex_exit(&EMLXS_PORT_LOCK); return (0); } mb = (MAILBOX *)mbq; mutex_exit(&EMLXS_PORT_LOCK); break; default: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_completion_error_msg, "Invalid Mailbox flag (%x)."); mutex_exit(&EMLXS_PORT_LOCK); return (0); } /* Set port context */ port = (emlxs_port_t *)mbq->port; /* Get first word of mailbox */ if (hba->flag & FC_SLIM2_MODE) { mbox = FC_SLIM2_MAILBOX(hba); offset = (off_t)((uint64_t)((unsigned long)mbox) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL); *word0 = *((volatile uint32_t *)mbox); *word0 = BE_SWAP32(*word0); } else { mbox = FC_SLIM1_MAILBOX(hba); *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mbox)); } i = 0; while (swpmb.mbxOwner == OWN_CHIP) { if (i++ > 10000) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_stray_mbox_intr_msg, "OWN_CHIP: %s: status=%x", emlxs_mb_cmd_xlate(swpmb.mbxCommand), swpmb.mbxStatus); return (1); } /* Get first word of mailbox */ if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL); *word0 = *((volatile uint32_t *)mbox); *word0 = BE_SWAP32(*word0); } else { *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mbox)); } } /* Now that we are the owner, DMA Sync entire mailbox if needed */ if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, MAILBOX_CMD_BSIZE, DDI_DMA_SYNC_FORKERNEL); BE_SWAP32_BCOPY((uint8_t *)mbox, (uint8_t *)mb, MAILBOX_CMD_BSIZE); } else { READ_SLIM_COPY(hba, (uint32_t *)mb, (uint32_t *)mbox, MAILBOX_CMD_WSIZE); } #ifdef MBOX_EXT_SUPPORT if (mbq->extbuf) { uint32_t *mbox_ext = (uint32_t *)((uint8_t *)mbox + MBOX_EXTENSION_OFFSET); off_t offset_ext = offset + MBOX_EXTENSION_OFFSET; if (hba->flag & FC_SLIM2_MODE) { EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset_ext, mbq->extsize, DDI_DMA_SYNC_FORKERNEL); BE_SWAP32_BCOPY((uint8_t *)mbox_ext, (uint8_t *)mbq->extbuf, mbq->extsize); } else { READ_SLIM_COPY(hba, (uint32_t *)mbq->extbuf, mbox_ext, (mbq->extsize / 4)); } } #endif /* MBOX_EXT_SUPPORT */ #ifdef FMA_SUPPORT if (!(hba->flag & FC_SLIM2_MODE)) { /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); } #endif /* FMA_SUPPORT */ /* Now sync the memory buffer if one was used */ if (mbq->bp) { mbox_bp = (MATCHMAP *)mbq->bp; EMLXS_MPDATA_SYNC(mbox_bp->dma_handle, 0, mbox_bp->size, DDI_DMA_SYNC_FORKERNEL); } /* Mailbox has been completely received at this point */ if (mb->mbxCommand == MBX_HEARTBEAT) { hba->heartbeat_active = 0; goto done; } if (hba->mbox_queue_flag == MBX_SLEEP) { if (swpmb.mbxCommand != MBX_DOWN_LOAD && swpmb.mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Received. %s: status=%x Sleep.", emlxs_mb_cmd_xlate(swpmb.mbxCommand), swpmb.mbxStatus); } } else { if (swpmb.mbxCommand != MBX_DOWN_LOAD && swpmb.mbxCommand != MBX_DUMP_MEMORY) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Completed. %s: status=%x", emlxs_mb_cmd_xlate(swpmb.mbxCommand), swpmb.mbxStatus); } } /* Filter out passthru mailbox */ if (mbq->flag & MBQ_PASSTHRU) { goto done; } if (mb->mbxStatus) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "%s: status=0x%x", emlxs_mb_cmd_xlate(mb->mbxCommand), (uint32_t)mb->mbxStatus); } if (mbq->mbox_cmpl) { rc = (mbq->mbox_cmpl)(hba, mbq); /* If mbox was retried, return immediately */ if (rc) { return (0); } } done: /* Clean up the mailbox area */ emlxs_mb_fini(hba, mb, mb->mbxStatus); mbq = (MAILBOXQ *)emlxs_mb_get(hba); if (mbq) { /* Attempt to send pending mailboxes */ rc = emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_NOWAIT, 0); if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) { emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); } } return (0); } /* emlxs_handle_mb_event() */ static void emlxs_sli3_timer(emlxs_hba_t *hba) { /* Perform SLI3 level timer checks */ emlxs_sli3_timer_check_mbox(hba); } /* emlxs_sli3_timer() */ static void emlxs_sli3_timer_check_mbox(emlxs_hba_t *hba) { emlxs_port_t *port = &PPORT; emlxs_config_t *cfg = &CFG; MAILBOX *mb = NULL; MAILBOX swpmb; uint32_t *word0; uint32_t offset; uint32_t ha_copy = 0; if (!cfg[CFG_TIMEOUT_ENABLE].current) { return; } mutex_enter(&EMLXS_PORT_LOCK); /* Return if timer hasn't expired */ if (!hba->mbox_timer || (hba->timer_tics < hba->mbox_timer)) { mutex_exit(&EMLXS_PORT_LOCK); return; } /* Mailbox timed out, first check for error attention */ ha_copy = emlxs_check_attention(hba); if (ha_copy & HA_ERATT) { hba->mbox_timer = 0; mutex_exit(&EMLXS_PORT_LOCK); emlxs_handle_ff_error(hba); return; } word0 = (uint32_t *)&swpmb; if (hba->mbox_queue_flag) { /* Get first word of mailbox */ if (hba->flag & FC_SLIM2_MODE) { mb = FC_SLIM2_MAILBOX(hba); offset = (off_t)((uint64_t)((unsigned long)mb) - (uint64_t) ((unsigned long)hba->sli.sli3.slim2.virt)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, offset, sizeof (uint32_t), DDI_DMA_SYNC_FORKERNEL); *word0 = *((volatile uint32_t *)mb); *word0 = BE_SWAP32(*word0); } else { mb = FC_SLIM1_MAILBOX(hba); *word0 = READ_SLIM_ADDR(hba, ((volatile uint32_t *)mb)); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); #endif /* FMA_SUPPORT */ } mb = &swpmb; /* Check if mailbox has actually completed */ if (mb->mbxOwner == OWN_HOST) { /* Read host attention register to determine */ /* interrupt source */ uint32_t ha_copy = emlxs_check_attention(hba); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_detail_msg, "Mailbox attention missed: %s. Forcing event. " "hc=%x ha=%x", emlxs_mb_cmd_xlate(mb->mbxCommand), hba->sli.sli3.hc_copy, ha_copy); mutex_exit(&EMLXS_PORT_LOCK); (void) emlxs_handle_mb_event(hba); return; } /* The first to service the mbox queue will clear the timer */ /* We will service the mailbox here */ hba->mbox_timer = 0; mutex_enter(&EMLXS_MBOX_LOCK); mb = (MAILBOX *)hba->mbox_mbq; mutex_exit(&EMLXS_MBOX_LOCK); } if (mb) { switch (hba->mbox_queue_flag) { case MBX_NOWAIT: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, "%s: Nowait.", emlxs_mb_cmd_xlate(mb->mbxCommand)); break; case MBX_SLEEP: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, "%s: mb=%p Sleep.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); break; case MBX_POLL: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, "%s: mb=%p Polled.", emlxs_mb_cmd_xlate(mb->mbxCommand), mb); break; default: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, "%s: mb=%p (%d).", emlxs_mb_cmd_xlate(mb->mbxCommand), mb, hba->mbox_queue_flag); break; } } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mbox_timeout_msg, NULL); } hba->flag |= FC_MBOX_TIMEOUT; EMLXS_STATE_CHANGE_LOCKED(hba, FC_ERROR); mutex_exit(&EMLXS_PORT_LOCK); /* Perform mailbox cleanup */ /* This will wake any sleeping or polling threads */ emlxs_mb_fini(hba, NULL, MBX_TIMEOUT); /* Trigger adapter shutdown */ emlxs_thread_spawn(hba, emlxs_shutdown_thread, NULL, NULL); return; } /* emlxs_sli3_timer_check_mbox() */ /* * emlxs_mb_config_port Issue a CONFIG_PORT mailbox command */ static uint32_t emlxs_mb_config_port(emlxs_hba_t *hba, MAILBOXQ *mbq, uint32_t sli_mode, uint32_t hbainit) { MAILBOX *mb = (MAILBOX *)mbq; emlxs_vpd_t *vpd = &VPD; emlxs_port_t *port = &PPORT; emlxs_config_t *cfg; RING *rp; uint64_t pcb; uint64_t mbx; uint64_t hgp; uint64_t pgp; uint64_t rgp; MAILBOX *mbox; SLIM2 *slim; SLI2_RDSC *rdsc; uint64_t offset; uint32_t Laddr; uint32_t i; cfg = &CFG; bzero((void *)mb, MAILBOX_CMD_BSIZE); mbox = NULL; slim = NULL; mb->mbxCommand = MBX_CONFIG_PORT; mb->mbxOwner = OWN_HOST; mbq->mbox_cmpl = NULL; mb->un.varCfgPort.pcbLen = sizeof (PCB); mb->un.varCfgPort.hbainit[0] = hbainit; pcb = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(slim->pcb)); mb->un.varCfgPort.pcbLow = PADDR_LO(pcb); mb->un.varCfgPort.pcbHigh = PADDR_HI(pcb); /* Set Host pointers in SLIM flag */ mb->un.varCfgPort.hps = 1; /* Initialize hba structure for assumed default SLI2 mode */ /* If config port succeeds, then we will update it then */ hba->sli_mode = sli_mode; hba->vpi_max = 0; hba->flag &= ~FC_NPIV_ENABLED; if (sli_mode == EMLXS_HBA_SLI3_MODE) { mb->un.varCfgPort.sli_mode = EMLXS_HBA_SLI3_MODE; mb->un.varCfgPort.cerbm = 1; mb->un.varCfgPort.max_hbq = EMLXS_NUM_HBQ; if (cfg[CFG_NPIV_ENABLE].current) { if (vpd->feaLevelHigh >= 0x09) { if (hba->model_info.chip >= EMLXS_SATURN_CHIP) { mb->un.varCfgPort.vpi_max = MAX_VPORTS - 1; } else { mb->un.varCfgPort.vpi_max = MAX_VPORTS_LIMITED - 1; } mb->un.varCfgPort.cmv = 1; } else { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "CFGPORT: Firmware does not support NPIV. " "level=%d", vpd->feaLevelHigh); } } } /* * Now setup pcb */ ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.type = TYPE_NATIVE_SLI2; ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.feature = FEATURE_INITIAL_SLI2; ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.maxRing = (hba->sli.sli3.ring_count - 1); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.mailBoxSize = sizeof (MAILBOX) + MBOX_EXTENSION_SIZE; mbx = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(slim->mbx)); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.mbAddrHigh = PADDR_HI(mbx); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.mbAddrLow = PADDR_LO(mbx); /* * Set up HGP - Port Memory * * CR0Put - SLI2(no HBQs) = 0xc0, With HBQs = 0x80 * RR0Get 0xc4 0x84 * CR1Put 0xc8 0x88 * RR1Get 0xcc 0x8c * CR2Put 0xd0 0x90 * RR2Get 0xd4 0x94 * CR3Put 0xd8 0x98 * RR3Get 0xdc 0x9c * * Reserved 0xa0-0xbf * * If HBQs configured: * HBQ 0 Put ptr 0xc0 * HBQ 1 Put ptr 0xc4 * HBQ 2 Put ptr 0xc8 * ... * HBQ(M-1)Put Pointer 0xc0+(M-1)*4 */ if (sli_mode >= EMLXS_HBA_SLI3_MODE) { /* ERBM is enabled */ hba->sli.sli3.hgp_ring_offset = 0x80; hba->sli.sli3.hgp_hbq_offset = 0xC0; hba->sli.sli3.iocb_cmd_size = SLI3_IOCB_CMD_SIZE; hba->sli.sli3.iocb_rsp_size = SLI3_IOCB_RSP_SIZE; } else { /* SLI2 */ /* ERBM is disabled */ hba->sli.sli3.hgp_ring_offset = 0xC0; hba->sli.sli3.hgp_hbq_offset = 0; hba->sli.sli3.iocb_cmd_size = SLI2_IOCB_CMD_SIZE; hba->sli.sli3.iocb_rsp_size = SLI2_IOCB_RSP_SIZE; } /* The Sbus card uses Host Memory. The PCI card uses SLIM POINTER */ if (hba->bus_type == SBUS_FC) { hgp = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(mbox->us.s2.host)); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.hgpAddrHigh = PADDR_HI(hgp); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.hgpAddrLow = PADDR_LO(hgp); } else { ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.hgpAddrHigh = (uint32_t)ddi_get32(hba->pci_acc_handle, (uint32_t *)(hba->pci_addr + PCI_BAR_1_REGISTER)); Laddr = ddi_get32(hba->pci_acc_handle, (uint32_t *)(hba->pci_addr + PCI_BAR_0_REGISTER)); Laddr &= ~0x4; ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.hgpAddrLow = (uint32_t)(Laddr + hba->sli.sli3.hgp_ring_offset); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->pci_acc_handle); #endif /* FMA_SUPPORT */ } pgp = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(mbox->us.s2.port)); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.pgpAddrHigh = PADDR_HI(pgp); ((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.pgpAddrLow = PADDR_LO(pgp); offset = 0; for (i = 0; i < 4; i++) { rp = &hba->sli.sli3.ring[i]; rdsc = &((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb.rdsc[i]; /* Setup command ring */ rgp = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(slim->IOCBs[offset])); rdsc->cmdAddrHigh = PADDR_HI(rgp); rdsc->cmdAddrLow = PADDR_LO(rgp); rdsc->cmdEntries = rp->fc_numCiocb; rp->fc_cmdringaddr = (void *)&((SLIM2 *)hba->sli.sli3.slim2.virt)->IOCBs[offset]; offset += rdsc->cmdEntries * hba->sli.sli3.iocb_cmd_size; /* Setup response ring */ rgp = hba->sli.sli3.slim2.phys + (uint64_t)((unsigned long)&(slim->IOCBs[offset])); rdsc->rspAddrHigh = PADDR_HI(rgp); rdsc->rspAddrLow = PADDR_LO(rgp); rdsc->rspEntries = rp->fc_numRiocb; rp->fc_rspringaddr = (void *)&((SLIM2 *)hba->sli.sli3.slim2.virt)->IOCBs[offset]; offset += rdsc->rspEntries * hba->sli.sli3.iocb_rsp_size; } BE_SWAP32_BCOPY((uint8_t *) (&((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb), (uint8_t *)(&((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb), sizeof (PCB)); offset = ((uint64_t)((unsigned long) &(((SLIM2 *)hba->sli.sli3.slim2.virt)->pcb)) - (uint64_t)((unsigned long)hba->sli.sli3.slim2.virt)); EMLXS_MPDATA_SYNC(hba->sli.sli3.slim2.dma_handle, (off_t)offset, sizeof (PCB), DDI_DMA_SYNC_FORDEV); return (0); } /* emlxs_mb_config_port() */ static uint32_t emlxs_hbq_setup(emlxs_hba_t *hba, uint32_t hbq_id) { emlxs_port_t *port = &PPORT; HBQ_INIT_t *hbq; MATCHMAP *mp; HBQE_t *hbqE; MAILBOX *mb; MAILBOXQ *mbq; void *ioa2; uint32_t j; uint32_t count; uint32_t size; uint32_t ringno; uint32_t seg; switch (hbq_id) { case EMLXS_ELS_HBQ_ID: count = MEM_ELSBUF_COUNT; size = MEM_ELSBUF_SIZE; ringno = FC_ELS_RING; seg = MEM_ELSBUF; HBASTATS.ElsUbPosted = count; break; case EMLXS_IP_HBQ_ID: count = MEM_IPBUF_COUNT; size = MEM_IPBUF_SIZE; ringno = FC_IP_RING; seg = MEM_IPBUF; HBASTATS.IpUbPosted = count; break; case EMLXS_CT_HBQ_ID: count = MEM_CTBUF_COUNT; size = MEM_CTBUF_SIZE; ringno = FC_CT_RING; seg = MEM_CTBUF; HBASTATS.CtUbPosted = count; break; #ifdef SFCT_SUPPORT case EMLXS_FCT_HBQ_ID: count = MEM_FCTBUF_COUNT; size = MEM_FCTBUF_SIZE; ringno = FC_FCT_RING; seg = MEM_FCTBUF; HBASTATS.FctUbPosted = count; break; #endif /* SFCT_SUPPORT */ default: EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mem_alloc_msg, "hbq_setup: Invalid HBQ id. (%x)", hbq_id); return (1); } /* Configure HBQ */ hbq = &hba->sli.sli3.hbq_table[hbq_id]; hbq->HBQ_numEntries = count; /* Get a Mailbox buffer to setup mailbox commands for CONFIG_HBQ */ if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX)) == 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mem_alloc_msg, "hbq_setup: Unable to get mailbox."); return (1); } mb = (MAILBOX *)mbq; /* Allocate HBQ Host buffer and Initialize the HBQEs */ if (emlxs_hbq_alloc(hba, hbq_id)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mem_alloc_msg, "hbq_setup: Unable to allocate HBQ."); emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); return (1); } hbq->HBQ_recvNotify = 1; hbq->HBQ_num_mask = 0; /* Bind to ring */ hbq->HBQ_profile = 0; /* Selection profile */ /* 0=all, 7=logentry */ hbq->HBQ_ringMask = 1 << ringno; /* b0100 * ringno - Binds */ /* HBQ to a ring */ /* Ring0=b0001, Ring1=b0010, */ /* Ring2=b0100 */ hbq->HBQ_headerLen = 0; /* 0 if not profile 4 or 5 */ hbq->HBQ_logEntry = 0; /* Set to 1 if this HBQ will */ /* be used for */ hbq->HBQ_id = hbq_id; hbq->HBQ_PutIdx_next = 0; hbq->HBQ_PutIdx = hbq->HBQ_numEntries - 1; hbq->HBQ_GetIdx = 0; hbq->HBQ_PostBufCnt = hbq->HBQ_numEntries; bzero(hbq->HBQ_PostBufs, sizeof (hbq->HBQ_PostBufs)); /* Fill in POST BUFFERs in HBQE */ hbqE = (HBQE_t *)hbq->HBQ_host_buf.virt; for (j = 0; j < hbq->HBQ_numEntries; j++, hbqE++) { /* Allocate buffer to post */ if ((mp = (MATCHMAP *)emlxs_mem_get(hba, seg)) == 0) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_mem_alloc_msg, "hbq_setup: Unable to allocate HBQ buffer. " "cnt=%d", j); emlxs_hbq_free_all(hba, hbq_id); return (1); } hbq->HBQ_PostBufs[j] = mp; hbqE->unt.ext.HBQ_tag = hbq_id; hbqE->unt.ext.HBQE_tag = j; hbqE->bde.tus.f.bdeSize = size; hbqE->bde.tus.f.bdeFlags = 0; hbqE->unt.w = BE_SWAP32(hbqE->unt.w); hbqE->bde.tus.w = BE_SWAP32(hbqE->bde.tus.w); hbqE->bde.addrLow = BE_SWAP32(PADDR_LO(mp->phys)); hbqE->bde.addrHigh = BE_SWAP32(PADDR_HI(mp->phys)); } /* Issue CONFIG_HBQ */ emlxs_mb_config_hbq(hba, mbq, hbq_id); if (emlxs_sli3_issue_mbox_cmd(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg, "hbq_setup: Unable to config HBQ. cmd=%x status=%x", mb->mbxCommand, mb->mbxStatus); emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); emlxs_hbq_free_all(hba, hbq_id); return (1); } /* Setup HBQ Get/Put indexes */ ioa2 = (void *)((char *)hba->sli.sli3.slim_addr + (hba->sli.sli3.hgp_hbq_offset + (hbq_id * sizeof (uint32_t)))); WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, hbq->HBQ_PutIdx); hba->sli.sli3.hbq_count++; emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); #ifdef FMA_SUPPORT /* Access handle validation */ if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle) != DDI_FM_OK) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_invalid_access_handle_msg, NULL); emlxs_hbq_free_all(hba, hbq_id); return (1); } #endif /* FMA_SUPPORT */ return (0); } /* emlxs_hbq_setup() */ extern void emlxs_hbq_free_all(emlxs_hba_t *hba, uint32_t hbq_id) { HBQ_INIT_t *hbq; MBUF_INFO *buf_info; MBUF_INFO bufinfo; uint32_t seg; uint32_t j; switch (hbq_id) { case EMLXS_ELS_HBQ_ID: seg = MEM_ELSBUF; HBASTATS.ElsUbPosted = 0; break; case EMLXS_IP_HBQ_ID: seg = MEM_IPBUF; HBASTATS.IpUbPosted = 0; break; case EMLXS_CT_HBQ_ID: seg = MEM_CTBUF; HBASTATS.CtUbPosted = 0; break; #ifdef SFCT_SUPPORT case EMLXS_FCT_HBQ_ID: seg = MEM_FCTBUF; HBASTATS.FctUbPosted = 0; break; #endif /* SFCT_SUPPORT */ default: return; } hbq = &hba->sli.sli3.hbq_table[hbq_id]; if (hbq->HBQ_host_buf.virt != 0) { for (j = 0; j < hbq->HBQ_PostBufCnt; j++) { emlxs_mem_put(hba, seg, (void *)hbq->HBQ_PostBufs[j]); hbq->HBQ_PostBufs[j] = NULL; } hbq->HBQ_PostBufCnt = 0; buf_info = &bufinfo; bzero(buf_info, sizeof (MBUF_INFO)); buf_info->size = hbq->HBQ_host_buf.size; buf_info->virt = hbq->HBQ_host_buf.virt; buf_info->phys = hbq->HBQ_host_buf.phys; buf_info->dma_handle = hbq->HBQ_host_buf.dma_handle; buf_info->data_handle = hbq->HBQ_host_buf.data_handle; buf_info->flags = FC_MBUF_DMA; emlxs_mem_free(hba, buf_info); hbq->HBQ_host_buf.virt = NULL; } return; } /* emlxs_hbq_free_all() */ extern void emlxs_update_HBQ_index(emlxs_hba_t *hba, uint32_t hbq_id) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ void *ioa2; uint32_t status; uint32_t HBQ_PortGetIdx; HBQ_INIT_t *hbq; switch (hbq_id) { case EMLXS_ELS_HBQ_ID: HBASTATS.ElsUbPosted++; break; case EMLXS_IP_HBQ_ID: HBASTATS.IpUbPosted++; break; case EMLXS_CT_HBQ_ID: HBASTATS.CtUbPosted++; break; #ifdef SFCT_SUPPORT case EMLXS_FCT_HBQ_ID: HBASTATS.FctUbPosted++; break; #endif /* SFCT_SUPPORT */ default: return; } hbq = &hba->sli.sli3.hbq_table[hbq_id]; hbq->HBQ_PutIdx = (hbq->HBQ_PutIdx + 1 >= hbq->HBQ_numEntries) ? 0 : hbq->HBQ_PutIdx + 1; if (hbq->HBQ_PutIdx == hbq->HBQ_GetIdx) { HBQ_PortGetIdx = BE_SWAP32(((SLIM2 *)hba->sli.sli3.slim2.virt)->mbx.us.s2. HBQ_PortGetIdx[hbq_id]); hbq->HBQ_GetIdx = HBQ_PortGetIdx; if (hbq->HBQ_PutIdx == hbq->HBQ_GetIdx) { return; } } ioa2 = (void *)((char *)hba->sli.sli3.slim_addr + (hba->sli.sli3.hgp_hbq_offset + (hbq_id * sizeof (uint32_t)))); status = hbq->HBQ_PutIdx; WRITE_SLIM_ADDR(hba, (volatile uint32_t *)ioa2, status); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.slim_acc_handle); #endif /* FMA_SUPPORT */ return; } /* emlxs_update_HBQ_index() */ static void emlxs_sli3_enable_intr(emlxs_hba_t *hba) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ uint32_t status; /* Enable mailbox, error attention interrupts */ status = (uint32_t)(HC_MBINT_ENA); /* Enable ring interrupts */ if (hba->sli.sli3.ring_count >= 4) { status |= (HC_R3INT_ENA | HC_R2INT_ENA | HC_R1INT_ENA | HC_R0INT_ENA); } else if (hba->sli.sli3.ring_count == 3) { status |= (HC_R2INT_ENA | HC_R1INT_ENA | HC_R0INT_ENA); } else if (hba->sli.sli3.ring_count == 2) { status |= (HC_R1INT_ENA | HC_R0INT_ENA); } else if (hba->sli.sli3.ring_count == 1) { status |= (HC_R0INT_ENA); } hba->sli.sli3.hc_copy = status; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ } /* emlxs_sli3_enable_intr() */ static void emlxs_enable_latt(emlxs_hba_t *hba) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ mutex_enter(&EMLXS_PORT_LOCK); hba->sli.sli3.hc_copy |= HC_LAINT_ENA; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ mutex_exit(&EMLXS_PORT_LOCK); } /* emlxs_enable_latt() */ static void emlxs_sli3_disable_intr(emlxs_hba_t *hba, uint32_t att) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ /* Disable all adapter interrupts */ hba->sli.sli3.hc_copy = att; WRITE_CSR_REG(hba, FC_HC_REG(hba), hba->sli.sli3.hc_copy); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ } /* emlxs_sli3_disable_intr() */ static uint32_t emlxs_check_attention(emlxs_hba_t *hba) { #ifdef FMA_SUPPORT emlxs_port_t *port = &PPORT; #endif /* FMA_SUPPORT */ uint32_t ha_copy; ha_copy = READ_CSR_REG(hba, FC_HA_REG(hba)); #ifdef FMA_SUPPORT /* Access handle validation */ EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.csr_acc_handle); #endif /* FMA_SUPPORT */ return (ha_copy); } /* emlxs_check_attention() */ static void emlxs_sli3_poll_erratt(emlxs_hba_t *hba) { uint32_t ha_copy; ha_copy = emlxs_check_attention(hba); /* Adapter error */ if (ha_copy & HA_ERATT) { HBASTATS.IntrEvent[6]++; emlxs_handle_ff_error(hba); } } /* emlxs_sli3_poll_erratt() */ static uint32_t emlxs_sli3_reg_did_mbcmpl(emlxs_hba_t *hba, MAILBOXQ *mbq) { emlxs_port_t *port = (emlxs_port_t *)mbq->port; MAILBOXQ *mboxq; MAILBOX *mb; MATCHMAP *mp; NODELIST *ndlp; emlxs_port_t *vport; SERV_PARM *sp; int32_t i; uint32_t control; uint32_t ldata; uint32_t ldid; uint16_t lrpi; uint16_t lvpi; uint32_t rval; mb = (MAILBOX *)mbq; if (mb->mbxStatus) { if (mb->mbxStatus == MBXERR_NO_RESOURCES) { control = mb->un.varRegLogin.un.sp.bdeSize; if (control == 0) { /* Special handle for vport PLOGI */ if (mbq->iocbq == (uint8_t *)1) { mbq->iocbq = NULL; } return (0); } emlxs_mb_retry(hba, mbq); return (1); } if (mb->mbxStatus == MBXERR_RPI_FULL) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Limit reached. count=%d", port->node_count); } /* Special handle for vport PLOGI */ if (mbq->iocbq == (uint8_t *)1) { mbq->iocbq = NULL; } return (0); } mp = (MATCHMAP *)mbq->bp; if (!mp) { return (0); } ldata = mb->un.varWords[5]; lvpi = (ldata & 0xffff); port = &VPORT(lvpi); /* First copy command data */ ldata = mb->un.varWords[0]; /* get rpi */ lrpi = ldata & 0xffff; ldata = mb->un.varWords[1]; /* get did */ ldid = ldata & MASK_DID; sp = (SERV_PARM *)mp->virt; /* Create or update the node */ ndlp = emlxs_node_create(port, ldid, lrpi, sp); if (ndlp == NULL) { emlxs_ub_priv_t *ub_priv; /* * Fake a mailbox error, so the mbox_fini * can take appropriate action */ mb->mbxStatus = MBXERR_RPI_FULL; if (mbq->ubp) { ub_priv = ((fc_unsol_buf_t *)mbq->ubp)->ub_fca_private; ub_priv->flags |= EMLXS_UB_REPLY; } /* This must be (0xFFFFFE) which was registered by vport */ if (lrpi == 0) { return (0); } if (!(mboxq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "reg_did_mbcmpl:failed. Unable to allocate mbox"); return (0); } mb = (MAILBOX *)mboxq->mbox; mb->un.varUnregLogin.rpi = lrpi; mb->un.varUnregLogin.vpi = lvpi; mb->mbxCommand = MBX_UNREG_LOGIN; mb->mbxOwner = OWN_HOST; mboxq->sbp = NULL; mboxq->ubp = NULL; mboxq->iocbq = NULL; mboxq->mbox_cmpl = NULL; mboxq->context = NULL; mboxq->port = (void *)port; rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mboxq, MBX_NOWAIT, 0); if ((rval != MBX_BUSY) && (rval != MBX_SUCCESS)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "reg_did_mbcmpl:failed. Unable to send request."); emlxs_mem_put(hba, MEM_MBOX, (void *)mboxq); return (0); } return (0); } if (ndlp->nlp_DID == FABRIC_DID) { /* FLOGI/FDISC successfully completed on this port */ mutex_enter(&EMLXS_PORT_LOCK); port->flag |= EMLXS_PORT_FLOGI_CMPL; mutex_exit(&EMLXS_PORT_LOCK); /* If CLEAR_LA has been sent, then attempt to */ /* register the vpi now */ if (hba->state == FC_READY) { (void) emlxs_mb_reg_vpi(port, NULL); } /* * If NPIV Fabric support has just been established on * the physical port, then notify the vports of the * link up */ if ((lvpi == 0) && (hba->flag & FC_NPIV_ENABLED) && (hba->flag & FC_NPIV_SUPPORTED)) { /* Skip the physical port */ for (i = 1; i < MAX_VPORTS; i++) { vport = &VPORT(i); if (!(vport->flag & EMLXS_PORT_BOUND) || !(vport->flag & EMLXS_PORT_ENABLED)) { continue; } emlxs_port_online(vport); } } } /* Check for special restricted login flag */ if (mbq->iocbq == (uint8_t *)1) { mbq->iocbq = NULL; (void) EMLXS_SLI_UNREG_NODE(port, ndlp, NULL, NULL, NULL); return (0); } /* Needed for FCT trigger in emlxs_mb_deferred_cmpl */ if (mbq->sbp) { ((emlxs_buf_t *)mbq->sbp)->node = ndlp; } #ifdef DHCHAP_SUPPORT if (mbq->sbp || mbq->ubp) { if (emlxs_dhc_auth_start(port, ndlp, mbq->sbp, mbq->ubp) == 0) { /* Auth started - auth completion will */ /* handle sbp and ubp now */ mbq->sbp = NULL; mbq->ubp = NULL; } } #endif /* DHCHAP_SUPPORT */ return (0); } /* emlxs_sli3_reg_did_mbcmpl() */ static uint32_t emlxs_sli3_reg_did(emlxs_port_t *port, uint32_t did, SERV_PARM *param, emlxs_buf_t *sbp, fc_unsol_buf_t *ubp, IOCBQ *iocbq) { emlxs_hba_t *hba = HBA; MATCHMAP *mp; MAILBOXQ *mbq; MAILBOX *mb; uint32_t rval; /* Check for invalid node ids to register */ if ((did == 0) && (!(hba->flag & FC_LOOPBACK_MODE))) { return (1); } if (did & 0xff000000) { return (1); } if ((rval = emlxs_mb_check_sparm(hba, param))) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Invalid service parameters. did=%06x rval=%d", did, rval); return (1); } /* Check if the node limit has been reached */ if (port->node_count >= hba->max_nodes) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Limit reached. did=%06x count=%d", did, port->node_count); return (1); } if (!(mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Unable to allocate mailbox. did=%x", did); return (1); } mb = (MAILBOX *)mbq->mbox; bzero((void *)mb, MAILBOX_CMD_BSIZE); /* Build login request */ if ((mp = (MATCHMAP *)emlxs_mem_get(hba, MEM_BUF)) == 0) { emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Unable to allocate buffer. did=%x", did); return (1); } bcopy((void *)param, (void *)mp->virt, sizeof (SERV_PARM)); mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (SERV_PARM); mb->un.varRegLogin.un.sp64.addrHigh = PADDR_HI(mp->phys); mb->un.varRegLogin.un.sp64.addrLow = PADDR_LO(mp->phys); mb->un.varRegLogin.did = did; mb->un.varWords[30] = 0; /* flags */ mb->mbxCommand = MBX_REG_LOGIN64; mb->mbxOwner = OWN_HOST; mb->un.varRegLogin.vpi = port->vpi; mb->un.varRegLogin.rpi = 0; mbq->sbp = (void *)sbp; mbq->ubp = (void *)ubp; mbq->iocbq = (void *)iocbq; mbq->bp = (void *)mp; mbq->mbox_cmpl = emlxs_sli3_reg_did_mbcmpl; mbq->context = NULL; mbq->port = (void *)port; rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_NOWAIT, 0); if ((rval != MBX_BUSY) && (rval != MBX_SUCCESS)) { emlxs_mem_put(hba, MEM_BUF, (void *)mp); emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_failed_msg, "Unable to send mbox. did=%x", did); return (1); } return (0); } /* emlxs_sli3_reg_did() */ /*ARGSUSED*/ static uint32_t emlxs_sli3_unreg_node_mbcmpl(emlxs_hba_t *hba, MAILBOXQ *mbq) { emlxs_port_t *port = (emlxs_port_t *)mbq->port; MAILBOX *mb; NODELIST *node; uint16_t rpi; node = (NODELIST *)mbq->context; mb = (MAILBOX *)mbq; rpi = (node)? node->nlp_Rpi:0xffff; if (mb->mbxStatus) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node_mbcmpl:failed. node=%p rpi=%d status=%x", node, rpi, mb->mbxStatus); return (0); } EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node_mbcmpl: node=%p rpi=%d", node, rpi); if (node) { emlxs_node_rm(port, node); } else { /* All nodes */ emlxs_node_destroy_all(port); } return (0); } /* emlxs_sli3_unreg_node_mbcmpl */ static uint32_t emlxs_sli3_unreg_node(emlxs_port_t *port, NODELIST *node, emlxs_buf_t *sbp, fc_unsol_buf_t *ubp, IOCBQ *iocbq) { emlxs_hba_t *hba = HBA; MAILBOXQ *mbq; MAILBOX *mb; uint16_t rpi; uint32_t rval; if (node) { /* Check for base node */ if (node == &port->node_base) { /* just flush base node */ (void) emlxs_tx_node_flush(port, &port->node_base, 0, 0, 0); (void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0); port->did = 0; /* Return now */ return (1); } rpi = (uint16_t)node->nlp_Rpi; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node:%p rpi=%d", node, rpi); /* This node must be (0xFFFFFE) which registered by vport */ if (rpi == 0) { emlxs_node_rm(port, node); return (0); } } else { /* Unreg all nodes */ rpi = 0xffff; EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node: All"); } if (!(mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node:failed. Unable to allocate mbox"); return (1); } mb = (MAILBOX *)mbq->mbox; mb->un.varUnregLogin.rpi = rpi; mb->un.varUnregLogin.vpi = port->vpip->VPI; mb->mbxCommand = MBX_UNREG_LOGIN; mb->mbxOwner = OWN_HOST; mbq->sbp = (void *)sbp; mbq->ubp = (void *)ubp; mbq->iocbq = (void *)iocbq; mbq->mbox_cmpl = emlxs_sli3_unreg_node_mbcmpl; mbq->context = (void *)node; mbq->port = (void *)port; rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_NOWAIT, 0); if ((rval != MBX_BUSY) && (rval != MBX_SUCCESS)) { EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg, "unreg_node:failed. Unable to send request."); emlxs_mem_put(hba, MEM_MBOX, (void *)mbq); return (1); } return (0); } /* emlxs_sli3_unreg_node() */