162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Broadcom. All Rights Reserved. The term 462306a36Sopenharmony_ci * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "efct_driver.h" 862306a36Sopenharmony_ci#include "efct_hw.h" 962306a36Sopenharmony_ci#include "efct_unsol.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistruct efct_hw_link_stat_cb_arg { 1262306a36Sopenharmony_ci void (*cb)(int status, u32 num_counters, 1362306a36Sopenharmony_ci struct efct_hw_link_stat_counts *counters, void *arg); 1462306a36Sopenharmony_ci void *arg; 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct efct_hw_host_stat_cb_arg { 1862306a36Sopenharmony_ci void (*cb)(int status, u32 num_counters, 1962306a36Sopenharmony_ci struct efct_hw_host_stat_counts *counters, void *arg); 2062306a36Sopenharmony_ci void *arg; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct efct_hw_fw_wr_cb_arg { 2462306a36Sopenharmony_ci void (*cb)(int status, u32 bytes_written, u32 change_status, void *arg); 2562306a36Sopenharmony_ci void *arg; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct efct_mbox_rqst_ctx { 2962306a36Sopenharmony_ci int (*callback)(struct efc *efc, int status, u8 *mqe, void *arg); 3062306a36Sopenharmony_ci void *arg; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int 3462306a36Sopenharmony_ciefct_hw_link_event_init(struct efct_hw *hw) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci hw->link.status = SLI4_LINK_STATUS_MAX; 3762306a36Sopenharmony_ci hw->link.topology = SLI4_LINK_TOPO_NONE; 3862306a36Sopenharmony_ci hw->link.medium = SLI4_LINK_MEDIUM_MAX; 3962306a36Sopenharmony_ci hw->link.speed = 0; 4062306a36Sopenharmony_ci hw->link.loop_map = NULL; 4162306a36Sopenharmony_ci hw->link.fc_id = U32_MAX; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int 4762306a36Sopenharmony_ciefct_hw_read_max_dump_size(struct efct_hw *hw) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE]; 5062306a36Sopenharmony_ci struct efct *efct = hw->os; 5162306a36Sopenharmony_ci int rc = 0; 5262306a36Sopenharmony_ci struct sli4_rsp_cmn_set_dump_location *rsp; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* attempt to detemine the dump size for function 0 only. */ 5562306a36Sopenharmony_ci if (PCI_FUNC(efct->pci->devfn) != 0) 5662306a36Sopenharmony_ci return rc; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (sli_cmd_common_set_dump_location(&hw->sli, buf, 1, 0, NULL, 0)) 5962306a36Sopenharmony_ci return -EIO; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rsp = (struct sli4_rsp_cmn_set_dump_location *) 6262306a36Sopenharmony_ci (buf + offsetof(struct sli4_cmd_sli_config, payload.embed)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); 6562306a36Sopenharmony_ci if (rc != 0) { 6662306a36Sopenharmony_ci efc_log_debug(hw->os, "set dump location cmd failed\n"); 6762306a36Sopenharmony_ci return rc; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci hw->dump_size = 7162306a36Sopenharmony_ci le32_to_cpu(rsp->buffer_length_dword) & SLI4_CMN_SET_DUMP_BUFFER_LEN; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci efc_log_debug(hw->os, "Dump size %x\n", hw->dump_size); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return rc; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int 7962306a36Sopenharmony_ci__efct_read_topology_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct sli4_cmd_read_topology *read_topo = 8262306a36Sopenharmony_ci (struct sli4_cmd_read_topology *)mqe; 8362306a36Sopenharmony_ci u8 speed; 8462306a36Sopenharmony_ci struct efc_domain_record drec = {0}; 8562306a36Sopenharmony_ci struct efct *efct = hw->os; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (status || le16_to_cpu(read_topo->hdr.status)) { 8862306a36Sopenharmony_ci efc_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status, 8962306a36Sopenharmony_ci le16_to_cpu(read_topo->hdr.status)); 9062306a36Sopenharmony_ci return -EIO; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci switch (le32_to_cpu(read_topo->dw2_attentype) & 9462306a36Sopenharmony_ci SLI4_READTOPO_ATTEN_TYPE) { 9562306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_LINK_UP: 9662306a36Sopenharmony_ci hw->link.status = SLI4_LINK_STATUS_UP; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_LINK_DOWN: 9962306a36Sopenharmony_ci hw->link.status = SLI4_LINK_STATUS_DOWN; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_LINK_NO_ALPA: 10262306a36Sopenharmony_ci hw->link.status = SLI4_LINK_STATUS_NO_ALPA; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci hw->link.status = SLI4_LINK_STATUS_MAX; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci switch (read_topo->topology) { 11062306a36Sopenharmony_ci case SLI4_READ_TOPO_NON_FC_AL: 11162306a36Sopenharmony_ci hw->link.topology = SLI4_LINK_TOPO_NON_FC_AL; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case SLI4_READ_TOPO_FC_AL: 11462306a36Sopenharmony_ci hw->link.topology = SLI4_LINK_TOPO_FC_AL; 11562306a36Sopenharmony_ci if (hw->link.status == SLI4_LINK_STATUS_UP) 11662306a36Sopenharmony_ci hw->link.loop_map = hw->loop_map.virt; 11762306a36Sopenharmony_ci hw->link.fc_id = read_topo->acquired_al_pa; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci default: 12062306a36Sopenharmony_ci hw->link.topology = SLI4_LINK_TOPO_MAX; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci hw->link.medium = SLI4_LINK_MEDIUM_FC; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci speed = (le32_to_cpu(read_topo->currlink_state) & 12762306a36Sopenharmony_ci SLI4_READTOPO_LINKSTATE_SPEED) >> 8; 12862306a36Sopenharmony_ci switch (speed) { 12962306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_1G: 13062306a36Sopenharmony_ci hw->link.speed = 1 * 1000; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_2G: 13362306a36Sopenharmony_ci hw->link.speed = 2 * 1000; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_4G: 13662306a36Sopenharmony_ci hw->link.speed = 4 * 1000; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_8G: 13962306a36Sopenharmony_ci hw->link.speed = 8 * 1000; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_16G: 14262306a36Sopenharmony_ci hw->link.speed = 16 * 1000; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_32G: 14562306a36Sopenharmony_ci hw->link.speed = 32 * 1000; 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_64G: 14862306a36Sopenharmony_ci hw->link.speed = 64 * 1000; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case SLI4_READ_TOPOLOGY_SPEED_128G: 15162306a36Sopenharmony_ci hw->link.speed = 128 * 1000; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci drec.speed = hw->link.speed; 15662306a36Sopenharmony_ci drec.fc_id = hw->link.fc_id; 15762306a36Sopenharmony_ci drec.is_nport = true; 15862306a36Sopenharmony_ci efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, &drec); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int 16462306a36Sopenharmony_ciefct_hw_cb_link(void *ctx, void *e) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct efct_hw *hw = ctx; 16762306a36Sopenharmony_ci struct sli4_link_event *event = e; 16862306a36Sopenharmony_ci struct efc_domain *d = NULL; 16962306a36Sopenharmony_ci int rc = 0; 17062306a36Sopenharmony_ci struct efct *efct = hw->os; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci efct_hw_link_event_init(hw); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci switch (event->status) { 17562306a36Sopenharmony_ci case SLI4_LINK_STATUS_UP: 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hw->link = *event; 17862306a36Sopenharmony_ci efct->efcport->link_status = EFC_LINK_STATUS_UP; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (event->topology == SLI4_LINK_TOPO_NON_FC_AL) { 18162306a36Sopenharmony_ci struct efc_domain_record drec = {0}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci efc_log_info(hw->os, "Link Up, NPORT, speed is %d\n", 18462306a36Sopenharmony_ci event->speed); 18562306a36Sopenharmony_ci drec.speed = event->speed; 18662306a36Sopenharmony_ci drec.fc_id = event->fc_id; 18762306a36Sopenharmony_ci drec.is_nport = true; 18862306a36Sopenharmony_ci efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, 18962306a36Sopenharmony_ci &drec); 19062306a36Sopenharmony_ci } else if (event->topology == SLI4_LINK_TOPO_FC_AL) { 19162306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE]; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci efc_log_info(hw->os, "Link Up, LOOP, speed is %d\n", 19462306a36Sopenharmony_ci event->speed); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!sli_cmd_read_topology(&hw->sli, buf, 19762306a36Sopenharmony_ci &hw->loop_map)) { 19862306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_NOWAIT, 19962306a36Sopenharmony_ci __efct_read_topology_cb, NULL); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (rc) 20362306a36Sopenharmony_ci efc_log_debug(hw->os, "READ_TOPOLOGY failed\n"); 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci efc_log_info(hw->os, "%s(%#x), speed is %d\n", 20662306a36Sopenharmony_ci "Link Up, unsupported topology ", 20762306a36Sopenharmony_ci event->topology, event->speed); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci case SLI4_LINK_STATUS_DOWN: 21162306a36Sopenharmony_ci efc_log_info(hw->os, "Link down\n"); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci hw->link.status = event->status; 21462306a36Sopenharmony_ci efct->efcport->link_status = EFC_LINK_STATUS_DOWN; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci d = efct->efcport->domain; 21762306a36Sopenharmony_ci if (d) 21862306a36Sopenharmony_ci efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST, d); 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci default: 22162306a36Sopenharmony_ci efc_log_debug(hw->os, "unhandled link status %#x\n", 22262306a36Sopenharmony_ci event->status); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint 23062306a36Sopenharmony_ciefct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u32 i, max_sgl, cpus; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (hw->hw_setup_called) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * efct_hw_init() relies on NULL pointers indicating that a structure 23962306a36Sopenharmony_ci * needs allocation. If a structure is non-NULL, efct_hw_init() won't 24062306a36Sopenharmony_ci * free/realloc that memory 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci memset(hw, 0, sizeof(struct efct_hw)); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci hw->hw_setup_called = true; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci hw->os = os; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci mutex_init(&hw->bmbx_lock); 24962306a36Sopenharmony_ci spin_lock_init(&hw->cmd_lock); 25062306a36Sopenharmony_ci INIT_LIST_HEAD(&hw->cmd_head); 25162306a36Sopenharmony_ci INIT_LIST_HEAD(&hw->cmd_pending); 25262306a36Sopenharmony_ci hw->cmd_head_count = 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Create mailbox command ctx pool */ 25562306a36Sopenharmony_ci hw->cmd_ctx_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ, 25662306a36Sopenharmony_ci sizeof(struct efct_command_ctx)); 25762306a36Sopenharmony_ci if (!hw->cmd_ctx_pool) { 25862306a36Sopenharmony_ci efc_log_err(hw->os, "failed to allocate mailbox buffer pool\n"); 25962306a36Sopenharmony_ci return -EIO; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Create mailbox request ctx pool for library callback */ 26362306a36Sopenharmony_ci hw->mbox_rqst_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ, 26462306a36Sopenharmony_ci sizeof(struct efct_mbox_rqst_ctx)); 26562306a36Sopenharmony_ci if (!hw->mbox_rqst_pool) { 26662306a36Sopenharmony_ci efc_log_err(hw->os, "failed to allocate mbox request pool\n"); 26762306a36Sopenharmony_ci return -EIO; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci spin_lock_init(&hw->io_lock); 27162306a36Sopenharmony_ci INIT_LIST_HEAD(&hw->io_inuse); 27262306a36Sopenharmony_ci INIT_LIST_HEAD(&hw->io_free); 27362306a36Sopenharmony_ci INIT_LIST_HEAD(&hw->io_wait_free); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci atomic_set(&hw->io_alloc_failed_count, 0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci hw->config.speed = SLI4_LINK_SPEED_AUTO_16_8_4; 27862306a36Sopenharmony_ci if (sli_setup(&hw->sli, hw->os, pdev, ((struct efct *)os)->reg)) { 27962306a36Sopenharmony_ci efc_log_err(hw->os, "SLI setup failed\n"); 28062306a36Sopenharmony_ci return -EIO; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci efct_hw_link_event_init(hw); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci sli_callback(&hw->sli, SLI4_CB_LINK, efct_hw_cb_link, hw); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Set all the queue sizes to the maximum allowed. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) 29162306a36Sopenharmony_ci hw->num_qentries[i] = hw->sli.qinfo.max_qentries[i]; 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * Adjust the size of the WQs so that the CQ is twice as big as 29462306a36Sopenharmony_ci * the WQ to allow for 2 completions per IO. This allows us to 29562306a36Sopenharmony_ci * handle multi-phase as well as aborts. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci hw->num_qentries[SLI4_QTYPE_WQ] = hw->num_qentries[SLI4_QTYPE_CQ] / 2; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * The RQ assignment for RQ pair mode. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci hw->config.rq_default_buffer_size = EFCT_HW_RQ_SIZE_PAYLOAD; 30462306a36Sopenharmony_ci hw->config.n_io = hw->sli.ext[SLI4_RSRC_XRI].size; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci cpus = num_possible_cpus(); 30762306a36Sopenharmony_ci hw->config.n_eq = cpus > EFCT_HW_MAX_NUM_EQ ? EFCT_HW_MAX_NUM_EQ : cpus; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci max_sgl = sli_get_max_sgl(&hw->sli) - SLI4_SGE_MAX_RESERVED; 31062306a36Sopenharmony_ci max_sgl = (max_sgl > EFCT_FC_MAX_SGL) ? EFCT_FC_MAX_SGL : max_sgl; 31162306a36Sopenharmony_ci hw->config.n_sgl = max_sgl; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci (void)efct_hw_read_max_dump_size(hw); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void 31962306a36Sopenharmony_ciefct_logfcfi(struct efct_hw *hw, u32 j, u32 i, u32 id) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci efc_log_info(hw->os, 32262306a36Sopenharmony_ci "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n", 32362306a36Sopenharmony_ci j, hw->config.filter_def[j], i, id); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic inline void 32762306a36Sopenharmony_ciefct_hw_init_free_io(struct efct_hw_io *io) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * Set io->done to NULL, to avoid any callbacks, should 33162306a36Sopenharmony_ci * a completion be received for one of these IOs 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci io->done = NULL; 33462306a36Sopenharmony_ci io->abort_done = NULL; 33562306a36Sopenharmony_ci io->status_saved = false; 33662306a36Sopenharmony_ci io->abort_in_progress = false; 33762306a36Sopenharmony_ci io->type = 0xFFFF; 33862306a36Sopenharmony_ci io->wq = NULL; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic bool efct_hw_iotype_is_originator(u16 io_type) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci switch (io_type) { 34462306a36Sopenharmony_ci case EFCT_HW_FC_CT: 34562306a36Sopenharmony_ci case EFCT_HW_ELS_REQ: 34662306a36Sopenharmony_ci return true; 34762306a36Sopenharmony_ci default: 34862306a36Sopenharmony_ci return false; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void 35362306a36Sopenharmony_ciefct_hw_io_restore_sgl(struct efct_hw *hw, struct efct_hw_io *io) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci /* Restore the default */ 35662306a36Sopenharmony_ci io->sgl = &io->def_sgl; 35762306a36Sopenharmony_ci io->sgl_count = io->def_sgl_count; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void 36162306a36Sopenharmony_ciefct_hw_wq_process_io(void *arg, u8 *cqe, int status) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct efct_hw_io *io = arg; 36462306a36Sopenharmony_ci struct efct_hw *hw = io->hw; 36562306a36Sopenharmony_ci struct sli4_fc_wcqe *wcqe = (void *)cqe; 36662306a36Sopenharmony_ci u32 len = 0; 36762306a36Sopenharmony_ci u32 ext = 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* clear xbusy flag if WCQE[XB] is clear */ 37062306a36Sopenharmony_ci if (io->xbusy && (wcqe->flags & SLI4_WCQE_XB) == 0) 37162306a36Sopenharmony_ci io->xbusy = false; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* get extended CQE status */ 37462306a36Sopenharmony_ci switch (io->type) { 37562306a36Sopenharmony_ci case EFCT_HW_BLS_ACC: 37662306a36Sopenharmony_ci case EFCT_HW_BLS_RJT: 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci case EFCT_HW_ELS_REQ: 37962306a36Sopenharmony_ci sli_fc_els_did(&hw->sli, cqe, &ext); 38062306a36Sopenharmony_ci len = sli_fc_response_length(&hw->sli, cqe); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case EFCT_HW_ELS_RSP: 38362306a36Sopenharmony_ci case EFCT_HW_FC_CT_RSP: 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci case EFCT_HW_FC_CT: 38662306a36Sopenharmony_ci len = sli_fc_response_length(&hw->sli, cqe); 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_WRITE: 38962306a36Sopenharmony_ci len = sli_fc_io_length(&hw->sli, cqe); 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_READ: 39262306a36Sopenharmony_ci len = sli_fc_io_length(&hw->sli, cqe); 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_RSP: 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case EFCT_HW_IO_DNRX_REQUEUE: 39762306a36Sopenharmony_ci /* release the count for re-posting the buffer */ 39862306a36Sopenharmony_ci /* efct_hw_io_free(hw, io); */ 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci efc_log_err(hw->os, "unhandled io type %#x for XRI 0x%x\n", 40262306a36Sopenharmony_ci io->type, io->indicator); 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci if (status) { 40662306a36Sopenharmony_ci ext = sli_fc_ext_status(&hw->sli, cqe); 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * If we're not an originator IO, and XB is set, then issue 40962306a36Sopenharmony_ci * abort for the IO from within the HW 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (efct_hw_iotype_is_originator(io->type) && 41262306a36Sopenharmony_ci wcqe->flags & SLI4_WCQE_XB) { 41362306a36Sopenharmony_ci int rc; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci efc_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", 41662306a36Sopenharmony_ci io->indicator, io->reqtag); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* 41962306a36Sopenharmony_ci * Because targets may send a response when the IO 42062306a36Sopenharmony_ci * completes using the same XRI, we must wait for the 42162306a36Sopenharmony_ci * XRI_ABORTED CQE to issue the IO callback 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci rc = efct_hw_io_abort(hw, io, false, NULL, NULL); 42462306a36Sopenharmony_ci if (rc == 0) { 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * latch status to return after abort is 42762306a36Sopenharmony_ci * complete 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci io->status_saved = true; 43062306a36Sopenharmony_ci io->saved_status = status; 43162306a36Sopenharmony_ci io->saved_ext = ext; 43262306a36Sopenharmony_ci io->saved_len = len; 43362306a36Sopenharmony_ci goto exit_efct_hw_wq_process_io; 43462306a36Sopenharmony_ci } else if (rc == -EINPROGRESS) { 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * Already being aborted by someone else (ABTS 43762306a36Sopenharmony_ci * perhaps). Just return original 43862306a36Sopenharmony_ci * error. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci efc_log_debug(hw->os, "%s%#x tag=%#x\n", 44162306a36Sopenharmony_ci "abort in progress xri=", 44262306a36Sopenharmony_ci io->indicator, io->reqtag); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci } else { 44562306a36Sopenharmony_ci /* Failed to abort for some other reason, log 44662306a36Sopenharmony_ci * error 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci efc_log_debug(hw->os, "%s%#x tag=%#x rc=%d\n", 44962306a36Sopenharmony_ci "Failed to abort xri=", 45062306a36Sopenharmony_ci io->indicator, io->reqtag, rc); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (io->done) { 45662306a36Sopenharmony_ci efct_hw_done_t done = io->done; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci io->done = NULL; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (io->status_saved) { 46162306a36Sopenharmony_ci /* use latched status if exists */ 46262306a36Sopenharmony_ci status = io->saved_status; 46362306a36Sopenharmony_ci len = io->saved_len; 46462306a36Sopenharmony_ci ext = io->saved_ext; 46562306a36Sopenharmony_ci io->status_saved = false; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Restore default SGL */ 46962306a36Sopenharmony_ci efct_hw_io_restore_sgl(hw, io); 47062306a36Sopenharmony_ci done(io, len, status, ext, io->arg); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ciexit_efct_hw_wq_process_io: 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int 47862306a36Sopenharmony_ciefct_hw_setup_io(struct efct_hw *hw) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci u32 i = 0; 48162306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 48262306a36Sopenharmony_ci uintptr_t xfer_virt = 0; 48362306a36Sopenharmony_ci uintptr_t xfer_phys = 0; 48462306a36Sopenharmony_ci u32 index; 48562306a36Sopenharmony_ci bool new_alloc = true; 48662306a36Sopenharmony_ci struct efc_dma *dma; 48762306a36Sopenharmony_ci struct efct *efct = hw->os; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!hw->io) { 49062306a36Sopenharmony_ci hw->io = kmalloc_array(hw->config.n_io, sizeof(io), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!hw->io) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci memset(hw->io, 0, hw->config.n_io * sizeof(io)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci for (i = 0; i < hw->config.n_io; i++) { 49762306a36Sopenharmony_ci hw->io[i] = kzalloc(sizeof(*io), GFP_KERNEL); 49862306a36Sopenharmony_ci if (!hw->io[i]) 49962306a36Sopenharmony_ci goto error; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Create WQE buffs for IO */ 50362306a36Sopenharmony_ci hw->wqe_buffs = kzalloc((hw->config.n_io * hw->sli.wqe_size), 50462306a36Sopenharmony_ci GFP_KERNEL); 50562306a36Sopenharmony_ci if (!hw->wqe_buffs) { 50662306a36Sopenharmony_ci kfree(hw->io); 50762306a36Sopenharmony_ci return -ENOMEM; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci } else { 51162306a36Sopenharmony_ci /* re-use existing IOs, including SGLs */ 51262306a36Sopenharmony_ci new_alloc = false; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (new_alloc) { 51662306a36Sopenharmony_ci dma = &hw->xfer_rdy; 51762306a36Sopenharmony_ci dma->size = sizeof(struct fcp_txrdy) * hw->config.n_io; 51862306a36Sopenharmony_ci dma->virt = dma_alloc_coherent(&efct->pci->dev, 51962306a36Sopenharmony_ci dma->size, &dma->phys, GFP_KERNEL); 52062306a36Sopenharmony_ci if (!dma->virt) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci xfer_virt = (uintptr_t)hw->xfer_rdy.virt; 52462306a36Sopenharmony_ci xfer_phys = hw->xfer_rdy.phys; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Initialize the pool of HW IO objects */ 52762306a36Sopenharmony_ci for (i = 0; i < hw->config.n_io; i++) { 52862306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci io = hw->io[i]; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* initialize IO fields */ 53362306a36Sopenharmony_ci io->hw = hw; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Assign a WQE buff */ 53662306a36Sopenharmony_ci io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.wqe_size]; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* Allocate the request tag for this IO */ 53962306a36Sopenharmony_ci wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_io, io); 54062306a36Sopenharmony_ci if (!wqcb) { 54162306a36Sopenharmony_ci efc_log_err(hw->os, "can't allocate request tag\n"); 54262306a36Sopenharmony_ci return -ENOSPC; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci io->reqtag = wqcb->instance_index; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Now for the fields that are initialized on each free */ 54762306a36Sopenharmony_ci efct_hw_init_free_io(io); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* The XB flag isn't cleared on IO free, so init to zero */ 55062306a36Sopenharmony_ci io->xbusy = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (sli_resource_alloc(&hw->sli, SLI4_RSRC_XRI, 55362306a36Sopenharmony_ci &io->indicator, &index)) { 55462306a36Sopenharmony_ci efc_log_err(hw->os, 55562306a36Sopenharmony_ci "sli_resource_alloc failed @ %d\n", i); 55662306a36Sopenharmony_ci return -ENOMEM; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (new_alloc) { 56062306a36Sopenharmony_ci dma = &io->def_sgl; 56162306a36Sopenharmony_ci dma->size = hw->config.n_sgl * 56262306a36Sopenharmony_ci sizeof(struct sli4_sge); 56362306a36Sopenharmony_ci dma->virt = dma_alloc_coherent(&efct->pci->dev, 56462306a36Sopenharmony_ci dma->size, &dma->phys, 56562306a36Sopenharmony_ci GFP_KERNEL); 56662306a36Sopenharmony_ci if (!dma->virt) { 56762306a36Sopenharmony_ci efc_log_err(hw->os, "dma_alloc fail %d\n", i); 56862306a36Sopenharmony_ci memset(&io->def_sgl, 0, 56962306a36Sopenharmony_ci sizeof(struct efc_dma)); 57062306a36Sopenharmony_ci return -ENOMEM; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci io->def_sgl_count = hw->config.n_sgl; 57462306a36Sopenharmony_ci io->sgl = &io->def_sgl; 57562306a36Sopenharmony_ci io->sgl_count = io->def_sgl_count; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (hw->xfer_rdy.size) { 57862306a36Sopenharmony_ci io->xfer_rdy.virt = (void *)xfer_virt; 57962306a36Sopenharmony_ci io->xfer_rdy.phys = xfer_phys; 58062306a36Sopenharmony_ci io->xfer_rdy.size = sizeof(struct fcp_txrdy); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci xfer_virt += sizeof(struct fcp_txrdy); 58362306a36Sopenharmony_ci xfer_phys += sizeof(struct fcp_txrdy); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_cierror: 58962306a36Sopenharmony_ci for (i = 0; i < hw->config.n_io && hw->io[i]; i++) { 59062306a36Sopenharmony_ci kfree(hw->io[i]); 59162306a36Sopenharmony_ci hw->io[i] = NULL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci kfree(hw->io); 59562306a36Sopenharmony_ci hw->io = NULL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return -ENOMEM; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int 60162306a36Sopenharmony_ciefct_hw_init_prereg_io(struct efct_hw *hw) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci u32 i, idx = 0; 60462306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 60562306a36Sopenharmony_ci u8 cmd[SLI4_BMBX_SIZE]; 60662306a36Sopenharmony_ci int rc = 0; 60762306a36Sopenharmony_ci u32 n_rem; 60862306a36Sopenharmony_ci u32 n = 0; 60962306a36Sopenharmony_ci u32 sgls_per_request = 256; 61062306a36Sopenharmony_ci struct efc_dma **sgls = NULL; 61162306a36Sopenharmony_ci struct efc_dma req; 61262306a36Sopenharmony_ci struct efct *efct = hw->os; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci sgls = kmalloc_array(sgls_per_request, sizeof(*sgls), GFP_KERNEL); 61562306a36Sopenharmony_ci if (!sgls) 61662306a36Sopenharmony_ci return -ENOMEM; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci memset(&req, 0, sizeof(struct efc_dma)); 61962306a36Sopenharmony_ci req.size = 32 + sgls_per_request * 16; 62062306a36Sopenharmony_ci req.virt = dma_alloc_coherent(&efct->pci->dev, req.size, &req.phys, 62162306a36Sopenharmony_ci GFP_KERNEL); 62262306a36Sopenharmony_ci if (!req.virt) { 62362306a36Sopenharmony_ci kfree(sgls); 62462306a36Sopenharmony_ci return -ENOMEM; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci for (n_rem = hw->config.n_io; n_rem; n_rem -= n) { 62862306a36Sopenharmony_ci /* Copy address of SGL's into local sgls[] array, break 62962306a36Sopenharmony_ci * out if the xri is not contiguous. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci u32 min = (sgls_per_request < n_rem) ? sgls_per_request : n_rem; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (n = 0; n < min; n++) { 63462306a36Sopenharmony_ci /* Check that we have contiguous xri values */ 63562306a36Sopenharmony_ci if (n > 0) { 63662306a36Sopenharmony_ci if (hw->io[idx + n]->indicator != 63762306a36Sopenharmony_ci hw->io[idx + n - 1]->indicator + 1) 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci sgls[n] = hw->io[idx + n]->sgl; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (sli_cmd_post_sgl_pages(&hw->sli, cmd, 64562306a36Sopenharmony_ci hw->io[idx]->indicator, n, sgls, NULL, &req)) { 64662306a36Sopenharmony_ci rc = -EIO; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci rc = efct_hw_command(hw, cmd, EFCT_CMD_POLL, NULL, NULL); 65162306a36Sopenharmony_ci if (rc) { 65262306a36Sopenharmony_ci efc_log_err(hw->os, "SGL post failed, rc=%d\n", rc); 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Add to tail if successful */ 65762306a36Sopenharmony_ci for (i = 0; i < n; i++, idx++) { 65862306a36Sopenharmony_ci io = hw->io[idx]; 65962306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_FREE; 66062306a36Sopenharmony_ci INIT_LIST_HEAD(&io->list_entry); 66162306a36Sopenharmony_ci list_add_tail(&io->list_entry, &hw->io_free); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, req.size, req.virt, req.phys); 66662306a36Sopenharmony_ci memset(&req, 0, sizeof(struct efc_dma)); 66762306a36Sopenharmony_ci kfree(sgls); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return rc; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int 67362306a36Sopenharmony_ciefct_hw_init_io(struct efct_hw *hw) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci u32 i, idx = 0; 67662306a36Sopenharmony_ci bool prereg = false; 67762306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 67862306a36Sopenharmony_ci int rc = 0; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci prereg = hw->sli.params.sgl_pre_registered; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (prereg) 68362306a36Sopenharmony_ci return efct_hw_init_prereg_io(hw); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; i < hw->config.n_io; i++, idx++) { 68662306a36Sopenharmony_ci io = hw->io[idx]; 68762306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_FREE; 68862306a36Sopenharmony_ci INIT_LIST_HEAD(&io->list_entry); 68962306a36Sopenharmony_ci list_add_tail(&io->list_entry, &hw->io_free); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return rc; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int 69662306a36Sopenharmony_ciefct_hw_config_set_fdt_xfer_hint(struct efct_hw *hw, u32 fdt_xfer_hint) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci int rc = 0; 69962306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE]; 70062306a36Sopenharmony_ci struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint param; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci memset(¶m, 0, sizeof(param)); 70362306a36Sopenharmony_ci param.fdt_xfer_hint = cpu_to_le32(fdt_xfer_hint); 70462306a36Sopenharmony_ci /* build the set_features command */ 70562306a36Sopenharmony_ci sli_cmd_common_set_features(&hw->sli, buf, 70662306a36Sopenharmony_ci SLI4_SET_FEATURES_SET_FTD_XFER_HINT, sizeof(param), ¶m); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); 70962306a36Sopenharmony_ci if (rc) 71062306a36Sopenharmony_ci efc_log_warn(hw->os, "set FDT hint %d failed: %d\n", 71162306a36Sopenharmony_ci fdt_xfer_hint, rc); 71262306a36Sopenharmony_ci else 71362306a36Sopenharmony_ci efc_log_info(hw->os, "Set FTD transfer hint to %d\n", 71462306a36Sopenharmony_ci le32_to_cpu(param.fdt_xfer_hint)); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return rc; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int 72062306a36Sopenharmony_ciefct_hw_config_rq(struct efct_hw *hw) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci u32 min_rq_count, i, rc; 72362306a36Sopenharmony_ci struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]; 72462306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE]; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci efc_log_info(hw->os, "using REG_FCFI standard\n"); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* 72962306a36Sopenharmony_ci * Set the filter match/mask values from hw's 73062306a36Sopenharmony_ci * filter_def values 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { 73362306a36Sopenharmony_ci rq_cfg[i].rq_id = cpu_to_le16(0xffff); 73462306a36Sopenharmony_ci rq_cfg[i].r_ctl_mask = (u8)hw->config.filter_def[i]; 73562306a36Sopenharmony_ci rq_cfg[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 8); 73662306a36Sopenharmony_ci rq_cfg[i].type_mask = (u8)(hw->config.filter_def[i] >> 16); 73762306a36Sopenharmony_ci rq_cfg[i].type_match = (u8)(hw->config.filter_def[i] >> 24); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * Update the rq_id's of the FCF configuration 74262306a36Sopenharmony_ci * (don't update more than the number of rq_cfg 74362306a36Sopenharmony_ci * elements) 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_ci min_rq_count = (hw->hw_rq_count < SLI4_CMD_REG_FCFI_NUM_RQ_CFG) ? 74662306a36Sopenharmony_ci hw->hw_rq_count : SLI4_CMD_REG_FCFI_NUM_RQ_CFG; 74762306a36Sopenharmony_ci for (i = 0; i < min_rq_count; i++) { 74862306a36Sopenharmony_ci struct hw_rq *rq = hw->hw_rq[i]; 74962306a36Sopenharmony_ci u32 j; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) { 75262306a36Sopenharmony_ci u32 mask = (rq->filter_mask != 0) ? 75362306a36Sopenharmony_ci rq->filter_mask : 1; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (!(mask & (1U << j))) 75662306a36Sopenharmony_ci continue; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci rq_cfg[i].rq_id = cpu_to_le16(rq->hdr->id); 75962306a36Sopenharmony_ci efct_logfcfi(hw, j, i, rq->hdr->id); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci rc = -EIO; 76462306a36Sopenharmony_ci if (!sli_cmd_reg_fcfi(&hw->sli, buf, 0, rq_cfg)) 76562306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (rc != 0) { 76862306a36Sopenharmony_ci efc_log_err(hw->os, "FCFI registration failed\n"); 76962306a36Sopenharmony_ci return rc; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci hw->fcf_indicator = 77262306a36Sopenharmony_ci le16_to_cpu(((struct sli4_cmd_reg_fcfi *)buf)->fcfi); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return rc; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int 77862306a36Sopenharmony_ciefct_hw_config_mrq(struct efct_hw *hw, u8 mode, u16 fcf_index) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE], mrq_bitmask = 0; 78162306a36Sopenharmony_ci struct hw_rq *rq; 78262306a36Sopenharmony_ci struct sli4_cmd_reg_fcfi_mrq *rsp = NULL; 78362306a36Sopenharmony_ci struct sli4_cmd_rq_cfg rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG]; 78462306a36Sopenharmony_ci u32 rc, i; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) 78762306a36Sopenharmony_ci goto issue_cmd; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Set the filter match/mask values from hw's filter_def values */ 79062306a36Sopenharmony_ci for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { 79162306a36Sopenharmony_ci rq_filter[i].rq_id = cpu_to_le16(0xffff); 79262306a36Sopenharmony_ci rq_filter[i].type_mask = (u8)hw->config.filter_def[i]; 79362306a36Sopenharmony_ci rq_filter[i].type_match = (u8)(hw->config.filter_def[i] >> 8); 79462306a36Sopenharmony_ci rq_filter[i].r_ctl_mask = (u8)(hw->config.filter_def[i] >> 16); 79562306a36Sopenharmony_ci rq_filter[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 24); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci rq = hw->hw_rq[0]; 79962306a36Sopenharmony_ci rq_filter[0].rq_id = cpu_to_le16(rq->hdr->id); 80062306a36Sopenharmony_ci rq_filter[1].rq_id = cpu_to_le16(rq->hdr->id); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci mrq_bitmask = 0x2; 80362306a36Sopenharmony_ciissue_cmd: 80462306a36Sopenharmony_ci efc_log_debug(hw->os, "Issue reg_fcfi_mrq count:%d policy:%d mode:%d\n", 80562306a36Sopenharmony_ci hw->hw_rq_count, hw->config.rq_selection_policy, mode); 80662306a36Sopenharmony_ci /* Invoke REG_FCFI_MRQ */ 80762306a36Sopenharmony_ci rc = sli_cmd_reg_fcfi_mrq(&hw->sli, buf, mode, fcf_index, 80862306a36Sopenharmony_ci hw->config.rq_selection_policy, mrq_bitmask, 80962306a36Sopenharmony_ci hw->hw_mrq_count, rq_filter); 81062306a36Sopenharmony_ci if (rc) { 81162306a36Sopenharmony_ci efc_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed\n"); 81262306a36Sopenharmony_ci return -EIO; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci rsp = (struct sli4_cmd_reg_fcfi_mrq *)buf; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if ((rc) || (le16_to_cpu(rsp->hdr.status))) { 82062306a36Sopenharmony_ci efc_log_err(hw->os, "FCFI MRQ reg failed. cmd=%x status=%x\n", 82162306a36Sopenharmony_ci rsp->hdr.command, le16_to_cpu(rsp->hdr.status)); 82262306a36Sopenharmony_ci return -EIO; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) 82662306a36Sopenharmony_ci hw->fcf_indicator = le16_to_cpu(rsp->fcfi); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic void 83262306a36Sopenharmony_ciefct_hw_queue_hash_add(struct efct_queue_hash *hash, 83362306a36Sopenharmony_ci u16 id, u16 index) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci u32 hash_index = id & (EFCT_HW_Q_HASH_SIZE - 1); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * Since the hash is always bigger than the number of queues, then we 83962306a36Sopenharmony_ci * never have to worry about an infinite loop. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci while (hash[hash_index].in_use) 84262306a36Sopenharmony_ci hash_index = (hash_index + 1) & (EFCT_HW_Q_HASH_SIZE - 1); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* not used, claim the entry */ 84562306a36Sopenharmony_ci hash[hash_index].id = id; 84662306a36Sopenharmony_ci hash[hash_index].in_use = true; 84762306a36Sopenharmony_ci hash[hash_index].index = index; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int 85162306a36Sopenharmony_ciefct_hw_config_sli_port_health_check(struct efct_hw *hw, u8 query, u8 enable) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci int rc = 0; 85462306a36Sopenharmony_ci u8 buf[SLI4_BMBX_SIZE]; 85562306a36Sopenharmony_ci struct sli4_rqst_cmn_set_features_health_check param; 85662306a36Sopenharmony_ci u32 health_check_flag = 0; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci memset(¶m, 0, sizeof(param)); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (enable) 86162306a36Sopenharmony_ci health_check_flag |= SLI4_RQ_HEALTH_CHECK_ENABLE; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (query) 86462306a36Sopenharmony_ci health_check_flag |= SLI4_RQ_HEALTH_CHECK_QUERY; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci param.health_check_dword = cpu_to_le32(health_check_flag); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* build the set_features command */ 86962306a36Sopenharmony_ci sli_cmd_common_set_features(&hw->sli, buf, 87062306a36Sopenharmony_ci SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK, sizeof(param), ¶m); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); 87362306a36Sopenharmony_ci if (rc) 87462306a36Sopenharmony_ci efc_log_err(hw->os, "efct_hw_command returns %d\n", rc); 87562306a36Sopenharmony_ci else 87662306a36Sopenharmony_ci efc_log_debug(hw->os, "SLI Port Health Check is enabled\n"); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return rc; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciint 88262306a36Sopenharmony_ciefct_hw_init(struct efct_hw *hw) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int rc; 88562306a36Sopenharmony_ci u32 i = 0; 88662306a36Sopenharmony_ci int rem_count; 88762306a36Sopenharmony_ci unsigned long flags = 0; 88862306a36Sopenharmony_ci struct efct_hw_io *temp; 88962306a36Sopenharmony_ci struct efc_dma *dma; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* 89262306a36Sopenharmony_ci * Make sure the command lists are empty. If this is start-of-day, 89362306a36Sopenharmony_ci * they'll be empty since they were just initialized in efct_hw_setup. 89462306a36Sopenharmony_ci * If we've just gone through a reset, the command and command pending 89562306a36Sopenharmony_ci * lists should have been cleaned up as part of the reset 89662306a36Sopenharmony_ci * (efct_hw_reset()). 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci spin_lock_irqsave(&hw->cmd_lock, flags); 89962306a36Sopenharmony_ci if (!list_empty(&hw->cmd_head)) { 90062306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 90162306a36Sopenharmony_ci efc_log_err(hw->os, "command found on cmd list\n"); 90262306a36Sopenharmony_ci return -EIO; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci if (!list_empty(&hw->cmd_pending)) { 90562306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 90662306a36Sopenharmony_ci efc_log_err(hw->os, "command found on pending list\n"); 90762306a36Sopenharmony_ci return -EIO; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* Free RQ buffers if prevously allocated */ 91262306a36Sopenharmony_ci efct_hw_rx_free(hw); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * The IO queues must be initialized here for the reset case. The 91662306a36Sopenharmony_ci * efct_hw_init_io() function will re-add the IOs to the free list. 91762306a36Sopenharmony_ci * The cmd_head list should be OK since we free all entries in 91862306a36Sopenharmony_ci * efct_hw_command_cancel() that is called in the efct_hw_reset(). 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* If we are in this function due to a reset, there may be stale items 92262306a36Sopenharmony_ci * on lists that need to be removed. Clean them up. 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_ci rem_count = 0; 92562306a36Sopenharmony_ci while ((!list_empty(&hw->io_wait_free))) { 92662306a36Sopenharmony_ci rem_count++; 92762306a36Sopenharmony_ci temp = list_first_entry(&hw->io_wait_free, struct efct_hw_io, 92862306a36Sopenharmony_ci list_entry); 92962306a36Sopenharmony_ci list_del_init(&temp->list_entry); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (rem_count > 0) 93262306a36Sopenharmony_ci efc_log_debug(hw->os, "rmvd %d items from io_wait_free list\n", 93362306a36Sopenharmony_ci rem_count); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci rem_count = 0; 93662306a36Sopenharmony_ci while ((!list_empty(&hw->io_inuse))) { 93762306a36Sopenharmony_ci rem_count++; 93862306a36Sopenharmony_ci temp = list_first_entry(&hw->io_inuse, struct efct_hw_io, 93962306a36Sopenharmony_ci list_entry); 94062306a36Sopenharmony_ci list_del_init(&temp->list_entry); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci if (rem_count > 0) 94362306a36Sopenharmony_ci efc_log_debug(hw->os, "rmvd %d items from io_inuse list\n", 94462306a36Sopenharmony_ci rem_count); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci rem_count = 0; 94762306a36Sopenharmony_ci while ((!list_empty(&hw->io_free))) { 94862306a36Sopenharmony_ci rem_count++; 94962306a36Sopenharmony_ci temp = list_first_entry(&hw->io_free, struct efct_hw_io, 95062306a36Sopenharmony_ci list_entry); 95162306a36Sopenharmony_ci list_del_init(&temp->list_entry); 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci if (rem_count > 0) 95462306a36Sopenharmony_ci efc_log_debug(hw->os, "rmvd %d items from io_free list\n", 95562306a36Sopenharmony_ci rem_count); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* If MRQ not required, Make sure we dont request feature. */ 95862306a36Sopenharmony_ci if (hw->config.n_rq == 1) 95962306a36Sopenharmony_ci hw->sli.features &= (~SLI4_REQFEAT_MRQP); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (sli_init(&hw->sli)) { 96262306a36Sopenharmony_ci efc_log_err(hw->os, "SLI failed to initialize\n"); 96362306a36Sopenharmony_ci return -EIO; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (hw->sliport_healthcheck) { 96762306a36Sopenharmony_ci rc = efct_hw_config_sli_port_health_check(hw, 0, 1); 96862306a36Sopenharmony_ci if (rc != 0) { 96962306a36Sopenharmony_ci efc_log_err(hw->os, "Enable port Health check fail\n"); 97062306a36Sopenharmony_ci return rc; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* 97562306a36Sopenharmony_ci * Set FDT transfer hint, only works on Lancer 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci if (hw->sli.if_type == SLI4_INTF_IF_TYPE_2) { 97862306a36Sopenharmony_ci /* 97962306a36Sopenharmony_ci * Non-fatal error. In particular, we can disregard failure to 98062306a36Sopenharmony_ci * set EFCT_HW_FDT_XFER_HINT on devices with legacy firmware 98162306a36Sopenharmony_ci * that do not support EFCT_HW_FDT_XFER_HINT feature. 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci efct_hw_config_set_fdt_xfer_hint(hw, EFCT_HW_FDT_XFER_HINT); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* zero the hashes */ 98762306a36Sopenharmony_ci memset(hw->cq_hash, 0, sizeof(hw->cq_hash)); 98862306a36Sopenharmony_ci efc_log_debug(hw->os, "Max CQs %d, hash size = %d\n", 98962306a36Sopenharmony_ci EFCT_HW_MAX_NUM_CQ, EFCT_HW_Q_HASH_SIZE); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci memset(hw->rq_hash, 0, sizeof(hw->rq_hash)); 99262306a36Sopenharmony_ci efc_log_debug(hw->os, "Max RQs %d, hash size = %d\n", 99362306a36Sopenharmony_ci EFCT_HW_MAX_NUM_RQ, EFCT_HW_Q_HASH_SIZE); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci memset(hw->wq_hash, 0, sizeof(hw->wq_hash)); 99662306a36Sopenharmony_ci efc_log_debug(hw->os, "Max WQs %d, hash size = %d\n", 99762306a36Sopenharmony_ci EFCT_HW_MAX_NUM_WQ, EFCT_HW_Q_HASH_SIZE); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci rc = efct_hw_init_queues(hw); 100062306a36Sopenharmony_ci if (rc) 100162306a36Sopenharmony_ci return rc; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci rc = efct_hw_map_wq_cpu(hw); 100462306a36Sopenharmony_ci if (rc) 100562306a36Sopenharmony_ci return rc; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* Allocate and p_st RQ buffers */ 100862306a36Sopenharmony_ci rc = efct_hw_rx_allocate(hw); 100962306a36Sopenharmony_ci if (rc) { 101062306a36Sopenharmony_ci efc_log_err(hw->os, "rx_allocate failed\n"); 101162306a36Sopenharmony_ci return rc; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci rc = efct_hw_rx_post(hw); 101562306a36Sopenharmony_ci if (rc) { 101662306a36Sopenharmony_ci efc_log_err(hw->os, "WARNING - error posting RQ buffers\n"); 101762306a36Sopenharmony_ci return rc; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (hw->config.n_eq == 1) { 102162306a36Sopenharmony_ci rc = efct_hw_config_rq(hw); 102262306a36Sopenharmony_ci if (rc) { 102362306a36Sopenharmony_ci efc_log_err(hw->os, "config rq failed %d\n", rc); 102462306a36Sopenharmony_ci return rc; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } else { 102762306a36Sopenharmony_ci rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0); 102862306a36Sopenharmony_ci if (rc != 0) { 102962306a36Sopenharmony_ci efc_log_err(hw->os, "REG_FCFI_MRQ FCFI reg failed\n"); 103062306a36Sopenharmony_ci return rc; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0); 103462306a36Sopenharmony_ci if (rc != 0) { 103562306a36Sopenharmony_ci efc_log_err(hw->os, "REG_FCFI_MRQ MRQ reg failed\n"); 103662306a36Sopenharmony_ci return rc; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* 104162306a36Sopenharmony_ci * Allocate the WQ request tag pool, if not previously allocated 104262306a36Sopenharmony_ci * (the request tag value is 16 bits, thus the pool allocation size 104362306a36Sopenharmony_ci * of 64k) 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci hw->wq_reqtag_pool = efct_hw_reqtag_pool_alloc(hw); 104662306a36Sopenharmony_ci if (!hw->wq_reqtag_pool) { 104762306a36Sopenharmony_ci efc_log_err(hw->os, "efct_hw_reqtag_pool_alloc failed\n"); 104862306a36Sopenharmony_ci return -ENOMEM; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci rc = efct_hw_setup_io(hw); 105262306a36Sopenharmony_ci if (rc) { 105362306a36Sopenharmony_ci efc_log_err(hw->os, "IO allocation failure\n"); 105462306a36Sopenharmony_ci return rc; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci rc = efct_hw_init_io(hw); 105862306a36Sopenharmony_ci if (rc) { 105962306a36Sopenharmony_ci efc_log_err(hw->os, "IO initialization failure\n"); 106062306a36Sopenharmony_ci return rc; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci dma = &hw->loop_map; 106462306a36Sopenharmony_ci dma->size = SLI4_MIN_LOOP_MAP_BYTES; 106562306a36Sopenharmony_ci dma->virt = dma_alloc_coherent(&hw->os->pci->dev, dma->size, &dma->phys, 106662306a36Sopenharmony_ci GFP_KERNEL); 106762306a36Sopenharmony_ci if (!dma->virt) 106862306a36Sopenharmony_ci return -EIO; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* 107162306a36Sopenharmony_ci * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ 107262306a36Sopenharmony_ci * entries 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_ci for (i = 0; i < hw->eq_count; i++) 107562306a36Sopenharmony_ci sli_queue_arm(&hw->sli, &hw->eq[i], true); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* 107862306a36Sopenharmony_ci * Initialize RQ hash 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_ci for (i = 0; i < hw->rq_count; i++) 108162306a36Sopenharmony_ci efct_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * Initialize WQ hash 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci for (i = 0; i < hw->wq_count; i++) 108762306a36Sopenharmony_ci efct_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* 109062306a36Sopenharmony_ci * Arming the CQ allows (e.g.) MQ completions to write CQ entries 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci for (i = 0; i < hw->cq_count; i++) { 109362306a36Sopenharmony_ci efct_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i); 109462306a36Sopenharmony_ci sli_queue_arm(&hw->sli, &hw->cq[i], true); 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* Set RQ process limit*/ 109862306a36Sopenharmony_ci for (i = 0; i < hw->hw_rq_count; i++) { 109962306a36Sopenharmony_ci struct hw_rq *rq = hw->hw_rq[i]; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci hw->cq[rq->cq->instance].proc_limit = hw->config.n_io / 2; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* record the fact that the queues are functional */ 110562306a36Sopenharmony_ci hw->state = EFCT_HW_STATE_ACTIVE; 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * Allocate a HW IOs for send frame. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci hw->hw_wq[0]->send_frame_io = efct_hw_io_alloc(hw); 111062306a36Sopenharmony_ci if (!hw->hw_wq[0]->send_frame_io) 111162306a36Sopenharmony_ci efc_log_err(hw->os, "alloc for send_frame_io failed\n"); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* Initialize send frame sequence id */ 111462306a36Sopenharmony_ci atomic_set(&hw->send_frame_seq_id, 0); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ciint 112062306a36Sopenharmony_ciefct_hw_parse_filter(struct efct_hw *hw, void *value) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int rc = 0; 112362306a36Sopenharmony_ci char *p = NULL; 112462306a36Sopenharmony_ci char *token; 112562306a36Sopenharmony_ci u32 idx = 0; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) 112862306a36Sopenharmony_ci hw->config.filter_def[idx] = 0; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci p = kstrdup(value, GFP_KERNEL); 113162306a36Sopenharmony_ci if (!p || !*p) { 113262306a36Sopenharmony_ci efc_log_err(hw->os, "p is NULL\n"); 113362306a36Sopenharmony_ci return -ENOMEM; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci idx = 0; 113762306a36Sopenharmony_ci while ((token = strsep(&p, ",")) && *token) { 113862306a36Sopenharmony_ci if (kstrtou32(token, 0, &hw->config.filter_def[idx++])) 113962306a36Sopenharmony_ci efc_log_err(hw->os, "kstrtoint failed\n"); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (!p || !*p) 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (idx == ARRAY_SIZE(hw->config.filter_def)) 114562306a36Sopenharmony_ci break; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci kfree(p); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return rc; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ciu64 115362306a36Sopenharmony_ciefct_get_wwnn(struct efct_hw *hw) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct sli4 *sli = &hw->sli; 115662306a36Sopenharmony_ci u8 p[8]; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci memcpy(p, sli->wwnn, sizeof(p)); 115962306a36Sopenharmony_ci return get_unaligned_be64(p); 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ciu64 116362306a36Sopenharmony_ciefct_get_wwpn(struct efct_hw *hw) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct sli4 *sli = &hw->sli; 116662306a36Sopenharmony_ci u8 p[8]; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci memcpy(p, sli->wwpn, sizeof(p)); 116962306a36Sopenharmony_ci return get_unaligned_be64(p); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic struct efc_hw_rq_buffer * 117362306a36Sopenharmony_ciefct_hw_rx_buffer_alloc(struct efct_hw *hw, u32 rqindex, u32 count, 117462306a36Sopenharmony_ci u32 size) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct efct *efct = hw->os; 117762306a36Sopenharmony_ci struct efc_hw_rq_buffer *rq_buf = NULL; 117862306a36Sopenharmony_ci struct efc_hw_rq_buffer *prq; 117962306a36Sopenharmony_ci u32 i; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (!count) 118262306a36Sopenharmony_ci return NULL; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci rq_buf = kmalloc_array(count, sizeof(*rq_buf), GFP_KERNEL); 118562306a36Sopenharmony_ci if (!rq_buf) 118662306a36Sopenharmony_ci return NULL; 118762306a36Sopenharmony_ci memset(rq_buf, 0, sizeof(*rq_buf) * count); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci for (i = 0, prq = rq_buf; i < count; i ++, prq++) { 119062306a36Sopenharmony_ci prq->rqindex = rqindex; 119162306a36Sopenharmony_ci prq->dma.size = size; 119262306a36Sopenharmony_ci prq->dma.virt = dma_alloc_coherent(&efct->pci->dev, 119362306a36Sopenharmony_ci prq->dma.size, 119462306a36Sopenharmony_ci &prq->dma.phys, 119562306a36Sopenharmony_ci GFP_KERNEL); 119662306a36Sopenharmony_ci if (!prq->dma.virt) { 119762306a36Sopenharmony_ci efc_log_err(hw->os, "DMA allocation failed\n"); 119862306a36Sopenharmony_ci kfree(rq_buf); 119962306a36Sopenharmony_ci return NULL; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci return rq_buf; 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic void 120662306a36Sopenharmony_ciefct_hw_rx_buffer_free(struct efct_hw *hw, 120762306a36Sopenharmony_ci struct efc_hw_rq_buffer *rq_buf, 120862306a36Sopenharmony_ci u32 count) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct efct *efct = hw->os; 121162306a36Sopenharmony_ci u32 i; 121262306a36Sopenharmony_ci struct efc_hw_rq_buffer *prq; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (rq_buf) { 121562306a36Sopenharmony_ci for (i = 0, prq = rq_buf; i < count; i++, prq++) { 121662306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, 121762306a36Sopenharmony_ci prq->dma.size, prq->dma.virt, 121862306a36Sopenharmony_ci prq->dma.phys); 121962306a36Sopenharmony_ci memset(&prq->dma, 0, sizeof(struct efc_dma)); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci kfree(rq_buf); 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ciint 122762306a36Sopenharmony_ciefct_hw_rx_allocate(struct efct_hw *hw) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct efct *efct = hw->os; 123062306a36Sopenharmony_ci u32 i; 123162306a36Sopenharmony_ci int rc = 0; 123262306a36Sopenharmony_ci u32 rqindex = 0; 123362306a36Sopenharmony_ci u32 hdr_size = EFCT_HW_RQ_SIZE_HDR; 123462306a36Sopenharmony_ci u32 payload_size = hw->config.rq_default_buffer_size; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci rqindex = 0; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci for (i = 0; i < hw->hw_rq_count; i++) { 123962306a36Sopenharmony_ci struct hw_rq *rq = hw->hw_rq[i]; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci /* Allocate header buffers */ 124262306a36Sopenharmony_ci rq->hdr_buf = efct_hw_rx_buffer_alloc(hw, rqindex, 124362306a36Sopenharmony_ci rq->entry_count, 124462306a36Sopenharmony_ci hdr_size); 124562306a36Sopenharmony_ci if (!rq->hdr_buf) { 124662306a36Sopenharmony_ci efc_log_err(efct, "rx_buffer_alloc hdr_buf failed\n"); 124762306a36Sopenharmony_ci rc = -EIO; 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci efc_log_debug(hw->os, 125262306a36Sopenharmony_ci "rq[%2d] rq_id %02d header %4d by %4d bytes\n", 125362306a36Sopenharmony_ci i, rq->hdr->id, rq->entry_count, hdr_size); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci rqindex++; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Allocate payload buffers */ 125862306a36Sopenharmony_ci rq->payload_buf = efct_hw_rx_buffer_alloc(hw, rqindex, 125962306a36Sopenharmony_ci rq->entry_count, 126062306a36Sopenharmony_ci payload_size); 126162306a36Sopenharmony_ci if (!rq->payload_buf) { 126262306a36Sopenharmony_ci efc_log_err(efct, "rx_buffer_alloc fb_buf failed\n"); 126362306a36Sopenharmony_ci rc = -EIO; 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci efc_log_debug(hw->os, 126762306a36Sopenharmony_ci "rq[%2d] rq_id %02d default %4d by %4d bytes\n", 126862306a36Sopenharmony_ci i, rq->data->id, rq->entry_count, payload_size); 126962306a36Sopenharmony_ci rqindex++; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return rc ? -EIO : 0; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ciint 127662306a36Sopenharmony_ciefct_hw_rx_post(struct efct_hw *hw) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci u32 i; 127962306a36Sopenharmony_ci u32 idx; 128062306a36Sopenharmony_ci u32 rq_idx; 128162306a36Sopenharmony_ci int rc = 0; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (!hw->seq_pool) { 128462306a36Sopenharmony_ci u32 count = 0; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci for (i = 0; i < hw->hw_rq_count; i++) 128762306a36Sopenharmony_ci count += hw->hw_rq[i]->entry_count; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci hw->seq_pool = kmalloc_array(count, 129062306a36Sopenharmony_ci sizeof(struct efc_hw_sequence), GFP_KERNEL); 129162306a36Sopenharmony_ci if (!hw->seq_pool) 129262306a36Sopenharmony_ci return -ENOMEM; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* 129662306a36Sopenharmony_ci * In RQ pair mode, we MUST post the header and payload buffer at the 129762306a36Sopenharmony_ci * same time. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) { 130062306a36Sopenharmony_ci struct hw_rq *rq = hw->hw_rq[rq_idx]; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci for (i = 0; i < rq->entry_count - 1; i++) { 130362306a36Sopenharmony_ci struct efc_hw_sequence *seq; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci seq = hw->seq_pool + idx; 130662306a36Sopenharmony_ci idx++; 130762306a36Sopenharmony_ci seq->header = &rq->hdr_buf[i]; 130862306a36Sopenharmony_ci seq->payload = &rq->payload_buf[i]; 130962306a36Sopenharmony_ci rc = efct_hw_sequence_free(hw, seq); 131062306a36Sopenharmony_ci if (rc) 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci if (rc) 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (rc && hw->seq_pool) 131862306a36Sopenharmony_ci kfree(hw->seq_pool); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return rc; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_civoid 132462306a36Sopenharmony_ciefct_hw_rx_free(struct efct_hw *hw) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci u32 i; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* Free hw_rq buffers */ 132962306a36Sopenharmony_ci for (i = 0; i < hw->hw_rq_count; i++) { 133062306a36Sopenharmony_ci struct hw_rq *rq = hw->hw_rq[i]; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (rq) { 133362306a36Sopenharmony_ci efct_hw_rx_buffer_free(hw, rq->hdr_buf, 133462306a36Sopenharmony_ci rq->entry_count); 133562306a36Sopenharmony_ci rq->hdr_buf = NULL; 133662306a36Sopenharmony_ci efct_hw_rx_buffer_free(hw, rq->payload_buf, 133762306a36Sopenharmony_ci rq->entry_count); 133862306a36Sopenharmony_ci rq->payload_buf = NULL; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic int 134462306a36Sopenharmony_ciefct_hw_cmd_submit_pending(struct efct_hw *hw) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci int rc = 0; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Assumes lock held */ 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* Only submit MQE if there's room */ 135162306a36Sopenharmony_ci while (hw->cmd_head_count < (EFCT_HW_MQ_DEPTH - 1) && 135262306a36Sopenharmony_ci !list_empty(&hw->cmd_pending)) { 135362306a36Sopenharmony_ci struct efct_command_ctx *ctx; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci ctx = list_first_entry(&hw->cmd_pending, 135662306a36Sopenharmony_ci struct efct_command_ctx, list_entry); 135762306a36Sopenharmony_ci if (!ctx) 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci list_del_init(&ctx->list_entry); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci list_add_tail(&ctx->list_entry, &hw->cmd_head); 136362306a36Sopenharmony_ci hw->cmd_head_count++; 136462306a36Sopenharmony_ci if (sli_mq_write(&hw->sli, hw->mq, ctx->buf) < 0) { 136562306a36Sopenharmony_ci efc_log_debug(hw->os, 136662306a36Sopenharmony_ci "sli_queue_write failed: %d\n", rc); 136762306a36Sopenharmony_ci rc = -EIO; 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci return rc; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ciint 137562306a36Sopenharmony_ciefct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb, void *arg) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci int rc = -EIO; 137862306a36Sopenharmony_ci unsigned long flags = 0; 137962306a36Sopenharmony_ci void *bmbx = NULL; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* 138262306a36Sopenharmony_ci * If the chip is in an error state (UE'd) then reject this mailbox 138362306a36Sopenharmony_ci * command. 138462306a36Sopenharmony_ci */ 138562306a36Sopenharmony_ci if (sli_fw_error_status(&hw->sli) > 0) { 138662306a36Sopenharmony_ci efc_log_crit(hw->os, "Chip in an error state - reset needed\n"); 138762306a36Sopenharmony_ci efc_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n", 138862306a36Sopenharmony_ci sli_reg_read_status(&hw->sli), 138962306a36Sopenharmony_ci sli_reg_read_err1(&hw->sli), 139062306a36Sopenharmony_ci sli_reg_read_err2(&hw->sli)); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci return -EIO; 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* 139662306a36Sopenharmony_ci * Send a mailbox command to the hardware, and either wait for 139762306a36Sopenharmony_ci * a completion (EFCT_CMD_POLL) or get an optional asynchronous 139862306a36Sopenharmony_ci * completion (EFCT_CMD_NOWAIT). 139962306a36Sopenharmony_ci */ 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (opts == EFCT_CMD_POLL) { 140262306a36Sopenharmony_ci mutex_lock(&hw->bmbx_lock); 140362306a36Sopenharmony_ci bmbx = hw->sli.bmbx.virt; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci memcpy(bmbx, cmd, SLI4_BMBX_SIZE); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (sli_bmbx_command(&hw->sli) == 0) { 140862306a36Sopenharmony_ci rc = 0; 140962306a36Sopenharmony_ci memcpy(cmd, bmbx, SLI4_BMBX_SIZE); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci mutex_unlock(&hw->bmbx_lock); 141262306a36Sopenharmony_ci } else if (opts == EFCT_CMD_NOWAIT) { 141362306a36Sopenharmony_ci struct efct_command_ctx *ctx = NULL; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) { 141662306a36Sopenharmony_ci efc_log_err(hw->os, "Can't send command, HW state=%d\n", 141762306a36Sopenharmony_ci hw->state); 141862306a36Sopenharmony_ci return -EIO; 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci ctx = mempool_alloc(hw->cmd_ctx_pool, GFP_ATOMIC); 142262306a36Sopenharmony_ci if (!ctx) 142362306a36Sopenharmony_ci return -ENOSPC; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci memset(ctx, 0, sizeof(struct efct_command_ctx)); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (cb) { 142862306a36Sopenharmony_ci ctx->cb = cb; 142962306a36Sopenharmony_ci ctx->arg = arg; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci memcpy(ctx->buf, cmd, SLI4_BMBX_SIZE); 143362306a36Sopenharmony_ci ctx->ctx = hw; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci spin_lock_irqsave(&hw->cmd_lock, flags); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* Add to pending list */ 143862306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->list_entry); 143962306a36Sopenharmony_ci list_add_tail(&ctx->list_entry, &hw->cmd_pending); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* Submit as much of the pending list as we can */ 144262306a36Sopenharmony_ci rc = efct_hw_cmd_submit_pending(hw); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci return rc; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int 145162306a36Sopenharmony_ciefct_hw_command_process(struct efct_hw *hw, int status, u8 *mqe, 145262306a36Sopenharmony_ci size_t size) 145362306a36Sopenharmony_ci{ 145462306a36Sopenharmony_ci struct efct_command_ctx *ctx = NULL; 145562306a36Sopenharmony_ci unsigned long flags = 0; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci spin_lock_irqsave(&hw->cmd_lock, flags); 145862306a36Sopenharmony_ci if (!list_empty(&hw->cmd_head)) { 145962306a36Sopenharmony_ci ctx = list_first_entry(&hw->cmd_head, 146062306a36Sopenharmony_ci struct efct_command_ctx, list_entry); 146162306a36Sopenharmony_ci list_del_init(&ctx->list_entry); 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci if (!ctx) { 146462306a36Sopenharmony_ci efc_log_err(hw->os, "no command context\n"); 146562306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 146662306a36Sopenharmony_ci return -EIO; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci hw->cmd_head_count--; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* Post any pending requests */ 147262306a36Sopenharmony_ci efct_hw_cmd_submit_pending(hw); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (ctx->cb) { 147762306a36Sopenharmony_ci memcpy(ctx->buf, mqe, size); 147862306a36Sopenharmony_ci ctx->cb(hw, status, ctx->buf, ctx->arg); 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci mempool_free(ctx, hw->cmd_ctx_pool); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci return 0; 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int 148762306a36Sopenharmony_ciefct_hw_mq_process(struct efct_hw *hw, 148862306a36Sopenharmony_ci int status, struct sli4_queue *mq) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci u8 mqe[SLI4_BMBX_SIZE]; 149162306a36Sopenharmony_ci int rc; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci rc = sli_mq_read(&hw->sli, mq, mqe); 149462306a36Sopenharmony_ci if (!rc) 149562306a36Sopenharmony_ci rc = efct_hw_command_process(hw, status, mqe, mq->size); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return rc; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int 150162306a36Sopenharmony_ciefct_hw_command_cancel(struct efct_hw *hw) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci unsigned long flags = 0; 150462306a36Sopenharmony_ci int rc = 0; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci spin_lock_irqsave(&hw->cmd_lock, flags); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci /* 150962306a36Sopenharmony_ci * Manually clean up remaining commands. Note: since this calls 151062306a36Sopenharmony_ci * efct_hw_command_process(), we'll also process the cmd_pending 151162306a36Sopenharmony_ci * list, so no need to manually clean that out. 151262306a36Sopenharmony_ci */ 151362306a36Sopenharmony_ci while (!list_empty(&hw->cmd_head)) { 151462306a36Sopenharmony_ci u8 mqe[SLI4_BMBX_SIZE] = { 0 }; 151562306a36Sopenharmony_ci struct efct_command_ctx *ctx; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci ctx = list_first_entry(&hw->cmd_head, 151862306a36Sopenharmony_ci struct efct_command_ctx, list_entry); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci efc_log_debug(hw->os, "hung command %08x\n", 152162306a36Sopenharmony_ci !ctx ? U32_MAX : *((u32 *)ctx->buf)); 152262306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 152362306a36Sopenharmony_ci rc = efct_hw_command_process(hw, -1, mqe, SLI4_BMBX_SIZE); 152462306a36Sopenharmony_ci spin_lock_irqsave(&hw->cmd_lock, flags); 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->cmd_lock, flags); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return rc; 153062306a36Sopenharmony_ci} 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cistatic void 153362306a36Sopenharmony_ciefct_mbox_rsp_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg) 153462306a36Sopenharmony_ci{ 153562306a36Sopenharmony_ci struct efct_mbox_rqst_ctx *ctx = arg; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (ctx) { 153862306a36Sopenharmony_ci if (ctx->callback) 153962306a36Sopenharmony_ci (*ctx->callback)(hw->os->efcport, status, mqe, 154062306a36Sopenharmony_ci ctx->arg); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci mempool_free(ctx, hw->mbox_rqst_pool); 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ciint 154762306a36Sopenharmony_ciefct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct efct_mbox_rqst_ctx *ctx; 155062306a36Sopenharmony_ci struct efct *efct = base; 155162306a36Sopenharmony_ci struct efct_hw *hw = &efct->hw; 155262306a36Sopenharmony_ci int rc; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* 155562306a36Sopenharmony_ci * Allocate a callback context (which includes the mbox cmd buffer), 155662306a36Sopenharmony_ci * we need this to be persistent as the mbox cmd submission may be 155762306a36Sopenharmony_ci * queued and executed later execution. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci ctx = mempool_alloc(hw->mbox_rqst_pool, GFP_ATOMIC); 156062306a36Sopenharmony_ci if (!ctx) 156162306a36Sopenharmony_ci return -EIO; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci ctx->callback = cb; 156462306a36Sopenharmony_ci ctx->arg = arg; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci rc = efct_hw_command(hw, cmd, EFCT_CMD_NOWAIT, efct_mbox_rsp_cb, ctx); 156762306a36Sopenharmony_ci if (rc) { 156862306a36Sopenharmony_ci efc_log_err(efct, "issue mbox rqst failure rc:%d\n", rc); 156962306a36Sopenharmony_ci mempool_free(ctx, hw->mbox_rqst_pool); 157062306a36Sopenharmony_ci return -EIO; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci return 0; 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_cistatic inline struct efct_hw_io * 157762306a36Sopenharmony_ci_efct_hw_io_alloc(struct efct_hw *hw) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (!list_empty(&hw->io_free)) { 158262306a36Sopenharmony_ci io = list_first_entry(&hw->io_free, struct efct_hw_io, 158362306a36Sopenharmony_ci list_entry); 158462306a36Sopenharmony_ci list_del(&io->list_entry); 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci if (io) { 158762306a36Sopenharmony_ci INIT_LIST_HEAD(&io->list_entry); 158862306a36Sopenharmony_ci list_add_tail(&io->list_entry, &hw->io_inuse); 158962306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_INUSE; 159062306a36Sopenharmony_ci io->abort_reqtag = U32_MAX; 159162306a36Sopenharmony_ci io->wq = hw->wq_cpu_array[raw_smp_processor_id()]; 159262306a36Sopenharmony_ci if (!io->wq) { 159362306a36Sopenharmony_ci efc_log_err(hw->os, "WQ not assigned for cpu:%d\n", 159462306a36Sopenharmony_ci raw_smp_processor_id()); 159562306a36Sopenharmony_ci io->wq = hw->hw_wq[0]; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci kref_init(&io->ref); 159862306a36Sopenharmony_ci io->release = efct_hw_io_free_internal; 159962306a36Sopenharmony_ci } else { 160062306a36Sopenharmony_ci atomic_add(1, &hw->io_alloc_failed_count); 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci return io; 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistruct efct_hw_io * 160762306a36Sopenharmony_ciefct_hw_io_alloc(struct efct_hw *hw) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 161062306a36Sopenharmony_ci unsigned long flags = 0; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci spin_lock_irqsave(&hw->io_lock, flags); 161362306a36Sopenharmony_ci io = _efct_hw_io_alloc(hw); 161462306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->io_lock, flags); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci return io; 161762306a36Sopenharmony_ci} 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_cistatic void 162062306a36Sopenharmony_ciefct_hw_io_free_move_correct_list(struct efct_hw *hw, 162162306a36Sopenharmony_ci struct efct_hw_io *io) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci /* 162462306a36Sopenharmony_ci * When an IO is freed, depending on the exchange busy flag, 162562306a36Sopenharmony_ci * move it to the correct list. 162662306a36Sopenharmony_ci */ 162762306a36Sopenharmony_ci if (io->xbusy) { 162862306a36Sopenharmony_ci /* 162962306a36Sopenharmony_ci * add to wait_free list and wait for XRI_ABORTED CQEs to clean 163062306a36Sopenharmony_ci * up 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci INIT_LIST_HEAD(&io->list_entry); 163362306a36Sopenharmony_ci list_add_tail(&io->list_entry, &hw->io_wait_free); 163462306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_WAIT_FREE; 163562306a36Sopenharmony_ci } else { 163662306a36Sopenharmony_ci /* IO not busy, add to free list */ 163762306a36Sopenharmony_ci INIT_LIST_HEAD(&io->list_entry); 163862306a36Sopenharmony_ci list_add_tail(&io->list_entry, &hw->io_free); 163962306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_FREE; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic inline void 164462306a36Sopenharmony_ciefct_hw_io_free_common(struct efct_hw *hw, struct efct_hw_io *io) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci /* initialize IO fields */ 164762306a36Sopenharmony_ci efct_hw_init_free_io(io); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci /* Restore default SGL */ 165062306a36Sopenharmony_ci efct_hw_io_restore_sgl(hw, io); 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_civoid 165462306a36Sopenharmony_ciefct_hw_io_free_internal(struct kref *arg) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci unsigned long flags = 0; 165762306a36Sopenharmony_ci struct efct_hw_io *io = container_of(arg, struct efct_hw_io, ref); 165862306a36Sopenharmony_ci struct efct_hw *hw = io->hw; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* perform common cleanup */ 166162306a36Sopenharmony_ci efct_hw_io_free_common(hw, io); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci spin_lock_irqsave(&hw->io_lock, flags); 166462306a36Sopenharmony_ci /* remove from in-use list */ 166562306a36Sopenharmony_ci if (!list_empty(&io->list_entry) && !list_empty(&hw->io_inuse)) { 166662306a36Sopenharmony_ci list_del_init(&io->list_entry); 166762306a36Sopenharmony_ci efct_hw_io_free_move_correct_list(hw, io); 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->io_lock, flags); 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ciint 167362306a36Sopenharmony_ciefct_hw_io_free(struct efct_hw *hw, struct efct_hw_io *io) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci return kref_put(&io->ref, io->release); 167662306a36Sopenharmony_ci} 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_cistruct efct_hw_io * 167962306a36Sopenharmony_ciefct_hw_io_lookup(struct efct_hw *hw, u32 xri) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci u32 ioindex; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci ioindex = xri - hw->sli.ext[SLI4_RSRC_XRI].base[0]; 168462306a36Sopenharmony_ci return hw->io[ioindex]; 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ciint 168862306a36Sopenharmony_ciefct_hw_io_init_sges(struct efct_hw *hw, struct efct_hw_io *io, 168962306a36Sopenharmony_ci enum efct_hw_io_type type) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct sli4_sge *data = NULL; 169262306a36Sopenharmony_ci u32 i = 0; 169362306a36Sopenharmony_ci u32 skips = 0; 169462306a36Sopenharmony_ci u32 sge_flags = 0; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (!io) { 169762306a36Sopenharmony_ci efc_log_err(hw->os, "bad parameter hw=%p io=%p\n", hw, io); 169862306a36Sopenharmony_ci return -EIO; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* Clear / reset the scatter-gather list */ 170262306a36Sopenharmony_ci io->sgl = &io->def_sgl; 170362306a36Sopenharmony_ci io->sgl_count = io->def_sgl_count; 170462306a36Sopenharmony_ci io->first_data_sge = 0; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci memset(io->sgl->virt, 0, 2 * sizeof(struct sli4_sge)); 170762306a36Sopenharmony_ci io->n_sge = 0; 170862306a36Sopenharmony_ci io->sge_offset = 0; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci io->type = type; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci data = io->sgl->virt; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* 171562306a36Sopenharmony_ci * Some IO types have underlying hardware requirements on the order 171662306a36Sopenharmony_ci * of SGEs. Process all special entries here. 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci switch (type) { 171962306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_WRITE: 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci /* populate host resident XFER_RDY buffer */ 172262306a36Sopenharmony_ci sge_flags = le32_to_cpu(data->dw2_flags); 172362306a36Sopenharmony_ci sge_flags &= (~SLI4_SGE_TYPE_MASK); 172462306a36Sopenharmony_ci sge_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT); 172562306a36Sopenharmony_ci data->buffer_address_high = 172662306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(io->xfer_rdy.phys)); 172762306a36Sopenharmony_ci data->buffer_address_low = 172862306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(io->xfer_rdy.phys)); 172962306a36Sopenharmony_ci data->buffer_length = cpu_to_le32(io->xfer_rdy.size); 173062306a36Sopenharmony_ci data->dw2_flags = cpu_to_le32(sge_flags); 173162306a36Sopenharmony_ci data++; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci skips = EFCT_TARGET_WRITE_SKIPS; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci io->n_sge = 1; 173662306a36Sopenharmony_ci break; 173762306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_READ: 173862306a36Sopenharmony_ci /* 173962306a36Sopenharmony_ci * For FCP_TSEND64, the first 2 entries are SKIP SGE's 174062306a36Sopenharmony_ci */ 174162306a36Sopenharmony_ci skips = EFCT_TARGET_READ_SKIPS; 174262306a36Sopenharmony_ci break; 174362306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_RSP: 174462306a36Sopenharmony_ci /* 174562306a36Sopenharmony_ci * No skips, etc. for FCP_TRSP64 174662306a36Sopenharmony_ci */ 174762306a36Sopenharmony_ci break; 174862306a36Sopenharmony_ci default: 174962306a36Sopenharmony_ci efc_log_err(hw->os, "unsupported IO type %#x\n", type); 175062306a36Sopenharmony_ci return -EIO; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci /* 175462306a36Sopenharmony_ci * Write skip entries 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci for (i = 0; i < skips; i++) { 175762306a36Sopenharmony_ci sge_flags = le32_to_cpu(data->dw2_flags); 175862306a36Sopenharmony_ci sge_flags &= (~SLI4_SGE_TYPE_MASK); 175962306a36Sopenharmony_ci sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT); 176062306a36Sopenharmony_ci data->dw2_flags = cpu_to_le32(sge_flags); 176162306a36Sopenharmony_ci data++; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci io->n_sge += skips; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci /* 176762306a36Sopenharmony_ci * Set last 176862306a36Sopenharmony_ci */ 176962306a36Sopenharmony_ci sge_flags = le32_to_cpu(data->dw2_flags); 177062306a36Sopenharmony_ci sge_flags |= SLI4_SGE_LAST; 177162306a36Sopenharmony_ci data->dw2_flags = cpu_to_le32(sge_flags); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return 0; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ciint 177762306a36Sopenharmony_ciefct_hw_io_add_sge(struct efct_hw *hw, struct efct_hw_io *io, 177862306a36Sopenharmony_ci uintptr_t addr, u32 length) 177962306a36Sopenharmony_ci{ 178062306a36Sopenharmony_ci struct sli4_sge *data = NULL; 178162306a36Sopenharmony_ci u32 sge_flags = 0; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci if (!io || !addr || !length) { 178462306a36Sopenharmony_ci efc_log_err(hw->os, 178562306a36Sopenharmony_ci "bad parameter hw=%p io=%p addr=%lx length=%u\n", 178662306a36Sopenharmony_ci hw, io, addr, length); 178762306a36Sopenharmony_ci return -EIO; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (length > hw->sli.sge_supported_length) { 179162306a36Sopenharmony_ci efc_log_err(hw->os, 179262306a36Sopenharmony_ci "length of SGE %d bigger than allowed %d\n", 179362306a36Sopenharmony_ci length, hw->sli.sge_supported_length); 179462306a36Sopenharmony_ci return -EIO; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci data = io->sgl->virt; 179862306a36Sopenharmony_ci data += io->n_sge; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci sge_flags = le32_to_cpu(data->dw2_flags); 180162306a36Sopenharmony_ci sge_flags &= ~SLI4_SGE_TYPE_MASK; 180262306a36Sopenharmony_ci sge_flags |= SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT; 180362306a36Sopenharmony_ci sge_flags &= ~SLI4_SGE_DATA_OFFSET_MASK; 180462306a36Sopenharmony_ci sge_flags |= SLI4_SGE_DATA_OFFSET_MASK & io->sge_offset; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci data->buffer_address_high = cpu_to_le32(upper_32_bits(addr)); 180762306a36Sopenharmony_ci data->buffer_address_low = cpu_to_le32(lower_32_bits(addr)); 180862306a36Sopenharmony_ci data->buffer_length = cpu_to_le32(length); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci /* 181162306a36Sopenharmony_ci * Always assume this is the last entry and mark as such. 181262306a36Sopenharmony_ci * If this is not the first entry unset the "last SGE" 181362306a36Sopenharmony_ci * indication for the previous entry 181462306a36Sopenharmony_ci */ 181562306a36Sopenharmony_ci sge_flags |= SLI4_SGE_LAST; 181662306a36Sopenharmony_ci data->dw2_flags = cpu_to_le32(sge_flags); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci if (io->n_sge) { 181962306a36Sopenharmony_ci sge_flags = le32_to_cpu(data[-1].dw2_flags); 182062306a36Sopenharmony_ci sge_flags &= ~SLI4_SGE_LAST; 182162306a36Sopenharmony_ci data[-1].dw2_flags = cpu_to_le32(sge_flags); 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* Set first_data_bde if not previously set */ 182562306a36Sopenharmony_ci if (io->first_data_sge == 0) 182662306a36Sopenharmony_ci io->first_data_sge = io->n_sge; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci io->sge_offset += length; 182962306a36Sopenharmony_ci io->n_sge++; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci return 0; 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_civoid 183562306a36Sopenharmony_ciefct_hw_io_abort_all(struct efct_hw *hw) 183662306a36Sopenharmony_ci{ 183762306a36Sopenharmony_ci struct efct_hw_io *io_to_abort = NULL; 183862306a36Sopenharmony_ci struct efct_hw_io *next_io = NULL; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci list_for_each_entry_safe(io_to_abort, next_io, 184162306a36Sopenharmony_ci &hw->io_inuse, list_entry) { 184262306a36Sopenharmony_ci efct_hw_io_abort(hw, io_to_abort, true, NULL, NULL); 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic void 184762306a36Sopenharmony_ciefct_hw_wq_process_abort(void *arg, u8 *cqe, int status) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct efct_hw_io *io = arg; 185062306a36Sopenharmony_ci struct efct_hw *hw = io->hw; 185162306a36Sopenharmony_ci u32 ext = 0; 185262306a36Sopenharmony_ci u32 len = 0; 185362306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci /* 185662306a36Sopenharmony_ci * For IOs that were aborted internally, we may need to issue the 185762306a36Sopenharmony_ci * callback here depending on whether a XRI_ABORTED CQE is expected ot 185862306a36Sopenharmony_ci * not. If the status is Local Reject/No XRI, then 185962306a36Sopenharmony_ci * issue the callback now. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_ci ext = sli_fc_ext_status(&hw->sli, cqe); 186262306a36Sopenharmony_ci if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT && 186362306a36Sopenharmony_ci ext == SLI4_FC_LOCAL_REJECT_NO_XRI && io->done) { 186462306a36Sopenharmony_ci efct_hw_done_t done = io->done; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci io->done = NULL; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* 186962306a36Sopenharmony_ci * Use latched status as this is always saved for an internal 187062306a36Sopenharmony_ci * abort Note: We won't have both a done and abort_done 187162306a36Sopenharmony_ci * function, so don't worry about 187262306a36Sopenharmony_ci * clobbering the len, status and ext fields. 187362306a36Sopenharmony_ci */ 187462306a36Sopenharmony_ci status = io->saved_status; 187562306a36Sopenharmony_ci len = io->saved_len; 187662306a36Sopenharmony_ci ext = io->saved_ext; 187762306a36Sopenharmony_ci io->status_saved = false; 187862306a36Sopenharmony_ci done(io, len, status, ext, io->arg); 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (io->abort_done) { 188262306a36Sopenharmony_ci efct_hw_done_t done = io->abort_done; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci io->abort_done = NULL; 188562306a36Sopenharmony_ci done(io, len, status, ext, io->abort_arg); 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci /* clear abort bit to indicate abort is complete */ 188962306a36Sopenharmony_ci io->abort_in_progress = false; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci /* Free the WQ callback */ 189262306a36Sopenharmony_ci if (io->abort_reqtag == U32_MAX) { 189362306a36Sopenharmony_ci efc_log_err(hw->os, "HW IO already freed\n"); 189462306a36Sopenharmony_ci return; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci wqcb = efct_hw_reqtag_get_instance(hw, io->abort_reqtag); 189862306a36Sopenharmony_ci efct_hw_reqtag_free(hw, wqcb); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* 190162306a36Sopenharmony_ci * Call efct_hw_io_free() because this releases the WQ reservation as 190262306a36Sopenharmony_ci * well as doing the refcount put. Don't duplicate the code here. 190362306a36Sopenharmony_ci */ 190462306a36Sopenharmony_ci (void)efct_hw_io_free(hw, io); 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic void 190862306a36Sopenharmony_ciefct_hw_fill_abort_wqe(struct efct_hw *hw, struct efct_hw_wqe *wqe) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct sli4_abort_wqe *abort = (void *)wqe->wqebuf; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci memset(abort, 0, hw->sli.wqe_size); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG; 191562306a36Sopenharmony_ci abort->ia_ir_byte |= wqe->send_abts ? 0 : 1; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* Suppress ABTS retries */ 191862306a36Sopenharmony_ci abort->ia_ir_byte |= SLI4_ABRT_WQE_IR; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci abort->t_tag = cpu_to_le32(wqe->id); 192162306a36Sopenharmony_ci abort->command = SLI4_WQE_ABORT; 192262306a36Sopenharmony_ci abort->request_tag = cpu_to_le16(wqe->abort_reqtag); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci abort->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); 192762306a36Sopenharmony_ci} 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ciint 193062306a36Sopenharmony_ciefct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort, 193162306a36Sopenharmony_ci bool send_abts, void *cb, void *arg) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 193462306a36Sopenharmony_ci unsigned long flags = 0; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (!io_to_abort) { 193762306a36Sopenharmony_ci efc_log_err(hw->os, "bad parameter hw=%p io=%p\n", 193862306a36Sopenharmony_ci hw, io_to_abort); 193962306a36Sopenharmony_ci return -EIO; 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) { 194362306a36Sopenharmony_ci efc_log_err(hw->os, "cannot send IO abort, HW state=%d\n", 194462306a36Sopenharmony_ci hw->state); 194562306a36Sopenharmony_ci return -EIO; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci /* take a reference on IO being aborted */ 194962306a36Sopenharmony_ci if (kref_get_unless_zero(&io_to_abort->ref) == 0) { 195062306a36Sopenharmony_ci /* command no longer active */ 195162306a36Sopenharmony_ci efc_log_debug(hw->os, 195262306a36Sopenharmony_ci "io not active xri=0x%x tag=0x%x\n", 195362306a36Sopenharmony_ci io_to_abort->indicator, io_to_abort->reqtag); 195462306a36Sopenharmony_ci return -ENOENT; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci /* Must have a valid WQ reference */ 195862306a36Sopenharmony_ci if (!io_to_abort->wq) { 195962306a36Sopenharmony_ci efc_log_debug(hw->os, "io_to_abort xri=0x%x not active on WQ\n", 196062306a36Sopenharmony_ci io_to_abort->indicator); 196162306a36Sopenharmony_ci /* efct_ref_get(): same function */ 196262306a36Sopenharmony_ci kref_put(&io_to_abort->ref, io_to_abort->release); 196362306a36Sopenharmony_ci return -ENOENT; 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* 196762306a36Sopenharmony_ci * Validation checks complete; now check to see if already being 196862306a36Sopenharmony_ci * aborted, if not set the flag. 196962306a36Sopenharmony_ci */ 197062306a36Sopenharmony_ci if (cmpxchg(&io_to_abort->abort_in_progress, false, true)) { 197162306a36Sopenharmony_ci /* efct_ref_get(): same function */ 197262306a36Sopenharmony_ci kref_put(&io_to_abort->ref, io_to_abort->release); 197362306a36Sopenharmony_ci efc_log_debug(hw->os, 197462306a36Sopenharmony_ci "io already being aborted xri=0x%x tag=0x%x\n", 197562306a36Sopenharmony_ci io_to_abort->indicator, io_to_abort->reqtag); 197662306a36Sopenharmony_ci return -EINPROGRESS; 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* 198062306a36Sopenharmony_ci * If we got here, the possibilities are: 198162306a36Sopenharmony_ci * - host owned xri 198262306a36Sopenharmony_ci * - io_to_abort->wq_index != U32_MAX 198362306a36Sopenharmony_ci * - submit ABORT_WQE to same WQ 198462306a36Sopenharmony_ci * - port owned xri: 198562306a36Sopenharmony_ci * - rxri: io_to_abort->wq_index == U32_MAX 198662306a36Sopenharmony_ci * - submit ABORT_WQE to any WQ 198762306a36Sopenharmony_ci * - non-rxri 198862306a36Sopenharmony_ci * - io_to_abort->index != U32_MAX 198962306a36Sopenharmony_ci * - submit ABORT_WQE to same WQ 199062306a36Sopenharmony_ci * - io_to_abort->index == U32_MAX 199162306a36Sopenharmony_ci * - submit ABORT_WQE to any WQ 199262306a36Sopenharmony_ci */ 199362306a36Sopenharmony_ci io_to_abort->abort_done = cb; 199462306a36Sopenharmony_ci io_to_abort->abort_arg = arg; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci /* Allocate a request tag for the abort portion of this IO */ 199762306a36Sopenharmony_ci wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort); 199862306a36Sopenharmony_ci if (!wqcb) { 199962306a36Sopenharmony_ci efc_log_err(hw->os, "can't allocate request tag\n"); 200062306a36Sopenharmony_ci return -ENOSPC; 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci io_to_abort->abort_reqtag = wqcb->instance_index; 200462306a36Sopenharmony_ci io_to_abort->wqe.send_abts = send_abts; 200562306a36Sopenharmony_ci io_to_abort->wqe.id = io_to_abort->indicator; 200662306a36Sopenharmony_ci io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* 200962306a36Sopenharmony_ci * If the wqe is on the pending list, then set this wqe to be 201062306a36Sopenharmony_ci * aborted when the IO's wqe is removed from the list. 201162306a36Sopenharmony_ci */ 201262306a36Sopenharmony_ci if (io_to_abort->wq) { 201362306a36Sopenharmony_ci spin_lock_irqsave(&io_to_abort->wq->queue->lock, flags); 201462306a36Sopenharmony_ci if (io_to_abort->wqe.list_entry.next) { 201562306a36Sopenharmony_ci io_to_abort->wqe.abort_wqe_submit_needed = true; 201662306a36Sopenharmony_ci spin_unlock_irqrestore(&io_to_abort->wq->queue->lock, 201762306a36Sopenharmony_ci flags); 201862306a36Sopenharmony_ci return 0; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci spin_unlock_irqrestore(&io_to_abort->wq->queue->lock, flags); 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci efct_hw_fill_abort_wqe(hw, &io_to_abort->wqe); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci /* ABORT_WQE does not actually utilize an XRI on the Port, 202662306a36Sopenharmony_ci * therefore, keep xbusy as-is to track the exchange's state, 202762306a36Sopenharmony_ci * not the ABORT_WQE's state 202862306a36Sopenharmony_ci */ 202962306a36Sopenharmony_ci if (efct_hw_wq_write(io_to_abort->wq, &io_to_abort->wqe)) { 203062306a36Sopenharmony_ci io_to_abort->abort_in_progress = false; 203162306a36Sopenharmony_ci /* efct_ref_get(): same function */ 203262306a36Sopenharmony_ci kref_put(&io_to_abort->ref, io_to_abort->release); 203362306a36Sopenharmony_ci return -EIO; 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci return 0; 203762306a36Sopenharmony_ci} 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_civoid 204062306a36Sopenharmony_ciefct_hw_reqtag_pool_free(struct efct_hw *hw) 204162306a36Sopenharmony_ci{ 204262306a36Sopenharmony_ci u32 i; 204362306a36Sopenharmony_ci struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool; 204462306a36Sopenharmony_ci struct hw_wq_callback *wqcb = NULL; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci if (reqtag_pool) { 204762306a36Sopenharmony_ci for (i = 0; i < U16_MAX; i++) { 204862306a36Sopenharmony_ci wqcb = reqtag_pool->tags[i]; 204962306a36Sopenharmony_ci if (!wqcb) 205062306a36Sopenharmony_ci continue; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci kfree(wqcb); 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci kfree(reqtag_pool); 205562306a36Sopenharmony_ci hw->wq_reqtag_pool = NULL; 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_cistruct reqtag_pool * 206062306a36Sopenharmony_ciefct_hw_reqtag_pool_alloc(struct efct_hw *hw) 206162306a36Sopenharmony_ci{ 206262306a36Sopenharmony_ci u32 i = 0; 206362306a36Sopenharmony_ci struct reqtag_pool *reqtag_pool; 206462306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci reqtag_pool = kzalloc(sizeof(*reqtag_pool), GFP_KERNEL); 206762306a36Sopenharmony_ci if (!reqtag_pool) 206862306a36Sopenharmony_ci return NULL; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci INIT_LIST_HEAD(&reqtag_pool->freelist); 207162306a36Sopenharmony_ci /* initialize reqtag pool lock */ 207262306a36Sopenharmony_ci spin_lock_init(&reqtag_pool->lock); 207362306a36Sopenharmony_ci for (i = 0; i < U16_MAX; i++) { 207462306a36Sopenharmony_ci wqcb = kmalloc(sizeof(*wqcb), GFP_KERNEL); 207562306a36Sopenharmony_ci if (!wqcb) 207662306a36Sopenharmony_ci break; 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci reqtag_pool->tags[i] = wqcb; 207962306a36Sopenharmony_ci wqcb->instance_index = i; 208062306a36Sopenharmony_ci wqcb->callback = NULL; 208162306a36Sopenharmony_ci wqcb->arg = NULL; 208262306a36Sopenharmony_ci INIT_LIST_HEAD(&wqcb->list_entry); 208362306a36Sopenharmony_ci list_add_tail(&wqcb->list_entry, &reqtag_pool->freelist); 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci return reqtag_pool; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistruct hw_wq_callback * 209062306a36Sopenharmony_ciefct_hw_reqtag_alloc(struct efct_hw *hw, 209162306a36Sopenharmony_ci void (*callback)(void *arg, u8 *cqe, int status), 209262306a36Sopenharmony_ci void *arg) 209362306a36Sopenharmony_ci{ 209462306a36Sopenharmony_ci struct hw_wq_callback *wqcb = NULL; 209562306a36Sopenharmony_ci struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool; 209662306a36Sopenharmony_ci unsigned long flags = 0; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci if (!callback) 209962306a36Sopenharmony_ci return wqcb; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci spin_lock_irqsave(&reqtag_pool->lock, flags); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci if (!list_empty(&reqtag_pool->freelist)) { 210462306a36Sopenharmony_ci wqcb = list_first_entry(&reqtag_pool->freelist, 210562306a36Sopenharmony_ci struct hw_wq_callback, list_entry); 210662306a36Sopenharmony_ci } 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci if (wqcb) { 210962306a36Sopenharmony_ci list_del_init(&wqcb->list_entry); 211062306a36Sopenharmony_ci spin_unlock_irqrestore(&reqtag_pool->lock, flags); 211162306a36Sopenharmony_ci wqcb->callback = callback; 211262306a36Sopenharmony_ci wqcb->arg = arg; 211362306a36Sopenharmony_ci } else { 211462306a36Sopenharmony_ci spin_unlock_irqrestore(&reqtag_pool->lock, flags); 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci return wqcb; 211862306a36Sopenharmony_ci} 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_civoid 212162306a36Sopenharmony_ciefct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci unsigned long flags = 0; 212462306a36Sopenharmony_ci struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (!wqcb->callback) 212762306a36Sopenharmony_ci efc_log_err(hw->os, "WQCB is already freed\n"); 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci spin_lock_irqsave(&reqtag_pool->lock, flags); 213062306a36Sopenharmony_ci wqcb->callback = NULL; 213162306a36Sopenharmony_ci wqcb->arg = NULL; 213262306a36Sopenharmony_ci INIT_LIST_HEAD(&wqcb->list_entry); 213362306a36Sopenharmony_ci list_add(&wqcb->list_entry, &hw->wq_reqtag_pool->freelist); 213462306a36Sopenharmony_ci spin_unlock_irqrestore(&reqtag_pool->lock, flags); 213562306a36Sopenharmony_ci} 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistruct hw_wq_callback * 213862306a36Sopenharmony_ciefct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index) 213962306a36Sopenharmony_ci{ 214062306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci wqcb = hw->wq_reqtag_pool->tags[instance_index]; 214362306a36Sopenharmony_ci if (!wqcb) 214462306a36Sopenharmony_ci efc_log_err(hw->os, "wqcb for instance %d is null\n", 214562306a36Sopenharmony_ci instance_index); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci return wqcb; 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ciint 215162306a36Sopenharmony_ciefct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci int index = -1; 215462306a36Sopenharmony_ci int i = id & (EFCT_HW_Q_HASH_SIZE - 1); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci /* 215762306a36Sopenharmony_ci * Since the hash is always bigger than the maximum number of Qs, then 215862306a36Sopenharmony_ci * we never have to worry about an infinite loop. We will always find 215962306a36Sopenharmony_ci * an unused entry. 216062306a36Sopenharmony_ci */ 216162306a36Sopenharmony_ci do { 216262306a36Sopenharmony_ci if (hash[i].in_use && hash[i].id == id) 216362306a36Sopenharmony_ci index = hash[i].index; 216462306a36Sopenharmony_ci else 216562306a36Sopenharmony_ci i = (i + 1) & (EFCT_HW_Q_HASH_SIZE - 1); 216662306a36Sopenharmony_ci } while (index == -1 && hash[i].in_use); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci return index; 216962306a36Sopenharmony_ci} 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ciint 217262306a36Sopenharmony_ciefct_hw_process(struct efct_hw *hw, u32 vector, 217362306a36Sopenharmony_ci u32 max_isr_time_msec) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci struct hw_eq *eq; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci /* 217862306a36Sopenharmony_ci * The caller should disable interrupts if they wish to prevent us 217962306a36Sopenharmony_ci * from processing during a shutdown. The following states are defined: 218062306a36Sopenharmony_ci * EFCT_HW_STATE_UNINITIALIZED - No queues allocated 218162306a36Sopenharmony_ci * EFCT_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset, 218262306a36Sopenharmony_ci * queues are cleared. 218362306a36Sopenharmony_ci * EFCT_HW_STATE_ACTIVE - Chip and queues are operational 218462306a36Sopenharmony_ci * EFCT_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions 218562306a36Sopenharmony_ci * EFCT_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox 218662306a36Sopenharmony_ci * completions. 218762306a36Sopenharmony_ci */ 218862306a36Sopenharmony_ci if (hw->state == EFCT_HW_STATE_UNINITIALIZED) 218962306a36Sopenharmony_ci return 0; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci /* Get pointer to struct hw_eq */ 219262306a36Sopenharmony_ci eq = hw->hw_eq[vector]; 219362306a36Sopenharmony_ci if (!eq) 219462306a36Sopenharmony_ci return 0; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci eq->use_count++; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci return efct_hw_eq_process(hw, eq, max_isr_time_msec); 219962306a36Sopenharmony_ci} 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ciint 220262306a36Sopenharmony_ciefct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq, 220362306a36Sopenharmony_ci u32 max_isr_time_msec) 220462306a36Sopenharmony_ci{ 220562306a36Sopenharmony_ci u8 eqe[sizeof(struct sli4_eqe)] = { 0 }; 220662306a36Sopenharmony_ci u32 tcheck_count; 220762306a36Sopenharmony_ci u64 tstart; 220862306a36Sopenharmony_ci u64 telapsed; 220962306a36Sopenharmony_ci bool done = false; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS; 221262306a36Sopenharmony_ci tstart = jiffies_to_msecs(jiffies); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci while (!done && !sli_eq_read(&hw->sli, eq->queue, eqe)) { 221562306a36Sopenharmony_ci u16 cq_id = 0; 221662306a36Sopenharmony_ci int rc; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci rc = sli_eq_parse(&hw->sli, eqe, &cq_id); 221962306a36Sopenharmony_ci if (unlikely(rc)) { 222062306a36Sopenharmony_ci if (rc == SLI4_EQE_STATUS_EQ_FULL) { 222162306a36Sopenharmony_ci u32 i; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* 222462306a36Sopenharmony_ci * Received a sentinel EQE indicating the 222562306a36Sopenharmony_ci * EQ is full. Process all CQs 222662306a36Sopenharmony_ci */ 222762306a36Sopenharmony_ci for (i = 0; i < hw->cq_count; i++) 222862306a36Sopenharmony_ci efct_hw_cq_process(hw, hw->hw_cq[i]); 222962306a36Sopenharmony_ci continue; 223062306a36Sopenharmony_ci } else { 223162306a36Sopenharmony_ci return rc; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci } else { 223462306a36Sopenharmony_ci int index; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci index = efct_hw_queue_hash_find(hw->cq_hash, cq_id); 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (likely(index >= 0)) 223962306a36Sopenharmony_ci efct_hw_cq_process(hw, hw->hw_cq[index]); 224062306a36Sopenharmony_ci else 224162306a36Sopenharmony_ci efc_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id); 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (eq->queue->n_posted > eq->queue->posted_limit) 224562306a36Sopenharmony_ci sli_queue_arm(&hw->sli, eq->queue, false); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci if (tcheck_count && (--tcheck_count == 0)) { 224862306a36Sopenharmony_ci tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS; 224962306a36Sopenharmony_ci telapsed = jiffies_to_msecs(jiffies) - tstart; 225062306a36Sopenharmony_ci if (telapsed >= max_isr_time_msec) 225162306a36Sopenharmony_ci done = true; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci sli_queue_eq_arm(&hw->sli, eq->queue, true); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci return 0; 225762306a36Sopenharmony_ci} 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_cistatic int 226062306a36Sopenharmony_ci_efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe) 226162306a36Sopenharmony_ci{ 226262306a36Sopenharmony_ci int queue_rc; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci /* Every so often, set the wqec bit to generate comsummed completions */ 226562306a36Sopenharmony_ci if (wq->wqec_count) 226662306a36Sopenharmony_ci wq->wqec_count--; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci if (wq->wqec_count == 0) { 226962306a36Sopenharmony_ci struct sli4_generic_wqe *genwqe = (void *)wqe->wqebuf; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci genwqe->cmdtype_wqec_byte |= SLI4_GEN_WQE_WQEC; 227262306a36Sopenharmony_ci wq->wqec_count = wq->wqec_set_count; 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci /* Decrement WQ free count */ 227662306a36Sopenharmony_ci wq->free_count--; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci queue_rc = sli_wq_write(&wq->hw->sli, wq->queue, wqe->wqebuf); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci return (queue_rc < 0) ? -EIO : 0; 228162306a36Sopenharmony_ci} 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_cistatic void 228462306a36Sopenharmony_cihw_wq_submit_pending(struct hw_wq *wq, u32 update_free_count) 228562306a36Sopenharmony_ci{ 228662306a36Sopenharmony_ci struct efct_hw_wqe *wqe; 228762306a36Sopenharmony_ci unsigned long flags = 0; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci spin_lock_irqsave(&wq->queue->lock, flags); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci /* Update free count with value passed in */ 229262306a36Sopenharmony_ci wq->free_count += update_free_count; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci while ((wq->free_count > 0) && (!list_empty(&wq->pending_list))) { 229562306a36Sopenharmony_ci wqe = list_first_entry(&wq->pending_list, 229662306a36Sopenharmony_ci struct efct_hw_wqe, list_entry); 229762306a36Sopenharmony_ci list_del_init(&wqe->list_entry); 229862306a36Sopenharmony_ci _efct_hw_wq_write(wq, wqe); 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci if (wqe->abort_wqe_submit_needed) { 230162306a36Sopenharmony_ci wqe->abort_wqe_submit_needed = false; 230262306a36Sopenharmony_ci efct_hw_fill_abort_wqe(wq->hw, wqe); 230362306a36Sopenharmony_ci INIT_LIST_HEAD(&wqe->list_entry); 230462306a36Sopenharmony_ci list_add_tail(&wqe->list_entry, &wq->pending_list); 230562306a36Sopenharmony_ci wq->wq_pending_count++; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci spin_unlock_irqrestore(&wq->queue->lock, flags); 231062306a36Sopenharmony_ci} 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_civoid 231362306a36Sopenharmony_ciefct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq) 231462306a36Sopenharmony_ci{ 231562306a36Sopenharmony_ci u8 cqe[sizeof(struct sli4_mcqe)]; 231662306a36Sopenharmony_ci u16 rid = U16_MAX; 231762306a36Sopenharmony_ci /* completion type */ 231862306a36Sopenharmony_ci enum sli4_qentry ctype; 231962306a36Sopenharmony_ci u32 n_processed = 0; 232062306a36Sopenharmony_ci u32 tstart, telapsed; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci tstart = jiffies_to_msecs(jiffies); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci while (!sli_cq_read(&hw->sli, cq->queue, cqe)) { 232562306a36Sopenharmony_ci int status; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid); 232862306a36Sopenharmony_ci /* 232962306a36Sopenharmony_ci * The sign of status is significant. If status is: 233062306a36Sopenharmony_ci * == 0 : call completed correctly and 233162306a36Sopenharmony_ci * the CQE indicated success 233262306a36Sopenharmony_ci * > 0 : call completed correctly and 233362306a36Sopenharmony_ci * the CQE indicated an error 233462306a36Sopenharmony_ci * < 0 : call failed and no information is available about the 233562306a36Sopenharmony_ci * CQE 233662306a36Sopenharmony_ci */ 233762306a36Sopenharmony_ci if (status < 0) { 233862306a36Sopenharmony_ci if (status == SLI4_MCQE_STATUS_NOT_COMPLETED) 233962306a36Sopenharmony_ci /* 234062306a36Sopenharmony_ci * Notification that an entry was consumed, 234162306a36Sopenharmony_ci * but not completed 234262306a36Sopenharmony_ci */ 234362306a36Sopenharmony_ci continue; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci break; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci switch (ctype) { 234962306a36Sopenharmony_ci case SLI4_QENTRY_ASYNC: 235062306a36Sopenharmony_ci sli_cqe_async(&hw->sli, cqe); 235162306a36Sopenharmony_ci break; 235262306a36Sopenharmony_ci case SLI4_QENTRY_MQ: 235362306a36Sopenharmony_ci /* 235462306a36Sopenharmony_ci * Process MQ entry. Note there is no way to determine 235562306a36Sopenharmony_ci * the MQ_ID from the completion entry. 235662306a36Sopenharmony_ci */ 235762306a36Sopenharmony_ci efct_hw_mq_process(hw, status, hw->mq); 235862306a36Sopenharmony_ci break; 235962306a36Sopenharmony_ci case SLI4_QENTRY_WQ: 236062306a36Sopenharmony_ci efct_hw_wq_process(hw, cq, cqe, status, rid); 236162306a36Sopenharmony_ci break; 236262306a36Sopenharmony_ci case SLI4_QENTRY_WQ_RELEASE: { 236362306a36Sopenharmony_ci u32 wq_id = rid; 236462306a36Sopenharmony_ci int index; 236562306a36Sopenharmony_ci struct hw_wq *wq = NULL; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci index = efct_hw_queue_hash_find(hw->wq_hash, wq_id); 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci if (likely(index >= 0)) { 237062306a36Sopenharmony_ci wq = hw->hw_wq[index]; 237162306a36Sopenharmony_ci } else { 237262306a36Sopenharmony_ci efc_log_err(hw->os, "bad WQ_ID %#06x\n", wq_id); 237362306a36Sopenharmony_ci break; 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci /* Submit any HW IOs that are on the WQ pending list */ 237662306a36Sopenharmony_ci hw_wq_submit_pending(wq, wq->wqec_set_count); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci break; 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci case SLI4_QENTRY_RQ: 238262306a36Sopenharmony_ci efct_hw_rqpair_process_rq(hw, cq, cqe); 238362306a36Sopenharmony_ci break; 238462306a36Sopenharmony_ci case SLI4_QENTRY_XABT: { 238562306a36Sopenharmony_ci efct_hw_xabt_process(hw, cq, cqe, rid); 238662306a36Sopenharmony_ci break; 238762306a36Sopenharmony_ci } 238862306a36Sopenharmony_ci default: 238962306a36Sopenharmony_ci efc_log_debug(hw->os, "unhandled ctype=%#x rid=%#x\n", 239062306a36Sopenharmony_ci ctype, rid); 239162306a36Sopenharmony_ci break; 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci n_processed++; 239562306a36Sopenharmony_ci if (n_processed == cq->queue->proc_limit) 239662306a36Sopenharmony_ci break; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (cq->queue->n_posted >= cq->queue->posted_limit) 239962306a36Sopenharmony_ci sli_queue_arm(&hw->sli, cq->queue, false); 240062306a36Sopenharmony_ci } 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci sli_queue_arm(&hw->sli, cq->queue, true); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci if (n_processed > cq->queue->max_num_processed) 240562306a36Sopenharmony_ci cq->queue->max_num_processed = n_processed; 240662306a36Sopenharmony_ci telapsed = jiffies_to_msecs(jiffies) - tstart; 240762306a36Sopenharmony_ci if (telapsed > cq->queue->max_process_time) 240862306a36Sopenharmony_ci cq->queue->max_process_time = telapsed; 240962306a36Sopenharmony_ci} 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_civoid 241262306a36Sopenharmony_ciefct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq, 241362306a36Sopenharmony_ci u8 *cqe, int status, u16 rid) 241462306a36Sopenharmony_ci{ 241562306a36Sopenharmony_ci struct hw_wq_callback *wqcb; 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci if (rid == EFCT_HW_REQUE_XRI_REGTAG) { 241862306a36Sopenharmony_ci if (status) 241962306a36Sopenharmony_ci efc_log_err(hw->os, "reque xri failed, status = %d\n", 242062306a36Sopenharmony_ci status); 242162306a36Sopenharmony_ci return; 242262306a36Sopenharmony_ci } 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci wqcb = efct_hw_reqtag_get_instance(hw, rid); 242562306a36Sopenharmony_ci if (!wqcb) { 242662306a36Sopenharmony_ci efc_log_err(hw->os, "invalid request tag: x%x\n", rid); 242762306a36Sopenharmony_ci return; 242862306a36Sopenharmony_ci } 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (!wqcb->callback) { 243162306a36Sopenharmony_ci efc_log_err(hw->os, "wqcb callback is NULL\n"); 243262306a36Sopenharmony_ci return; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci (*wqcb->callback)(wqcb->arg, cqe, status); 243662306a36Sopenharmony_ci} 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_civoid 243962306a36Sopenharmony_ciefct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq, 244062306a36Sopenharmony_ci u8 *cqe, u16 rid) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci /* search IOs wait free list */ 244362306a36Sopenharmony_ci struct efct_hw_io *io = NULL; 244462306a36Sopenharmony_ci unsigned long flags = 0; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci io = efct_hw_io_lookup(hw, rid); 244762306a36Sopenharmony_ci if (!io) { 244862306a36Sopenharmony_ci /* IO lookup failure should never happen */ 244962306a36Sopenharmony_ci efc_log_err(hw->os, "xabt io lookup failed rid=%#x\n", rid); 245062306a36Sopenharmony_ci return; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci if (!io->xbusy) 245462306a36Sopenharmony_ci efc_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid); 245562306a36Sopenharmony_ci else 245662306a36Sopenharmony_ci /* mark IO as no longer busy */ 245762306a36Sopenharmony_ci io->xbusy = false; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* 246062306a36Sopenharmony_ci * For IOs that were aborted internally, we need to issue any pending 246162306a36Sopenharmony_ci * callback here. 246262306a36Sopenharmony_ci */ 246362306a36Sopenharmony_ci if (io->done) { 246462306a36Sopenharmony_ci efct_hw_done_t done = io->done; 246562306a36Sopenharmony_ci void *arg = io->arg; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci /* 246862306a36Sopenharmony_ci * Use latched status as this is always saved for an internal 246962306a36Sopenharmony_ci * abort 247062306a36Sopenharmony_ci */ 247162306a36Sopenharmony_ci int status = io->saved_status; 247262306a36Sopenharmony_ci u32 len = io->saved_len; 247362306a36Sopenharmony_ci u32 ext = io->saved_ext; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci io->done = NULL; 247662306a36Sopenharmony_ci io->status_saved = false; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci done(io, len, status, ext, arg); 247962306a36Sopenharmony_ci } 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci spin_lock_irqsave(&hw->io_lock, flags); 248262306a36Sopenharmony_ci if (io->state == EFCT_HW_IO_STATE_INUSE || 248362306a36Sopenharmony_ci io->state == EFCT_HW_IO_STATE_WAIT_FREE) { 248462306a36Sopenharmony_ci /* if on wait_free list, caller has already freed IO; 248562306a36Sopenharmony_ci * remove from wait_free list and add to free list. 248662306a36Sopenharmony_ci * if on in-use list, already marked as no longer busy; 248762306a36Sopenharmony_ci * just leave there and wait for caller to free. 248862306a36Sopenharmony_ci */ 248962306a36Sopenharmony_ci if (io->state == EFCT_HW_IO_STATE_WAIT_FREE) { 249062306a36Sopenharmony_ci io->state = EFCT_HW_IO_STATE_FREE; 249162306a36Sopenharmony_ci list_del_init(&io->list_entry); 249262306a36Sopenharmony_ci efct_hw_io_free_move_correct_list(hw, io); 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci } 249562306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->io_lock, flags); 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cistatic int 249962306a36Sopenharmony_ciefct_hw_flush(struct efct_hw *hw) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci u32 i = 0; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci /* Process any remaining completions */ 250462306a36Sopenharmony_ci for (i = 0; i < hw->eq_count; i++) 250562306a36Sopenharmony_ci efct_hw_process(hw, i, ~0); 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci return 0; 250862306a36Sopenharmony_ci} 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ciint 251162306a36Sopenharmony_ciefct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe) 251262306a36Sopenharmony_ci{ 251362306a36Sopenharmony_ci int rc = 0; 251462306a36Sopenharmony_ci unsigned long flags = 0; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci spin_lock_irqsave(&wq->queue->lock, flags); 251762306a36Sopenharmony_ci if (list_empty(&wq->pending_list)) { 251862306a36Sopenharmony_ci if (wq->free_count > 0) { 251962306a36Sopenharmony_ci rc = _efct_hw_wq_write(wq, wqe); 252062306a36Sopenharmony_ci } else { 252162306a36Sopenharmony_ci INIT_LIST_HEAD(&wqe->list_entry); 252262306a36Sopenharmony_ci list_add_tail(&wqe->list_entry, &wq->pending_list); 252362306a36Sopenharmony_ci wq->wq_pending_count++; 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci spin_unlock_irqrestore(&wq->queue->lock, flags); 252762306a36Sopenharmony_ci return rc; 252862306a36Sopenharmony_ci } 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci INIT_LIST_HEAD(&wqe->list_entry); 253162306a36Sopenharmony_ci list_add_tail(&wqe->list_entry, &wq->pending_list); 253262306a36Sopenharmony_ci wq->wq_pending_count++; 253362306a36Sopenharmony_ci while (wq->free_count > 0) { 253462306a36Sopenharmony_ci wqe = list_first_entry(&wq->pending_list, struct efct_hw_wqe, 253562306a36Sopenharmony_ci list_entry); 253662306a36Sopenharmony_ci if (!wqe) 253762306a36Sopenharmony_ci break; 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci list_del_init(&wqe->list_entry); 254062306a36Sopenharmony_ci rc = _efct_hw_wq_write(wq, wqe); 254162306a36Sopenharmony_ci if (rc) 254262306a36Sopenharmony_ci break; 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci if (wqe->abort_wqe_submit_needed) { 254562306a36Sopenharmony_ci wqe->abort_wqe_submit_needed = false; 254662306a36Sopenharmony_ci efct_hw_fill_abort_wqe(wq->hw, wqe); 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci INIT_LIST_HEAD(&wqe->list_entry); 254962306a36Sopenharmony_ci list_add_tail(&wqe->list_entry, &wq->pending_list); 255062306a36Sopenharmony_ci wq->wq_pending_count++; 255162306a36Sopenharmony_ci } 255262306a36Sopenharmony_ci } 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci spin_unlock_irqrestore(&wq->queue->lock, flags); 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci return rc; 255762306a36Sopenharmony_ci} 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ciint 256062306a36Sopenharmony_ciefct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci struct efct *efct = efc->base; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci return efct_hw_bls_send(efct, type, bls, NULL, NULL); 256562306a36Sopenharmony_ci} 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ciint 256862306a36Sopenharmony_ciefct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params, 256962306a36Sopenharmony_ci void *cb, void *arg) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci struct efct_hw *hw = &efct->hw; 257262306a36Sopenharmony_ci struct efct_hw_io *hio; 257362306a36Sopenharmony_ci struct sli_bls_payload bls; 257462306a36Sopenharmony_ci int rc; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) { 257762306a36Sopenharmony_ci efc_log_err(hw->os, 257862306a36Sopenharmony_ci "cannot send BLS, HW state=%d\n", hw->state); 257962306a36Sopenharmony_ci return -EIO; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci hio = efct_hw_io_alloc(hw); 258362306a36Sopenharmony_ci if (!hio) { 258462306a36Sopenharmony_ci efc_log_err(hw->os, "HIO allocation failed\n"); 258562306a36Sopenharmony_ci return -EIO; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci hio->done = cb; 258962306a36Sopenharmony_ci hio->arg = arg; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci bls_params->xri = hio->indicator; 259262306a36Sopenharmony_ci bls_params->tag = hio->reqtag; 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci if (type == FC_RCTL_BA_ACC) { 259562306a36Sopenharmony_ci hio->type = EFCT_HW_BLS_ACC; 259662306a36Sopenharmony_ci bls.type = SLI4_SLI_BLS_ACC; 259762306a36Sopenharmony_ci memcpy(&bls.u.acc, bls_params->payload, sizeof(bls.u.acc)); 259862306a36Sopenharmony_ci } else { 259962306a36Sopenharmony_ci hio->type = EFCT_HW_BLS_RJT; 260062306a36Sopenharmony_ci bls.type = SLI4_SLI_BLS_RJT; 260162306a36Sopenharmony_ci memcpy(&bls.u.rjt, bls_params->payload, sizeof(bls.u.rjt)); 260262306a36Sopenharmony_ci } 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci bls.ox_id = cpu_to_le16(bls_params->ox_id); 260562306a36Sopenharmony_ci bls.rx_id = cpu_to_le16(bls_params->rx_id); 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci if (sli_xmit_bls_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, 260862306a36Sopenharmony_ci &bls, bls_params)) { 260962306a36Sopenharmony_ci efc_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n"); 261062306a36Sopenharmony_ci return -EIO; 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci hio->xbusy = true; 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci /* 261662306a36Sopenharmony_ci * Add IO to active io wqe list before submitting, in case the 261762306a36Sopenharmony_ci * wcqe processing preempts this thread. 261862306a36Sopenharmony_ci */ 261962306a36Sopenharmony_ci hio->wq->use_count++; 262062306a36Sopenharmony_ci rc = efct_hw_wq_write(hio->wq, &hio->wqe); 262162306a36Sopenharmony_ci if (rc >= 0) { 262262306a36Sopenharmony_ci /* non-negative return is success */ 262362306a36Sopenharmony_ci rc = 0; 262462306a36Sopenharmony_ci } else { 262562306a36Sopenharmony_ci /* failed to write wqe, remove from active wqe list */ 262662306a36Sopenharmony_ci efc_log_err(hw->os, 262762306a36Sopenharmony_ci "sli_queue_write failed: %d\n", rc); 262862306a36Sopenharmony_ci hio->xbusy = false; 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci return rc; 263262306a36Sopenharmony_ci} 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_cistatic int 263562306a36Sopenharmony_ciefct_els_ssrs_send_cb(struct efct_hw_io *hio, u32 length, int status, 263662306a36Sopenharmony_ci u32 ext_status, void *arg) 263762306a36Sopenharmony_ci{ 263862306a36Sopenharmony_ci struct efc_disc_io *io = arg; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci efc_disc_io_complete(io, length, status, ext_status); 264162306a36Sopenharmony_ci return 0; 264262306a36Sopenharmony_ci} 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_cistatic inline void 264562306a36Sopenharmony_ciefct_fill_els_params(struct efc_disc_io *io, struct sli_els_params *params) 264662306a36Sopenharmony_ci{ 264762306a36Sopenharmony_ci u8 *cmd = io->req.virt; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci params->cmd = *cmd; 265062306a36Sopenharmony_ci params->s_id = io->s_id; 265162306a36Sopenharmony_ci params->d_id = io->d_id; 265262306a36Sopenharmony_ci params->ox_id = io->iparam.els.ox_id; 265362306a36Sopenharmony_ci params->rpi = io->rpi; 265462306a36Sopenharmony_ci params->vpi = io->vpi; 265562306a36Sopenharmony_ci params->rpi_registered = io->rpi_registered; 265662306a36Sopenharmony_ci params->xmit_len = io->xmit_len; 265762306a36Sopenharmony_ci params->rsp_len = io->rsp_len; 265862306a36Sopenharmony_ci params->timeout = io->iparam.els.timeout; 265962306a36Sopenharmony_ci} 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic inline void 266262306a36Sopenharmony_ciefct_fill_ct_params(struct efc_disc_io *io, struct sli_ct_params *params) 266362306a36Sopenharmony_ci{ 266462306a36Sopenharmony_ci params->r_ctl = io->iparam.ct.r_ctl; 266562306a36Sopenharmony_ci params->type = io->iparam.ct.type; 266662306a36Sopenharmony_ci params->df_ctl = io->iparam.ct.df_ctl; 266762306a36Sopenharmony_ci params->d_id = io->d_id; 266862306a36Sopenharmony_ci params->ox_id = io->iparam.ct.ox_id; 266962306a36Sopenharmony_ci params->rpi = io->rpi; 267062306a36Sopenharmony_ci params->vpi = io->vpi; 267162306a36Sopenharmony_ci params->rpi_registered = io->rpi_registered; 267262306a36Sopenharmony_ci params->xmit_len = io->xmit_len; 267362306a36Sopenharmony_ci params->rsp_len = io->rsp_len; 267462306a36Sopenharmony_ci params->timeout = io->iparam.ct.timeout; 267562306a36Sopenharmony_ci} 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci/** 267862306a36Sopenharmony_ci * efct_els_hw_srrs_send() - Send a single request and response cmd. 267962306a36Sopenharmony_ci * @efc: efc library structure 268062306a36Sopenharmony_ci * @io: Discovery IO used to hold els and ct cmd context. 268162306a36Sopenharmony_ci * 268262306a36Sopenharmony_ci * This routine supports communication sequences consisting of a single 268362306a36Sopenharmony_ci * request and single response between two endpoints. Examples include: 268462306a36Sopenharmony_ci * - Sending an ELS request. 268562306a36Sopenharmony_ci * - Sending an ELS response - To send an ELS response, the caller must provide 268662306a36Sopenharmony_ci * the OX_ID from the received request. 268762306a36Sopenharmony_ci * - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request, 268862306a36Sopenharmony_ci * the caller must provide the R_CTL, TYPE, and DF_CTL 268962306a36Sopenharmony_ci * values to place in the FC frame header. 269062306a36Sopenharmony_ci * 269162306a36Sopenharmony_ci * Return: Status of the request. 269262306a36Sopenharmony_ci */ 269362306a36Sopenharmony_ciint 269462306a36Sopenharmony_ciefct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io) 269562306a36Sopenharmony_ci{ 269662306a36Sopenharmony_ci struct efct *efct = efc->base; 269762306a36Sopenharmony_ci struct efct_hw_io *hio; 269862306a36Sopenharmony_ci struct efct_hw *hw = &efct->hw; 269962306a36Sopenharmony_ci struct efc_dma *send = &io->req; 270062306a36Sopenharmony_ci struct efc_dma *receive = &io->rsp; 270162306a36Sopenharmony_ci struct sli4_sge *sge = NULL; 270262306a36Sopenharmony_ci int rc = 0; 270362306a36Sopenharmony_ci u32 len = io->xmit_len; 270462306a36Sopenharmony_ci u32 sge0_flags; 270562306a36Sopenharmony_ci u32 sge1_flags; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci hio = efct_hw_io_alloc(hw); 270862306a36Sopenharmony_ci if (!hio) { 270962306a36Sopenharmony_ci pr_err("HIO alloc failed\n"); 271062306a36Sopenharmony_ci return -EIO; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) { 271462306a36Sopenharmony_ci efc_log_debug(hw->os, 271562306a36Sopenharmony_ci "cannot send SRRS, HW state=%d\n", hw->state); 271662306a36Sopenharmony_ci return -EIO; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci hio->done = efct_els_ssrs_send_cb; 272062306a36Sopenharmony_ci hio->arg = io; 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci sge = hio->sgl->virt; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci /* clear both SGE */ 272562306a36Sopenharmony_ci memset(hio->sgl->virt, 0, 2 * sizeof(struct sli4_sge)); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci sge0_flags = le32_to_cpu(sge[0].dw2_flags); 272862306a36Sopenharmony_ci sge1_flags = le32_to_cpu(sge[1].dw2_flags); 272962306a36Sopenharmony_ci if (send->size) { 273062306a36Sopenharmony_ci sge[0].buffer_address_high = 273162306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(send->phys)); 273262306a36Sopenharmony_ci sge[0].buffer_address_low = 273362306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(send->phys)); 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci sge0_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT); 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci sge[0].buffer_length = cpu_to_le32(len); 273862306a36Sopenharmony_ci } 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci if (io->io_type == EFC_DISC_IO_ELS_REQ || 274162306a36Sopenharmony_ci io->io_type == EFC_DISC_IO_CT_REQ) { 274262306a36Sopenharmony_ci sge[1].buffer_address_high = 274362306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(receive->phys)); 274462306a36Sopenharmony_ci sge[1].buffer_address_low = 274562306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(receive->phys)); 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci sge1_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT); 274862306a36Sopenharmony_ci sge1_flags |= SLI4_SGE_LAST; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci sge[1].buffer_length = cpu_to_le32(receive->size); 275162306a36Sopenharmony_ci } else { 275262306a36Sopenharmony_ci sge0_flags |= SLI4_SGE_LAST; 275362306a36Sopenharmony_ci } 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci sge[0].dw2_flags = cpu_to_le32(sge0_flags); 275662306a36Sopenharmony_ci sge[1].dw2_flags = cpu_to_le32(sge1_flags); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci switch (io->io_type) { 275962306a36Sopenharmony_ci case EFC_DISC_IO_ELS_REQ: { 276062306a36Sopenharmony_ci struct sli_els_params els_params; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci hio->type = EFCT_HW_ELS_REQ; 276362306a36Sopenharmony_ci efct_fill_els_params(io, &els_params); 276462306a36Sopenharmony_ci els_params.xri = hio->indicator; 276562306a36Sopenharmony_ci els_params.tag = hio->reqtag; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci if (sli_els_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, 276862306a36Sopenharmony_ci &els_params)) { 276962306a36Sopenharmony_ci efc_log_err(hw->os, "REQ WQE error\n"); 277062306a36Sopenharmony_ci rc = -EIO; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci break; 277362306a36Sopenharmony_ci } 277462306a36Sopenharmony_ci case EFC_DISC_IO_ELS_RESP: { 277562306a36Sopenharmony_ci struct sli_els_params els_params; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci hio->type = EFCT_HW_ELS_RSP; 277862306a36Sopenharmony_ci efct_fill_els_params(io, &els_params); 277962306a36Sopenharmony_ci els_params.xri = hio->indicator; 278062306a36Sopenharmony_ci els_params.tag = hio->reqtag; 278162306a36Sopenharmony_ci if (sli_xmit_els_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, send, 278262306a36Sopenharmony_ci &els_params)){ 278362306a36Sopenharmony_ci efc_log_err(hw->os, "RSP WQE error\n"); 278462306a36Sopenharmony_ci rc = -EIO; 278562306a36Sopenharmony_ci } 278662306a36Sopenharmony_ci break; 278762306a36Sopenharmony_ci } 278862306a36Sopenharmony_ci case EFC_DISC_IO_CT_REQ: { 278962306a36Sopenharmony_ci struct sli_ct_params ct_params; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci hio->type = EFCT_HW_FC_CT; 279262306a36Sopenharmony_ci efct_fill_ct_params(io, &ct_params); 279362306a36Sopenharmony_ci ct_params.xri = hio->indicator; 279462306a36Sopenharmony_ci ct_params.tag = hio->reqtag; 279562306a36Sopenharmony_ci if (sli_gen_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, 279662306a36Sopenharmony_ci &ct_params)){ 279762306a36Sopenharmony_ci efc_log_err(hw->os, "GEN WQE error\n"); 279862306a36Sopenharmony_ci rc = -EIO; 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci break; 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci case EFC_DISC_IO_CT_RESP: { 280362306a36Sopenharmony_ci struct sli_ct_params ct_params; 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci hio->type = EFCT_HW_FC_CT_RSP; 280662306a36Sopenharmony_ci efct_fill_ct_params(io, &ct_params); 280762306a36Sopenharmony_ci ct_params.xri = hio->indicator; 280862306a36Sopenharmony_ci ct_params.tag = hio->reqtag; 280962306a36Sopenharmony_ci if (sli_xmit_sequence64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, 281062306a36Sopenharmony_ci &ct_params)){ 281162306a36Sopenharmony_ci efc_log_err(hw->os, "XMIT SEQ WQE error\n"); 281262306a36Sopenharmony_ci rc = -EIO; 281362306a36Sopenharmony_ci } 281462306a36Sopenharmony_ci break; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci default: 281762306a36Sopenharmony_ci efc_log_err(hw->os, "bad SRRS type %#x\n", io->io_type); 281862306a36Sopenharmony_ci rc = -EIO; 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci if (rc == 0) { 282262306a36Sopenharmony_ci hio->xbusy = true; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci /* 282562306a36Sopenharmony_ci * Add IO to active io wqe list before submitting, in case the 282662306a36Sopenharmony_ci * wcqe processing preempts this thread. 282762306a36Sopenharmony_ci */ 282862306a36Sopenharmony_ci hio->wq->use_count++; 282962306a36Sopenharmony_ci rc = efct_hw_wq_write(hio->wq, &hio->wqe); 283062306a36Sopenharmony_ci if (rc >= 0) { 283162306a36Sopenharmony_ci /* non-negative return is success */ 283262306a36Sopenharmony_ci rc = 0; 283362306a36Sopenharmony_ci } else { 283462306a36Sopenharmony_ci /* failed to write wqe, remove from active wqe list */ 283562306a36Sopenharmony_ci efc_log_err(hw->os, 283662306a36Sopenharmony_ci "sli_queue_write failed: %d\n", rc); 283762306a36Sopenharmony_ci hio->xbusy = false; 283862306a36Sopenharmony_ci } 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci return rc; 284262306a36Sopenharmony_ci} 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ciint 284562306a36Sopenharmony_ciefct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type, 284662306a36Sopenharmony_ci struct efct_hw_io *io, union efct_hw_io_param_u *iparam, 284762306a36Sopenharmony_ci void *cb, void *arg) 284862306a36Sopenharmony_ci{ 284962306a36Sopenharmony_ci int rc = 0; 285062306a36Sopenharmony_ci bool send_wqe = true; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci if (!io) { 285362306a36Sopenharmony_ci pr_err("bad parm hw=%p io=%p\n", hw, io); 285462306a36Sopenharmony_ci return -EIO; 285562306a36Sopenharmony_ci } 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) { 285862306a36Sopenharmony_ci efc_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state); 285962306a36Sopenharmony_ci return -EIO; 286062306a36Sopenharmony_ci } 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci /* 286362306a36Sopenharmony_ci * Save state needed during later stages 286462306a36Sopenharmony_ci */ 286562306a36Sopenharmony_ci io->type = type; 286662306a36Sopenharmony_ci io->done = cb; 286762306a36Sopenharmony_ci io->arg = arg; 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci /* 287062306a36Sopenharmony_ci * Format the work queue entry used to send the IO 287162306a36Sopenharmony_ci */ 287262306a36Sopenharmony_ci switch (type) { 287362306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_WRITE: { 287462306a36Sopenharmony_ci u16 *flags = &iparam->fcp_tgt.flags; 287562306a36Sopenharmony_ci struct fcp_txrdy *xfer = io->xfer_rdy.virt; 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci /* 287862306a36Sopenharmony_ci * Fill in the XFER_RDY for IF_TYPE 0 devices 287962306a36Sopenharmony_ci */ 288062306a36Sopenharmony_ci xfer->ft_data_ro = cpu_to_be32(iparam->fcp_tgt.offset); 288162306a36Sopenharmony_ci xfer->ft_burst_len = cpu_to_be32(iparam->fcp_tgt.xmit_len); 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci if (io->xbusy) 288462306a36Sopenharmony_ci *flags |= SLI4_IO_CONTINUATION; 288562306a36Sopenharmony_ci else 288662306a36Sopenharmony_ci *flags &= ~SLI4_IO_CONTINUATION; 288762306a36Sopenharmony_ci iparam->fcp_tgt.xri = io->indicator; 288862306a36Sopenharmony_ci iparam->fcp_tgt.tag = io->reqtag; 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, 289162306a36Sopenharmony_ci &io->def_sgl, io->first_data_sge, 289262306a36Sopenharmony_ci SLI4_CQ_DEFAULT, 289362306a36Sopenharmony_ci 0, 0, &iparam->fcp_tgt)) { 289462306a36Sopenharmony_ci efc_log_err(hw->os, "TRECEIVE WQE error\n"); 289562306a36Sopenharmony_ci rc = -EIO; 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci break; 289862306a36Sopenharmony_ci } 289962306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_READ: { 290062306a36Sopenharmony_ci u16 *flags = &iparam->fcp_tgt.flags; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci if (io->xbusy) 290362306a36Sopenharmony_ci *flags |= SLI4_IO_CONTINUATION; 290462306a36Sopenharmony_ci else 290562306a36Sopenharmony_ci *flags &= ~SLI4_IO_CONTINUATION; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci iparam->fcp_tgt.xri = io->indicator; 290862306a36Sopenharmony_ci iparam->fcp_tgt.tag = io->reqtag; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, 291162306a36Sopenharmony_ci &io->def_sgl, io->first_data_sge, 291262306a36Sopenharmony_ci SLI4_CQ_DEFAULT, 291362306a36Sopenharmony_ci 0, 0, &iparam->fcp_tgt)) { 291462306a36Sopenharmony_ci efc_log_err(hw->os, "TSEND WQE error\n"); 291562306a36Sopenharmony_ci rc = -EIO; 291662306a36Sopenharmony_ci } 291762306a36Sopenharmony_ci break; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci case EFCT_HW_IO_TARGET_RSP: { 292062306a36Sopenharmony_ci u16 *flags = &iparam->fcp_tgt.flags; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci if (io->xbusy) 292362306a36Sopenharmony_ci *flags |= SLI4_IO_CONTINUATION; 292462306a36Sopenharmony_ci else 292562306a36Sopenharmony_ci *flags &= ~SLI4_IO_CONTINUATION; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci iparam->fcp_tgt.xri = io->indicator; 292862306a36Sopenharmony_ci iparam->fcp_tgt.tag = io->reqtag; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, 293162306a36Sopenharmony_ci &io->def_sgl, SLI4_CQ_DEFAULT, 293262306a36Sopenharmony_ci 0, &iparam->fcp_tgt)) { 293362306a36Sopenharmony_ci efc_log_err(hw->os, "TRSP WQE error\n"); 293462306a36Sopenharmony_ci rc = -EIO; 293562306a36Sopenharmony_ci } 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci break; 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci default: 294062306a36Sopenharmony_ci efc_log_err(hw->os, "unsupported IO type %#x\n", type); 294162306a36Sopenharmony_ci rc = -EIO; 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci if (send_wqe && rc == 0) { 294562306a36Sopenharmony_ci io->xbusy = true; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci /* 294862306a36Sopenharmony_ci * Add IO to active io wqe list before submitting, in case the 294962306a36Sopenharmony_ci * wcqe processing preempts this thread. 295062306a36Sopenharmony_ci */ 295162306a36Sopenharmony_ci hw->tcmd_wq_submit[io->wq->instance]++; 295262306a36Sopenharmony_ci io->wq->use_count++; 295362306a36Sopenharmony_ci rc = efct_hw_wq_write(io->wq, &io->wqe); 295462306a36Sopenharmony_ci if (rc >= 0) { 295562306a36Sopenharmony_ci /* non-negative return is success */ 295662306a36Sopenharmony_ci rc = 0; 295762306a36Sopenharmony_ci } else { 295862306a36Sopenharmony_ci /* failed to write wqe, remove from active wqe list */ 295962306a36Sopenharmony_ci efc_log_err(hw->os, 296062306a36Sopenharmony_ci "sli_queue_write failed: %d\n", rc); 296162306a36Sopenharmony_ci io->xbusy = false; 296262306a36Sopenharmony_ci } 296362306a36Sopenharmony_ci } 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci return rc; 296662306a36Sopenharmony_ci} 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ciint 296962306a36Sopenharmony_ciefct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr, 297062306a36Sopenharmony_ci u8 sof, u8 eof, struct efc_dma *payload, 297162306a36Sopenharmony_ci struct efct_hw_send_frame_context *ctx, 297262306a36Sopenharmony_ci void (*callback)(void *arg, u8 *cqe, int status), 297362306a36Sopenharmony_ci void *arg) 297462306a36Sopenharmony_ci{ 297562306a36Sopenharmony_ci int rc; 297662306a36Sopenharmony_ci struct efct_hw_wqe *wqe; 297762306a36Sopenharmony_ci u32 xri; 297862306a36Sopenharmony_ci struct hw_wq *wq; 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci wqe = &ctx->wqe; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci /* populate the callback object */ 298362306a36Sopenharmony_ci ctx->hw = hw; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci /* Fetch and populate request tag */ 298662306a36Sopenharmony_ci ctx->wqcb = efct_hw_reqtag_alloc(hw, callback, arg); 298762306a36Sopenharmony_ci if (!ctx->wqcb) { 298862306a36Sopenharmony_ci efc_log_err(hw->os, "can't allocate request tag\n"); 298962306a36Sopenharmony_ci return -ENOSPC; 299062306a36Sopenharmony_ci } 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci wq = hw->hw_wq[0]; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci /* Set XRI and RX_ID in the header based on which WQ, and which 299562306a36Sopenharmony_ci * send_frame_io we are using 299662306a36Sopenharmony_ci */ 299762306a36Sopenharmony_ci xri = wq->send_frame_io->indicator; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci /* Build the send frame WQE */ 300062306a36Sopenharmony_ci rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, 300162306a36Sopenharmony_ci sof, eof, (u32 *)hdr, payload, payload->len, 300262306a36Sopenharmony_ci EFCT_HW_SEND_FRAME_TIMEOUT, xri, 300362306a36Sopenharmony_ci ctx->wqcb->instance_index); 300462306a36Sopenharmony_ci if (rc) { 300562306a36Sopenharmony_ci efc_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc); 300662306a36Sopenharmony_ci return -EIO; 300762306a36Sopenharmony_ci } 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci /* Write to WQ */ 301062306a36Sopenharmony_ci rc = efct_hw_wq_write(wq, wqe); 301162306a36Sopenharmony_ci if (rc) { 301262306a36Sopenharmony_ci efc_log_err(hw->os, "efct_hw_wq_write failed: %d\n", rc); 301362306a36Sopenharmony_ci return -EIO; 301462306a36Sopenharmony_ci } 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci wq->use_count++; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci return 0; 301962306a36Sopenharmony_ci} 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_cistatic int 302262306a36Sopenharmony_ciefct_hw_cb_link_stat(struct efct_hw *hw, int status, 302362306a36Sopenharmony_ci u8 *mqe, void *arg) 302462306a36Sopenharmony_ci{ 302562306a36Sopenharmony_ci struct sli4_cmd_read_link_stats *mbox_rsp; 302662306a36Sopenharmony_ci struct efct_hw_link_stat_cb_arg *cb_arg = arg; 302762306a36Sopenharmony_ci struct efct_hw_link_stat_counts counts[EFCT_HW_LINK_STAT_MAX]; 302862306a36Sopenharmony_ci u32 num_counters, i; 302962306a36Sopenharmony_ci u32 mbox_rsp_flags = 0; 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci mbox_rsp = (struct sli4_cmd_read_link_stats *)mqe; 303262306a36Sopenharmony_ci mbox_rsp_flags = le32_to_cpu(mbox_rsp->dw1_flags); 303362306a36Sopenharmony_ci num_counters = (mbox_rsp_flags & SLI4_READ_LNKSTAT_GEC) ? 20 : 13; 303462306a36Sopenharmony_ci memset(counts, 0, sizeof(struct efct_hw_link_stat_counts) * 303562306a36Sopenharmony_ci EFCT_HW_LINK_STAT_MAX); 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci /* Fill overflow counts, mask starts from SLI4_READ_LNKSTAT_W02OF*/ 303862306a36Sopenharmony_ci for (i = 0; i < EFCT_HW_LINK_STAT_MAX; i++) 303962306a36Sopenharmony_ci counts[i].overflow = (mbox_rsp_flags & (1 << (i + 2))); 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter = 304262306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->linkfail_errcnt); 304362306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter = 304462306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->losssync_errcnt); 304562306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter = 304662306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->losssignal_errcnt); 304762306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter = 304862306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->primseq_errcnt); 304962306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter = 305062306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->inval_txword_errcnt); 305162306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_CRC_COUNT].counter = 305262306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->crc_errcnt); 305362306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter = 305462306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->primseq_eventtimeout_cnt); 305562306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter = 305662306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->elastic_bufoverrun_errcnt); 305762306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter = 305862306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->arbit_fc_al_timeout_cnt); 305962306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter = 306062306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->adv_rx_buftor_to_buf_credit); 306162306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter = 306262306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->curr_rx_buf_to_buf_credit); 306362306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter = 306462306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->adv_tx_buf_to_buf_credit); 306562306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter = 306662306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->curr_tx_buf_to_buf_credit); 306762306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_EOFA_COUNT].counter = 306862306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_eofa_cnt); 306962306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter = 307062306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_eofdti_cnt); 307162306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_EOFNI_COUNT].counter = 307262306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_eofni_cnt); 307362306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_SOFF_COUNT].counter = 307462306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_soff_cnt); 307562306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter = 307662306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_dropped_no_aer_cnt); 307762306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter = 307862306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_dropped_no_avail_rpi_rescnt); 307962306a36Sopenharmony_ci counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter = 308062306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->rx_dropped_no_avail_xri_rescnt); 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci if (cb_arg) { 308362306a36Sopenharmony_ci if (cb_arg->cb) { 308462306a36Sopenharmony_ci if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status)) 308562306a36Sopenharmony_ci status = le16_to_cpu(mbox_rsp->hdr.status); 308662306a36Sopenharmony_ci cb_arg->cb(status, num_counters, counts, cb_arg->arg); 308762306a36Sopenharmony_ci } 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci kfree(cb_arg); 309062306a36Sopenharmony_ci } 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci return 0; 309362306a36Sopenharmony_ci} 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ciint 309662306a36Sopenharmony_ciefct_hw_get_link_stats(struct efct_hw *hw, u8 req_ext_counters, 309762306a36Sopenharmony_ci u8 clear_overflow_flags, u8 clear_all_counters, 309862306a36Sopenharmony_ci void (*cb)(int status, u32 num_counters, 309962306a36Sopenharmony_ci struct efct_hw_link_stat_counts *counters, 310062306a36Sopenharmony_ci void *arg), 310162306a36Sopenharmony_ci void *arg) 310262306a36Sopenharmony_ci{ 310362306a36Sopenharmony_ci int rc = -EIO; 310462306a36Sopenharmony_ci struct efct_hw_link_stat_cb_arg *cb_arg; 310562306a36Sopenharmony_ci u8 mbxdata[SLI4_BMBX_SIZE]; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(*cb_arg), GFP_ATOMIC); 310862306a36Sopenharmony_ci if (!cb_arg) 310962306a36Sopenharmony_ci return -ENOMEM; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci cb_arg->cb = cb; 311262306a36Sopenharmony_ci cb_arg->arg = arg; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci /* Send the HW command */ 311562306a36Sopenharmony_ci if (!sli_cmd_read_link_stats(&hw->sli, mbxdata, req_ext_counters, 311662306a36Sopenharmony_ci clear_overflow_flags, clear_all_counters)) 311762306a36Sopenharmony_ci rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT, 311862306a36Sopenharmony_ci efct_hw_cb_link_stat, cb_arg); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci if (rc) 312162306a36Sopenharmony_ci kfree(cb_arg); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci return rc; 312462306a36Sopenharmony_ci} 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_cistatic int 312762306a36Sopenharmony_ciefct_hw_cb_host_stat(struct efct_hw *hw, int status, u8 *mqe, void *arg) 312862306a36Sopenharmony_ci{ 312962306a36Sopenharmony_ci struct sli4_cmd_read_status *mbox_rsp = 313062306a36Sopenharmony_ci (struct sli4_cmd_read_status *)mqe; 313162306a36Sopenharmony_ci struct efct_hw_host_stat_cb_arg *cb_arg = arg; 313262306a36Sopenharmony_ci struct efct_hw_host_stat_counts counts[EFCT_HW_HOST_STAT_MAX]; 313362306a36Sopenharmony_ci u32 num_counters = EFCT_HW_HOST_STAT_MAX; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci memset(counts, 0, sizeof(struct efct_hw_host_stat_counts) * 313662306a36Sopenharmony_ci EFCT_HW_HOST_STAT_MAX); 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter = 313962306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->trans_kbyte_cnt); 314062306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter = 314162306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->recv_kbyte_cnt); 314262306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter = 314362306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->trans_frame_cnt); 314462306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter = 314562306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->recv_frame_cnt); 314662306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_TX_SEQ_COUNT].counter = 314762306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->trans_seq_cnt); 314862306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_RX_SEQ_COUNT].counter = 314962306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->recv_seq_cnt); 315062306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter = 315162306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->tot_exchanges_orig); 315262306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_RESP].counter = 315362306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->tot_exchanges_resp); 315462306a36Sopenharmony_ci counts[EFCT_HW_HOSY_STAT_RX_P_BSY_COUNT].counter = 315562306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->recv_p_bsy_cnt); 315662306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_RX_F_BSY_COUNT].counter = 315762306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->recv_f_bsy_cnt); 315862306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter = 315962306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->no_rq_buf_dropped_frames_cnt); 316062306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter = 316162306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->empty_rq_timeout_cnt); 316262306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter = 316362306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->no_xri_dropped_frames_cnt); 316462306a36Sopenharmony_ci counts[EFCT_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter = 316562306a36Sopenharmony_ci le32_to_cpu(mbox_rsp->empty_xri_pool_cnt); 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci if (cb_arg) { 316862306a36Sopenharmony_ci if (cb_arg->cb) { 316962306a36Sopenharmony_ci if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status)) 317062306a36Sopenharmony_ci status = le16_to_cpu(mbox_rsp->hdr.status); 317162306a36Sopenharmony_ci cb_arg->cb(status, num_counters, counts, cb_arg->arg); 317262306a36Sopenharmony_ci } 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci kfree(cb_arg); 317562306a36Sopenharmony_ci } 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci return 0; 317862306a36Sopenharmony_ci} 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ciint 318162306a36Sopenharmony_ciefct_hw_get_host_stats(struct efct_hw *hw, u8 cc, 318262306a36Sopenharmony_ci void (*cb)(int status, u32 num_counters, 318362306a36Sopenharmony_ci struct efct_hw_host_stat_counts *counters, 318462306a36Sopenharmony_ci void *arg), 318562306a36Sopenharmony_ci void *arg) 318662306a36Sopenharmony_ci{ 318762306a36Sopenharmony_ci int rc = -EIO; 318862306a36Sopenharmony_ci struct efct_hw_host_stat_cb_arg *cb_arg; 318962306a36Sopenharmony_ci u8 mbxdata[SLI4_BMBX_SIZE]; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci cb_arg = kmalloc(sizeof(*cb_arg), GFP_ATOMIC); 319262306a36Sopenharmony_ci if (!cb_arg) 319362306a36Sopenharmony_ci return -ENOMEM; 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci cb_arg->cb = cb; 319662306a36Sopenharmony_ci cb_arg->arg = arg; 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci /* Send the HW command to get the host stats */ 319962306a36Sopenharmony_ci if (!sli_cmd_read_status(&hw->sli, mbxdata, cc)) 320062306a36Sopenharmony_ci rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT, 320162306a36Sopenharmony_ci efct_hw_cb_host_stat, cb_arg); 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci if (rc) { 320462306a36Sopenharmony_ci efc_log_debug(hw->os, "READ_HOST_STATS failed\n"); 320562306a36Sopenharmony_ci kfree(cb_arg); 320662306a36Sopenharmony_ci } 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci return rc; 320962306a36Sopenharmony_ci} 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_cistruct efct_hw_async_call_ctx { 321262306a36Sopenharmony_ci efct_hw_async_cb_t callback; 321362306a36Sopenharmony_ci void *arg; 321462306a36Sopenharmony_ci u8 cmd[SLI4_BMBX_SIZE]; 321562306a36Sopenharmony_ci}; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_cistatic void 321862306a36Sopenharmony_ciefct_hw_async_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg) 321962306a36Sopenharmony_ci{ 322062306a36Sopenharmony_ci struct efct_hw_async_call_ctx *ctx = arg; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci if (ctx) { 322362306a36Sopenharmony_ci if (ctx->callback) 322462306a36Sopenharmony_ci (*ctx->callback)(hw, status, mqe, ctx->arg); 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci kfree(ctx); 322762306a36Sopenharmony_ci } 322862306a36Sopenharmony_ci} 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ciint 323162306a36Sopenharmony_ciefct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg) 323262306a36Sopenharmony_ci{ 323362306a36Sopenharmony_ci struct efct_hw_async_call_ctx *ctx; 323462306a36Sopenharmony_ci int rc; 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci /* 323762306a36Sopenharmony_ci * Allocate a callback context (which includes the mbox cmd buffer), 323862306a36Sopenharmony_ci * we need this to be persistent as the mbox cmd submission may be 323962306a36Sopenharmony_ci * queued and executed later execution. 324062306a36Sopenharmony_ci */ 324162306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 324262306a36Sopenharmony_ci if (!ctx) 324362306a36Sopenharmony_ci return -ENOMEM; 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci ctx->callback = callback; 324662306a36Sopenharmony_ci ctx->arg = arg; 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci /* Build and send a NOP mailbox command */ 324962306a36Sopenharmony_ci if (sli_cmd_common_nop(&hw->sli, ctx->cmd, 0)) { 325062306a36Sopenharmony_ci efc_log_err(hw->os, "COMMON_NOP format failure\n"); 325162306a36Sopenharmony_ci kfree(ctx); 325262306a36Sopenharmony_ci return -EIO; 325362306a36Sopenharmony_ci } 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci rc = efct_hw_command(hw, ctx->cmd, EFCT_CMD_NOWAIT, efct_hw_async_cb, 325662306a36Sopenharmony_ci ctx); 325762306a36Sopenharmony_ci if (rc) { 325862306a36Sopenharmony_ci efc_log_err(hw->os, "COMMON_NOP command failure, rc=%d\n", rc); 325962306a36Sopenharmony_ci kfree(ctx); 326062306a36Sopenharmony_ci return -EIO; 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci return 0; 326362306a36Sopenharmony_ci} 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_cistatic int 326662306a36Sopenharmony_ciefct_hw_cb_fw_write(struct efct_hw *hw, int status, u8 *mqe, void *arg) 326762306a36Sopenharmony_ci{ 326862306a36Sopenharmony_ci struct sli4_cmd_sli_config *mbox_rsp = 326962306a36Sopenharmony_ci (struct sli4_cmd_sli_config *)mqe; 327062306a36Sopenharmony_ci struct sli4_rsp_cmn_write_object *wr_obj_rsp; 327162306a36Sopenharmony_ci struct efct_hw_fw_wr_cb_arg *cb_arg = arg; 327262306a36Sopenharmony_ci u32 bytes_written; 327362306a36Sopenharmony_ci u16 mbox_status; 327462306a36Sopenharmony_ci u32 change_status; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci wr_obj_rsp = (struct sli4_rsp_cmn_write_object *) 327762306a36Sopenharmony_ci &mbox_rsp->payload.embed; 327862306a36Sopenharmony_ci bytes_written = le32_to_cpu(wr_obj_rsp->actual_write_length); 327962306a36Sopenharmony_ci mbox_status = le16_to_cpu(mbox_rsp->hdr.status); 328062306a36Sopenharmony_ci change_status = (le32_to_cpu(wr_obj_rsp->change_status_dword) & 328162306a36Sopenharmony_ci RSP_CHANGE_STATUS); 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci if (cb_arg) { 328462306a36Sopenharmony_ci if (cb_arg->cb) { 328562306a36Sopenharmony_ci if (!status && mbox_status) 328662306a36Sopenharmony_ci status = mbox_status; 328762306a36Sopenharmony_ci cb_arg->cb(status, bytes_written, change_status, 328862306a36Sopenharmony_ci cb_arg->arg); 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci kfree(cb_arg); 329262306a36Sopenharmony_ci } 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci return 0; 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ciint 329862306a36Sopenharmony_ciefct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma, u32 size, 329962306a36Sopenharmony_ci u32 offset, int last, 330062306a36Sopenharmony_ci void (*cb)(int status, u32 bytes_written, 330162306a36Sopenharmony_ci u32 change_status, void *arg), 330262306a36Sopenharmony_ci void *arg) 330362306a36Sopenharmony_ci{ 330462306a36Sopenharmony_ci int rc = -EIO; 330562306a36Sopenharmony_ci u8 mbxdata[SLI4_BMBX_SIZE]; 330662306a36Sopenharmony_ci struct efct_hw_fw_wr_cb_arg *cb_arg; 330762306a36Sopenharmony_ci int noc = 0; 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(*cb_arg), GFP_KERNEL); 331062306a36Sopenharmony_ci if (!cb_arg) 331162306a36Sopenharmony_ci return -ENOMEM; 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci cb_arg->cb = cb; 331462306a36Sopenharmony_ci cb_arg->arg = arg; 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci /* Write a portion of a firmware image to the device */ 331762306a36Sopenharmony_ci if (!sli_cmd_common_write_object(&hw->sli, mbxdata, 331862306a36Sopenharmony_ci noc, last, size, offset, "/prg/", 331962306a36Sopenharmony_ci dma)) 332062306a36Sopenharmony_ci rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT, 332162306a36Sopenharmony_ci efct_hw_cb_fw_write, cb_arg); 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci if (rc != 0) { 332462306a36Sopenharmony_ci efc_log_debug(hw->os, "COMMON_WRITE_OBJECT failed\n"); 332562306a36Sopenharmony_ci kfree(cb_arg); 332662306a36Sopenharmony_ci } 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci return rc; 332962306a36Sopenharmony_ci} 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_cistatic int 333262306a36Sopenharmony_ciefct_hw_cb_port_control(struct efct_hw *hw, int status, u8 *mqe, 333362306a36Sopenharmony_ci void *arg) 333462306a36Sopenharmony_ci{ 333562306a36Sopenharmony_ci return 0; 333662306a36Sopenharmony_ci} 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ciint 333962306a36Sopenharmony_ciefct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl, 334062306a36Sopenharmony_ci uintptr_t value, 334162306a36Sopenharmony_ci void (*cb)(int status, uintptr_t value, void *arg), 334262306a36Sopenharmony_ci void *arg) 334362306a36Sopenharmony_ci{ 334462306a36Sopenharmony_ci int rc = -EIO; 334562306a36Sopenharmony_ci u8 link[SLI4_BMBX_SIZE]; 334662306a36Sopenharmony_ci u32 speed = 0; 334762306a36Sopenharmony_ci u8 reset_alpa = 0; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci switch (ctrl) { 335062306a36Sopenharmony_ci case EFCT_HW_PORT_INIT: 335162306a36Sopenharmony_ci if (!sli_cmd_config_link(&hw->sli, link)) 335262306a36Sopenharmony_ci rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT, 335362306a36Sopenharmony_ci efct_hw_cb_port_control, NULL); 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci if (rc != 0) { 335662306a36Sopenharmony_ci efc_log_err(hw->os, "CONFIG_LINK failed\n"); 335762306a36Sopenharmony_ci break; 335862306a36Sopenharmony_ci } 335962306a36Sopenharmony_ci speed = hw->config.speed; 336062306a36Sopenharmony_ci reset_alpa = (u8)(value & 0xff); 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci rc = -EIO; 336362306a36Sopenharmony_ci if (!sli_cmd_init_link(&hw->sli, link, speed, reset_alpa)) 336462306a36Sopenharmony_ci rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT, 336562306a36Sopenharmony_ci efct_hw_cb_port_control, NULL); 336662306a36Sopenharmony_ci /* Free buffer on error, since no callback is coming */ 336762306a36Sopenharmony_ci if (rc) 336862306a36Sopenharmony_ci efc_log_err(hw->os, "INIT_LINK failed\n"); 336962306a36Sopenharmony_ci break; 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci case EFCT_HW_PORT_SHUTDOWN: 337262306a36Sopenharmony_ci if (!sli_cmd_down_link(&hw->sli, link)) 337362306a36Sopenharmony_ci rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT, 337462306a36Sopenharmony_ci efct_hw_cb_port_control, NULL); 337562306a36Sopenharmony_ci /* Free buffer on error, since no callback is coming */ 337662306a36Sopenharmony_ci if (rc) 337762306a36Sopenharmony_ci efc_log_err(hw->os, "DOWN_LINK failed\n"); 337862306a36Sopenharmony_ci break; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci default: 338162306a36Sopenharmony_ci efc_log_debug(hw->os, "unhandled control %#x\n", ctrl); 338262306a36Sopenharmony_ci break; 338362306a36Sopenharmony_ci } 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci return rc; 338662306a36Sopenharmony_ci} 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_civoid 338962306a36Sopenharmony_ciefct_hw_teardown(struct efct_hw *hw) 339062306a36Sopenharmony_ci{ 339162306a36Sopenharmony_ci u32 i = 0; 339262306a36Sopenharmony_ci u32 destroy_queues; 339362306a36Sopenharmony_ci u32 free_memory; 339462306a36Sopenharmony_ci struct efc_dma *dma; 339562306a36Sopenharmony_ci struct efct *efct = hw->os; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE); 339862306a36Sopenharmony_ci free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED); 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci /* Cancel Sliport Healthcheck */ 340162306a36Sopenharmony_ci if (hw->sliport_healthcheck) { 340262306a36Sopenharmony_ci hw->sliport_healthcheck = 0; 340362306a36Sopenharmony_ci efct_hw_config_sli_port_health_check(hw, 0, 0); 340462306a36Sopenharmony_ci } 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_QUEUES_ALLOCATED) { 340762306a36Sopenharmony_ci hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS; 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci efct_hw_flush(hw); 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if (list_empty(&hw->cmd_head)) 341262306a36Sopenharmony_ci efc_log_debug(hw->os, 341362306a36Sopenharmony_ci "All commands completed on MQ queue\n"); 341462306a36Sopenharmony_ci else 341562306a36Sopenharmony_ci efc_log_debug(hw->os, 341662306a36Sopenharmony_ci "Some cmds still pending on MQ queue\n"); 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci /* Cancel any remaining commands */ 341962306a36Sopenharmony_ci efct_hw_command_cancel(hw); 342062306a36Sopenharmony_ci } else { 342162306a36Sopenharmony_ci hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS; 342262306a36Sopenharmony_ci } 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, 342562306a36Sopenharmony_ci hw->rnode_mem.size, hw->rnode_mem.virt, 342662306a36Sopenharmony_ci hw->rnode_mem.phys); 342762306a36Sopenharmony_ci memset(&hw->rnode_mem, 0, sizeof(struct efc_dma)); 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci if (hw->io) { 343062306a36Sopenharmony_ci for (i = 0; i < hw->config.n_io; i++) { 343162306a36Sopenharmony_ci if (hw->io[i] && hw->io[i]->sgl && 343262306a36Sopenharmony_ci hw->io[i]->sgl->virt) { 343362306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, 343462306a36Sopenharmony_ci hw->io[i]->sgl->size, 343562306a36Sopenharmony_ci hw->io[i]->sgl->virt, 343662306a36Sopenharmony_ci hw->io[i]->sgl->phys); 343762306a36Sopenharmony_ci } 343862306a36Sopenharmony_ci kfree(hw->io[i]); 343962306a36Sopenharmony_ci hw->io[i] = NULL; 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci kfree(hw->io); 344262306a36Sopenharmony_ci hw->io = NULL; 344362306a36Sopenharmony_ci kfree(hw->wqe_buffs); 344462306a36Sopenharmony_ci hw->wqe_buffs = NULL; 344562306a36Sopenharmony_ci } 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci dma = &hw->xfer_rdy; 344862306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, 344962306a36Sopenharmony_ci dma->size, dma->virt, dma->phys); 345062306a36Sopenharmony_ci memset(dma, 0, sizeof(struct efc_dma)); 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci dma = &hw->loop_map; 345362306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, 345462306a36Sopenharmony_ci dma->size, dma->virt, dma->phys); 345562306a36Sopenharmony_ci memset(dma, 0, sizeof(struct efc_dma)); 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci for (i = 0; i < hw->wq_count; i++) 345862306a36Sopenharmony_ci sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues, 345962306a36Sopenharmony_ci free_memory); 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci for (i = 0; i < hw->rq_count; i++) 346262306a36Sopenharmony_ci sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues, 346362306a36Sopenharmony_ci free_memory); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci for (i = 0; i < hw->mq_count; i++) 346662306a36Sopenharmony_ci sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues, 346762306a36Sopenharmony_ci free_memory); 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci for (i = 0; i < hw->cq_count; i++) 347062306a36Sopenharmony_ci sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues, 347162306a36Sopenharmony_ci free_memory); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci for (i = 0; i < hw->eq_count; i++) 347462306a36Sopenharmony_ci sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues, 347562306a36Sopenharmony_ci free_memory); 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci /* Free rq buffers */ 347862306a36Sopenharmony_ci efct_hw_rx_free(hw); 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci efct_hw_queue_teardown(hw); 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci kfree(hw->wq_cpu_array); 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci sli_teardown(&hw->sli); 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci /* record the fact that the queues are non-functional */ 348762306a36Sopenharmony_ci hw->state = EFCT_HW_STATE_UNINITIALIZED; 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci /* free sequence free pool */ 349062306a36Sopenharmony_ci kfree(hw->seq_pool); 349162306a36Sopenharmony_ci hw->seq_pool = NULL; 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci /* free hw_wq_callback pool */ 349462306a36Sopenharmony_ci efct_hw_reqtag_pool_free(hw); 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_ci mempool_destroy(hw->cmd_ctx_pool); 349762306a36Sopenharmony_ci mempool_destroy(hw->mbox_rqst_pool); 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci /* Mark HW setup as not having been called */ 350062306a36Sopenharmony_ci hw->hw_setup_called = false; 350162306a36Sopenharmony_ci} 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_cistatic int 350462306a36Sopenharmony_ciefct_hw_sli_reset(struct efct_hw *hw, enum efct_hw_reset reset, 350562306a36Sopenharmony_ci enum efct_hw_state prev_state) 350662306a36Sopenharmony_ci{ 350762306a36Sopenharmony_ci int rc = 0; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci switch (reset) { 351062306a36Sopenharmony_ci case EFCT_HW_RESET_FUNCTION: 351162306a36Sopenharmony_ci efc_log_debug(hw->os, "issuing function level reset\n"); 351262306a36Sopenharmony_ci if (sli_reset(&hw->sli)) { 351362306a36Sopenharmony_ci efc_log_err(hw->os, "sli_reset failed\n"); 351462306a36Sopenharmony_ci rc = -EIO; 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci break; 351762306a36Sopenharmony_ci case EFCT_HW_RESET_FIRMWARE: 351862306a36Sopenharmony_ci efc_log_debug(hw->os, "issuing firmware reset\n"); 351962306a36Sopenharmony_ci if (sli_fw_reset(&hw->sli)) { 352062306a36Sopenharmony_ci efc_log_err(hw->os, "sli_soft_reset failed\n"); 352162306a36Sopenharmony_ci rc = -EIO; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci /* 352462306a36Sopenharmony_ci * Because the FW reset leaves the FW in a non-running state, 352562306a36Sopenharmony_ci * follow that with a regular reset. 352662306a36Sopenharmony_ci */ 352762306a36Sopenharmony_ci efc_log_debug(hw->os, "issuing function level reset\n"); 352862306a36Sopenharmony_ci if (sli_reset(&hw->sli)) { 352962306a36Sopenharmony_ci efc_log_err(hw->os, "sli_reset failed\n"); 353062306a36Sopenharmony_ci rc = -EIO; 353162306a36Sopenharmony_ci } 353262306a36Sopenharmony_ci break; 353362306a36Sopenharmony_ci default: 353462306a36Sopenharmony_ci efc_log_err(hw->os, "unknown type - no reset performed\n"); 353562306a36Sopenharmony_ci hw->state = prev_state; 353662306a36Sopenharmony_ci rc = -EINVAL; 353762306a36Sopenharmony_ci break; 353862306a36Sopenharmony_ci } 353962306a36Sopenharmony_ci 354062306a36Sopenharmony_ci return rc; 354162306a36Sopenharmony_ci} 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ciint 354462306a36Sopenharmony_ciefct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset) 354562306a36Sopenharmony_ci{ 354662306a36Sopenharmony_ci int rc = 0; 354762306a36Sopenharmony_ci enum efct_hw_state prev_state = hw->state; 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci if (hw->state != EFCT_HW_STATE_ACTIVE) 355062306a36Sopenharmony_ci efc_log_debug(hw->os, 355162306a36Sopenharmony_ci "HW state %d is not active\n", hw->state); 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci hw->state = EFCT_HW_STATE_RESET_IN_PROGRESS; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci /* 355662306a36Sopenharmony_ci * If the prev_state is already reset/teardown in progress, 355762306a36Sopenharmony_ci * don't continue further 355862306a36Sopenharmony_ci */ 355962306a36Sopenharmony_ci if (prev_state == EFCT_HW_STATE_RESET_IN_PROGRESS || 356062306a36Sopenharmony_ci prev_state == EFCT_HW_STATE_TEARDOWN_IN_PROGRESS) 356162306a36Sopenharmony_ci return efct_hw_sli_reset(hw, reset, prev_state); 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci if (prev_state != EFCT_HW_STATE_UNINITIALIZED) { 356462306a36Sopenharmony_ci efct_hw_flush(hw); 356562306a36Sopenharmony_ci 356662306a36Sopenharmony_ci if (list_empty(&hw->cmd_head)) 356762306a36Sopenharmony_ci efc_log_debug(hw->os, 356862306a36Sopenharmony_ci "All commands completed on MQ queue\n"); 356962306a36Sopenharmony_ci else 357062306a36Sopenharmony_ci efc_log_err(hw->os, 357162306a36Sopenharmony_ci "Some commands still pending on MQ queue\n"); 357262306a36Sopenharmony_ci } 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci /* Reset the chip */ 357562306a36Sopenharmony_ci rc = efct_hw_sli_reset(hw, reset, prev_state); 357662306a36Sopenharmony_ci if (rc == -EINVAL) 357762306a36Sopenharmony_ci return -EIO; 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci return rc; 358062306a36Sopenharmony_ci} 3581