#include "ocs.h"
#include "ocs_os.h"
#include "ocs_hw.h"
#include "ocs_hw_queues.h"
#define OCS_HW_MQ_DEPTH 128
#define OCS_HW_READ_FCF_SIZE 4096
#define OCS_HW_DEFAULT_AUTO_XFER_RDY_IOS 256
#define OCS_HW_WQ_TIMER_PERIOD_MS 500
#define OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT 0
#define OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT TRUE
#define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT FALSE
#define OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT 0
#define OCS_HW_REQUE_XRI_REGTAG 65534
#define OCS_HW_DMTF_CLP_CMD_MAX 256
#define OCS_HW_DMTF_CLP_RSP_MAX 256
ocs_hw_global_t hw_global;
static void ocs_hw_queue_hash_add(ocs_queue_hash_t *, uint16_t, uint16_t);
static void ocs_hw_adjust_wqs(ocs_hw_t *hw);
static uint32_t ocs_hw_get_num_chutes(ocs_hw_t *hw);
static int32_t ocs_hw_cb_link(void *, void *);
static int32_t ocs_hw_cb_fip(void *, void *);
static int32_t ocs_hw_command_process(ocs_hw_t *, int32_t, uint8_t *, size_t);
static int32_t ocs_hw_mq_process(ocs_hw_t *, int32_t, sli4_queue_t *);
static int32_t ocs_hw_cb_read_fcf(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_node_attach(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_node_free(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_node_free_all(ocs_hw_t *, int32_t, uint8_t *, void *);
static ocs_hw_rtn_e ocs_hw_setup_io(ocs_hw_t *);
static ocs_hw_rtn_e ocs_hw_init_io(ocs_hw_t *);
static int32_t ocs_hw_flush(ocs_hw_t *);
static int32_t ocs_hw_command_cancel(ocs_hw_t *);
static int32_t ocs_hw_io_cancel(ocs_hw_t *);
static void ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io);
static void ocs_hw_io_restore_sgl(ocs_hw_t *, ocs_hw_io_t *);
static int32_t ocs_hw_io_ini_sge(ocs_hw_t *, ocs_hw_io_t *, ocs_dma_t *, uint32_t, ocs_dma_t *);
static ocs_hw_rtn_e ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg);
static int32_t ocs_hw_cb_fw_write(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_sfp(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_temp(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_link_stat(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
static void ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
static int32_t ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len);
typedef void (*ocs_hw_dmtf_clp_cb_t)(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
static ocs_hw_rtn_e ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg);
static void ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg);
static int32_t __ocs_read_topology_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
static ocs_hw_rtn_e ocs_hw_get_linkcfg(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
static ocs_hw_rtn_e ocs_hw_get_linkcfg_lancer(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
static ocs_hw_rtn_e ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *, uint32_t, ocs_hw_port_control_cb_t, void *);
static ocs_hw_rtn_e ocs_hw_set_linkcfg(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
static ocs_hw_rtn_e ocs_hw_set_linkcfg_lancer(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
static ocs_hw_rtn_e ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *, ocs_hw_linkcfg_e, uint32_t, ocs_hw_port_control_cb_t, void *);
static void ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
static ocs_hw_rtn_e ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license);
static ocs_hw_rtn_e ocs_hw_set_dif_seed(ocs_hw_t *hw);
static ocs_hw_rtn_e ocs_hw_set_dif_mode(ocs_hw_t *hw);
static void ocs_hw_io_free_internal(void *arg);
static void ocs_hw_io_free_port_owned(void *arg);
static ocs_hw_rtn_e ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf);
static ocs_hw_rtn_e ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint);
static void ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status);
static int32_t ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t, uint16_t, uint16_t);
static ocs_hw_rtn_e ocs_hw_config_watchdog_timer(ocs_hw_t *hw);
static ocs_hw_rtn_e ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable);
static int32_t ocs_hw_domain_add(ocs_hw_t *, ocs_domain_t *);
static int32_t ocs_hw_domain_del(ocs_hw_t *, ocs_domain_t *);
static void *__ocs_hw_port_alloc_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_port_done(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_domain_init(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void * __ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *, ocs_sm_event_t, void *);
static void *__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data);
static int32_t __ocs_hw_domain_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t __ocs_hw_port_cb(ocs_hw_t *, int32_t, uint8_t *, void *);
static int32_t __ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg);
static void ocs_hw_check_sec_hio_list(ocs_hw_t *hw);
static void target_wqe_timer_cb(void *arg);
static void shutdown_target_wqe_timer(ocs_hw_t *hw);
static inline uint8_t
ocs_hw_set_io_wqe_timeout(ocs_hw_io_t *io, uint32_t timeout)
{
if (timeout > 255) {
io->wqe_timeout = timeout;
return 0;
} else {
return timeout;
}
}
static inline void
ocs_hw_add_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (hw->config.emulate_wqe_timeout && io->wqe_timeout) {
ocs_lock(&hw->io_lock);
ocs_list_add_tail(&hw->io_timed_wqe, io);
getmicrouptime(&io->submit_time);
ocs_unlock(&hw->io_lock);
}
}
static inline void
ocs_hw_remove_io_timed_wqe(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (hw->config.emulate_wqe_timeout) {
ocs_lock(&hw->io_lock);
if (ocs_list_on_list(&io->wqe_link)) {
ocs_list_remove(&hw->io_timed_wqe, io);
}
ocs_unlock(&hw->io_lock);
}
}
static uint8_t ocs_hw_iotype_is_originator(uint16_t io_type)
{
switch (io_type) {
case OCS_HW_IO_INITIATOR_READ:
case OCS_HW_IO_INITIATOR_WRITE:
case OCS_HW_IO_INITIATOR_NODATA:
case OCS_HW_FC_CT:
case OCS_HW_ELS_REQ:
return 1;
default:
return 0;
}
}
static uint8_t ocs_hw_wcqe_abort_needed(uint16_t status, uint8_t ext, uint8_t xb)
{
if (!xb) {
return FALSE;
}
if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT) {
switch (ext) {
case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
return FALSE;
default:
break;
}
}
return TRUE;
}
static uint32_t
ocs_hw_get_num_chutes(ocs_hw_t *hw)
{
uint32_t num_chutes = 1;
if (sli_get_is_dual_ulp_capable(&hw->sli) &&
sli_get_is_ulp_enabled(&hw->sli, 0) &&
sli_get_is_ulp_enabled(&hw->sli, 1)) {
num_chutes = 2;
}
return num_chutes;
}
static ocs_hw_rtn_e
ocs_hw_link_event_init(ocs_hw_t *hw)
{
ocs_hw_assert(hw);
hw->link.status = SLI_LINK_STATUS_MAX;
hw->link.topology = SLI_LINK_TOPO_NONE;
hw->link.medium = SLI_LINK_MEDIUM_MAX;
hw->link.speed = 0;
hw->link.loop_map = NULL;
hw->link.fc_id = UINT32_MAX;
return OCS_HW_RTN_SUCCESS;
}
static ocs_hw_rtn_e
ocs_hw_read_max_dump_size(ocs_hw_t *hw)
{
uint8_t buf[SLI4_BMBX_SIZE];
uint8_t bus, dev, func;
int rc;
if ((SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) &&
(SLI4_IF_TYPE_LANCER_G7 != sli_get_if_type(&hw->sli))) {
ocs_log_debug(hw->os, "Function only supported for I/F type 2\n");
return OCS_HW_RTN_ERROR;
}
if (hw->workaround.disable_dump_loc) {
ocs_log_test(hw->os, "FW version is too old for this feature\n");
return OCS_HW_RTN_ERROR;
}
ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
if (func == 0) {
if (sli_cmd_common_set_dump_location(&hw->sli, buf,
SLI4_BMBX_SIZE, 1, 0, NULL, 0)) {
sli4_res_common_set_dump_location_t *rsp =
(sli4_res_common_set_dump_location_t *)
(buf + offsetof(sli4_cmd_sli_config_t,
payload.embed));
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "set dump location command failed\n");
return rc;
} else {
hw->dump_size = rsp->buffer_length;
ocs_log_debug(hw->os, "Dump size %x\n", rsp->buffer_length);
}
}
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_setup(ocs_hw_t *hw, ocs_os_handle_t os, sli4_port_type_e port_type)
{
uint32_t i;
char prop_buf[32];
if (hw == NULL) {
ocs_log_err(os, "bad parameter(s) hw=%p\n", hw);
return OCS_HW_RTN_ERROR;
}
if (hw->hw_setup_called) {
ocs_hw_workaround_setup(hw);
return OCS_HW_RTN_SUCCESS;
}
ocs_memset(hw, 0, sizeof(ocs_hw_t));
hw->hw_setup_called = TRUE;
hw->os = os;
ocs_lock_init(hw->os, &hw->cmd_lock, "HW_cmd_lock[%d]", ocs_instance(hw->os));
ocs_list_init(&hw->cmd_head, ocs_command_ctx_t, link);
ocs_list_init(&hw->cmd_pending, ocs_command_ctx_t, link);
hw->cmd_head_count = 0;
ocs_lock_init(hw->os, &hw->io_lock, "HW_io_lock[%d]", ocs_instance(hw->os));
ocs_lock_init(hw->os, &hw->io_abort_lock, "HW_io_abort_lock[%d]", ocs_instance(hw->os));
ocs_atomic_init(&hw->io_alloc_failed_count, 0);
hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
hw->config.dif_seed = 0;
hw->config.auto_xfer_rdy_blk_size_chip = OCS_HW_AUTO_XFER_RDY_BLK_SIZE_DEFAULT;
hw->config.auto_xfer_rdy_ref_tag_is_lba = OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA_DEFAULT;
hw->config.auto_xfer_rdy_app_tag_valid = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID_DEFAULT;
hw->config.auto_xfer_rdy_app_tag_value = OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE_DEFAULT;
if (sli_setup(&hw->sli, hw->os, port_type)) {
ocs_log_err(hw->os, "SLI setup failed\n");
return OCS_HW_RTN_ERROR;
}
ocs_memset(hw->domains, 0, sizeof(hw->domains));
ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
ocs_hw_link_event_init(hw);
sli_callback(&hw->sli, SLI4_CB_LINK, ocs_hw_cb_link, hw);
sli_callback(&hw->sli, SLI4_CB_FIP, ocs_hw_cb_fip, hw);
for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) {
hw->num_qentries[i] = sli_get_max_qentries(&hw->sli, i);
}
hw->config.rq_default_buffer_size = OCS_HW_RQ_SIZE_PAYLOAD;
hw->config.n_io = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
if (ocs_get_property("auto_xfer_rdy_xri_cnt", prop_buf, sizeof(prop_buf)) == 0) {
hw->config.auto_xfer_rdy_xri_cnt = ocs_strtoul(prop_buf, 0, 0);
}
hw->config.i_only_aab = TRUE;
ocs_hw_workaround_setup(hw);
if (hw->workaround.override_fcfi) {
hw->first_domain_idx = -1;
}
if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))) {
(void)ocs_hw_read_max_dump_size(hw);
}
ocs_hw_adjust_wqs(hw);
if (! sli_is_dif_inline_capable(&hw->sli)) {
ocs_log_test(hw->os, "not inline capable, setting mode to separate\n");
hw->config.dif_mode = OCS_HW_DIF_MODE_SEPARATE;
}
if (hw->workaround.use_dif_sec_xri) {
ocs_list_init(&hw->sec_hio_wait_list, ocs_hw_io_t, link);
}
if (sli_get_is_dual_ulp_capable(&hw->sli)) {
if (sli_get_is_ulp_enabled(&hw->sli, 0) &&
sli_get_is_ulp_enabled(&hw->sli, 1)) {
hw->ulp_start = 0;
hw->ulp_max = 1;
} else if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
hw->ulp_start = 0;
hw->ulp_max = 0;
} else {
hw->ulp_start = 1;
hw->ulp_max = 1;
}
} else {
if (sli_get_is_ulp_enabled(&hw->sli, 0)) {
hw->ulp_start = 0;
hw->ulp_max = 0;
} else {
hw->ulp_start = 1;
hw->ulp_max = 1;
}
}
ocs_log_debug(hw->os, "ulp_start %d, ulp_max %d\n",
hw->ulp_start, hw->ulp_max);
hw->config.queue_topology = hw_global.queue_topology_string;
hw->qtop = ocs_hw_qtop_parse(hw, hw->config.queue_topology);
hw->config.n_eq = hw->qtop->entry_counts[QTOP_EQ];
hw->config.n_cq = hw->qtop->entry_counts[QTOP_CQ];
hw->config.n_rq = hw->qtop->entry_counts[QTOP_RQ];
hw->config.n_wq = hw->qtop->entry_counts[QTOP_WQ];
hw->config.n_mq = hw->qtop->entry_counts[QTOP_MQ];
if (hw->config.n_rq > OCE_HW_MAX_NUM_MRQ_PAIRS) {
ocs_log_crit(hw->os, "Max supported MRQ pairs = %d\n",
OCE_HW_MAX_NUM_MRQ_PAIRS);
return OCS_HW_RTN_ERROR;
}
if (hw->config.n_eq > OCS_HW_MAX_NUM_EQ) {
ocs_log_crit(hw->os, "Max supported EQs = %d\n",
OCS_HW_MAX_NUM_EQ);
return OCS_HW_RTN_ERROR;
}
if (hw->config.n_cq > OCS_HW_MAX_NUM_CQ) {
ocs_log_crit(hw->os, "Max supported CQs = %d\n",
OCS_HW_MAX_NUM_CQ);
return OCS_HW_RTN_ERROR;
}
if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
ocs_log_crit(hw->os, "Max supported WQs = %d\n",
OCS_HW_MAX_NUM_WQ);
return OCS_HW_RTN_ERROR;
}
if (hw->config.n_mq > OCS_HW_MAX_NUM_MQ) {
ocs_log_crit(hw->os, "Max supported MQs = %d\n",
OCS_HW_MAX_NUM_MQ);
return OCS_HW_RTN_ERROR;
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_init(ocs_hw_t *hw)
{
ocs_hw_rtn_e rc;
uint32_t i = 0;
uint8_t buf[SLI4_BMBX_SIZE];
uint32_t max_rpi;
int rem_count;
int written_size = 0;
uint32_t count;
char prop_buf[32];
uint32_t ramdisc_blocksize = 512;
uint32_t q_count = 0;
ocs_lock(&hw->cmd_lock);
if (!ocs_list_empty(&hw->cmd_head)) {
ocs_log_test(hw->os, "command found on cmd list\n");
ocs_unlock(&hw->cmd_lock);
return OCS_HW_RTN_ERROR;
}
if (!ocs_list_empty(&hw->cmd_pending)) {
ocs_log_test(hw->os, "command found on pending list\n");
ocs_unlock(&hw->cmd_lock);
return OCS_HW_RTN_ERROR;
}
ocs_unlock(&hw->cmd_lock);
ocs_hw_rx_free(hw);
rem_count=0;
if (ocs_list_valid(&hw->io_wait_free)) {
while ((!ocs_list_empty(&hw->io_wait_free))) {
rem_count++;
ocs_list_remove_head(&hw->io_wait_free);
}
if (rem_count > 0) {
ocs_log_debug(hw->os, "removed %d items from io_wait_free list\n", rem_count);
}
}
rem_count=0;
if (ocs_list_valid(&hw->io_inuse)) {
while ((!ocs_list_empty(&hw->io_inuse))) {
rem_count++;
ocs_list_remove_head(&hw->io_inuse);
}
if (rem_count > 0) {
ocs_log_debug(hw->os, "removed %d items from io_inuse list\n", rem_count);
}
}
rem_count=0;
if (ocs_list_valid(&hw->io_free)) {
while ((!ocs_list_empty(&hw->io_free))) {
rem_count++;
ocs_list_remove_head(&hw->io_free);
}
if (rem_count > 0) {
ocs_log_debug(hw->os, "removed %d items from io_free list\n", rem_count);
}
}
if (ocs_list_valid(&hw->io_port_owned)) {
while ((!ocs_list_empty(&hw->io_port_owned))) {
ocs_list_remove_head(&hw->io_port_owned);
}
}
ocs_list_init(&hw->io_inuse, ocs_hw_io_t, link);
ocs_list_init(&hw->io_free, ocs_hw_io_t, link);
ocs_list_init(&hw->io_port_owned, ocs_hw_io_t, link);
ocs_list_init(&hw->io_wait_free, ocs_hw_io_t, link);
ocs_list_init(&hw->io_timed_wqe, ocs_hw_io_t, wqe_link);
ocs_list_init(&hw->io_port_dnrx, ocs_hw_io_t, dnrx_link);
if (hw->config.n_rq == 1) {
hw->sli.config.features.flag.mrqp = FALSE;
}
if (sli_init(&hw->sli)) {
ocs_log_err(hw->os, "SLI failed to initialize\n");
return OCS_HW_RTN_ERROR;
}
hw->auto_xfer_rdy_enabled = FALSE;
if (sli_get_auto_xfer_rdy_capable(&hw->sli) &&
hw->config.auto_xfer_rdy_size > 0) {
if (hw->config.esoc){
if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
}
written_size = sli_cmd_config_auto_xfer_rdy_hp(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size, 1, ramdisc_blocksize);
} else {
written_size = sli_cmd_config_auto_xfer_rdy(&hw->sli, buf, SLI4_BMBX_SIZE, hw->config.auto_xfer_rdy_size);
}
if (written_size) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "config auto xfer rdy failed\n");
return rc;
}
}
hw->auto_xfer_rdy_enabled = TRUE;
if (hw->config.auto_xfer_rdy_t10_enable) {
rc = ocs_hw_config_auto_xfer_rdy_t10pi(hw, buf);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "set parameters auto xfer rdy T10 PI failed\n");
return rc;
}
}
}
if(hw->sliport_healthcheck) {
rc = ocs_hw_config_sli_port_health_check(hw, 0, 1);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "Enabling Sliport Health check failed \n");
return rc;
}
}
if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) && (OCS_HW_FDT_XFER_HINT != 0)) {
ocs_hw_config_set_fdt_xfer_hint(hw, OCS_HW_FDT_XFER_HINT);
}
q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_EQ),
OCS_HW_MAX_NUM_EQ);
if (hw->config.n_eq > q_count) {
ocs_log_err(hw->os, "requested %d EQ but %d allowed\n",
hw->config.n_eq, q_count);
return OCS_HW_RTN_ERROR;
}
q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_CQ),
OCS_HW_MAX_NUM_CQ);
if (hw->config.n_cq > q_count) {
ocs_log_err(hw->os, "requested %d CQ but %d allowed\n",
hw->config.n_cq, q_count);
return OCS_HW_RTN_ERROR;
}
q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_MQ),
OCS_HW_MAX_NUM_MQ);
if (hw->config.n_mq > q_count) {
ocs_log_err(hw->os, "requested %d MQ but %d allowed\n",
hw->config.n_mq, q_count);
return OCS_HW_RTN_ERROR;
}
q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_RQ),
OCS_HW_MAX_NUM_RQ);
if (hw->config.n_rq > q_count) {
ocs_log_err(hw->os, "requested %d RQ but %d allowed\n",
hw->config.n_rq, q_count);
return OCS_HW_RTN_ERROR;
}
q_count = MIN(sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ),
OCS_HW_MAX_NUM_WQ);
if (hw->config.n_wq > q_count) {
ocs_log_err(hw->os, "requested %d WQ but %d allowed\n",
hw->config.n_wq, q_count);
return OCS_HW_RTN_ERROR;
}
ocs_memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
ocs_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
OCS_HW_MAX_NUM_CQ, OCS_HW_Q_HASH_SIZE);
ocs_memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
ocs_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
OCS_HW_MAX_NUM_RQ, OCS_HW_Q_HASH_SIZE);
ocs_memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
ocs_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
OCS_HW_MAX_NUM_WQ, OCS_HW_Q_HASH_SIZE);
rc = ocs_hw_init_queues(hw, hw->qtop);
if (rc != OCS_HW_RTN_SUCCESS) {
return rc;
}
max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
i = sli_fc_get_rpi_requirements(&hw->sli, max_rpi);
if (i) {
ocs_dma_t payload_memory;
rc = OCS_HW_RTN_ERROR;
if (hw->rnode_mem.size) {
ocs_dma_free(hw->os, &hw->rnode_mem);
}
if (ocs_dma_alloc(hw->os, &hw->rnode_mem, i, 4096)) {
ocs_log_err(hw->os, "remote node memory allocation fail\n");
return OCS_HW_RTN_NO_MEMORY;
}
payload_memory.size = 0;
if (sli_cmd_fcoe_post_hdr_templates(&hw->sli, buf, SLI4_BMBX_SIZE,
&hw->rnode_mem, UINT16_MAX, &payload_memory)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (payload_memory.size != 0) {
ocs_dma_free(hw->os, &payload_memory);
}
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "header template registration failed\n");
return rc;
}
}
rc = ocs_hw_rx_allocate(hw);
if (rc) {
ocs_log_err(hw->os, "rx_allocate failed\n");
return rc;
}
if (hw->seq_pool == NULL) {
uint32_t count = 0;
uint32_t i;
for (i = 0; i < hw->hw_rq_count; i++) {
count += hw->hw_rq[i]->entry_count;
}
hw->seq_pool = ocs_array_alloc(hw->os, sizeof(ocs_hw_sequence_t), count);
if (hw->seq_pool == NULL) {
ocs_log_err(hw->os, "malloc seq_pool failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
}
if(ocs_hw_rx_post(hw)) {
ocs_log_err(hw->os, "WARNING - error posting RQ buffers\n");
}
if (hw->rpi_ref == NULL) {
hw->rpi_ref = ocs_malloc(hw->os, max_rpi * sizeof(*hw->rpi_ref),
OCS_M_ZERO | OCS_M_NOWAIT);
if (hw->rpi_ref == NULL) {
ocs_log_err(hw->os, "rpi_ref allocation failure (%d)\n", i);
return OCS_HW_RTN_NO_MEMORY;
}
}
for (i = 0; i < max_rpi; i ++) {
ocs_atomic_init(&hw->rpi_ref[i].rpi_count, 0);
ocs_atomic_init(&hw->rpi_ref[i].rpi_attached, 0);
}
ocs_memset(hw->domains, 0, sizeof(hw->domains));
if (hw->workaround.override_fcfi) {
hw->first_domain_idx = -1;
}
ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
if (hw->hw_mrq_count) {
ocs_log_debug(hw->os, "using REG_FCFI MRQ\n");
rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0, 0);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "REG_FCFI_MRQ FCFI registration failed\n");
return rc;
}
rc = ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0, 0);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "REG_FCFI_MRQ MRQ registration failed\n");
return rc;
}
} else {
sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
ocs_log_debug(hw->os, "using REG_FCFI standard\n");
for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
rq_cfg[i].rq_id = 0xffff;
rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
}
for (i = 0; i < OCS_MIN(hw->hw_rq_count, SLI4_CMD_REG_FCFI_NUM_RQ_CFG); i++) {
hw_rq_t *rq = hw->hw_rq[i];
uint32_t j;
for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
uint32_t mask = (rq->filter_mask != 0) ? rq->filter_mask : 1;
if (mask & (1U << j)) {
rq_cfg[j].rq_id = rq->hdr->id;
ocs_log_debug(hw->os, "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
j, hw->config.filter_def[j], i, rq->hdr->id);
}
}
}
rc = OCS_HW_RTN_ERROR;
if (sli_cmd_reg_fcfi(&hw->sli, buf, SLI4_BMBX_SIZE, 0, rq_cfg, 0)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "FCFI registration failed\n");
return rc;
}
hw->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)buf)->fcfi;
}
}
rc = ocs_hw_reqtag_init(hw);
if (rc) {
ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed: %d\n", rc);
return rc;
}
rc = ocs_hw_setup_io(hw);
if (rc) {
ocs_log_err(hw->os, "IO allocation failure\n");
return rc;
}
rc = ocs_hw_init_io(hw);
if (rc) {
ocs_log_err(hw->os, "IO initialization failure\n");
return rc;
}
ocs_queue_history_init(hw->os, &hw->q_hist);
hw->linkcfg = OCS_HW_LINKCFG_NA;
ocs_hw_get_linkcfg(hw, OCS_CMD_POLL, ocs_hw_init_linkcfg_cb, hw);
if ((hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) &&
(sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_ETHERNET)) {
if (ocs_hw_set_eth_license(hw, hw->eth_license)) {
ocs_log_err(hw->os, "Failed to set ethernet license\n");
}
}
if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli) &&
ocs_hw_set_dif_seed(hw) != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "Failed to set DIF seed value\n");
return rc;
}
if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli) &&
sli_get_dif_capable(&hw->sli)) {
rc = ocs_hw_set_dif_mode(hw);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "Failed to set DIF mode value\n");
return rc;
}
}
for (i = 0; i < hw->eq_count; i++) {
sli_queue_arm(&hw->sli, &hw->eq[i], TRUE);
}
for (i = 0; i < hw->rq_count; i++) {
ocs_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
}
for (i = 0; i < hw->wq_count; i++) {
ocs_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
}
for (i = 0; i < hw->cq_count; i++) {
ocs_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
sli_queue_arm(&hw->sli, &hw->cq[i], TRUE);
}
hw->state = OCS_HW_STATE_ACTIVE;
if (ocs_hw_rqpair_init(hw)) {
ocs_log_err(hw->os, "WARNING - error initializing RQ pair\n");
}
if (hw->config.emulate_wqe_timeout) {
ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw,
OCS_HW_WQ_TIMER_PERIOD_MS);
}
if ((count = ocs_varray_get_count(hw->wq_class_array[1])) > 0) {
for (i = 0; i < count; i++) {
hw_wq_t *wq = ocs_varray_iter_next(hw->wq_class_array[1]);
wq->send_frame_io = ocs_hw_io_alloc(hw);
if (wq->send_frame_io == NULL) {
ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
}
}
} else {
hw->hw_wq[0]->send_frame_io = ocs_hw_io_alloc(hw);
if (hw->hw_wq[0]->send_frame_io == NULL) {
ocs_log_err(hw->os, "ocs_hw_io_alloc for send_frame_io failed\n");
}
}
ocs_atomic_init(&hw->send_frame_seq_id, 0);
hw->expiration_logged = 0;
if(hw->watchdog_timeout) {
if((hw->watchdog_timeout < 1) || (hw->watchdog_timeout > 65534)) {
ocs_log_err(hw->os, "watchdog_timeout out of range: Valid range is 1 - 65534\n");
}else if(!ocs_hw_config_watchdog_timer(hw)) {
ocs_log_info(hw->os, "watchdog timer configured with timeout = %d seconds \n", hw->watchdog_timeout);
}
}
if (ocs_dma_alloc(hw->os, &hw->domain_dmem, 112, 4)) {
ocs_log_err(hw->os, "domain node memory allocation fail\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (ocs_dma_alloc(hw->os, &hw->fcf_dmem, OCS_HW_READ_FCF_SIZE, OCS_HW_READ_FCF_SIZE)) {
ocs_log_err(hw->os, "domain fcf memory allocation fail\n");
return OCS_HW_RTN_NO_MEMORY;
}
if ((0 == hw->loop_map.size) && ocs_dma_alloc(hw->os, &hw->loop_map,
SLI4_MIN_LOOP_MAP_BYTES, 4)) {
ocs_log_err(hw->os, "Loop dma alloc failed size:%d \n", hw->loop_map.size);
}
return OCS_HW_RTN_SUCCESS;
}
static int32_t
ocs_hw_config_mrq(ocs_hw_t *hw, uint8_t mode, uint16_t vlanid, uint16_t fcf_index)
{
uint8_t buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
hw_rq_t *rq;
sli4_cmd_reg_fcfi_mrq_t *rsp = NULL;
uint32_t i, j;
sli4_cmd_rq_cfg_t rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
int32_t rc;
if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
goto issue_cmd;
}
for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
rq_filter[i].rq_id = 0xffff;
rq_filter[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
rq_filter[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
rq_filter[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
rq_filter[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
}
for (i = 0; i < hw->hw_rq_count; i++) {
rq = hw->hw_rq[i];
for (j = 0; j < SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG; j++) {
if (rq->filter_mask & (1U << j)) {
if (rq_filter[j].rq_id != 0xffff) {
if (!rq->is_mrq || (rq_filter[j].rq_id != rq->base_mrq_id)) {
ocs_log_err(hw->os, "Wrong queue topology.\n");
return OCS_HW_RTN_ERROR;
}
continue;
}
if (rq->is_mrq) {
rq_filter[j].rq_id = rq->base_mrq_id;
mrq_bitmask |= (1U << j);
} else {
rq_filter[j].rq_id = rq->hdr->id;
}
}
}
}
issue_cmd:
rc = sli_cmd_reg_fcfi_mrq(&hw->sli,
buf,
SLI4_BMBX_SIZE,
mode,
fcf_index,
vlanid,
hw->config.rq_selection_policy,
mrq_bitmask,
hw->hw_mrq_count,
rq_filter);
if (rc == 0) {
ocs_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed: %d\n", rc);
return OCS_HW_RTN_ERROR;
}
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
rsp = (sli4_cmd_reg_fcfi_mrq_t *)buf;
if ((rc != OCS_HW_RTN_SUCCESS) || (rsp->hdr.status)) {
ocs_log_err(hw->os, "FCFI MRQ registration failed. cmd = %x status = %x\n",
rsp->hdr.command, rsp->hdr.status);
return OCS_HW_RTN_ERROR;
}
if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
hw->fcf_indicator = rsp->fcfi;
}
return 0;
}
static void
ocs_hw_init_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
{
ocs_hw_t *hw = (ocs_hw_t *)arg;
if (status == 0) {
hw->linkcfg = (ocs_hw_linkcfg_e)value;
} else {
hw->linkcfg = OCS_HW_LINKCFG_NA;
}
ocs_log_debug(hw->os, "linkcfg=%d\n", hw->linkcfg);
}
ocs_hw_rtn_e
ocs_hw_teardown(ocs_hw_t *hw)
{
uint32_t i = 0;
uint32_t iters = 10;
uint32_t max_rpi;
uint32_t destroy_queues;
uint32_t free_memory;
if (!hw) {
ocs_log_err(NULL, "bad parameter(s) hw=%p\n", hw);
return OCS_HW_RTN_ERROR;
}
destroy_queues = (hw->state == OCS_HW_STATE_ACTIVE);
free_memory = (hw->state != OCS_HW_STATE_UNINITIALIZED);
shutdown_target_wqe_timer(hw);
if(hw->watchdog_timeout) {
hw->watchdog_timeout = 0;
ocs_hw_config_watchdog_timer(hw);
}
if(hw->sliport_healthcheck) {
hw->sliport_healthcheck = 0;
ocs_hw_config_sli_port_health_check(hw, 0, 0);
}
if (hw->state != OCS_HW_STATE_QUEUES_ALLOCATED) {
hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
ocs_hw_flush(hw);
while (!ocs_list_empty(&hw->cmd_head) && iters) {
ocs_udelay(10000);
ocs_hw_flush(hw);
iters--;
}
if (ocs_list_empty(&hw->cmd_head)) {
ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
} else {
ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
}
ocs_hw_command_cancel(hw);
} else {
hw->state = OCS_HW_STATE_TEARDOWN_IN_PROGRESS;
}
ocs_lock_free(&hw->cmd_lock);
if (hw->workaround.use_unregistered_rpi) {
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, hw->workaround.unregistered_rid);
}
max_rpi = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
if (hw->rpi_ref) {
for (i = 0; i < max_rpi; i++) {
if (ocs_atomic_read(&hw->rpi_ref[i].rpi_count)) {
ocs_log_debug(hw->os, "non-zero ref [%d]=%d\n",
i, ocs_atomic_read(&hw->rpi_ref[i].rpi_count));
}
}
ocs_free(hw->os, hw->rpi_ref, max_rpi * sizeof(*hw->rpi_ref));
hw->rpi_ref = NULL;
}
ocs_dma_free(hw->os, &hw->rnode_mem);
if (hw->io) {
for (i = 0; i < hw->config.n_io; i++) {
if (hw->io[i] && (hw->io[i]->sgl != NULL) &&
(hw->io[i]->sgl->virt != NULL)) {
if(hw->io[i]->is_port_owned) {
ocs_lock_free(&hw->io[i]->axr_lock);
}
ocs_dma_free(hw->os, hw->io[i]->sgl);
}
ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
hw->io[i] = NULL;
}
ocs_free(hw->os, hw->wqe_buffs, hw->config.n_io * hw->sli.config.wqe_size);
hw->wqe_buffs = NULL;
ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t *));
hw->io = NULL;
}
ocs_dma_free(hw->os, &hw->xfer_rdy);
ocs_dma_free(hw->os, &hw->dump_sges);
ocs_dma_free(hw->os, &hw->loop_map);
ocs_lock_free(&hw->io_lock);
ocs_lock_free(&hw->io_abort_lock);
for (i = 0; i < hw->wq_count; i++) {
sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues, free_memory);
}
for (i = 0; i < hw->rq_count; i++) {
sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, free_memory);
}
for (i = 0; i < hw->mq_count; i++) {
sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, free_memory);
}
for (i = 0; i < hw->cq_count; i++) {
sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, free_memory);
}
for (i = 0; i < hw->eq_count; i++) {
sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, free_memory);
}
ocs_hw_qtop_free(hw->qtop);
ocs_hw_rx_free(hw);
hw_queue_teardown(hw);
ocs_hw_rqpair_teardown(hw);
if (sli_teardown(&hw->sli)) {
ocs_log_err(hw->os, "SLI teardown failed\n");
}
ocs_queue_history_free(&hw->q_hist);
hw->state = OCS_HW_STATE_UNINITIALIZED;
ocs_array_free(hw->seq_pool);
hw->seq_pool = NULL;
ocs_pool_free(hw->wq_reqtag_pool);
ocs_dma_free(hw->os, &hw->domain_dmem);
ocs_dma_free(hw->os, &hw->fcf_dmem);
hw->hw_setup_called = FALSE;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_reset(ocs_hw_t *hw, ocs_hw_reset_e reset)
{
uint32_t i;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint32_t iters;
ocs_hw_state_e prev_state = hw->state;
if (hw->state != OCS_HW_STATE_ACTIVE) {
ocs_log_test(hw->os, "HW state %d is not active\n", hw->state);
}
hw->state = OCS_HW_STATE_RESET_IN_PROGRESS;
shutdown_target_wqe_timer(hw);
ocs_hw_flush(hw);
iters = 10;
while (!ocs_list_empty(&hw->cmd_head) && iters) {
ocs_udelay(10000);
ocs_hw_flush(hw);
iters--;
}
if (ocs_list_empty(&hw->cmd_head)) {
ocs_log_debug(hw->os, "All commands completed on MQ queue\n");
} else {
ocs_log_debug(hw->os, "Some commands still pending on MQ queue\n");
}
switch(reset) {
case OCS_HW_RESET_FUNCTION:
ocs_log_debug(hw->os, "issuing function level reset\n");
if (sli_reset(&hw->sli)) {
ocs_log_err(hw->os, "sli_reset failed\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_RESET_FIRMWARE:
ocs_log_debug(hw->os, "issuing firmware reset\n");
if (sli_fw_reset(&hw->sli)) {
ocs_log_err(hw->os, "sli_soft_reset failed\n");
rc = OCS_HW_RTN_ERROR;
}
ocs_log_debug(hw->os, "issuing function level reset\n");
if (sli_reset(&hw->sli)) {
ocs_log_err(hw->os, "sli_reset failed\n");
rc = OCS_HW_RTN_ERROR;
}
break;
default:
ocs_log_test(hw->os, "unknown reset type - no reset performed\n");
hw->state = prev_state;
return OCS_HW_RTN_ERROR;
}
if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
ocs_hw_command_cancel(hw);
ocs_hw_io_cancel(hw);
ocs_memset(hw->domains, 0, sizeof(hw->domains));
ocs_memset(hw->fcf_index_fcfi, 0, sizeof(hw->fcf_index_fcfi));
ocs_hw_link_event_init(hw);
ocs_lock(&hw->io_lock);
while (!ocs_list_empty(&hw->io_timed_wqe)) {
ocs_list_remove_head(&hw->io_timed_wqe);
}
while (!ocs_list_empty(&hw->io_free)) {
ocs_list_remove_head(&hw->io_free);
}
while (!ocs_list_empty(&hw->io_wait_free)) {
ocs_list_remove_head(&hw->io_wait_free);
}
ocs_hw_reqtag_reset(hw);
ocs_unlock(&hw->io_lock);
}
if (prev_state != OCS_HW_STATE_UNINITIALIZED) {
for (i = 0; i < hw->wq_count; i++) {
sli_queue_reset(&hw->sli, &hw->wq[i]);
}
for (i = 0; i < hw->rq_count; i++) {
sli_queue_reset(&hw->sli, &hw->rq[i]);
}
for (i = 0; i < hw->hw_rq_count; i++) {
hw_rq_t *rq = hw->hw_rq[i];
if (rq->rq_tracker != NULL) {
uint32_t j;
for (j = 0; j < rq->entry_count; j++) {
rq->rq_tracker[j] = NULL;
}
}
}
for (i = 0; i < hw->mq_count; i++) {
sli_queue_reset(&hw->sli, &hw->mq[i]);
}
for (i = 0; i < hw->cq_count; i++) {
sli_queue_reset(&hw->sli, &hw->cq[i]);
}
for (i = 0; i < hw->eq_count; i++) {
sli_queue_reset(&hw->sli, &hw->eq[i]);
}
ocs_hw_rx_free(hw);
hw_queue_teardown(hw);
} else {
ocs_hw_rx_free(hw);
}
ocs_hw_workaround_setup(hw);
hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
return rc;
}
int32_t
ocs_hw_get_num_eq(ocs_hw_t *hw)
{
return hw->eq_count;
}
static int32_t
ocs_hw_get_fw_timed_out(ocs_hw_t *hw)
{
return (sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1) == 0x2 &&
sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2) == 0x10);
}
ocs_hw_rtn_e
ocs_hw_get(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t *value)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
int32_t tmp;
if (!value) {
return OCS_HW_RTN_ERROR;
}
*value = 0;
switch (prop) {
case OCS_HW_N_IO:
*value = hw->config.n_io;
break;
case OCS_HW_N_SGL:
*value = (hw->config.n_sgl - SLI4_SGE_MAX_RESERVED);
break;
case OCS_HW_MAX_IO:
*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI);
break;
case OCS_HW_MAX_NODES:
*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI);
break;
case OCS_HW_MAX_RQ_ENTRIES:
*value = hw->num_qentries[SLI_QTYPE_RQ];
break;
case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
*value = hw->config.rq_default_buffer_size;
break;
case OCS_HW_AUTO_XFER_RDY_CAPABLE:
*value = sli_get_auto_xfer_rdy_capable(&hw->sli);
break;
case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
*value = hw->config.auto_xfer_rdy_xri_cnt;
break;
case OCS_HW_AUTO_XFER_RDY_SIZE:
*value = hw->config.auto_xfer_rdy_size;
break;
case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
switch (hw->config.auto_xfer_rdy_blk_size_chip) {
case 0:
*value = 512;
break;
case 1:
*value = 1024;
break;
case 2:
*value = 2048;
break;
case 3:
*value = 4096;
break;
case 4:
*value = 520;
break;
default:
*value = 0;
rc = OCS_HW_RTN_ERROR;
break;
}
break;
case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
*value = hw->config.auto_xfer_rdy_t10_enable;
break;
case OCS_HW_AUTO_XFER_RDY_P_TYPE:
*value = hw->config.auto_xfer_rdy_p_type;
break;
case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
*value = hw->config.auto_xfer_rdy_ref_tag_is_lba;
break;
case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
*value = hw->config.auto_xfer_rdy_app_tag_valid;
break;
case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
*value = hw->config.auto_xfer_rdy_app_tag_value;
break;
case OCS_HW_MAX_SGE:
*value = sli_get_max_sge(&hw->sli);
break;
case OCS_HW_MAX_SGL:
*value = sli_get_max_sgl(&hw->sli);
break;
case OCS_HW_TOPOLOGY:
if (hw->link.speed == 0) {
*value = OCS_HW_TOPOLOGY_NONE;
break;
}
switch (hw->link.topology) {
case SLI_LINK_TOPO_NPORT:
*value = OCS_HW_TOPOLOGY_NPORT;
break;
case SLI_LINK_TOPO_LOOP:
*value = OCS_HW_TOPOLOGY_LOOP;
break;
case SLI_LINK_TOPO_NONE:
*value = OCS_HW_TOPOLOGY_NONE;
break;
default:
ocs_log_test(hw->os, "unsupported topology %#x\n", hw->link.topology);
rc = OCS_HW_RTN_ERROR;
break;
}
break;
case OCS_HW_CONFIG_TOPOLOGY:
*value = hw->config.topology;
break;
case OCS_HW_LINK_SPEED:
*value = hw->link.speed;
break;
case OCS_HW_LINK_CONFIG_SPEED:
switch (hw->config.speed) {
case FC_LINK_SPEED_10G:
*value = 10000;
break;
case FC_LINK_SPEED_AUTO_16_8_4:
*value = 0;
break;
case FC_LINK_SPEED_2G:
*value = 2000;
break;
case FC_LINK_SPEED_4G:
*value = 4000;
break;
case FC_LINK_SPEED_8G:
*value = 8000;
break;
case FC_LINK_SPEED_16G:
*value = 16000;
break;
case FC_LINK_SPEED_32G:
*value = 32000;
break;
default:
ocs_log_test(hw->os, "unsupported speed %#x\n", hw->config.speed);
rc = OCS_HW_RTN_ERROR;
break;
}
break;
case OCS_HW_IF_TYPE:
*value = sli_get_if_type(&hw->sli);
break;
case OCS_HW_SLI_REV:
*value = sli_get_sli_rev(&hw->sli);
break;
case OCS_HW_SLI_FAMILY:
*value = sli_get_sli_family(&hw->sli);
break;
case OCS_HW_DIF_CAPABLE:
*value = sli_get_dif_capable(&hw->sli);
break;
case OCS_HW_DIF_SEED:
*value = hw->config.dif_seed;
break;
case OCS_HW_DIF_MODE:
*value = hw->config.dif_mode;
break;
case OCS_HW_DIF_MULTI_SEPARATE:
if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
*value = TRUE;
} else {
*value = FALSE;
}
break;
case OCS_HW_DUMP_MAX_SIZE:
*value = hw->dump_size;
break;
case OCS_HW_DUMP_READY:
*value = sli_dump_is_ready(&hw->sli);
break;
case OCS_HW_DUMP_PRESENT:
*value = sli_dump_is_present(&hw->sli);
break;
case OCS_HW_RESET_REQUIRED:
tmp = sli_reset_required(&hw->sli);
if(tmp < 0) {
rc = OCS_HW_RTN_ERROR;
} else {
*value = tmp;
}
break;
case OCS_HW_FW_ERROR:
*value = sli_fw_error_status(&hw->sli);
break;
case OCS_HW_FW_READY:
*value = sli_fw_ready(&hw->sli);
break;
case OCS_HW_FW_TIMED_OUT:
*value = ocs_hw_get_fw_timed_out(hw);
break;
case OCS_HW_HIGH_LOGIN_MODE:
*value = sli_get_hlm_capable(&hw->sli);
break;
case OCS_HW_PREREGISTER_SGL:
*value = sli_get_sgl_preregister_required(&hw->sli);
break;
case OCS_HW_HW_REV1:
*value = sli_get_hw_revision(&hw->sli, 0);
break;
case OCS_HW_HW_REV2:
*value = sli_get_hw_revision(&hw->sli, 1);
break;
case OCS_HW_HW_REV3:
*value = sli_get_hw_revision(&hw->sli, 2);
break;
case OCS_HW_LINKCFG:
*value = hw->linkcfg;
break;
case OCS_HW_ETH_LICENSE:
*value = hw->eth_license;
break;
case OCS_HW_LINK_MODULE_TYPE:
*value = sli_get_link_module_type(&hw->sli);
break;
case OCS_HW_NUM_CHUTES:
*value = ocs_hw_get_num_chutes(hw);
break;
case OCS_HW_DISABLE_AR_TGT_DIF:
*value = hw->workaround.disable_ar_tgt_dif;
break;
case OCS_HW_EMULATE_I_ONLY_AAB:
*value = hw->config.i_only_aab;
break;
case OCS_HW_EMULATE_WQE_TIMEOUT:
*value = hw->config.emulate_wqe_timeout;
break;
case OCS_HW_VPD_LEN:
*value = sli_get_vpd_len(&hw->sli);
break;
case OCS_HW_SGL_CHAINING_CAPABLE:
*value = sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported;
break;
case OCS_HW_SGL_CHAINING_ALLOWED:
*value = FALSE;
if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
!sli_get_sgl_preregister(&hw->sli) &&
SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
*value = TRUE;
}
if ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
sli_get_sgl_preregister(&hw->sli) &&
((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
*value = TRUE;
}
break;
case OCS_HW_SGL_CHAINING_HOST_ALLOCATED:
*value = ((sli_get_is_sgl_chaining_capable(&hw->sli) || hw->workaround.sglc_misreported) &&
(SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)));
break;
case OCS_HW_SEND_FRAME_CAPABLE:
if (hw->workaround.ignore_send_frame) {
*value = 0;
} else {
*value = sli_get_if_type(&hw->sli) == SLI4_IF_TYPE_LANCER_FC_ETH;
}
break;
case OCS_HW_RQ_SELECTION_POLICY:
*value = hw->config.rq_selection_policy;
break;
case OCS_HW_RR_QUANTA:
*value = hw->config.rr_quanta;
break;
case OCS_HW_MAX_VPORTS:
*value = sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_VPI);
break;
default:
ocs_log_test(hw->os, "unsupported property %#x\n", prop);
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
void *
ocs_hw_get_ptr(ocs_hw_t *hw, ocs_hw_property_e prop)
{
void *rc = NULL;
switch (prop) {
case OCS_HW_WWN_NODE:
rc = sli_get_wwn_node(&hw->sli);
break;
case OCS_HW_WWN_PORT:
rc = sli_get_wwn_port(&hw->sli);
break;
case OCS_HW_VPD:
if (sli_get_vpd_len(&hw->sli)) {
rc = sli_get_vpd(&hw->sli);
}
break;
case OCS_HW_FW_REV:
rc = sli_get_fw_name(&hw->sli, 0);
break;
case OCS_HW_FW_REV2:
rc = sli_get_fw_name(&hw->sli, 1);
break;
case OCS_HW_IPL:
rc = sli_get_ipl_name(&hw->sli);
break;
case OCS_HW_PORTNUM:
rc = sli_get_portnum(&hw->sli);
break;
case OCS_HW_BIOS_VERSION_STRING:
rc = sli_get_bios_version_string(&hw->sli);
break;
default:
ocs_log_test(hw->os, "unsupported property %#x\n", prop);
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_set(ocs_hw_t *hw, ocs_hw_property_e prop, uint32_t value)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
switch (prop) {
case OCS_HW_N_IO:
if (value > sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI) ||
value == 0) {
ocs_log_test(hw->os, "IO value out of range %d vs %d\n",
value, sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_XRI));
rc = OCS_HW_RTN_ERROR;
} else {
hw->config.n_io = value;
}
break;
case OCS_HW_N_SGL:
value += SLI4_SGE_MAX_RESERVED;
if (value > sli_get_max_sgl(&hw->sli)) {
ocs_log_test(hw->os, "SGL value out of range %d vs %d\n",
value, sli_get_max_sgl(&hw->sli));
rc = OCS_HW_RTN_ERROR;
} else {
hw->config.n_sgl = value;
}
break;
case OCS_HW_TOPOLOGY:
if ((sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) &&
(value != OCS_HW_TOPOLOGY_AUTO)) {
ocs_log_test(hw->os, "unsupported topology=%#x medium=%#x\n",
value, sli_get_medium(&hw->sli));
rc = OCS_HW_RTN_ERROR;
break;
}
switch (value) {
case OCS_HW_TOPOLOGY_AUTO:
if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC);
} else {
sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FCOE);
}
break;
case OCS_HW_TOPOLOGY_NPORT:
sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_DA);
break;
case OCS_HW_TOPOLOGY_LOOP:
sli_set_topology(&hw->sli, SLI4_READ_CFG_TOPO_FC_AL);
break;
default:
ocs_log_test(hw->os, "unsupported topology %#x\n", value);
rc = OCS_HW_RTN_ERROR;
}
hw->config.topology = value;
break;
case OCS_HW_LINK_SPEED:
if (sli_get_medium(&hw->sli) != SLI_LINK_MEDIUM_FC) {
switch (value) {
case 0:
case 10000:
hw->config.speed = FC_LINK_SPEED_10G;
break;
default:
ocs_log_test(hw->os, "unsupported speed=%#x medium=%#x\n",
value, sli_get_medium(&hw->sli));
rc = OCS_HW_RTN_ERROR;
}
break;
}
switch (value) {
case 0:
hw->config.speed = FC_LINK_SPEED_AUTO_16_8_4;
break;
case 2000:
hw->config.speed = FC_LINK_SPEED_2G;
break;
case 4000:
hw->config.speed = FC_LINK_SPEED_4G;
break;
case 8000:
hw->config.speed = FC_LINK_SPEED_8G;
break;
case 16000:
hw->config.speed = FC_LINK_SPEED_16G;
break;
case 32000:
hw->config.speed = FC_LINK_SPEED_32G;
break;
default:
ocs_log_test(hw->os, "unsupported speed %d\n", value);
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_DIF_SEED:
if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
ocs_log_test(hw->os, "DIF seed not supported for this device\n");
rc = OCS_HW_RTN_ERROR;
} else {
hw->config.dif_seed = value;
}
break;
case OCS_HW_DIF_MODE:
switch (value) {
case OCS_HW_DIF_MODE_INLINE:
if (sli_is_dif_inline_capable(&hw->sli)) {
hw->config.dif_mode = value;
} else {
ocs_log_test(hw->os, "chip does not support DIF inline\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_DIF_MODE_SEPARATE:
if (sli_is_dif_separate_capable(&hw->sli)) {
hw->config.dif_mode = value;
} else {
ocs_log_test(hw->os, "chip does not support DIF separate\n");
rc = OCS_HW_RTN_ERROR;
}
}
break;
case OCS_HW_RQ_PROCESS_LIMIT: {
hw_rq_t *rq;
uint32_t i;
for (i = 0; i < hw->hw_rq_count; i++) {
rq = hw->hw_rq[i];
hw->cq[rq->cq->instance].proc_limit = value;
}
break;
}
case OCS_HW_RQ_DEFAULT_BUFFER_SIZE:
hw->config.rq_default_buffer_size = value;
break;
case OCS_HW_AUTO_XFER_RDY_XRI_CNT:
hw->config.auto_xfer_rdy_xri_cnt = value;
break;
case OCS_HW_AUTO_XFER_RDY_SIZE:
hw->config.auto_xfer_rdy_size = value;
break;
case OCS_HW_AUTO_XFER_RDY_BLK_SIZE:
switch (value) {
case 512:
hw->config.auto_xfer_rdy_blk_size_chip = 0;
break;
case 1024:
hw->config.auto_xfer_rdy_blk_size_chip = 1;
break;
case 2048:
hw->config.auto_xfer_rdy_blk_size_chip = 2;
break;
case 4096:
hw->config.auto_xfer_rdy_blk_size_chip = 3;
break;
case 520:
hw->config.auto_xfer_rdy_blk_size_chip = 4;
break;
default:
ocs_log_err(hw->os, "Invalid block size %d\n",
value);
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_AUTO_XFER_RDY_T10_ENABLE:
hw->config.auto_xfer_rdy_t10_enable = value;
break;
case OCS_HW_AUTO_XFER_RDY_P_TYPE:
hw->config.auto_xfer_rdy_p_type = value;
break;
case OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA:
hw->config.auto_xfer_rdy_ref_tag_is_lba = value;
break;
case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID:
hw->config.auto_xfer_rdy_app_tag_valid = value;
break;
case OCS_HW_AUTO_XFER_RDY_APP_TAG_VALUE:
hw->config.auto_xfer_rdy_app_tag_value = value;
break;
case OCS_ESOC:
hw->config.esoc = value;
break;
case OCS_HW_HIGH_LOGIN_MODE:
rc = sli_set_hlm(&hw->sli, value);
break;
case OCS_HW_PREREGISTER_SGL:
rc = sli_set_sgl_preregister(&hw->sli, value);
break;
case OCS_HW_ETH_LICENSE:
hw->eth_license = value;
break;
case OCS_HW_EMULATE_I_ONLY_AAB:
hw->config.i_only_aab = value;
break;
case OCS_HW_EMULATE_WQE_TIMEOUT:
hw->config.emulate_wqe_timeout = value;
break;
case OCS_HW_BOUNCE:
hw->config.bounce = value;
break;
case OCS_HW_RQ_SELECTION_POLICY:
hw->config.rq_selection_policy = value;
break;
case OCS_HW_RR_QUANTA:
hw->config.rr_quanta = value;
break;
default:
ocs_log_test(hw->os, "unsupported property %#x\n", prop);
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_set_ptr(ocs_hw_t *hw, ocs_hw_property_e prop, void *value)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
switch (prop) {
case OCS_HW_WAR_VERSION:
hw->hw_war_version = value;
break;
case OCS_HW_FILTER_DEF: {
char *p = value;
uint32_t idx = 0;
for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) {
hw->config.filter_def[idx] = 0;
}
for (idx = 0; (idx < ARRAY_SIZE(hw->config.filter_def)) && (p != NULL) && *p; ) {
hw->config.filter_def[idx++] = ocs_strtoul(p, 0, 0);
p = ocs_strchr(p, ',');
if (p != NULL) {
p++;
}
}
break;
}
default:
ocs_log_test(hw->os, "unsupported property %#x\n", prop);
rc = OCS_HW_RTN_ERROR;
break;
}
return rc;
}
int32_t
ocs_hw_event_check(ocs_hw_t *hw, uint32_t vector)
{
int32_t rc = 0;
if (!hw) {
ocs_log_err(NULL, "HW context NULL?!?\n");
return -1;
}
if (vector > hw->eq_count) {
ocs_log_err(hw->os, "vector %d. max %d\n",
vector, hw->eq_count);
return -1;
}
if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
rc = sli_queue_is_empty(&hw->sli, &hw->eq[vector]);
if (rc != 0) {
sli_queue_arm(&hw->sli, &hw->eq[vector], TRUE);
}
}
return rc;
}
void
ocs_hw_unsol_process_bounce(void *arg)
{
ocs_hw_sequence_t *seq = arg;
ocs_hw_t *hw = seq->hw;
ocs_hw_assert(hw != NULL);
ocs_hw_assert(hw->callback.unsolicited != NULL);
hw->callback.unsolicited(hw->args.unsolicited, seq);
}
int32_t
ocs_hw_process(ocs_hw_t *hw, uint32_t vector, uint32_t max_isr_time_msec)
{
hw_eq_t *eq;
int32_t rc = 0;
CPUTRACE("");
if (hw->state == OCS_HW_STATE_UNINITIALIZED) {
return 0;
}
eq = hw->hw_eq[vector];
OCS_STAT(eq->use_count++);
rc = ocs_hw_eq_process(hw, eq, max_isr_time_msec);
return rc;
}
int32_t
ocs_hw_eq_process(ocs_hw_t *hw, hw_eq_t *eq, uint32_t max_isr_time_msec)
{
uint8_t eqe[sizeof(sli4_eqe_t)] = { 0 };
uint32_t done = FALSE;
uint32_t tcheck_count;
time_t tstart;
time_t telapsed;
tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
tstart = ocs_msectime();
CPUTRACE("");
while (!done && !sli_queue_read(&hw->sli, eq->queue, eqe)) {
uint16_t cq_id = 0;
int32_t rc;
rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
if (unlikely(rc)) {
if (rc > 0) {
uint32_t i;
for (i = 0; i < hw->cq_count; i++) {
ocs_hw_cq_process(hw, hw->hw_cq[i]);
}
continue;
} else {
return rc;
}
} else {
int32_t index = ocs_hw_queue_hash_find(hw->cq_hash, cq_id);
if (likely(index >= 0)) {
ocs_hw_cq_process(hw, hw->hw_cq[index]);
} else {
ocs_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
}
}
if (eq->queue->n_posted > (eq->queue->posted_limit)) {
sli_queue_arm(&hw->sli, eq->queue, FALSE);
}
if (tcheck_count && (--tcheck_count == 0)) {
tcheck_count = OCS_HW_TIMECHECK_ITERATIONS;
telapsed = ocs_msectime() - tstart;
if (telapsed >= max_isr_time_msec) {
done = TRUE;
}
}
}
sli_queue_eq_arm(&hw->sli, eq->queue, TRUE);
return 0;
}
static int32_t
ocs_hw_cmd_submit_pending(ocs_hw_t *hw)
{
ocs_command_ctx_t *ctx;
int32_t rc = 0;
while (hw->cmd_head_count < (OCS_HW_MQ_DEPTH - 1)) {
ctx = ocs_list_remove_head(&hw->cmd_pending);
if (ctx == NULL) {
break;
}
ocs_list_add_tail(&hw->cmd_head, ctx);
hw->cmd_head_count++;
if (sli_queue_write(&hw->sli, hw->mq, ctx->buf) < 0) {
ocs_log_test(hw->os, "sli_queue_write failed: %d\n", rc);
rc = -1;
break;
}
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_command(ocs_hw_t *hw, uint8_t *cmd, uint32_t opts, void *cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
if (sli_fw_error_status(&hw->sli) > 0) {
uint32_t err1 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR1);
uint32_t err2 = sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_ERROR2);
if (hw->expiration_logged == 0 && err1 == 0x2 && err2 == 0x10) {
hw->expiration_logged = 1;
ocs_log_crit(hw->os,"Emulex: Heartbeat expired after %d seconds\n",
hw->watchdog_timeout);
}
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
ocs_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
sli_reg_read(&hw->sli, SLI4_REG_SLIPORT_STATUS),
err1, err2);
return OCS_HW_RTN_ERROR;
}
if (OCS_CMD_POLL == opts) {
ocs_lock(&hw->cmd_lock);
if (hw->mq->length && !sli_queue_is_empty(&hw->sli, hw->mq)) {
rc = OCS_HW_RTN_ERROR;
} else {
void *bmbx = hw->sli.bmbx.virt;
ocs_memset(bmbx, 0, SLI4_BMBX_SIZE);
ocs_memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
if (sli_bmbx_command(&hw->sli) == 0) {
rc = OCS_HW_RTN_SUCCESS;
ocs_memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
}
}
ocs_unlock(&hw->cmd_lock);
} else if (OCS_CMD_NOWAIT == opts) {
ocs_command_ctx_t *ctx = NULL;
ctx = ocs_malloc(hw->os, sizeof(ocs_command_ctx_t), OCS_M_ZERO | OCS_M_NOWAIT);
if (!ctx) {
ocs_log_err(hw->os, "can't allocate command context\n");
return OCS_HW_RTN_NO_RESOURCES;
}
if (hw->state != OCS_HW_STATE_ACTIVE) {
ocs_log_err(hw->os, "Can't send command, HW state=%d\n", hw->state);
ocs_free(hw->os, ctx, sizeof(*ctx));
return OCS_HW_RTN_ERROR;
}
if (cb) {
ctx->cb = cb;
ctx->arg = arg;
}
ctx->buf = cmd;
ctx->ctx = hw;
ocs_lock(&hw->cmd_lock);
ocs_list_add_tail(&hw->cmd_pending, ctx);
if (ocs_hw_cmd_submit_pending(hw) == 0) {
rc = OCS_HW_RTN_SUCCESS;
}
ocs_unlock(&hw->cmd_lock);
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_callback(ocs_hw_t *hw, ocs_hw_callback_e which, void *func, void *arg)
{
if (!hw || !func || (which >= OCS_HW_CB_MAX)) {
ocs_log_err(NULL, "bad parameter hw=%p which=%#x func=%p\n",
hw, which, func);
return OCS_HW_RTN_ERROR;
}
switch (which) {
case OCS_HW_CB_DOMAIN:
hw->callback.domain = func;
hw->args.domain = arg;
break;
case OCS_HW_CB_PORT:
hw->callback.port = func;
hw->args.port = arg;
break;
case OCS_HW_CB_UNSOLICITED:
hw->callback.unsolicited = func;
hw->args.unsolicited = arg;
break;
case OCS_HW_CB_REMOTE_NODE:
hw->callback.rnode = func;
hw->args.rnode = arg;
break;
case OCS_HW_CB_BOUNCE:
hw->callback.bounce = func;
hw->args.bounce = arg;
break;
default:
ocs_log_test(hw->os, "unknown callback %#x\n", which);
return OCS_HW_RTN_ERROR;
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_port_alloc(ocs_hw_t *hw, ocs_sli_port_t *sport, ocs_domain_t *domain,
uint8_t *wwpn)
{
uint8_t *cmd = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint32_t index;
sport->indicator = UINT32_MAX;
sport->hw = hw;
sport->ctx.app = sport;
sport->sm_free_req_pending = 0;
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
if (wwpn) {
ocs_memcpy(&sport->sli_wwpn, wwpn, sizeof(sport->sli_wwpn));
}
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VPI, &sport->indicator, &index)) {
ocs_log_err(hw->os, "FCOE_VPI allocation failure\n");
return OCS_HW_RTN_ERROR;
}
if (domain != NULL) {
ocs_sm_function_t next = NULL;
cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (!cmd) {
ocs_log_err(hw->os, "command memory allocation failed\n");
rc = OCS_HW_RTN_NO_MEMORY;
goto ocs_hw_port_alloc_out;
}
if (!wwpn) {
next = __ocs_hw_port_alloc_read_sparm64;
} else {
next = __ocs_hw_port_alloc_init_vpi;
}
ocs_sm_transition(&sport->ctx, next, cmd);
} else if (!wwpn) {
ocs_log_test(hw->os, "need WWN for physical port\n");
rc = OCS_HW_RTN_ERROR;
} else {
ocs_sm_transition(&sport->ctx, __ocs_hw_port_alloc_init, NULL);
}
ocs_hw_port_alloc_out:
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_port_attach(ocs_hw_t *hw, ocs_sli_port_t *sport, uint32_t fc_id)
{
uint8_t *buf = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (!hw || !sport) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter(s) hw=%p sport=%p\n", hw,
sport);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
sport->fc_id = fc_id;
ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_ATTACH, buf);
return rc;
}
static int32_t
ocs_hw_cb_port_control(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_port_control(ocs_hw_t *hw, ocs_hw_port_e ctrl, uintptr_t value, ocs_hw_port_control_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
switch (ctrl) {
case OCS_HW_PORT_INIT:
{
uint8_t *init_link;
uint32_t speed = 0;
uint8_t reset_alpa = 0;
if (SLI_LINK_MEDIUM_FC == sli_get_medium(&hw->sli)) {
uint8_t *cfg_link;
cfg_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (cfg_link == NULL) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_config_link(&hw->sli, cfg_link, SLI4_BMBX_SIZE)) {
rc = ocs_hw_command(hw, cfg_link, OCS_CMD_NOWAIT,
ocs_hw_cb_port_control, NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, cfg_link, SLI4_BMBX_SIZE);
ocs_log_err(hw->os, "CONFIG_LINK failed\n");
break;
}
speed = hw->config.speed;
reset_alpa = (uint8_t)(value & 0xff);
} else {
speed = FC_LINK_SPEED_10G;
}
if (hw->workaround.fw_version_too_low) {
if (SLI4_IF_TYPE_LANCER_FC_ETH == hw->sli.if_type) {
ocs_log_err(hw->os, "Cannot bring up link. Please update firmware to %s or later (current version is %s)\n",
OCS_FW_VER_STR(OCS_MIN_FW_VER_LANCER), (char *) sli_get_fw_name(&hw->sli,0));
} else {
ocs_log_err(hw->os, "Cannot bring up link. Please update firmware to %s or later (current version is %s)\n",
OCS_FW_VER_STR(OCS_MIN_FW_VER_SKYHAWK), (char *) sli_get_fw_name(&hw->sli, 0));
}
return OCS_HW_RTN_ERROR;
}
rc = OCS_HW_RTN_ERROR;
init_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (init_link == NULL) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE, speed, reset_alpa)) {
rc = ocs_hw_command(hw, init_link, OCS_CMD_NOWAIT,
ocs_hw_cb_port_control, NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, init_link, SLI4_BMBX_SIZE);
ocs_log_err(hw->os, "INIT_LINK failed\n");
}
break;
}
case OCS_HW_PORT_SHUTDOWN:
{
uint8_t *down_link;
down_link = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (down_link == NULL) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE)) {
rc = ocs_hw_command(hw, down_link, OCS_CMD_NOWAIT,
ocs_hw_cb_port_control, NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, down_link, SLI4_BMBX_SIZE);
ocs_log_err(hw->os, "DOWN_LINK failed\n");
}
break;
}
case OCS_HW_PORT_SET_LINK_CONFIG:
rc = ocs_hw_set_linkcfg(hw, (ocs_hw_linkcfg_e)value, OCS_CMD_NOWAIT, cb, arg);
break;
default:
ocs_log_test(hw->os, "unhandled control %#x\n", ctrl);
break;
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_port_free(ocs_hw_t *hw, ocs_sli_port_t *sport)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (!hw || !sport) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter(s) hw=%p sport=%p\n", hw,
sport);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
ocs_sm_post_event(&sport->ctx, OCS_EVT_HW_PORT_REQ_FREE, NULL);
return rc;
}
ocs_hw_rtn_e
ocs_hw_domain_alloc(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fcf, uint32_t vlan)
{
uint8_t *cmd = NULL;
uint32_t index;
if (!hw || !domain || !domain->sport) {
ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p sport=%p\n",
hw, domain, domain ? domain->sport : NULL);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (!cmd) {
ocs_log_err(hw->os, "command memory allocation failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
domain->dma = hw->domain_dmem;
domain->hw = hw;
domain->sm.app = domain;
domain->fcf = fcf;
domain->fcf_indicator = UINT32_MAX;
domain->vlan_id = vlan;
domain->indicator = UINT32_MAX;
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_VFI, &domain->indicator, &index)) {
ocs_log_err(hw->os, "FCOE_VFI allocation failure\n");
ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
return OCS_HW_RTN_ERROR;
}
ocs_sm_transition(&domain->sm, __ocs_hw_domain_init, cmd);
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_domain_attach(ocs_hw_t *hw, ocs_domain_t *domain, uint32_t fc_id)
{
uint8_t *buf = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (!hw || !domain) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter(s) hw=%p domain=%p\n",
hw, domain);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
domain->sport->fc_id = fc_id;
ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_ATTACH, buf);
return rc;
}
ocs_hw_rtn_e
ocs_hw_domain_free(ocs_hw_t *hw, ocs_domain_t *domain)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (!hw || !domain) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter(s) hw=%p domain=%p\n",
hw, domain);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
ocs_sm_post_event(&domain->sm, OCS_EVT_HW_DOMAIN_REQ_FREE, NULL);
return rc;
}
ocs_hw_rtn_e
ocs_hw_domain_force_free(ocs_hw_t *hw, ocs_domain_t *domain)
{
if (!hw || !domain) {
ocs_log_err(NULL, "bad parameter(s) hw=%p domain=%p\n", hw, domain);
return OCS_HW_RTN_ERROR;
}
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_node_alloc(ocs_hw_t *hw, ocs_remote_node_t *rnode, uint32_t fc_addr,
ocs_sli_port_t *sport)
{
if (UINT32_MAX != rnode->indicator) {
ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x rpi=%#x\n",
fc_addr, rnode->indicator);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
rnode->sport = NULL;
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &rnode->indicator, &rnode->index)) {
ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
fc_addr);
return OCS_HW_RTN_ERROR;
}
rnode->fc_id = fc_addr;
rnode->sport = sport;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_node_attach(ocs_hw_t *hw, ocs_remote_node_t *rnode, ocs_dma_t *sparms)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
uint8_t *buf = NULL;
uint32_t count = 0;
if (!hw || !rnode || !sparms) {
ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p sparms=%p\n",
hw, rnode, sparms);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (rnode->index == UINT32_MAX) {
ocs_log_err(NULL, "bad parameter rnode->index invalid\n");
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
return OCS_HW_RTN_ERROR;
}
count = ocs_atomic_add_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
if (count) {
if (sli_get_hlm(&hw->sli) == FALSE) {
ocs_log_test(hw->os, "attach to already attached node HLM=%d count=%d\n",
sli_get_hlm(&hw->sli), count);
rc = OCS_HW_RTN_SUCCESS;
} else {
rnode->node_group = TRUE;
rnode->attached = ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_attached);
rc = rnode->attached ? OCS_HW_RTN_SUCCESS_SYNC : OCS_HW_RTN_SUCCESS;
}
} else {
rnode->node_group = FALSE;
ocs_display_sparams("", "reg rpi", 0, NULL, sparms->virt);
if (sli_cmd_reg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->fc_id,
rnode->indicator, rnode->sport->indicator,
sparms, 0, (hw->auto_xfer_rdy_enabled && hw->config.auto_xfer_rdy_t10_enable))) {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT,
ocs_hw_cb_node_attach, rnode);
}
}
if (count || rc) {
if (rc < OCS_HW_RTN_SUCCESS) {
ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
ocs_log_err(hw->os, "%s error\n", count ? "HLM" : "REG_RPI");
}
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_node_free_resources(ocs_hw_t *hw, ocs_remote_node_t *rnode)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (!hw || !rnode) {
ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
hw, rnode);
return OCS_HW_RTN_ERROR;
}
if (rnode->sport) {
if (!rnode->attached) {
if (rnode->indicator != UINT32_MAX) {
if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
ocs_log_err(hw->os, "FCOE_RPI free failure RPI %d addr=%#x\n",
rnode->indicator, rnode->fc_id);
rc = OCS_HW_RTN_ERROR;
} else {
rnode->node_group = FALSE;
rnode->indicator = UINT32_MAX;
rnode->index = UINT32_MAX;
rnode->free_group = FALSE;
}
}
} else {
ocs_log_err(hw->os, "Error: rnode is still attached\n");
rc = OCS_HW_RTN_ERROR;
}
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_node_detach(ocs_hw_t *hw, ocs_remote_node_t *rnode)
{
uint8_t *buf = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS_SYNC;
uint32_t index = UINT32_MAX;
if (!hw || !rnode) {
ocs_log_err(NULL, "bad parameter(s) hw=%p rnode=%p\n",
hw, rnode);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
index = rnode->index;
if (rnode->sport) {
uint32_t count = 0;
uint32_t fc_id;
if (!rnode->attached) {
return OCS_HW_RTN_SUCCESS_SYNC;
}
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
count = ocs_atomic_sub_return(&hw->rpi_ref[index].rpi_count, 1);
if (count <= 1) {
fc_id = UINT32_MAX;
rnode->node_group = FALSE;
rnode->free_group = TRUE;
} else {
if (sli_get_hlm(&hw->sli) == FALSE) {
ocs_log_test(hw->os, "Invalid count with HLM disabled, count=%d\n",
count);
}
fc_id = rnode->fc_id & 0x00ffffff;
}
rc = OCS_HW_RTN_ERROR;
if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, rnode->indicator,
SLI_RSRC_FCOE_RPI, fc_id)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free, rnode);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "UNREG_RPI failed\n");
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
rc = OCS_HW_RTN_ERROR;
}
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_node_free_all(ocs_hw_t *hw)
{
uint8_t *buf = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
if (!hw) {
ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
return OCS_HW_RTN_ERROR;
}
if (sli_fw_error_status(&hw->sli) > 0) {
ocs_log_crit(hw->os, "Chip is in an error state - reset needed\n");
return OCS_HW_RTN_ERROR;
}
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_unreg_rpi(&hw->sli, buf, SLI4_BMBX_SIZE, 0xffff,
SLI_RSRC_FCOE_FCFI, UINT32_MAX)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_node_free_all,
NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "UNREG_RPI failed\n");
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_node_group_alloc(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
{
if (!hw || !ngroup) {
ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
hw, ngroup);
return OCS_HW_RTN_ERROR;
}
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &ngroup->indicator,
&ngroup->index)) {
ocs_log_err(hw->os, "FCOE_RPI allocation failure addr=%#x\n",
ngroup->indicator);
return OCS_HW_RTN_ERROR;
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_node_group_attach(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup, ocs_remote_node_t *rnode)
{
if (!hw || !ngroup || !rnode) {
ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p rnode=%p\n",
hw, ngroup, rnode);
return OCS_HW_RTN_ERROR;
}
if (rnode->attached) {
ocs_log_err(hw->os, "node already attached RPI=%#x addr=%#x\n",
rnode->indicator, rnode->fc_id);
return OCS_HW_RTN_ERROR;
}
if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, rnode->indicator)) {
ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
rnode->indicator);
return OCS_HW_RTN_ERROR;
}
rnode->indicator = ngroup->indicator;
rnode->index = ngroup->index;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_node_group_free(ocs_hw_t *hw, ocs_remote_node_group_t *ngroup)
{
int ref;
if (!hw || !ngroup) {
ocs_log_err(NULL, "bad parameter hw=%p ngroup=%p\n",
hw, ngroup);
return OCS_HW_RTN_ERROR;
}
ref = ocs_atomic_read(&hw->rpi_ref[ngroup->index].rpi_count);
if (ref) {
ocs_log_debug(hw->os, "node group reference=%d (RPI=%#x)\n",
ref, ngroup->indicator);
if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_RPI, ngroup->indicator)) {
ocs_log_err(hw->os, "FCOE_RPI free failure RPI=%#x\n",
ngroup->indicator);
return OCS_HW_RTN_ERROR;
}
ocs_atomic_set(&hw->rpi_ref[ngroup->index].rpi_count, 0);
}
ngroup->indicator = UINT32_MAX;
ngroup->index = UINT32_MAX;
return OCS_HW_RTN_SUCCESS;
}
static inline void
ocs_hw_init_free_io(ocs_hw_io_t *io)
{
io->done = NULL;
io->abort_done = NULL;
io->status_saved = 0;
io->abort_in_progress = FALSE;
io->port_owned_abort_count = 0;
io->rnode = NULL;
io->type = 0xFFFF;
io->wq = NULL;
io->ul_io = NULL;
io->wqe_timeout = 0;
}
static inline ocs_hw_io_t *
_ocs_hw_io_alloc(ocs_hw_t *hw)
{
ocs_hw_io_t *io = NULL;
if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
ocs_list_add_tail(&hw->io_inuse, io);
io->state = OCS_HW_IO_STATE_INUSE;
io->quarantine = FALSE;
io->quarantine_first_phase = TRUE;
io->abort_reqtag = UINT32_MAX;
ocs_ref_init(&io->ref, ocs_hw_io_free_internal, io);
} else {
ocs_atomic_add_return(&hw->io_alloc_failed_count, 1);
}
return io;
}
ocs_hw_io_t *
ocs_hw_io_alloc(ocs_hw_t *hw)
{
ocs_hw_io_t *io = NULL;
ocs_lock(&hw->io_lock);
io = _ocs_hw_io_alloc(hw);
ocs_unlock(&hw->io_lock);
return io;
}
ocs_hw_io_t *
ocs_hw_io_activate_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (ocs_ref_read_count(&io->ref) > 0) {
ocs_log_err(hw->os, "Bad parameter: refcount > 0\n");
return NULL;
}
if (io->wq != NULL) {
ocs_log_err(hw->os, "XRI %x already in use\n", io->indicator);
return NULL;
}
ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
io->xbusy = TRUE;
return io;
}
static void
ocs_hw_io_free_move_correct_list(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (io->xbusy) {
ocs_list_add_tail(&hw->io_wait_free, io);
io->state = OCS_HW_IO_STATE_WAIT_FREE;
} else {
ocs_list_add_tail(&hw->io_free, io);
io->state = OCS_HW_IO_STATE_FREE;
}
if (hw->workaround.use_dif_sec_xri) {
ocs_hw_check_sec_hio_list(hw);
}
}
static inline void
ocs_hw_io_free_common(ocs_hw_t *hw, ocs_hw_io_t *io)
{
ocs_hw_init_free_io(io);
ocs_hw_io_restore_sgl(hw, io);
}
static void
ocs_hw_io_free_port_owned(void *arg)
{
ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
ocs_hw_t *hw = io->hw;
if (io->auto_xfer_rdy_dnrx) {
ocs_lock(&hw->io_lock);
ocs_ref_init(&io->ref, ocs_hw_io_free_port_owned, io);
ocs_list_add_tail(&hw->io_port_dnrx, io);
ocs_unlock(&hw->io_lock);
}
ocs_hw_io_free_common(hw, io);
}
static void
ocs_hw_io_free_internal(void *arg)
{
ocs_hw_io_t *io = (ocs_hw_io_t *)arg;
ocs_hw_t *hw = io->hw;
ocs_hw_io_free_common(hw, io);
ocs_lock(&hw->io_lock);
ocs_list_remove(&hw->io_inuse, io);
ocs_hw_io_free_move_correct_list(hw, io);
ocs_unlock(&hw->io_lock);
}
int32_t
ocs_hw_io_free(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (ocs_ref_read_count(&io->ref) <= 0) {
ocs_log_err(hw->os, "Bad parameter: refcount <= 0 xri=%x tag=%x\n",
io->indicator, io->reqtag);
return -1;
}
return ocs_ref_put(&io->ref);
}
uint8_t
ocs_hw_io_inuse(ocs_hw_t *hw, ocs_hw_io_t *io)
{
return (ocs_ref_read_count(&io->ref) > 0);
}
static int32_t
_hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
{
int32_t rc;
int32_t queue_rc;
if (wq->wqec_count) {
wq->wqec_count--;
}
if (wq->wqec_count == 0) {
sli4_generic_wqe_t *genwqe = (void*)wqe->wqebuf;
genwqe->wqec = 1;
wq->wqec_count = wq->wqec_set_count;
}
wq->free_count--;
queue_rc = _sli_queue_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
if (queue_rc < 0) {
rc = -1;
} else {
rc = 0;
ocs_queue_history_wq(&wq->hw->q_hist, (void *) wqe->wqebuf, wq->queue->id, queue_rc);
}
return rc;
}
int32_t
hw_wq_write(hw_wq_t *wq, ocs_hw_wqe_t *wqe)
{
int32_t rc = 0;
sli_queue_lock(wq->queue);
if ( ! ocs_list_empty(&wq->pending_list)) {
ocs_list_add_tail(&wq->pending_list, wqe);
OCS_STAT(wq->wq_pending_count++;)
while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
rc = _hw_wq_write(wq, wqe);
if (rc < 0) {
break;
}
if (wqe->abort_wqe_submit_needed) {
wqe->abort_wqe_submit_needed = 0;
sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT );
ocs_list_add_tail(&wq->pending_list, wqe);
OCS_STAT(wq->wq_pending_count++;)
}
}
} else {
if (wq->free_count > 0) {
rc = _hw_wq_write(wq, wqe);
} else {
ocs_list_add_tail(&wq->pending_list, wqe);
OCS_STAT(wq->wq_pending_count++;)
}
}
sli_queue_unlock(wq->queue);
return rc;
}
static void
hw_wq_submit_pending(hw_wq_t *wq, uint32_t update_free_count)
{
ocs_hw_wqe_t *wqe;
sli_queue_lock(wq->queue);
wq->free_count += update_free_count;
while ((wq->free_count > 0) && ((wqe = ocs_list_remove_head(&wq->pending_list)) != NULL)) {
_hw_wq_write(wq, wqe);
if (wqe->abort_wqe_submit_needed) {
wqe->abort_wqe_submit_needed = 0;
sli_abort_wqe(&wq->hw->sli, wqe->wqebuf, wq->hw->sli.config.wqe_size, SLI_ABORT_XRI,
wqe->send_abts, wqe->id, 0, wqe->abort_reqtag, SLI4_CQ_DEFAULT);
ocs_list_add_tail(&wq->pending_list, wqe);
OCS_STAT(wq->wq_pending_count++;)
}
}
sli_queue_unlock(wq->queue);
}
static void
ocs_hw_check_sec_hio_list(ocs_hw_t *hw)
{
ocs_hw_io_t *io;
ocs_hw_io_t *sec_io;
int rc = 0;
while (!ocs_list_empty(&hw->sec_hio_wait_list)) {
uint16_t flags;
sec_io = _ocs_hw_io_alloc(hw);
if (sec_io == NULL) {
break;
}
io = ocs_list_remove_head(&hw->sec_hio_wait_list);
ocs_list_add_tail(&hw->io_inuse, io);
io->state = OCS_HW_IO_STATE_INUSE;
io->sec_hio = sec_io;
if (io->xbusy) {
sec_io->quarantine = TRUE;
}
flags = io->sec_iparam.fcp_tgt.flags;
if (io->xbusy) {
flags |= SLI4_IO_CONTINUATION;
} else {
flags &= ~SLI4_IO_CONTINUATION;
}
io->wqe_timeout = io->sec_iparam.fcp_tgt.timeout;
if (io->xbusy) {
if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
io->first_data_sge,
io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator, io->sec_hio->indicator,
io->reqtag, SLI4_CQ_DEFAULT,
io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
flags,
io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size, io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
ocs_log_test(hw->os, "TRECEIVE WQE error\n");
break;
}
} else {
if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
io->first_data_sge,
io->sec_iparam.fcp_tgt.offset, io->sec_len, io->indicator,
io->reqtag, SLI4_CQ_DEFAULT,
io->sec_iparam.fcp_tgt.ox_id, io->rnode->indicator, io->rnode,
flags,
io->sec_iparam.fcp_tgt.dif_oper, io->sec_iparam.fcp_tgt.blk_size,
io->sec_iparam.fcp_tgt.cs_ctl, io->sec_iparam.fcp_tgt.app_id)) {
ocs_log_test(hw->os, "TRECEIVE WQE error\n");
break;
}
}
if (io->wq == NULL) {
io->wq = ocs_hw_queue_next_wq(hw, io);
ocs_hw_assert(io->wq != NULL);
}
io->xbusy = TRUE;
ocs_hw_add_io_timed_wqe(hw, io);
rc = hw_wq_write(io->wq, &io->wqe);
if (rc >= 0) {
rc = 0;
} else {
ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
io->xbusy = FALSE;
ocs_hw_remove_io_timed_wqe(hw, io);
}
}
}
ocs_hw_rtn_e
ocs_hw_srrs_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
ocs_dma_t *send, uint32_t len, ocs_dma_t *receive,
ocs_remote_node_t *rnode, ocs_hw_io_param_t *iparam,
ocs_hw_srrs_cb_t cb, void *arg)
{
sli4_sge_t *sge = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint16_t local_flags = 0;
if (!hw || !io || !rnode || !iparam) {
ocs_log_err(NULL, "bad parm hw=%p io=%p send=%p receive=%p rnode=%p iparam=%p\n",
hw, io, send, receive, rnode, iparam);
return OCS_HW_RTN_ERROR;
}
if (hw->state != OCS_HW_STATE_ACTIVE) {
ocs_log_test(hw->os, "cannot send SRRS, HW state=%d\n", hw->state);
return OCS_HW_RTN_ERROR;
}
if (ocs_hw_is_xri_port_owned(hw, io->indicator)) {
local_flags |= SLI4_IO_CONTINUATION;
}
io->rnode = rnode;
io->type = type;
io->done = cb;
io->arg = arg;
sge = io->sgl->virt;
ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
if (send) {
sge[0].buffer_address_high = ocs_addr32_hi(send->phys);
sge[0].buffer_address_low = ocs_addr32_lo(send->phys);
sge[0].sge_type = SLI4_SGE_TYPE_DATA;
sge[0].buffer_length = len;
}
if ((OCS_HW_ELS_REQ == type) || (OCS_HW_FC_CT == type)) {
sge[1].buffer_address_high = ocs_addr32_hi(receive->phys);
sge[1].buffer_address_low = ocs_addr32_lo(receive->phys);
sge[1].sge_type = SLI4_SGE_TYPE_DATA;
sge[1].buffer_length = receive->size;
sge[1].last = TRUE;
} else {
sge[0].last = TRUE;
}
switch (type) {
case OCS_HW_ELS_REQ:
if ( (!send) || sli_els_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl,
*((uint8_t *)(send->virt)),
len, receive->size,
iparam->els.timeout, io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rnode)) {
ocs_log_err(hw->os, "REQ WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_ELS_RSP:
if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
iparam->els.ox_id,
rnode, local_flags, UINT32_MAX)) {
ocs_log_err(hw->os, "RSP WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_ELS_RSP_SID:
if ( (!send) || sli_xmit_els_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
iparam->els_sid.ox_id,
rnode, local_flags, iparam->els_sid.s_id)) {
ocs_log_err(hw->os, "RSP (SID) WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_FC_CT:
if ( (!send) || sli_gen_request64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
receive->size, iparam->fc_ct.timeout, io->indicator,
io->reqtag, SLI4_CQ_DEFAULT, rnode, iparam->fc_ct.r_ctl,
iparam->fc_ct.type, iparam->fc_ct.df_ctl)) {
ocs_log_err(hw->os, "GEN WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_FC_CT_RSP:
if ( (!send) || sli_xmit_sequence64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->sgl, len,
iparam->fc_ct_rsp.timeout, iparam->fc_ct_rsp.ox_id, io->indicator,
io->reqtag, rnode, iparam->fc_ct_rsp.r_ctl,
iparam->fc_ct_rsp.type, iparam->fc_ct_rsp.df_ctl)) {
ocs_log_err(hw->os, "XMIT SEQ WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_BLS_ACC:
case OCS_HW_BLS_RJT:
{
sli_bls_payload_t bls;
if (OCS_HW_BLS_ACC == type) {
bls.type = SLI_BLS_ACC;
ocs_memcpy(&bls.u.acc, iparam->bls.payload, sizeof(bls.u.acc));
} else {
bls.type = SLI_BLS_RJT;
ocs_memcpy(&bls.u.rjt, iparam->bls.payload, sizeof(bls.u.rjt));
}
bls.ox_id = iparam->bls.ox_id;
bls.rx_id = iparam->bls.rx_id;
if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
io->indicator, io->reqtag,
SLI4_CQ_DEFAULT,
rnode, UINT32_MAX)) {
ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
}
case OCS_HW_BLS_ACC_SID:
{
sli_bls_payload_t bls;
bls.type = SLI_BLS_ACC;
ocs_memcpy(&bls.u.acc, iparam->bls_sid.payload, sizeof(bls.u.acc));
bls.ox_id = iparam->bls_sid.ox_id;
bls.rx_id = iparam->bls_sid.rx_id;
if (sli_xmit_bls_rsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &bls,
io->indicator, io->reqtag,
SLI4_CQ_DEFAULT,
rnode, iparam->bls_sid.s_id)) {
ocs_log_err(hw->os, "XMIT_BLS_RSP64 WQE SID error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
}
case OCS_HW_BCAST:
if ( (!send) || sli_xmit_bcast64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, send, len,
iparam->bcast.timeout, io->indicator, io->reqtag,
SLI4_CQ_DEFAULT, rnode,
iparam->bcast.r_ctl, iparam->bcast.type, iparam->bcast.df_ctl)) {
ocs_log_err(hw->os, "XMIT_BCAST64 WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
default:
ocs_log_err(hw->os, "bad SRRS type %#x\n", type);
rc = OCS_HW_RTN_ERROR;
}
if (OCS_HW_RTN_SUCCESS == rc) {
if (io->wq == NULL) {
io->wq = ocs_hw_queue_next_wq(hw, io);
ocs_hw_assert(io->wq != NULL);
}
io->xbusy = TRUE;
OCS_STAT(io->wq->use_count++);
ocs_hw_add_io_timed_wqe(hw, io);
rc = hw_wq_write(io->wq, &io->wqe);
if (rc >= 0) {
rc = 0;
} else {
ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
io->xbusy = FALSE;
ocs_hw_remove_io_timed_wqe(hw, io);
}
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_io_send(ocs_hw_t *hw, ocs_hw_io_type_e type, ocs_hw_io_t *io,
uint32_t len, ocs_hw_io_param_t *iparam, ocs_remote_node_t *rnode,
void *cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint32_t rpi;
uint8_t send_wqe = TRUE;
uint8_t timeout = 0;
CPUTRACE("");
if (!hw || !io || !rnode || !iparam) {
ocs_log_err(NULL, "bad parm hw=%p io=%p iparam=%p rnode=%p\n",
hw, io, iparam, rnode);
return OCS_HW_RTN_ERROR;
}
if (hw->state != OCS_HW_STATE_ACTIVE) {
ocs_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
return OCS_HW_RTN_ERROR;
}
rpi = rnode->indicator;
if (hw->workaround.use_unregistered_rpi && (rpi == UINT32_MAX)) {
rpi = hw->workaround.unregistered_rid;
ocs_log_test(hw->os, "using unregistered RPI: %d\n", rpi);
}
io->rnode = rnode;
io->type = type;
io->done = cb;
io->arg = arg;
switch (type) {
case OCS_HW_IO_INITIATOR_READ:
timeout = ocs_hw_set_io_wqe_timeout(io, iparam->fcp_ini.timeout);
if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
(iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
io->quarantine = TRUE;
}
ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
iparam->fcp_ini.rsp);
if (sli_fcp_iread64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge, len,
io->indicator, io->reqtag, SLI4_CQ_DEFAULT, rpi, rnode,
iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
timeout)) {
ocs_log_err(hw->os, "IREAD WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_IO_INITIATOR_WRITE:
timeout = ocs_hw_set_io_wqe_timeout(io, iparam->fcp_ini.timeout);
ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
iparam->fcp_ini.rsp);
if (sli_fcp_iwrite64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
len, iparam->fcp_ini.first_burst,
io->indicator, io->reqtag,
SLI4_CQ_DEFAULT, rpi, rnode,
iparam->fcp_ini.dif_oper, iparam->fcp_ini.blk_size,
timeout)) {
ocs_log_err(hw->os, "IWRITE WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_IO_INITIATOR_NODATA:
timeout = ocs_hw_set_io_wqe_timeout(io, iparam->fcp_ini.timeout);
ocs_hw_io_ini_sge(hw, io, iparam->fcp_ini.cmnd, iparam->fcp_ini.cmnd_size,
iparam->fcp_ini.rsp);
if (sli_fcp_icmnd64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl,
io->indicator, io->reqtag, SLI4_CQ_DEFAULT,
rpi, rnode, timeout)) {
ocs_log_err(hw->os, "ICMND WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
case OCS_HW_IO_TARGET_WRITE: {
uint16_t flags = iparam->fcp_tgt.flags;
fcp_xfer_rdy_iu_t *xfer = io->xfer_rdy.virt;
*((uint32_t *)xfer->fcp_data_ro) = ocs_htobe32(iparam->fcp_tgt.offset);
*((uint32_t *)xfer->fcp_burst_len) = ocs_htobe32(len);
*((uint32_t *)xfer->rsvd) = 0;
if (io->xbusy) {
flags |= SLI4_IO_CONTINUATION;
} else {
flags &= ~SLI4_IO_CONTINUATION;
}
io->wqe_timeout = iparam->fcp_tgt.timeout;
if (hw->workaround.use_dif_quarantine && (hw->config.dif_mode == OCS_HW_DIF_MODE_SEPARATE) &&
(iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
io->quarantine = TRUE;
}
if (hw->workaround.use_dif_sec_xri && (iparam->fcp_tgt.dif_oper != OCS_HW_DIF_OPER_DISABLED)) {
if (io->ovfl_io != NULL) {
io->sec_hio = io->ovfl_io;
io->sec_hio->quarantine = TRUE;
} else {
io->sec_hio = ocs_hw_io_alloc(hw);
}
if (io->sec_hio == NULL) {
io->sec_iparam = *iparam;
io->sec_len = len;
ocs_lock(&hw->io_lock);
ocs_list_remove(&hw->io_inuse, io);
ocs_list_add_tail(&hw->sec_hio_wait_list, io);
io->state = OCS_HW_IO_STATE_WAIT_SEC_HIO;
hw->sec_hio_wait_count++;
ocs_unlock(&hw->io_lock);
send_wqe = FALSE;
break;
}
if (io->xbusy) {
io->sec_hio->quarantine = TRUE;
}
}
if (io->xbusy && (io->sec_hio != NULL)) {
if (sli_fcp_cont_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
iparam->fcp_tgt.offset, len, io->indicator, io->sec_hio->indicator,
io->reqtag, SLI4_CQ_DEFAULT,
iparam->fcp_tgt.ox_id, rpi, rnode,
flags,
iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
ocs_log_err(hw->os, "TRECEIVE WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
} else {
if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
SLI4_CQ_DEFAULT,
iparam->fcp_tgt.ox_id, rpi, rnode,
flags,
iparam->fcp_tgt.dif_oper, iparam->fcp_tgt.blk_size,
iparam->fcp_tgt.cs_ctl, iparam->fcp_tgt.app_id)) {
ocs_log_err(hw->os, "TRECEIVE WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
}
break;
}
case OCS_HW_IO_TARGET_READ: {
uint16_t flags = iparam->fcp_tgt.flags;
if (io->xbusy) {
flags |= SLI4_IO_CONTINUATION;
} else {
flags &= ~SLI4_IO_CONTINUATION;
}
io->wqe_timeout = iparam->fcp_tgt.timeout;
if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, &io->def_sgl, io->first_data_sge,
iparam->fcp_tgt.offset, len, io->indicator, io->reqtag,
SLI4_CQ_DEFAULT,
iparam->fcp_tgt.ox_id, rpi, rnode,
flags,
iparam->fcp_tgt.dif_oper,
iparam->fcp_tgt.blk_size,
iparam->fcp_tgt.cs_ctl,
iparam->fcp_tgt.app_id)) {
ocs_log_err(hw->os, "TSEND WQE error\n");
rc = OCS_HW_RTN_ERROR;
} else if (hw->workaround.retain_tsend_io_length) {
io->length = len;
}
break;
}
case OCS_HW_IO_TARGET_RSP: {
uint16_t flags = iparam->fcp_tgt.flags;
if (io->xbusy) {
flags |= SLI4_IO_CONTINUATION;
} else {
flags &= ~SLI4_IO_CONTINUATION;
}
if (hw->auto_xfer_rdy_enabled && io->is_port_owned) {
if ((io->auto_xfer_rdy_dnrx = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1))) {
flags |= SLI4_IO_DNRX;
}
}
io->wqe_timeout = iparam->fcp_tgt.timeout;
if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size,
&io->def_sgl,
len,
io->indicator, io->reqtag,
SLI4_CQ_DEFAULT,
iparam->fcp_tgt.ox_id,
rpi, rnode,
flags, iparam->fcp_tgt.cs_ctl,
io->is_port_owned,
iparam->fcp_tgt.app_id)) {
ocs_log_err(hw->os, "TRSP WQE error\n");
rc = OCS_HW_RTN_ERROR;
}
break;
}
default:
ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
rc = OCS_HW_RTN_ERROR;
}
if (send_wqe && (OCS_HW_RTN_SUCCESS == rc)) {
if (io->wq == NULL) {
io->wq = ocs_hw_queue_next_wq(hw, io);
ocs_hw_assert(io->wq != NULL);
}
io->xbusy = TRUE;
OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
OCS_STAT(io->wq->use_count++);
ocs_hw_add_io_timed_wqe(hw, io);
rc = hw_wq_write(io->wq, &io->wqe);
if (rc >= 0) {
rc = 0;
} else {
ocs_log_err(hw->os, "sli_queue_write failed: %d\n", rc);
io->xbusy = FALSE;
ocs_hw_remove_io_timed_wqe(hw, io);
}
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_send_frame(ocs_hw_t *hw, fc_header_le_t *hdr, uint8_t sof, uint8_t eof, ocs_dma_t *payload,
ocs_hw_send_frame_context_t *ctx, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
{
int32_t rc;
ocs_hw_wqe_t *wqe;
uint32_t xri;
hw_wq_t *wq;
wqe = &ctx->wqe;
ctx->hw = hw;
ctx->wqcb = ocs_hw_reqtag_alloc(hw, callback, arg);
if (ctx->wqcb == NULL) {
ocs_log_err(hw->os, "can't allocate request tag\n");
return OCS_HW_RTN_NO_RESOURCES;
}
wq = ocs_varray_iter_next(hw->wq_class_array[1]);
if (wq == NULL) {
wq = hw->hw_wq[0];
}
xri = wq->send_frame_io->indicator;
rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, hw->sli.config.wqe_size, sof, eof, (uint32_t*) hdr, payload,
payload->len, OCS_HW_SEND_FRAME_TIMEOUT, xri, ctx->wqcb->instance_index);
if (rc) {
ocs_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
return OCS_HW_RTN_ERROR;
}
rc = hw_wq_write(wq, wqe);
if (rc) {
ocs_log_err(hw->os, "hw_wq_write failed: %d\n", rc);
return OCS_HW_RTN_ERROR;
}
OCS_STAT(wq->use_count++);
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_io_register_sgl(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *sgl, uint32_t sgl_count)
{
if (sli_get_sgl_preregister(&hw->sli)) {
ocs_log_err(hw->os, "can't use temporary SGL with pre-registered SGLs\n");
return OCS_HW_RTN_ERROR;
}
io->ovfl_sgl = sgl;
io->ovfl_sgl_count = sgl_count;
io->ovfl_io = NULL;
return OCS_HW_RTN_SUCCESS;
}
static void
ocs_hw_io_restore_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
{
io->sgl = &io->def_sgl;
io->sgl_count = io->def_sgl_count;
if (io->ovfl_io != NULL) {
ocs_hw_io_free(hw, io->ovfl_io);
io->ovfl_io = NULL;
}
io->ovfl_sgl = NULL;
io->ovfl_sgl_count = 0;
io->ovfl_lsp = NULL;
}
ocs_hw_rtn_e
ocs_hw_io_init_sges(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_io_type_e type)
{
sli4_sge_t *data = NULL;
uint32_t i = 0;
uint32_t skips = 0;
if (!hw || !io) {
ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p\n",
hw, io);
return OCS_HW_RTN_ERROR;
}
io->sgl = &io->def_sgl;
io->sgl_count = io->def_sgl_count;
io->first_data_sge = 0;
ocs_memset(io->sgl->virt, 0, 2 * sizeof(sli4_sge_t));
io->n_sge = 0;
io->sge_offset = 0;
io->type = type;
data = io->sgl->virt;
switch (type) {
case OCS_HW_IO_INITIATOR_READ:
case OCS_HW_IO_INITIATOR_WRITE:
case OCS_HW_IO_INITIATOR_NODATA:
data->sge_type = SLI4_SGE_TYPE_DATA;
data++;
data->sge_type = SLI4_SGE_TYPE_DATA;
if (OCS_HW_IO_INITIATOR_NODATA == type) {
data->last = TRUE;
}
data++;
io->n_sge = 2;
break;
case OCS_HW_IO_TARGET_WRITE:
#define OCS_TARGET_WRITE_SKIPS 2
skips = OCS_TARGET_WRITE_SKIPS;
data->sge_type = SLI4_SGE_TYPE_DATA;
data->buffer_address_high = ocs_addr32_hi(io->xfer_rdy.phys);
data->buffer_address_low = ocs_addr32_lo(io->xfer_rdy.phys);
data->buffer_length = io->xfer_rdy.size;
data++;
skips--;
io->n_sge = 1;
break;
case OCS_HW_IO_TARGET_READ:
#define OCS_TARGET_READ_SKIPS 2
skips = OCS_TARGET_READ_SKIPS;
break;
case OCS_HW_IO_TARGET_RSP:
break;
default:
ocs_log_err(hw->os, "unsupported IO type %#x\n", type);
return OCS_HW_RTN_ERROR;
}
for (i = 0; i < skips; i++) {
data->sge_type = SLI4_SGE_TYPE_SKIP;
data++;
}
io->n_sge += skips;
data->last = TRUE;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_io_add_seed_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_hw_dif_info_t *dif_info)
{
sli4_sge_t *data = NULL;
sli4_diseed_sge_t *dif_seed;
if ((dif_info == NULL) || (dif_info->dif_oper == OCS_HW_DIF_OPER_DISABLED)) {
return OCS_HW_RTN_SUCCESS;
}
if (!hw || !io) {
ocs_log_err(hw ? hw->os : NULL, "bad parameter hw=%p io=%p dif_info=%p\n",
hw, io, dif_info);
return OCS_HW_RTN_ERROR;
}
data = io->sgl->virt;
data += io->n_sge;
ocs_memset(data, 0, sizeof(sli4_diseed_sge_t));
dif_seed = (sli4_diseed_sge_t *)data;
dif_seed->ref_tag_cmp = dif_info->ref_tag_cmp;
dif_seed->ref_tag_repl = dif_info->ref_tag_repl;
dif_seed->app_tag_repl = dif_info->app_tag_repl;
dif_seed->repl_app_tag = dif_info->repl_app_tag;
if (SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) {
dif_seed->atrt = dif_info->disable_app_ref_ffff;
dif_seed->at = dif_info->disable_app_ffff;
}
dif_seed->sge_type = SLI4_SGE_TYPE_DISEED;
if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type) && dif_info->dif_separate) {
dif_seed->sge_type = SLI4_SGE_TYPE_SKIP;
}
dif_seed->app_tag_cmp = dif_info->app_tag_cmp;
dif_seed->dif_blk_size = dif_info->blk_size;
dif_seed->auto_incr_ref_tag = dif_info->auto_incr_ref_tag;
dif_seed->check_app_tag = dif_info->check_app_tag;
dif_seed->check_ref_tag = dif_info->check_ref_tag;
dif_seed->check_crc = dif_info->check_guard;
dif_seed->new_ref_tag = dif_info->repl_ref_tag;
switch(dif_info->dif_oper) {
case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CRC;
break;
case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_NODIF;
break;
case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
break;
case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
break;
case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CRC;
break;
case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
break;
case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
break;
case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
break;
case OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW:
dif_seed->dif_op_rx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
dif_seed->dif_op_tx = SLI4_SGE_DIF_OP_IN_RAW_OUT_RAW;
break;
default:
ocs_log_err(hw->os, "unsupported DIF operation %#x\n",
dif_info->dif_oper);
return OCS_HW_RTN_ERROR;
}
data->last = TRUE;
if (io->n_sge) {
data[-1].last = FALSE;
}
io->n_sge++;
return OCS_HW_RTN_SUCCESS;
}
static ocs_hw_rtn_e
ocs_hw_io_overflow_sgl(ocs_hw_t *hw, ocs_hw_io_t *io)
{
sli4_lsp_sge_t *lsp;
if (io->sgl == io->ovfl_sgl) {
return OCS_HW_RTN_ERROR;
}
if (sli_get_sgl_preregister(&hw->sli) &&
io->def_sgl_count > 4 &&
io->ovfl_io == NULL &&
((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli)))) {
io->ovfl_io = ocs_hw_io_alloc(hw);
if (io->ovfl_io != NULL) {
io->ovfl_sgl = &io->ovfl_io->def_sgl;
io->ovfl_sgl_count = io->ovfl_io->def_sgl_count;
}
}
if (io->ovfl_io == NULL || io->ovfl_sgl == NULL) {
return OCS_HW_RTN_ERROR;
}
((sli4_sge_t*)io->ovfl_sgl->virt)[0] = ((sli4_sge_t*)io->sgl->virt)[io->n_sge - 1];
lsp = &((sli4_lsp_sge_t*)io->sgl->virt)[io->n_sge - 1];
ocs_memset(lsp, 0, sizeof(*lsp));
if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
sli_skh_chain_sge_build(&hw->sli,
(sli4_sge_t*)lsp,
io->ovfl_io->indicator,
0,
0);
} else {
lsp->buffer_address_high = ocs_addr32_hi(io->ovfl_sgl->phys);
lsp->buffer_address_low = ocs_addr32_lo(io->ovfl_sgl->phys);
lsp->sge_type = SLI4_SGE_TYPE_LSP;
lsp->last = 0;
io->ovfl_lsp = lsp;
io->ovfl_lsp->segment_length = sizeof(sli4_sge_t);
}
io->sgl = io->ovfl_sgl;
io->sgl_count = io->ovfl_sgl_count;
io->n_sge = 1;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_io_add_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr, uint32_t length)
{
sli4_sge_t *data = NULL;
if (!hw || !io || !addr || !length) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter hw=%p io=%p addr=%lx length=%u\n",
hw, io, addr, length);
return OCS_HW_RTN_ERROR;
}
if ((length != 0) && (io->n_sge + 1) > io->sgl_count) {
if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
return OCS_HW_RTN_ERROR;
}
}
if (length > sli_get_max_sge(&hw->sli)) {
ocs_log_err(hw->os, "length of SGE %d bigger than allowed %d\n",
length, sli_get_max_sge(&hw->sli));
return OCS_HW_RTN_ERROR;
}
data = io->sgl->virt;
data += io->n_sge;
data->sge_type = SLI4_SGE_TYPE_DATA;
data->buffer_address_high = ocs_addr32_hi(addr);
data->buffer_address_low = ocs_addr32_lo(addr);
data->buffer_length = length;
data->data_offset = io->sge_offset;
data->last = TRUE;
if (io->n_sge) {
data[-1].last = FALSE;
}
if (io->first_data_sge == 0) {
io->first_data_sge = io->n_sge;
}
io->sge_offset += length;
io->n_sge++;
if (io->ovfl_lsp != NULL) {
io->ovfl_lsp->segment_length = io->n_sge * sizeof(sli4_sge_t);
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_io_add_dif_sge(ocs_hw_t *hw, ocs_hw_io_t *io, uintptr_t addr)
{
sli4_dif_sge_t *data = NULL;
if (!hw || !io || !addr) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter hw=%p io=%p addr=%lx\n",
hw, io, addr);
return OCS_HW_RTN_ERROR;
}
if ((io->n_sge + 1) > hw->config.n_sgl) {
if (ocs_hw_io_overflow_sgl(hw, io) != OCS_HW_RTN_ERROR) {
ocs_log_err(hw->os, "SGL full (%d)\n", io->n_sge);
return OCS_HW_RTN_ERROR;
}
}
data = io->sgl->virt;
data += io->n_sge;
data->sge_type = SLI4_SGE_TYPE_DIF;
if (((io->type == OCS_HW_IO_TARGET_WRITE) || (io->type == OCS_HW_IO_INITIATOR_READ)) &&
(SLI4_IF_TYPE_LANCER_FC_ETH != hw->sli.if_type)) {
data->sge_type = SLI4_SGE_TYPE_SKIP;
}
data->buffer_address_high = ocs_addr32_hi(addr);
data->buffer_address_low = ocs_addr32_lo(addr);
data->last = TRUE;
if (io->n_sge) {
data[-1].last = FALSE;
}
io->n_sge++;
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_io_abort(ocs_hw_t *hw, ocs_hw_io_t *io_to_abort, uint32_t send_abts, void *cb, void *arg)
{
sli4_abort_type_e atype = SLI_ABORT_MAX;
uint32_t id = 0, mask = 0;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
hw_wq_callback_t *wqcb;
if (!hw || !io_to_abort) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter hw=%p io=%p\n",
hw, io_to_abort);
return OCS_HW_RTN_ERROR;
}
if (hw->state != OCS_HW_STATE_ACTIVE) {
ocs_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
hw->state);
return OCS_HW_RTN_ERROR;
}
if (ocs_ref_get_unless_zero(&io_to_abort->ref) == 0) {
ocs_log_test(hw ? hw->os : NULL,
"io not active xri=0x%x tag=0x%x\n",
io_to_abort->indicator, io_to_abort->reqtag);
return OCS_HW_RTN_IO_NOT_ACTIVE;
}
if (io_to_abort->wq == NULL) {
ocs_log_test(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
io_to_abort->indicator);
ocs_ref_put(&io_to_abort->ref);
return OCS_HW_RTN_IO_NOT_ACTIVE;
}
ocs_lock(&hw->io_abort_lock);
if (io_to_abort->abort_in_progress) {
ocs_unlock(&hw->io_abort_lock);
ocs_ref_put(&io_to_abort->ref);
ocs_log_debug(hw ? hw->os : NULL,
"io already being aborted xri=0x%x tag=0x%x\n",
io_to_abort->indicator, io_to_abort->reqtag);
return OCS_HW_RTN_IO_ABORT_IN_PROGRESS;
}
io_to_abort->abort_in_progress = 1;
ocs_unlock(&hw->io_abort_lock);
io_to_abort->abort_done = cb;
io_to_abort->abort_arg = arg;
atype = SLI_ABORT_XRI;
id = io_to_abort->indicator;
wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_abort, io_to_abort);
if (wqcb == NULL) {
ocs_log_err(hw->os, "can't allocate request tag\n");
return OCS_HW_RTN_NO_RESOURCES;
}
io_to_abort->abort_reqtag = wqcb->instance_index;
if (io_to_abort->wq != NULL) {
sli_queue_lock(io_to_abort->wq->queue);
if (ocs_list_on_list(&io_to_abort->wqe.link)) {
io_to_abort->wqe.abort_wqe_submit_needed = 1;
io_to_abort->wqe.send_abts = send_abts;
io_to_abort->wqe.id = id;
io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
sli_queue_unlock(io_to_abort->wq->queue);
return 0;
}
sli_queue_unlock(io_to_abort->wq->queue);
}
if (sli_abort_wqe(&hw->sli, io_to_abort->wqe.wqebuf, hw->sli.config.wqe_size, atype, send_abts, id, mask,
io_to_abort->abort_reqtag, SLI4_CQ_DEFAULT)) {
ocs_log_err(hw->os, "ABORT WQE error\n");
io_to_abort->abort_reqtag = UINT32_MAX;
ocs_hw_reqtag_free(hw, wqcb);
rc = OCS_HW_RTN_ERROR;
}
if (OCS_HW_RTN_SUCCESS == rc) {
if (io_to_abort->wq == NULL) {
io_to_abort->wq = ocs_hw_queue_next_wq(hw, io_to_abort);
ocs_hw_assert(io_to_abort->wq != NULL);
}
rc = hw_wq_write(io_to_abort->wq, &io_to_abort->wqe);
if (rc > 0) {
rc = 0;
}
}
if (OCS_HW_RTN_SUCCESS != rc) {
ocs_lock(&hw->io_abort_lock);
io_to_abort->abort_in_progress = 0;
ocs_unlock(&hw->io_abort_lock);
ocs_ref_put(&io_to_abort->ref);
}
return rc;
}
int32_t
ocs_hw_io_get_xid(ocs_hw_t *hw, ocs_hw_io_t *io)
{
if (!hw || !io) {
ocs_log_err(hw ? hw->os : NULL,
"bad parameter hw=%p io=%p\n", hw, io);
return -1;
}
return io->indicator;
}
typedef struct ocs_hw_fw_write_cb_arg {
ocs_hw_fw_cb_t cb;
void *arg;
} ocs_hw_fw_write_cb_arg_t;
typedef struct ocs_hw_sfp_cb_arg {
ocs_hw_sfp_cb_t cb;
void *arg;
ocs_dma_t payload;
} ocs_hw_sfp_cb_arg_t;
typedef struct ocs_hw_temp_cb_arg {
ocs_hw_temp_cb_t cb;
void *arg;
} ocs_hw_temp_cb_arg_t;
typedef struct ocs_hw_link_stat_cb_arg {
ocs_hw_link_stat_cb_t cb;
void *arg;
} ocs_hw_link_stat_cb_arg_t;
typedef struct ocs_hw_host_stat_cb_arg {
ocs_hw_host_stat_cb_t cb;
void *arg;
} ocs_hw_host_stat_cb_arg_t;
typedef struct ocs_hw_dump_get_cb_arg {
ocs_hw_dump_get_cb_t cb;
void *arg;
void *mbox_cmd;
} ocs_hw_dump_get_cb_arg_t;
typedef struct ocs_hw_dump_clear_cb_arg {
ocs_hw_dump_clear_cb_t cb;
void *arg;
void *mbox_cmd;
} ocs_hw_dump_clear_cb_arg_t;
ocs_hw_rtn_e
ocs_hw_firmware_write(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
{
if (hw->sli.if_type == SLI4_IF_TYPE_LANCER_FC_ETH) {
return ocs_hw_firmware_write_lancer(hw, dma, size, offset, last, cb, arg);
} else {
return -1;
}
}
ocs_hw_rtn_e
ocs_hw_firmware_write_lancer(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, int last, ocs_hw_fw_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
uint8_t *mbxdata;
ocs_hw_fw_write_cb_arg_t *cb_arg;
int noc=0;
if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_fw_write_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (sli_cmd_common_write_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE, noc, last,
size, offset, "/prg/", dma)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_fw_write, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "COMMON_WRITE_OBJECT failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
}
return rc;
}
static int32_t
ocs_hw_cb_fw_write(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
sli4_res_common_write_object_t* wr_obj_rsp = (sli4_res_common_write_object_t*) &(mbox_rsp->payload.embed);
ocs_hw_fw_write_cb_arg_t *cb_arg = arg;
uint32_t bytes_written;
uint16_t mbox_status;
uint32_t change_status;
bytes_written = wr_obj_rsp->actual_write_length;
mbox_status = mbox_rsp->hdr.status;
change_status = wr_obj_rsp->change_status;
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_status) {
status = mbox_status;
}
cb_arg->cb(status, bytes_written, change_status, cb_arg->arg);
}
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
}
return 0;
}
static int32_t
ocs_hw_cb_sfp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_sfp_cb_arg_t *cb_arg = arg;
ocs_dma_t *payload = NULL;
sli4_res_common_read_transceiver_data_t* mbox_rsp = NULL;
uint32_t bytes_written;
if (cb_arg) {
payload = &(cb_arg->payload);
if (cb_arg->cb) {
mbox_rsp = (sli4_res_common_read_transceiver_data_t*) payload->virt;
bytes_written = mbox_rsp->hdr.response_length;
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(hw->os, status, bytes_written, mbox_rsp->page_data, cb_arg->arg);
}
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_sfp(ocs_hw_t *hw, uint16_t page, ocs_hw_sfp_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
ocs_hw_sfp_cb_arg_t *cb_arg;
uint8_t *mbxdata;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_sfp_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_read_transceiver_data_t),
OCS_MIN_DMA_ALIGNMENT)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_common_read_transceiver_data(&hw->sli, mbxdata, SLI4_BMBX_SIZE, page,
&cb_arg->payload)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_sfp, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "READ_TRANSCEIVER_DATA failed with status %d\n",
rc);
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_sfp_cb_arg_t));
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_get_temperature(ocs_hw_t *hw, ocs_hw_temp_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
ocs_hw_temp_cb_arg_t *cb_arg;
uint8_t *mbxdata;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_temp_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (sli_cmd_dump_type4(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
SLI4_WKI_TAG_SAT_TEM)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_temp, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "DUMP_TYPE4 failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
}
return rc;
}
static int32_t
ocs_hw_cb_temp(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_dump4_t* mbox_rsp = (sli4_cmd_dump4_t*) mqe;
ocs_hw_temp_cb_arg_t *cb_arg = arg;
uint32_t curr_temp = mbox_rsp->resp_data[0];
uint32_t crit_temp_thrshld = mbox_rsp->resp_data[1];
uint32_t warn_temp_thrshld = mbox_rsp->resp_data[2];
uint32_t norm_temp_thrshld = mbox_rsp->resp_data[3];
uint32_t fan_off_thrshld = mbox_rsp->resp_data[4];
uint32_t fan_on_thrshld = mbox_rsp->resp_data[5];
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(status,
curr_temp,
crit_temp_thrshld,
warn_temp_thrshld,
norm_temp_thrshld,
fan_off_thrshld,
fan_on_thrshld,
cb_arg->arg);
}
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_temp_cb_arg_t));
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_link_stats(ocs_hw_t *hw,
uint8_t req_ext_counters,
uint8_t clear_overflow_flags,
uint8_t clear_all_counters,
ocs_hw_link_stat_cb_t cb,
void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
ocs_hw_link_stat_cb_arg_t *cb_arg;
uint8_t *mbxdata;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_link_stat_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (sli_cmd_read_link_stats(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
req_ext_counters,
clear_overflow_flags,
clear_all_counters)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_link_stat, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "READ_LINK_STATS failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
}
return rc;
}
static int32_t
ocs_hw_cb_link_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_read_link_stats_t* mbox_rsp = (sli4_cmd_read_link_stats_t*) mqe;
ocs_hw_link_stat_cb_arg_t *cb_arg = arg;
ocs_hw_link_stat_counts_t counts[OCS_HW_LINK_STAT_MAX];
uint32_t num_counters = (mbox_rsp->gec ? 20 : 13);
ocs_memset(counts, 0, sizeof(ocs_hw_link_stat_counts_t) *
OCS_HW_LINK_STAT_MAX);
counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].overflow = mbox_rsp->w02of;
counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].overflow = mbox_rsp->w03of;
counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].overflow = mbox_rsp->w04of;
counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].overflow = mbox_rsp->w05of;
counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].overflow = mbox_rsp->w06of;
counts[OCS_HW_LINK_STAT_CRC_COUNT].overflow = mbox_rsp->w07of;
counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].overflow = mbox_rsp->w08of;
counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].overflow = mbox_rsp->w09of;
counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].overflow = mbox_rsp->w10of;
counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].overflow = mbox_rsp->w11of;
counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].overflow = mbox_rsp->w12of;
counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].overflow = mbox_rsp->w13of;
counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].overflow = mbox_rsp->w14of;
counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].overflow = mbox_rsp->w15of;
counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].overflow = mbox_rsp->w16of;
counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].overflow = mbox_rsp->w17of;
counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].overflow = mbox_rsp->w18of;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].overflow = mbox_rsp->w19of;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].overflow = mbox_rsp->w20of;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].overflow = mbox_rsp->w21of;
counts[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = mbox_rsp->link_failure_error_count;
counts[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = mbox_rsp->loss_of_sync_error_count;
counts[OCS_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = mbox_rsp->loss_of_signal_error_count;
counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = mbox_rsp->primitive_sequence_error_count;
counts[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = mbox_rsp->invalid_transmission_word_error_count;
counts[OCS_HW_LINK_STAT_CRC_COUNT].counter = mbox_rsp->crc_error_count;
counts[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = mbox_rsp->primitive_sequence_event_timeout_count;
counts[OCS_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = mbox_rsp->elastic_buffer_overrun_error_count;
counts[OCS_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = mbox_rsp->arbitration_fc_al_timout_count;
counts[OCS_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = mbox_rsp->advertised_receive_bufftor_to_buffer_credit;
counts[OCS_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = mbox_rsp->current_receive_buffer_to_buffer_credit;
counts[OCS_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = mbox_rsp->advertised_transmit_buffer_to_buffer_credit;
counts[OCS_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = mbox_rsp->current_transmit_buffer_to_buffer_credit;
counts[OCS_HW_LINK_STAT_RCV_EOFA_COUNT].counter = mbox_rsp->received_eofa_count;
counts[OCS_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = mbox_rsp->received_eofdti_count;
counts[OCS_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = mbox_rsp->received_eofni_count;
counts[OCS_HW_LINK_STAT_RCV_SOFF_COUNT].counter = mbox_rsp->received_soff_count;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = mbox_rsp->received_dropped_no_aer_count;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = mbox_rsp->received_dropped_no_available_rpi_resources_count;
counts[OCS_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = mbox_rsp->received_dropped_no_available_xri_resources_count;
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(status,
num_counters,
counts,
cb_arg->arg);
}
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_link_stat_cb_arg_t));
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_host_stats(ocs_hw_t *hw, uint8_t cc, ocs_hw_host_stat_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
ocs_hw_host_stat_cb_arg_t *cb_arg;
uint8_t *mbxdata;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_host_stat_cb_arg_t), 0);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (sli_cmd_read_status(&hw->sli, mbxdata, SLI4_BMBX_SIZE, cc)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_cb_host_stat, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "READ_HOST_STATS failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
}
return rc;
}
static int32_t
ocs_hw_cb_host_stat(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_read_status_t* mbox_rsp = (sli4_cmd_read_status_t*) mqe;
ocs_hw_host_stat_cb_arg_t *cb_arg = arg;
ocs_hw_host_stat_counts_t counts[OCS_HW_HOST_STAT_MAX];
uint32_t num_counters = OCS_HW_HOST_STAT_MAX;
ocs_memset(counts, 0, sizeof(ocs_hw_host_stat_counts_t) *
OCS_HW_HOST_STAT_MAX);
counts[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter = mbox_rsp->transmit_kbyte_count;
counts[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter = mbox_rsp->receive_kbyte_count;
counts[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter = mbox_rsp->transmit_frame_count;
counts[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter = mbox_rsp->receive_frame_count;
counts[OCS_HW_HOST_STAT_TX_SEQ_COUNT].counter = mbox_rsp->transmit_sequence_count;
counts[OCS_HW_HOST_STAT_RX_SEQ_COUNT].counter = mbox_rsp->receive_sequence_count;
counts[OCS_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = mbox_rsp->total_exchanges_originator;
counts[OCS_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = mbox_rsp->total_exchanges_responder;
counts[OCS_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = mbox_rsp->receive_p_bsy_count;
counts[OCS_HW_HOST_STAT_RX_F_BSY_COUNT].counter = mbox_rsp->receive_f_bsy_count;
counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_rq_buffer_count;
counts[OCS_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = mbox_rsp->empty_rq_timeout_count;
counts[OCS_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = mbox_rsp->dropped_frames_due_to_no_xri_count;
counts[OCS_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = mbox_rsp->empty_xri_pool_count;
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(status,
num_counters,
counts,
cb_arg->arg);
}
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_host_stat_cb_arg_t));
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
typedef struct ocs_hw_linkcfg_map_s {
ocs_hw_linkcfg_e linkcfg;
const char *clp_str;
} ocs_hw_linkcfg_map_t;
static ocs_hw_linkcfg_map_t linkcfg_map[] = {
{OCS_HW_LINKCFG_4X10G, "ELX_4x10G"},
{OCS_HW_LINKCFG_1X40G, "ELX_1x40G"},
{OCS_HW_LINKCFG_2X16G, "ELX_2x16G"},
{OCS_HW_LINKCFG_4X8G, "ELX_4x8G"},
{OCS_HW_LINKCFG_4X1G, "ELX_4x1G"},
{OCS_HW_LINKCFG_2X10G, "ELX_2x10G"},
{OCS_HW_LINKCFG_2X10G_2X8G, "ELX_2x10G_2x8G"}};
typedef struct ocs_hw_skyhawk_linkcfg_map_s {
ocs_hw_linkcfg_e linkcfg;
uint32_t config_id;
} ocs_hw_skyhawk_linkcfg_map_t;
static ocs_hw_skyhawk_linkcfg_map_t skyhawk_linkcfg_map[] = {
{OCS_HW_LINKCFG_4X10G, 0x0a},
{OCS_HW_LINKCFG_1X40G, 0x09},
};
static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_clp(const char *clp_str)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
if (ocs_strncmp(linkcfg_map[i].clp_str, clp_str, ocs_strlen(clp_str)) == 0) {
return linkcfg_map[i].linkcfg;
}
}
return OCS_HW_LINKCFG_NA;
}
static const char *
ocs_hw_clp_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(linkcfg_map); i++) {
if (linkcfg_map[i].linkcfg == linkcfg) {
return linkcfg_map[i].clp_str;
}
}
return NULL;
}
static uint32_t
ocs_hw_config_id_from_linkcfg(ocs_hw_linkcfg_e linkcfg)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
if (skyhawk_linkcfg_map[i].linkcfg == linkcfg) {
return skyhawk_linkcfg_map[i].config_id;
}
}
return 0;
}
static ocs_hw_linkcfg_e
ocs_hw_linkcfg_from_config_id(const uint32_t config_id)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(skyhawk_linkcfg_map); i++) {
if (skyhawk_linkcfg_map[i].config_id == config_id) {
return skyhawk_linkcfg_map[i].linkcfg;
}
}
return OCS_HW_LINKCFG_NA;
}
typedef struct ocs_hw_linkcfg_cb_arg_s {
ocs_hw_port_control_cb_t cb;
void *arg;
uint32_t opts;
int32_t status;
ocs_dma_t dma_cmd;
ocs_dma_t dma_resp;
uint32_t result_len;
} ocs_hw_linkcfg_cb_arg_t;
static ocs_hw_rtn_e
ocs_hw_set_linkcfg(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
if (!sli_link_is_configurable(&hw->sli)) {
ocs_log_debug(hw->os, "Function not supported\n");
return OCS_HW_RTN_ERROR;
}
if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
return ocs_hw_set_linkcfg_lancer(hw, value, opts, cb, arg);
} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
return ocs_hw_set_linkcfg_skyhawk(hw, value, opts, cb, arg);
} else {
ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
return OCS_HW_RTN_ERROR;
}
}
static ocs_hw_rtn_e
ocs_hw_set_linkcfg_lancer(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
ocs_hw_linkcfg_cb_arg_t *cb_arg;
const char *value_str = NULL;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
value_str = ocs_hw_clp_from_linkcfg(value);
cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
return OCS_HW_RTN_NO_MEMORY;
}
ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_LinkConfig=%s", value_str);
if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
return OCS_HW_RTN_NO_MEMORY;
}
ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->opts = opts;
rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
if (rc) {
ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
(char *)cb_arg->dma_cmd.virt);
}
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_dma_free(hw->os, &cb_arg->dma_resp);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
return rc;
}
static void
ocs_hw_set_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
if (status) {
ocs_log_test(hw->os, "SET_RECONFIG_LINK_ID failed, status=%d\n", status);
}
if (cb_arg->cb) {
cb_arg->cb(status, 0, cb_arg->arg);
}
if (cb_arg->opts != OCS_CMD_POLL) {
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
}
static ocs_hw_rtn_e
ocs_hw_set_linkcfg_skyhawk(ocs_hw_t *hw, ocs_hw_linkcfg_e value, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
uint8_t *mbxdata;
ocs_hw_linkcfg_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint32_t config_id;
config_id = ocs_hw_config_id_from_linkcfg(value);
if (config_id == 0) {
ocs_log_test(hw->os, "Link config %d not supported by Skyhawk\n", value);
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
if (sli_cmd_common_set_reconfig_link_id(&hw->sli, mbxdata, SLI4_BMBX_SIZE, NULL, 0, config_id)) {
rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_set_active_link_config_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "SET_RECONFIG_LINK_ID failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
} else if (opts == OCS_CMD_POLL) {
ocs_hw_set_active_link_config_cb(hw, 0, mbxdata, cb_arg);
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
} else {
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_get_linkcfg(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
if (!sli_link_is_configurable(&hw->sli)) {
ocs_log_debug(hw->os, "Function not supported\n");
return OCS_HW_RTN_ERROR;
}
if ((SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_LANCER_G7 == sli_get_if_type(&hw->sli))){
return ocs_hw_get_linkcfg_lancer(hw, opts, cb, arg);
} else if ((SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) ||
(SLI4_IF_TYPE_BE3_SKH_VF == sli_get_if_type(&hw->sli))) {
return ocs_hw_get_linkcfg_skyhawk(hw, opts, cb, arg);
} else {
ocs_log_test(hw->os, "Function not supported for this IF_TYPE\n");
return OCS_HW_RTN_ERROR;
}
}
static ocs_hw_rtn_e
ocs_hw_get_linkcfg_lancer(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
ocs_hw_linkcfg_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
return OCS_HW_RTN_NO_MEMORY;
}
ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "show / OEMELX_LinkConfig");
if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, ocs_strlen(cmd)+1, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
return OCS_HW_RTN_NO_MEMORY;
}
ocs_memset(cb_arg->dma_cmd.virt, 0, ocs_strlen(cmd)+1);
ocs_memcpy(cb_arg->dma_cmd.virt, cmd, ocs_strlen(cmd));
if (ocs_dma_alloc(hw->os, &cb_arg->dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->opts = opts;
rc = ocs_hw_exec_dmtf_clp_cmd(hw, &cb_arg->dma_cmd, &cb_arg->dma_resp,
opts, ocs_hw_linkcfg_dmtf_clp_cb, cb_arg);
if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
if (rc) {
ocs_log_test(hw->os, "CLP cmd=\"%s\" failed\n",
(char *)cb_arg->dma_cmd.virt);
}
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_dma_free(hw->os, &cb_arg->dma_resp);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
return rc;
}
static void
ocs_hw_get_active_link_config_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
sli4_res_common_get_reconfig_link_info_t *rsp = cb_arg->dma_cmd.virt;
ocs_hw_linkcfg_e value = OCS_HW_LINKCFG_NA;
if (status) {
ocs_log_test(hw->os, "GET_RECONFIG_LINK_INFO failed, status=%d\n", status);
} else {
value = ocs_hw_linkcfg_from_config_id(rsp->active_link_config_id);
}
if (cb_arg->cb) {
cb_arg->cb(status, value, cb_arg->arg);
}
if (cb_arg->opts != OCS_CMD_POLL) {
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
}
static ocs_hw_rtn_e
ocs_hw_get_linkcfg_skyhawk(ocs_hw_t *hw, uint32_t opts, ocs_hw_port_control_cb_t cb, void *arg)
{
uint8_t *mbxdata;
ocs_hw_linkcfg_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_linkcfg_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->opts = opts;
if (ocs_dma_alloc(hw->os, &cb_arg->dma_cmd, sizeof(sli4_res_common_get_reconfig_link_info_t), 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_common_get_reconfig_link_info(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->dma_cmd)) {
rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_get_active_link_config_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "GET_RECONFIG_LINK_INFO failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
} else if (opts == OCS_CMD_POLL) {
ocs_hw_get_active_link_config_cb(hw, 0, mbxdata, cb_arg);
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_linkcfg_cb_arg_t));
} else {
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_set_dif_seed(ocs_hw_t *hw)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
sli4_req_common_set_features_dif_seed_t seed_param;
ocs_memset(&seed_param, 0, sizeof(seed_param));
seed_param.seed = hw->config.dif_seed;
if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_DIF_SEED,
4,
(uint32_t*)&seed_param)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
} else {
ocs_log_debug(hw->os, "DIF seed set to 0x%x\n",
hw->config.dif_seed);
}
} else {
ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_set_dif_mode(ocs_hw_t *hw)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
sli4_req_common_set_features_t10_pi_mem_model_t mode_param;
ocs_memset(&mode_param, 0, sizeof(mode_param));
mode_param.tmm = (hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? 0 : 1);
if (sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_DIF_MEMORY_MODE,
sizeof(mode_param),
(uint32_t*)&mode_param)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
} else {
ocs_log_test(hw->os, "DIF mode set to %s\n",
(hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE ? "inline" : "separate"));
}
} else {
ocs_log_err(hw->os, "sli_cmd_common_set_features failed\n");
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
static void
ocs_hw_watchdog_timer_cb(void *arg)
{
ocs_hw_t *hw = (ocs_hw_t *)arg;
ocs_hw_config_watchdog_timer(hw);
return;
}
static void
ocs_hw_cb_cfg_watchdog(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
uint16_t timeout = hw->watchdog_timeout;
if (status != 0) {
ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", status);
} else {
if(timeout != 0) {
ocs_setup_timer(hw->os, &hw->watchdog_timer, ocs_hw_watchdog_timer_cb, hw, (timeout*1000 - 500) );
}else {
ocs_del_timer(&hw->watchdog_timer);
}
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return;
}
static ocs_hw_rtn_e
ocs_hw_config_watchdog_timer(ocs_hw_t *hw)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t *buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
sli4_cmd_lowlevel_set_watchdog(&hw->sli, buf, SLI4_BMBX_SIZE, hw->watchdog_timeout);
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_cfg_watchdog, NULL);
if (rc) {
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
ocs_log_err(hw->os, "config watchdog timer failed, rc = %d\n", rc);
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_config_auto_xfer_rdy_t10pi(ocs_hw_t *hw, uint8_t *buf)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
sli4_req_common_set_features_xfer_rdy_t10pi_t param;
ocs_memset(¶m, 0, sizeof(param));
param.rtc = (hw->config.auto_xfer_rdy_ref_tag_is_lba ? 0 : 1);
param.atv = (hw->config.auto_xfer_rdy_app_tag_valid ? 1 : 0);
param.tmm = ((hw->config.dif_mode == OCS_HW_DIF_MODE_INLINE) ? 0 : 1);
param.app_tag = hw->config.auto_xfer_rdy_app_tag_value;
param.blk_size = hw->config.auto_xfer_rdy_blk_size_chip;
switch (hw->config.auto_xfer_rdy_p_type) {
case 1:
param.p_type = 0;
break;
case 3:
param.p_type = 2;
break;
default:
ocs_log_err(hw->os, "unsupported p_type %d\n",
hw->config.auto_xfer_rdy_p_type);
return OCS_HW_RTN_ERROR;
}
sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_SET_CONFIG_AUTO_XFER_RDY_T10PI,
sizeof(param),
¶m);
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
} else {
ocs_log_test(hw->os, "Auto XFER RDY T10 PI configured rtc:%d atv:%d p_type:%d app_tag:%x blk_size:%d\n",
param.rtc, param.atv, param.p_type,
param.app_tag, param.blk_size);
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_config_sli_port_health_check(ocs_hw_t *hw, uint8_t query, uint8_t enable)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
sli4_req_common_set_features_health_check_t param;
ocs_memset(¶m, 0, sizeof(param));
param.hck = enable;
param.qry = query;
sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK,
sizeof(param),
¶m);
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "ocs_hw_command returns %d\n", rc);
} else {
ocs_log_test(hw->os, "SLI Port Health Check is enabled \n");
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_config_set_fdt_xfer_hint(ocs_hw_t *hw, uint32_t fdt_xfer_hint)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
sli4_req_common_set_features_set_fdt_xfer_hint_t param;
ocs_memset(¶m, 0, sizeof(param));
param.fdt_xfer_hint = fdt_xfer_hint;
sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_SET_FTD_XFER_HINT,
sizeof(param),
¶m);
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_warn(hw->os, "set FDT hint %d failed: %d\n", fdt_xfer_hint, rc);
} else {
ocs_log_debug(hw->os, "Set FTD transfer hint to %d\n", param.fdt_xfer_hint);
}
return rc;
}
static void
ocs_hw_linkcfg_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint32_t result_len, void *arg)
{
int32_t rval;
char retdata_str[64];
ocs_hw_linkcfg_cb_arg_t *cb_arg = (ocs_hw_linkcfg_cb_arg_t *)arg;
ocs_hw_linkcfg_e linkcfg = OCS_HW_LINKCFG_NA;
if (status) {
ocs_log_test(hw->os, "CLP cmd failed, status=%d\n", status);
} else {
rval = ocs_hw_clp_resp_get_value(hw, "retdata", retdata_str,
sizeof(retdata_str),
cb_arg->dma_resp.virt,
result_len);
if (rval <= 0) {
ocs_log_err(hw->os, "failed to get retdata %d\n", result_len);
} else {
linkcfg = ocs_hw_linkcfg_from_clp(retdata_str);
}
}
if (cb_arg->cb) {
cb_arg->cb(status, linkcfg, cb_arg->arg);
}
if (cb_arg->opts != OCS_CMD_POLL) {
ocs_dma_free(hw->os, &cb_arg->dma_cmd);
ocs_dma_free(hw->os, &cb_arg->dma_resp);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
}
ocs_hw_rtn_e
ocs_hw_set_dump_location(ocs_hw_t *hw, uint32_t num_buffers, ocs_dma_t *dump_buffers, uint8_t fdb)
{
uint8_t bus, dev, func;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
if (hw->workaround.disable_dump_loc) {
ocs_log_test(hw->os, "FW version is too old for this feature\n");
return OCS_HW_RTN_ERROR;
}
ocs_get_bus_dev_func(hw->os, &bus, &dev, &func);
if (fdb == 0 && func != 0) {
ocs_log_test(hw->os, "function only valid for pci function 0, %d passed\n",
func);
return OCS_HW_RTN_ERROR;
}
if (num_buffers > 1) {
uint32_t sge_size = num_buffers * sizeof(sli4_sge_t);
sli4_sge_t *sge;
uint32_t i;
if (hw->dump_sges.size < sge_size) {
ocs_dma_free(hw->os, &hw->dump_sges);
if (ocs_dma_alloc(hw->os, &hw->dump_sges, sge_size, OCS_MIN_DMA_ALIGNMENT)) {
ocs_log_err(hw->os, "SGE DMA allocation failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
}
ocs_memset(hw->dump_sges.virt, 0, hw->dump_sges.size);
hw->dump_sges.len = sge_size;
sge = hw->dump_sges.virt;
for (i = 0; i < num_buffers; i++) {
sge[i].buffer_address_high = ocs_addr32_hi(dump_buffers[i].phys);
sge[i].buffer_address_low = ocs_addr32_lo(dump_buffers[i].phys);
sge[i].last = (i == num_buffers - 1 ? 1 : 0);
sge[i].buffer_length = dump_buffers[i].size;
}
rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
SLI4_BMBX_SIZE, FALSE, TRUE,
&hw->dump_sges, fdb);
} else {
dump_buffers->len = dump_buffers->size;
rc = sli_cmd_common_set_dump_location(&hw->sli, (void *)buf,
SLI4_BMBX_SIZE, FALSE, FALSE,
dump_buffers, fdb);
}
if (rc) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL,
NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "ocs_hw_command returns %d\n",
rc);
}
} else {
ocs_log_err(hw->os,
"sli_cmd_common_set_dump_location failed\n");
rc = OCS_HW_RTN_ERROR;
}
return rc;
}
static ocs_hw_rtn_e
ocs_hw_set_eth_license(ocs_hw_t *hw, uint32_t license)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
char cmd[OCS_HW_DMTF_CLP_CMD_MAX];
ocs_dma_t dma_cmd;
ocs_dma_t dma_resp;
if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
return OCS_HW_RTN_ERROR;
}
ocs_snprintf(cmd, OCS_HW_DMTF_CLP_CMD_MAX, "set / OEMELX_Ethernet_License=%X", license);
if (ocs_dma_alloc(hw->os, &dma_cmd, ocs_strlen(cmd)+1, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
ocs_memset(dma_cmd.virt, 0, ocs_strlen(cmd)+1);
ocs_memcpy(dma_cmd.virt, cmd, ocs_strlen(cmd));
if (ocs_dma_alloc(hw->os, &dma_resp, OCS_HW_DMTF_CLP_RSP_MAX, 4096)) {
ocs_log_err(hw->os, "malloc failed\n");
ocs_dma_free(hw->os, &dma_cmd);
return OCS_HW_RTN_NO_MEMORY;
}
if (ocs_hw_exec_dmtf_clp_cmd(hw, &dma_cmd, &dma_resp, OCS_CMD_POLL, NULL, NULL)) {
ocs_log_err(hw->os, "CLP cmd=\"%s\" failed\n", (char *)dma_cmd.virt);
rc = OCS_HW_RTN_ERROR;
}
ocs_dma_free(hw->os, &dma_cmd);
ocs_dma_free(hw->os, &dma_resp);
return rc;
}
typedef struct ocs_hw_clp_cb_arg_s {
ocs_hw_dmtf_clp_cb_t cb;
ocs_dma_t *dma_resp;
int32_t status;
uint32_t opts;
void *arg;
} ocs_hw_clp_cb_arg_t;
static ocs_hw_rtn_e
ocs_hw_exec_dmtf_clp_cmd(ocs_hw_t *hw, ocs_dma_t *dma_cmd, ocs_dma_t *dma_resp, uint32_t opts, ocs_hw_dmtf_clp_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
ocs_hw_clp_cb_arg_t *cb_arg;
uint8_t *mbxdata;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(*cb_arg), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->dma_resp = dma_resp;
cb_arg->opts = opts;
if (sli_cmd_dmtf_exec_clp_cmd(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
dma_cmd, dma_resp)) {
rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_dmtf_clp_cb, cb_arg);
if (opts == OCS_CMD_POLL && rc == OCS_HW_RTN_SUCCESS) {
ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
ocs_hw_dmtf_clp_cb(hw, 0, mbxdata, cb_arg);
rc = cb_arg->status;
}
if (opts == OCS_CMD_POLL || rc != OCS_HW_RTN_SUCCESS) {
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "ocs_hw_command failed\n");
}
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
} else {
ocs_log_test(hw->os, "sli_cmd_dmtf_exec_clp_cmd failed\n");
rc = OCS_HW_RTN_ERROR;
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
}
return rc;
}
static void
ocs_hw_dmtf_clp_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
int32_t cb_status = 0;
sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
sli4_res_dmtf_exec_clp_cmd_t *clp_rsp = (sli4_res_dmtf_exec_clp_cmd_t *) mbox_rsp->payload.embed;
ocs_hw_clp_cb_arg_t *cb_arg = arg;
uint32_t result_len = 0;
int32_t stat_len;
char stat_str[8];
if (status || mbox_rsp->hdr.status || clp_rsp->clp_status) {
ocs_log_debug(hw->os, "status=x%x/x%x/x%x addl=x%x clp=x%x detail=x%x\n",
status,
mbox_rsp->hdr.status,
clp_rsp->hdr.status,
clp_rsp->hdr.additional_status,
clp_rsp->clp_status,
clp_rsp->clp_detailed_status);
if (status) {
cb_status = status;
} else if (mbox_rsp->hdr.status) {
cb_status = mbox_rsp->hdr.status;
} else {
cb_status = clp_rsp->clp_status;
}
} else {
result_len = clp_rsp->resp_length;
}
if (cb_status) {
goto ocs_hw_cb_dmtf_clp_done;
}
if ((result_len == 0) || (cb_arg->dma_resp->size < result_len)) {
ocs_log_test(hw->os, "Invalid response length: resp_len=%zu result len=%d\n",
cb_arg->dma_resp->size, result_len);
cb_status = -1;
goto ocs_hw_cb_dmtf_clp_done;
}
stat_len = ocs_hw_clp_resp_get_value(hw, "status", stat_str,
sizeof(stat_str),
cb_arg->dma_resp->virt,
result_len);
if (stat_len <= 0) {
ocs_log_test(hw->os, "failed to get status %d\n", stat_len);
cb_status = -1;
goto ocs_hw_cb_dmtf_clp_done;
}
if (ocs_strcmp(stat_str, "0") != 0) {
ocs_log_test(hw->os, "CLP status indicates failure=%s\n", stat_str);
cb_status = -1;
goto ocs_hw_cb_dmtf_clp_done;
}
ocs_hw_cb_dmtf_clp_done:
cb_arg->status = cb_status;
if (cb_arg->cb) {
cb_arg->cb(hw, cb_status, result_len, cb_arg->arg);
}
if (cb_arg->opts != OCS_CMD_POLL) {
ocs_free(hw->os, cb_arg, sizeof(*cb_arg));
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
}
}
static int32_t
ocs_hw_clp_resp_get_value(ocs_hw_t *hw, const char *keyword, char *value, uint32_t value_len, const char *resp, uint32_t resp_len)
{
char *start = NULL;
char *end = NULL;
start = ocs_strstr(resp, keyword);
if (start == NULL) {
ocs_log_test(hw->os, "could not find keyword=%s in CLP response\n",
keyword);
return -1;
}
start = ocs_strchr(start, '=');
if (start == NULL) {
ocs_log_test(hw->os, "could not find \'=\' in CLP response for keyword=%s\n",
keyword);
return -1;
}
start++;
end = ocs_strstr(start, "\r\n");
if (end == NULL) {
ocs_log_test(hw->os, "could not find \\r\\n for keyword=%s in CLP response\n",
keyword);
return -1;
}
if ((end - start + 1) > value_len) {
ocs_log_test(hw->os, "value len=%d not large enough for actual=%ld\n",
value_len, (end-start));
return -1;
}
ocs_strncpy(value, start, (end - start));
value[end-start] = '\0';
return (end-start+1);
}
ocs_hw_rtn_e
ocs_hw_raise_ue(ocs_hw_t *hw, uint8_t dump)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (sli_raise_ue(&hw->sli, dump) != 0) {
rc = OCS_HW_RTN_ERROR;
} else {
if (hw->state != OCS_HW_STATE_UNINITIALIZED) {
hw->state = OCS_HW_STATE_QUEUES_ALLOCATED;
}
}
return rc;
}
static int32_t
ocs_hw_cb_dump_get(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
sli4_res_common_read_object_t* rd_obj_rsp = (sli4_res_common_read_object_t*) mbox_rsp->payload.embed;
ocs_hw_dump_get_cb_arg_t *cb_arg = arg;
uint32_t bytes_read;
uint8_t eof;
bytes_read = rd_obj_rsp->actual_read_length;
eof = rd_obj_rsp->eof;
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(status, bytes_read, eof, cb_arg->arg);
}
ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
}
return 0;
}
ocs_hw_rtn_e
ocs_hw_dump_get(ocs_hw_t *hw, ocs_dma_t *dma, uint32_t size, uint32_t offset, ocs_hw_dump_get_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
uint8_t *mbxdata;
ocs_hw_dump_get_cb_arg_t *cb_arg;
uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
return OCS_HW_RTN_ERROR;
}
if (1 != sli_dump_is_present(&hw->sli)) {
ocs_log_test(hw->os, "No dump is present\n");
return OCS_HW_RTN_ERROR;
}
if (1 == sli_reset_required(&hw->sli)) {
ocs_log_test(hw->os, "device reset required\n");
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_get_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->mbox_cmd = mbxdata;
if (sli_cmd_common_read_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
size, offset, "/dbg/dump.bin", dma)) {
rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_get, cb_arg);
if (rc == 0 && opts == OCS_CMD_POLL) {
ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
rc = ocs_hw_cb_dump_get(hw, 0, mbxdata, cb_arg);
}
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "COMMON_READ_OBJECT failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_get_cb_arg_t));
}
return rc;
}
static int32_t
ocs_hw_cb_dump_clear(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_dump_clear_cb_arg_t *cb_arg = arg;
sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
if (cb_arg) {
if (cb_arg->cb) {
if ((status == 0) && mbox_rsp->hdr.status) {
status = mbox_rsp->hdr.status;
}
cb_arg->cb(status, cb_arg->arg);
}
ocs_free(hw->os, cb_arg->mbox_cmd, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
}
return 0;
}
ocs_hw_rtn_e
ocs_hw_dump_clear(ocs_hw_t *hw, ocs_hw_dump_clear_cb_t cb, void *arg)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
uint8_t *mbxdata;
ocs_hw_dump_clear_cb_arg_t *cb_arg;
uint32_t opts = (hw->state == OCS_HW_STATE_ACTIVE ? OCS_CMD_NOWAIT : OCS_CMD_POLL);
if (SLI4_IF_TYPE_LANCER_FC_ETH != sli_get_if_type(&hw->sli)) {
ocs_log_test(hw->os, "Function only supported for I/F type 2\n");
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_dump_clear_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = arg;
cb_arg->mbox_cmd = mbxdata;
if (sli_cmd_common_delete_object(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
"/dbg/dump.bin")) {
rc = ocs_hw_command(hw, mbxdata, opts, ocs_hw_cb_dump_clear, cb_arg);
if (rc == 0 && opts == OCS_CMD_POLL) {
ocs_memcpy(mbxdata, hw->sli.bmbx.virt, SLI4_BMBX_SIZE);
rc = ocs_hw_cb_dump_clear(hw, 0, mbxdata, cb_arg);
}
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "COMMON_DELETE_OBJECT failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_dump_clear_cb_arg_t));
}
return rc;
}
typedef struct ocs_hw_get_port_protocol_cb_arg_s {
ocs_get_port_protocol_cb_t cb;
void *arg;
uint32_t pci_func;
ocs_dma_t payload;
} ocs_hw_get_port_protocol_cb_arg_t;
static int32_t
ocs_hw_get_port_protocol_cb(ocs_hw_t *hw, int32_t status,
uint8_t *mqe, void *arg)
{
ocs_hw_get_port_protocol_cb_arg_t *cb_arg = arg;
ocs_dma_t *payload = &(cb_arg->payload);
sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
ocs_hw_port_protocol_e port_protocol;
int num_descriptors;
sli4_resource_descriptor_v1_t *desc_p;
sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
int i;
port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
num_descriptors = response->desc_count;
desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
for (i=0; i<num_descriptors; i++) {
if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
if (pcie_desc_p->pf_number == cb_arg->pci_func) {
switch(pcie_desc_p->pf_type) {
case 0x02:
port_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
break;
case 0x04:
port_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
break;
case 0x10:
port_protocol = OCS_HW_PORT_PROTOCOL_FC;
break;
default:
port_protocol = OCS_HW_PORT_PROTOCOL_OTHER;
break;
}
}
}
desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
}
if (cb_arg->cb) {
cb_arg->cb(status, port_protocol, cb_arg->arg);
}
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_port_protocol(ocs_hw_t *hw, uint32_t pci_func,
ocs_get_port_protocol_cb_t cb, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_get_port_protocol_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_port_protocol_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
cb_arg->pci_func = pci_func;
if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_port_protocol_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
ocs_dma_free(hw->os, &cb_arg->payload);
}
return rc;
}
typedef struct ocs_hw_set_port_protocol_cb_arg_s {
ocs_set_port_protocol_cb_t cb;
void *arg;
ocs_dma_t payload;
uint32_t new_protocol;
uint32_t pci_func;
} ocs_hw_set_port_protocol_cb_arg_t;
static int32_t
ocs_hw_set_port_protocol_cb2(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
if (cb_arg->cb) {
cb_arg->cb( status, cb_arg->arg);
}
ocs_dma_free(hw->os, &(cb_arg->payload));
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
return 0;
}
static int32_t
ocs_hw_set_port_protocol_cb1(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_set_port_protocol_cb_arg_t *cb_arg = arg;
ocs_dma_t *payload = &(cb_arg->payload);
sli4_res_common_get_profile_config_t* response = (sli4_res_common_get_profile_config_t*) payload->virt;
int num_descriptors;
sli4_resource_descriptor_v1_t *desc_p;
sli4_pcie_resource_descriptor_v1_t *pcie_desc_p;
int i;
ocs_hw_set_port_protocol_cb_arg_t *new_cb_arg;
ocs_hw_port_protocol_e new_protocol;
uint8_t *dst;
sli4_isap_resouce_descriptor_v1_t *isap_desc_p;
uint8_t *mbxdata;
int pci_descriptor_count;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
int num_fcoe_ports = 0;
int num_iscsi_ports = 0;
new_protocol = (ocs_hw_port_protocol_e)cb_arg->new_protocol;
num_descriptors = response->desc_count;
pci_descriptor_count = 0;
desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
for (i=0; i<num_descriptors; i++) {
if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
++pci_descriptor_count;
}
desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
new_cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
if (new_cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
new_cb_arg->cb = cb_arg->cb;
new_cb_arg->arg = cb_arg->arg;
if (ocs_dma_alloc(hw->os, &new_cb_arg->payload, sizeof(sli4_req_common_set_profile_config_t) +
(pci_descriptor_count * sizeof(sli4_pcie_resource_descriptor_v1_t)) +
sizeof(sli4_isap_resouce_descriptor_v1_t), 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
return OCS_HW_RTN_NO_MEMORY;
}
sli_cmd_common_set_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE,
&new_cb_arg->payload,
0, pci_descriptor_count+1, 1);
dst = (uint8_t *)&(((sli4_req_common_set_profile_config_t *) new_cb_arg->payload.virt)->desc);
desc_p = (sli4_resource_descriptor_v1_t *)response->desc;
for (i=0; i<num_descriptors; i++) {
if (desc_p->descriptor_type == SLI4_RESOURCE_DESCRIPTOR_TYPE_PCIE) {
pcie_desc_p = (sli4_pcie_resource_descriptor_v1_t*) desc_p;
if (pcie_desc_p->pf_number == cb_arg->pci_func) {
switch(new_protocol) {
case OCS_HW_PORT_PROTOCOL_FC:
pcie_desc_p->pf_type = SLI4_PROTOCOL_FC;
break;
case OCS_HW_PORT_PROTOCOL_FCOE:
pcie_desc_p->pf_type = SLI4_PROTOCOL_FCOE;
break;
case OCS_HW_PORT_PROTOCOL_ISCSI:
pcie_desc_p->pf_type = SLI4_PROTOCOL_ISCSI;
break;
default:
pcie_desc_p->pf_type = SLI4_PROTOCOL_DEFAULT;
break;
}
}
if (pcie_desc_p->pf_type == SLI4_PROTOCOL_FCOE) {
++num_fcoe_ports;
}
if (pcie_desc_p->pf_type == SLI4_PROTOCOL_ISCSI) {
++num_iscsi_ports;
}
ocs_memcpy(dst, pcie_desc_p, sizeof(sli4_pcie_resource_descriptor_v1_t));
dst += sizeof(sli4_pcie_resource_descriptor_v1_t);
}
desc_p = (sli4_resource_descriptor_v1_t *) ((uint8_t *)desc_p + desc_p->descriptor_length);
}
isap_desc_p = (sli4_isap_resouce_descriptor_v1_t*)dst;
isap_desc_p->descriptor_type = SLI4_RESOURCE_DESCRIPTOR_TYPE_ISAP;
isap_desc_p->descriptor_length = sizeof(sli4_isap_resouce_descriptor_v1_t);
if (num_iscsi_ports > 0) {
isap_desc_p->iscsi_tgt = 1;
isap_desc_p->iscsi_ini = 1;
isap_desc_p->iscsi_dif = 1;
}
if (num_fcoe_ports > 0) {
isap_desc_p->fcoe_tgt = 1;
isap_desc_p->fcoe_ini = 1;
isap_desc_p->fcoe_dif = 1;
}
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb2, new_cb_arg);
if (rc) {
ocs_log_err(hw->os, "Error posting COMMON_SET_PROFILE_CONFIG\n");
if (new_cb_arg->cb) {
new_cb_arg->cb( rc, new_cb_arg->arg);
}
ocs_dma_free(hw->os, &new_cb_arg->payload);
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, new_cb_arg, sizeof(ocs_hw_set_port_protocol_cb_arg_t));
}
return rc;
}
ocs_hw_rtn_e
ocs_hw_set_port_protocol(ocs_hw_t *hw, ocs_hw_port_protocol_e new_protocol,
uint32_t pci_func, ocs_set_port_protocol_cb_t cb, void *ul_arg)
{
uint8_t *mbxdata;
ocs_hw_set_port_protocol_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_port_protocol_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
cb_arg->new_protocol = new_protocol;
cb_arg->pci_func = pci_func;
if (ocs_dma_alloc(hw->os, &cb_arg->payload, 4096, 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_port_protocol_cb_arg_t));
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_common_get_profile_config(&hw->sli, mbxdata, SLI4_BMBX_SIZE, &cb_arg->payload)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_port_protocol_cb1, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "GET_PROFILE_CONFIG failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_fw_write_cb_arg_t));
ocs_dma_free(hw->os, &cb_arg->payload);
}
return rc;
}
typedef struct ocs_hw_get_profile_list_cb_arg_s {
ocs_get_profile_list_cb_t cb;
void *arg;
ocs_dma_t payload;
} ocs_hw_get_profile_list_cb_arg_t;
static int32_t
ocs_hw_get_profile_list_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_profile_list_t *list;
ocs_hw_get_profile_list_cb_arg_t *cb_arg = arg;
ocs_dma_t *payload = &(cb_arg->payload);
sli4_res_common_get_profile_list_t *response = (sli4_res_common_get_profile_list_t *)payload->virt;
int i;
int num_descriptors;
list = ocs_malloc(hw->os, sizeof(ocs_hw_profile_list_t), OCS_M_ZERO);
if (list == NULL) {
ocs_log_err(hw->os, "failed to malloc list\n");
return OCS_HW_RTN_NO_MEMORY;
}
list->num_descriptors = response->profile_descriptor_count;
num_descriptors = list->num_descriptors;
if (num_descriptors > OCS_HW_MAX_PROFILES) {
num_descriptors = OCS_HW_MAX_PROFILES;
}
for (i=0; i<num_descriptors; i++) {
list->descriptors[i].profile_id = response->profile_descriptor[i].profile_id;
list->descriptors[i].profile_index = response->profile_descriptor[i].profile_index;
ocs_strcpy(list->descriptors[i].profile_description, (char *)response->profile_descriptor[i].profile_description);
}
if (cb_arg->cb) {
cb_arg->cb(status, list, cb_arg->arg);
} else {
ocs_free(hw->os, list, sizeof(*list));
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_profile_list(ocs_hw_t *hw, ocs_get_profile_list_cb_t cb, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_get_profile_list_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_profile_list_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
if (ocs_dma_alloc(hw->os, &cb_arg->payload, sizeof(sli4_res_common_get_profile_list_t), 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA buffer\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_common_get_profile_list(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, &cb_arg->payload)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_profile_list_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "GET_PROFILE_LIST failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_dma_free(hw->os, &cb_arg->payload);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_profile_list_cb_arg_t));
}
return rc;
}
typedef struct ocs_hw_get_active_profile_cb_arg_s {
ocs_get_active_profile_cb_t cb;
void *arg;
} ocs_hw_get_active_profile_cb_arg_t;
static int32_t
ocs_hw_get_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_get_active_profile_cb_arg_t *cb_arg = arg;
sli4_cmd_sli_config_t* mbox_rsp = (sli4_cmd_sli_config_t*) mqe;
sli4_res_common_get_active_profile_t* response = (sli4_res_common_get_active_profile_t*) mbox_rsp->payload.embed;
uint32_t active_profile;
active_profile = response->active_profile_id;
if (cb_arg->cb) {
cb_arg->cb(status, active_profile, cb_arg->arg);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_active_profile(ocs_hw_t *hw, ocs_get_active_profile_cb_t cb, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_get_active_profile_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_active_profile_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
if (sli_cmd_common_get_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_active_profile_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "GET_ACTIVE_PROFILE failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
}
return rc;
}
typedef struct ocs_hw_get_nvparms_cb_arg_s {
ocs_get_nvparms_cb_t cb;
void *arg;
} ocs_hw_get_nvparms_cb_arg_t;
static int32_t
ocs_hw_get_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_get_nvparms_cb_arg_t *cb_arg = arg;
sli4_cmd_read_nvparms_t* mbox_rsp = (sli4_cmd_read_nvparms_t*) mqe;
if (cb_arg->cb) {
cb_arg->cb(status, mbox_rsp->wwpn, mbox_rsp->wwnn, mbox_rsp->hard_alpa,
mbox_rsp->preferred_d_id, cb_arg->arg);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
return 0;
}
ocs_hw_rtn_e
ocs_hw_get_nvparms(ocs_hw_t *hw, ocs_get_nvparms_cb_t cb, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_get_nvparms_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_get_nvparms_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
if (sli_cmd_read_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_get_nvparms_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "READ_NVPARMS failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_nvparms_cb_arg_t));
}
return rc;
}
typedef struct ocs_hw_set_nvparms_cb_arg_s {
ocs_set_nvparms_cb_t cb;
void *arg;
} ocs_hw_set_nvparms_cb_arg_t;
static int32_t
ocs_hw_set_nvparms_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_set_nvparms_cb_arg_t *cb_arg = arg;
if (cb_arg->cb) {
cb_arg->cb(status, cb_arg->arg);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
return 0;
}
ocs_hw_rtn_e
ocs_hw_set_nvparms(ocs_hw_t *hw, ocs_set_nvparms_cb_t cb, uint8_t *wwpn,
uint8_t *wwnn, uint8_t hard_alpa, uint32_t preferred_d_id, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_set_nvparms_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_nvparms_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
if (sli_cmd_write_nvparms(&hw->sli, mbxdata, SLI4_BMBX_SIZE, wwpn, wwnn, hard_alpa, preferred_d_id)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_nvparms_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "SET_NVPARMS failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_nvparms_cb_arg_t));
}
return rc;
}
uint32_t
ocs_hw_io_get_count(ocs_hw_t *hw, ocs_hw_io_count_type_e io_count_type)
{
ocs_hw_io_t *io = NULL;
uint32_t count = 0;
ocs_lock(&hw->io_lock);
switch (io_count_type) {
case OCS_HW_IO_INUSE_COUNT :
ocs_list_foreach(&hw->io_inuse, io) {
count++;
}
break;
case OCS_HW_IO_FREE_COUNT :
ocs_list_foreach(&hw->io_free, io) {
count++;
}
break;
case OCS_HW_IO_WAIT_FREE_COUNT :
ocs_list_foreach(&hw->io_wait_free, io) {
count++;
}
break;
case OCS_HW_IO_PORT_OWNED_COUNT:
ocs_list_foreach(&hw->io_port_owned, io) {
count++;
}
break;
case OCS_HW_IO_N_TOTAL_IO_COUNT :
count = hw->config.n_io;
break;
}
ocs_unlock(&hw->io_lock);
return count;
}
uint32_t
ocs_hw_get_rqes_produced_count(ocs_hw_t *hw)
{
uint32_t count = 0;
uint32_t i;
uint32_t j;
for (i = 0; i < hw->hw_rq_count; i++) {
hw_rq_t *rq = hw->hw_rq[i];
if (rq->rq_tracker != NULL) {
for (j = 0; j < rq->entry_count; j++) {
if (rq->rq_tracker[j] != NULL) {
count++;
}
}
}
}
return count;
}
typedef struct ocs_hw_set_active_profile_cb_arg_s {
ocs_set_active_profile_cb_t cb;
void *arg;
} ocs_hw_set_active_profile_cb_arg_t;
static int32_t
ocs_hw_set_active_profile_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_set_active_profile_cb_arg_t *cb_arg = arg;
if (cb_arg->cb) {
cb_arg->cb(status, cb_arg->arg);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_get_active_profile_cb_arg_t));
return 0;
}
ocs_hw_rtn_e
ocs_hw_set_active_profile(ocs_hw_t *hw, ocs_set_active_profile_cb_t cb, uint32_t profile_id, void* ul_arg)
{
uint8_t *mbxdata;
ocs_hw_set_active_profile_cb_arg_t *cb_arg;
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
if (sli_get_if_type(&hw->sli) != SLI4_IF_TYPE_BE3_SKH_PF) {
return OCS_HW_RTN_ERROR;
}
mbxdata = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mbxdata == NULL) {
ocs_log_err(hw->os, "failed to malloc mbox\n");
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg = ocs_malloc(hw->os, sizeof(ocs_hw_set_active_profile_cb_arg_t), OCS_M_NOWAIT);
if (cb_arg == NULL) {
ocs_log_err(hw->os, "failed to malloc cb_arg\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
return OCS_HW_RTN_NO_MEMORY;
}
cb_arg->cb = cb;
cb_arg->arg = ul_arg;
if (sli_cmd_common_set_active_profile(&hw->sli, mbxdata, SLI4_BMBX_SIZE, 0, profile_id)) {
rc = ocs_hw_command(hw, mbxdata, OCS_CMD_NOWAIT, ocs_hw_set_active_profile_cb, cb_arg);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "SET_ACTIVE_PROFILE failed\n");
ocs_free(hw->os, mbxdata, SLI4_BMBX_SIZE);
ocs_free(hw->os, cb_arg, sizeof(ocs_hw_set_active_profile_cb_arg_t));
}
return rc;
}
static void
ocs_hw_queue_hash_add(ocs_queue_hash_t *hash, uint16_t id, uint16_t index)
{
uint32_t hash_index = id & (OCS_HW_Q_HASH_SIZE - 1);
while(hash[hash_index].in_use) {
hash_index = (hash_index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
}
hash[hash_index].id = id;
hash[hash_index].in_use = 1;
hash[hash_index].index = index;
}
int32_t
ocs_hw_queue_hash_find(ocs_queue_hash_t *hash, uint16_t id)
{
int32_t rc = -1;
int32_t index = id & (OCS_HW_Q_HASH_SIZE - 1);
do {
if (hash[index].in_use &&
hash[index].id == id) {
rc = hash[index].index;
} else {
index = (index + 1) & (OCS_HW_Q_HASH_SIZE - 1);
}
} while(rc == -1 && hash[index].in_use);
return rc;
}
static int32_t
ocs_hw_domain_add(ocs_hw_t *hw, ocs_domain_t *domain)
{
int32_t rc = OCS_HW_RTN_ERROR;
uint16_t fcfi = UINT16_MAX;
if ((hw == NULL) || (domain == NULL)) {
ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
hw, domain);
return OCS_HW_RTN_ERROR;
}
fcfi = domain->fcf_indicator;
if (fcfi < SLI4_MAX_FCFI) {
uint16_t fcf_index = UINT16_MAX;
ocs_log_debug(hw->os, "adding domain %p @ %#x\n",
domain, fcfi);
hw->domains[fcfi] = domain;
if (hw->workaround.override_fcfi) {
if (hw->first_domain_idx < 0) {
hw->first_domain_idx = fcfi;
}
}
fcf_index = domain->fcf;
if (fcf_index < SLI4_MAX_FCF_INDEX) {
ocs_log_debug(hw->os, "adding map of FCF index %d to FCFI %d\n",
fcf_index, fcfi);
hw->fcf_index_fcfi[fcf_index] = fcfi;
rc = OCS_HW_RTN_SUCCESS;
} else {
ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
fcf_index, SLI4_MAX_FCF_INDEX);
hw->domains[fcfi] = NULL;
}
} else {
ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
fcfi, SLI4_MAX_FCFI);
}
return rc;
}
static int32_t
ocs_hw_domain_del(ocs_hw_t *hw, ocs_domain_t *domain)
{
int32_t rc = OCS_HW_RTN_ERROR;
uint16_t fcfi = UINT16_MAX;
if ((hw == NULL) || (domain == NULL)) {
ocs_log_err(NULL, "bad parameter hw=%p domain=%p\n",
hw, domain);
return OCS_HW_RTN_ERROR;
}
fcfi = domain->fcf_indicator;
if (fcfi < SLI4_MAX_FCFI) {
uint16_t fcf_index = UINT16_MAX;
ocs_log_debug(hw->os, "deleting domain %p @ %#x\n",
domain, fcfi);
if (domain != hw->domains[fcfi]) {
ocs_log_test(hw->os, "provided domain %p does not match stored domain %p\n",
domain, hw->domains[fcfi]);
return OCS_HW_RTN_ERROR;
}
hw->domains[fcfi] = NULL;
if (hw->workaround.override_fcfi) {
if (hw->first_domain_idx == fcfi) {
hw->first_domain_idx = -1;
}
}
fcf_index = domain->fcf;
if (fcf_index < SLI4_MAX_FCF_INDEX) {
if (hw->fcf_index_fcfi[fcf_index] == fcfi) {
hw->fcf_index_fcfi[fcf_index] = 0;
rc = OCS_HW_RTN_SUCCESS;
} else {
ocs_log_test(hw->os, "indexed FCFI %#x doesn't match provided %#x @ %d\n",
hw->fcf_index_fcfi[fcf_index], fcfi, fcf_index);
}
} else {
ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
fcf_index, SLI4_MAX_FCF_INDEX);
}
} else {
ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
fcfi, SLI4_MAX_FCFI);
}
return rc;
}
ocs_domain_t *
ocs_hw_domain_get(ocs_hw_t *hw, uint16_t fcfi)
{
if (hw == NULL) {
ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
return NULL;
}
if (fcfi < SLI4_MAX_FCFI) {
return hw->domains[fcfi];
} else {
ocs_log_test(hw->os, "FCFI %#x out of range (max %#x)\n",
fcfi, SLI4_MAX_FCFI);
return NULL;
}
}
static ocs_domain_t *
ocs_hw_domain_get_indexed(ocs_hw_t *hw, uint16_t fcf_index)
{
if (hw == NULL) {
ocs_log_err(NULL, "bad parameter hw=%p\n", hw);
return NULL;
}
if (fcf_index < SLI4_MAX_FCF_INDEX) {
return ocs_hw_domain_get(hw, hw->fcf_index_fcfi[fcf_index]);
} else {
ocs_log_test(hw->os, "FCF index %d out of range (max %d)\n",
fcf_index, SLI4_MAX_FCF_INDEX);
return NULL;
}
}
static void
ocs_hw_io_quarantine(ocs_hw_t *hw, hw_wq_t *wq, ocs_hw_io_t *io)
{
ocs_quarantine_info_t *q_info = &wq->quarantine_info;
uint32_t index;
ocs_hw_io_t *free_io = NULL;
if (!io->quarantine) {
return;
}
if (ocs_ref_get_unless_zero(&io->ref) == 0) {
ocs_log_debug(hw ? hw->os : NULL,
"io not active xri=0x%x tag=0x%x\n",
io->indicator, io->reqtag);
return;
}
sli_queue_lock(wq->queue);
index = q_info->quarantine_index;
free_io = q_info->quarantine_ios[index];
q_info->quarantine_ios[index] = io;
q_info->quarantine_index = (index + 1) % OCS_HW_QUARANTINE_QUEUE_DEPTH;
sli_queue_unlock(wq->queue);
if (free_io != NULL) {
ocs_ref_put(&free_io->ref);
}
}
void
ocs_hw_cq_process(ocs_hw_t *hw, hw_cq_t *cq)
{
uint8_t cqe[sizeof(sli4_mcqe_t)];
uint16_t rid = UINT16_MAX;
sli4_qentry_e ctype;
int32_t status;
uint32_t n_processed = 0;
time_t tstart;
time_t telapsed;
tstart = ocs_msectime();
while (!sli_queue_read(&hw->sli, cq->queue, cqe)) {
status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
if (status < 0) {
if (status == -2) {
continue;
}
break;
}
switch (ctype) {
case SLI_QENTRY_ASYNC:
CPUTRACE("async");
sli_cqe_async(&hw->sli, cqe);
break;
case SLI_QENTRY_MQ:
CPUTRACE("mq");
ocs_hw_mq_process(hw, status, hw->mq);
break;
case SLI_QENTRY_OPT_WRITE_CMD:
ocs_hw_rqpair_process_auto_xfr_rdy_cmd(hw, cq, cqe);
break;
case SLI_QENTRY_OPT_WRITE_DATA:
ocs_hw_rqpair_process_auto_xfr_rdy_data(hw, cq, cqe);
break;
case SLI_QENTRY_WQ:
CPUTRACE("wq");
ocs_hw_wq_process(hw, cq, cqe, status, rid);
break;
case SLI_QENTRY_WQ_RELEASE: {
uint32_t wq_id = rid;
int32_t index = ocs_hw_queue_hash_find(hw->wq_hash, wq_id);
if (unlikely(index < 0)) {
ocs_log_err(hw->os, "unknown idx=%#x rid=%#x\n",
index, rid);
break;
}
hw_wq_t *wq = hw->hw_wq[index];
hw_wq_submit_pending(wq, wq->wqec_set_count);
break;
}
case SLI_QENTRY_RQ:
CPUTRACE("rq");
ocs_hw_rqpair_process_rq(hw, cq, cqe);
break;
case SLI_QENTRY_XABT: {
CPUTRACE("xabt");
ocs_hw_xabt_process(hw, cq, cqe, rid);
break;
}
default:
ocs_log_test(hw->os, "unhandled ctype=%#x rid=%#x\n", ctype, rid);
break;
}
n_processed++;
if (n_processed == cq->queue->proc_limit) {
break;
}
if (cq->queue->n_posted >= (cq->queue->posted_limit)) {
sli_queue_arm(&hw->sli, cq->queue, FALSE);
}
}
sli_queue_arm(&hw->sli, cq->queue, TRUE);
if (n_processed > cq->queue->max_num_processed) {
cq->queue->max_num_processed = n_processed;
}
telapsed = ocs_msectime() - tstart;
if (telapsed > cq->queue->max_process_time) {
cq->queue->max_process_time = telapsed;
}
}
void
ocs_hw_wq_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, int32_t status, uint16_t rid)
{
hw_wq_callback_t *wqcb;
ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_WQ, (void *)cqe, ((sli4_fc_wcqe_t *)cqe)->status, cq->queue->id,
((cq->queue->index - 1) & (cq->queue->length - 1)));
if(rid == OCS_HW_REQUE_XRI_REGTAG) {
if(status) {
ocs_log_err(hw->os, "reque xri failed, status = %d \n", status);
}
return;
}
wqcb = ocs_hw_reqtag_get_instance(hw, rid);
if (wqcb == NULL) {
ocs_log_err(hw->os, "invalid request tag: x%x\n", rid);
return;
}
if (wqcb->callback == NULL) {
ocs_log_err(hw->os, "wqcb callback is NULL\n");
return;
}
(*wqcb->callback)(wqcb->arg, cqe, status);
}
static void
ocs_hw_wq_process_io(void *arg, uint8_t *cqe, int32_t status)
{
ocs_hw_io_t *io = arg;
ocs_hw_t *hw = io->hw;
sli4_fc_wcqe_t *wcqe = (void *)cqe;
uint32_t len = 0;
uint32_t ext = 0;
uint8_t out_of_order_axr_cmd = 0;
uint8_t out_of_order_axr_data = 0;
uint8_t lock_taken = 0;
#if defined(OCS_DISC_SPIN_DELAY)
uint32_t delay = 0;
char prop_buf[32];
#endif
if (io->quarantine && io->quarantine_first_phase) {
io->quarantine = (wcqe->qx == 1);
ocs_hw_io_quarantine(hw, io->wq, io);
}
io->quarantine_first_phase = FALSE;
if (io->sec_hio != NULL &&
io->sec_hio->quarantine) {
io->sec_hio->quarantine = (wcqe->qx == 1);
ocs_hw_io_quarantine(hw, io->wq, io->sec_hio);
}
ocs_hw_remove_io_timed_wqe(hw, io);
if (io->xbusy && wcqe->xb == 0) {
io->xbusy = FALSE;
}
switch (io->type) {
case OCS_HW_BLS_ACC:
case OCS_HW_BLS_ACC_SID:
break;
case OCS_HW_ELS_REQ:
sli_fc_els_did(&hw->sli, cqe, &ext);
len = sli_fc_response_length(&hw->sli, cqe);
break;
case OCS_HW_ELS_RSP:
case OCS_HW_ELS_RSP_SID:
case OCS_HW_FC_CT_RSP:
break;
case OCS_HW_FC_CT:
len = sli_fc_response_length(&hw->sli, cqe);
break;
case OCS_HW_IO_TARGET_WRITE:
len = sli_fc_io_length(&hw->sli, cqe);
#if defined(OCS_DISC_SPIN_DELAY)
if (ocs_get_property("disk_spin_delay", prop_buf, sizeof(prop_buf)) == 0) {
delay = ocs_strtoul(prop_buf, 0, 0);
ocs_udelay(delay);
}
#endif
break;
case OCS_HW_IO_TARGET_READ:
len = sli_fc_io_length(&hw->sli, cqe);
if (hw->workaround.retain_tsend_io_length && !len && !status) {
len = io->length;
}
break;
case OCS_HW_IO_TARGET_RSP:
if(io->is_port_owned) {
ocs_lock(&io->axr_lock);
lock_taken = 1;
if(io->axr_buf->call_axr_cmd) {
out_of_order_axr_cmd = 1;
}
if(io->axr_buf->call_axr_data) {
out_of_order_axr_data = 1;
}
}
break;
case OCS_HW_IO_INITIATOR_READ:
len = sli_fc_io_length(&hw->sli, cqe);
break;
case OCS_HW_IO_INITIATOR_WRITE:
len = sli_fc_io_length(&hw->sli, cqe);
break;
case OCS_HW_IO_INITIATOR_NODATA:
break;
case OCS_HW_IO_DNRX_REQUEUE:
break;
default:
ocs_log_test(hw->os, "XXX unhandled io type %#x for XRI 0x%x\n",
io->type, io->indicator);
break;
}
if (status) {
ext = sli_fc_ext_status(&hw->sli, cqe);
if (hw->config.i_only_aab &&
(ocs_hw_iotype_is_originator(io->type)) &&
(ocs_hw_wcqe_abort_needed(status, ext, wcqe->xb))) {
ocs_hw_rtn_e rc;
ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
io->indicator, io->reqtag);
rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
if (rc == OCS_HW_RTN_SUCCESS) {
io->status_saved = 1;
io->saved_status = status;
io->saved_ext = ext;
io->saved_len = len;
goto exit_ocs_hw_wq_process_io;
} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
io->indicator, io->reqtag);
} else {
ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
io->indicator, io->reqtag, rc);
}
}
if ( (! ocs_hw_iotype_is_originator(io->type)) && wcqe->xb) {
ocs_hw_rtn_e rc;
ocs_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", io->indicator, io->reqtag);
rc = ocs_hw_io_abort(hw, io, FALSE, NULL, NULL);
if (rc == OCS_HW_RTN_SUCCESS) {
io->status_saved = 1;
io->saved_status = status;
io->saved_ext = ext;
io->saved_len = len;
goto exit_ocs_hw_wq_process_io;
} else if (rc == OCS_HW_RTN_IO_ABORT_IN_PROGRESS) {
ocs_log_debug(hw->os, "abort in progress xri=%#x tag=%#x\n",
io->indicator, io->reqtag);
} else {
ocs_log_test(hw->os, "Failed to abort xri=%#x tag=%#x rc=%d\n",
io->indicator, io->reqtag, rc);
}
}
}
if (io->sec_hio != NULL) {
ocs_hw_io_free(hw, io->sec_hio);
io->sec_hio = NULL;
}
if (io->done != NULL) {
ocs_hw_done_t done = io->done;
void *arg = io->arg;
io->done = NULL;
if (io->status_saved) {
status = io->saved_status;
len = io->saved_len;
ext = io->saved_ext;
io->status_saved = 0;
}
ocs_hw_io_restore_sgl(hw, io);
done(io, io->rnode, len, status, ext, arg);
}
if(out_of_order_axr_cmd) {
if (hw->config.bounce) {
fc_header_t *hdr = io->axr_buf->cmd_seq->header->dma.virt;
uint32_t s_id = fc_be24toh(hdr->s_id);
uint32_t d_id = fc_be24toh(hdr->d_id);
uint32_t ox_id = ocs_be16toh(hdr->ox_id);
if (hw->callback.bounce != NULL) {
(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, io->axr_buf->cmd_seq, s_id, d_id, ox_id);
}
}else {
hw->callback.unsolicited(hw->args.unsolicited, io->axr_buf->cmd_seq);
}
if(out_of_order_axr_data) {
if (hw->config.bounce) {
fc_header_t *hdr = io->axr_buf->seq.header->dma.virt;
uint32_t s_id = fc_be24toh(hdr->s_id);
uint32_t d_id = fc_be24toh(hdr->d_id);
uint32_t ox_id = ocs_be16toh(hdr->ox_id);
if (hw->callback.bounce != NULL) {
(*hw->callback.bounce)(ocs_hw_unsol_process_bounce, &io->axr_buf->seq, s_id, d_id, ox_id);
}
}else {
hw->callback.unsolicited(hw->args.unsolicited, &io->axr_buf->seq);
}
}
}
exit_ocs_hw_wq_process_io:
if(lock_taken) {
ocs_unlock(&io->axr_lock);
}
}
static void
ocs_hw_wq_process_abort(void *arg, uint8_t *cqe, int32_t status)
{
ocs_hw_io_t *io = arg;
ocs_hw_t *hw = io->hw;
uint32_t ext = 0;
uint32_t len = 0;
hw_wq_callback_t *wqcb;
ext = sli_fc_ext_status(&hw->sli, cqe);
if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
io->done != NULL) {
ocs_hw_done_t done = io->done;
void *arg = io->arg;
io->done = NULL;
status = io->saved_status;
len = io->saved_len;
ext = io->saved_ext;
io->status_saved = 0;
done(io, io->rnode, len, status, ext, arg);
}
if (io->abort_done != NULL) {
ocs_hw_done_t done = io->abort_done;
void *arg = io->abort_arg;
io->abort_done = NULL;
done(io, io->rnode, len, status, ext, arg);
}
ocs_lock(&hw->io_abort_lock);
io->abort_in_progress = 0;
ocs_unlock(&hw->io_abort_lock);
ocs_hw_assert(io->abort_reqtag != UINT32_MAX);
wqcb = ocs_hw_reqtag_get_instance(hw, io->abort_reqtag);
ocs_hw_reqtag_free(hw, wqcb);
(void)ocs_hw_io_free(hw, io);
}
void
ocs_hw_xabt_process(ocs_hw_t *hw, hw_cq_t *cq, uint8_t *cqe, uint16_t rid)
{
ocs_hw_io_t *io = NULL;
io = ocs_hw_io_lookup(hw, rid);
ocs_queue_history_cqe(&hw->q_hist, SLI_QENTRY_XABT, (void *)cqe, 0, cq->queue->id,
((cq->queue->index - 1) & (cq->queue->length - 1)));
if (io == NULL) {
ocs_log_err(hw->os, "Error: xabt io lookup failed rid=%#x\n", rid);
return;
}
if (!io->xbusy) {
ocs_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
} else {
io->xbusy = FALSE;
}
if (io->is_port_owned) {
ocs_lock(&hw->io_lock);
ocs_ref_get(&io->ref);
ocs_unlock(&hw->io_lock);
}
if (io->done != NULL) {
ocs_hw_done_t done = io->done;
void *arg = io->arg;
int32_t status = io->saved_status;
uint32_t len = io->saved_len;
uint32_t ext = io->saved_ext;
io->done = NULL;
io->status_saved = 0;
done(io, io->rnode, len, status, ext, arg);
}
if (io->is_port_owned) {
ocs_lock(&hw->io_lock);
ocs_hw_reque_xri(hw, io);
ocs_unlock(&hw->io_lock);
ocs_hw_io_free(hw, io);
return;
}
ocs_lock(&hw->io_lock);
if ((io->state == OCS_HW_IO_STATE_INUSE) || (io->state == OCS_HW_IO_STATE_WAIT_FREE)) {
if (io->state == OCS_HW_IO_STATE_WAIT_FREE) {
io->state = OCS_HW_IO_STATE_FREE;
ocs_list_remove(&hw->io_wait_free, io);
ocs_hw_io_free_move_correct_list(hw, io);
}
}
ocs_unlock(&hw->io_lock);
}
static void
ocs_hw_adjust_wqs(ocs_hw_t *hw)
{
uint32_t max_wq_num = sli_get_max_queue(&hw->sli, SLI_QTYPE_WQ);
uint32_t max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ];
uint32_t max_cq_entries = hw->num_qentries[SLI_QTYPE_CQ];
if (max_cq_entries < max_wq_entries * 2) {
max_wq_entries = hw->num_qentries[SLI_QTYPE_WQ] = max_cq_entries / 2;
}
hw->config.n_wq = ((hw->config.n_io * 2) + (max_wq_entries - 1)) / max_wq_entries;
if (hw->config.n_wq < 4 &&
SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
hw->config.n_wq = 4;
}
if (hw->config.n_wq < 2 &&
ocs_hw_get_num_chutes(hw) > 1) {
hw->config.n_wq = 2;
}
if (hw->config.n_wq > OCS_HW_MAX_NUM_WQ) {
hw->config.n_wq = OCS_HW_MAX_NUM_WQ;
}
if (hw->config.n_wq > max_wq_num) {
hw->config.n_wq = max_wq_num;
}
hw->config.n_wq /= ocs_hw_get_num_chutes(hw);
}
static int32_t
ocs_hw_command_process(ocs_hw_t *hw, int32_t status, uint8_t *mqe, size_t size)
{
ocs_command_ctx_t *ctx = NULL;
ocs_lock(&hw->cmd_lock);
if (NULL == (ctx = ocs_list_remove_head(&hw->cmd_head))) {
ocs_log_err(hw->os, "XXX no command context?!?\n");
ocs_unlock(&hw->cmd_lock);
return -1;
}
hw->cmd_head_count--;
ocs_hw_cmd_submit_pending(hw);
ocs_unlock(&hw->cmd_lock);
if (ctx->cb) {
if (ctx->buf) {
ocs_memcpy(ctx->buf, mqe, size);
}
ctx->cb(hw, status, ctx->buf, ctx->arg);
}
ocs_memset(ctx, 0, sizeof(ocs_command_ctx_t));
ocs_free(hw->os, ctx, sizeof(ocs_command_ctx_t));
return 0;
}
static int32_t
ocs_hw_mq_process(ocs_hw_t *hw, int32_t status, sli4_queue_t *mq)
{
uint8_t mqe[SLI4_BMBX_SIZE];
if (!sli_queue_read(&hw->sli, mq, mqe)) {
ocs_hw_command_process(hw, status, mqe, mq->size);
}
return 0;
}
static ocs_hw_rtn_e
ocs_hw_read_fcf(ocs_hw_t *hw, uint32_t index)
{
uint8_t *buf = NULL;
int32_t rc = OCS_HW_RTN_ERROR;
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_fcoe_read_fcf_table(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->fcf_dmem,
index)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_cb_read_fcf, &hw->fcf_dmem);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "FCOE_READ_FCF_TABLE failed\n");
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
}
return rc;
}
static int32_t
ocs_hw_cb_read_fcf(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_dma_t *dma = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
if (status || hdr->status) {
ocs_log_test(hw->os, "bad status cqe=%#x mqe=%#x\n",
status, hdr->status);
} else if (dma->virt) {
sli4_res_fcoe_read_fcf_table_t *read_fcf = dma->virt;
if (read_fcf->fcf_entry.fc ||
(read_fcf->fcf_entry.val && !read_fcf->fcf_entry.sol)) {
if (hw->callback.domain != NULL) {
ocs_domain_record_t drec = {0};
if (read_fcf->fcf_entry.fc) {
drec.speed = hw->link.speed;
drec.fc_id = hw->link.fc_id;
drec.is_fc = TRUE;
if (SLI_LINK_TOPO_LOOP == hw->link.topology) {
drec.is_loop = TRUE;
ocs_memcpy(drec.map.loop, hw->link.loop_map,
sizeof(drec.map.loop));
} else if (SLI_LINK_TOPO_NPORT == hw->link.topology) {
drec.is_nport = TRUE;
}
} else {
drec.index = read_fcf->fcf_entry.fcf_index;
drec.priority = read_fcf->fcf_entry.fip_priority;
ocs_memcpy(drec.address, read_fcf->fcf_entry.fcf_mac_address,
sizeof(drec.address));
ocs_memcpy(drec.wwn, read_fcf->fcf_entry.fabric_name_id,
sizeof(drec.wwn));
ocs_memcpy(drec.map.vlan, read_fcf->fcf_entry.vlan_bitmap,
sizeof(drec.map.vlan));
drec.is_ethernet = TRUE;
drec.is_nport = TRUE;
}
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_FOUND,
&drec);
}
} else {
ocs_log_test(hw->os, "ignore invalid FCF entry\n");
}
if (SLI4_FCOE_FCF_TABLE_LAST != read_fcf->next_index) {
ocs_hw_read_fcf(hw, read_fcf->next_index);
}
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
static int32_t
ocs_hw_cb_link(void *ctx, void *e)
{
ocs_hw_t *hw = ctx;
sli4_link_event_t *event = e;
ocs_domain_t *d = NULL;
uint32_t i = 0;
int32_t rc = OCS_HW_RTN_ERROR;
ocs_t *ocs = hw->os;
ocs_hw_link_event_init(hw);
switch (event->status) {
case SLI_LINK_STATUS_UP:
hw->link = *event;
if (SLI_LINK_TOPO_NPORT == event->topology) {
device_printf(ocs->dev, "Link Up, NPORT, speed is %d\n", event->speed);
ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
} else if (SLI_LINK_TOPO_LOOP == event->topology) {
uint8_t *buf = NULL;
device_printf(ocs->dev, "Link Up, LOOP, speed is %d\n", event->speed);
buf = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (!buf) {
ocs_log_err(hw->os, "no buffer for command\n");
break;
}
if (sli_cmd_read_topology(&hw->sli, buf, SLI4_BMBX_SIZE, &hw->loop_map)) {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, __ocs_read_topology_cb, NULL);
}
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_test(hw->os, "READ_TOPOLOGY failed\n");
ocs_free(hw->os, buf, SLI4_BMBX_SIZE);
}
} else {
device_printf(ocs->dev, "Link Up, unsupported topology (%#x), speed is %d\n",
event->topology, event->speed);
}
break;
case SLI_LINK_STATUS_DOWN:
device_printf(ocs->dev, "Link Down\n");
hw->link.status = event->status;
for (i = 0; i < SLI4_MAX_FCFI; i++) {
d = hw->domains[i];
if (d != NULL &&
hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, d);
}
}
break;
default:
ocs_log_test(hw->os, "unhandled link status %#x\n", event->status);
break;
}
return 0;
}
static int32_t
ocs_hw_cb_fip(void *ctx, void *e)
{
ocs_hw_t *hw = ctx;
ocs_domain_t *domain = NULL;
sli4_fip_event_t *event = e;
ocs_hw_assert(event);
ocs_hw_assert(hw);
if (event->type == SLI4_FCOE_FIP_FCF_CLEAR_VLINK) {
ocs_domain_t *d = NULL;
uint32_t i = 0;
for (i = 0; i < SLI4_MAX_FCFI; i++) {
d = hw->domains[i];
if (d != NULL) {
ocs_sport_t *sport = NULL;
ocs_list_foreach(&d->sport_list, sport) {
if (sport->indicator == event->index) {
domain = d;
break;
}
}
if (domain != NULL) {
break;
}
}
}
} else {
domain = ocs_hw_domain_get_indexed(hw, event->index);
}
switch (event->type) {
case SLI4_FCOE_FIP_FCF_DISCOVERED:
ocs_hw_read_fcf(hw, event->index);
break;
case SLI4_FCOE_FIP_FCF_DEAD:
if (domain != NULL &&
hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
}
break;
case SLI4_FCOE_FIP_FCF_CLEAR_VLINK:
if (domain != NULL &&
hw->callback.domain != NULL) {
domain->req_rediscover_fcf = TRUE;
hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
}
break;
case SLI4_FCOE_FIP_FCF_MODIFIED:
if (domain != NULL &&
hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain, OCS_HW_DOMAIN_LOST, domain);
}
ocs_hw_read_fcf(hw, event->index);
break;
default:
ocs_log_test(hw->os, "unsupported event %#x\n", event->type);
}
return 0;
}
static int32_t
ocs_hw_cb_node_attach(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_remote_node_t *rnode = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_hw_remote_node_event_e evt = 0;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
hdr->status);
ocs_atomic_sub_return(&hw->rpi_ref[rnode->index].rpi_count, 1);
rnode->attached = FALSE;
ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
evt = OCS_HW_NODE_ATTACH_FAIL;
} else {
rnode->attached = TRUE;
ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 1);
evt = OCS_HW_NODE_ATTACH_OK;
}
if (hw->callback.rnode != NULL) {
hw->callback.rnode(hw->args.rnode, evt, rnode);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
static int32_t
ocs_hw_cb_node_free(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_remote_node_t *rnode = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_hw_remote_node_event_e evt = OCS_HW_NODE_FREE_FAIL;
int32_t rc = 0;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
hdr->status);
if (!rnode->attached || ((sli_get_hlm(&hw->sli) == TRUE) && !rnode->node_group) ||
(hdr->status != SLI4_MBOX_STATUS_RPI_NOT_REG)) {
rc = -1;
}
}
if (rc == 0) {
rnode->node_group = FALSE;
rnode->attached = FALSE;
if (ocs_atomic_read(&hw->rpi_ref[rnode->index].rpi_count) == 0) {
ocs_atomic_set(&hw->rpi_ref[rnode->index].rpi_attached, 0);
}
evt = OCS_HW_NODE_FREE_OK;
}
if (hw->callback.rnode != NULL) {
hw->callback.rnode(hw->args.rnode, evt, rnode);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return rc;
}
static int32_t
ocs_hw_cb_node_free_all(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_hw_remote_node_event_e evt = OCS_HW_NODE_FREE_FAIL;
int32_t rc = 0;
uint32_t i;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
hdr->status);
} else {
evt = OCS_HW_NODE_FREE_ALL_OK;
}
if (evt == OCS_HW_NODE_FREE_ALL_OK) {
for (i = 0; i < sli_get_max_rsrc(&hw->sli, SLI_RSRC_FCOE_RPI); i++) {
ocs_atomic_set(&hw->rpi_ref[i].rpi_count, 0);
}
if (sli_resource_reset(&hw->sli, SLI_RSRC_FCOE_RPI)) {
ocs_log_test(hw->os, "FCOE_RPI free all failure\n");
rc = -1;
}
}
if (hw->callback.rnode != NULL) {
hw->callback.rnode(hw->args.rnode, evt, NULL);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return rc;
}
static ocs_hw_rtn_e
ocs_hw_setup_io(ocs_hw_t *hw)
{
uint32_t i = 0;
ocs_hw_io_t *io = NULL;
uintptr_t xfer_virt = 0;
uintptr_t xfer_phys = 0;
uint32_t index;
uint8_t new_alloc = TRUE;
if (NULL == hw->io) {
hw->io = ocs_malloc(hw->os, hw->config.n_io * sizeof(ocs_hw_io_t *), OCS_M_ZERO | OCS_M_NOWAIT);
if (NULL == hw->io) {
ocs_log_err(hw->os, "IO pointer memory allocation failed, %d Ios at size %zu\n",
hw->config.n_io,
sizeof(ocs_hw_io_t *));
return OCS_HW_RTN_NO_MEMORY;
}
for (i = 0; i < hw->config.n_io; i++) {
hw->io[i] = ocs_malloc(hw->os, sizeof(ocs_hw_io_t),
OCS_M_ZERO | OCS_M_NOWAIT);
if (hw->io[i] == NULL) {
ocs_log_err(hw->os, "IO(%d) memory allocation failed\n", i);
goto error;
}
}
hw->wqe_buffs = ocs_malloc(hw->os, hw->config.n_io * hw->sli.config.wqe_size,
OCS_M_ZERO | OCS_M_NOWAIT);
if (NULL == hw->wqe_buffs) {
ocs_free(hw->os, hw->io, hw->config.n_io * sizeof(ocs_hw_io_t));
ocs_log_err(hw->os, "%s: IO WQE buff allocation failed, %d Ios at size %zu\n",
__func__, hw->config.n_io, hw->sli.config.wqe_size);
return OCS_HW_RTN_NO_MEMORY;
}
} else {
new_alloc = FALSE;
}
if (new_alloc) {
if (ocs_dma_alloc(hw->os, &hw->xfer_rdy,
sizeof(fcp_xfer_rdy_iu_t) * hw->config.n_io,
4)) {
ocs_log_err(hw->os, "XFER_RDY buffer allocation failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
}
xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
xfer_phys = hw->xfer_rdy.phys;
for (i = 0; i < hw->config.n_io; i++) {
hw_wq_callback_t *wqcb;
io = hw->io[i];
io->hw = hw;
io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.config.wqe_size];
wqcb = ocs_hw_reqtag_alloc(hw, ocs_hw_wq_process_io, io);
if (wqcb == NULL) {
ocs_log_err(hw->os, "can't allocate request tag\n");
return OCS_HW_RTN_NO_RESOURCES;
}
io->reqtag = wqcb->instance_index;
ocs_hw_init_free_io(io);
io->xbusy = 0;
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_XRI, &io->indicator, &index)) {
ocs_log_err(hw->os, "sli_resource_alloc failed @ %d\n", i);
return OCS_HW_RTN_NO_MEMORY;
}
if (new_alloc && ocs_dma_alloc(hw->os, &io->def_sgl, hw->config.n_sgl * sizeof(sli4_sge_t), 64)) {
ocs_log_err(hw->os, "ocs_dma_alloc failed @ %d\n", i);
ocs_memset(&io->def_sgl, 0, sizeof(ocs_dma_t));
return OCS_HW_RTN_NO_MEMORY;
}
io->def_sgl_count = hw->config.n_sgl;
io->sgl = &io->def_sgl;
io->sgl_count = io->def_sgl_count;
if (hw->xfer_rdy.size) {
io->xfer_rdy.virt = (void *)xfer_virt;
io->xfer_rdy.phys = xfer_phys;
io->xfer_rdy.size = sizeof(fcp_xfer_rdy_iu_t);
xfer_virt += sizeof(fcp_xfer_rdy_iu_t);
xfer_phys += sizeof(fcp_xfer_rdy_iu_t);
}
}
return OCS_HW_RTN_SUCCESS;
error:
for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
ocs_free(hw->os, hw->io[i], sizeof(ocs_hw_io_t));
hw->io[i] = NULL;
}
return OCS_HW_RTN_NO_MEMORY;
}
static ocs_hw_rtn_e
ocs_hw_init_io(ocs_hw_t *hw)
{
uint32_t i = 0, io_index = 0;
uint32_t prereg = 0;
ocs_hw_io_t *io = NULL;
uint8_t cmd[SLI4_BMBX_SIZE];
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint32_t nremaining;
uint32_t n = 0;
uint32_t sgls_per_request = 256;
ocs_dma_t **sgls = NULL;
ocs_dma_t reqbuf = { 0 };
prereg = sli_get_sgl_preregister(&hw->sli);
if (prereg) {
sgls = ocs_malloc(hw->os, sizeof(*sgls) * sgls_per_request, OCS_M_NOWAIT);
if (sgls == NULL) {
ocs_log_err(hw->os, "ocs_malloc sgls failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
rc = ocs_dma_alloc(hw->os, &reqbuf, 32 + sgls_per_request*16, OCS_MIN_DMA_ALIGNMENT);
if (rc) {
ocs_log_err(hw->os, "ocs_dma_alloc reqbuf failed\n");
ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
return OCS_HW_RTN_NO_MEMORY;
}
}
io = hw->io[io_index];
for (nremaining = hw->config.n_io; nremaining; nremaining -= n) {
if (prereg) {
for (n = 0; n < MIN(sgls_per_request, nremaining); n++) {
if (n > 0) {
if (hw->io[io_index + n]->indicator != (hw->io[io_index + n-1]->indicator+1)) {
break;
}
}
sgls[n] = hw->io[io_index + n]->sgl;
}
if (sli_cmd_fcoe_post_sgl_pages(&hw->sli, cmd, sizeof(cmd),
io->indicator, n, sgls, NULL, &reqbuf)) {
if (ocs_hw_command(hw, cmd, OCS_CMD_POLL, NULL, NULL)) {
rc = OCS_HW_RTN_ERROR;
ocs_log_err(hw->os, "SGL post failed\n");
break;
}
}
} else {
n = nremaining;
}
for (i = 0; i < n; i ++) {
io->is_port_owned = 0;
io->state = OCS_HW_IO_STATE_FREE;
ocs_list_add_tail(&hw->io_free, io);
io = hw->io[io_index+1];
io_index++;
}
}
if (prereg) {
ocs_dma_free(hw->os, &reqbuf);
ocs_free(hw->os, sgls, sizeof(*sgls) * sgls_per_request);
}
return rc;
}
static int32_t
ocs_hw_flush(ocs_hw_t *hw)
{
uint32_t i = 0;
for (i = 0; i < hw->eq_count; i++) {
ocs_hw_process(hw, i, ~0);
}
return 0;
}
static int32_t
ocs_hw_command_cancel(ocs_hw_t *hw)
{
ocs_lock(&hw->cmd_lock);
while (!ocs_list_empty(&hw->cmd_head)) {
uint8_t mqe[SLI4_BMBX_SIZE] = { 0 };
ocs_command_ctx_t *ctx = ocs_list_get_head(&hw->cmd_head);
ocs_log_test(hw->os, "hung command %08x\n",
NULL == ctx ? UINT32_MAX :
(NULL == ctx->buf ? UINT32_MAX : *((uint32_t *)ctx->buf)));
ocs_unlock(&hw->cmd_lock);
ocs_hw_command_process(hw, -1, mqe, SLI4_BMBX_SIZE);
ocs_lock(&hw->cmd_lock);
}
ocs_unlock(&hw->cmd_lock);
return 0;
}
ocs_hw_io_t *
ocs_hw_io_lookup(ocs_hw_t *hw, uint32_t xri)
{
uint32_t ioindex;
ioindex = xri - hw->sli.config.extent[SLI_RSRC_FCOE_XRI].base[0];
return hw->io[ioindex];
}
static void
ocs_hw_io_cancel_cleanup(ocs_hw_t *hw, ocs_hw_io_t *io)
{
ocs_hw_done_t done = io->done;
ocs_hw_done_t abort_done = io->abort_done;
if (ocs_list_on_list(&io->wqe_link)) {
ocs_list_remove(&hw->io_timed_wqe, io);
}
if ((io->wq != NULL) && ocs_list_on_list(&io->wq->pending_list)) {
ocs_list_remove(&io->wq->pending_list, io);
}
if (io->done) {
void *arg = io->arg;
io->done = NULL;
ocs_unlock(&hw->io_lock);
done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, arg);
ocs_lock(&hw->io_lock);
}
if (io->abort_done != NULL) {
void *abort_arg = io->abort_arg;
io->abort_done = NULL;
ocs_unlock(&hw->io_lock);
abort_done(io, io->rnode, 0, SLI4_FC_WCQE_STATUS_SHUTDOWN, 0, abort_arg);
ocs_lock(&hw->io_lock);
}
}
static int32_t
ocs_hw_io_cancel(ocs_hw_t *hw)
{
ocs_hw_io_t *io = NULL;
ocs_hw_io_t *tmp_io = NULL;
uint32_t iters = 100;
ocs_lock(&hw->io_lock);
ocs_list_foreach_safe(&hw->io_inuse, io, tmp_io) {
ocs_hw_done_t done = io->done;
ocs_hw_done_t abort_done = io->abort_done;
ocs_hw_io_cancel_cleanup(hw, io);
if (done == NULL &&
abort_done == NULL) {
ocs_hw_io_free_common(hw, io);
ocs_list_remove(&hw->io_inuse, io);
ocs_hw_io_free_move_correct_list(hw, io);
}
}
ocs_list_foreach_safe(&hw->io_port_owned, io, tmp_io) {
if (ocs_list_on_list(&io->dnrx_link)) {
ocs_list_remove(&hw->io_port_dnrx, io);
ocs_ref_put(&io->ref);
}
ocs_hw_io_cancel_cleanup(hw, io);
ocs_list_remove(&hw->io_port_owned, io);
ocs_hw_io_free_common(hw, io);
}
ocs_unlock(&hw->io_lock);
do {
ocs_udelay(10000);
iters--;
} while (!ocs_list_empty(&hw->io_inuse) && iters);
if (!ocs_list_empty(&hw->io_inuse)) {
ocs_log_test(hw->os, "io_inuse list is not empty\n");
}
return 0;
}
static int32_t
ocs_hw_io_ini_sge(ocs_hw_t *hw, ocs_hw_io_t *io, ocs_dma_t *cmnd, uint32_t cmnd_size,
ocs_dma_t *rsp)
{
sli4_sge_t *data = NULL;
if (!hw || !io) {
ocs_log_err(NULL, "bad parm hw=%p io=%p\n", hw, io);
return OCS_HW_RTN_ERROR;
}
data = io->def_sgl.virt;
data->buffer_address_high = ocs_addr32_hi(cmnd->phys);
data->buffer_address_low = ocs_addr32_lo(cmnd->phys);
data->buffer_length = cmnd_size;
data++;
data->buffer_address_high = ocs_addr32_hi(rsp->phys);
data->buffer_address_low = ocs_addr32_lo(rsp->phys);
data->buffer_length = rsp->size;
return 0;
}
static int32_t
__ocs_read_topology_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_read_topology_t *read_topo = (sli4_cmd_read_topology_t *)mqe;
if (status || read_topo->hdr.status) {
ocs_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n",
status, read_topo->hdr.status);
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return -1;
}
switch (read_topo->attention_type) {
case SLI4_READ_TOPOLOGY_LINK_UP:
hw->link.status = SLI_LINK_STATUS_UP;
break;
case SLI4_READ_TOPOLOGY_LINK_DOWN:
hw->link.status = SLI_LINK_STATUS_DOWN;
break;
case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
hw->link.status = SLI_LINK_STATUS_NO_ALPA;
break;
default:
hw->link.status = SLI_LINK_STATUS_MAX;
break;
}
switch (read_topo->topology) {
case SLI4_READ_TOPOLOGY_NPORT:
hw->link.topology = SLI_LINK_TOPO_NPORT;
break;
case SLI4_READ_TOPOLOGY_FC_AL:
hw->link.topology = SLI_LINK_TOPO_LOOP;
if (SLI_LINK_STATUS_UP == hw->link.status) {
hw->link.loop_map = hw->loop_map.virt;
}
hw->link.fc_id = read_topo->acquired_al_pa;
break;
default:
hw->link.topology = SLI_LINK_TOPO_MAX;
break;
}
hw->link.medium = SLI_LINK_MEDIUM_FC;
switch (read_topo->link_current.link_speed) {
case SLI4_READ_TOPOLOGY_SPEED_1G:
hw->link.speed = 1 * 1000;
break;
case SLI4_READ_TOPOLOGY_SPEED_2G:
hw->link.speed = 2 * 1000;
break;
case SLI4_READ_TOPOLOGY_SPEED_4G:
hw->link.speed = 4 * 1000;
break;
case SLI4_READ_TOPOLOGY_SPEED_8G:
hw->link.speed = 8 * 1000;
break;
case SLI4_READ_TOPOLOGY_SPEED_16G:
hw->link.speed = 16 * 1000;
hw->link.loop_map = NULL;
break;
case SLI4_READ_TOPOLOGY_SPEED_32G:
hw->link.speed = 32 * 1000;
hw->link.loop_map = NULL;
break;
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
ocs_hw_read_fcf(hw, SLI4_FCOE_FCF_TABLE_FIRST);
return 0;
}
static int32_t
__ocs_hw_port_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_EXIT:
break;
case OCS_EVT_HW_PORT_REQ_FREE:
case OCS_EVT_HW_PORT_REQ_ATTACH:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
default:
ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
break;
}
return 0;
}
static void *
__ocs_hw_port_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_FREE_FAIL, sport);
}
break;
default:
break;
}
return NULL;
}
static void *
__ocs_hw_port_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator)) {
ocs_log_err(hw->os, "FCOE_VPI free failure addr=%#x\n", sport->fc_id);
}
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_FREE_OK, sport);
}
break;
default:
break;
}
return NULL;
}
static void *
__ocs_hw_port_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_ATTACH_FAIL, sport);
}
if (sport->sm_free_req_pending) {
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
}
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_free_unreg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
uint8_t *cmd = NULL;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
cmd = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (!cmd) {
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (0 == sli_cmd_unreg_vpi(&hw->sli, cmd, SLI4_BMBX_SIZE, sport->indicator,
SLI4_UNREG_TYPE_PORT)) {
ocs_log_err(hw->os, "UNREG_VPI format failure\n");
ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, cmd, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
ocs_log_err(hw->os, "UNREG_VPI command failure\n");
ocs_free(hw->os, cmd, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_free_nop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (ocs_hw_async_call(hw, __ocs_hw_port_realloc_cb, sport)) {
ocs_log_err(hw->os, "ocs_hw_async_call failed\n");
}
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_port_freed, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_port_free_report_fail, data);
break;
default:
break;
}
return NULL;
}
static void *
__ocs_hw_port_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_ATTACH_OK, sport);
}
if (sport->sm_free_req_pending) {
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
}
break;
case OCS_EVT_HW_PORT_REQ_FREE:
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_attach_reg_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (0 == sli_cmd_reg_vpi(&hw->sli, data, SLI4_BMBX_SIZE, sport, FALSE)) {
ocs_log_err(hw->os, "REG_VPI format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
ocs_log_err(hw->os, "REG_VPI command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_port_attach_report_fail, data);
break;
case OCS_EVT_HW_PORT_REQ_FREE:
sport->sm_free_req_pending = 1;
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_done(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_ALLOC_OK, sport);
}
if (sport->sm_free_req_pending) {
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
}
break;
case OCS_EVT_HW_PORT_REQ_ATTACH:
ocs_sm_transition(ctx, __ocs_hw_port_attach_reg_vpi, data);
break;
case OCS_EVT_HW_PORT_ATTACH_OK:
ocs_sm_transition(ctx, __ocs_hw_port_attached, data);
break;
case OCS_EVT_HW_PORT_REQ_FREE:
if (SLI4_IF_TYPE_LANCER_FC_ETH == sli_get_if_type(&hw->sli)) {
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
} else {
ocs_sm_transition(ctx, __ocs_hw_port_free_nop, NULL);
}
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VPI, sport->indicator);
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
if (hw->callback.port != NULL) {
hw->callback.port(hw->args.port,
OCS_HW_PORT_ALLOC_FAIL, sport);
}
if (sport->sm_free_req_pending) {
ocs_sm_transition(ctx, __ocs_hw_port_free_unreg_vpi, NULL);
}
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
uint8_t *payload = NULL;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (ocs_dma_alloc(hw->os, &sport->dma, 112, 4)) {
ocs_log_err(hw->os, "Failed to allocate DMA memory\n");
ocs_sm_transition(ctx, __ocs_hw_port_done, data);
break;
}
if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
&sport->dma, sport->indicator)) {
ocs_log_err(hw->os, "READ_SPARM64 allocation failure\n");
ocs_dma_free(hw->os, &sport->dma);
ocs_sm_transition(ctx, __ocs_hw_port_done, data);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
ocs_dma_free(hw->os, &sport->dma);
ocs_sm_transition(ctx, __ocs_hw_port_done, data);
break;
}
break;
case OCS_EVT_RESPONSE:
payload = sport->dma.virt;
ocs_display_sparams(sport->display_name, "sport sparm64", 0, NULL, payload);
ocs_memcpy(&sport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
sizeof(sport->sli_wwpn));
ocs_memcpy(&sport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
sizeof(sport->sli_wwnn));
ocs_dma_free(hw->os, &sport->dma);
ocs_sm_transition(ctx, __ocs_hw_port_alloc_init_vpi, data);
break;
case OCS_EVT_ERROR:
ocs_dma_free(hw->os, &sport->dma);
ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
break;
case OCS_EVT_HW_PORT_REQ_FREE:
sport->sm_free_req_pending = 1;
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_alloc_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
break;
case OCS_EVT_HW_PORT_ALLOC_OK:
ocs_sm_transition(ctx, __ocs_hw_port_allocated, NULL);
break;
case OCS_EVT_HW_PORT_ALLOC_FAIL:
ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, NULL);
break;
case OCS_EVT_HW_PORT_REQ_FREE:
sport->sm_free_req_pending = 1;
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_port_alloc_init_vpi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_sli_port_t *sport = ctx->app;
ocs_hw_t *hw = sport->hw;
smtrace("port");
switch (evt) {
case OCS_EVT_ENTER:
if (sport->sm_free_req_pending) {
ocs_sm_transition(ctx, __ocs_hw_port_freed, NULL);
return NULL;
}
if (0 == sli_cmd_init_vpi(&hw->sli, data, SLI4_BMBX_SIZE,
sport->indicator, sport->domain->indicator)) {
ocs_log_err(hw->os, "INIT_VPI allocation failure\n");
ocs_sm_transition(ctx, __ocs_hw_port_done, data);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_port_cb, sport)) {
ocs_log_err(hw->os, "INIT_VPI command failure\n");
ocs_sm_transition(ctx, __ocs_hw_port_done, data);
break;
}
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_port_allocated, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_port_alloc_report_fail, data);
break;
case OCS_EVT_HW_PORT_REQ_FREE:
sport->sm_free_req_pending = 1;
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_port_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static int32_t
__ocs_hw_port_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_sli_port_t *sport = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_sm_event_t evt;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
sport->indicator, status, hdr->status);
evt = OCS_EVT_ERROR;
} else {
evt = OCS_EVT_RESPONSE;
}
ocs_sm_post_event(&sport->ctx, evt, mqe);
return 0;
}
static int32_t
__ocs_hw_port_realloc_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_sli_port_t *sport = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_sm_event_t evt;
uint8_t *mqecpy;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status vpi=%#x st=%x hdr=%x\n",
sport->indicator, status, hdr->status);
evt = OCS_EVT_ERROR;
} else {
evt = OCS_EVT_RESPONSE;
}
mqecpy = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (mqecpy == NULL) {
ocs_log_err(hw->os, "malloc mqecpy failed\n");
return -1;
}
ocs_memcpy(mqecpy, mqe, SLI4_BMBX_SIZE);
ocs_sm_post_event(&sport->ctx, evt, mqecpy);
return 0;
}
static int32_t
__ocs_hw_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_EXIT:
break;
default:
ocs_log_test(hw->os, "%s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
break;
}
return 0;
}
static void *
__ocs_hw_domain_alloc_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_ALLOC_FAIL,
domain);
}
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ATTACH_OK, NULL);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_ATTACH_OK,
domain);
}
break;
case OCS_EVT_HW_DOMAIN_REQ_FREE:
ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_attach_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (data != NULL) {
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
}
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI, domain->indicator);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_ATTACH_FAIL,
domain);
}
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_attach_reg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
ocs_display_sparams("", "reg vpi", 0, NULL, domain->dma.virt);
if (0 == sli_cmd_reg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain)) {
ocs_log_err(hw->os, "REG_VFI format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "REG_VFI command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_domain_attached, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_attach_report_fail, data);
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(&domain->sport->ctx, OCS_EVT_HW_PORT_ALLOC_OK, NULL);
ocs_hw_domain_add(hw, domain);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_ALLOC_OK,
domain);
}
break;
case OCS_EVT_HW_DOMAIN_REQ_ATTACH:
ocs_sm_transition(ctx, __ocs_hw_domain_attach_reg_vfi, data);
break;
case OCS_EVT_HW_DOMAIN_REQ_FREE:
if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, NULL);
} else {
ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_vfi, NULL);
}
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_alloc_read_sparm64(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (0 == sli_cmd_read_sparm64(&hw->sli, data, SLI4_BMBX_SIZE,
&domain->dma, SLI4_READ_SPARM64_VPI_DEFAULT)) {
ocs_log_err(hw->os, "READ_SPARM64 format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "READ_SPARM64 command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_EXIT:
break;
case OCS_EVT_RESPONSE:
ocs_display_sparams(domain->display_name, "domain sparm64", 0, NULL, domain->dma.virt);
ocs_sm_transition(ctx, __ocs_hw_domain_allocated, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_alloc_init_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_sli_port_t *sport = domain->sport;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (0 == sli_cmd_init_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->indicator,
domain->fcf_indicator, sport->indicator)) {
ocs_log_err(hw->os, "INIT_VFI format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "INIT_VFI command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_EXIT:
break;
case OCS_EVT_RESPONSE:
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_alloc_reg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER: {
sli4_cmd_rq_cfg_t rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
uint32_t i;
for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
rq_cfg[i].rq_id = 0xffff;
rq_cfg[i].r_ctl_mask = (uint8_t) hw->config.filter_def[i];
rq_cfg[i].r_ctl_match = (uint8_t) (hw->config.filter_def[i] >> 8);
rq_cfg[i].type_mask = (uint8_t) (hw->config.filter_def[i] >> 16);
rq_cfg[i].type_match = (uint8_t) (hw->config.filter_def[i] >> 24);
}
for (i = 0; i < hw->hw_rq_count; i++) {
if (i >= ARRAY_SIZE(rq_cfg)) {
ocs_log_warn(hw->os, "more RQs than REG_FCFI filter entries\n");
break;
}
rq_cfg[i].rq_id = hw->hw_rq[i]->hdr->id;
}
if (!data) {
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (hw->hw_mrq_count) {
if (OCS_HW_RTN_SUCCESS != ocs_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE,
domain->vlan_id, domain->fcf)) {
ocs_log_err(hw->os, "REG_FCFI_MRQ format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
} else {
if (0 == sli_cmd_reg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf,
rq_cfg, domain->vlan_id)) {
ocs_log_err(hw->os, "REG_FCFI format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "REG_FCFI command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
}
case OCS_EVT_EXIT:
break;
case OCS_EVT_RESPONSE:
if (!data) {
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
domain->fcf_indicator = ((sli4_cmd_reg_fcfi_t *)data)->fcfi;
if (SLI4_IF_TYPE_BE3_SKH_PF == sli_get_if_type(&hw->sli)) {
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_read_sparm64, data);
} else {
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_init_vfi, data);
}
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_alloc_report_fail, data);
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC) {
domain->fcf_indicator = hw->fcf_indicator;
ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_init_vfi, data);
} else {
ocs_sm_transition(&domain->sm, __ocs_hw_domain_alloc_reg_fcfi, data);
}
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_free_report_fail(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (domain != NULL) {
ocs_hw_t *hw = domain->hw;
ocs_hw_domain_del(hw, domain);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_FREE_FAIL,
domain);
}
}
if (data != NULL) {
ocs_free(domain != NULL ? domain->hw->os : NULL, data, SLI4_BMBX_SIZE);
}
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_freed(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (domain != NULL) {
ocs_hw_t *hw = domain->hw;
sli_resource_free(&hw->sli, SLI_RSRC_FCOE_VFI,
domain->indicator);
ocs_hw_domain_del(hw, domain);
if (hw->callback.domain != NULL) {
hw->callback.domain(hw->args.domain,
OCS_HW_DOMAIN_FREE_OK,
domain);
}
}
if (data != NULL) {
ocs_free(NULL, data, SLI4_BMBX_SIZE);
}
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_free_redisc_fcf(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (hw->state == OCS_HW_STATE_TEARDOWN_IN_PROGRESS) {
ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
break;
}
if (0 == sli_cmd_fcoe_rediscover_fcf(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf)) {
ocs_log_err(hw->os, "REDISCOVER_FCF format failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "REDISCOVER_FCF command failure\n");
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
}
break;
case OCS_EVT_RESPONSE:
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_free_unreg_fcfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
smtrace("domain");
switch (evt) {
case OCS_EVT_ENTER:
if (data == NULL) {
data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (!data) {
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
}
if (0 == sli_cmd_unreg_fcfi(&hw->sli, data, SLI4_BMBX_SIZE, domain->fcf_indicator)) {
ocs_log_err(hw->os, "UNREG_FCFI format failure\n");
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "UNREG_FCFI command failure\n");
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_RESPONSE:
if (domain->req_rediscover_fcf) {
domain->req_rediscover_fcf = FALSE;
ocs_sm_transition(ctx, __ocs_hw_domain_free_redisc_fcf, data);
} else {
ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
}
break;
case OCS_EVT_ERROR:
ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
break;
case OCS_EVT_EXIT:
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static void *
__ocs_hw_domain_free_unreg_vfi(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *data)
{
ocs_domain_t *domain = ctx->app;
ocs_hw_t *hw = domain->hw;
uint8_t is_fc = FALSE;
smtrace("domain");
is_fc = (sli_get_medium(&hw->sli) == SLI_LINK_MEDIUM_FC);
switch (evt) {
case OCS_EVT_ENTER:
if (data == NULL) {
data = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
if (!data) {
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
}
if (0 == sli_cmd_unreg_vfi(&hw->sli, data, SLI4_BMBX_SIZE, domain,
SLI4_UNREG_TYPE_DOMAIN)) {
ocs_log_err(hw->os, "UNREG_VFI format failure\n");
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
if (ocs_hw_command(hw, data, OCS_CMD_NOWAIT, __ocs_hw_domain_cb, domain)) {
ocs_log_err(hw->os, "UNREG_VFI command failure\n");
ocs_free(hw->os, data, SLI4_BMBX_SIZE);
ocs_sm_post_event(ctx, OCS_EVT_ERROR, NULL);
break;
}
break;
case OCS_EVT_ERROR:
if (is_fc) {
ocs_sm_transition(ctx, __ocs_hw_domain_free_report_fail, data);
} else {
ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
}
break;
case OCS_EVT_RESPONSE:
if (is_fc) {
ocs_sm_transition(ctx, __ocs_hw_domain_freed, data);
} else {
ocs_sm_transition(ctx, __ocs_hw_domain_free_unreg_fcfi, data);
}
break;
default:
__ocs_hw_domain_common(__func__, ctx, evt, data);
break;
}
return NULL;
}
static int32_t
__ocs_hw_domain_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_domain_t *domain = arg;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
ocs_sm_event_t evt;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status vfi=%#x st=%x hdr=%x\n",
domain->indicator, status, hdr->status);
evt = OCS_EVT_ERROR;
} else {
evt = OCS_EVT_RESPONSE;
}
ocs_sm_post_event(&domain->sm, evt, mqe);
return 0;
}
static int32_t
target_wqe_timer_nop_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_io_t *io = NULL;
ocs_hw_io_t *io_next = NULL;
ocs_hw_rtn_e rc;
struct timeval cur_time;
sli4_mbox_command_header_t *hdr = (sli4_mbox_command_header_t *)mqe;
if (status || hdr->status) {
ocs_log_debug(hw->os, "bad status st=%x hdr=%x\n",
status, hdr->status);
}
ocs_lock(&hw->io_lock);
ocs_list_foreach_safe(&hw->io_timed_wqe, io, io_next) {
getmicrouptime(&cur_time);
timevalsub(&cur_time, &io->submit_time);
if (cur_time.tv_sec > io->wqe_timeout) {
ocs_log_info(hw->os, "IO timeout xri=0x%x tag=0x%x type=%d elapsed time:%u\n",
io->indicator, io->reqtag, io->type, cur_time.tv_sec);
ocs_list_remove(&hw->io_timed_wqe, io);
io->status_saved = 1;
io->saved_status = SLI4_FC_WCQE_STATUS_WQE_TIMEOUT;
io->saved_ext = 0;
io->saved_len = 0;
rc = ocs_hw_io_abort(hw, io, TRUE, NULL, NULL);
if (rc) {
ocs_log_test(hw->os,
"abort failed xri=%#x tag=%#x rc=%d\n",
io->indicator, io->reqtag, rc);
}
}
}
ocs_unlock(&hw->io_lock);
if (!hw->active_wqe_timer_shutdown) {
ocs_setup_timer(hw->os, &hw->wqe_timer, target_wqe_timer_cb, hw, OCS_HW_WQ_TIMER_PERIOD_MS);
}
hw->in_active_wqe_timer = FALSE;
return 0;
}
static void
target_wqe_timer_cb(void *arg)
{
ocs_hw_t *hw = (ocs_hw_t *)arg;
hw->in_active_wqe_timer = TRUE;
if (ocs_hw_async_call(hw, target_wqe_timer_nop_cb, hw)) {
ocs_log_test(hw->os, "ocs_hw_async_call failed\n");
}
}
static void
shutdown_target_wqe_timer(ocs_hw_t *hw)
{
uint32_t iters = 100;
if (hw->config.emulate_wqe_timeout) {
hw->active_wqe_timer_shutdown = TRUE;
ocs_del_timer(&hw->wqe_timer);
while (hw->in_active_wqe_timer && iters) {
ocs_hw_flush(hw);
iters--;
}
if (iters == 0) {
ocs_log_test(hw->os, "Failed to shutdown active wqe timer\n");
}
}
}
uint8_t
ocs_hw_is_io_port_owned(ocs_hw_t *hw, ocs_hw_io_t *io)
{
return io->is_port_owned;
}
uint8_t
ocs_hw_is_xri_port_owned(ocs_hw_t *hw, uint32_t xri)
{
ocs_hw_io_t *io = ocs_hw_io_lookup(hw, xri);
return (io == NULL ? FALSE : io->is_port_owned);
}
static void
ocs_hw_reclaim_xri(ocs_hw_t *hw, uint16_t xri_base, uint16_t xri_count)
{
ocs_hw_io_t *io;
uint32_t i;
for (i = 0; i < xri_count; i++) {
io = ocs_hw_io_lookup(hw, xri_base + i);
if (hw->auto_xfer_rdy_enabled) {
ocs_hw_rqpair_auto_xfer_rdy_move_to_host(hw, io);
}
ocs_lock(&hw->io_lock);
ocs_list_remove(&hw->io_port_owned, io);
io->is_port_owned = 0;
ocs_list_add_tail(&hw->io_free, io);
ocs_unlock(&hw->io_lock);
}
}
static int32_t
ocs_hw_cb_post_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_post_xri_t *post_xri = (sli4_cmd_post_xri_t*)mqe;
if (status != 0) {
ocs_log_debug(hw->os, "Status 0x%x for XRI base 0x%x, cnt =x%x\n",
status, post_xri->xri_base, post_xri->xri_count);
ocs_hw_reclaim_xri(hw, post_xri->xri_base, post_xri->xri_count);
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
static ocs_hw_rtn_e
ocs_hw_post_xri(ocs_hw_t *hw, uint32_t xri_start, uint32_t num_to_post)
{
uint8_t *post_xri;
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
post_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (post_xri == NULL) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_post_xri(&hw->sli, post_xri, SLI4_BMBX_SIZE,
xri_start, num_to_post)) {
rc = ocs_hw_command(hw, post_xri, OCS_CMD_NOWAIT, ocs_hw_cb_post_xri, NULL);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, post_xri, SLI4_BMBX_SIZE);
ocs_log_err(hw->os, "post_xri failed\n");
}
}
return rc;
}
uint32_t
ocs_hw_xri_move_to_port_owned(ocs_hw_t *hw, uint32_t num_xri)
{
ocs_hw_io_t *io;
uint32_t i;
uint32_t num_posted = 0;
ocs_lock(&hw->io_lock);
for (i = 0; i < num_xri; i++) {
if (NULL != (io = ocs_list_remove_head(&hw->io_free))) {
ocs_hw_rtn_e rc;
if (hw->auto_xfer_rdy_enabled) {
ocs_unlock(&hw->io_lock);
rc = ocs_hw_rqpair_auto_xfer_rdy_move_to_port(hw, io);
ocs_lock(&hw->io_lock);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_list_add_head(&hw->io_free, io);
break;
}
}
ocs_lock_init(hw->os, &io->axr_lock, "HW_axr_lock[%d]", io->indicator);
io->is_port_owned = 1;
ocs_list_add_tail(&hw->io_port_owned, io);
if (ocs_hw_post_xri(hw, io->indicator, 1) != OCS_HW_RTN_SUCCESS ) {
ocs_hw_reclaim_xri(hw, io->indicator, i);
break;
}
num_posted++;
} else {
break;
}
}
ocs_unlock(&hw->io_lock);
return num_posted;
}
static int32_t
ocs_hw_cb_release_xri(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
sli4_cmd_release_xri_t *release_xri = (sli4_cmd_release_xri_t*)mqe;
uint8_t i;
if (status != 0) {
ocs_log_err(hw->os, "Status 0x%x\n", status);
} else {
for (i = 0; i < release_xri->released_xri_count; i++) {
uint16_t xri = ((i & 1) == 0 ? release_xri->xri_tbl[i/2].xri_tag0 :
release_xri->xri_tbl[i/2].xri_tag1);
ocs_hw_reclaim_xri(hw, xri, 1);
}
}
ocs_free(hw->os, mqe, SLI4_BMBX_SIZE);
return 0;
}
ocs_hw_rtn_e
ocs_hw_xri_move_to_host_owned(ocs_hw_t *hw, uint8_t num_xri)
{
uint8_t *release_xri;
ocs_hw_rtn_e rc = OCS_HW_RTN_ERROR;
release_xri = ocs_malloc(hw->os, SLI4_BMBX_SIZE, OCS_M_NOWAIT);
if (release_xri == NULL) {
ocs_log_err(hw->os, "no buffer for command\n");
return OCS_HW_RTN_NO_MEMORY;
}
if (sli_cmd_release_xri(&hw->sli, release_xri, SLI4_BMBX_SIZE, num_xri)) {
rc = ocs_hw_command(hw, release_xri, OCS_CMD_NOWAIT, ocs_hw_cb_release_xri, NULL);
if (rc != OCS_HW_RTN_SUCCESS) {
ocs_log_err(hw->os, "release_xri failed\n");
}
}
if (release_xri != NULL && rc != OCS_HW_RTN_SUCCESS) {
ocs_free(hw->os, release_xri, SLI4_BMBX_SIZE);
}
return rc;
}
static ocs_hw_rq_buffer_t *
ocs_hw_rx_buffer_alloc(ocs_hw_t *hw, uint32_t rqindex, uint32_t count, uint32_t size)
{
ocs_t *ocs = hw->os;
ocs_hw_rq_buffer_t *rq_buf = NULL;
ocs_hw_rq_buffer_t *prq;
uint32_t i;
if (count != 0) {
rq_buf = ocs_malloc(hw->os, sizeof(*rq_buf) * count, OCS_M_NOWAIT | OCS_M_ZERO);
if (rq_buf == NULL) {
ocs_log_err(hw->os, "Failure to allocate unsolicited DMA trackers\n");
return NULL;
}
for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
prq->rqindex = rqindex;
if (ocs_dma_alloc(ocs, &prq->dma, size, OCS_MIN_DMA_ALIGNMENT)) {
ocs_log_err(hw->os, "DMA allocation failed\n");
ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
rq_buf = NULL;
break;
}
}
}
return rq_buf;
}
static void
ocs_hw_rx_buffer_free(ocs_hw_t *hw, ocs_hw_rq_buffer_t *rq_buf, uint32_t count)
{
ocs_t *ocs = hw->os;
uint32_t i;
ocs_hw_rq_buffer_t *prq;
if (rq_buf != NULL) {
for (i = 0, prq = rq_buf; i < count; i++, prq++) {
ocs_dma_free(ocs, &prq->dma);
}
ocs_free(hw->os, rq_buf, sizeof(*rq_buf) * count);
}
}
ocs_hw_rtn_e
ocs_hw_rx_allocate(ocs_hw_t *hw)
{
ocs_t *ocs = hw->os;
uint32_t i;
int32_t rc = OCS_HW_RTN_SUCCESS;
uint32_t rqindex = 0;
hw_rq_t *rq;
uint32_t hdr_size = OCS_HW_RQ_SIZE_HDR;
uint32_t payload_size = hw->config.rq_default_buffer_size;
rqindex = 0;
for (i = 0; i < hw->hw_rq_count; i++) {
rq = hw->hw_rq[i];
rq->hdr_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, hdr_size);
if (rq->hdr_buf == NULL) {
ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc hdr_buf failed\n");
rc = OCS_HW_RTN_ERROR;
break;
}
ocs_log_debug(hw->os, "rq[%2d] rq_id %02d header %4d by %4d bytes\n", i, rq->hdr->id,
rq->entry_count, hdr_size);
rqindex++;
rq->payload_buf = ocs_hw_rx_buffer_alloc(hw, rqindex, rq->entry_count, payload_size);
if (rq->payload_buf == NULL) {
ocs_log_err(ocs, "ocs_hw_rx_buffer_alloc fb_buf failed\n");
rc = OCS_HW_RTN_ERROR;
break;
}
ocs_log_debug(hw->os, "rq[%2d] rq_id %02d default %4d by %4d bytes\n", i, rq->data->id,
rq->entry_count, payload_size);
rqindex++;
}
return rc ? OCS_HW_RTN_ERROR : OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_rx_post(ocs_hw_t *hw)
{
uint32_t i;
uint32_t idx;
uint32_t rq_idx;
int32_t rc = 0;
for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
hw_rq_t *rq = hw->hw_rq[rq_idx];
for (i = 0; i < rq->entry_count-1; i++) {
ocs_hw_sequence_t *seq = ocs_array_get(hw->seq_pool, idx++);
ocs_hw_assert(seq != NULL);
seq->header = &rq->hdr_buf[i];
seq->payload = &rq->payload_buf[i];
rc = ocs_hw_sequence_free(hw, seq);
if (rc) {
break;
}
}
if (rc) {
break;
}
}
return rc;
}
void
ocs_hw_rx_free(ocs_hw_t *hw)
{
hw_rq_t *rq;
uint32_t i;
for (i = 0; i < hw->hw_rq_count; i++) {
rq = hw->hw_rq[i];
if (rq != NULL) {
ocs_hw_rx_buffer_free(hw, rq->hdr_buf, rq->entry_count);
rq->hdr_buf = NULL;
ocs_hw_rx_buffer_free(hw, rq->payload_buf, rq->entry_count);
rq->payload_buf = NULL;
}
}
}
typedef struct {
ocs_hw_async_cb_t callback;
void *arg;
uint8_t cmd[SLI4_BMBX_SIZE];
} ocs_hw_async_call_ctx_t;
static void
ocs_hw_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_async_call_ctx_t *ctx = arg;
if (ctx != NULL) {
if (ctx->callback != NULL) {
(*ctx->callback)(hw, status, mqe, ctx->arg);
}
ocs_free(hw->os, ctx, sizeof(*ctx));
}
}
int32_t
ocs_hw_async_call(ocs_hw_t *hw, ocs_hw_async_cb_t callback, void *arg)
{
ocs_hw_async_call_ctx_t *ctx;
ctx = ocs_malloc(hw->os, sizeof(*ctx), OCS_M_ZERO | OCS_M_NOWAIT);
if (ctx == NULL) {
ocs_log_err(hw->os, "failed to malloc async call context\n");
return OCS_HW_RTN_NO_MEMORY;
}
ctx->callback = callback;
ctx->arg = arg;
if (sli_cmd_common_nop(&hw->sli, ctx->cmd, sizeof(ctx->cmd), 0) == 0) {
ocs_log_err(hw->os, "COMMON_NOP format failure\n");
ocs_free(hw->os, ctx, sizeof(*ctx));
return OCS_HW_RTN_ERROR;
}
if (ocs_hw_command(hw, ctx->cmd, OCS_CMD_NOWAIT, ocs_hw_async_cb, ctx)) {
ocs_log_err(hw->os, "COMMON_NOP command failure\n");
ocs_free(hw->os, ctx, sizeof(*ctx));
return OCS_HW_RTN_ERROR;
}
return OCS_HW_RTN_SUCCESS;
}
ocs_hw_rtn_e
ocs_hw_reqtag_init(ocs_hw_t *hw)
{
if (hw->wq_reqtag_pool == NULL) {
hw->wq_reqtag_pool = ocs_pool_alloc(hw->os, sizeof(hw_wq_callback_t), 65536, TRUE);
if (hw->wq_reqtag_pool == NULL) {
ocs_log_err(hw->os, "ocs_pool_alloc hw_wq_callback_t failed\n");
return OCS_HW_RTN_NO_MEMORY;
}
}
ocs_hw_reqtag_reset(hw);
return OCS_HW_RTN_SUCCESS;
}
hw_wq_callback_t *
ocs_hw_reqtag_alloc(ocs_hw_t *hw, void (*callback)(void *arg, uint8_t *cqe, int32_t status), void *arg)
{
hw_wq_callback_t *wqcb;
ocs_hw_assert(callback != NULL);
wqcb = ocs_pool_get(hw->wq_reqtag_pool);
if (wqcb != NULL) {
ocs_hw_assert(wqcb->callback == NULL);
wqcb->callback = callback;
wqcb->arg = arg;
}
return wqcb;
}
void
ocs_hw_reqtag_free(ocs_hw_t *hw, hw_wq_callback_t *wqcb)
{
ocs_hw_assert(wqcb->callback != NULL);
wqcb->callback = NULL;
wqcb->arg = NULL;
ocs_pool_put(hw->wq_reqtag_pool, wqcb);
}
hw_wq_callback_t *
ocs_hw_reqtag_get_instance(ocs_hw_t *hw, uint32_t instance_index)
{
hw_wq_callback_t *wqcb;
wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, instance_index);
if (wqcb == NULL) {
ocs_log_err(hw->os, "wqcb for instance %d is null\n", instance_index);
}
return wqcb;
}
void
ocs_hw_reqtag_reset(ocs_hw_t *hw)
{
hw_wq_callback_t *wqcb;
uint32_t i;
while(ocs_pool_get(hw->wq_reqtag_pool) != NULL) {
;
}
for (i = 0; ((wqcb = ocs_pool_get_instance(hw->wq_reqtag_pool, i)) != NULL); i++) {
wqcb->instance_index = i;
wqcb->callback = NULL;
wqcb->arg = NULL;
ocs_pool_put(hw->wq_reqtag_pool, wqcb);
}
}
void
_ocs_hw_assert(const char *cond, const char *filename, int linenum)
{
ocs_printf("%s(%d): HW assertion (%s) failed\n", filename, linenum, cond);
ocs_abort();
}
void
_ocs_hw_verify(const char *cond, const char *filename, int linenum)
{
ocs_printf("%s(%d): HW verify (%s) failed\n", filename, linenum, cond);
ocs_print_stack();
}
int32_t
ocs_hw_reque_xri( ocs_hw_t *hw, ocs_hw_io_t *io )
{
int32_t rc = 0;
rc = ocs_hw_rqpair_auto_xfer_rdy_buffer_post(hw, io, 1);
if (rc) {
ocs_list_add_tail(&hw->io_port_dnrx, io);
rc = -1;
goto exit_ocs_hw_reque_xri;
}
io->auto_xfer_rdy_dnrx = 0;
io->type = OCS_HW_IO_DNRX_REQUEUE;
if (sli_requeue_xri_wqe(&hw->sli, io->wqe.wqebuf, hw->sli.config.wqe_size, io->indicator, OCS_HW_REQUE_XRI_REGTAG, SLI4_CQ_DEFAULT)) {
ocs_pool_put(hw->auto_xfer_rdy_buf_pool, io->axr_buf);
io->axr_buf = NULL;
ocs_log_err(hw->os, "requeue_xri WQE error\n");
ocs_list_add_tail(&hw->io_port_dnrx, io);
rc = -1;
goto exit_ocs_hw_reque_xri;
}
if (io->wq == NULL) {
io->wq = ocs_hw_queue_next_wq(hw, io);
ocs_hw_assert(io->wq != NULL);
}
OCS_STAT(hw->tcmd_wq_submit[io->wq->instance]++);
OCS_STAT(io->wq->use_count++);
rc = hw_wq_write(io->wq, &io->wqe);
if (rc < 0) {
ocs_log_err(hw->os, "sli_queue_write reque xri failed: %d\n", rc);
rc = -1;
}
exit_ocs_hw_reque_xri:
return 0;
}
uint32_t
ocs_hw_get_def_wwn(ocs_t *ocs, uint32_t chan, uint64_t *wwpn, uint64_t *wwnn)
{
sli4_t *sli4 = &ocs->hw.sli;
ocs_dma_t dma;
uint8_t *payload = NULL;
int indicator = sli4->config.extent[SLI_RSRC_FCOE_VPI].base[0] + chan;
if (ocs_dma_alloc(ocs, &dma, 112, 4)) {
ocs_log_err(ocs, "Failed to allocate DMA memory\n");
return 1;
}
if (0 == sli_cmd_read_sparm64(sli4, sli4->bmbx.virt, SLI4_BMBX_SIZE,
&dma, indicator)) {
ocs_log_err(ocs, "READ_SPARM64 allocation failure\n");
ocs_dma_free(ocs, &dma);
return 1;
}
if (sli_bmbx_command(sli4)) {
ocs_log_err(ocs, "READ_SPARM64 command failure\n");
ocs_dma_free(ocs, &dma);
return 1;
}
payload = dma.virt;
ocs_memcpy(wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, sizeof(*wwpn));
ocs_memcpy(wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, sizeof(*wwnn));
ocs_dma_free(ocs, &dma);
return 0;
}
uint32_t
ocs_hw_get_config_persistent_topology(ocs_hw_t *hw)
{
uint32_t topology = OCS_HW_TOPOLOGY_AUTO;
sli4_t *sli = &hw->sli;
if (!sli_persist_topology_enabled(sli))
return topology;
switch (sli->config.pt) {
case SLI4_INIT_LINK_F_P2P_ONLY:
topology = OCS_HW_TOPOLOGY_NPORT;
break;
case SLI4_INIT_LINK_F_FCAL_ONLY:
topology = OCS_HW_TOPOLOGY_LOOP;
break;
default:
break;
}
return topology;
}
typedef struct ocs_hw_persistent_topo_cb_arg {
ocs_sem_t semaphore;
int32_t status;
} ocs_hw_persistent_topo_cb_arg_t;
static int32_t
ocs_hw_set_persistent_topolgy_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
{
ocs_hw_persistent_topo_cb_arg_t *req = (ocs_hw_persistent_topo_cb_arg_t *)arg;
req->status = status;
ocs_sem_v(&req->semaphore);
return 0;
}
ocs_hw_rtn_e
ocs_hw_set_persistent_topology(ocs_hw_t *hw, uint32_t topology, uint32_t opts)
{
ocs_hw_rtn_e rc = OCS_HW_RTN_SUCCESS;
uint8_t buf[SLI4_BMBX_SIZE];
sli4_req_common_set_features_persistent_topo_param_t param;
ocs_hw_persistent_topo_cb_arg_t request;
ocs_memset(¶m, 0, sizeof(param));
param.persistent_topo = topology;
switch (topology) {
case OCS_HW_TOPOLOGY_AUTO:
if (sli_get_asic_type(&hw->sli) == SLI4_ASIC_TYPE_LANCER) {
param.persistent_topo = SLI4_INIT_LINK_F_P2P_FAIL_OVER;
param.topo_failover = 1;
} else {
param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;
param.topo_failover = 0;
}
break;
case OCS_HW_TOPOLOGY_NPORT:
param.persistent_topo = SLI4_INIT_LINK_F_P2P_ONLY;
param.topo_failover = 0;
break;
case OCS_HW_TOPOLOGY_LOOP:
param.persistent_topo = SLI4_INIT_LINK_F_FCAL_ONLY;
param.topo_failover = 0;
break;
default:
ocs_log_err(hw->os, "unsupported topology %#x\n", topology);
return -1;
}
ocs_sem_init(&request.semaphore, 0, "set_persistent_topo");
sli_cmd_common_set_features(&hw->sli, buf, SLI4_BMBX_SIZE,
SLI4_SET_FEATURES_PERSISTENT_TOPOLOGY, sizeof(param), ¶m);
if (opts == OCS_CMD_POLL) {
rc = ocs_hw_command(hw, buf, OCS_CMD_POLL, NULL, NULL);
if (rc) {
ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
return rc;
}
} else {
rc = ocs_hw_command(hw, buf, OCS_CMD_NOWAIT, ocs_hw_set_persistent_topolgy_cb, &request);
if (rc) {
ocs_log_err(hw->os, "Failed to set persistent topology, rc: %#x\n", rc);
return rc;
}
if (ocs_sem_p(&request.semaphore, OCS_SEM_FOREVER)) {
ocs_log_err(hw->os, "ocs_sem_p failed\n");
return -ENXIO;
}
if (request.status) {
ocs_log_err(hw->os, "set persistent topology failed; status: %d\n", request.status);
return -EFAULT;
}
}
sli_config_persistent_topology(&hw->sli, ¶m);
return rc;
}
#define HW_FWREV_ZERO (0ull)
#define HW_FWREV_MAX (~0ull)
#define SLI4_ASIC_TYPE_ANY 0
#define SLI4_ASIC_REV_ANY 0
typedef enum {
HW_WORKAROUND_TEST = 1,
HW_WORKAROUND_MAX_QUEUE,
HW_WORKAROUND_MAX_RQ,
HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH,
HW_WORKAROUND_WQE_COUNT_METHOD,
HW_WORKAROUND_RQE_COUNT_METHOD,
HW_WORKAROUND_USE_UNREGISTERD_RPI,
HW_WORKAROUND_DISABLE_AR_TGT_DIF,
HW_WORKAROUND_DISABLE_SET_DUMP_LOC,
HW_WORKAROUND_USE_DIF_QUARANTINE,
HW_WORKAROUND_USE_DIF_SEC_XRI,
HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB,
HW_WORKAROUND_FW_VERSION_TOO_LOW,
HW_WORKAROUND_SGLC_MISREPORTED,
HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE,
} hw_workaround_e;
typedef struct {
sli4_asic_type_e asic_type;
sli4_asic_rev_e asic_rev;
uint64_t fwrev_low;
uint64_t fwrev_high;
hw_workaround_e workaround;
uint32_t value;
} hw_workaround_t;
static hw_workaround_t hw_workarounds[] = {
{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_TEST, 999},
{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH, 0},
{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_A0, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_MAX_QUEUE, 2048},
{SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,6,293,0),
HW_WORKAROUND_MAX_RQ, 2048},
{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(10,0,594,0),
HW_WORKAROUND_MAX_RQ, 2048},
{SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
HW_WORKAROUND_WQE_COUNT_METHOD, 1},
{SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(4,2,314,0),
HW_WORKAROUND_RQE_COUNT_METHOD, 1},
{SLI4_ASIC_TYPE_BE3, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_USE_UNREGISTERD_RPI, 0},
{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_DISABLE_AR_TGT_DIF, 0},
{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV(1,1,65,0),
HW_WORKAROUND_DISABLE_SET_DUMP_LOC, 0},
{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_USE_DIF_QUARANTINE, 0},
{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_USE_DIF_SEC_XRI, 0},
{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB, 0},
#if 0
{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_LANCER),
HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
#endif
{SLI4_ASIC_TYPE_SKYHAWK, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_1(OCS_MIN_FW_VER_SKYHAWK),
HW_WORKAROUND_FW_VERSION_TOO_LOW, 0},
{SLI4_ASIC_TYPE_LANCER, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_SGLC_MISREPORTED, 0},
{SLI4_ASIC_TYPE_ANY, SLI4_ASIC_REV_ANY, HW_FWREV_ZERO, HW_FWREV_MAX,
HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE, 0},
};
static int32_t ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w);
static uint64_t
parse_fw_version(const char *fwrev_string)
{
int v[4] = {0};
const char *p;
int i;
for (p = fwrev_string, i = 0; *p && (i < 4); i ++) {
v[i] = ocs_strtoul(p, 0, 0);
while(*p && *p != '.') {
p ++;
}
if (*p) {
p ++;
}
}
if (v[2] == 9999) {
return HW_FWREV_MAX;
} else {
return HW_FWREV(v[0], v[1], v[2], v[3]);
}
}
static int32_t
ocs_hw_workaround_match(ocs_hw_t *hw, hw_workaround_t *w)
{
return (((w->asic_type == SLI4_ASIC_TYPE_ANY) || (w->asic_type == hw->sli.asic_type)) &&
((w->asic_rev == SLI4_ASIC_REV_ANY) || (w->asic_rev == hw->sli.asic_rev)) &&
(w->fwrev_low <= hw->workaround.fwrev) &&
((w->fwrev_high == HW_FWREV_MAX) || (hw->workaround.fwrev < w->fwrev_high)));
}
void
ocs_hw_workaround_setup(struct ocs_hw_s *hw)
{
hw_workaround_t *w;
sli4_t *sli4 = &hw->sli;
uint32_t i;
ocs_memset(&hw->workaround, 0, sizeof(hw->workaround));
if (hw->hw_war_version) {
hw->workaround.fwrev = parse_fw_version(hw->hw_war_version);
} else {
hw->workaround.fwrev = parse_fw_version((char*) sli4->config.fw_name[0]);
}
for (i = 0, w = hw_workarounds; i < ARRAY_SIZE(hw_workarounds); i++, w++) {
if (ocs_hw_workaround_match(hw, w)) {
switch(w->workaround) {
case HW_WORKAROUND_TEST: {
ocs_log_debug(hw->os, "Override: test: %d\n", w->value);
break;
}
case HW_WORKAROUND_RETAIN_TSEND_IO_LENGTH: {
ocs_log_debug(hw->os, "HW Workaround: retain TSEND IO length\n");
hw->workaround.retain_tsend_io_length = 1;
break;
}
case HW_WORKAROUND_MAX_QUEUE: {
sli4_qtype_e q;
ocs_log_debug(hw->os, "HW Workaround: override max_qentries: %d\n", w->value);
for (q = SLI_QTYPE_EQ; q < SLI_QTYPE_MAX; q++) {
if (hw->num_qentries[q] > w->value) {
hw->num_qentries[q] = w->value;
}
}
break;
}
case HW_WORKAROUND_MAX_RQ: {
ocs_log_debug(hw->os, "HW Workaround: override RQ max_qentries: %d\n", w->value);
if (hw->num_qentries[SLI_QTYPE_RQ] > w->value) {
hw->num_qentries[SLI_QTYPE_RQ] = w->value;
}
break;
}
case HW_WORKAROUND_WQE_COUNT_METHOD: {
ocs_log_debug(hw->os, "HW Workaround: set WQE count method=%d\n", w->value);
sli4->config.count_method[SLI_QTYPE_WQ] = w->value;
sli_calc_max_qentries(sli4);
break;
}
case HW_WORKAROUND_RQE_COUNT_METHOD: {
ocs_log_debug(hw->os, "HW Workaround: set RQE count method=%d\n", w->value);
sli4->config.count_method[SLI_QTYPE_RQ] = w->value;
sli_calc_max_qentries(sli4);
break;
}
case HW_WORKAROUND_USE_UNREGISTERD_RPI:
ocs_log_debug(hw->os, "HW Workaround: use unreg'd RPI if rnode->indicator == 0xFFFF\n");
hw->workaround.use_unregistered_rpi = TRUE;
if (sli_resource_alloc(&hw->sli, SLI_RSRC_FCOE_RPI, &hw->workaround.unregistered_rid,
&hw->workaround.unregistered_index)) {
ocs_log_err(hw->os, "sli_resource_alloc unregistered RPI failed\n");
hw->workaround.use_unregistered_rpi = FALSE;
}
break;
case HW_WORKAROUND_DISABLE_AR_TGT_DIF:
ocs_log_debug(hw->os, "HW Workaround: disable AR on T10-PI TSEND\n");
hw->workaround.disable_ar_tgt_dif = TRUE;
break;
case HW_WORKAROUND_DISABLE_SET_DUMP_LOC:
ocs_log_debug(hw->os, "HW Workaround: disable set_dump_loc\n");
hw->workaround.disable_dump_loc = TRUE;
break;
case HW_WORKAROUND_USE_DIF_QUARANTINE:
ocs_log_debug(hw->os, "HW Workaround: use DIF quarantine\n");
hw->workaround.use_dif_quarantine = TRUE;
break;
case HW_WORKAROUND_USE_DIF_SEC_XRI:
ocs_log_debug(hw->os, "HW Workaround: use DIF secondary xri\n");
hw->workaround.use_dif_sec_xri = TRUE;
break;
case HW_WORKAROUND_OVERRIDE_FCFI_IN_SRB:
ocs_log_debug(hw->os, "HW Workaround: override FCFI in SRB\n");
hw->workaround.override_fcfi = TRUE;
break;
case HW_WORKAROUND_FW_VERSION_TOO_LOW:
ocs_log_debug(hw->os, "HW Workaround: fw version is below the minimum for this driver\n");
hw->workaround.fw_version_too_low = TRUE;
break;
case HW_WORKAROUND_SGLC_MISREPORTED:
ocs_log_debug(hw->os, "HW Workaround: SGLC misreported - chaining is enabled\n");
hw->workaround.sglc_misreported = TRUE;
break;
case HW_WORKAROUND_IGNORE_SEND_FRAME_CAPABLE:
ocs_log_debug(hw->os, "HW Workaround: not SEND_FRAME capable - disabled\n");
hw->workaround.ignore_send_frame = TRUE;
break;
}
}
}
}