/* * 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 usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * 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 2015 QLogic Corporation */ /* * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. */ /* * ISP2xxx Solaris Fibre Channel Adapter (FCA) driver source file. * * *********************************************************************** * * ** * * NOTICE ** * * COPYRIGHT (C) 1996-2015 QLOGIC CORPORATION ** * * ALL RIGHTS RESERVED ** * * ** * *********************************************************************** * */ #include #include #include #include #include #include #include #include #include /* * Local data */ /* * Local prototypes */ static uint16_t ql_nvram_request(ql_adapter_state_t *, uint32_t); static int ql_nvram_24xx_config(ql_adapter_state_t *); static void ql_23_properties(ql_adapter_state_t *, ql_init_cb_t *); static void ql_24xx_properties(ql_adapter_state_t *, ql_init_24xx_cb_t *); static int ql_check_isp_firmware(ql_adapter_state_t *); static int ql_load_flash_fw(ql_adapter_state_t *); static int ql_configure_loop(ql_adapter_state_t *); static int ql_configure_hba(ql_adapter_state_t *); static int ql_configure_fabric(ql_adapter_state_t *); static int ql_configure_device_d_id(ql_adapter_state_t *); static void ql_update_dev(ql_adapter_state_t *, uint32_t); static void ql_set_max_read_req(ql_adapter_state_t *); static void ql_configure_n_port_info(ql_adapter_state_t *); static void ql_reset_24xx_chip(ql_adapter_state_t *); static void ql_mps_reset(ql_adapter_state_t *); /* * ql_initialize_adapter * Initialize board. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_initialize_adapter(ql_adapter_state_t *ha) { int rval; class_svc_param_t *class3_param; caddr_t msg; la_els_logi_t *els = &ha->loginparams; int retries = 5; QL_PRINT_10(ha, "started cfg=0x%llx\n", ha->cfg_flags); do { /* Clear adapter flags. */ TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= TASK_DAEMON_STOP_FLG | TASK_DAEMON_SLEEPING_FLG | TASK_DAEMON_ALIVE_FLG | TASK_DAEMON_IDLE_CHK_FLG; ha->task_daemon_flags |= LOOP_DOWN; TASK_DAEMON_UNLOCK(ha); ha->loop_down_timer = LOOP_DOWN_TIMER_OFF; ADAPTER_STATE_LOCK(ha); ha->flags |= ABORT_CMDS_LOOP_DOWN_TMO; ha->flags &= ~ONLINE; ADAPTER_STATE_UNLOCK(ha); ha->state = FC_STATE_OFFLINE; msg = "Loop OFFLINE"; rval = ql_pci_sbus_config(ha); if (rval != QL_SUCCESS) { TASK_DAEMON_LOCK(ha); if (!(ha->task_daemon_flags & ABORT_ISP_ACTIVE)) { EL(ha, "ql_pci_sbus_cfg, isp_abort_needed\n"); ha->task_daemon_flags |= ISP_ABORT_NEEDED; } TASK_DAEMON_UNLOCK(ha); continue; } (void) ql_setup_fcache(ha); /* Reset ISP chip. */ ql_reset_chip(ha); /* Get NVRAM configuration if needed. */ if (ha->init_ctrl_blk.cb.version == 0) { (void) ql_nvram_config(ha); } /* Determine which RISC code to use. */ if ((rval = ql_check_isp_firmware(ha)) != QL_SUCCESS) { if (ha->dev_state != NX_DEV_READY) { EL(ha, "dev_state not ready, isp_abort_needed_2" "\n"); TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= ISP_ABORT_NEEDED; TASK_DAEMON_UNLOCK(ha); break; } if ((rval = ql_mbx_wrap_test(ha, NULL)) == QL_SUCCESS) { rval = ql_load_isp_firmware(ha); } } if (rval == QL_SUCCESS && (rval = ql_set_cache_line(ha)) == QL_SUCCESS && (rval = ql_init_rings(ha)) == QL_SUCCESS) { ql_enable_intr(ha); (void) ql_fw_ready(ha, ha->fwwait); if (!DRIVER_SUSPENDED(ha) && ha->loop_down_timer == LOOP_DOWN_TIMER_OFF) { if (ha->topology & QL_LOOP_CONNECTION) { ha->state = ha->state | FC_STATE_LOOP; msg = "Loop ONLINE"; TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= STATE_ONLINE; TASK_DAEMON_UNLOCK(ha); } else if (ha->topology & QL_P2P_CONNECTION) { ha->state = ha->state | FC_STATE_ONLINE; msg = "Link ONLINE"; TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= STATE_ONLINE; TASK_DAEMON_UNLOCK(ha); } else { msg = "Unknown Link state"; } } } else { TASK_DAEMON_LOCK(ha); if (!(ha->task_daemon_flags & ABORT_ISP_ACTIVE)) { EL(ha, "failed, isp_abort_needed\n"); ha->task_daemon_flags |= ISP_ABORT_NEEDED | LOOP_DOWN; } TASK_DAEMON_UNLOCK(ha); } } while (retries-- != 0 && ha->task_daemon_flags & ISP_ABORT_NEEDED); cmn_err(CE_NOTE, "!Qlogic %s(%d): %s", QL_NAME, ha->instance, msg); /* Enable ISP interrupts if not already enabled. */ if (!(ha->flags & INTERRUPTS_ENABLED)) { ql_enable_intr(ha); } ADAPTER_STATE_LOCK(ha); ha->flags |= ONLINE; ADAPTER_STATE_UNLOCK(ha); /* * Set flash write-protection. */ if (CFG_IST(ha, CFG_ISP_FW_TYPE_2) && ha->dev_state == NX_DEV_READY) { ql_24xx_protect_flash(ha); } TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~(FC_STATE_CHANGE | MARKER_NEEDED | COMMAND_WAIT_NEEDED); TASK_DAEMON_UNLOCK(ha); /* * Setup login parameters. */ bcopy(QL_VERSION, ha->adapter_stats->revlvl.qlddv, strlen(QL_VERSION)); els->common_service.fcph_version = 0x2006; els->common_service.btob_credit = 3; els->common_service.cmn_features = ha->topology & QL_N_PORT ? 0x8000 : 0x8800; els->common_service.conc_sequences = 0xff; els->common_service.relative_offset = 3; els->common_service.e_d_tov = 0x07d0; class3_param = (class_svc_param_t *)&els->class_3; class3_param->class_valid_svc_opt = 0x8800; class3_param->rcv_data_size = els->common_service.rx_bufsize; class3_param->conc_sequences = 0xff; class3_param->open_sequences_per_exch = 1; if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_pci_sbus_config * Setup device PCI/SBUS configuration registers. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_pci_sbus_config(ql_adapter_state_t *ha) { uint32_t timer; uint16_t cmd, w16; QL_PRINT_10(ha, "started\n"); if (CFG_IST(ha, CFG_SBUS_CARD)) { w16 = (uint16_t)ddi_get16(ha->sbus_fpga_dev_handle, (uint16_t *)(ha->sbus_fpga_iobase + FPGA_REVISION)); EL(ha, "FPGA rev is %d.%d", (w16 & 0xf0) >> 4, w16 & 0xf); } else { /* * we want to respect framework's setting of PCI * configuration space command register and also * want to make sure that all bits of interest to us * are properly set in command register. */ cmd = (uint16_t)ql_pci_config_get16(ha, PCI_CONF_COMM); cmd = (uint16_t)(cmd | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME | PCI_COMM_PARITY_DETECT | PCI_COMM_SERR_ENABLE); if (ql_get_cap_ofst(ha, PCI_CAP_ID_PCIX)) { cmd = (uint16_t)(cmd | PCI_COMM_MEMWR_INVAL); } /* * If this is a 2300 card and not 2312, reset the * MEMWR_INVAL due to a bug in the 2300. Unfortunately, the * 2310 also reports itself as a 2300 so we need to get the * fb revision level -- a 6 indicates it really is a 2300 and * not a 2310. */ if (ha->device_id == 0x2300) { /* Pause RISC. */ WRT16_IO_REG(ha, hccr, HC_PAUSE_RISC); for (timer = 0; timer < 30000; timer++) { if ((RD16_IO_REG(ha, hccr) & HC_RISC_PAUSE) != 0) { break; } else { drv_usecwait(MILLISEC); } } /* Select FPM registers. */ WRT16_IO_REG(ha, ctrl_status, 0x20); /* Get the fb rev level */ if (RD16_IO_REG(ha, fb_cmd) == 6) { cmd = (uint16_t)(cmd & ~PCI_COMM_MEMWR_INVAL); } /* Deselect FPM registers. */ WRT16_IO_REG(ha, ctrl_status, 0x0); /* Release RISC module. */ WRT16_IO_REG(ha, hccr, HC_RELEASE_RISC); for (timer = 0; timer < 30000; timer++) { if ((RD16_IO_REG(ha, hccr) & HC_RISC_PAUSE) == 0) { break; } else { drv_usecwait(MILLISEC); } } } else if (ha->device_id == 0x2312) { /* * cPCI ISP2312 specific code to service function 1 * hot-swap registers. */ if ((RD16_IO_REG(ha, ctrl_status) & ISP_FUNC_NUM_MASK) != 0) { ql_pci_config_put8(ha, 0x66, 0xc2); } } if (!(CFG_IST(ha, CFG_CTRL_82XX)) && ha->pci_max_read_req != 0) { ql_set_max_read_req(ha); } ql_pci_config_put16(ha, PCI_CONF_COMM, cmd); /* Set cache line register. */ ql_pci_config_put8(ha, PCI_CONF_CACHE_LINESZ, 0x10); /* Set latency register. */ ql_pci_config_put8(ha, PCI_CONF_LATENCY_TIMER, 0x40); /* Reset expansion ROM address decode enable. */ if (!CFG_IST(ha, CFG_CTRL_278083)) { w16 = (uint16_t)ql_pci_config_get16(ha, PCI_CONF_ROM); w16 = (uint16_t)(w16 & ~BIT_0); ql_pci_config_put16(ha, PCI_CONF_ROM, w16); } } QL_PRINT_10(ha, "done\n"); return (QL_SUCCESS); } /* * Set the PCI max read request value. * * Input: * ha: adapter state pointer. * * Output: * none. * * Returns: * * Context: * Kernel context. */ static void ql_set_max_read_req(ql_adapter_state_t *ha) { int ofst; uint16_t read_req, w16; uint16_t tmp = ha->pci_max_read_req; QL_PRINT_3(ha, "started\n"); if ((ofst = ql_get_cap_ofst(ha, PCI_CAP_ID_PCIX))) { ofst += PCI_PCIX_COMMAND; QL_PRINT_10(ha, "PCI-X Command Reg = %xh\n", ofst); /* check for vaild override value */ if (tmp == 512 || tmp == 1024 || tmp == 2048 || tmp == 4096) { /* shift away the don't cares */ tmp = (uint16_t)(tmp >> 10); /* convert bit pos to request value */ for (read_req = 0; tmp != 0; read_req++) { tmp = (uint16_t)(tmp >> 1); } w16 = (uint16_t)ql_pci_config_get16(ha, ofst); w16 = (uint16_t)(w16 & ~(BIT_3 & BIT_2)); w16 = (uint16_t)(w16 | (read_req << 2)); ql_pci_config_put16(ha, ofst, w16); } else { EL(ha, "invalid parameter value for " "'pci-max-read-request': %d; using system " "default\n", tmp); } } else if ((ofst = ql_get_cap_ofst(ha, PCI_CAP_ID_PCI_E))) { ofst += PCI_PCIE_DEVICE_CONTROL; QL_PRINT_10(ha, "PCI-E Device Control Reg = %xh\n", ofst); if (tmp == 128 || tmp == 256 || tmp == 512 || tmp == 1024 || tmp == 2048 || tmp == 4096) { /* shift away the don't cares */ tmp = (uint16_t)(tmp >> 8); /* convert bit pos to request value */ for (read_req = 0; tmp != 0; read_req++) { tmp = (uint16_t)(tmp >> 1); } w16 = (uint16_t)ql_pci_config_get16(ha, ofst); w16 = (uint16_t)(w16 & ~(BIT_14 | BIT_13 | BIT_12)); w16 = (uint16_t)(w16 | (read_req << 12)); ql_pci_config_put16(ha, ofst, w16); } else { EL(ha, "invalid parameter value for " "'pci-max-read-request': %d; using system " "default\n", tmp); } } QL_PRINT_3(ha, "done\n"); } /* * NVRAM configuration. * * Input: * ha: adapter state pointer. * ha->req_q[0]: request ring * * Output: * ha->init_ctrl_blk = initialization control block * host adapters parameters in host adapter block * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_nvram_config(ql_adapter_state_t *ha) { uint32_t cnt; caddr_t dptr1, dptr2; ql_init_cb_t *icb = &ha->init_ctrl_blk.cb; ql_ip_init_cb_t *ip_icb = &ha->ip_init_ctrl_blk.cb; nvram_t *nv = (nvram_t *)ha->req_q[0]->req_ring.bp; uint16_t *wptr = (uint16_t *)ha->req_q[0]->req_ring.bp; uint8_t chksum = 0; int rval; int idpromlen; char idprombuf[32]; uint32_t start_addr; la_els_logi_t *els = &ha->loginparams; QL_PRINT_10(ha, "started\n"); if (CFG_IST(ha, CFG_ISP_FW_TYPE_2)) { return (ql_nvram_24xx_config(ha)); } start_addr = 0; if ((rval = ql_lock_nvram(ha, &start_addr, LNF_NVRAM_DATA)) == QL_SUCCESS) { /* Verify valid NVRAM checksum. */ for (cnt = 0; cnt < sizeof (nvram_t) / 2; cnt++) { *wptr = (uint16_t)ql_get_nvram_word(ha, (uint32_t)(cnt + start_addr)); chksum = (uint8_t)(chksum + (uint8_t)*wptr); chksum = (uint8_t)(chksum + (uint8_t)(*wptr >> 8)); wptr++; } ql_release_nvram(ha); } /* Bad NVRAM data, set defaults parameters. */ if (rval != QL_SUCCESS || chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) { EL(ha, "failed, rval=%xh, checksum=%xh, " "id=%02x%02x%02x%02xh, flsz=%xh, pciconfvid=%xh, " "nvram_version=%x\n", rval, chksum, nv->id[0], nv->id[1], nv->id[2], nv->id[3], ha->xioctl->fdesc.flash_size, ha->subven_id, nv->nvram_version); /* Don't print nvram message if it's an on-board 2200 */ if (!((CFG_IST(ha, CFG_CTRL_22XX)) && (ha->xioctl->fdesc.flash_size == 0))) { cmn_err(CE_WARN, "%s(%d): NVRAM configuration failed," " using driver defaults.", QL_NAME, ha->instance); } /* Reset NVRAM data. */ bzero((void *)nv, sizeof (nvram_t)); /* * Set default initialization control block. */ nv->parameter_block_version = ICB_VERSION; nv->firmware_options[0] = BIT_4 | BIT_3 | BIT_2 | BIT_1; nv->firmware_options[1] = BIT_7 | BIT_5 | BIT_2; nv->max_frame_length[1] = 4; /* * Allow 2048 byte frames for 2300 */ if (CFG_IST(ha, CFG_CTRL_2363)) { nv->max_frame_length[1] = 8; } nv->max_iocb_allocation[1] = 1; nv->execution_throttle[0] = 16; nv->login_retry_count = 8; idpromlen = 32; /*LINTED [Solaris DDI_DEV_T_ANY Lint warning]*/ if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ha->dip, DDI_PROP_CANSLEEP, "idprom", (caddr_t)idprombuf, &idpromlen) != DDI_PROP_SUCCESS) { QL_PRINT_10(ha, "Unable to read idprom " "property\n"); cmn_err(CE_WARN, "%s(%d) : Unable to read idprom " "property", QL_NAME, ha->instance); nv->port_name[2] = 33; nv->port_name[3] = 224; nv->port_name[4] = 139; nv->port_name[7] = (uint8_t) (NAA_ID_IEEE_EXTENDED << 4 | ha->instance); } else { nv->port_name[2] = idprombuf[2]; nv->port_name[3] = idprombuf[3]; nv->port_name[4] = idprombuf[4]; nv->port_name[5] = idprombuf[5]; nv->port_name[6] = idprombuf[6]; nv->port_name[7] = idprombuf[7]; nv->port_name[0] = (uint8_t) (NAA_ID_IEEE_EXTENDED << 4 | ha->instance); } /* Don't print nvram message if it's an on-board 2200 */ if (!(CFG_IST(ha, CFG_CTRL_22XX)) && (ha->xioctl->fdesc.flash_size == 0)) { cmn_err(CE_WARN, "%s(%d): Unreliable HBA NVRAM, using" " default HBA parameters and temporary WWPN:" " %02x%02x%02x%02x%02x%02x%02x%02x", QL_NAME, ha->instance, nv->port_name[0], nv->port_name[1], nv->port_name[2], nv->port_name[3], nv->port_name[4], nv->port_name[5], nv->port_name[6], nv->port_name[7]); } nv->login_timeout = 4; /* Set default connection options for the 23xx to 2 */ if (!(CFG_IST(ha, CFG_CTRL_22XX))) { nv->add_fw_opt[0] = (uint8_t)(nv->add_fw_opt[0] | BIT_5); } /* * Set default host adapter parameters */ nv->host_p[0] = BIT_1; nv->host_p[1] = BIT_2; nv->reset_delay = 5; nv->port_down_retry_count = 8; nv->maximum_luns_per_target[0] = 8; rval = QL_FUNCTION_FAILED; } /* Reset initialization control blocks. */ bzero((void *)icb, sizeof (ql_init_cb_t)); bzero((void *)ip_icb, sizeof (ql_ip_init_cb_t)); /* * Copy over NVRAM RISC parameter block * to initialization control block. */ dptr1 = (caddr_t)icb; dptr2 = (caddr_t)&nv->parameter_block_version; cnt = (uint32_t)((uintptr_t)&icb->request_q_outpointer[0] - (uintptr_t)&icb->version); while (cnt-- != 0) { *dptr1++ = *dptr2++; } /* Copy 2nd half. */ dptr1 = (caddr_t)&icb->add_fw_opt[0]; cnt = (uint32_t)((uintptr_t)&icb->reserved_3[0] - (uintptr_t)&icb->add_fw_opt[0]); while (cnt-- != 0) { *dptr1++ = *dptr2++; } ha->execution_throttle = CHAR_TO_SHORT(nv->execution_throttle[0], nv->execution_throttle[1]); ha->loop_reset_delay = nv->reset_delay; ha->port_down_retry_count = nv->port_down_retry_count; ha->maximum_luns_per_target = CHAR_TO_SHORT( nv->maximum_luns_per_target[0], nv->maximum_luns_per_target[1]); if (ha->maximum_luns_per_target == 0) { ha->maximum_luns_per_target++; } ha->adapter_features = CHAR_TO_SHORT(nv->adapter_features[0], nv->adapter_features[1]); /* Check for adapter node name (big endian). */ for (cnt = 0; cnt < 8; cnt++) { if (icb->node_name[cnt] != 0) { break; } } /* Copy port name if no node name (big endian). */ if (cnt == 8) { for (cnt = 0; cnt < 8; cnt++) { icb->node_name[cnt] = icb->port_name[cnt]; } icb->node_name[0] = (uint8_t)(icb->node_name[0] & ~BIT_0); icb->port_name[0] = (uint8_t)(icb->node_name[0] | BIT_0); } ADAPTER_STATE_LOCK(ha); ha->cfg_flags &= ~(CFG_ENABLE_FULL_LIP_LOGIN | CFG_ENABLE_TARGET_RESET | CFG_ENABLE_LIP_RESET | CFG_LOAD_FLASH_FW | CFG_FAST_TIMEOUT | CFG_DISABLE_RISC_CODE_LOAD | CFG_ENABLE_FWEXTTRACE | CFG_ENABLE_FWFCETRACE | CFG_SET_CACHE_LINE_SIZE_1 | CFG_LR_SUPPORT); if (nv->host_p[0] & BIT_4) { ha->cfg_flags |= CFG_DISABLE_RISC_CODE_LOAD; } if (nv->host_p[0] & BIT_5) { ha->cfg_flags |= CFG_SET_CACHE_LINE_SIZE_1; } if (nv->host_p[1] & BIT_2) { ha->cfg_flags |= CFG_ENABLE_FULL_LIP_LOGIN; } if (nv->host_p[1] & BIT_3) { ha->cfg_flags |= CFG_ENABLE_TARGET_RESET; } nv->adapter_features[0] & BIT_3 ? (ha->flags |= MULTI_CHIP_ADAPTER) : (ha->flags &= ~MULTI_CHIP_ADAPTER); ADAPTER_STATE_UNLOCK(ha); /* Get driver properties. */ ql_23_properties(ha, icb); /* * Setup driver firmware options. */ icb->firmware_options[0] = (uint8_t) (icb->firmware_options[0] | BIT_6 | BIT_1); /* * There is no use enabling fast post for SBUS or 2300 * Always enable 64bit addressing, except SBUS cards. */ ha->cfg_flags |= CFG_ENABLE_64BIT_ADDRESSING; if (CFG_IST(ha, CFG_SBUS_CARD | CFG_CTRL_2363)) { icb->firmware_options[0] = (uint8_t) (icb->firmware_options[0] & ~BIT_3); if (CFG_IST(ha, CFG_SBUS_CARD)) { icb->special_options[0] = (uint8_t) (icb->special_options[0] | BIT_5); ha->cfg_flags &= ~CFG_ENABLE_64BIT_ADDRESSING; } } else { icb->firmware_options[0] = (uint8_t) (icb->firmware_options[0] | BIT_3); } /* RIO and ZIO not supported. */ icb->add_fw_opt[0] = (uint8_t)(icb->add_fw_opt[0] & ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); icb->firmware_options[1] = (uint8_t)(icb->firmware_options[1] | BIT_7 | BIT_6 | BIT_5 | BIT_2 | BIT_0); icb->firmware_options[0] = (uint8_t) (icb->firmware_options[0] & ~(BIT_5 | BIT_4)); icb->firmware_options[1] = (uint8_t) (icb->firmware_options[1] & ~BIT_4); if (CFG_IST(ha, CFG_ENABLE_FCP_2_SUPPORT)) { icb->firmware_options[1] = (uint8_t) (icb->firmware_options[1] | BIT_7 | BIT_6); icb->add_fw_opt[1] = (uint8_t) (icb->add_fw_opt[1] | BIT_5 | BIT_4); } icb->add_fw_opt[1] = (uint8_t)(icb->add_fw_opt[1] & ~(BIT_5 | BIT_4)); icb->special_options[0] = (uint8_t)(icb->special_options[0] | BIT_1); if (CFG_IST(ha, CFG_CTRL_2363)) { if ((icb->special_options[1] & 0x20) == 0) { EL(ha, "50 ohm is not set\n"); } } /* * Set host adapter parameters */ /* Get adapter id string for Sun branded 23xx only */ if (CFG_IST(ha, CFG_CTRL_23XX) && nv->adapInfo[0] != 0) { (void) snprintf((int8_t *)ha->adapInfo, 16, "%s", nv->adapInfo); } ha->r_a_tov = (uint16_t)(icb->login_timeout < R_A_TOV_DEFAULT ? R_A_TOV_DEFAULT : icb->login_timeout); els->common_service.rx_bufsize = CHAR_TO_SHORT( icb->max_frame_length[0], icb->max_frame_length[1]); bcopy((void *)icb->port_name, (void *)els->nport_ww_name.raw_wwn, 8); bcopy((void *)icb->node_name, (void *)els->node_ww_name.raw_wwn, 8); cmn_err(CE_CONT, "!Qlogic %s(%d) WWPN=%02x%02x%02x%02x" "%02x%02x%02x%02x : WWNN=%02x%02x%02x%02x%02x%02x%02x%02x\n", QL_NAME, ha->instance, els->nport_ww_name.raw_wwn[0], els->nport_ww_name.raw_wwn[1], els->nport_ww_name.raw_wwn[2], els->nport_ww_name.raw_wwn[3], els->nport_ww_name.raw_wwn[4], els->nport_ww_name.raw_wwn[5], els->nport_ww_name.raw_wwn[6], els->nport_ww_name.raw_wwn[7], els->node_ww_name.raw_wwn[0], els->node_ww_name.raw_wwn[1], els->node_ww_name.raw_wwn[2], els->node_ww_name.raw_wwn[3], els->node_ww_name.raw_wwn[4], els->node_ww_name.raw_wwn[5], els->node_ww_name.raw_wwn[6], els->node_ww_name.raw_wwn[7]); /* * Setup ring parameters in initialization control block */ cnt = ha->req_q[0]->req_entry_cnt; icb->request_q_length[0] = LSB(cnt); icb->request_q_length[1] = MSB(cnt); cnt = ha->rsp_queues[0]->rsp_entry_cnt; icb->response_q_length[0] = LSB(cnt); icb->response_q_length[1] = MSB(cnt); start_addr = ha->req_q[0]->req_ring.cookie.dmac_address; icb->request_q_address[0] = LSB(LSW(start_addr)); icb->request_q_address[1] = MSB(LSW(start_addr)); icb->request_q_address[2] = LSB(MSW(start_addr)); icb->request_q_address[3] = MSB(MSW(start_addr)); start_addr = ha->req_q[0]->req_ring.cookie.dmac_notused; icb->request_q_address[4] = LSB(LSW(start_addr)); icb->request_q_address[5] = MSB(LSW(start_addr)); icb->request_q_address[6] = LSB(MSW(start_addr)); icb->request_q_address[7] = MSB(MSW(start_addr)); start_addr = ha->rsp_queues[0]->rsp_ring.cookie.dmac_address; icb->response_q_address[0] = LSB(LSW(start_addr)); icb->response_q_address[1] = MSB(LSW(start_addr)); icb->response_q_address[2] = LSB(MSW(start_addr)); icb->response_q_address[3] = MSB(MSW(start_addr)); start_addr = ha->rsp_queues[0]->rsp_ring.cookie.dmac_notused; icb->response_q_address[4] = LSB(LSW(start_addr)); icb->response_q_address[5] = MSB(LSW(start_addr)); icb->response_q_address[6] = LSB(MSW(start_addr)); icb->response_q_address[7] = MSB(MSW(start_addr)); /* * Setup IP initialization control block */ ip_icb->version = IP_ICB_VERSION; if (CFG_IST(ha, CFG_ENABLE_64BIT_ADDRESSING)) { ip_icb->ip_firmware_options[0] = (uint8_t) (ip_icb->ip_firmware_options[0] | BIT_2 | BIT_0); } else { ip_icb->ip_firmware_options[0] = (uint8_t) (ip_icb->ip_firmware_options[0] | BIT_2); } cnt = RCVBUF_CONTAINER_CNT; ip_icb->queue_size[0] = LSB(cnt); ip_icb->queue_size[1] = MSB(cnt); start_addr = ha->rcv_ring.cookie.dmac_address; ip_icb->queue_address[0] = LSB(LSW(start_addr)); ip_icb->queue_address[1] = MSB(LSW(start_addr)); ip_icb->queue_address[2] = LSB(MSW(start_addr)); ip_icb->queue_address[3] = MSB(MSW(start_addr)); start_addr = ha->rcv_ring.cookie.dmac_notused; ip_icb->queue_address[4] = LSB(LSW(start_addr)); ip_icb->queue_address[5] = MSB(LSW(start_addr)); ip_icb->queue_address[6] = LSB(MSW(start_addr)); ip_icb->queue_address[7] = MSB(MSW(start_addr)); if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * Get NVRAM data word * Calculates word position in NVRAM and calls request routine to * get the word from NVRAM. * * Input: * ha = adapter state pointer. * address = NVRAM word address. * * Returns: * data word. * * Context: * Kernel context. */ uint16_t ql_get_nvram_word(ql_adapter_state_t *ha, uint32_t address) { uint32_t nv_cmd; uint16_t rval; QL_PRINT_4(ha, "started\n"); nv_cmd = address << 16; nv_cmd = nv_cmd | NV_READ_OP; rval = (uint16_t)ql_nvram_request(ha, nv_cmd); QL_PRINT_4(ha, "NVRAM data = %xh\n", rval); return (rval); } /* * NVRAM request * Sends read command to NVRAM and gets data from NVRAM. * * Input: * ha = adapter state pointer. * nv_cmd = Bit 26= start bit * Bit 25, 24 = opcode * Bit 23-16 = address * Bit 15-0 = write data * * Returns: * data word. * * Context: * Kernel context. */ static uint16_t ql_nvram_request(ql_adapter_state_t *ha, uint32_t nv_cmd) { uint8_t cnt; uint16_t reg_data; uint16_t data = 0; /* Send command to NVRAM. */ nv_cmd <<= 5; for (cnt = 0; cnt < 11; cnt++) { if (nv_cmd & BIT_31) { ql_nv_write(ha, NV_DATA_OUT); } else { ql_nv_write(ha, 0); } nv_cmd <<= 1; } /* Read data from NVRAM. */ for (cnt = 0; cnt < 16; cnt++) { WRT16_IO_REG(ha, nvram, NV_SELECT + NV_CLOCK); ql_nv_delay(); data <<= 1; reg_data = RD16_IO_REG(ha, nvram); if (reg_data & NV_DATA_IN) { data = (uint16_t)(data | BIT_0); } WRT16_IO_REG(ha, nvram, NV_SELECT); ql_nv_delay(); } /* Deselect chip. */ WRT16_IO_REG(ha, nvram, NV_DESELECT); ql_nv_delay(); return (data); } void ql_nv_write(ql_adapter_state_t *ha, uint16_t data) { WRT16_IO_REG(ha, nvram, (uint16_t)(data | NV_SELECT)); ql_nv_delay(); WRT16_IO_REG(ha, nvram, (uint16_t)(data | NV_SELECT | NV_CLOCK)); ql_nv_delay(); WRT16_IO_REG(ha, nvram, (uint16_t)(data | NV_SELECT)); ql_nv_delay(); } void ql_nv_delay(void) { drv_usecwait(NV_DELAY_COUNT); } /* * ql_nvram_24xx_config * ISP2400 nvram. * * Input: * ha: adapter state pointer. * ha->req_q[0]: request ring * * Output: * ha->init_ctrl_blk = initialization control block * host adapters parameters in host adapter block * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_nvram_24xx_config(ql_adapter_state_t *ha) { uint32_t index, addr; uint32_t chksum = 0, saved_chksum = 0; uint32_t *longptr; nvram_24xx_t nvram; int idpromlen; char idprombuf[32]; caddr_t src, dst; uint16_t w1; int rval; nvram_24xx_t *nv = (nvram_24xx_t *)&nvram; ql_init_24xx_cb_t *icb = (ql_init_24xx_cb_t *)&ha->init_ctrl_blk.cb24; ql_ip_init_24xx_cb_t *ip_icb = &ha->ip_init_ctrl_blk.cb24; la_els_logi_t *els = &ha->loginparams; QL_PRINT_10(ha, "started\n"); if ((rval = ql_lock_nvram(ha, &addr, LNF_NVRAM_DATA)) == QL_SUCCESS) { /* Get NVRAM data and calculate checksum. */ longptr = (uint32_t *)nv; chksum = saved_chksum = 0; for (index = 0; index < sizeof (nvram_24xx_t) / 4; index++) { rval = ql_24xx_read_flash(ha, addr++, longptr); if (rval != QL_SUCCESS) { EL(ha, "24xx_read_flash failed=%xh\n", rval); break; } saved_chksum = chksum; chksum += *longptr; LITTLE_ENDIAN_32(longptr); longptr++; } ql_release_nvram(ha); } /* Bad NVRAM data, set defaults parameters. */ if (rval != QL_SUCCESS || chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || (nv->nvram_version[0] | nv->nvram_version[1]) == 0) { cmn_err(CE_WARN, "%s(%d): NVRAM configuration failed, using " "driver defaults.", QL_NAME, ha->instance); EL(ha, "failed, rval=%xh, checksum=%xh, id=%c%c%c%c, " "nvram_version=%x\n", rval, chksum, nv->id[0], nv->id[1], nv->id[2], nv->id[3], CHAR_TO_SHORT(nv->nvram_version[0], nv->nvram_version[1])); saved_chksum = ~saved_chksum + 1; (void) ql_flash_errlog(ha, FLASH_ERRLOG_NVRAM_CHKSUM_ERR, 0, MSW(saved_chksum), LSW(saved_chksum)); /* Reset NVRAM data. */ bzero((void *)nv, sizeof (nvram_24xx_t)); /* * Set default initialization control block. */ nv->nvram_version[0] = LSB(ICB_24XX_VERSION); nv->nvram_version[1] = MSB(ICB_24XX_VERSION); nv->version[0] = 1; nv->max_frame_length[1] = 8; nv->execution_throttle[0] = 16; nv->exchange_count[0] = 128; nv->max_luns_per_target[0] = 8; idpromlen = 32; /*LINTED [Solaris DDI_DEV_T_ANY Lint warning]*/ if (rval = ddi_getlongprop_buf(DDI_DEV_T_ANY, ha->dip, DDI_PROP_CANSLEEP, "idprom", (caddr_t)idprombuf, &idpromlen) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s(%d) : Unable to read idprom " "property, rval=%x", QL_NAME, ha->instance, rval); nv->port_name[0] = 33; nv->port_name[3] = 224; nv->port_name[4] = 139; nv->port_name[7] = (uint8_t) (NAA_ID_IEEE_EXTENDED << 4 | ha->instance); } else { nv->port_name[2] = idprombuf[2]; nv->port_name[3] = idprombuf[3]; nv->port_name[4] = idprombuf[4]; nv->port_name[5] = idprombuf[5]; nv->port_name[6] = idprombuf[6]; nv->port_name[7] = idprombuf[7]; nv->port_name[0] = (uint8_t) (NAA_ID_IEEE_EXTENDED << 4 | ha->instance); } cmn_err(CE_WARN, "%s(%d): Unreliable HBA NVRAM, using default " "HBA parameters and temporary " "WWPN: %02x%02x%02x%02x%02x%02x%02x%02x", QL_NAME, ha->instance, nv->port_name[0], nv->port_name[1], nv->port_name[2], nv->port_name[3], nv->port_name[4], nv->port_name[5], nv->port_name[6], nv->port_name[7]); nv->login_retry_count[0] = 8; nv->firmware_options_1[0] = BIT_2 | BIT_1; nv->firmware_options_1[1] = BIT_5; nv->firmware_options_2[0] = BIT_5; nv->firmware_options_2[1] = BIT_4; nv->firmware_options_3[1] = BIT_6; /* * Set default host adapter parameters */ nv->host_p[0] = BIT_4 | BIT_1; nv->host_p[1] = BIT_3 | BIT_2; nv->reset_delay = 5; nv->max_luns_per_target[0] = 128; nv->port_down_retry_count[0] = 30; nv->link_down_timeout[0] = 30; if (CFG_IST(ha, CFG_FCOE_SUPPORT)) { nv->firmware_options_3[2] = BIT_4; nv->feature_mask_l[0] = 9; nv->ext_blk.version[0] = 1; nv->ext_blk.fcf_vlan_match = 1; nv->ext_blk.fcf_vlan_id[0] = LSB(1002); nv->ext_blk.fcf_vlan_id[1] = MSB(1002); nv->fw.isp8001.e_node_mac_addr[1] = 2; nv->fw.isp8001.e_node_mac_addr[2] = 3; nv->fw.isp8001.e_node_mac_addr[3] = 4; nv->fw.isp8001.e_node_mac_addr[4] = MSB(ha->instance); nv->fw.isp8001.e_node_mac_addr[5] = LSB(ha->instance); } rval = QL_FUNCTION_FAILED; } /* Reset initialization control blocks. */ bzero((void *)icb, sizeof (ql_init_24xx_cb_t)); /* * Copy over NVRAM Firmware Initialization Control Block. */ dst = (caddr_t)icb; src = (caddr_t)&nv->version; index = (uint32_t)((uintptr_t)&icb->response_q_inpointer[0] - (uintptr_t)icb); while (index--) { *dst++ = *src++; } icb->login_retry_count[0] = nv->login_retry_count[0]; icb->login_retry_count[1] = nv->login_retry_count[1]; icb->link_down_on_nos[0] = nv->link_down_on_nos[0]; icb->link_down_on_nos[1] = nv->link_down_on_nos[1]; /* Copy 2nd half. */ dst = (caddr_t)&icb->interrupt_delay_timer; src = (caddr_t)&nv->interrupt_delay_timer; index = (uint32_t)((uintptr_t)&icb->qos - (uintptr_t)&icb->interrupt_delay_timer); while (index--) { *dst++ = *src++; } ha->execution_throttle = 16; ha->loop_reset_delay = nv->reset_delay; ha->port_down_retry_count = CHAR_TO_SHORT(nv->port_down_retry_count[0], nv->port_down_retry_count[1]); ha->maximum_luns_per_target = CHAR_TO_SHORT( nv->max_luns_per_target[0], nv->max_luns_per_target[1]); if (ha->maximum_luns_per_target == 0) { ha->maximum_luns_per_target++; } if (CFG_IST(ha, CFG_FCOE_SUPPORT)) { dst = (caddr_t)icb->enode_mac_addr; src = (caddr_t)nv->fw.isp8001.e_node_mac_addr; index = sizeof (nv->fw.isp8001.e_node_mac_addr); while (index--) { *dst++ = *src++; } dst = (caddr_t)&icb->ext_blk; src = (caddr_t)&nv->ext_blk; index = sizeof (ql_ext_icb_8100_t); while (index--) { *dst++ = *src++; } EL(ha, "e_node_mac_addr=%02x-%02x-%02x-%02x-%02x-%02x\n", icb->enode_mac_addr[0], icb->enode_mac_addr[1], icb->enode_mac_addr[2], icb->enode_mac_addr[3], icb->enode_mac_addr[4], icb->enode_mac_addr[5]); } /* Check for adapter node name (big endian). */ for (index = 0; index < 8; index++) { if (icb->node_name[index] != 0) { break; } } /* Copy port name if no node name (big endian). */ if (index == 8) { for (index = 0; index < 8; index++) { icb->node_name[index] = icb->port_name[index]; } icb->node_name[0] = (uint8_t)(icb->node_name[0] & ~BIT_0); icb->port_name[0] = (uint8_t)(icb->node_name[0] | BIT_0); } ADAPTER_STATE_LOCK(ha); ha->cfg_flags &= ~(CFG_ENABLE_FULL_LIP_LOGIN | CFG_ENABLE_TARGET_RESET | CFG_ENABLE_LIP_RESET | CFG_LOAD_FLASH_FW | CFG_FAST_TIMEOUT | CFG_DISABLE_RISC_CODE_LOAD | CFG_ENABLE_FWEXTTRACE | CFG_ENABLE_FWFCETRACE | CFG_SET_CACHE_LINE_SIZE_1 | CFG_LR_SUPPORT); if (nv->host_p[1] & BIT_2) { ha->cfg_flags |= CFG_ENABLE_FULL_LIP_LOGIN; } if (nv->host_p[1] & BIT_3) { ha->cfg_flags |= CFG_ENABLE_TARGET_RESET; } ha->flags &= ~MULTI_CHIP_ADAPTER; ADAPTER_STATE_UNLOCK(ha); /* Get driver properties. */ ql_24xx_properties(ha, icb); /* * Setup driver firmware options. */ if (!CFG_IST(ha, CFG_FCOE_SUPPORT)) { icb->firmware_options_1[0] = (uint8_t) (icb->firmware_options_1[0] | BIT_1); icb->firmware_options_1[1] = (uint8_t) (icb->firmware_options_1[1] | BIT_5 | BIT_2); icb->firmware_options_3[0] = (uint8_t) (icb->firmware_options_3[0] | BIT_1); } icb->firmware_options_1[0] = (uint8_t)(icb->firmware_options_1[0] & ~(BIT_5 | BIT_4)); icb->firmware_options_1[1] = (uint8_t)(icb->firmware_options_1[1] | BIT_6); icb->firmware_options_2[0] = (uint8_t)(icb->firmware_options_2[0] & ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); if (CFG_IST(ha, CFG_ENABLE_FCP_2_SUPPORT)) { icb->firmware_options_2[1] = (uint8_t) (icb->firmware_options_2[1] | BIT_4); } else { icb->firmware_options_2[1] = (uint8_t) (icb->firmware_options_2[1] & ~BIT_4); } icb->firmware_options_3[0] = (uint8_t)(icb->firmware_options_3[0] & ~BIT_7); /* * Set host adapter parameters */ w1 = CHAR_TO_SHORT(icb->login_timeout[0], icb->login_timeout[1]); ha->r_a_tov = (uint16_t)(w1 < R_A_TOV_DEFAULT ? R_A_TOV_DEFAULT : w1); ADAPTER_STATE_LOCK(ha); ha->cfg_flags |= CFG_ENABLE_64BIT_ADDRESSING; if (CFG_IST(ha, CFG_CTRL_81XX) && nv->enhanced_features[0] & BIT_0) { ha->cfg_flags |= CFG_LR_SUPPORT; } ADAPTER_STATE_UNLOCK(ha); /* Queue shadowing */ if (ha->flags & QUEUE_SHADOW_PTRS) { icb->firmware_options_2[3] = (uint8_t) (icb->firmware_options_2[3] | BIT_6 | BIT_5); } else { icb->firmware_options_2[3] = (uint8_t) (icb->firmware_options_2[3] | ~(BIT_6 | BIT_5)); } /* ISP2422 Serial Link Control */ if (CFG_IST(ha, CFG_CTRL_24XX)) { ha->serdes_param[0] = CHAR_TO_SHORT(nv->fw.isp2400.swing_opt[0], nv->fw.isp2400.swing_opt[1]); ha->serdes_param[1] = CHAR_TO_SHORT(nv->fw.isp2400.swing_1g[0], nv->fw.isp2400.swing_1g[1]); ha->serdes_param[2] = CHAR_TO_SHORT(nv->fw.isp2400.swing_2g[0], nv->fw.isp2400.swing_2g[1]); ha->serdes_param[3] = CHAR_TO_SHORT(nv->fw.isp2400.swing_4g[0], nv->fw.isp2400.swing_4g[1]); } els->common_service.rx_bufsize = CHAR_TO_SHORT( icb->max_frame_length[0], icb->max_frame_length[1]); bcopy((void *)icb->port_name, (void *)els->nport_ww_name.raw_wwn, 8); bcopy((void *)icb->node_name, (void *)els->node_ww_name.raw_wwn, 8); cmn_err(CE_CONT, "!Qlogic %s(%d) WWPN=%02x%02x%02x%02x" "%02x%02x%02x%02x : WWNN=%02x%02x%02x%02x%02x%02x%02x%02x\n", QL_NAME, ha->instance, els->nport_ww_name.raw_wwn[0], els->nport_ww_name.raw_wwn[1], els->nport_ww_name.raw_wwn[2], els->nport_ww_name.raw_wwn[3], els->nport_ww_name.raw_wwn[4], els->nport_ww_name.raw_wwn[5], els->nport_ww_name.raw_wwn[6], els->nport_ww_name.raw_wwn[7], els->node_ww_name.raw_wwn[0], els->node_ww_name.raw_wwn[1], els->node_ww_name.raw_wwn[2], els->node_ww_name.raw_wwn[3], els->node_ww_name.raw_wwn[4], els->node_ww_name.raw_wwn[5], els->node_ww_name.raw_wwn[6], els->node_ww_name.raw_wwn[7]); /* * Setup ring parameters in initialization control block */ w1 = ha->req_q[0]->req_entry_cnt; icb->request_q_length[0] = LSB(w1); icb->request_q_length[1] = MSB(w1); w1 = ha->rsp_queues[0]->rsp_entry_cnt; icb->response_q_length[0] = LSB(w1); icb->response_q_length[1] = MSB(w1); addr = ha->req_q[0]->req_ring.cookie.dmac_address; icb->request_q_address[0] = LSB(LSW(addr)); icb->request_q_address[1] = MSB(LSW(addr)); icb->request_q_address[2] = LSB(MSW(addr)); icb->request_q_address[3] = MSB(MSW(addr)); addr = ha->req_q[0]->req_ring.cookie.dmac_notused; icb->request_q_address[4] = LSB(LSW(addr)); icb->request_q_address[5] = MSB(LSW(addr)); icb->request_q_address[6] = LSB(MSW(addr)); icb->request_q_address[7] = MSB(MSW(addr)); addr = ha->rsp_queues[0]->rsp_ring.cookie.dmac_address; icb->response_q_address[0] = LSB(LSW(addr)); icb->response_q_address[1] = MSB(LSW(addr)); icb->response_q_address[2] = LSB(MSW(addr)); icb->response_q_address[3] = MSB(MSW(addr)); addr = ha->rsp_queues[0]->rsp_ring.cookie.dmac_notused; icb->response_q_address[4] = LSB(LSW(addr)); icb->response_q_address[5] = MSB(LSW(addr)); icb->response_q_address[6] = LSB(MSW(addr)); icb->response_q_address[7] = MSB(MSW(addr)); /* * Setup IP initialization control block */ ip_icb->version = IP_ICB_24XX_VERSION; ip_icb->ip_firmware_options[0] = (uint8_t) (ip_icb->ip_firmware_options[0] | BIT_2); if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_lock_nvram * Locks NVRAM access and returns starting address of NVRAM. * * Input: * ha: adapter state pointer. * addr: pointer for start address. * flags: Are mutually exclusive: * LNF_NVRAM_DATA --> get nvram * LNF_VPD_DATA --> get vpd data (24/25xx only). * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_lock_nvram(ql_adapter_state_t *ha, uint32_t *addr, uint32_t flags) { int i; QL_PRINT_3(ha, "started\n"); if ((flags & LNF_NVRAM_DATA) && (flags & LNF_VPD_DATA)) { EL(ha, "invalid options for function"); return (QL_FUNCTION_FAILED); } if (ha->device_id == 0x2312 || ha->device_id == 0x2322) { if ((flags & LNF_NVRAM_DATA) == 0) { EL(ha, "invalid 2312/2322 option for HBA"); return (QL_FUNCTION_FAILED); } /* if function number is non-zero, then adjust offset */ *addr = ha->flash_nvram_addr; /* Try to get resource lock. Wait for 10 seconds max */ for (i = 0; i < 10000; i++) { /* if nvram busy bit is reset, acquire sema */ if ((RD16_IO_REG(ha, nvram) & 0x8000) == 0) { WRT16_IO_REG(ha, host_to_host_sema, 1); drv_usecwait(MILLISEC); if (RD16_IO_REG(ha, host_to_host_sema) & 1) { break; } } drv_usecwait(MILLISEC); } if ((RD16_IO_REG(ha, host_to_host_sema) & 1) == 0) { cmn_err(CE_WARN, "%s(%d): unable to get NVRAM lock", QL_NAME, ha->instance); return (QL_FUNCTION_FAILED); } } else if (CFG_IST(ha, CFG_CTRL_24XX)) { if (flags & LNF_VPD_DATA) { *addr = NVRAM_DATA_ADDR | ha->flash_vpd_addr; } else if (flags & LNF_NVRAM_DATA) { *addr = NVRAM_DATA_ADDR | ha->flash_nvram_addr; } else { EL(ha, "invalid 2422 option for HBA"); return (QL_FUNCTION_FAILED); } GLOBAL_HW_LOCK(); } else if (CFG_IST(ha, CFG_CTRL_252780818283)) { if (flags & LNF_VPD_DATA) { *addr = ha->flash_data_addr | ha->flash_vpd_addr; } else if (flags & LNF_NVRAM_DATA) { *addr = ha->flash_data_addr | ha->flash_nvram_addr; } else { EL(ha, "invalid 2581 option for HBA"); return (QL_FUNCTION_FAILED); } GLOBAL_HW_LOCK(); } else { if ((flags & LNF_NVRAM_DATA) == 0) { EL(ha, "invalid option for HBA"); return (QL_FUNCTION_FAILED); } *addr = 0; GLOBAL_HW_LOCK(); } QL_PRINT_3(ha, "done\n"); return (QL_SUCCESS); } /* * ql_release_nvram * Releases NVRAM access. * * Input: * ha: adapter state pointer. * * Context: * Kernel context. */ void ql_release_nvram(ql_adapter_state_t *ha) { QL_PRINT_3(ha, "started\n"); if (ha->device_id == 0x2312 || ha->device_id == 0x2322) { /* Release resource lock */ WRT16_IO_REG(ha, host_to_host_sema, 0); } else { GLOBAL_HW_UNLOCK(); } QL_PRINT_3(ha, "done\n"); } /* * ql_23_properties * Copies driver properties to NVRAM or adapter structure. * * Driver properties are by design global variables and hidden * completely from administrators. Knowledgeable folks can * override the default values using driver.conf * * Input: * ha: adapter state pointer. * icb: Init control block structure pointer. * * Context: * Kernel context. */ static void ql_23_properties(ql_adapter_state_t *ha, ql_init_cb_t *icb) { uint32_t data, cnt; QL_PRINT_3(ha, "started\n"); /* Get frame payload size. */ if ((data = ql_get_prop(ha, "max-frame-length")) == 0xffffffff) { data = 2048; } if (data == 512 || data == 1024 || data == 2048) { icb->max_frame_length[0] = LSB(data); icb->max_frame_length[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'max-frame-length': " "%d; using nvram default of %d\n", data, CHAR_TO_SHORT( icb->max_frame_length[0], icb->max_frame_length[1])); } /* Get max IOCB allocation. */ icb->max_iocb_allocation[0] = 0; icb->max_iocb_allocation[1] = 1; /* Get execution throttle. */ if ((data = ql_get_prop(ha, "execution-throttle")) == 0xffffffff) { data = 32; } if (data != 0 && data < 65536) { icb->execution_throttle[0] = LSB(data); icb->execution_throttle[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'execution-throttle': " "%d; using nvram default of %d\n", data, CHAR_TO_SHORT( icb->execution_throttle[0], icb->execution_throttle[1])); } /* Get Login timeout. */ if ((data = ql_get_prop(ha, "login-timeout")) == 0xffffffff) { data = 3; } if (data < 256) { icb->login_timeout = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'login-timeout': " "%d; using nvram value of %d\n", data, icb->login_timeout); } /* Get retry count. */ if ((data = ql_get_prop(ha, "login-retry-count")) == 0xffffffff) { data = 4; } if (data < 256) { icb->login_retry_count = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'login-retry-count': " "%d; using nvram value of %d\n", data, icb->login_retry_count); } /* Get adapter hard loop ID enable. */ data = ql_get_prop(ha, "enable-adapter-hard-loop-ID"); if (data == 0) { icb->firmware_options[0] = (uint8_t)(icb->firmware_options[0] & ~BIT_0); } else if (data == 1) { icb->firmware_options[0] = (uint8_t)(icb->firmware_options[0] | BIT_0); } else if (data != 0xffffffff) { EL(ha, "invalid parameter value for " "'enable-adapter-hard-loop-ID': %d; using nvram value " "of %d\n", data, icb->firmware_options[0] & BIT_0 ? 1 : 0); } /* Get adapter hard loop ID. */ data = ql_get_prop(ha, "adapter-hard-loop-ID"); if (data < 126) { icb->hard_address[0] = (uint8_t)data; } else if (data != 0xffffffff) { EL(ha, "invalid parameter value for 'adapter-hard-loop-ID': " "%d; using nvram value of %d\n", data, icb->hard_address[0]); } /* Get LIP reset. */ if ((data = ql_get_prop(ha, "enable-LIP-reset-on-bus-reset")) == 0xffffffff) { data = 0; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_LIP_RESET; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_LIP_RESET; } else { EL(ha, "invalid parameter value for " "'enable-LIP-reset-on-bus-reset': %d; using nvram value " "of %d\n", data, CFG_IST(ha, CFG_ENABLE_LIP_RESET) ? 1 : 0); } /* Get LIP full login. */ if ((data = ql_get_prop(ha, "enable-LIP-full-login-on-bus-reset")) == 0xffffffff) { data = 1; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_FULL_LIP_LOGIN; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_FULL_LIP_LOGIN; } else { EL(ha, "invalid parameter value for " "'enable-LIP-full-login-on-bus-reset': %d; using nvram " "value of %d\n", data, CFG_IST(ha, CFG_ENABLE_FULL_LIP_LOGIN) ? 1 : 0); } /* Get target reset. */ if ((data = ql_get_prop(ha, "enable-target-reset-on-bus-reset")) == 0xffffffff) { data = 0; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_TARGET_RESET; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_TARGET_RESET; } else { EL(ha, "invalid parameter value for " "'enable-target-reset-on-bus-reset': %d; using nvram " "value of %d", data, CFG_IST(ha, CFG_ENABLE_TARGET_RESET) ? 1 : 0); } /* Get reset delay. */ if ((data = ql_get_prop(ha, "reset-delay")) == 0xffffffff) { data = 5; } if (data != 0 && data < 256) { ha->loop_reset_delay = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'reset-delay': %d; " "using nvram value of %d", data, ha->loop_reset_delay); } /* Get port down retry count. */ if ((data = ql_get_prop(ha, "port-down-retry-count")) == 0xffffffff) { data = 8; } if (data < 256) { ha->port_down_retry_count = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'port-down-retry-count':" " %d; using nvram value of %d\n", data, ha->port_down_retry_count); } /* Get connection mode setting. */ if ((data = ql_get_prop(ha, "connection-options")) == 0xffffffff) { data = 2; } cnt = CFG_IST(ha, CFG_CTRL_22XX) ? 3 : 2; if (data <= cnt) { icb->add_fw_opt[0] = (uint8_t)(icb->add_fw_opt[0] & ~(BIT_6 | BIT_5 | BIT_4)); icb->add_fw_opt[0] = (uint8_t)(icb->add_fw_opt[0] | (uint8_t)(data << 4)); } else { EL(ha, "invalid parameter value for 'connection-options': " "%d; using nvram value of %d\n", data, (icb->add_fw_opt[0] >> 4) & 0x3); } /* Get data rate setting. */ if ((CFG_IST(ha, CFG_CTRL_22XX)) == 0) { if ((data = ql_get_prop(ha, "fc-data-rate")) == 0xffffffff) { data = 2; } if (data < 3) { icb->special_options[1] = (uint8_t) (icb->special_options[1] & 0x3f); icb->special_options[1] = (uint8_t) (icb->special_options[1] | (uint8_t)(data << 6)); } else { EL(ha, "invalid parameter value for 'fc-data-rate': " "%d; using nvram value of %d\n", data, (icb->special_options[1] >> 6) & 0x3); } } /* Get IP FW container count. */ ha->ip_init_ctrl_blk.cb.cc[0] = LSB(ql_ip_buffer_count); ha->ip_init_ctrl_blk.cb.cc[1] = MSB(ql_ip_buffer_count); /* Get IP low water mark. */ ha->ip_init_ctrl_blk.cb.low_water_mark[0] = LSB(ql_ip_low_water); ha->ip_init_ctrl_blk.cb.low_water_mark[1] = MSB(ql_ip_low_water); /* Get IP fast register post count. */ ha->ip_init_ctrl_blk.cb.fast_post_reg_count[0] = ql_ip_fast_post_count; ADAPTER_STATE_LOCK(ha); ql_common_properties(ha); ADAPTER_STATE_UNLOCK(ha); QL_PRINT_3(ha, "done\n"); } /* * ql_common_properties * Driver properties adapter structure. * * Driver properties are by design global variables and hidden * completely from administrators. Knowledgeable folks can * override the default values using driver.conf * * Input: * ha: adapter state pointer. * * Context: * Kernel context. */ void ql_common_properties(ql_adapter_state_t *ha) { uint32_t data; QL_PRINT_10(ha, "started\n"); /* Get extended logging enable. */ if ((data = ql_get_prop(ha, "extended-logging")) == 0xffffffff || data == 0) { ha->cfg_flags &= ~CFG_ENABLE_EXTENDED_LOGGING; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_EXTENDED_LOGGING; } else { EL(ha, "invalid parameter value for 'extended-logging': %d;" " using default value of 0\n", data); ha->cfg_flags &= ~CFG_ENABLE_EXTENDED_LOGGING; } /* Get FCP 2 Error Recovery. */ if ((data = ql_get_prop(ha, "enable-FCP-2-error-recovery")) == 0xffffffff || data == 1) { ha->cfg_flags |= CFG_ENABLE_FCP_2_SUPPORT; } else if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_FCP_2_SUPPORT; } else { EL(ha, "invalid parameter value for " "'enable-FCP-2-error-recovery': %d; using nvram value of " "1\n", data); ha->cfg_flags |= CFG_ENABLE_FCP_2_SUPPORT; } #ifdef QL_DEBUG_LEVEL_2 ha->cfg_flags |= CFG_ENABLE_EXTENDED_LOGGING; #endif /* Get port down retry delay. */ if ((data = ql_get_prop(ha, "port-down-retry-delay")) == 0xffffffff) { ha->port_down_retry_delay = PORT_RETRY_TIME; } else if (data < 256) { ha->port_down_retry_delay = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'port-down-retry-delay':" " %d; using default value of %d", data, PORT_RETRY_TIME); ha->port_down_retry_delay = PORT_RETRY_TIME; } /* Get queue full retry count. */ if ((data = ql_get_prop(ha, "queue-full-retry-count")) == 0xffffffff) { ha->qfull_retry_count = 16; } else if (data < 256) { ha->qfull_retry_count = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'queue-full-retry-count':" " %d; using default value of 16", data); ha->qfull_retry_count = 16; } /* Get queue full retry delay. */ if ((data = ql_get_prop(ha, "queue-full-retry-delay")) == 0xffffffff) { ha->qfull_retry_delay = PORT_RETRY_TIME; } else if (data < 256) { ha->qfull_retry_delay = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'queue-full-retry-delay':" " %d; using default value of %d", data, PORT_RETRY_TIME); ha->qfull_retry_delay = PORT_RETRY_TIME; } /* Get loop down timeout. */ if ((data = ql_get_prop(ha, "link-down-timeout")) == 0xffffffff) { data = 0; } else if (data > 255) { EL(ha, "invalid parameter value for 'link-down-timeout': %d;" " using nvram value of 0\n", data); data = 0; } ha->loop_down_abort_time = (uint8_t)(LOOP_DOWN_TIMER_START - data); if (ha->loop_down_abort_time == LOOP_DOWN_TIMER_START) { ha->loop_down_abort_time--; } else if (ha->loop_down_abort_time <= LOOP_DOWN_TIMER_END) { ha->loop_down_abort_time = LOOP_DOWN_TIMER_END + 1; } /* Get link down error enable. */ if ((data = ql_get_prop(ha, "enable-link-down-error")) == 0xffffffff || data == 1) { ha->cfg_flags |= CFG_ENABLE_LINK_DOWN_REPORTING; } else if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_LINK_DOWN_REPORTING; } else { EL(ha, "invalid parameter value for 'link-down-error': %d;" " using default value of 1\n", data); } /* * Get firmware dump flags. * TAKE_FW_DUMP_ON_MAILBOX_TIMEOUT BIT_0 * TAKE_FW_DUMP_ON_ISP_SYSTEM_ERROR BIT_1 * TAKE_FW_DUMP_ON_DRIVER_COMMAND_TIMEOUT BIT_2 * TAKE_FW_DUMP_ON_LOOP_OFFLINE_TIMEOUT BIT_3 */ ha->cfg_flags &= ~(CFG_DUMP_MAILBOX_TIMEOUT | CFG_DUMP_ISP_SYSTEM_ERROR | CFG_DUMP_DRIVER_COMMAND_TIMEOUT | CFG_DUMP_LOOP_OFFLINE_TIMEOUT); if ((data = ql_get_prop(ha, "firmware-dump-flags")) != 0xffffffff) { if (data & BIT_0) { ha->cfg_flags |= CFG_DUMP_MAILBOX_TIMEOUT; } if (data & BIT_1) { ha->cfg_flags |= CFG_DUMP_ISP_SYSTEM_ERROR; } if (data & BIT_2) { ha->cfg_flags |= CFG_DUMP_DRIVER_COMMAND_TIMEOUT; } if (data & BIT_3) { ha->cfg_flags |= CFG_DUMP_LOOP_OFFLINE_TIMEOUT; } } /* Get the PCI max read request size override. */ ha->pci_max_read_req = 0; if ((data = ql_get_prop(ha, "pci-max-read-request")) != 0xffffffff && data != 0) { ha->pci_max_read_req = (uint16_t)(data); } /* Get the plogi retry params overrides. */ if ((data = ql_get_prop(ha, "plogi_params_retry_count")) != 0xffffffff && data != 0) { ha->plogi_params->retry_cnt = (uint32_t)(data); } if ((data = ql_get_prop(ha, "plogi_params_retry_delay")) != 0xffffffff && data != 0) { ha->plogi_params->retry_dly_usec = (uint32_t)(data); } /* * Set default fw wait, adjusted for slow FCF's. * Revisit when FCF's as fast as FC switches. */ ha->fwwait = (uint8_t)(CFG_IST(ha, CFG_FCOE_SUPPORT) ? 45 : 10); /* Get the attach fw_ready override value. */ if ((data = ql_get_prop(ha, "init-loop-sync-wait")) != 0xffffffff) { if (data > 0 && data <= 240) { ha->fwwait = (uint8_t)data; } else { EL(ha, "invalid parameter value for " "'init-loop-sync-wait': %d; using default " "value of %d\n", data, ha->fwwait); } } /* Get fm-capable property */ ha->fm_capabilities = DDI_FM_NOT_CAPABLE; if ((data = ql_get_prop(ha, "fm-capable")) != 0xffffffff) { if (data == 0) { ha->fm_capabilities = DDI_FM_NOT_CAPABLE; } else if (data > 0xf) { ha->fm_capabilities = 0xf; } else { ha->fm_capabilities = (int)(data); } } else { ha->fm_capabilities = (int)(DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE); } if ((data = ql_get_prop(ha, "msix-vectors")) == 0xffffffff) { ha->mq_msix_vectors = 0; } else if (data < 256) { ha->mq_msix_vectors = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'msix-vectors': " "%d; using value of %d\n", data, 0); ha->mq_msix_vectors = 0; } /* Get number of completion threads. */ if ((data = ql_get_prop(ha, "completion-threads")) == 0xffffffff) { ha->completion_thds = 4; } else if (data < 256 && data >= 1) { ha->completion_thds = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'completion-threads':" " %d; using default value of %d", data, 4); ha->completion_thds = 4; } QL_PRINT_3(ha, "done\n"); } /* * ql_24xx_properties * Copies driver properties to NVRAM or adapter structure. * * Driver properties are by design global variables and hidden * completely from administrators. Knowledgeable folks can * override the default values using /etc/system. * * Input: * ha: adapter state pointer. * icb: Init control block structure pointer. * * Context: * Kernel context. */ static void ql_24xx_properties(ql_adapter_state_t *ha, ql_init_24xx_cb_t *icb) { uint32_t data; QL_PRINT_10(ha, "started\n"); /* Get frame size */ if ((data = ql_get_prop(ha, "max-frame-length")) == 0xffffffff) { data = 2048; } if (data == 512 || data == 1024 || data == 2048 || data == 2112) { icb->max_frame_length[0] = LSB(data); icb->max_frame_length[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'max-frame-length': %d;" " using nvram default of %d\n", data, CHAR_TO_SHORT( icb->max_frame_length[0], icb->max_frame_length[1])); } /* Get execution throttle. */ if ((data = ql_get_prop(ha, "execution-throttle")) == 0xffffffff) { data = 32; } if (data != 0 && data < 65536) { icb->execution_throttle[0] = LSB(data); icb->execution_throttle[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'execution-throttle':" " %d; using nvram default of %d\n", data, CHAR_TO_SHORT( icb->execution_throttle[0], icb->execution_throttle[1])); } /* Get Login timeout. */ if ((data = ql_get_prop(ha, "login-timeout")) == 0xffffffff) { data = 3; } if (data < 65536) { icb->login_timeout[0] = LSB(data); icb->login_timeout[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'login-timeout': %d; " "using nvram value of %d\n", data, CHAR_TO_SHORT( icb->login_timeout[0], icb->login_timeout[1])); } /* Get retry count. */ if ((data = ql_get_prop(ha, "login-retry-count")) == 0xffffffff) { data = 4; } if (data < 65536) { icb->login_retry_count[0] = LSB(data); icb->login_retry_count[1] = MSB(data); } else { EL(ha, "invalid parameter value for 'login-retry-count': " "%d; using nvram value of %d\n", data, CHAR_TO_SHORT( icb->login_retry_count[0], icb->login_retry_count[1])); } /* Get adapter hard loop ID enable. */ data = ql_get_prop(ha, "enable-adapter-hard-loop-ID"); if (data == 0) { icb->firmware_options_1[0] = (uint8_t)(icb->firmware_options_1[0] & ~BIT_0); } else if (data == 1) { icb->firmware_options_1[0] = (uint8_t)(icb->firmware_options_1[0] | BIT_0); } else if (data != 0xffffffff) { EL(ha, "invalid parameter value for " "'enable-adapter-hard-loop-ID': %d; using nvram value " "of %d\n", data, icb->firmware_options_1[0] & BIT_0 ? 1 : 0); } /* Get adapter hard loop ID. */ data = ql_get_prop(ha, "adapter-hard-loop-ID"); if (data < 126) { icb->hard_address[0] = LSB(data); icb->hard_address[1] = MSB(data); } else if (data != 0xffffffff) { EL(ha, "invalid parameter value for 'adapter-hard-loop-ID':" " %d; using nvram value of %d\n", data, CHAR_TO_SHORT( icb->hard_address[0], icb->hard_address[1])); } /* Get LIP reset. */ if ((data = ql_get_prop(ha, "enable-LIP-reset-on-bus-reset")) == 0xffffffff) { data = 0; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_LIP_RESET; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_LIP_RESET; } else { EL(ha, "invalid parameter value for " "'enable-LIP-reset-on-bus-reset': %d; using value of 0\n", data); } /* Get LIP full login. */ if ((data = ql_get_prop(ha, "enable-LIP-full-login-on-bus-reset")) == 0xffffffff) { data = 1; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_FULL_LIP_LOGIN; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_FULL_LIP_LOGIN; } else { EL(ha, "invalid parameter value for " "'enable-LIP-full-login-on-bus-reset': %d; using nvram " "value of %d\n", data, ha->cfg_flags & CFG_ENABLE_FULL_LIP_LOGIN ? 1 : 0); } /* Get target reset. */ if ((data = ql_get_prop(ha, "enable-target-reset-on-bus-reset")) == 0xffffffff) { data = 0; } if (data == 0) { ha->cfg_flags &= ~CFG_ENABLE_TARGET_RESET; } else if (data == 1) { ha->cfg_flags |= CFG_ENABLE_TARGET_RESET; } else { EL(ha, "invalid parameter value for " "'enable-target-reset-on-bus-reset': %d; using nvram " "value of %d", data, ha->cfg_flags & CFG_ENABLE_TARGET_RESET ? 1 : 0); } /* Get reset delay. */ if ((data = ql_get_prop(ha, "reset-delay")) == 0xffffffff) { data = 5; } if (data != 0 && data < 256) { ha->loop_reset_delay = (uint8_t)data; } else { EL(ha, "invalid parameter value for 'reset-delay': %d; " "using nvram value of %d", data, ha->loop_reset_delay); } /* Get port down retry count. */ if ((data = ql_get_prop(ha, "port-down-retry-count")) == 0xffffffff) { data = 8; } if (data < 256) { ha->port_down_retry_count = (uint16_t)data; } else { EL(ha, "invalid parameter value for 'port-down-retry-count':" " %d; using nvram value of %d\n", data, ha->port_down_retry_count); } if (!(CFG_IST(ha, CFG_FCOE_SUPPORT))) { uint32_t conn; /* Get connection mode setting. */ if ((conn = ql_get_prop(ha, "connection-options")) == 0xffffffff) { conn = 2; } if (conn <= 2) { icb->firmware_options_2[0] = (uint8_t) (icb->firmware_options_2[0] & ~(BIT_6 | BIT_5 | BIT_4)); icb->firmware_options_2[0] = (uint8_t) (icb->firmware_options_2[0] | (uint8_t)(conn << 4)); } else { EL(ha, "invalid parameter value for 'connection-" "options': %d; using nvram value of %d\n", conn, (icb->firmware_options_2[0] >> 4) & 0x3); } conn = icb->firmware_options_2[0] >> 4 & 0x3; if (conn == 0 && ha->max_vports > 125) { ha->max_vports = 125; } /* Get data rate setting. */ if ((data = ql_get_prop(ha, "fc-data-rate")) == 0xffffffff) { data = 2; } if ((CFG_IST(ha, CFG_CTRL_24XX) && data < 4) || (CFG_IST(ha, CFG_CTRL_25XX) && data < 5) || (CFG_IST(ha, CFG_CTRL_2783) && data < 6)) { if (CFG_IST(ha, CFG_CTRL_2783) && data == 5 && conn == 0) { EL(ha, "invalid parameter value for 'fc-data-" "rate': %d; using nvram value of %d\n", data, 2); data = 2; } icb->firmware_options_3[1] = (uint8_t) (icb->firmware_options_3[1] & 0x1f); icb->firmware_options_3[1] = (uint8_t) (icb->firmware_options_3[1] | (uint8_t)(data << 5)); } else { EL(ha, "invalid parameter value for 'fc-data-rate': " "%d; using nvram value of %d\n", data, (icb->firmware_options_3[1] >> 5) & 0x7); } } /* Get IP FW container count. */ ha->ip_init_ctrl_blk.cb24.cc[0] = LSB(ql_ip_buffer_count); ha->ip_init_ctrl_blk.cb24.cc[1] = MSB(ql_ip_buffer_count); /* Get IP low water mark. */ ha->ip_init_ctrl_blk.cb24.low_water_mark[0] = LSB(ql_ip_low_water); ha->ip_init_ctrl_blk.cb24.low_water_mark[1] = MSB(ql_ip_low_water); ADAPTER_STATE_LOCK(ha); /* Get enable flash load. */ if ((data = ql_get_prop(ha, "enable-flash-load")) == 0xffffffff || data == 0) { ha->cfg_flags &= ~CFG_LOAD_FLASH_FW; } else if (data == 1) { ha->cfg_flags |= CFG_LOAD_FLASH_FW; } else { EL(ha, "invalid parameter value for 'enable-flash-load': " "%d; using default value of 0\n", data); } /* Enable firmware extended tracing */ if ((data = ql_get_prop(ha, "enable-fwexttrace")) != 0xffffffff) { if (data != 0) { ha->cfg_flags |= CFG_ENABLE_FWEXTTRACE; } } /* Enable firmware fc tracing */ if ((data = ql_get_prop(ha, "enable-fwfcetrace")) != 0xffffffff) { ha->cfg_flags |= CFG_ENABLE_FWFCETRACE; ha->fwfcetraceopt = data; } /* Enable fast timeout */ if ((data = ql_get_prop(ha, "enable-fasttimeout")) != 0xffffffff) { if (data != 0) { ha->cfg_flags |= CFG_FAST_TIMEOUT; } } ql_common_properties(ha); ADAPTER_STATE_UNLOCK(ha); QL_PRINT_3(ha, "done\n"); } /* * ql_get_prop * Get property value from configuration file. * * Input: * ha= adapter state pointer. * string = property string pointer. * * Returns: * 0xFFFFFFFF = no property else property value. * * Context: * Kernel context. */ uint32_t ql_get_prop(ql_adapter_state_t *ha, char *string) { char buf[256]; uint32_t data = 0xffffffff; /* * Look for a adapter instance NPIV (virtual port) specific parameter */ if (CFG_IST(ha, CFG_ISP_FW_TYPE_2)) { (void) sprintf(buf, "hba%d-vp%d-%s", ha->instance, ha->vp_index, string); /*LINTED [Solaris DDI_DEV_T_ANY Lint warning]*/ data = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, ha->dip, 0, buf, (int)0xffffffff); } /* * Get adapter instance parameter if a vp specific one isn't found. */ if (data == 0xffffffff) { (void) sprintf(buf, "hba%d-%s", ha->instance, string); /*LINTED [Solaris DDI_DEV_T_ANY Lint warning]*/ data = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, ha->dip, 0, buf, (int)0xffffffff); } /* Adapter instance parameter found? */ if (data == 0xffffffff) { /* No, get default parameter. */ /*LINTED [Solaris DDI_DEV_T_ANY Lint warning]*/ data = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, ha->dip, 0, string, (int)0xffffffff); } return (data); } /* * ql_check_isp_firmware * Checks if using already loaded RISC code or drivers copy. * If using already loaded code, save a copy of it. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ static int ql_check_isp_firmware(ql_adapter_state_t *ha) { int rval; uint16_t word_count; uint32_t byte_count; uint32_t fw_size, *lptr; caddr_t bufp; uint16_t risc_address = (uint16_t)ha->risc_fw[0].addr; QL_PRINT_10(ha, "started\n"); /* Test for firmware running. */ if (CFG_IST(ha, CFG_CTRL_82XX)) { if ((rval = ql_8021_fw_chk(ha)) == QL_SUCCESS) { rval = ql_start_firmware(ha); } } else if (CFG_IST(ha, CFG_CTRL_278083)) { ha->dev_state = NX_DEV_READY; if (ha->rom_status == MBS_ROM_FW_RUNNING) { EL(ha, "ISP ROM Status = MBS_ROM_FW_RUNNING\n"); rval = QL_SUCCESS; } else if (ha->rom_status == MBS_ROM_IDLE) { EL(ha, "ISP ROM Status = MBS_ROM_IDLE\n"); rval = QL_FUNCTION_FAILED; } else { EL(ha, "ISP ROM Status, mbx0=%xh\n", ha->rom_status); rval = QL_FUNCTION_FAILED; } } else if (CFG_IST(ha, CFG_DISABLE_RISC_CODE_LOAD)) { ha->dev_state = NX_DEV_READY; if (ha->risc_code != NULL) { kmem_free(ha->risc_code, ha->risc_code_size); ha->risc_code = NULL; ha->risc_code_size = 0; } /* Get RISC code length. */ rval = ql_rd_risc_ram(ha, risc_address + 3, ha->req_q[0]->req_ring.cookie.dmac_laddress, 1); if (rval == QL_SUCCESS) { lptr = (uint32_t *)ha->req_q[0]->req_ring.bp; fw_size = *lptr << 1; if ((bufp = kmem_alloc(fw_size, KM_SLEEP)) != NULL) { ha->risc_code_size = fw_size; ha->risc_code = bufp; ha->fw_transfer_size = 128; /* Dump RISC code. */ do { if (fw_size > ha->fw_transfer_size) { byte_count = ha->fw_transfer_size; } else { byte_count = fw_size; } word_count = (uint16_t)(byte_count >> 1); rval = ql_rd_risc_ram(ha, risc_address, ha->req_q[0]->req_ring.cookie. dmac_laddress, word_count); if (rval != QL_SUCCESS) { kmem_free(ha->risc_code, ha->risc_code_size); ha->risc_code = NULL; ha->risc_code_size = 0; break; } (void) ddi_dma_sync( ha->req_q[0]->req_ring.dma_handle, 0, byte_count, DDI_DMA_SYNC_FORKERNEL); ddi_rep_get16( ha->req_q[0]->req_ring.acc_handle, (uint16_t *)bufp, (uint16_t *) ha->req_q[0]->req_ring.bp, word_count, DDI_DEV_AUTOINCR); risc_address += word_count; fw_size -= byte_count; bufp += byte_count; } while (fw_size != 0); } rval = QL_FUNCTION_FAILED; } } else { ha->dev_state = NX_DEV_READY; rval = QL_FUNCTION_FAILED; } if (rval != QL_SUCCESS) { EL(ha, "Load RISC code\n"); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_load_isp_firmware * Load and start RISC firmware. * Uses request ring for DMA buffer. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_load_isp_firmware(ql_adapter_state_t *vha) { caddr_t risc_code_address; uint32_t risc_address, risc_code_size; int rval = QL_FUNCTION_FAILED; uint32_t word_count, cnt; size_t byte_count; ql_adapter_state_t *ha = vha->pha; QL_PRINT_10(ha, "started\n"); if (CFG_IST(ha, CFG_CTRL_82XX)) { rval = ql_8021_reset_fw(ha) == NX_DEV_READY ? QL_SUCCESS : QL_FUNCTION_FAILED; } else { if (CFG_IST(ha, CFG_CTRL_81XX)) { ql_mps_reset(ha); } if (CFG_IST(ha, CFG_LOAD_FLASH_FW)) { QL_PRINT_10(ha, "CFG_LOAD_FLASH_FW exit\n"); return (ql_load_flash_fw(ha)); } if (CFG_IST(ha, CFG_CTRL_27XX)) { (void) ql_2700_get_module_dmp_template(ha); } /* Load firmware segments */ for (cnt = 0; cnt < MAX_RISC_CODE_SEGMENTS && ha->risc_fw[cnt].code != NULL; cnt++) { risc_code_address = ha->risc_fw[cnt].code; risc_address = ha->risc_fw[cnt].addr; if ((risc_address = ha->risc_fw[cnt].addr) == 0) { continue; } risc_code_size = ha->risc_fw[cnt].length; while (risc_code_size) { if (CFG_IST(ha, CFG_ISP_FW_TYPE_2)) { word_count = ha->fw_transfer_size >> 2; if (word_count > risc_code_size) { word_count = risc_code_size; } byte_count = word_count << 2; ddi_rep_put32( ha->req_q[0]->req_ring.acc_handle, (uint32_t *)risc_code_address, (uint32_t *) ha->req_q[0]->req_ring.bp, word_count, DDI_DEV_AUTOINCR); } else { word_count = ha->fw_transfer_size >> 1; if (word_count > risc_code_size) { word_count = risc_code_size; } byte_count = word_count << 1; ddi_rep_put16( ha->req_q[0]->req_ring.acc_handle, (uint16_t *)risc_code_address, (uint16_t *) ha->req_q[0]->req_ring.bp, word_count, DDI_DEV_AUTOINCR); } (void) ddi_dma_sync( ha->req_q[0]->req_ring.dma_handle, 0, byte_count, DDI_DMA_SYNC_FORDEV); rval = ql_wrt_risc_ram(ha, risc_address, ha->req_q[0]->req_ring.cookie.dmac_laddress, word_count); if (rval != QL_SUCCESS) { EL(ha, "failed, load=%xh\n", rval); cnt = MAX_RISC_CODE_SEGMENTS; break; } risc_address += word_count; risc_code_size -= word_count; risc_code_address += byte_count; } } } bzero(ha->req_q[0]->req_ring.bp, ha->fw_transfer_size); /* Start firmware. */ if (rval == QL_SUCCESS) { rval = ql_start_firmware(ha); } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_load_flash_fw * Gets ISP24xx firmware from flash and loads ISP. * * Input: * ha: adapter state pointer. * * Returns: * ql local function return status code. */ static int ql_load_flash_fw(ql_adapter_state_t *ha) { int rval; uint8_t seg_cnt; uint32_t risc_address, xfer_size, count, *bp, faddr; uint32_t risc_code_size = 0; QL_PRINT_10(ha, "started\n"); if (CFG_IST(ha, CFG_CTRL_278083)) { if ((rval = ql_load_flash_image(ha)) != QL_SUCCESS) { EL(ha, "load_flash_image status=%xh\n", rval); } else if (CFG_IST(ha, CFG_CTRL_27XX) && (rval = ql_2700_get_flash_dmp_template(ha)) != QL_SUCCESS) { EL(ha, "get_flash_dmp_template status=%xh\n", rval); } } else { faddr = ha->flash_data_addr | ha->flash_fw_addr; for (seg_cnt = 0; seg_cnt < 2; seg_cnt++) { xfer_size = ha->fw_transfer_size >> 2; do { GLOBAL_HW_LOCK(); /* Read data from flash. */ bp = (uint32_t *)ha->req_q[0]->req_ring.bp; for (count = 0; count < xfer_size; count++) { rval = ql_24xx_read_flash(ha, faddr++, bp); if (rval != QL_SUCCESS) { break; } ql_chg_endian((uint8_t *)bp++, 4); } GLOBAL_HW_UNLOCK(); if (rval != QL_SUCCESS) { EL(ha, "24xx_read_flash failed=%xh\n", rval); break; } if (risc_code_size == 0) { bp = (uint32_t *) ha->req_q[0]->req_ring.bp; risc_address = bp[2]; risc_code_size = bp[3]; ha->risc_fw[seg_cnt].addr = risc_address; } if (risc_code_size < xfer_size) { faddr -= xfer_size - risc_code_size; xfer_size = risc_code_size; } (void) ddi_dma_sync( ha->req_q[0]->req_ring.dma_handle, 0, xfer_size << 2, DDI_DMA_SYNC_FORDEV); rval = ql_wrt_risc_ram(ha, risc_address, ha->req_q[0]->req_ring.cookie.dmac_laddress, xfer_size); if (rval != QL_SUCCESS) { EL(ha, "ql_wrt_risc_ram failed=%xh\n", rval); break; } risc_address += xfer_size; risc_code_size -= xfer_size; } while (risc_code_size); if (rval != QL_SUCCESS) { break; } } } /* Start firmware. */ if (rval == QL_SUCCESS) { rval = ql_start_firmware(ha); } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_start_firmware * Starts RISC code. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_start_firmware(ql_adapter_state_t *vha) { int rval, rval2; uint32_t data; ql_mbx_data_t mr = {0}; ql_adapter_state_t *ha = vha->pha; ql_init_24xx_cb_t *icb = (ql_init_24xx_cb_t *)&ha->init_ctrl_blk.cb24; QL_PRINT_10(ha, "started\n"); if (CFG_IST(ha, CFG_CTRL_82XX)) { /* Save firmware version. */ rval = ql_get_fw_version(ha, &mr, MAILBOX_TOV); ha->fw_major_version = mr.mb[1]; ha->fw_minor_version = mr.mb[2]; ha->fw_subminor_version = mr.mb[3]; ha->fw_attributes = mr.mb[6]; } else if ((rval = ql_verify_checksum(ha)) == QL_SUCCESS) { /* Verify checksum of loaded RISC code. */ /* Start firmware execution. */ (void) ql_execute_fw(ha); /* Save firmware version. */ (void) ql_get_fw_version(ha, &mr, MAILBOX_TOV); ha->fw_major_version = mr.mb[1]; ha->fw_minor_version = mr.mb[2]; ha->fw_subminor_version = mr.mb[3]; ha->fw_ext_memory_end = SHORT_TO_LONG(mr.mb[4], mr.mb[5]); ha->fw_ext_memory_size = ((ha->fw_ext_memory_end - 0x100000) + 1) * 4; if (CFG_IST(ha, CFG_CTRL_278083)) { ha->fw_attributes = SHORT_TO_LONG(mr.mb[6], mr.mb[15]); ha->phy_fw_major_version = LSB(mr.mb[13]); ha->phy_fw_minor_version = MSB(mr.mb[14]); ha->phy_fw_subminor_version = LSB(mr.mb[14]); ha->fw_ext_attributes = SHORT_TO_LONG(mr.mb[16], mr.mb[17]); } else { ha->fw_attributes = mr.mb[6]; ha->phy_fw_major_version = LSB(mr.mb[8]); ha->phy_fw_minor_version = MSB(mr.mb[9]); ha->phy_fw_subminor_version = LSB(mr.mb[9]); ha->mpi_capability_list = SHORT_TO_LONG(mr.mb[13], mr.mb[12]); } ha->mpi_fw_major_version = LSB(mr.mb[10]); ha->mpi_fw_minor_version = MSB(mr.mb[11]); ha->mpi_fw_subminor_version = LSB(mr.mb[11]); if (CFG_IST(ha, CFG_CTRL_27XX)) { ha->fw_shared_ram_start = SHORT_TO_LONG(mr.mb[18], mr.mb[19]); ha->fw_shared_ram_end = SHORT_TO_LONG(mr.mb[20], mr.mb[21]); ha->fw_ddr_ram_start = SHORT_TO_LONG(mr.mb[22], mr.mb[23]); ha->fw_ddr_ram_end = SHORT_TO_LONG(mr.mb[24], mr.mb[25]); } if (CFG_IST(ha, CFG_FLASH_ACC_SUPPORT)) { if ((rval2 = ql_flash_access(ha, FAC_GET_SECTOR_SIZE, 0, 0, &data)) == QL_SUCCESS) { ha->xioctl->fdesc.block_size = data << 2; QL_PRINT_10(ha, "fdesc.block_size=" "%xh\n", ha->xioctl->fdesc.block_size); } else { EL(ha, "flash_access status=%xh\n", rval2); } } /* Set Serdes Transmit Parameters. */ if (CFG_IST(ha, CFG_CTRL_24XX) && ha->serdes_param[0] & BIT_0) { mr.mb[1] = ha->serdes_param[0]; mr.mb[2] = ha->serdes_param[1]; mr.mb[3] = ha->serdes_param[2]; mr.mb[4] = ha->serdes_param[3]; (void) ql_serdes_param(ha, &mr); } } /* ETS workaround */ if (CFG_IST(ha, CFG_CTRL_81XX) && ql_enable_ets) { if (ql_get_firmware_option(ha, &mr) == QL_SUCCESS) { mr.mb[2] = (uint16_t) (mr.mb[2] | FO2_FCOE_512_MAX_MEM_WR_BURST); (void) ql_set_firmware_option(ha, &mr); } } if (ha->flags & MULTI_QUEUE) { QL_PRINT_10(ha, "MULTI_QUEUE\n"); icb->msi_x_vector[0] = LSB(ha->rsp_queues[0]->msi_x_vector); icb->msi_x_vector[1] = MSB(ha->rsp_queues[0]->msi_x_vector); if (ha->iflags & IFLG_INTR_MSIX && CFG_IST(ha, CFG_NO_INTR_HSHAKE_SUP)) { QL_PRINT_10(ha, "NO_INTR_HANDSHAKE\n"); ADAPTER_STATE_LOCK(ha); ha->flags |= NO_INTR_HANDSHAKE; ADAPTER_STATE_UNLOCK(ha); icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] & ~(BIT_6 | BIT_5)); icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] | BIT_7); } else { icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] & ~BIT_5); icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] | BIT_7 | BIT_6); } } else { icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] & ~(BIT_7 | BIT_5)); icb->firmware_options_2[2] = (uint8_t) (icb->firmware_options_2[2] | BIT_6); } icb->firmware_options_2[3] = (uint8_t) (icb->firmware_options_2[3] & ~(BIT_1 | BIT_0)); /* Set fw execution throttle. */ if (CFG_IST(ha, CFG_CTRL_22XX) || ql_get_resource_cnts(ha, &mr) != QL_SUCCESS) { icb->execution_throttle[0] = 0xff; icb->execution_throttle[1] = 0xff; } else { icb->execution_throttle[0] = LSB(mr.mb[6]); icb->execution_throttle[1] = MSB(mr.mb[6]); } EL(ha, "icb->execution_throttle %d\n", CHAR_TO_SHORT(icb->execution_throttle[0], icb->execution_throttle[1])); if (rval != QL_SUCCESS) { ha->task_daemon_flags &= ~FIRMWARE_LOADED; EL(ha, "failed, rval = %xh\n", rval); } else { ha->task_daemon_flags |= FIRMWARE_LOADED; QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_set_cache_line * Sets PCI cache line parameter. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_set_cache_line(ql_adapter_state_t *ha) { QL_PRINT_3(ha, "started\n"); /* Set the cache line. */ if (CFG_IST(ha->pha, CFG_SET_CACHE_LINE_SIZE_1)) { /* Set cache line register. */ ql_pci_config_put8(ha->pha, PCI_CONF_CACHE_LINESZ, 1); } QL_PRINT_3(ha, "done\n"); return (QL_SUCCESS); } /* * ql_init_rings * Initializes firmware and ring pointers. * * Beginning of response ring has initialization control block * already built by nvram config routine. * * Input: * ha = adapter state pointer. * ha->req_q = request rings * ha->rsp_queues = response rings * ha->init_ctrl_blk = initialization control block * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_init_rings(ql_adapter_state_t *vha2) { int rval, rval2; uint16_t index; ql_mbx_data_t mr; ql_adapter_state_t *ha = vha2->pha; QL_PRINT_3(ha, "started\n"); /* Clear outstanding commands array. */ for (index = 0; index < ha->osc_max_cnt; index++) { ha->outstanding_cmds[index] = NULL; } ha->osc_index = 1; ha->pending_cmds.first = NULL; ha->pending_cmds.last = NULL; /* Initialize firmware. */ ha->req_q[0]->req_ring_ptr = ha->req_q[0]->req_ring.bp; ha->req_q[0]->req_ring_index = 0; ha->req_q[0]->req_q_cnt = REQUEST_ENTRY_CNT - 1; ha->rsp_queues[0]->rsp_ring_ptr = ha->rsp_queues[0]->rsp_ring.bp; ha->rsp_queues[0]->rsp_ring_index = 0; if (ha->flags & VP_ENABLED) { ql_adapter_state_t *vha; ql_init_24xx_cb_t *icb = &ha->init_ctrl_blk.cb24; bzero(icb->vp_count, ((uintptr_t)icb + sizeof (ql_init_24xx_cb_t)) - (uintptr_t)icb->vp_count); icb->vp_count[0] = ha->max_vports - 1; /* Allow connection option 2. */ icb->global_vp_option[0] = BIT_1; /* Setup default options for all ports. */ for (index = 0; index < ha->max_vports; index++) { icb->vpc[index].options = VPO_TARGET_MODE_DISABLED | VPO_INITIATOR_MODE_ENABLED; } /* Setup enabled ports. */ for (vha = ha->vp_next; vha != NULL; vha = vha->vp_next) { if (vha->vp_index == 0 || vha->vp_index >= ha->max_vports) { continue; } index = (uint8_t)(vha->vp_index - 1); bcopy(vha->loginparams.node_ww_name.raw_wwn, icb->vpc[index].node_name, 8); bcopy(vha->loginparams.nport_ww_name.raw_wwn, icb->vpc[index].port_name, 8); if (vha->flags & VP_ENABLED) { icb->vpc[index].options = (uint8_t) (icb->vpc[index].options | VPO_ENABLED); } } } for (index = 0; index < 2; index++) { rval = ql_init_firmware(ha); if (rval == QL_COMMAND_ERROR) { EL(ha, "stopping firmware\n"); (void) ql_stop_firmware(ha); } else { break; } } if (rval == QL_SUCCESS && CFG_IST(ha, CFG_ISP_FW_TYPE_1)) { /* Tell firmware to enable MBA_PORT_BYPASS_CHANGED event */ rval = ql_get_firmware_option(ha, &mr); if (rval == QL_SUCCESS) { mr.mb[1] = (uint16_t)(mr.mb[1] | BIT_9); mr.mb[2] = 0; mr.mb[3] = BIT_10; rval = ql_set_firmware_option(ha, &mr); } } if ((rval == QL_SUCCESS) && (CFG_IST(ha, CFG_ENABLE_FWFCETRACE))) { /* Firmware Fibre Channel Event Trace Buffer */ if ((rval2 = ql_get_dma_mem(ha, &ha->fwfcetracebuf, FWFCESIZE, LITTLE_ENDIAN_DMA, QL_DMA_RING_ALIGN)) != QL_SUCCESS) { EL(ha, "fcetrace buffer alloc failed: %xh\n", rval2); } else { if ((rval2 = ql_fw_etrace(ha, &ha->fwfcetracebuf, FTO_FCE_TRACE_ENABLE, NULL)) != QL_SUCCESS) { EL(ha, "fcetrace enable failed: %xh\n", rval2); ql_free_phys(ha, &ha->fwfcetracebuf); } } } if ((rval == QL_SUCCESS) && (CFG_IST(ha, CFG_ENABLE_FWEXTTRACE))) { /* Firmware Extended Trace Buffer */ if ((rval2 = ql_get_dma_mem(ha, &ha->fwexttracebuf, FWEXTSIZE, LITTLE_ENDIAN_DMA, QL_DMA_RING_ALIGN)) != QL_SUCCESS) { EL(ha, "exttrace buffer alloc failed: %xh\n", rval2); } else { if ((rval2 = ql_fw_etrace(ha, &ha->fwexttracebuf, FTO_EXT_TRACE_ENABLE, NULL)) != QL_SUCCESS) { EL(ha, "exttrace enable failed: %xh\n", rval2); ql_free_phys(ha, &ha->fwexttracebuf); } } } if (rval == QL_SUCCESS && CFG_IST(ha, CFG_CTRL_MENLO)) { ql_mbx_iocb_t *pkt; clock_t timer; /* Wait for firmware login of menlo. */ for (timer = 3000; timer; timer--) { if (ha->flags & MENLO_LOGIN_OPERATIONAL) { break; } if (!(ha->flags & INTERRUPTS_ENABLED) || ddi_in_panic()) { if (INTERRUPT_PENDING(ha)) { (void) ql_isr((caddr_t)ha); INTR_LOCK(ha); ha->intr_claimed = B_TRUE; INTR_UNLOCK(ha); } } /* Delay for 1 tick (10 milliseconds). */ ql_delay(ha, 10000); } if (timer == 0) { rval = QL_FUNCTION_TIMEOUT; } else { pkt = kmem_zalloc(sizeof (ql_mbx_iocb_t), KM_SLEEP); if (pkt == NULL) { EL(ha, "failed, kmem_zalloc\n"); rval = QL_MEMORY_ALLOC_FAILED; } else { pkt->mvfy.entry_type = VERIFY_MENLO_TYPE; pkt->mvfy.entry_count = 1; pkt->mvfy.options_status = LE_16(VMF_DO_NOT_UPDATE_FW); rval = ql_issue_mbx_iocb(ha, (caddr_t)pkt, sizeof (ql_mbx_iocb_t)); LITTLE_ENDIAN_16(&pkt->mvfy.options_status); LITTLE_ENDIAN_16(&pkt->mvfy.failure_code); if (rval != QL_SUCCESS || (pkt->mvfy.entry_status & 0x3c) != 0 || pkt->mvfy.options_status != CS_COMPLETE) { EL(ha, "failed, status=%xh, es=%xh, " "cs=%xh, fc=%xh\n", rval, pkt->mvfy.entry_status & 0x3c, pkt->mvfy.options_status, pkt->mvfy.failure_code); if (rval == QL_SUCCESS) { rval = QL_FUNCTION_FAILED; } } kmem_free(pkt, sizeof (ql_mbx_iocb_t)); } } } if (rval != QL_SUCCESS) { TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~FIRMWARE_UP; TASK_DAEMON_UNLOCK(ha); EL(ha, "failed, rval = %xh\n", rval); } else { TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= FIRMWARE_UP; TASK_DAEMON_UNLOCK(ha); QL_PRINT_3(ha, "done\n"); } return (rval); } /* * ql_fw_ready * Waits for firmware ready. If firmware becomes ready * device queues and RISC code are synchronized. * * Input: * ha = adapter state pointer. * secs = max wait time, in seconds (0-255). * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_fw_ready(ql_adapter_state_t *ha, uint8_t secs) { ql_mbx_data_t mr; clock_t timer, login_wait, wait; clock_t dly = 250000; clock_t sec_delay = MICROSEC / dly; int rval = QL_FUNCTION_FAILED; uint16_t state[6] = {0}; QL_PRINT_3(ha, "started\n"); login_wait = ha->r_a_tov * 2 * sec_delay; timer = wait = secs * sec_delay; state[0] = 0xffff; /* Wait for ISP to finish LIP */ while (login_wait != 0 && wait != 0 && !(ha->task_daemon_flags & ISP_ABORT_NEEDED) && !(ha->flags & MPI_RESET_NEEDED)) { rval = ql_get_firmware_state(ha, &mr); if (rval == QL_SUCCESS) { if (mr.mb[1] != FSTATE_READY) { if (mr.mb[1] == FSTATE_LOSS_SYNC && mr.mb[4] == FSTATE_MPI_NIC_ERROR && CFG_IST(ha, CFG_FCOE_SUPPORT)) { EL(ha, "mpi_nic_error, " "isp_abort_needed\n"); ADAPTER_STATE_LOCK(ha); ha->flags |= MPI_RESET_NEEDED; ADAPTER_STATE_UNLOCK(ha); if (!(ha->task_daemon_flags & ABORT_ISP_ACTIVE)) { TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= ISP_ABORT_NEEDED; TASK_DAEMON_UNLOCK(ha); } } if (mr.mb[1] != FSTATE_WAIT_LOGIN) { timer = --wait; } else { timer = --login_wait; } rval = QL_FUNCTION_FAILED; } else { /* Firmware is ready. Get 2 * R_A_TOV. */ rval = ql_get_timeout_parameters(ha, &ha->r_a_tov); if (rval != QL_SUCCESS) { EL(ha, "failed, get_timeout_param" "=%xh\n", rval); } /* Configure loop. */ rval = ql_configure_loop(ha); (void) ql_marker(ha, 0, 0, MK_SYNC_ALL); if (ha->task_daemon_flags & LOOP_RESYNC_NEEDED) { wait--; EL(ha, "loop trans; tdf=%xh\n", ha->task_daemon_flags); } else { break; } } } else { break; } if (state[0] != mr.mb[1] || state[1] != mr.mb[2] || state[2] != mr.mb[3] || state[3] != mr.mb[4] || state[4] != mr.mb[5] || state[5] != mr.mb[6]) { EL(ha, "mbx1=%xh, mbx2=%xh, mbx3=%xh, mbx4=%xh, " "mbx5=%xh, mbx6=%xh\n", mr.mb[1], mr.mb[2], mr.mb[3], mr.mb[4], mr.mb[5], mr.mb[6]); state[0] = mr.mb[1]; state[1] = mr.mb[2]; state[2] = mr.mb[3]; state[3] = mr.mb[4]; state[4] = mr.mb[5]; state[5] = mr.mb[6]; } /* Delay for a tick if waiting. */ if (timer != 0) { if (timer % 4 == 0) { delay(drv_usectohz(dly)); } else { drv_usecwait(dly); } } else { rval = QL_FUNCTION_TIMEOUT; } } if (rval != QL_SUCCESS) { if ((ha->task_daemon_flags & ISP_ABORT_NEEDED || ha->flags & MPI_RESET_NEEDED) && ha->task_daemon_flags & LOOP_RESYNC_NEEDED) { TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~LOOP_RESYNC_NEEDED; TASK_DAEMON_UNLOCK(ha); } EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_3(ha, "done\n"); } return (rval); } /* * ql_configure_loop * Setup configurations based on loop. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ static int ql_configure_loop(ql_adapter_state_t *ha) { int rval = QL_SUCCESS; ql_adapter_state_t *vha; QL_PRINT_10(ha, "started\n"); for (vha = ha; vha != NULL; vha = vha->vp_next) { TASK_DAEMON_LOCK(ha); if (!(vha->task_daemon_flags & LOOP_RESYNC_NEEDED) && vha->vp_index != 0 && (!(vha->flags & VP_ENABLED) || vha->flags & VP_ID_NOT_ACQUIRED)) { TASK_DAEMON_UNLOCK(ha); continue; } vha->task_daemon_flags &= ~LOOP_RESYNC_NEEDED; TASK_DAEMON_UNLOCK(ha); rval = ql_configure_hba(vha); if (rval == QL_SUCCESS && !(ha->task_daemon_flags & (LOOP_RESYNC_NEEDED | LOOP_DOWN))) { rval = ql_configure_device_d_id(vha); if (rval == QL_SUCCESS && !(ha->task_daemon_flags & (LOOP_RESYNC_NEEDED | LOOP_DOWN))) { (void) ql_configure_fabric(vha); } } } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_configure_n_port_info * Setup configurations based on N port 2 N port topology. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. * ADAPTER_STATE_LOCK must be already obtained */ static void ql_configure_n_port_info(ql_adapter_state_t *ha) { ql_tgt_t tmp_tq; ql_tgt_t *tq; uint8_t *cb_port_name; ql_link_t *link; int index, rval; uint16_t loop_id = 0; uint32_t found = 0; ql_dev_id_list_t *list; uint32_t list_size; ql_mbx_data_t mr; port_id_t d_id = {0, 0, 0, 0}; QL_PRINT_10(ha, "started\n"); /* Free existing target queues. */ for (index = 0; index < DEVICE_HEAD_LIST_SIZE; index++) { link = ha->dev[index].first; while (link != NULL) { tq = link->base_address; link = link->next; /* workaround FW issue, do implicit logout */ /* Never logo to the reused loopid!! */ if ((tq->loop_id != 0x7ff) && (tq->loop_id != 0x7fe)) { if (found == 0) { rval = ql_get_port_database(ha, tq, PDF_NONE); if ((rval == QL_SUCCESS) && (tq->master_state == PD_STATE_PORT_LOGGED_IN)) { EL(ha, "nport id (%xh) " "loop_id=%xh " "reappeared\n", tq->d_id.b24, tq->loop_id); bcopy((void *)&tq->port_name[0], (void *)&ha->n_port-> port_name[0], 8); bcopy((void *)&tq->node_name[0], (void *)&ha->n_port-> node_name[0], 8); ha->n_port->d_id.b24 = tq->d_id.b24; found = 1; continue; } } (void) ql_logout_fabric_port(ha, tq); } tq->loop_id = PORT_NO_LOOP_ID; } } if (found == 1) { QL_PRINT_10(ha, "done found\n"); return; } tq = &tmp_tq; /* * If the N_Port's WWPN is larger than our's then it has the * N_Port login initiative. It will have determined that and * logged in with the firmware. This results in a device * database entry. In this situation we will later send up a PLOGI * by proxy for the N_Port to get things going. * * If the N_Ports WWPN is smaller then the firmware has the * N_Port login initiative and does a FLOGI in order to obtain the * N_Ports WWNN and WWPN. These names are required later * during Leadvilles FLOGI. No PLOGI is done by the firmware in * anticipation of a PLOGI via the driver from the upper layers. * Upon reciept of said PLOGI the driver issues an ELS PLOGI * pass-through command and the firmware assumes the s_id * and the N_Port assumes the d_id and Bob's your uncle. */ /* * In N port 2 N port topology the FW provides a port database entry at * loop_id 0x7fe which allows us to acquire the Ports WWPN. */ tq->d_id.b.al_pa = 0; tq->d_id.b.area = 0; tq->d_id.b.domain = 0; tq->loop_id = 0x7fe; rval = ql_get_port_database(ha, tq, PDF_NONE); /* * Only collect the P2P remote port information in the case of * QL_SUCCESS. FW should have always logged in (flogi) to remote * port at this point. */ if (rval == QL_SUCCESS) { cb_port_name = &ha->loginparams.nport_ww_name.raw_wwn[0]; if ((ql_wwn_cmp(ha, (la_wwn_t *)&tq->port_name[0], (la_wwn_t *)cb_port_name) == 1)) { EL(ha, "target port has N_Port login initiative\n"); } else { EL(ha, "host port has N_Port login initiative\n"); } /* Capture the N Ports WWPN */ bcopy((void *)&tq->port_name[0], (void *)&ha->n_port->port_name[0], 8); bcopy((void *)&tq->node_name[0], (void *)&ha->n_port->node_name[0], 8); /* Resolve an n_port_handle */ ha->n_port->n_port_handle = 0x7fe; } list_size = sizeof (ql_dev_id_list_t) * DEVICE_LIST_ENTRIES; list = (ql_dev_id_list_t *)kmem_zalloc(list_size, KM_SLEEP); if (ql_get_id_list(ha, (caddr_t)list, list_size, &mr) == QL_SUCCESS) { /* For the p2p mr.mb[1] must be 1 */ if (mr.mb[1] == 1) { index = 0; ql_dev_list(ha, list, index, &d_id, &loop_id); ha->n_port->n_port_handle = loop_id; tq->loop_id = loop_id; tq->d_id.b24 = d_id.b24; ha->n_port->d_id.b24 = d_id.b24; } else { for (index = 0; index <= LAST_LOCAL_LOOP_ID; index++) { /* resuse tq */ tq->loop_id = (uint16_t)index; rval = ql_get_port_database(ha, tq, PDF_NONE); if (rval == QL_NOT_LOGGED_IN) { if (tq->master_state == PD_STATE_PLOGI_PENDING) { ha->n_port-> n_port_handle = tq->loop_id; ha->n_port->d_id.b24 = tq->hard_addr.b24; break; } } else if (rval == QL_SUCCESS) { ha->n_port->n_port_handle = tq->loop_id; ha->n_port->d_id.b24 = tq->hard_addr.b24; break; } } if (index > LAST_LOCAL_LOOP_ID) { EL(ha, "P2P:exceeded last id, " "n_port_handle = %xh\n", ha->n_port->n_port_handle); ha->n_port->n_port_handle = 0; tq->loop_id = 0; } } } else { kmem_free(list, list_size); EL(ha, "ql_get_dev_list unsuccessful\n"); return; } /* with the tq->loop_id to get the port database */ rval = ql_get_port_database(ha, tq, PDF_NONE); if (rval == QL_NOT_LOGGED_IN) { if (tq->master_state == PD_STATE_PLOGI_PENDING) { bcopy((void *)&tq->port_name[0], (void *)&ha->n_port->port_name[0], 8); bcopy((void *)&tq->node_name[0], (void *)&ha->n_port->node_name[0], 8); bcopy((void *)&tq->hard_addr, (void *)&ha->n_port->d_id, sizeof (port_id_t)); ha->n_port->d_id.b24 = d_id.b24; } } else if (rval == QL_SUCCESS) { bcopy((void *)&tq->port_name[0], (void *)&ha->n_port->port_name[0], 8); bcopy((void *)&tq->node_name[0], (void *)&ha->n_port->node_name[0], 8); bcopy((void *)&tq->hard_addr, (void *)&ha->n_port->d_id, sizeof (port_id_t)); ha->n_port->d_id.b24 = d_id.b24; } kmem_free(list, list_size); EL(ha, "d_id = %xh, nport_handle = %xh, tq->loop_id = %xh", tq->d_id.b24, ha->n_port->n_port_handle, tq->loop_id); } /* * ql_configure_hba * Setup adapter context. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ static int ql_configure_hba(ql_adapter_state_t *ha) { uint8_t *bp; int rval; uint32_t state; ql_mbx_data_t mr; QL_PRINT_10(ha, "started\n"); /* Get host addresses. */ rval = ql_get_adapter_id(ha, &mr); if (rval == QL_SUCCESS) { ha->topology = (uint8_t)(ha->topology & ~(QL_N_PORT | QL_NL_PORT | QL_F_PORT | QL_FL_PORT)); /* Save Host d_id, alpa, loop ID. */ ha->loop_id = mr.mb[1]; ha->d_id.b.al_pa = LSB(mr.mb[2]); ha->d_id.b.area = MSB(mr.mb[2]); ha->d_id.b.domain = LSB(mr.mb[3]); ha->bbcr_initial = LSB(mr.mb[15]); ha->bbcr_runtime = MSB(mr.mb[15]); ADAPTER_STATE_LOCK(ha); ha->flags &= ~FDISC_ENABLED; ADAPTER_STATE_UNLOCK(ha); /* Get loop topology. */ switch (mr.mb[6]) { case GID_TOP_NL_PORT: ha->topology = (uint8_t)(ha->topology | QL_NL_PORT); ha->loop_id = mr.mb[1]; break; case GID_TOP_FL_PORT: ha->topology = (uint8_t)(ha->topology | QL_FL_PORT); ha->loop_id = mr.mb[1]; break; case GID_TOP_N_PORT: case GID_TOP_N_PORT_NO_TGT: ha->flags |= POINT_TO_POINT; ha->topology = (uint8_t)(ha->topology | QL_N_PORT); ha->loop_id = 0xffff; if (CFG_IST(ha, CFG_N2N_SUPPORT)) { ql_configure_n_port_info(ha); } break; case GID_TOP_F_PORT: ha->flags |= POINT_TO_POINT; ha->topology = (uint8_t)(ha->topology | QL_F_PORT); ha->loop_id = 0xffff; /* Get supported option. */ if (CFG_IST(ha, CFG_ISP_FW_TYPE_2) && mr.mb[7] & GID_FP_NPIV_SUPPORT) { ADAPTER_STATE_LOCK(ha); ha->flags |= FDISC_ENABLED; ADAPTER_STATE_UNLOCK(ha); } /* Get VLAN ID, mac address */ if (CFG_IST(ha, CFG_FCOE_SUPPORT)) { ha->flags |= FDISC_ENABLED; ha->fabric_params = mr.mb[7]; ha->fcoe_vlan_id = (uint16_t)(mr.mb[9] & 0xfff); ha->fcoe_fcf_idx = mr.mb[10]; ha->fcoe_vnport_mac[5] = MSB(mr.mb[11]); ha->fcoe_vnport_mac[4] = LSB(mr.mb[11]); ha->fcoe_vnport_mac[3] = MSB(mr.mb[12]); ha->fcoe_vnport_mac[2] = LSB(mr.mb[12]); ha->fcoe_vnport_mac[1] = MSB(mr.mb[13]); ha->fcoe_vnport_mac[0] = LSB(mr.mb[13]); } break; default: QL_PRINT_2(ha, "UNKNOWN topology=%xh, d_id=%xh\n", mr.mb[6], ha->d_id.b24); rval = QL_FUNCTION_FAILED; break; } if (CFG_IST(ha, CFG_CTRL_2363 | CFG_ISP_FW_TYPE_2)) { mr.mb[1] = 0; mr.mb[2] = 0; rval = ql_data_rate(ha, &mr); if (rval != QL_SUCCESS) { EL(ha, "data_rate status=%xh\n", rval); state = FC_STATE_FULL_SPEED; } else { ha->iidma_rate = mr.mb[1]; if (mr.mb[1] == IIDMA_RATE_1GB) { state = FC_STATE_1GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_2GB) { state = FC_STATE_2GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_4GB) { state = FC_STATE_4GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_8GB) { state = FC_STATE_8GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_10GB) { state = FC_STATE_10GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_16GB) { state = FC_STATE_16GBIT_SPEED; } else if (mr.mb[1] == IIDMA_RATE_32GB) { state = FC_STATE_32GBIT_SPEED; } else { state = 0; } } } else { ha->iidma_rate = IIDMA_RATE_1GB; state = FC_STATE_FULL_SPEED; } ha->state = FC_PORT_STATE_MASK(ha->state) | state; } else if (rval == MBS_COMMAND_ERROR) { EL(ha, "mbox cmd error, rval = %xh, mr.mb[1]=%hx\n", rval, mr.mb[1]); } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { bp = ha->loginparams.nport_ww_name.raw_wwn; EL(ha, "topology=%xh, hba port id=%xh, " "wwpn=%02x%02x%02x%02x%02x%02x%02x%02xh\n", ha->topology, ha->d_id.b24, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]); } return (rval); } /* * ql_configure_device_d_id * Updates device loop ID. * Also adds to device queue any new devices found on private loop. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ static int ql_configure_device_d_id(ql_adapter_state_t *ha) { port_id_t d_id; ql_link_t *link; int rval; int loop; ql_tgt_t *tq; ql_dev_id_list_t *list; uint32_t list_size; uint16_t index, loop_id; ql_mbx_data_t mr; uint8_t retries = MAX_DEVICE_LOST_RETRY; QL_PRINT_10(ha, "started\n"); list_size = sizeof (ql_dev_id_list_t) * DEVICE_LIST_ENTRIES; list = kmem_zalloc(list_size, KM_SLEEP); if (list == NULL) { rval = QL_MEMORY_ALLOC_FAILED; EL(ha, "failed, rval = %xh\n", rval); return (rval); } do { /* * Get data from RISC code d_id list to init each device queue. */ rval = ql_get_id_list(ha, (caddr_t)list, list_size, &mr); if (rval != QL_SUCCESS) { kmem_free(list, list_size); EL(ha, "failed, rval = %xh\n", rval); return (rval); } /* * Mark queues as unusable selectively. * If the current topology is AL, only fabric tgt queues * are marked as unusable and eventually removed. * If the current topology is P2P, all fabric tgt queues * are processed in ql_configure_n_port_info(). * If the current topology is Fabric, all previous created * non-fabric device should be marked as lost and eventually * should be removed. */ for (index = 0; index < DEVICE_HEAD_LIST_SIZE; index++) { for (link = ha->dev[index].first; link != NULL; link = link->next) { tq = link->base_address; if (VALID_DEVICE_ID(ha, tq->loop_id)) { DEVICE_QUEUE_LOCK(tq); if (!(tq->flags & TQF_PLOGI_PROGRS) && !(ha->topology & QL_N_PORT)) { tq->loop_id = (uint16_t) (tq->loop_id | PORT_LOST_ID); } if ((ha->topology & QL_NL_PORT) && (tq->flags & TQF_FABRIC_DEVICE)) { tq->loop_id = (uint16_t) (tq->loop_id | PORT_LOST_ID); } DEVICE_QUEUE_UNLOCK(tq); } } } /* If device not in queues add new queue. */ for (index = 0; index < mr.mb[1]; index++) { ql_dev_list(ha, list, index, &d_id, &loop_id); if (VALID_DEVICE_ID(ha, loop_id)) { ADAPTER_STATE_LOCK(ha); tq = ql_dev_init(ha, d_id, loop_id); ADAPTER_STATE_UNLOCK(ha); if (tq != NULL) { tq->loop_id = loop_id; /* Test for fabric device. */ if (ha->topology & QL_F_PORT || d_id.b.domain != ha->d_id.b.domain || d_id.b.area != ha->d_id.b.area) { tq->flags |= TQF_FABRIC_DEVICE; } if (ql_get_port_database(ha, tq, PDF_NONE) == QL_SUCCESS) { tq->loop_id = (uint16_t) (tq->loop_id & ~PORT_LOST_ID); } } } } /* 24xx does not report switch devices in ID list. */ if (CFG_IST(ha, CFG_ISP_FW_TYPE_2) && ha->topology & QL_FABRIC_CONNECTION) { d_id.b24 = FS_FABRIC_F_PORT; ADAPTER_STATE_LOCK(ha); tq = ql_dev_init(ha, d_id, FL_PORT_24XX_HDL); ADAPTER_STATE_UNLOCK(ha); if (tq != NULL) { tq->flags |= TQF_FABRIC_DEVICE; (void) ql_get_port_database(ha, tq, PDF_NONE); } d_id.b24 = FS_NAME_SERVER; ADAPTER_STATE_LOCK(ha); tq = ql_dev_init(ha, d_id, SNS_24XX_HDL); ADAPTER_STATE_UNLOCK(ha); if (tq != NULL) { tq->flags |= TQF_FABRIC_DEVICE; if (ha->vp_index != 0) { (void) ql_login_fport(ha, tq, SNS_24XX_HDL, LFF_NONE, NULL); } (void) ql_get_port_database(ha, tq, PDF_NONE); } } /* Allocate queue for broadcast. */ d_id.b24 = FS_BROADCAST; ADAPTER_STATE_LOCK(ha); (void) ql_dev_init(ha, d_id, (uint16_t) (CFG_IST(ha, CFG_ISP_FW_TYPE_2) ? BROADCAST_24XX_HDL : IP_BROADCAST_LOOP_ID)); ADAPTER_STATE_UNLOCK(ha); /* * Topology change (fabric<->p2p),(fabric<->al) * (al<->p2p) have to be taken care of. */ loop = FALSE; for (index = 0; index < DEVICE_HEAD_LIST_SIZE; index++) { ql_update_dev(ha, index); } if ((ha->topology & QL_NL_PORT) && (mr.mb[1] != 0)) { loop = FALSE; } else if (mr.mb[1] == 0 && !(ha->topology & QL_F_PORT)) { loop = TRUE; } /* Give devices time to recover. */ if (loop == TRUE) { drv_usecwait(1000000); } } while (retries-- && loop == TRUE && !(ha->pha->task_daemon_flags & LOOP_RESYNC_NEEDED)); kmem_free(list, list_size); if (rval != QL_SUCCESS) { EL(ha, "failed=%xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_dev_list * Gets device d_id and loop ID from firmware device list. * * Input: * ha: adapter state pointer. * list device list pointer. * index: list index of device data. * d_id: pointer for d_id data. * id: pointer for loop ID. * * Context: * Kernel context. */ void ql_dev_list(ql_adapter_state_t *ha, union ql_dev_id_list *list, uint32_t index, port_id_t *d_id, uint16_t *id) { if (CFG_IST(ha, CFG_ISP_FW_TYPE_2)) { struct ql_24_dev_id *list24 = (struct ql_24_dev_id *)list; d_id->b.al_pa = list24[index].al_pa; d_id->b.area = list24[index].area; d_id->b.domain = list24[index].domain; *id = CHAR_TO_SHORT(list24[index].n_port_hdl_l, list24[index].n_port_hdl_h); } else if (CFG_IST(ha, CFG_EXT_FW_INTERFACE)) { struct ql_ex_dev_id *list23 = (struct ql_ex_dev_id *)list; d_id->b.al_pa = list23[index].al_pa; d_id->b.area = list23[index].area; d_id->b.domain = list23[index].domain; *id = CHAR_TO_SHORT(list23[index].loop_id_l, list23[index].loop_id_h); } else { struct ql_dev_id *list22 = (struct ql_dev_id *)list; d_id->b.al_pa = list22[index].al_pa; d_id->b.area = list22[index].area; d_id->b.domain = list22[index].domain; *id = (uint16_t)list22[index].loop_id; } } /* * ql_configure_fabric * Setup fabric context. * * Input: * ha = adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ static int ql_configure_fabric(ql_adapter_state_t *ha) { port_id_t d_id; ql_tgt_t *tq; int rval = QL_FUNCTION_FAILED; QL_PRINT_10(ha, "started\n"); if (ha->topology & QL_FABRIC_CONNECTION) { /* Test switch fabric controller present. */ d_id.b24 = FS_FABRIC_F_PORT; tq = ql_d_id_to_queue(ha, d_id); if (tq != NULL) { /* Get port/node names of F_Port. */ (void) ql_get_port_database(ha, tq, PDF_NONE); d_id.b24 = FS_NAME_SERVER; tq = ql_d_id_to_queue(ha, d_id); if (tq != NULL) { (void) ql_get_port_database(ha, tq, PDF_NONE); rval = QL_SUCCESS; } } } if (rval != QL_SUCCESS) { EL(ha, "failed=%xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_reset_chip * Reset ISP chip. * * Input: * ha = adapter block pointer. * All activity on chip must be already stopped. * ADAPTER_STATE_LOCK must be released. * * Context: * Interrupt or Kernel context, no mailbox commands allowed. */ void ql_reset_chip(ql_adapter_state_t *vha) { uint32_t cnt; uint16_t cmd; ql_adapter_state_t *ha = vha->pha; QL_PRINT_10(ha, "started\n"); /* * accessing pci space while not powered can cause panic's * on some platforms (i.e. Sunblade 1000's) */ if (ha->power_level == PM_LEVEL_D3) { QL_PRINT_2(ha, "Low Power exit\n"); return; } /* Disable ISP interrupts. */ ql_disable_intr(ha); /* Reset all outbound mailbox registers */ for (cnt = 0; cnt < ha->reg_off->mbox_cnt; cnt++) { WRT16_IO_REG(ha, mailbox_in[cnt], (uint16_t)0); } if (CFG_IST(ha, CFG_CTRL_82XX)) { ha->timeout_cnt = 0; ql_8021_reset_chip(ha); QL_PRINT_10(ha, "8021 exit\n"); return; } if (CFG_IST(ha, CFG_ISP_FW_TYPE_2)) { ql_reset_24xx_chip(ha); QL_PRINT_10(ha, "24xx exit\n"); return; } QL_PRINT_10(ha, "CFG_ISP_FW_TYPE_1 reset\n"); /* * We are going to reset the chip in case of 2300. That might cause * a PBM ERR if a DMA transaction is in progress. One way of * avoiding it is to disable Bus Master operation before we start * the reset activity. */ cmd = (uint16_t)ql_pci_config_get16(ha, PCI_CONF_COMM); cmd = (uint16_t)(cmd & ~PCI_COMM_ME); ql_pci_config_put16(ha, PCI_CONF_COMM, cmd); /* Pause RISC. */ WRT16_IO_REG(ha, hccr, HC_PAUSE_RISC); for (cnt = 0; cnt < 30000; cnt++) { if ((RD16_IO_REG(ha, hccr) & HC_RISC_PAUSE) != 0) { break; } drv_usecwait(MILLISEC); } /* * A call to ql_isr() can still happen through * ql_mailbox_command(). So Mark that we are/(will-be) * running from rom code now. */ TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~(FIRMWARE_UP | FIRMWARE_LOADED); TASK_DAEMON_UNLOCK(ha); /* Select FPM registers. */ WRT16_IO_REG(ha, ctrl_status, 0x20); /* FPM Soft Reset. */ WRT16_IO_REG(ha, fpm_diag_config, 0x100); /* Toggle FPM reset for 2300 */ if (CFG_IST(ha, CFG_CTRL_2363)) { WRT16_IO_REG(ha, fpm_diag_config, 0); } /* Select frame buffer registers. */ WRT16_IO_REG(ha, ctrl_status, 0x10); /* Reset frame buffer FIFOs. */ if (CFG_IST(ha, CFG_CTRL_2363)) { WRT16_IO_REG(ha, fb_cmd, 0x00fc); /* read back fb_cmd until zero or 3 seconds max */ for (cnt = 0; cnt < 300000; cnt++) { if ((RD16_IO_REG(ha, fb_cmd) & 0xff) == 0) { break; } drv_usecwait(10); } } else { WRT16_IO_REG(ha, fb_cmd, 0xa000); } /* Select RISC module registers. */ WRT16_IO_REG(ha, ctrl_status, 0); /* Reset RISC module. */ WRT16_IO_REG(ha, hccr, HC_RESET_RISC); /* Reset ISP semaphore. */ WRT16_IO_REG(ha, semaphore, 0); /* Release RISC module. */ WRT16_IO_REG(ha, hccr, HC_RELEASE_RISC); /* Insure mailbox registers are free. */ WRT16_IO_REG(ha, hccr, HC_CLR_RISC_INT); WRT16_IO_REG(ha, hccr, HC_CLR_HOST_INT); /* clear the mailbox command pointer. */ INTR_LOCK(ha); ha->mcp = NULL; INTR_UNLOCK(ha); MBX_REGISTER_LOCK(ha); ha->mailbox_flags = (uint8_t)(ha->mailbox_flags & ~(MBX_BUSY_FLG | MBX_WANT_FLG | MBX_ABORT | MBX_INTERRUPT)); MBX_REGISTER_UNLOCK(ha); /* Bus Master is disabled so chip reset is safe. */ if (CFG_IST(ha, CFG_CTRL_2363)) { WRT16_IO_REG(ha, ctrl_status, ISP_RESET); drv_usecwait(MILLISEC); /* Wait for reset to finish. */ for (cnt = 0; cnt < 30000; cnt++) { if ((RD16_IO_REG(ha, ctrl_status) & ISP_RESET) == 0) { break; } drv_usecwait(MILLISEC); } } /* Wait for RISC to recover from reset. */ for (cnt = 0; cnt < 30000; cnt++) { if (RD16_IO_REG(ha, mailbox_out[0]) != MBS_ROM_BUSY) { break; } drv_usecwait(MILLISEC); } /* restore bus master */ cmd = (uint16_t)ql_pci_config_get16(ha, PCI_CONF_COMM); cmd = (uint16_t)(cmd | PCI_COMM_ME); ql_pci_config_put16(ha, PCI_CONF_COMM, cmd); /* Disable RISC pause on FPM parity error. */ WRT16_IO_REG(ha, hccr, HC_DISABLE_PARITY_PAUSE); if (CFG_IST(ha, CFG_CTRL_22XX) && RD16_IO_REG(ha, mailbox_out[7]) == 4) { ha->fw_transfer_size = 128; } /* Initialize probe registers */ if (CFG_IST(ha, CFG_SBUS_CARD)) { /* Pause RISC. */ WRT16_IO_REG(ha, hccr, HC_PAUSE_RISC); for (cnt = 0; cnt < 30000; cnt++) { if ((RD16_IO_REG(ha, hccr) & HC_RISC_PAUSE) != 0) { break; } else { drv_usecwait(MILLISEC); } } /* Select FPM registers. */ WRT16_IO_REG(ha, ctrl_status, 0x30); /* Set probe register */ WRT16_IO_REG(ha, mailbox_in[23], 0x204c); /* Select RISC module registers. */ WRT16_IO_REG(ha, ctrl_status, 0); /* Release RISC module. */ WRT16_IO_REG(ha, hccr, HC_RELEASE_RISC); } QL_PRINT_10(ha, "done\n"); } /* * ql_reset_24xx_chip * Reset ISP24xx chip. * * Input: * ha = adapter block pointer. * All activity on chip must be already stopped. * * Context: * Interrupt or Kernel context, no mailbox commands allowed. */ static void ql_reset_24xx_chip(ql_adapter_state_t *ha) { uint32_t timer, stat; QL_PRINT_10(ha, "started\n"); /* Shutdown DMA. */ if (CFG_IST(ha, CFG_MWB_4096_SUPPORT)) { WRT32_IO_REG(ha, ctrl_status, DMA_SHUTDOWN | MWB_4096_BYTES); } else { WRT32_IO_REG(ha, ctrl_status, DMA_SHUTDOWN); } /* Wait for DMA to stop. */ for (timer = 0; timer < 30000; timer++) { if ((RD32_IO_REG(ha, ctrl_status) & DMA_ACTIVE) == 0) { break; } drv_usecwait(100); } /* Stop the firmware. */ WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); WRT16_IO_REG(ha, mailbox_in[0], MBC_STOP_FIRMWARE); WRT16_IO_REG(ha, mailbox_in[1], 0); WRT16_IO_REG(ha, mailbox_in[2], 0); WRT16_IO_REG(ha, mailbox_in[3], 0); WRT16_IO_REG(ha, mailbox_in[4], 0); WRT16_IO_REG(ha, mailbox_in[5], 0); WRT16_IO_REG(ha, mailbox_in[6], 0); WRT16_IO_REG(ha, mailbox_in[7], 0); WRT16_IO_REG(ha, mailbox_in[8], 0); WRT32_IO_REG(ha, hccr, HC24_SET_HOST_INT); for (timer = 0; timer < 30000; timer++) { stat = RD32_IO_REG(ha, risc2host); if (stat & BIT_15) { if ((stat & 0xff) < 0x12) { WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); break; } WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); } drv_usecwait(100); } /* Reset the chip. */ WRT32_IO_REG(ha, ctrl_status, ISP_RESET); drv_usecwait(100); /* Wait for RISC to recover from reset. */ for (timer = 30000; timer; timer--) { ha->rom_status = RD16_IO_REG(ha, mailbox_out[0]); if (CFG_IST(ha, CFG_CTRL_278083)) { /* Wait for RISC to recover from reset. */ if ((ha->rom_status & MBS_ROM_STATUS_MASK) != MBS_ROM_BUSY) { break; } } else { /* Wait for idle status from ROM firmware. */ if (ha->rom_status == MBS_ROM_IDLE) { break; } } drv_usecwait(100); } /* Wait for reset to finish. */ for (timer = 0; timer < 30000; timer++) { if ((RD32_IO_REG(ha, ctrl_status) & ISP_RESET) == 0) { break; } drv_usecwait(100); } ha->adapter_stats->revlvl.isp2200 = RD16_IO_REG(ha, mailbox_out[4]); ha->adapter_stats->revlvl.risc = RD16_IO_REG(ha, mailbox_out[5]); ha->adapter_stats->revlvl.frmbfr = RD16_IO_REG(ha, mailbox_out[6]); ha->adapter_stats->revlvl.riscrom = RD16_IO_REG(ha, mailbox_out[8]); /* Insure mailbox registers are free. */ WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); WRT32_IO_REG(ha, hccr, HC24_CLR_HOST_INT); /* clear the mailbox command pointer. */ INTR_LOCK(ha); ha->mcp = NULL; INTR_UNLOCK(ha); /* Insure mailbox registers are free. */ MBX_REGISTER_LOCK(ha); ha->mailbox_flags = (uint8_t)(ha->mailbox_flags & ~(MBX_BUSY_FLG | MBX_WANT_FLG | MBX_ABORT | MBX_INTERRUPT)); MBX_REGISTER_UNLOCK(ha); if (ha->flags & MPI_RESET_NEEDED) { WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); WRT16_IO_REG(ha, mailbox_in[0], MBC_RESTART_MPI); WRT32_IO_REG(ha, hccr, HC24_SET_HOST_INT); for (timer = 0; timer < 30000; timer++) { stat = RD32_IO_REG(ha, risc2host); if (stat & BIT_15) { if ((stat & 0xff) < 0x12) { WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); break; } WRT32_IO_REG(ha, hccr, HC24_CLR_RISC_INT); } drv_usecwait(100); } ADAPTER_STATE_LOCK(ha); ha->flags &= ~MPI_RESET_NEEDED; ADAPTER_STATE_UNLOCK(ha); } QL_PRINT_10(ha, "done\n"); } /* * ql_abort_isp * Resets ISP and aborts all outstanding commands. * * Input: * ha = adapter state pointer. * DEVICE_QUEUE_LOCK must be released. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_abort_isp(ql_adapter_state_t *vha) { ql_link_t *link, *link2; uint16_t index; ql_tgt_t *tq; ql_lun_t *lq; int rval = QL_SUCCESS; ql_adapter_state_t *ha = vha->pha; boolean_t abort_loop_down = B_FALSE; QL_PRINT_2(ha, "started\n"); TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~ISP_ABORT_NEEDED; if (ha->task_daemon_flags & ABORT_ISP_ACTIVE || (ha->flags & ONLINE) == 0 || ha->flags & ADAPTER_SUSPENDED) { TASK_DAEMON_UNLOCK(ha); QL_PRINT_2(ha, "already active or suspended tdf=0x%llx, " "flgs=0x%llx\n", ha->task_daemon_flags, ha->flags); return (rval); } ha->task_daemon_flags |= ABORT_ISP_ACTIVE; ha->task_daemon_flags &= ~(MARKER_NEEDED | FIRMWARE_UP | FIRMWARE_LOADED); for (vha = ha; vha != NULL; vha = vha->vp_next) { vha->task_daemon_flags &= ~(COMMAND_WAIT_NEEDED | LOOP_RESYNC_NEEDED); vha->task_daemon_flags |= LOOP_DOWN; if (vha->loop_down_timer == LOOP_DOWN_TIMER_OFF) { abort_loop_down = B_TRUE; vha->loop_down_timer = LOOP_DOWN_TIMER_START; } } TASK_DAEMON_UNLOCK(ha); ql_port_state(ha, FC_STATE_OFFLINE, FC_STATE_CHANGE); if (ha->mailbox_flags & MBX_BUSY_FLG) { /* Acquire mailbox register lock. */ MBX_REGISTER_LOCK(ha); /* Wake up mailbox box routine. */ ha->mailbox_flags = (uint8_t)(ha->mailbox_flags | MBX_ABORT); cv_broadcast(&ha->cv_mbx_intr); /* Release mailbox register lock. */ MBX_REGISTER_UNLOCK(ha); /* Wait for mailbox. */ for (index = 100; index && ha->mailbox_flags & MBX_ABORT; index--) { delay(1); } } /* Wait for commands to end gracefully if not in panic. */ if (ha->flags & PARITY_ERROR) { ADAPTER_STATE_LOCK(ha); ha->flags &= ~PARITY_ERROR; ADAPTER_STATE_UNLOCK(ha); } else if (ddi_in_panic() == 0) { ql_cmd_wait(ha); } rval = QL_ABORTED; if (ha->flags & FW_DUMP_NEEDED) { rval = ql_binary_fw_dump(ha, TRUE); } /* Shutdown IP. */ if (ha->flags & IP_INITIALIZED) { (void) ql_shutdown_ip(ha); } if (ha->task_daemon_flags & ISP_ABORT_NEEDED) { TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~ISP_ABORT_NEEDED; TASK_DAEMON_UNLOCK(ha); } /* Reset the chip. */ if (rval != QL_SUCCESS) { rval = QL_SUCCESS; ql_reset_chip(ha); } /* * Even though we have waited for outstanding commands to complete, * except for ones marked SRB_COMMAND_TIMEOUT, and reset the ISP, * there could still be an interrupt thread active. The interrupt * lock will prevent us from getting an sp from the outstanding * cmds array that the ISR may be using. */ /* Place all commands in outstanding cmd list on device queue. */ ql_requeue_all_cmds(ha); /* * Clear per LUN active count, because there should not be * any IO outstanding at this time. */ for (vha = ha; vha != NULL; vha = vha->vp_next) { for (index = 0; index < DEVICE_HEAD_LIST_SIZE; index++) { link = vha->dev[index].first; while (link != NULL) { tq = link->base_address; link = link->next; DEVICE_QUEUE_LOCK(tq); tq->outcnt = 0; tq->flags &= ~TQF_QUEUE_SUSPENDED; for (link2 = tq->lun_queues.first; link2 != NULL; link2 = link2->next) { lq = link2->base_address; lq->lun_outcnt = 0; lq->flags &= ~LQF_UNTAGGED_PENDING; } DEVICE_QUEUE_UNLOCK(tq); } } } if ((rval = ql_check_isp_firmware(ha)) != QL_SUCCESS) { if (ha->dev_state != NX_DEV_READY) { EL(ha, "dev_state not ready\n"); } else if ((rval = ql_mbx_wrap_test(ha, NULL)) == QL_SUCCESS) { rval = ql_load_isp_firmware(ha); } } if (rval == QL_SUCCESS && (rval = ql_set_cache_line(ha)) == QL_SUCCESS && (rval = ql_init_rings(ha)) == QL_SUCCESS && (rval = ql_fw_ready(ha, 10)) == QL_SUCCESS) { /* Enable ISP interrupts. */ if (!(ha->flags & INTERRUPTS_ENABLED)) { ql_enable_intr(ha); } /* If reset abort needed that may have been set. */ TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~(ISP_ABORT_NEEDED | ABORT_ISP_ACTIVE); TASK_DAEMON_UNLOCK(ha); /* Set loop online, if it really is. */ ql_loop_online(ha); } else { /* Enable ISP interrupts. */ if (!(ha->flags & INTERRUPTS_ENABLED)) { ql_enable_intr(ha); } TASK_DAEMON_LOCK(ha); for (vha = ha; vha != NULL; vha = vha->vp_next) { vha->task_daemon_flags |= LOOP_DOWN; } ha->task_daemon_flags &= ~ISP_ABORT_NEEDED; TASK_DAEMON_UNLOCK(ha); ql_port_state(ha, FC_STATE_OFFLINE, FC_STATE_CHANGE); ql_abort_queues(ha); TASK_DAEMON_LOCK(ha); ha->task_daemon_flags &= ~ABORT_ISP_ACTIVE; TASK_DAEMON_UNLOCK(ha); } for (vha = ha; vha != NULL; vha = vha->vp_next) { if (!(vha->task_daemon_flags & LOOP_DOWN) && abort_loop_down == B_TRUE) { vha->loop_down_timer = LOOP_DOWN_TIMER_OFF; } } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_2(ha, "done\n"); } return (rval); } /* * ql_requeue_all_cmds * Requeue all commands. * * Input: * ha = virtual adapter state pointer. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ void ql_requeue_all_cmds(ql_adapter_state_t *ha) { ql_link_t *link; ql_tgt_t *tq; ql_lun_t *lq; ql_srb_t *sp; uint16_t index; /* Place all commands in outstanding cmd list on device queue. */ for (index = 1; index < ha->osc_max_cnt; index++) { INTR_LOCK(ha); REQUEST_RING_LOCK(ha); if ((link = ha->pending_cmds.first) != NULL) { sp = link->base_address; ql_remove_link(&ha->pending_cmds, &sp->cmd); REQUEST_RING_UNLOCK(ha); index = 0; } else { REQUEST_RING_UNLOCK(ha); if ((sp = ha->outstanding_cmds[index]) == NULL || sp == QL_ABORTED_SRB(ha)) { INTR_UNLOCK(ha); continue; } } /* * It's not obvious but the index for commands pulled from * pending will be zero and that entry in the outstanding array * is not used so nulling it is "no harm, no foul". */ ha->outstanding_cmds[index] = NULL; sp->handle = 0; sp->flags &= ~SRB_IN_TOKEN_ARRAY; INTR_UNLOCK(ha); /* If command timeout. */ if (sp->flags & SRB_COMMAND_TIMEOUT) { sp->pkt->pkt_reason = CS_TIMEOUT; sp->flags &= ~SRB_RETRY; sp->flags |= SRB_ISP_COMPLETED; /* Call done routine to handle completion. */ ql_done(&sp->cmd, B_FALSE); continue; } /* Acquire target queue lock. */ lq = sp->lun_queue; tq = lq->target_queue; /* return any tape IO as exchange dropped due to chip reset */ if (tq->flags & TQF_TAPE_DEVICE) { sp->pkt->pkt_reason = CS_TRANSPORT; sp->flags &= ~SRB_RETRY; sp->flags |= SRB_ISP_COMPLETED; EL(ha, "rtn seq IO, sp=%ph", sp); /* Call done routine to handle completion. */ ql_done(&sp->cmd, B_FALSE); continue; } DEVICE_QUEUE_LOCK(tq); /* Reset watchdog time. */ sp->wdg_q_time = sp->init_wdg_q_time; /* Place request back on top of device queue. */ sp->flags &= ~(SRB_ISP_STARTED | SRB_ISP_COMPLETED | SRB_RETRY); ql_add_link_t(&lq->cmd, &sp->cmd); sp->flags |= SRB_IN_DEVICE_QUEUE; /* Release target queue lock. */ DEVICE_QUEUE_UNLOCK(tq); } } /* * ql_vport_control * Issue Virtual Port Control command. * * Input: * ha = virtual adapter state pointer. * cmd = control command. * * Returns: * ql local function return status code. * * Context: * Kernel context. */ int ql_vport_control(ql_adapter_state_t *ha, uint8_t cmd) { ql_mbx_iocb_t *pkt; uint8_t bit; int rval; uint32_t pkt_size; QL_PRINT_10(ha, "started\n"); if (ha->vp_index != 0) { pkt_size = sizeof (ql_mbx_iocb_t); pkt = kmem_zalloc(pkt_size, KM_SLEEP); if (pkt == NULL) { EL(ha, "failed, kmem_zalloc\n"); return (QL_MEMORY_ALLOC_FAILED); } pkt->vpc.entry_type = VP_CONTROL_TYPE; pkt->vpc.entry_count = 1; pkt->vpc.command = cmd; pkt->vpc.vp_count = 1; pkt->vpc.fcf_index = ha->fcoe_fcf_idx; bit = (uint8_t)(ha->vp_index - 1); pkt->vpc.vp_index[bit / 8] = (uint8_t) (pkt->vpc.vp_index[bit / 8] | BIT_0 << bit % 8); rval = ql_issue_mbx_iocb(ha, (caddr_t)pkt, pkt_size); if (rval == QL_SUCCESS && pkt->vpc.status != 0) { rval = QL_COMMAND_ERROR; } kmem_free(pkt, pkt_size); } else { rval = QL_SUCCESS; } if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_vport_modify * Issue of Modify Virtual Port command. * * Input: * ha = virtual adapter state pointer. * cmd = command. * opt = option. * * Context: * Interrupt or Kernel context, no mailbox commands allowed. */ int ql_vport_modify(ql_adapter_state_t *ha, uint8_t cmd, uint8_t opt) { ql_mbx_iocb_t *pkt; int rval; uint32_t pkt_size; QL_PRINT_10(ha, "started\n"); if (ha->pha->task_daemon_flags & LOOP_DOWN) { QL_PRINT_10(ha, "loop_down\n"); return (QL_FUNCTION_FAILED); } pkt_size = sizeof (ql_mbx_iocb_t); pkt = kmem_zalloc(pkt_size, KM_SLEEP); if (pkt == NULL) { EL(ha, "failed, kmem_zalloc\n"); return (QL_MEMORY_ALLOC_FAILED); } pkt->vpm.entry_type = VP_MODIFY_TYPE; pkt->vpm.entry_count = 1; pkt->vpm.command = cmd; pkt->vpm.vp_count = 1; pkt->vpm.first_vp_index = ha->vp_index; pkt->vpm.first_options = opt; pkt->vpm.fcf_index = ha->fcoe_fcf_idx; bcopy(ha->loginparams.nport_ww_name.raw_wwn, pkt->vpm.first_port_name, 8); bcopy(ha->loginparams.node_ww_name.raw_wwn, pkt->vpm.first_node_name, 8); rval = ql_issue_mbx_iocb(ha, (caddr_t)pkt, pkt_size); if (rval == QL_SUCCESS && pkt->vpm.status != 0) { EL(ha, "failed, ql_issue_mbx_iocb=%xh, status=%xh\n", rval, pkt->vpm.status); rval = QL_COMMAND_ERROR; } kmem_free(pkt, pkt_size); if (rval != QL_SUCCESS) { EL(ha, "failed, rval = %xh\n", rval); } else { /*EMPTY*/ QL_PRINT_10(ha, "done\n"); } return (rval); } /* * ql_vport_enable * Enable virtual port. * * Input: * ha = virtual adapter state pointer. * * Context: * Kernel context. */ int ql_vport_enable(ql_adapter_state_t *ha) { int timer; QL_PRINT_10(ha, "started\n"); ha->state = FC_PORT_SPEED_MASK(ha->state) | FC_STATE_OFFLINE; TASK_DAEMON_LOCK(ha); ha->task_daemon_flags |= LOOP_DOWN; ha->task_daemon_flags &= ~(FC_STATE_CHANGE | STATE_ONLINE); TASK_DAEMON_UNLOCK(ha); ADAPTER_STATE_LOCK(ha); ha->flags |= VP_ENABLED; ha->flags &= ~VP_ID_NOT_ACQUIRED; ADAPTER_STATE_UNLOCK(ha); ha->fcoe_fcf_idx = 0; if (ql_vport_modify(ha, VPM_MODIFY_ENABLE, VPO_TARGET_MODE_DISABLED | VPO_INITIATOR_MODE_ENABLED | VPO_ENABLED) != QL_SUCCESS) { QL_PRINT_2(ha, "failed to enable virtual port\n"); return (QL_FUNCTION_FAILED); } if (!(ha->pha->task_daemon_flags & LOOP_DOWN)) { /* Wait for loop to come up. */ for (timer = 0; timer < 3000 && !(ha->task_daemon_flags & STATE_ONLINE); timer++) { if (ha->flags & VP_ID_NOT_ACQUIRED) { break; } delay(1); } } QL_PRINT_10(ha, "done\n"); return (QL_SUCCESS); } /* * ql_vport_create * Create virtual port context. * * Input: * ha: parent adapter state pointer. * index: virtual port index number. * * Context: * Kernel context. */ ql_adapter_state_t * ql_vport_create(ql_adapter_state_t *ha, uint8_t index) { ql_adapter_state_t *vha; QL_PRINT_10(ha, "started\n"); /* Inherit the parents data. */ vha = kmem_alloc(sizeof (ql_adapter_state_t), KM_SLEEP); ADAPTER_STATE_LOCK(ha); bcopy(ha, vha, sizeof (ql_adapter_state_t)); vha->pi_attrs = NULL; vha->ub_outcnt = 0; vha->ub_allocated = 0; vha->flags = 0; vha->task_daemon_flags = 0; ha->vp_next = vha; vha->pha = ha; vha->vp_index = index; ADAPTER_STATE_UNLOCK(ha); vha->hba.next = NULL; vha->hba.prev = NULL; vha->hba.base_address = vha; vha->state = FC_PORT_SPEED_MASK(ha->state) | FC_STATE_OFFLINE; vha->dev = kmem_zalloc(sizeof (*vha->dev) * DEVICE_HEAD_LIST_SIZE, KM_SLEEP); vha->ub_array = kmem_zalloc(sizeof (*vha->ub_array) * QL_UB_LIMIT, KM_SLEEP); QL_PRINT_10(ha, "done\n"); return (vha); } /* * ql_vport_destroy * Destroys virtual port context. * * Input: * ha = virtual adapter state pointer. * * Context: * Kernel context. */ void ql_vport_destroy(ql_adapter_state_t *ha) { ql_adapter_state_t *vha; QL_PRINT_10(ha, "started\n"); /* Remove port from list. */ ADAPTER_STATE_LOCK(ha); for (vha = ha->pha; vha != NULL; vha = vha->vp_next) { if (vha->vp_next == ha) { vha->vp_next = ha->vp_next; break; } } ADAPTER_STATE_UNLOCK(ha); if (ha->ub_array != NULL) { kmem_free(ha->ub_array, sizeof (*ha->ub_array) * QL_UB_LIMIT); } if (ha->dev != NULL) { kmem_free(ha->dev, sizeof (*vha->dev) * DEVICE_HEAD_LIST_SIZE); } kmem_free(ha, sizeof (ql_adapter_state_t)); QL_PRINT_10(ha, "done\n"); } /* * ql_mps_reset * Reset MPS for FCoE functions. * * Input: * ha = virtual adapter state pointer. * * Context: * Kernel context. */ static void ql_mps_reset(ql_adapter_state_t *ha) { uint32_t data, dctl = 1000; do { if (dctl-- == 0 || ql_wrt_risc_ram_word(ha, 0x7c00, 1) != QL_SUCCESS) { return; } if (ql_rd_risc_ram_word(ha, 0x7c00, &data) != QL_SUCCESS) { (void) ql_wrt_risc_ram_word(ha, 0x7c00, 0); return; } } while (!(data & BIT_0)); if (ql_rd_risc_ram_word(ha, 0x7A15, &data) == QL_SUCCESS) { dctl = (uint16_t)ql_pci_config_get16(ha, 0x54); if ((data & 0xe0) < (dctl & 0xe0)) { data &= 0xff1f; data |= dctl & 0xe0; (void) ql_wrt_risc_ram_word(ha, 0x7A15, data); } else if ((data & 0xe0) != (dctl & 0xe0)) { data &= 0xff1f; data |= dctl & 0xe0; (void) ql_wrt_risc_ram_word(ha, 0x7A15, data); } } (void) ql_wrt_risc_ram_word(ha, 0x7c00, 0); } /* * ql_update_dev * Updates device status on loop reconfigure. * * Input: * ha: adapter state pointer. * index: list index of device data. * * Context: * Kernel context. */ static void ql_update_dev(ql_adapter_state_t *ha, uint32_t index) { ql_link_t *link; ql_tgt_t *tq; int rval; QL_PRINT_3(ha, "started\n"); link = ha->dev[index].first; while (link != NULL) { tq = link->base_address; link = link->next; if (tq->loop_id & PORT_LOST_ID && !(tq->flags & (TQF_INITIATOR_DEVICE | TQF_FABRIC_DEVICE))) { tq->loop_id &= ~PORT_LOST_ID; if (VALID_DEVICE_ID(ha, tq->loop_id)) { /* implicit logo due to fw issue */ rval = ql_get_port_database(ha, tq, PDF_NONE); if (rval == QL_NOT_LOGGED_IN) { if (tq->master_state == PD_STATE_PORT_UNAVAILABLE) { (void) ql_logout_fabric_port( ha, tq); tq->loop_id = PORT_NO_LOOP_ID; } } else if (rval == QL_SUCCESS) { tq->loop_id = PORT_NO_LOOP_ID; } } } else if (ha->topology & QL_NL_PORT && tq->flags & TQF_FABRIC_DEVICE) { tq->loop_id &= ~PORT_LOST_ID; if (VALID_DEVICE_ID(ha, tq->loop_id)) { /* implicit logo due to fw issue */ rval = ql_get_port_database(ha, tq, PDF_NONE); if (rval == QL_NOT_LOGGED_IN) { if (tq->master_state == PD_STATE_PORT_UNAVAILABLE) { (void) ql_logout_fabric_port( ha, tq); /* * fabric to AL topo change */ tq->loop_id = PORT_NO_LOOP_ID; } } else if (rval == QL_SUCCESS) { /* * Normally this is 7fe, * Don't issue logo, it causes * logo in single tgt AL. */ tq->loop_id = PORT_NO_LOOP_ID; } } } } QL_PRINT_3(ha, "done\n"); }