1636 lines
49 KiB
C
1636 lines
49 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Huawei Hifc PCI Express Linux driver
|
|
* Copyright(c) 2017 Huawei Technologies Co., Ltd
|
|
*
|
|
*/
|
|
#include "hifc_module.h"
|
|
#include "hifc_chipitf.h"
|
|
#include "hifc_io.h"
|
|
#include "hifc_portmng.h"
|
|
#include "hifc_lld.h"
|
|
#include "hifc_cqm_object.h"
|
|
#include "hifc_cqm_main.h"
|
|
#include "hifc_mgmt.h"
|
|
#include "hifc_hba.h"
|
|
|
|
void hifc_flush_root_ctx(struct hifc_hba_s *v_hba);
|
|
void hifc_flush_srq_ctx(struct hifc_hba_s *v_hba);
|
|
void hifc_release_probe_index(unsigned int probe_index);
|
|
unsigned int hifc_assign_probe_index(unsigned int *v_probe_index);
|
|
int hifc_probe(struct hifc_lld_dev *lld_dev, void **uld_dev, char *uld_dev_name);
|
|
void hifc_remove(struct hifc_lld_dev *lld_dev, void *uld_dev);
|
|
void hifc_event(struct hifc_lld_dev *lld_dev, void *uld_dev,
|
|
struct hifc_event_info *event);
|
|
struct hifc_hba_s *hifc_hba[HIFC_HBA_PORT_MAX_NUM];
|
|
unsigned long probe_bit_map[HIFC_MAX_PROBE_PORT_NUM / HIFC_PORT_NUM_PER_TABLE];
|
|
static unsigned long card_num_bit_map[HIFC_MAX_PROBE_PORT_NUM /
|
|
HIFC_PORT_NUM_PER_TABLE];
|
|
static struct hifc_card_num_manage_s card_num_manage[HIFC_MAX_CARD_NUM];
|
|
/* probe global lock */
|
|
spinlock_t probe_spin_lock;
|
|
unsigned int max_parent_qpc_num;
|
|
|
|
static unsigned int hifc_port_config_set(void *v_hba,
|
|
enum unf_port_config_set_op_e op_code,
|
|
void *v_var_in);
|
|
static unsigned int hifc_port_config_get(void *v_hba,
|
|
enum unf_port_config_get_op_e op_code,
|
|
void *param_out);
|
|
static unsigned int hifc_sfp_switch(void *v_hba, void *v_para_in);
|
|
static unsigned int hifc_get_hba_pcie_link_state(void *v_hba,
|
|
void *v_link_state);
|
|
|
|
struct service_register_template_s service_cqm_temp = {
|
|
.scq_ctx_size = HIFC_SCQ_CNTX_SIZE,
|
|
/* srq, scq context_size configuration */
|
|
.srq_ctx_size = HIFC_SRQ_CNTX_SIZE,
|
|
/* the API of asynchronous event from TILE to driver */
|
|
.aeq_callback = hifc_process_aeqe,
|
|
};
|
|
|
|
/* default configuration: auto speed, auto topology, INI+TGT */
|
|
static struct unf_cfg_item_s hifc_port_cfg_parm[] = {
|
|
{ "port_id", 0, 0x110000, 0xffffff},
|
|
/* port mode:INI(0x20), TGT(0x10), BOTH(0x30) */
|
|
{ "port_mode", 0, 0x20, 0xff},
|
|
/* port topology, 0x3: loop, 0xc:p2p, 0xf:auto ,0x10:vn2vn */
|
|
{ "port_topology", 0, 0xf, 0x20},
|
|
/* alpa address of port */
|
|
{ "port_alpa", 0, 0xdead, 0xffff},
|
|
/* queue depth of originator registered to SCSI midlayer */
|
|
{ "max_queue_depth", 0, 512, 512},
|
|
{ "sest_num", 0, 4096, 4096},
|
|
{ "max_login", 0, 2048, 2048},
|
|
/* nodename from 32 bit to 64 bit */
|
|
{ "node_name_high", 0, 0x1000286e, 0xffffffff},
|
|
/* nodename from 0 bit to 31 bit */
|
|
{ "node_name_low", 0, 0xd4bbf12f, 0xffffffff},
|
|
/* portname from 32 bit to 64 bit */
|
|
{ "port_name_high", 0, 0x2000286e, 0xffffffff},
|
|
/* portname from 0 bit to 31 bit */
|
|
{ "port_name_low", 0, 0xd4bbf12f, 0xffffffff},
|
|
/* port speed 0:auto 1:1Gbps 2:2Gbps 3:4Gbps 4:8Gbps 5:16Gbps */
|
|
{ "port_speed", 0, 0, 32},
|
|
/* unit: us */
|
|
{ "interrupt_delay", 0, 0, 100},
|
|
{ "tape_support", 0, 0, 1}, /* tape support */
|
|
{ "End", 0, 0, 0}
|
|
};
|
|
|
|
struct unf_low_level_function_op_s hifc_fun_op = {
|
|
.low_level_type = UNF_HIFC_FC,
|
|
.name = "HIFC",
|
|
/* XID allocated from CM level */
|
|
.xchg_mgr_type = UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE,
|
|
.abts_xchg = UNF_NO_EXTRA_ABTS_XCHG,
|
|
.pass_through_flag = UNF_LOW_LEVEL_PASS_THROUGH_PORT_LOGIN,
|
|
.support_max_npiv_num = UNF_HIFC_MAXNPIV_NUM,
|
|
.chip_id = 0,
|
|
.support_max_speed = UNF_PORT_SPEED_32_G,
|
|
.support_max_rport = UNF_HIFC_MAXRPORT_NUM,
|
|
.sfp_type = UNF_PORT_TYPE_FC_SFP,
|
|
.rport_release_type = UNF_LOW_LEVEL_RELEASE_RPORT_ASYNC,
|
|
.sirt_page_mode = UNF_LOW_LEVEL_SIRT_PAGE_MODE_XCHG,
|
|
|
|
/* Link service */
|
|
.service_op = {
|
|
.pfn_unf_els_send = hifc_send_els_cmnd,
|
|
.pfn_unf_bls_send = hifc_send_bls_cmnd,
|
|
.pfn_unf_gs_send = hifc_send_gs_cmnd,
|
|
.pfn_unf_cmnd_send = hifc_send_scsi_cmnd,
|
|
.pfn_unf_release_rport_res = hifc_free_parent_resource,
|
|
.pfn_unf_flush_ini_resp_que = hifc_flush_ini_resp_queue,
|
|
.pfn_unf_alloc_rport_res = hifc_alloc_parent_resource,
|
|
.pfn_unf_rport_session_rst = hifc_rport_session_rst,
|
|
},
|
|
|
|
/* Port Mgr */
|
|
.port_mgr_op = {
|
|
.pfn_ll_port_config_set = hifc_port_config_set,
|
|
.pfn_ll_port_config_get = hifc_port_config_get,
|
|
.pfn_ll_port_diagnose = hifc_port_diagnose,
|
|
}
|
|
};
|
|
|
|
struct hifc_port_config_op_s {
|
|
enum unf_port_config_set_op_e op_code;
|
|
unsigned int (*pfn_hifc_operation)(void *v_hba, void *v_para_in);
|
|
};
|
|
|
|
struct hifc_port_config_op_s hifc_config_set_op[] = {
|
|
{ UNF_PORT_CFG_SET_SPEED, hifc_set_port_speed },
|
|
{ UNF_PORT_CFG_SET_TOPO, hifc_set_port_topo },
|
|
{ UNF_PORT_CFG_SET_BBSCN, hifc_set_port_bbscn },
|
|
{ UNF_PORT_CFG_SET_SFP_SWITCH, hifc_sfp_switch },
|
|
{ UNF_PORT_CFG_SET_PORT_SWITCH, hifc_sfp_switch },
|
|
{ UNF_PORT_CFG_SET_PORT_STATE, hifc_set_port_state },
|
|
{ UNF_PORT_CFG_UPDATE_WWN, NULL },
|
|
{ UNF_PORT_CFG_SET_FCP_CONF, hifc_set_port_fcp_conf },
|
|
{ UNF_PORT_CFG_SET_LOOP_ROLE, hifc_set_loop_role },
|
|
{ UNF_PORT_CFG_SET_MAX_SUPPORT_SPEED, hifc_set_max_support_speed },
|
|
{ UNF_PORT_CFG_UPDATE_FABRIC_PARAM, hifc_update_fabric_param },
|
|
{ UNF_PORT_CFG_UPDATE_PLOGI_PARAM, hifc_update_port_param },
|
|
{ UNF_PORT_CFG_UPDATE_FDISC_PARAM, NULL },
|
|
{ UNF_PORT_CFG_SAVE_HBA_INFO, hifc_save_hba_info },
|
|
{ UNF_PORT_CFG_SET_HBA_BASE_INFO, hifc_set_hba_base_info },
|
|
{ UNF_PORT_CFG_SET_FLASH_DATA_INFO, hifc_set_flash_data },
|
|
{ UNF_PORT_CFG_SET_BUTT, NULL }
|
|
};
|
|
|
|
struct hifc_port_cfg_get_op_s {
|
|
enum unf_port_config_get_op_e op_code;
|
|
unsigned int (*pfn_hifc_operation)(void *v_hba, void *param_out);
|
|
};
|
|
|
|
struct hifc_port_cfg_get_op_s hifc_config_get_op[] = {
|
|
{ UNF_PORT_CFG_GET_SPEED_CFG, hifc_get_speed_cfg },
|
|
{ UNF_PORT_CFG_GET_SPEED_ACT, hifc_get_speed_act },
|
|
{ UNF_PORT_CFG_GET_TOPO_CFG, hifc_get_topo_cfg },
|
|
{ UNF_PORT_CFG_GET_TOPO_ACT, hifc_get_topo_act },
|
|
{ UNF_PORT_CFG_GET_LOOP_MAP, hifc_get_loop_map },
|
|
{ UNF_PORT_CFG_GET_SFP_PRESENT, NULL },
|
|
{ UNF_PORT_CFG_GET_SFP_INFO, hifc_get_sfp_info },
|
|
{ UNF_PORT_CFG_GET_FW_VER, hifc_get_firmware_version },
|
|
{ UNF_PORT_CFG_GET_HW_VER, hifc_get_hardware_version },
|
|
{ UNF_PORT_CFG_GET_WORKBALE_BBCREDIT, hifc_get_work_bale_bbcredit },
|
|
{ UNF_PORT_CFG_GET_WORKBALE_BBSCN, hifc_get_work_bale_bbscn },
|
|
{ UNF_PORT_CFG_GET_LOOP_ALPA, hifc_get_loop_alpa },
|
|
{ UNF_PORT_CFG_GET_MAC_ADDR, hifc_get_chip_msg },
|
|
{ UNF_PORT_CFG_CLR_LESB, hifc_clear_port_error_code },
|
|
{ UNF_PORT_CFG_GET_LESB_THEN_CLR, hifc_get_and_clear_port_error_code},
|
|
{ UNF_PORT_CFG_GET_PORT_INFO, hifc_get_port_current_info },
|
|
{ UNF_PORT_CFG_GET_LED_STATE, hifc_get_lport_led },
|
|
{ UNF_PORT_CFG_GET_FEC, hifc_get_port_fec },
|
|
{ UNF_PORT_CFG_GET_PCIE_LINK_STATE, hifc_get_hba_pcie_link_state },
|
|
{ UNF_PORT_CFG_GET_FLASH_DATA_INFO, hifc_get_flash_data },
|
|
{ UNF_PORT_CFG_GET_BUTT, NULL }
|
|
};
|
|
|
|
static unsigned int hifc_port_config_set(void *v_phba,
|
|
enum unf_port_config_set_op_e op_code,
|
|
void *v_var_in)
|
|
{
|
|
unsigned int op_idx = 0;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, v_phba, return UNF_RETURN_ERROR);
|
|
|
|
for (op_idx = 0;
|
|
op_idx < sizeof(hifc_config_set_op) /
|
|
sizeof(struct hifc_port_config_op_s);
|
|
op_idx++) {
|
|
if (op_code == hifc_config_set_op[op_idx].op_code) {
|
|
if (!hifc_config_set_op[op_idx].pfn_hifc_operation) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN,
|
|
UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Null operation for configuration, opcode(0x%x), operation ID(0x%x)",
|
|
op_code, op_idx);
|
|
return UNF_RETURN_ERROR;
|
|
} else {
|
|
return hifc_config_set_op[op_idx].pfn_hifc_operation(v_phba, v_var_in);
|
|
}
|
|
}
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]No operation code for configuration, opcode(0x%x)",
|
|
op_code);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static unsigned int hifc_port_config_get(void *v_phba,
|
|
enum unf_port_config_get_op_e op_code,
|
|
void *v_para_out)
|
|
{
|
|
unsigned int op_idx = 0;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, v_phba, return UNF_RETURN_ERROR);
|
|
|
|
for (op_idx = 0;
|
|
op_idx < sizeof(hifc_config_get_op) /
|
|
sizeof(struct hifc_port_cfg_get_op_s);
|
|
op_idx++) {
|
|
if (op_code == hifc_config_get_op[op_idx].op_code) {
|
|
if (!hifc_config_get_op[op_idx].pfn_hifc_operation) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN,
|
|
UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Null operation to get configuration, opcode(0x%x), operation ID(0x%x)",
|
|
op_code, op_idx);
|
|
return UNF_RETURN_ERROR;
|
|
} else {
|
|
return hifc_config_get_op[op_idx].pfn_hifc_operation(v_phba, v_para_out);
|
|
}
|
|
}
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]No operation to get configuration, opcode(0x%x)",
|
|
op_code);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static unsigned int hifc_check_port_cfg(
|
|
const struct hifc_port_cfg_s *v_port_cfg)
|
|
{
|
|
int topo_condition, speed_condition;
|
|
/* About Work Topology */
|
|
topo_condition = ((v_port_cfg->port_topology != UNF_TOP_LOOP_MASK) &&
|
|
(v_port_cfg->port_topology != UNF_TOP_P2P_MASK) &&
|
|
(v_port_cfg->port_topology != UNF_TOP_AUTO_MASK));
|
|
if (topo_condition) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Configured port topology(0x%x) is incorrect",
|
|
v_port_cfg->port_topology);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* About Work Mode */
|
|
if (v_port_cfg->port_mode != UNF_PORT_MODE_INI) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Configured port mode(0x%x) is incorrect",
|
|
v_port_cfg->port_mode);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* About Work Speed */
|
|
speed_condition = ((v_port_cfg->port_speed != UNF_PORT_SPEED_AUTO) &&
|
|
(v_port_cfg->port_speed != UNF_PORT_SPEED_2_G) &&
|
|
(v_port_cfg->port_speed != UNF_PORT_SPEED_4_G) &&
|
|
(v_port_cfg->port_speed != UNF_PORT_SPEED_8_G) &&
|
|
(v_port_cfg->port_speed != UNF_PORT_SPEED_16_G) &&
|
|
(v_port_cfg->port_speed != UNF_PORT_SPEED_32_G));
|
|
if (speed_condition) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Configured port speed(0x%x) is incorrect",
|
|
v_port_cfg->port_speed);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Check port configuration OK");
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_get_port_cfg(struct hifc_hba_s *v_hba,
|
|
struct hifc_chip_info_s *v_chip_info,
|
|
unsigned char v_card_num)
|
|
{
|
|
#define UNF_CONFIG_ITEM_LEN 15
|
|
|
|
/*
|
|
* Maximum length of a configuration item name, including the end
|
|
* character
|
|
*/
|
|
#define UNF_MAX_ITEM_NAME_LEN (32 + 1)
|
|
|
|
/* Get and check parameters */
|
|
char cfg_item[UNF_MAX_ITEM_NAME_LEN];
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct hifc_hba_s *hba = v_hba;
|
|
int iret = RETURN_ERROR_S32;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, hba, return UNF_RETURN_ERROR);
|
|
memset((void *)cfg_item, 0, sizeof(cfg_item));
|
|
|
|
hba->card_info.func_num =
|
|
(hifc_global_func_id(v_hba->hw_dev_handle)) & UNF_FUN_ID_MASK;
|
|
hba->card_info.card_num = v_card_num;
|
|
|
|
/* The range of PF of FC server is from PF1 to PF2 */
|
|
iret = snprintf(cfg_item, UNF_CONFIG_ITEM_LEN, "hifc_cfg_%1u",
|
|
(hba->card_info.func_num));
|
|
UNF_FUNCTION_RETURN_CHECK(iret, UNF_CONFIG_ITEM_LEN);
|
|
cfg_item[UNF_MAX_ITEM_NAME_LEN - 1] = 0;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Get port configuration: %s", cfg_item);
|
|
|
|
/* Get configuration parameters from file */
|
|
UNF_LOWLEVEL_GET_CFG_PARMS(ret, cfg_item, &hifc_port_cfg_parm[0],
|
|
(unsigned int *)(void *)&hba->port_cfg,
|
|
sizeof(hifc_port_cfg_parm) /
|
|
sizeof(struct unf_cfg_item_s));
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can't get configuration",
|
|
hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (max_parent_qpc_num <= 2048) {
|
|
hba->port_cfg.sest_num = 2048;
|
|
hba->port_cfg.max_login = 2048;
|
|
}
|
|
|
|
hba->port_cfg.port_id &= 0xff0000;
|
|
hba->port_cfg.port_id |= hba->card_info.card_num << 8;
|
|
hba->port_cfg.port_id |= hba->card_info.func_num;
|
|
hba->port_cfg.tape_support = (unsigned int)v_chip_info->tape_support;
|
|
|
|
/* Parameters check */
|
|
ret = hifc_check_port_cfg(&hba->port_cfg);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) check configuration incorrect",
|
|
hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Set configuration which is got from file */
|
|
hba->port_speed_cfg = hba->port_cfg.port_speed;
|
|
hba->port_topo_cfg = hba->port_cfg.port_topology;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void hifc_flush_root_ctx(struct hifc_hba_s *v_hba)
|
|
{
|
|
int ret = 0;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_hba, return);
|
|
|
|
ret = hifc_func_rx_tx_flush(v_hba->hw_dev_handle);
|
|
if (ret) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]chipif_func_rx_tx_flush failed with return value(0x%x)",
|
|
ret);
|
|
}
|
|
}
|
|
|
|
static unsigned int hifc_delete_srqc_via_cmdq_sync(struct hifc_hba_s *v_hba,
|
|
unsigned long long sqrc_gpa)
|
|
{
|
|
/* Via CMND Queue */
|
|
#define HIFC_DEL_SRQC_TIMEOUT 3000
|
|
|
|
int ret;
|
|
struct hifcoe_cmdqe_delete_srqc_s del_srqc_cmd;
|
|
struct hifc_cmd_buf *cmdq_in_buf;
|
|
|
|
/* Alloc Cmnd buffer */
|
|
cmdq_in_buf = hifc_alloc_cmd_buf(v_hba->hw_dev_handle);
|
|
if (!cmdq_in_buf) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]cmdq in_cmd_buf allocate failed");
|
|
|
|
HIFC_ERR_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SRQC);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Build & Send Cmnd */
|
|
memset(&del_srqc_cmd, 0, sizeof(del_srqc_cmd));
|
|
del_srqc_cmd.wd0.task_type = HIFCOE_TASK_T_DEL_SRQC;
|
|
del_srqc_cmd.srqc_gpa_h = HIFC_HIGH_32_BITS(sqrc_gpa);
|
|
del_srqc_cmd.srqc_gpa_l = HIFC_LOW_32_BITS(sqrc_gpa);
|
|
hifc_cpu_to_big32(&del_srqc_cmd, sizeof(del_srqc_cmd));
|
|
memcpy(cmdq_in_buf->buf, &del_srqc_cmd, sizeof(del_srqc_cmd));
|
|
cmdq_in_buf->size = sizeof(del_srqc_cmd);
|
|
|
|
ret = hifc_cmdq_detail_resp(v_hba->hw_dev_handle, HIFC_ACK_TYPE_CMDQ,
|
|
HIFC_MOD_FCOE, 0,
|
|
cmdq_in_buf, NULL, HIFC_DEL_SRQC_TIMEOUT);
|
|
|
|
/* Free Cmnd Buffer */
|
|
hifc_free_cmd_buf(v_hba->hw_dev_handle, cmdq_in_buf);
|
|
|
|
if (ret) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Send del srqc via cmdq failed, ret=0x%x", ret);
|
|
|
|
HIFC_ERR_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SRQC);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SRQC);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void hifc_flush_srq_ctx(struct hifc_hba_s *v_hba)
|
|
{
|
|
struct hifc_srq_info_s *srq_info = NULL;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Start destroy ELS SRQC");
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_hba, return);
|
|
|
|
/* Check state to avoid to flush SRQC again */
|
|
srq_info = &v_hba->els_srq_info;
|
|
if (srq_info->srq_type == HIFC_SRQ_ELS &&
|
|
srq_info->enable == UNF_TRUE) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
|
|
"[event]HBA(0x%x) flush ELS SRQC",
|
|
v_hba->port_index);
|
|
|
|
(void)hifc_delete_srqc_via_cmdq_sync(
|
|
v_hba,
|
|
srq_info->cqm_srq_info->q_ctx_paddr);
|
|
}
|
|
}
|
|
|
|
static unsigned int hifc_create_queues(struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
ret = hifc_create_root_queues(v_hba);
|
|
if (ret != RETURN_OK)
|
|
goto out_creat_root_queue_fail;
|
|
|
|
/* Initialize shared resources of SCQ and SRQ in parent queue */
|
|
ret = hifc_create_common_share_queues(v_hba);
|
|
if (ret != RETURN_OK)
|
|
goto out_create_common_queue_fail;
|
|
|
|
/* Initialize parent queue manager resources */
|
|
ret = hifc_alloc_parent_queue_mgr(v_hba);
|
|
if (ret != RETURN_OK)
|
|
goto out_free_share_queue_resource;
|
|
|
|
/* Initialize shared WQE page pool in parent SQ */
|
|
ret = hifc_alloc_parent_sq_wqe_page_pool(v_hba);
|
|
if (ret != RETURN_OK)
|
|
goto out_free_parent_queue_resource;
|
|
|
|
/*
|
|
* Notice: the configuration of SQ and QID(default_sq_id)
|
|
* must be the same in FC
|
|
*/
|
|
v_hba->next_clearing_sq = 0;
|
|
v_hba->default_sq_id = HIFC_QID_SQ;
|
|
|
|
return RETURN_OK;
|
|
|
|
out_free_parent_queue_resource:
|
|
hifc_free_parent_queue_mgr(v_hba);
|
|
|
|
out_free_share_queue_resource:
|
|
hifc_flush_scq_ctx(v_hba);
|
|
hifc_flush_srq_ctx(v_hba);
|
|
hifc_destroy_common_share_queues(v_hba);
|
|
|
|
out_create_common_queue_fail:
|
|
hifc_destroy_root_queues(v_hba);
|
|
|
|
out_creat_root_queue_fail:
|
|
hifc_flush_root_ctx(v_hba);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void hifc_destroy_queues(struct hifc_hba_s *v_hba)
|
|
{
|
|
/* Free parent queue resource */
|
|
hifc_free_parent_queues(v_hba);
|
|
|
|
/* Free queue manager resource */
|
|
hifc_free_parent_queue_mgr(v_hba);
|
|
|
|
/* Free linked List SQ and WQE page pool resource */
|
|
hifc_free_parent_sq_wqe_page_pool(v_hba);
|
|
|
|
/* Free shared SRQ and SCQ queue resource */
|
|
hifc_destroy_common_share_queues(v_hba);
|
|
|
|
/* Free root queue resource */
|
|
hifc_destroy_root_queues(v_hba);
|
|
}
|
|
|
|
static unsigned int hifc_notify_up_open_timer(struct hifc_hba_s *v_hba)
|
|
{
|
|
int op_code = UNF_TRUE;
|
|
unsigned int cmd_scq_bit_map = 0;
|
|
unsigned int scq_index = 0;
|
|
unsigned int ret;
|
|
|
|
for (scq_index = 0; scq_index < HIFC_TOTAL_SCQ_NUM; scq_index++)
|
|
cmd_scq_bit_map |= HIFC_SCQ_IS_CMD(scq_index) ?
|
|
(1 << scq_index) : (0 << scq_index);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) open timer, cmdscq bitmap:0x%x",
|
|
v_hba->port_cfg.port_id, cmd_scq_bit_map);
|
|
|
|
ret = hifc_notify_up_config_timer(v_hba, op_code, cmd_scq_bit_map);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_notify_up_close_timer(struct hifc_hba_s *v_hba)
|
|
{
|
|
int op_code = UNF_FALSE;
|
|
unsigned int cmd_scq_bit_map = 0;
|
|
unsigned int scq_index = 0;
|
|
unsigned int ret;
|
|
|
|
for (scq_index = 0; scq_index < HIFC_TOTAL_SCQ_NUM; scq_index++)
|
|
cmd_scq_bit_map |= HIFC_SCQ_IS_CMD(scq_index) ?
|
|
(1 << scq_index) : (0 << scq_index);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) close timer with cmd_scq bitmap(0x%x)",
|
|
v_hba->port_cfg.port_id, cmd_scq_bit_map);
|
|
|
|
ret = hifc_notify_up_config_timer(v_hba, op_code, cmd_scq_bit_map);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_initial_chip_access(struct hifc_hba_s *v_hba)
|
|
{
|
|
int ret = RETURN_OK;
|
|
|
|
/* 1.
|
|
* Initialize cqm access related with scq, emb cq, aeq(ucode-->driver)
|
|
*/
|
|
service_cqm_temp.service_handle = v_hba;
|
|
ret = cqm_service_register(v_hba->hw_dev_handle, &service_cqm_temp);
|
|
if (ret != CQM_SUCCESS)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
/* 2. Initialize mailbox(driver-->up), aeq(up--->driver) access */
|
|
ret = hifc_register_mgmt_msg_cb(v_hba->hw_dev_handle,
|
|
HIFC_MOD_FC, v_hba,
|
|
hifc_up_msg_2_driver_proc);
|
|
if (ret != CQM_SUCCESS)
|
|
goto out_unreg_cqm;
|
|
|
|
return RETURN_OK;
|
|
|
|
out_unreg_cqm:
|
|
cqm_service_unregister(v_hba->hw_dev_handle);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static void hifc_release_chip_access(struct hifc_hba_s *v_hba)
|
|
{
|
|
HIFC_CHECK(INVALID_VALUE32, v_hba->hw_dev_handle, return);
|
|
|
|
hifc_unregister_mgmt_msg_cb(v_hba->hw_dev_handle, HIFC_MOD_FC);
|
|
|
|
cqm_service_unregister(v_hba->hw_dev_handle);
|
|
}
|
|
|
|
static void hifc_get_chip_info(struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int exi_base = 0;
|
|
unsigned int fun_index = 0;
|
|
|
|
v_hba->vpid_start = v_hba->fc_service_cap.dev_fc_cap.vp_id_start;
|
|
v_hba->vpid_end = v_hba->fc_service_cap.dev_fc_cap.vp_id_end;
|
|
fun_index = hifc_global_func_id(v_hba->hw_dev_handle);
|
|
exi_base = 0;
|
|
|
|
exi_base += (fun_index * HIFC_EXIT_STRIDE);
|
|
v_hba->exit_base = HIFC_LSW(exi_base);
|
|
v_hba->exit_count = HIFC_EXIT_STRIDE;
|
|
v_hba->image_count = UNF_HIFC_MAXRPORT_NUM;
|
|
v_hba->max_support_speed = max_speed;
|
|
v_hba->port_index = HIFC_LSB(fun_index);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) base information: PortIndex=0x%x, ImgCount=0x%x, ExiBase=0x%x, ExiCount=0x%x, VpIdStart=0x%x, VpIdEnd=0x%x, MaxSpeed=0x%x, Speed=0x%x, Topo=0x%x",
|
|
v_hba->port_cfg.port_id, v_hba->port_index,
|
|
v_hba->image_count, v_hba->exit_base,
|
|
v_hba->exit_count, v_hba->vpid_start,
|
|
v_hba->vpid_end, v_hba->max_support_speed,
|
|
v_hba->port_speed_cfg, v_hba->port_topo_cfg);
|
|
}
|
|
|
|
static unsigned int hifc_init_host_res(struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
struct hifc_hba_s *hba = v_hba;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, hba, return UNF_RETURN_ERROR);
|
|
|
|
/* Initialize spin lock */
|
|
spin_lock_init(&hba->hba_lock);
|
|
spin_lock_init(&hba->flush_state_lock);
|
|
spin_lock_init(&hba->delay_info.srq_lock);
|
|
/* Initialize init_completion */
|
|
init_completion(&hba->hba_init_complete);
|
|
init_completion(&hba->mbox_complete);
|
|
|
|
/* Step-1: initialize the communication channel between driver and uP */
|
|
ret = hifc_initial_chip_access(hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't initialize chip access",
|
|
hba->port_cfg.port_id);
|
|
|
|
goto out_unmap_memory;
|
|
}
|
|
/* Step-2: get chip configuration information before creating
|
|
* queue resources
|
|
*/
|
|
hifc_get_chip_info(hba);
|
|
|
|
/* Step-3: create queue resources */
|
|
ret = hifc_create_queues(hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't create queues",
|
|
hba->port_cfg.port_id);
|
|
|
|
goto out_release_chip_access;
|
|
}
|
|
|
|
/* Initialize status parameters */
|
|
hba->active_port_speed = UNF_PORT_SPEED_UNKNOWN;
|
|
hba->active_topo = UNF_ACT_TOP_UNKNOWN;
|
|
hba->sfp_on = UNF_FALSE;
|
|
hba->port_loop_role = UNF_LOOP_ROLE_MASTER_OR_SLAVE;
|
|
hba->phy_link = UNF_PORT_LINK_DOWN;
|
|
hba->q_set_stage = HIFC_QUEUE_SET_STAGE_INIT;
|
|
|
|
/* Initialize parameters referring to the lowlevel */
|
|
hba->remote_rttov_tag = 0;
|
|
hba->port_bbscn_cfg = HIFC_LOWLEVEL_DEFAULT_BB_SCN;
|
|
|
|
/* Initialize timer, and the unit of E_D_TOV is ms */
|
|
hba->remote_edtov_tag = 0;
|
|
hba->remote_bbcredit = 0;
|
|
hba->compared_bbscn = 0;
|
|
hba->compared_edtov_val = UNF_DEFAULT_EDTOV;
|
|
hba->compared_ratov_val = UNF_DEFAULT_RATOV;
|
|
hba->removing = UNF_FALSE;
|
|
hba->dev_present = UNF_TRUE;
|
|
|
|
/* Initialize parameters about cos */
|
|
hba->cos_bit_map = cos_bit_map;
|
|
memset(hba->cos_rport_cnt, 0, HIFC_MAX_COS_NUM * sizeof(atomic_t));
|
|
|
|
/* Mailbox access completion */
|
|
complete(&hba->mbox_complete);
|
|
|
|
/* Notify uP to open timer after creating scq */
|
|
ret = hifc_notify_up_open_timer(hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't open timer",
|
|
hba->port_cfg.port_id);
|
|
|
|
goto out_destroy_queues;
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]HIFC port(0x%x) initialize host resources succeeded",
|
|
hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
|
|
out_destroy_queues:
|
|
hifc_flush_scq_ctx(hba);
|
|
hifc_flush_srq_ctx(hba);
|
|
hifc_flush_root_ctx(hba);
|
|
hifc_destroy_queues(hba);
|
|
|
|
out_release_chip_access:
|
|
hifc_release_chip_access(hba);
|
|
|
|
out_unmap_memory:
|
|
return ret;
|
|
}
|
|
|
|
static void hifc_update_lport_config(
|
|
struct hifc_hba_s *v_hba,
|
|
struct unf_low_level_function_op_s *v_low_level_fun)
|
|
{
|
|
#define HIFC_MULTI_CONF_NONSUPPORT 0
|
|
|
|
struct unf_lport_cfg_item_s *lport_cfg_items = NULL;
|
|
|
|
lport_cfg_items = &v_low_level_fun->lport_cfg_items;
|
|
|
|
if (v_hba->port_cfg.max_login < v_low_level_fun->support_max_rport)
|
|
lport_cfg_items->max_login = v_hba->port_cfg.max_login;
|
|
else
|
|
lport_cfg_items->max_login = v_low_level_fun->support_max_rport;
|
|
|
|
if ((v_hba->port_cfg.sest_num / 2) < UNF_RESERVE_SFS_XCHG)
|
|
lport_cfg_items->max_io = v_hba->port_cfg.sest_num;
|
|
else
|
|
lport_cfg_items->max_io = v_hba->port_cfg.sest_num -
|
|
UNF_RESERVE_SFS_XCHG;
|
|
|
|
lport_cfg_items->max_sfs_xchg = UNF_MAX_SFS_XCHG;
|
|
lport_cfg_items->port_id = v_hba->port_cfg.port_id;
|
|
lport_cfg_items->port_mode = v_hba->port_cfg.port_mode;
|
|
lport_cfg_items->port_topology = v_hba->port_cfg.port_topology;
|
|
lport_cfg_items->max_queue_depth = v_hba->port_cfg.max_queue_depth;
|
|
|
|
lport_cfg_items->port_speed = v_hba->port_cfg.port_speed;
|
|
lport_cfg_items->tape_support = v_hba->port_cfg.tape_support;
|
|
lport_cfg_items->res_mgmt_enabled = UNF_FALSE;
|
|
|
|
v_low_level_fun->sys_port_name =
|
|
*(unsigned long long *)v_hba->sys_port_name;
|
|
v_low_level_fun->sys_node_name =
|
|
*(unsigned long long *)v_hba->sys_node_name;
|
|
|
|
/* Update chip information */
|
|
v_low_level_fun->dev = v_hba->pci_dev;
|
|
v_low_level_fun->chip_info.chip_work_mode = v_hba->work_mode;
|
|
v_low_level_fun->chip_info.chip_type = v_hba->chip_type;
|
|
v_low_level_fun->chip_info.disable_err_flag = 0;
|
|
v_low_level_fun->support_max_speed = v_hba->max_support_speed;
|
|
|
|
v_low_level_fun->chip_id = 0;
|
|
|
|
v_low_level_fun->sfp_type = UNF_PORT_TYPE_FC_SFP;
|
|
|
|
v_low_level_fun->multi_conf_support = HIFC_MULTI_CONF_NONSUPPORT;
|
|
v_low_level_fun->support_max_xid_range = v_hba->port_cfg.sest_num;
|
|
v_low_level_fun->update_fw_reset_active =
|
|
UNF_PORT_UNGRADE_FW_RESET_INACTIVE;
|
|
v_low_level_fun->port_type = DRV_PORT_ENTITY_TYPE_PHYSICAL;
|
|
|
|
if ((lport_cfg_items->port_id & UNF_FIRST_LPORT_ID_MASK) ==
|
|
lport_cfg_items->port_id) {
|
|
v_low_level_fun->support_upgrade_report =
|
|
UNF_PORT_SUPPORT_UPGRADE_REPORT;
|
|
} else {
|
|
v_low_level_fun->support_upgrade_report =
|
|
UNF_PORT_UNSUPPORT_UPGRADE_REPORT;
|
|
}
|
|
|
|
v_low_level_fun->low_level_type |= UNF_FC_PROTOCOL_TYPE;
|
|
}
|
|
|
|
static unsigned int hifc_create_lport(struct hifc_hba_s *v_hba)
|
|
{
|
|
void *lport = NULL;
|
|
struct unf_low_level_function_op_s low_level_fun;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, v_hba, return UNF_RETURN_ERROR);
|
|
hifc_fun_op.dev = v_hba->pci_dev;
|
|
memcpy(&low_level_fun, &hifc_fun_op,
|
|
sizeof(struct unf_low_level_function_op_s));
|
|
|
|
/* Update port configuration table */
|
|
hifc_update_lport_config(v_hba, &low_level_fun);
|
|
|
|
/* Apply for lport resources */
|
|
UNF_LOWLEVEL_ALLOC_LPORT(lport, v_hba, &low_level_fun);
|
|
if (!lport) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can't allocate Lport",
|
|
v_hba->port_cfg.port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
v_hba->lport = lport;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void hifc_release_probe_index(unsigned int probe_index)
|
|
{
|
|
if (probe_index >= HIFC_MAX_PROBE_PORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Probe index(0x%x) is invalid", probe_index);
|
|
|
|
return;
|
|
}
|
|
|
|
spin_lock(&probe_spin_lock);
|
|
if (!test_bit((int)probe_index, (const unsigned long *)probe_bit_map)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Probe index(0x%x) is not probed",
|
|
probe_index);
|
|
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
return;
|
|
}
|
|
|
|
clear_bit((int)probe_index, probe_bit_map);
|
|
spin_unlock(&probe_spin_lock);
|
|
}
|
|
|
|
static void hifc_release_host_res(struct hifc_hba_s *v_hba)
|
|
{
|
|
hifc_destroy_queues(v_hba);
|
|
hifc_release_chip_access(v_hba);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) release low level resource done",
|
|
v_hba->port_cfg.port_id);
|
|
}
|
|
|
|
static struct hifc_hba_s *hifc_init_hba(struct pci_dev *v_dev,
|
|
void *v_hwdev_handle,
|
|
struct hifc_chip_info_s *v_chip_info,
|
|
unsigned char v_card_num)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
struct hifc_hba_s *hba = NULL;
|
|
|
|
/* Allocate HBA */
|
|
hba = kmalloc(sizeof(*hba), GFP_ATOMIC);
|
|
HIFC_CHECK(INVALID_VALUE32, hba, return NULL);
|
|
memset(hba, 0, sizeof(struct hifc_hba_s));
|
|
|
|
/* Heartbeat default */
|
|
hba->heart_status = 1;
|
|
|
|
/* Private data in pciDev */
|
|
hba->pci_dev = v_dev; /* PCI device */
|
|
hba->hw_dev_handle = v_hwdev_handle;
|
|
|
|
/* Work mode */
|
|
hba->work_mode = v_chip_info->work_mode;
|
|
/* Create work queue */
|
|
hba->work_queue = create_singlethread_workqueue("hifc");
|
|
if (!hba->work_queue) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[err]Hifc creat workqueue failed");
|
|
|
|
goto out_free_hba;
|
|
}
|
|
/* Init delay work */
|
|
INIT_DELAYED_WORK(&hba->delay_info.del_work,
|
|
hifc_rcvd_els_from_srq_time_out);
|
|
|
|
/* Notice: Only use FC features */
|
|
(void)hifc_support_fc(v_hwdev_handle, &hba->fc_service_cap);
|
|
/* Check parent context available */
|
|
if (hba->fc_service_cap.dev_fc_cap.max_parent_qpc_num == 0) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]FC parent context is not allocated in this function");
|
|
|
|
goto out_destroy_workqueue;
|
|
}
|
|
max_parent_qpc_num = hba->fc_service_cap.dev_fc_cap.max_parent_qpc_num;
|
|
|
|
/* Get port configuration */
|
|
ret = hifc_get_port_cfg(hba, v_chip_info, v_card_num);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[err]Can't get port configuration");
|
|
|
|
goto out_destroy_workqueue;
|
|
}
|
|
/* Get WWN */
|
|
*(unsigned long long *)hba->sys_node_name = v_chip_info->wwnn;
|
|
*(unsigned long long *)hba->sys_port_name = v_chip_info->wwpn;
|
|
|
|
/* Initialize host resources */
|
|
ret = hifc_init_host_res(hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't initialize host resource",
|
|
hba->port_cfg.port_id);
|
|
|
|
goto out_destroy_workqueue;
|
|
}
|
|
|
|
/* Local Port create */
|
|
ret = hifc_create_lport(hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't create lport",
|
|
hba->port_cfg.port_id);
|
|
goto out_release_host_res;
|
|
}
|
|
complete(&hba->hba_init_complete);
|
|
|
|
/* Print reference count */
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[info]Port(0x%x) probe succeeded.",
|
|
hba->port_cfg.port_id);
|
|
|
|
return hba;
|
|
|
|
out_release_host_res:
|
|
hifc_flush_scq_ctx(hba);
|
|
hifc_flush_srq_ctx(hba);
|
|
hifc_flush_root_ctx(hba);
|
|
hifc_release_host_res(hba);
|
|
|
|
out_destroy_workqueue:
|
|
flush_workqueue(hba->work_queue);
|
|
destroy_workqueue(hba->work_queue);
|
|
hba->work_queue = NULL;
|
|
|
|
out_free_hba:
|
|
kfree(hba);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void hifc_get_total_probed_num(unsigned int *v_probe_cnt)
|
|
{
|
|
unsigned int i = 0;
|
|
unsigned int count = 0;
|
|
|
|
spin_lock(&probe_spin_lock);
|
|
for (i = 0; i < HIFC_MAX_PROBE_PORT_NUM; i++) {
|
|
if (test_bit((int)i, (const unsigned long *)probe_bit_map))
|
|
count++;
|
|
}
|
|
|
|
*v_probe_cnt = count;
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Probed port total number is 0x%x", count);
|
|
}
|
|
|
|
static unsigned int hifc_assign_card_num(struct hifc_lld_dev *lld_dev,
|
|
struct hifc_chip_info_s *v_chip_info,
|
|
unsigned char *v_card_num)
|
|
{
|
|
unsigned char i = 0;
|
|
unsigned long long card_index = 0;
|
|
|
|
card_index = (!pci_is_root_bus(lld_dev->pdev->bus)) ?
|
|
lld_dev->pdev->bus->parent->number :
|
|
lld_dev->pdev->bus->number;
|
|
|
|
spin_lock(&probe_spin_lock);
|
|
|
|
for (i = 0; i < HIFC_MAX_CARD_NUM; i++) {
|
|
if (test_bit((int)i, (const unsigned long *)card_num_bit_map)) {
|
|
if ((card_num_manage[i].card_number == card_index) &&
|
|
(card_num_manage[i].is_removing == UNF_FALSE)) {
|
|
card_num_manage[i].port_count++;
|
|
*v_card_num = i;
|
|
spin_unlock(&probe_spin_lock);
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < HIFC_MAX_CARD_NUM; i++) {
|
|
if (!test_bit((int)i,
|
|
(const unsigned long *)card_num_bit_map)) {
|
|
card_num_manage[i].card_number = card_index;
|
|
card_num_manage[i].port_count = 1;
|
|
card_num_manage[i].is_removing = UNF_FALSE;
|
|
*v_card_num = i;
|
|
set_bit(i, card_num_bit_map);
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Have probe more than 0x%x port, probe failed", i);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static void hifc_dec_and_free_card_num(unsigned char v_card_num)
|
|
{
|
|
/* 2 ports per card */
|
|
if (v_card_num >= HIFC_MAX_CARD_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Card number(0x%x) is invalid", v_card_num);
|
|
|
|
return;
|
|
}
|
|
|
|
spin_lock(&probe_spin_lock);
|
|
|
|
if (test_bit((int)v_card_num,
|
|
(const unsigned long *)card_num_bit_map)) {
|
|
card_num_manage[v_card_num].port_count--;
|
|
card_num_manage[v_card_num].is_removing = UNF_TRUE;
|
|
|
|
if (card_num_manage[v_card_num].port_count == 0) {
|
|
card_num_manage[v_card_num].card_number = 0;
|
|
card_num_manage[v_card_num].is_removing = UNF_FALSE;
|
|
clear_bit((int)v_card_num, card_num_bit_map);
|
|
}
|
|
} else {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Can not find card number(0x%x)", v_card_num);
|
|
}
|
|
|
|
spin_unlock(&probe_spin_lock);
|
|
}
|
|
|
|
unsigned int hifc_assign_probe_index(unsigned int *v_probe_index)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
spin_lock(&probe_spin_lock);
|
|
for (i = 0; i < HIFC_MAX_PROBE_PORT_NUM; i++) {
|
|
if (!test_bit((int)i, (const unsigned long *)probe_bit_map)) {
|
|
*v_probe_index = i;
|
|
set_bit(i, probe_bit_map);
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
spin_unlock(&probe_spin_lock);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Have probe more than 0x%x port, probe failed", i);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
int hifc_probe(struct hifc_lld_dev *lld_dev, void **uld_dev, char *uld_dev_name)
|
|
{
|
|
struct pci_dev *dev = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int probe_index = 0;
|
|
unsigned int probe_total_num = 0;
|
|
unsigned char card_num = INVALID_VALUE8;
|
|
struct hifc_chip_info_s chip_info;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, lld_dev, return UNF_RETURN_ERROR_S32);
|
|
HIFC_CHECK(INVALID_VALUE32, lld_dev->hwdev,
|
|
return UNF_RETURN_ERROR_S32);
|
|
HIFC_CHECK(INVALID_VALUE32, lld_dev->pdev, return UNF_RETURN_ERROR_S32);
|
|
HIFC_CHECK(INVALID_VALUE32, uld_dev, return UNF_RETURN_ERROR_S32);
|
|
HIFC_CHECK(INVALID_VALUE32, uld_dev_name, return UNF_RETURN_ERROR_S32);
|
|
|
|
dev = lld_dev->pdev; /* pcie device */
|
|
|
|
memset(&chip_info, 0, sizeof(struct hifc_chip_info_s));
|
|
/* 1. Get & check Total_Probed_number */
|
|
hifc_get_total_probed_num(&probe_total_num);
|
|
if (probe_total_num >= HIFC_MAX_PORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Total probe num (0x%x) is larger than allowed number(64)",
|
|
probe_total_num);
|
|
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
/* 2. Check device work mode */
|
|
if (hifc_support_fc(lld_dev->hwdev, NULL)) {
|
|
chip_info.work_mode = HIFC_SMARTIO_WORK_MODE_FC;
|
|
} else {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port work mode is not FC");
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
|
|
/* 4. Assign & Get new Probe index */
|
|
ret = hifc_assign_probe_index(&probe_index);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]AssignProbeIndex fail");
|
|
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
|
|
ret = hifc_get_chip_capability((void *)lld_dev->hwdev, &chip_info);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]GetChipCapability fail");
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
|
|
/* Assign & Get new Card number */
|
|
ret = hifc_assign_card_num(lld_dev, &chip_info, &card_num);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]hifc_assign_card_num fail");
|
|
hifc_release_probe_index(probe_index);
|
|
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
|
|
/* Init HBA resource */
|
|
hba = hifc_init_hba(dev, lld_dev->hwdev, &chip_info, card_num);
|
|
if (!hba) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Probe HBA(0x%x) failed.", probe_index);
|
|
|
|
hifc_release_probe_index(probe_index);
|
|
hifc_dec_and_free_card_num(card_num);
|
|
|
|
return UNF_RETURN_ERROR_S32;
|
|
}
|
|
|
|
/* Name by the order of probe */
|
|
*uld_dev = hba;
|
|
|
|
snprintf(uld_dev_name, HIFC_PORT_NAME_STR_LEN, "%s%02x%02x",
|
|
HIFC_PORT_NAME_LABEL,
|
|
hba->card_info.card_num, hba->card_info.func_num);
|
|
memcpy(hba->port_name, uld_dev_name, HIFC_PORT_NAME_STR_LEN);
|
|
|
|
hba->probe_index = probe_index;
|
|
hifc_hba[probe_index] = hba;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_port_check_fw_ready(struct hifc_hba_s *v_hba)
|
|
{
|
|
#define HIFC_PORT_CLEAR_DONE 0
|
|
#define HIFC_PORT_CLEAR_DOING 1
|
|
unsigned int clear_state = HIFC_PORT_CLEAR_DOING;
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int wait_time_out = 0;
|
|
|
|
do {
|
|
msleep(1000);
|
|
wait_time_out += 1000;
|
|
ret = hifc_mbx_get_fw_clear_stat(v_hba, &clear_state);
|
|
if (ret != RETURN_OK)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
/* Total time more than 30s, retry more than 3 times, failed */
|
|
if ((wait_time_out > 30000) &&
|
|
(clear_state != HIFC_PORT_CLEAR_DONE))
|
|
return UNF_RETURN_ERROR;
|
|
|
|
} while (clear_state != HIFC_PORT_CLEAR_DONE);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_sfp_switch(void *v_hba, void *v_para_in)
|
|
{
|
|
struct hifc_hba_s *hba = (struct hifc_hba_s *)v_hba;
|
|
int turn_on = UNF_FALSE;
|
|
unsigned int ret = RETURN_OK;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, hba, return UNF_RETURN_ERROR);
|
|
HIFC_CHECK(INVALID_VALUE32, v_para_in, return UNF_RETURN_ERROR);
|
|
|
|
/* Redundancy check */
|
|
turn_on = *((int *)v_para_in);
|
|
if (turn_on == hba->sfp_on) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Port(0x%x) FC physical port is already %s",
|
|
hba->port_cfg.port_id, (turn_on) ? "on" : "off");
|
|
|
|
return ret;
|
|
}
|
|
if (turn_on == UNF_TRUE) {
|
|
ret = hifc_port_check_fw_ready(hba);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_WARN,
|
|
"[warn]Get port(0x%x) clear state failed, turn on fail",
|
|
hba->port_cfg.port_id);
|
|
return ret;
|
|
}
|
|
/* At first, configure port table info if necessary */
|
|
ret = hifc_config_port_table(hba);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) can't configurate port table",
|
|
hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Switch physical port */
|
|
ret = hifc_port_switch(hba, turn_on);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[err]Port(0x%x) switch failed",
|
|
hba->port_cfg.port_id);
|
|
return ret;
|
|
}
|
|
|
|
/* Update HBA's sfp state */
|
|
hba->sfp_on = turn_on;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_destroy_lport(struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_LOWLEVEL_RELEASE_LOCAL_PORT(ret, v_hba->lport);
|
|
v_hba->lport = NULL;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) destroy L_Port done",
|
|
v_hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_port_reset(struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned long time_out = 0;
|
|
int sfp_before_reset = UNF_FALSE;
|
|
int off_para_in = UNF_FALSE;
|
|
struct pci_dev *dev = NULL;
|
|
struct hifc_hba_s *hba = v_hba;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, hba, return UNF_RETURN_ERROR);
|
|
dev = hba->pci_dev;
|
|
HIFC_CHECK(INVALID_VALUE32, dev, return UNF_RETURN_ERROR);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[event]Port(0x%x) reset HBA begin",
|
|
hba->port_cfg.port_id);
|
|
|
|
/* Wait for last init/reset completion */
|
|
time_out = wait_for_completion_timeout(
|
|
&hba->hba_init_complete,
|
|
(unsigned long)HIFC_PORT_INIT_TIME_SEC_MAX * HZ);
|
|
|
|
if (time_out == UNF_ZERO) {
|
|
UNF_TRACE(INVALID_VALUE32, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Last HBA initialize/reset timeout: %d second",
|
|
HIFC_PORT_INIT_TIME_SEC_MAX);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Save current port state */
|
|
sfp_before_reset = hba->sfp_on;
|
|
|
|
/* Inform the reset event to CM level before beginning */
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, hba->lport, UNF_PORT_RESET_START, NULL);
|
|
hba->reset_time = jiffies;
|
|
|
|
/* Close SFP */
|
|
ret = hifc_sfp_switch(hba, &off_para_in);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can't close SFP",
|
|
hba->port_cfg.port_id);
|
|
hba->sfp_on = sfp_before_reset;
|
|
|
|
complete(&hba->hba_init_complete);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret = hifc_port_check_fw_ready(hba);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Get port(0x%x) clear state failed, hang port and report chip error",
|
|
hba->port_cfg.port_id);
|
|
|
|
complete(&hba->hba_init_complete);
|
|
return ret;
|
|
}
|
|
|
|
hifc_queue_pre_process(hba, UNF_FALSE);
|
|
|
|
ret = hifc_mbox_reset_chip(hba, HIFC_MBOX_SUBTYPE_LIGHT_RESET);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]HIFC port(0x%x) can't reset chip mailbox",
|
|
hba->port_cfg.port_id);
|
|
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, hba->lport,
|
|
UNF_PORT_GET_FWLOG, NULL);
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, hba->lport,
|
|
UNF_PORT_DEBUG_DUMP, NULL);
|
|
}
|
|
|
|
/* Inform the success to CM level */
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, hba->lport, UNF_PORT_RESET_END, NULL);
|
|
|
|
/* Queue open */
|
|
hifc_enable_queues_dispatch(hba);
|
|
|
|
/* Open SFP */
|
|
(void)hifc_sfp_switch(hba, &sfp_before_reset);
|
|
|
|
complete(&hba->hba_init_complete);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[event]Port(0x%x) reset HBA done",
|
|
hba->port_cfg.port_id);
|
|
|
|
return ret;
|
|
#undef HIFC_WAIT_LINKDOWN_EVENT_MS
|
|
}
|
|
|
|
static unsigned int hifc_delete_scqc_via_cmdq_sync(struct hifc_hba_s *v_hba,
|
|
unsigned int scqn)
|
|
{
|
|
/* Via CMND Queue */
|
|
#define HIFC_DEL_SCQC_TIMEOUT 3000
|
|
|
|
int ret;
|
|
struct hifcoe_cmdqe_delete_scqc_s del_scqc_cmd;
|
|
struct hifc_cmd_buf *cmdq_in_buf;
|
|
|
|
/* Alloc cmd buffer */
|
|
cmdq_in_buf = hifc_alloc_cmd_buf(v_hba->hw_dev_handle);
|
|
if (!cmdq_in_buf) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]cmdq in_cmd_buf alloc failed");
|
|
HIFC_ERR_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SCQC);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Build & Send Cmnd */
|
|
memset(&del_scqc_cmd, 0, sizeof(del_scqc_cmd));
|
|
del_scqc_cmd.wd0.task_type = HIFCOE_TASK_T_DEL_SCQC;
|
|
del_scqc_cmd.wd1.scqn = HIFC_LSW(scqn);
|
|
hifc_cpu_to_big32(&del_scqc_cmd, sizeof(del_scqc_cmd));
|
|
memcpy(cmdq_in_buf->buf, &del_scqc_cmd, sizeof(del_scqc_cmd));
|
|
cmdq_in_buf->size = sizeof(del_scqc_cmd);
|
|
|
|
ret = hifc_cmdq_detail_resp(v_hba->hw_dev_handle, HIFC_ACK_TYPE_CMDQ,
|
|
HIFC_MOD_FCOE, 0,
|
|
cmdq_in_buf, NULL, HIFC_DEL_SCQC_TIMEOUT);
|
|
|
|
/* Free cmnd buffer */
|
|
hifc_free_cmd_buf(v_hba->hw_dev_handle, cmdq_in_buf);
|
|
|
|
if (ret) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Send del scqc via cmdq failed, ret=0x%x", ret);
|
|
|
|
HIFC_ERR_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SCQC);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_DEL_SCQC);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void hifc_flush_scq_ctx(struct hifc_hba_s *v_hba)
|
|
{
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Start destroy total 0x%x SCQC", HIFC_TOTAL_SCQ_NUM);
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_hba, return);
|
|
|
|
(void)hifc_delete_scqc_via_cmdq_sync(v_hba, 0);
|
|
}
|
|
|
|
void hifc_set_hba_flush_state(struct hifc_hba_s *v_hba, int in_flush)
|
|
{
|
|
unsigned long flag = 0;
|
|
|
|
spin_lock_irqsave(&v_hba->flush_state_lock, flag);
|
|
v_hba->in_flushing = in_flush;
|
|
spin_unlock_irqrestore(&v_hba->flush_state_lock, flag);
|
|
}
|
|
|
|
static int hifc_hba_is_present(struct hifc_hba_s *v_hba)
|
|
{
|
|
int ret = RETURN_OK;
|
|
int present = UNF_FALSE;
|
|
unsigned int vendor_id = 0;
|
|
|
|
ret = pci_read_config_dword(v_hba->pci_dev, 0, &vendor_id);
|
|
vendor_id &= HIFC_PCI_VENDOR_ID_MASK;
|
|
if ((ret == RETURN_OK) && (vendor_id == HIFC_PCI_VENDOR_ID)) {
|
|
present = UNF_TRUE;
|
|
} else {
|
|
present = UNF_FALSE;
|
|
v_hba->dev_present = UNF_FALSE;
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[info]Port %s remove: vender_id=0x%x, ret=0x%x",
|
|
present ? "normal" : "surprise", vendor_id, ret);
|
|
|
|
return present;
|
|
}
|
|
|
|
static void hifc_exit(struct pci_dev *v_dev, struct hifc_hba_s *v_hba)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
int sfp_switch = UNF_FALSE;
|
|
int present = UNF_TRUE;
|
|
|
|
v_hba->removing = UNF_TRUE;
|
|
|
|
/* 1. Check HBA present or not */
|
|
present = hifc_hba_is_present(v_hba);
|
|
if (present == UNF_TRUE) {
|
|
if (v_hba->phy_link == UNF_PORT_LINK_DOWN)
|
|
v_hba->q_set_stage = HIFC_QUEUE_SET_STAGE_FLUSHDONE;
|
|
|
|
/* At first, close sfp */
|
|
sfp_switch = UNF_FALSE;
|
|
(void)hifc_sfp_switch((void *)v_hba, (void *)&sfp_switch);
|
|
}
|
|
|
|
/* 2. Report COM with HBA removing: delete route timer delay work */
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, v_hba->lport, UNF_PORT_BEGIN_REMOVE, NULL);
|
|
|
|
/* 3. Report COM with HBA Nop, COM release I/O(s) & R_Port(s) forcely */
|
|
UNF_LOWLEVEL_PORT_EVENT(ret, v_hba->lport, UNF_PORT_NOP, NULL);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]PCI device(%p) remove port(0x%x) failed",
|
|
v_dev, v_hba->port_index);
|
|
}
|
|
|
|
if (present == UNF_TRUE) {
|
|
/* 4.1 Wait for all SQ empty, free SRQ buffer & SRQC */
|
|
hifc_queue_pre_process(v_hba, UNF_TRUE);
|
|
}
|
|
|
|
/* 5. Destroy L_Port */
|
|
(void)hifc_destroy_lport(v_hba);
|
|
|
|
/* 6. With HBA is present */
|
|
if (present == UNF_TRUE) {
|
|
/* Enable Queues dispatch */
|
|
hifc_enable_queues_dispatch(v_hba);
|
|
/* Need reset port if necessary */
|
|
(void)hifc_mbox_reset_chip(v_hba,
|
|
HIFC_MBOX_SUBTYPE_HEAVY_RESET);
|
|
|
|
/* Flush SCQ context */
|
|
hifc_flush_scq_ctx(v_hba);
|
|
|
|
/* Flush SRQ context */
|
|
hifc_flush_srq_ctx(v_hba);
|
|
|
|
/* Flush Root context in order to prevent DMA */
|
|
hifc_flush_root_ctx(v_hba);
|
|
|
|
/*
|
|
* NOTE: while flushing txrx, hash bucket will be cached out in
|
|
* UP. Wait to clear resources completely
|
|
*/
|
|
msleep(1000);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) flush scq & srq & root context done",
|
|
v_hba->port_cfg.port_id);
|
|
}
|
|
|
|
/* 7. Notify uP to close timer before delete SCQ */
|
|
ret = hifc_notify_up_close_timer(v_hba);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[err]HIFC port(0x%x) can't close timer",
|
|
v_hba->port_cfg.port_id);
|
|
}
|
|
|
|
/* 8. Release host resources */
|
|
hifc_release_host_res(v_hba);
|
|
|
|
/* 9. Destroy FC work queue */
|
|
if (v_hba->work_queue) {
|
|
flush_workqueue(v_hba->work_queue);
|
|
destroy_workqueue(v_hba->work_queue);
|
|
v_hba->work_queue = NULL;
|
|
}
|
|
|
|
/* 10. Release Probe index & Decrease card number */
|
|
hifc_release_probe_index(v_hba->probe_index);
|
|
hifc_dec_and_free_card_num((unsigned char)v_hba->card_info.card_num);
|
|
|
|
/* 11. Free HBA memory */
|
|
kfree(v_hba);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[event]PCI device(%p) remove succeed", v_dev);
|
|
}
|
|
|
|
void hifc_remove(struct hifc_lld_dev *lld_dev, void *uld_dev)
|
|
{
|
|
struct pci_dev *dev = NULL;
|
|
struct hifc_hba_s *hba = (struct hifc_hba_s *)uld_dev;
|
|
unsigned int probe_total_num = 0;
|
|
unsigned int probe_index = 0;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != uld_dev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev->hwdev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev->pdev, return);
|
|
|
|
dev = hba->pci_dev;
|
|
|
|
/* Get total probed port number */
|
|
hifc_get_total_probed_num(&probe_total_num);
|
|
if (probe_total_num < 1) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Port manager is empty and no need to remove");
|
|
return;
|
|
}
|
|
|
|
/* check pci vendor id */
|
|
if (dev->vendor != HIFC_PCI_VENDOR_ID_HUAWEI) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Wrong vendor id(0x%x) and exit", dev->vendor);
|
|
return;
|
|
}
|
|
|
|
/* Check function ability */
|
|
|
|
if (!(hifc_support_fc(lld_dev->hwdev, NULL))) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]FC is not enable in this function");
|
|
return;
|
|
}
|
|
|
|
/* Get probe index */
|
|
probe_index = hba->probe_index;
|
|
|
|
/* Parent context allocation check */
|
|
if (hba->fc_service_cap.dev_fc_cap.max_parent_qpc_num == 0) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]FC parent context not allocate in this function");
|
|
return;
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]HBA(0x%x) start removing...", hba->port_index);
|
|
|
|
/* HBA removinig... */
|
|
hifc_exit(dev, hba);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[event]Port(0x%x) pci device removed, vendorid(0x%04x) devid(0x%04x)",
|
|
probe_index, dev->vendor, dev->device);
|
|
|
|
/* Probe index check */
|
|
if (probe_index < HIFC_HBA_PORT_MAX_NUM) {
|
|
hifc_hba[probe_index] = NULL;
|
|
} else {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Probe index(0x%x) is invalid and remove failed",
|
|
probe_index);
|
|
}
|
|
|
|
hifc_get_total_probed_num(&probe_total_num);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[event]Removed index=%u, RemainNum=%u",
|
|
probe_index, probe_total_num);
|
|
}
|
|
|
|
void hifc_event(struct hifc_lld_dev *lld_dev, void *uld_dev,
|
|
struct hifc_event_info *event)
|
|
{
|
|
struct hifc_hba_s *hba = uld_dev;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev->hwdev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != lld_dev->pdev, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != hba, return);
|
|
HIFC_CHECK(INVALID_VALUE32, NULL != event, return);
|
|
|
|
switch (event->type) {
|
|
case HIFC_EVENT_HEART_LOST:
|
|
hba->heart_status = 0;
|
|
HIFC_COM_UP_ERR_EVENT_STAT(hba, HIFC_EVENT_HEART_LOST);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static unsigned int hifc_get_hba_pcie_link_state(void *v_hba,
|
|
void *v_link_state)
|
|
{
|
|
int *link_state = v_link_state;
|
|
int present = UNF_TRUE;
|
|
struct hifc_hba_s *hba = v_hba;
|
|
int ret;
|
|
int last_dev_state = UNF_TRUE;
|
|
int cur_dev_state = UNF_TRUE;
|
|
|
|
HIFC_CHECK(INVALID_VALUE32, v_hba, return UNF_RETURN_ERROR);
|
|
HIFC_CHECK(INVALID_VALUE32, v_link_state, return UNF_RETURN_ERROR);
|
|
last_dev_state = hba->dev_present;
|
|
ret = hifc_get_card_present_state(hba->hw_dev_handle, (bool *)&present);
|
|
if (ret || present != UNF_TRUE) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[event]port(0x%x) is not present,ret:%d, present:%d",
|
|
hba->port_cfg.port_id, ret, present);
|
|
cur_dev_state = UNF_FALSE;
|
|
} else {
|
|
cur_dev_state = UNF_TRUE;
|
|
}
|
|
|
|
hba->dev_present = cur_dev_state;
|
|
|
|
/* the heartbeat is considered lost only when the PCIE link is down for
|
|
* two times.
|
|
*/
|
|
if ((last_dev_state == UNF_FALSE) && (cur_dev_state == UNF_FALSE))
|
|
hba->heart_status = UNF_FALSE;
|
|
*link_state = hba->dev_present;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_ABNORMAL, UNF_INFO,
|
|
"Port:0x%x,get dev present:%d", hba->port_cfg.port_id,
|
|
*link_state);
|
|
return RETURN_OK;
|
|
}
|