diff options
Diffstat (limited to '')
21 files changed, 18931 insertions, 0 deletions
diff --git a/drivers/scsi/elx/libefc/efc.h b/drivers/scsi/elx/libefc/efc.h new file mode 100644 index 000000000..468ff3cc9 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFC_H__ +#define __EFC_H__ + +#include "../include/efc_common.h" +#include "efclib.h" +#include "efc_sm.h" +#include "efc_cmds.h" +#include "efc_domain.h" +#include "efc_nport.h" +#include "efc_node.h" +#include "efc_fabric.h" +#include "efc_device.h" +#include "efc_els.h" + +#define EFC_MAX_REMOTE_NODES 2048 +#define NODE_SPARAMS_SIZE 256 + +enum efc_scsi_del_initiator_reason { + EFC_SCSI_INITIATOR_DELETED, + EFC_SCSI_INITIATOR_MISSING, +}; + +enum efc_scsi_del_target_reason { + EFC_SCSI_TARGET_DELETED, + EFC_SCSI_TARGET_MISSING, +}; + +#define EFC_FC_ELS_DEFAULT_RETRIES 3 + +#define domain_sm_trace(domain) \ + efc_log_debug(domain->efc, "[domain:%s] %-20s %-20s\n", \ + domain->display_name, __func__, efc_sm_event_name(evt)) \ + +#define domain_trace(domain, fmt, ...) \ + efc_log_debug(domain->efc, \ + "[%s]" fmt, domain->display_name, ##__VA_ARGS__) \ + +#define node_sm_trace() \ + efc_log_debug(node->efc, "[%s] %-20s %-20s\n", \ + node->display_name, __func__, efc_sm_event_name(evt)) \ + +#define nport_sm_trace(nport) \ + efc_log_debug(nport->efc, \ + "[%s] %-20s %-20s\n", nport->display_name, __func__, efc_sm_event_name(evt)) \ + +#endif /* __EFC_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_cmds.c b/drivers/scsi/elx/libefc/efc_cmds.c new file mode 100644 index 000000000..da4ac8a4c --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_cmds.c @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efclib.h" +#include "../libefc_sli/sli4.h" +#include "efc_cmds.h" +#include "efc_sm.h" + +static void +efc_nport_free_resources(struct efc_nport *nport, int evt, void *data) +{ + struct efc *efc = nport->efc; + + /* Clear the nport attached flag */ + nport->attached = false; + + /* Free the service parameters buffer */ + if (nport->dma.virt) { + dma_free_coherent(&efc->pci->dev, nport->dma.size, + nport->dma.virt, nport->dma.phys); + memset(&nport->dma, 0, sizeof(struct efc_dma)); + } + + /* Free the SLI resources */ + sli_resource_free(efc->sli, SLI4_RSRC_VPI, nport->indicator); + + efc_nport_cb(efc, evt, nport); +} + +static int +efc_nport_get_mbox_status(struct efc_nport *nport, u8 *mqe, int status) +{ + struct efc *efc = nport->efc; + struct sli4_mbox_command_header *hdr = + (struct sli4_mbox_command_header *)mqe; + + if (status || le16_to_cpu(hdr->status)) { + efc_log_debug(efc, "bad status vpi=%#x st=%x hdr=%x\n", + nport->indicator, status, le16_to_cpu(hdr->status)); + return -EIO; + } + + return 0; +} + +static int +efc_nport_free_unreg_vpi_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_nport *nport = arg; + int evt = EFC_EVT_NPORT_FREE_OK; + int rc; + + rc = efc_nport_get_mbox_status(nport, mqe, status); + if (rc) + evt = EFC_EVT_NPORT_FREE_FAIL; + + efc_nport_free_resources(nport, evt, mqe); + return rc; +} + +static void +efc_nport_free_unreg_vpi(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + int rc; + u8 data[SLI4_BMBX_SIZE]; + + rc = sli_cmd_unreg_vpi(efc->sli, data, nport->indicator, + SLI4_UNREG_TYPE_PORT); + if (rc) { + efc_log_err(efc, "UNREG_VPI format failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_FAIL, data); + return; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_nport_free_unreg_vpi_cb, nport); + if (rc) { + efc_log_err(efc, "UNREG_VPI command failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_FAIL, data); + } +} + +static void +efc_nport_send_evt(struct efc_nport *nport, int evt, void *data) +{ + struct efc *efc = nport->efc; + + /* Now inform the registered callbacks */ + efc_nport_cb(efc, evt, nport); + + /* Set the nport attached flag */ + if (evt == EFC_EVT_NPORT_ATTACH_OK) + nport->attached = true; + + /* If there is a pending free request, then handle it now */ + if (nport->free_req_pending) + efc_nport_free_unreg_vpi(nport); +} + +static int +efc_nport_alloc_init_vpi_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_nport *nport = arg; + + if (efc_nport_get_mbox_status(nport, mqe, status)) { + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, mqe); + return -EIO; + } + + efc_nport_send_evt(nport, EFC_EVT_NPORT_ALLOC_OK, mqe); + return 0; +} + +static void +efc_nport_alloc_init_vpi(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + u8 data[SLI4_BMBX_SIZE]; + int rc; + + /* If there is a pending free request, then handle it now */ + if (nport->free_req_pending) { + efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_OK, data); + return; + } + + rc = sli_cmd_init_vpi(efc->sli, data, + nport->indicator, nport->domain->indicator); + if (rc) { + efc_log_err(efc, "INIT_VPI format failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data); + return; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_nport_alloc_init_vpi_cb, nport); + if (rc) { + efc_log_err(efc, "INIT_VPI command failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data); + } +} + +static int +efc_nport_alloc_read_sparm64_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_nport *nport = arg; + u8 *payload = NULL; + + if (efc_nport_get_mbox_status(nport, mqe, status)) { + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, mqe); + return -EIO; + } + + payload = nport->dma.virt; + + memcpy(&nport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET, + sizeof(nport->sli_wwpn)); + memcpy(&nport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET, + sizeof(nport->sli_wwnn)); + + dma_free_coherent(&efc->pci->dev, nport->dma.size, nport->dma.virt, + nport->dma.phys); + memset(&nport->dma, 0, sizeof(struct efc_dma)); + efc_nport_alloc_init_vpi(nport); + return 0; +} + +static void +efc_nport_alloc_read_sparm64(struct efc *efc, struct efc_nport *nport) +{ + u8 data[SLI4_BMBX_SIZE]; + int rc; + + /* Allocate memory for the service parameters */ + nport->dma.size = EFC_SPARAM_DMA_SZ; + nport->dma.virt = dma_alloc_coherent(&efc->pci->dev, + nport->dma.size, &nport->dma.phys, + GFP_KERNEL); + if (!nport->dma.virt) { + efc_log_err(efc, "Failed to allocate DMA memory\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data); + return; + } + + rc = sli_cmd_read_sparm64(efc->sli, data, + &nport->dma, nport->indicator); + if (rc) { + efc_log_err(efc, "READ_SPARM64 format failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data); + return; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_nport_alloc_read_sparm64_cb, nport); + if (rc) { + efc_log_err(efc, "READ_SPARM64 command failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data); + } +} + +int +efc_cmd_nport_alloc(struct efc *efc, struct efc_nport *nport, + struct efc_domain *domain, u8 *wwpn) +{ + u32 index; + + nport->indicator = U32_MAX; + nport->free_req_pending = false; + + if (wwpn) + memcpy(&nport->sli_wwpn, wwpn, sizeof(nport->sli_wwpn)); + + /* + * allocate a VPI object for the port and stores it in the + * indicator field of the port object. + */ + if (sli_resource_alloc(efc->sli, SLI4_RSRC_VPI, + &nport->indicator, &index)) { + efc_log_err(efc, "VPI allocation failure\n"); + return -EIO; + } + + if (domain) { + /* + * If the WWPN is NULL, fetch the default + * WWPN and WWNN before initializing the VPI + */ + if (!wwpn) + efc_nport_alloc_read_sparm64(efc, nport); + else + efc_nport_alloc_init_vpi(nport); + } else if (!wwpn) { + /* domain NULL and wwpn non-NULL */ + efc_log_err(efc, "need WWN for physical port\n"); + sli_resource_free(efc->sli, SLI4_RSRC_VPI, nport->indicator); + return -EIO; + } + + return 0; +} + +static int +efc_nport_attach_reg_vpi_cb(struct efc *efc, int status, u8 *mqe, + void *arg) +{ + struct efc_nport *nport = arg; + + nport->attaching = false; + if (efc_nport_get_mbox_status(nport, mqe, status)) { + efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, mqe); + return -EIO; + } + + efc_nport_send_evt(nport, EFC_EVT_NPORT_ATTACH_OK, mqe); + return 0; +} + +int +efc_cmd_nport_attach(struct efc *efc, struct efc_nport *nport, u32 fc_id) +{ + u8 buf[SLI4_BMBX_SIZE]; + int rc = 0; + + if (!nport) { + efc_log_err(efc, "bad param(s) nport=%p\n", nport); + return -EIO; + } + + nport->fc_id = fc_id; + + /* register previously-allocated VPI with the device */ + rc = sli_cmd_reg_vpi(efc->sli, buf, nport->fc_id, + nport->sli_wwpn, nport->indicator, + nport->domain->indicator, false); + if (rc) { + efc_log_err(efc, "REG_VPI format failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, buf); + return rc; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, buf, + efc_nport_attach_reg_vpi_cb, nport); + if (rc) { + efc_log_err(efc, "REG_VPI command failure\n"); + efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, buf); + } else { + nport->attaching = true; + } + + return rc; +} + +int +efc_cmd_nport_free(struct efc *efc, struct efc_nport *nport) +{ + if (!nport) { + efc_log_err(efc, "bad parameter(s) nport=%p\n", nport); + return -EIO; + } + + /* Issue the UNREG_VPI command to free the assigned VPI context */ + if (nport->attached) + efc_nport_free_unreg_vpi(nport); + else if (nport->attaching) + nport->free_req_pending = true; + else + efc_sm_post_event(&nport->sm, EFC_EVT_NPORT_FREE_OK, NULL); + + return 0; +} + +static int +efc_domain_get_mbox_status(struct efc_domain *domain, u8 *mqe, int status) +{ + struct efc *efc = domain->efc; + struct sli4_mbox_command_header *hdr = + (struct sli4_mbox_command_header *)mqe; + + if (status || le16_to_cpu(hdr->status)) { + efc_log_debug(efc, "bad status vfi=%#x st=%x hdr=%x\n", + domain->indicator, status, + le16_to_cpu(hdr->status)); + return -EIO; + } + + return 0; +} + +static void +efc_domain_free_resources(struct efc_domain *domain, int evt, void *data) +{ + struct efc *efc = domain->efc; + + /* Free the service parameters buffer */ + if (domain->dma.virt) { + dma_free_coherent(&efc->pci->dev, + domain->dma.size, domain->dma.virt, + domain->dma.phys); + memset(&domain->dma, 0, sizeof(struct efc_dma)); + } + + /* Free the SLI resources */ + sli_resource_free(efc->sli, SLI4_RSRC_VFI, domain->indicator); + + efc_domain_cb(efc, evt, domain); +} + +static void +efc_domain_send_nport_evt(struct efc_domain *domain, + int port_evt, int domain_evt, void *data) +{ + struct efc *efc = domain->efc; + + /* Send alloc/attach ok to the physical nport */ + efc_nport_send_evt(domain->nport, port_evt, NULL); + + /* Now inform the registered callbacks */ + efc_domain_cb(efc, domain_evt, domain); +} + +static int +efc_domain_alloc_read_sparm64_cb(struct efc *efc, int status, u8 *mqe, + void *arg) +{ + struct efc_domain *domain = arg; + + if (efc_domain_get_mbox_status(domain, mqe, status)) { + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, mqe); + return -EIO; + } + + efc_domain_send_nport_evt(domain, EFC_EVT_NPORT_ALLOC_OK, + EFC_HW_DOMAIN_ALLOC_OK, mqe); + return 0; +} + +static void +efc_domain_alloc_read_sparm64(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + u8 data[SLI4_BMBX_SIZE]; + int rc; + + rc = sli_cmd_read_sparm64(efc->sli, data, &domain->dma, 0); + if (rc) { + efc_log_err(efc, "READ_SPARM64 format failure\n"); + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, data); + return; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_domain_alloc_read_sparm64_cb, domain); + if (rc) { + efc_log_err(efc, "READ_SPARM64 command failure\n"); + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, data); + } +} + +static int +efc_domain_alloc_init_vfi_cb(struct efc *efc, int status, u8 *mqe, + void *arg) +{ + struct efc_domain *domain = arg; + + if (efc_domain_get_mbox_status(domain, mqe, status)) { + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, mqe); + return -EIO; + } + + efc_domain_alloc_read_sparm64(domain); + return 0; +} + +static void +efc_domain_alloc_init_vfi(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + struct efc_nport *nport = domain->nport; + u8 data[SLI4_BMBX_SIZE]; + int rc; + + /* + * For FC, the HW alread registered an FCFI. + * Copy FCF information into the domain and jump to INIT_VFI. + */ + domain->fcf_indicator = efc->fcfi; + rc = sli_cmd_init_vfi(efc->sli, data, domain->indicator, + domain->fcf_indicator, nport->indicator); + if (rc) { + efc_log_err(efc, "INIT_VFI format failure\n"); + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, data); + return; + } + + efc_log_err(efc, "%s issue mbox\n", __func__); + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_domain_alloc_init_vfi_cb, domain); + if (rc) { + efc_log_err(efc, "INIT_VFI command failure\n"); + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ALLOC_FAIL, data); + } +} + +int +efc_cmd_domain_alloc(struct efc *efc, struct efc_domain *domain, u32 fcf) +{ + u32 index; + + if (!domain || !domain->nport) { + efc_log_err(efc, "bad parameter(s) domain=%p nport=%p\n", + domain, domain ? domain->nport : NULL); + return -EIO; + } + + /* allocate memory for the service parameters */ + domain->dma.size = EFC_SPARAM_DMA_SZ; + domain->dma.virt = dma_alloc_coherent(&efc->pci->dev, + domain->dma.size, + &domain->dma.phys, GFP_KERNEL); + if (!domain->dma.virt) { + efc_log_err(efc, "Failed to allocate DMA memory\n"); + return -EIO; + } + + domain->fcf = fcf; + domain->fcf_indicator = U32_MAX; + domain->indicator = U32_MAX; + + if (sli_resource_alloc(efc->sli, SLI4_RSRC_VFI, &domain->indicator, + &index)) { + efc_log_err(efc, "VFI allocation failure\n"); + + dma_free_coherent(&efc->pci->dev, + domain->dma.size, domain->dma.virt, + domain->dma.phys); + memset(&domain->dma, 0, sizeof(struct efc_dma)); + + return -EIO; + } + + efc_domain_alloc_init_vfi(domain); + return 0; +} + +static int +efc_domain_attach_reg_vfi_cb(struct efc *efc, int status, u8 *mqe, + void *arg) +{ + struct efc_domain *domain = arg; + + if (efc_domain_get_mbox_status(domain, mqe, status)) { + efc_domain_free_resources(domain, + EFC_HW_DOMAIN_ATTACH_FAIL, mqe); + return -EIO; + } + + efc_domain_send_nport_evt(domain, EFC_EVT_NPORT_ATTACH_OK, + EFC_HW_DOMAIN_ATTACH_OK, mqe); + return 0; +} + +int +efc_cmd_domain_attach(struct efc *efc, struct efc_domain *domain, u32 fc_id) +{ + u8 buf[SLI4_BMBX_SIZE]; + int rc = 0; + + if (!domain) { + efc_log_err(efc, "bad param(s) domain=%p\n", domain); + return -EIO; + } + + domain->nport->fc_id = fc_id; + + rc = sli_cmd_reg_vfi(efc->sli, buf, SLI4_BMBX_SIZE, domain->indicator, + domain->fcf_indicator, domain->dma, + domain->nport->indicator, domain->nport->sli_wwpn, + domain->nport->fc_id); + if (rc) { + efc_log_err(efc, "REG_VFI format failure\n"); + goto cleanup; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, buf, + efc_domain_attach_reg_vfi_cb, domain); + if (rc) { + efc_log_err(efc, "REG_VFI command failure\n"); + goto cleanup; + } + + return rc; + +cleanup: + efc_domain_free_resources(domain, EFC_HW_DOMAIN_ATTACH_FAIL, buf); + + return rc; +} + +static int +efc_domain_free_unreg_vfi_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_domain *domain = arg; + int evt = EFC_HW_DOMAIN_FREE_OK; + int rc; + + rc = efc_domain_get_mbox_status(domain, mqe, status); + if (rc) { + evt = EFC_HW_DOMAIN_FREE_FAIL; + rc = -EIO; + } + + efc_domain_free_resources(domain, evt, mqe); + return rc; +} + +static void +efc_domain_free_unreg_vfi(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + int rc; + u8 data[SLI4_BMBX_SIZE]; + + rc = sli_cmd_unreg_vfi(efc->sli, data, domain->indicator, + SLI4_UNREG_TYPE_DOMAIN); + if (rc) { + efc_log_err(efc, "UNREG_VFI format failure\n"); + goto cleanup; + } + + rc = efc->tt.issue_mbox_rqst(efc->base, data, + efc_domain_free_unreg_vfi_cb, domain); + if (rc) { + efc_log_err(efc, "UNREG_VFI command failure\n"); + goto cleanup; + } + + return; + +cleanup: + efc_domain_free_resources(domain, EFC_HW_DOMAIN_FREE_FAIL, data); +} + +int +efc_cmd_domain_free(struct efc *efc, struct efc_domain *domain) +{ + if (!domain) { + efc_log_err(efc, "bad parameter(s) domain=%p\n", domain); + return -EIO; + } + + efc_domain_free_unreg_vfi(domain); + return 0; +} + +int +efc_cmd_node_alloc(struct efc *efc, struct efc_remote_node *rnode, u32 fc_addr, + struct efc_nport *nport) +{ + /* Check for invalid indicator */ + if (rnode->indicator != U32_MAX) { + efc_log_err(efc, + "RPI allocation failure addr=%#x rpi=%#x\n", + fc_addr, rnode->indicator); + return -EIO; + } + + /* NULL SLI port indicates an unallocated remote node */ + rnode->nport = NULL; + + if (sli_resource_alloc(efc->sli, SLI4_RSRC_RPI, + &rnode->indicator, &rnode->index)) { + efc_log_err(efc, "RPI allocation failure addr=%#x\n", + fc_addr); + return -EIO; + } + + rnode->fc_id = fc_addr; + rnode->nport = nport; + + return 0; +} + +static int +efc_cmd_node_attach_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_remote_node *rnode = arg; + struct sli4_mbox_command_header *hdr = + (struct sli4_mbox_command_header *)mqe; + int evt = 0; + + if (status || le16_to_cpu(hdr->status)) { + efc_log_debug(efc, "bad status cqe=%#x mqe=%#x\n", status, + le16_to_cpu(hdr->status)); + rnode->attached = false; + evt = EFC_EVT_NODE_ATTACH_FAIL; + } else { + rnode->attached = true; + evt = EFC_EVT_NODE_ATTACH_OK; + } + + efc_remote_node_cb(efc, evt, rnode); + + return 0; +} + +int +efc_cmd_node_attach(struct efc *efc, struct efc_remote_node *rnode, + struct efc_dma *sparms) +{ + int rc = -EIO; + u8 buf[SLI4_BMBX_SIZE]; + + if (!rnode || !sparms) { + efc_log_err(efc, "bad parameter(s) rnode=%p sparms=%p\n", + rnode, sparms); + return -EIO; + } + + /* + * If the attach count is non-zero, this RPI has already been reg'd. + * Otherwise, register the RPI + */ + if (rnode->index == U32_MAX) { + efc_log_err(efc, "bad parameter rnode->index invalid\n"); + return -EIO; + } + + /* Update a remote node object with the remote port's service params */ + if (!sli_cmd_reg_rpi(efc->sli, buf, rnode->indicator, + rnode->nport->indicator, rnode->fc_id, sparms, 0, 0)) + rc = efc->tt.issue_mbox_rqst(efc->base, buf, + efc_cmd_node_attach_cb, rnode); + + return rc; +} + +int +efc_node_free_resources(struct efc *efc, struct efc_remote_node *rnode) +{ + int rc = 0; + + if (!rnode) { + efc_log_err(efc, "bad parameter rnode=%p\n", rnode); + return -EIO; + } + + if (rnode->nport) { + if (rnode->attached) { + efc_log_err(efc, "rnode is still attached\n"); + return -EIO; + } + if (rnode->indicator != U32_MAX) { + if (sli_resource_free(efc->sli, SLI4_RSRC_RPI, + rnode->indicator)) { + efc_log_err(efc, + "RPI free fail RPI %d addr=%#x\n", + rnode->indicator, rnode->fc_id); + rc = -EIO; + } else { + rnode->indicator = U32_MAX; + rnode->index = U32_MAX; + } + } + } + + return rc; +} + +static int +efc_cmd_node_free_cb(struct efc *efc, int status, u8 *mqe, void *arg) +{ + struct efc_remote_node *rnode = arg; + struct sli4_mbox_command_header *hdr = + (struct sli4_mbox_command_header *)mqe; + int evt = EFC_EVT_NODE_FREE_FAIL; + int rc = 0; + + if (status || le16_to_cpu(hdr->status)) { + efc_log_debug(efc, "bad status cqe=%#x mqe=%#x\n", status, + le16_to_cpu(hdr->status)); + + /* + * In certain cases, a non-zero MQE status is OK (all must be + * true): + * - node is attached + * - status is 0x1400 + */ + if (!rnode->attached || + (le16_to_cpu(hdr->status) != SLI4_MBX_STATUS_RPI_NOT_REG)) + rc = -EIO; + } + + if (!rc) { + rnode->attached = false; + evt = EFC_EVT_NODE_FREE_OK; + } + + efc_remote_node_cb(efc, evt, rnode); + + return rc; +} + +int +efc_cmd_node_detach(struct efc *efc, struct efc_remote_node *rnode) +{ + u8 buf[SLI4_BMBX_SIZE]; + int rc = -EIO; + + if (!rnode) { + efc_log_err(efc, "bad parameter rnode=%p\n", rnode); + return -EIO; + } + + if (rnode->nport) { + if (!rnode->attached) + return -EIO; + + rc = -EIO; + + if (!sli_cmd_unreg_rpi(efc->sli, buf, rnode->indicator, + SLI4_RSRC_RPI, U32_MAX)) + rc = efc->tt.issue_mbox_rqst(efc->base, buf, + efc_cmd_node_free_cb, rnode); + + if (rc != 0) { + efc_log_err(efc, "UNREG_RPI failed\n"); + rc = -EIO; + } + } + + return rc; +} diff --git a/drivers/scsi/elx/libefc/efc_cmds.h b/drivers/scsi/elx/libefc/efc_cmds.h new file mode 100644 index 000000000..4d353ab04 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_cmds.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFC_CMDS_H__ +#define __EFC_CMDS_H__ + +#define EFC_SPARAM_DMA_SZ 112 +int +efc_cmd_nport_alloc(struct efc *efc, struct efc_nport *nport, + struct efc_domain *domain, u8 *wwpn); +int +efc_cmd_nport_attach(struct efc *efc, struct efc_nport *nport, u32 fc_id); +int +efc_cmd_nport_free(struct efc *efc, struct efc_nport *nport); +int +efc_cmd_domain_alloc(struct efc *efc, struct efc_domain *domain, u32 fcf); +int +efc_cmd_domain_attach(struct efc *efc, struct efc_domain *domain, u32 fc_id); +int +efc_cmd_domain_free(struct efc *efc, struct efc_domain *domain); +int +efc_cmd_node_detach(struct efc *efc, struct efc_remote_node *rnode); +int +efc_node_free_resources(struct efc *efc, struct efc_remote_node *rnode); +int +efc_cmd_node_attach(struct efc *efc, struct efc_remote_node *rnode, + struct efc_dma *sparms); +int +efc_cmd_node_alloc(struct efc *efc, struct efc_remote_node *rnode, u32 fc_addr, + struct efc_nport *nport); + +#endif /* __EFC_CMDS_H */ diff --git a/drivers/scsi/elx/libefc/efc_device.c b/drivers/scsi/elx/libefc/efc_device.c new file mode 100644 index 000000000..52be01333 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_device.c @@ -0,0 +1,1602 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * device_sm Node State Machine: Remote Device States + */ + +#include "efc.h" +#include "efc_device.h" +#include "efc_fabric.h" + +void +efc_d_send_prli_rsp(struct efc_node *node, u16 ox_id) +{ + int rc = EFC_SCSI_CALL_COMPLETE; + struct efc *efc = node->efc; + + node->ls_acc_oxid = ox_id; + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_PRLI; + + /* + * Wait for backend session registration + * to complete before sending PRLI resp + */ + + if (node->init) { + efc_log_info(efc, "[%s] found(initiator) WWPN:%s WWNN:%s\n", + node->display_name, node->wwpn, node->wwnn); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_new_node(efc, node); + } + + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_SESS_REG_FAIL, NULL); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, EFC_EVT_NODE_SESS_REG_OK, NULL); +} + +static void +__efc_d_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = NULL; + struct efc *efc = NULL; + + node = ctx->app; + efc = node->efc; + + switch (evt) { + /* Handle shutdown events */ + case EFC_EVT_SHUTDOWN: + efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name, + funcname, efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s %-20s\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name, + funcname, efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + default: + /* call default event handler common to all nodes */ + __efc_node_common(funcname, ctx, evt, arg); + } +} + +static void +__efc_d_wait_del_node(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + /* + * State is entered when a node sends a delete initiator/target call + * to the target-server/initiator-client and needs to wait for that + * work to complete. + */ + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + fallthrough; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* These are expected events. */ + break; + + case EFC_EVT_NODE_DEL_INI_COMPLETE: + case EFC_EVT_NODE_DEL_TGT_COMPLETE: + /* + * node has either been detached or is in the process + * of being detached, + * call common node's initiate cleanup function + */ + efc_node_initiate_cleanup(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +static void +__efc_d_wait_del_ini_tgt(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + fallthrough; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* These are expected events. */ + break; + + case EFC_EVT_NODE_DEL_INI_COMPLETE: + case EFC_EVT_NODE_DEL_TGT_COMPLETE: + efc_node_transition(node, __efc_d_wait_del_node, NULL); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + int rc = EFC_SCSI_CALL_COMPLETE; + + /* assume no wait needed */ + node->els_io_enabled = false; + + /* make necessary delete upcall(s) */ + if (node->init && !node->targ) { + efc_log_info(node->efc, + "[%s] delete (initiator) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + efc_node_transition(node, + __efc_d_wait_del_node, + NULL); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE || rc < 0) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + + } else if (node->targ && !node->init) { + efc_log_info(node->efc, + "[%s] delete (target) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + efc_node_transition(node, + __efc_d_wait_del_node, + NULL); + if (node->nport->enable_ini) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + + } else if (node->init && node->targ) { + efc_log_info(node->efc, + "[%s] delete (I+T) WWPN %s WWNN %s\n", + node->display_name, node->wwpn, node->wwnn); + efc_node_transition(node, __efc_d_wait_del_ini_tgt, + NULL); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + /* assume no wait needed */ + rc = EFC_SCSI_CALL_COMPLETE; + if (node->nport->enable_ini) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + } + + /* we've initiated the upcalls as needed, now kick off the node + * detach to precipitate the aborting of outstanding exchanges + * associated with said node + * + * Beware: if we've made upcall(s), we've already transitioned + * to a new state by the time we execute this. + * consider doing this before the upcalls? + */ + if (node->attached) { + /* issue hw node free; don't care if succeeds right + * away or sometime later, will check node->attached + * later in shutdown process + */ + rc = efc_cmd_node_detach(efc, &node->rnode); + if (rc < 0) + node_printf(node, + "Failed freeing HW node, rc=%d\n", + rc); + } + + /* if neither initiator nor target, proceed to cleanup */ + if (!node->init && !node->targ) { + /* + * node has either been detached or is in + * the process of being detached, + * call common node's initiate cleanup function + */ + efc_node_initiate_cleanup(node); + } + break; + } + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* Ignore, this can happen if an ELS is + * aborted while in a delay/retry state + */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_loop(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: { + /* send PLOGI automatically if initiator */ + efc_node_init_device(node, true); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +efc_send_ls_acc_after_attach(struct efc_node *node, + struct fc_frame_header *hdr, + enum efc_node_send_ls_acc ls) +{ + u16 ox_id = be16_to_cpu(hdr->fh_ox_id); + + /* Save the OX_ID for sending LS_ACC sometime later */ + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE); + + node->ls_acc_oxid = ox_id; + node->send_ls_acc = ls; + node->ls_acc_did = ntoh24(hdr->fh_d_id); +} + +void +efc_process_prli_payload(struct efc_node *node, void *prli) +{ + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = prli; + node->init = (pp->sp.spp_flags & FCP_SPPF_INIT_FCN) != 0; + node->targ = (pp->sp.spp_flags & FCP_SPPF_TARG_FCN) != 0; +} + +void +__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: /* PLOGI ACC completions */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + efc_node_transition(node, __efc_d_port_logged_in, NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* LOGO response received, sent shutdown */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_LOGO, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + node_printf(node, + "LOGO sent (evt=%s), shutdown node\n", + efc_sm_event_name(evt)); + /* sm: / post explicit logout */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +efc_node_init_device(struct efc_node *node, bool send_plogi) +{ + node->send_plogi = send_plogi; + if ((node->efc->nodedb_mask & EFC_NODEDB_PAUSE_NEW_NODES) && + (node->rnode.fc_id != FC_FID_DOM_MGR)) { + node->nodedb_state = __efc_d_init; + efc_node_transition(node, __efc_node_paused, NULL); + } else { + efc_node_transition(node, __efc_d_init, NULL); + } +} + +static void +efc_d_check_plogi_topology(struct efc_node *node, u32 d_id) +{ + switch (node->nport->topology) { + case EFC_NPORT_TOPO_P2P: + /* we're not attached and nport is p2p, + * need to attach + */ + efc_domain_attach(node->nport->domain, d_id); + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + case EFC_NPORT_TOPO_FABRIC: + /* we're not attached and nport is fabric, domain + * attach should have already been requested as part + * of the fabric state machine, wait for it + */ + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + case EFC_NPORT_TOPO_UNKNOWN: + /* Two possibilities: + * 1. received a PLOGI before our FLOGI has completed + * (possible since completion comes in on another + * CQ), thus we don't know what we're connected to + * yet; transition to a state to wait for the + * fabric node to tell us; + * 2. PLOGI received before link went down and we + * haven't performed domain attach yet. + * Note: we cannot distinguish between 1. and 2. + * so have to assume PLOGI + * was received after link back up. + */ + node_printf(node, "received PLOGI, unknown topology did=0x%x\n", + d_id); + efc_node_transition(node, __efc_d_wait_topology_notify, NULL); + break; + default: + node_printf(node, "received PLOGI, unexpected topology %d\n", + node->nport->topology); + } +} + +void +__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * This state is entered when a node is instantiated, + * either having been discovered from a name services query, + * or having received a PLOGI/FLOGI. + */ + switch (evt) { + case EFC_EVT_ENTER: + if (!node->send_plogi) + break; + /* only send if we have initiator capability, + * and domain is attached + */ + if (node->nport->enable_ini && + node->nport->domain->attached) { + efc_send_plogi(node); + + efc_node_transition(node, __efc_d_wait_plogi_rsp, NULL); + } else { + node_printf(node, "not sending plogi nport.ini=%d,", + node->nport->enable_ini); + node_printf(node, "domain attached=%d\n", + node->nport->domain->attached); + } + break; + case EFC_EVT_PLOGI_RCVD: { + /* T, or I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + int rc; + + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* domain not attached; several possibilities: */ + if (!node->nport->domain->attached) { + efc_d_check_plogi_topology(node, ntoh24(hdr->fh_d_id)); + break; + } + + /* domain already attached */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL); + + break; + } + + case EFC_EVT_FDISC_RCVD: { + __efc_d_common(__func__, ctx, evt, arg); + break; + } + + case EFC_EVT_FLOGI_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + u32 d_id = ntoh24(hdr->fh_d_id); + + /* sm: / save sparams, send FLOGI acc */ + memcpy(node->nport->domain->flogi_service_params, + cbdata->payload->dma.virt, + sizeof(struct fc_els_flogi)); + + /* send FC LS_ACC response, override s_id */ + efc_fabric_set_topology(node, EFC_NPORT_TOPO_P2P); + + efc_send_flogi_p2p_acc(node, be16_to_cpu(hdr->fh_ox_id), d_id); + + if (efc_p2p_setup(node->nport)) { + node_printf(node, "p2p failed, shutting down node\n"); + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + } + + efc_node_transition(node, __efc_p2p_wait_flogi_acc_cmpl, NULL); + break; + } + + case EFC_EVT_LOGO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a link + * down; drop and + * shut node down w/ "explicit logout" so pending + * frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + break; + } + + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + case EFC_EVT_PRLI_RCVD: + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a link + * down; drop and shut node down w/ "explicit logout" + * so pending frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + } + node_printf(node, "%s received, sending reject\n", + efc_sm_event_name(evt)); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0); + + break; + } + + case EFC_EVT_FCP_CMD_RCVD: { + /* note: problem, we're now expecting an ELS REQ completion + * from both the LOGO and PLOGI + */ + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a + * link down; drop and + * shut node down w/ "explicit logout" so pending + * frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + } + + /* Send LOGO */ + node_printf(node, "FCP_CMND received, send LOGO\n"); + if (efc_send_logo(node)) { + /* + * failed to send LOGO, go ahead and cleanup node + * anyways + */ + node_printf(node, "Failed to send LOGO\n"); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + } else { + /* sent LOGO, wait for response */ + efc_node_transition(node, + __efc_d_wait_logo_rsp, NULL); + } + break; + } + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_PLOGI_RCVD: { + /* T, or I+T */ + /* received PLOGI with svc parms, go ahead and attach node + * when PLOGI that was sent ultimately completes, it'll be a + * no-op + * + * If there is an outstanding PLOGI sent, can we set a flag + * to indicate that we don't want to retry it if it times out? + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + /* sm: domain->attached / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_FAIL, NULL); + + break; + } + + case EFC_EVT_PRLI_RCVD: + /* I, or I+T */ + /* sent PLOGI and before completion was seen, received the + * PRLI from the remote node (WCQEs and RCQEs come in on + * different queues and order of processing cannot be assumed) + * Save OXID so PRLI can be sent after the attach and continue + * to wait for PLOGI response + */ + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PRLI); + efc_node_transition(node, __efc_d_wait_plogi_rsp_recvd_prli, + NULL); + break; + + case EFC_EVT_LOGO_RCVD: /* why don't we do a shutdown here?? */ + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_FDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: + case EFC_EVT_SCR_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received, sending reject\n", + efc_sm_event_name(evt)); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0); + + break; + } + + case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ + /* Completion from PLOGI sent */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_FAIL, NULL); + + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ + /* PLOGI failed, shutdown the node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* Our PLOGI was rejected, this is ok in some cases */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + case EFC_EVT_FCP_CMD_RCVD: { + /* not logged in yet and outstanding PLOGI so don't send LOGO, + * just drop + */ + node_printf(node, "FCP_CMND received, drop\n"); + break; + } + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* + * Since we've received a PRLI, we have a port login and will + * just need to wait for the PLOGI response to do the node + * attach and then we can send the LS_ACC for the PRLI. If, + * during this time, we receive FCP_CMNDs (which is possible + * since we've already sent a PRLI and our peer may have + * accepted). At this time, we are not waiting on any other + * unsolicited frames to continue with the login process. Thus, + * it will not hurt to hold frames here. + */ + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ + /* Completion from PLOGI sent */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, + NULL); + + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* PLOGI failed, shutdown the node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + WARN_ON(!node->nport->domain->attached); + /* sm: / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, + NULL); + + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NPORT_TOPOLOGY_NOTIFY: { + enum efc_nport_topology *topology = arg; + + WARN_ON(node->nport->domain->attached); + + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); + + node_printf(node, "topology notification, topology=%d\n", + *topology); + + /* At the time the PLOGI was received, the topology was unknown, + * so we didn't know which node would perform the domain attach: + * 1. The node from which the PLOGI was sent (p2p) or + * 2. The node to which the FLOGI was sent (fabric). + */ + if (*topology == EFC_NPORT_TOPO_P2P) { + /* if this is p2p, need to attach to the domain using + * the d_id from the PLOGI received + */ + efc_domain_attach(node->nport->domain, + node->ls_acc_did); + } + /* else, if this is fabric, the domain attach + * should be performed by the fabric node (node sending FLOGI); + * just wait for attach to complete + */ + + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + } + case EFC_EVT_DOMAIN_ATTACH_OK: + WARN_ON(!node->nport->domain->attached); + node_printf(node, "domain attach ok\n"); + /* sm: / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_FAIL, NULL); + + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + switch (node->send_ls_acc) { + case EFC_NODE_SEND_LS_ACC_PLOGI: { + /* sm: send_plogi_acc is set / send PLOGI acc */ + /* Normal case for T, or I+T */ + efc_send_plogi_acc(node, node->ls_acc_oxid); + efc_node_transition(node, __efc_d_wait_plogi_acc_cmpl, + NULL); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + break; + } + case EFC_NODE_SEND_LS_ACC_PRLI: { + efc_d_send_prli_rsp(node, node->ls_acc_oxid); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + break; + } + case EFC_NODE_SEND_LS_ACC_NONE: + default: + /* Normal case for I */ + /* sm: send_plogi_acc is not set / send PLOGI acc */ + efc_node_transition(node, + __efc_d_port_logged_in, NULL); + break; + } + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "node attach failed\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + /* Handle shutdown events */ + case EFC_EVT_SHUTDOWN: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_wait_attach_evt_shutdown, + NULL); + break; + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO; + efc_node_transition(node, __efc_d_wait_attach_evt_shutdown, + NULL); + break; + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO; + efc_node_transition(node, + __efc_d_wait_attach_evt_shutdown, NULL); + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + /* wait for any of these attach events and then shutdown */ + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_port_logged_in(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* Normal case for I or I+T */ + if (node->nport->enable_ini && + !(node->rnode.fc_id != FC_FID_DOM_MGR)) { + /* sm: if enable_ini / send PRLI */ + efc_send_prli(node); + /* can now expect ELS_REQ_OK/FAIL/RJT */ + } + break; + + case EFC_EVT_FCP_CMD_RCVD: { + break; + } + + case EFC_EVT_PRLI_RCVD: { + /* Normal case for T or I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = cbdata->payload->dma.virt; + if (pp->sp.spp_type != FC_TYPE_FCP) { + /*Only FCP is supported*/ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0); + break; + } + + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_d_send_prli_rsp(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_NODE_SESS_REG_OK: + if (node->send_ls_acc == EFC_NODE_SEND_LS_ACC_PRLI) + efc_send_prli_acc(node, node->ls_acc_oxid); + + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + + case EFC_EVT_NODE_SESS_REG_FAIL: + efc_send_ls_rjt(node, node->ls_acc_oxid, ELS_RJT_UNAB, + ELS_EXPL_UNSUPR, 0); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: { /* PRLI response */ + /* Normal case for I or I+T */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / process PRLI payload */ + efc_process_prli_payload(node, cbdata->els_rsp.virt); + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_FAIL: { /* PRLI response failed */ + /* I, I+T, assume some link failure, shutdown node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_RJT: { + /* PRLI rejected by remote + * Normal for I, I+T (connected to an I) + * Node doesn't want to be a target, stay here and wait for a + * PRLI from the remote node + * if it really wants to connect to us as target + */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + } + + case EFC_EVT_SRRS_ELS_CMPL_OK: { + /* Normal T, I+T, target-server rejected the process login */ + /* This would be received only in the case where we sent + * LS_RJT for the PRLI, so + * do nothing. (note: as T only we could shutdown the node) + */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + break; + } + + case EFC_EVT_PLOGI_RCVD: { + /*sm: / save sparams, set send_plogi_acc, + *post implicit logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* Restart node attach with new service parameters, + * and send ACC + */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, + NULL); + break; + } + + case EFC_EVT_LOGO_RCVD: { + /* I, T, I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), + node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + /* sm: / post explicit logout */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_device_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + if (evt != EFC_EVT_FCP_CMD_RCVD) + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + node->fcp_enabled = true; + if (node->targ) { + efc_log_info(efc, + "[%s] found (target) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + if (node->nport->enable_ini) + efc->tt.scsi_new_node(efc, node); + } + break; + + case EFC_EVT_EXIT: + node->fcp_enabled = false; + break; + + case EFC_EVT_PLOGI_RCVD: { + /* sm: / save sparams, set send_plogi_acc, post implicit + * logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* + * Restart node attach with new service parameters, + * and send ACC + */ + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL); + break; + } + + case EFC_EVT_PRLI_RCVD: { + /* T, I+T: remote initiator is slow to get started */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = cbdata->payload->dma.virt; + if (pp->sp.spp_type != FC_TYPE_FCP) { + /*Only FCP is supported*/ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0); + break; + } + + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_prli_acc(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_PRLO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + /* sm: / send PRLO acc */ + efc_send_prlo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + /* need implicit logout? */ + break; + } + + case EFC_EVT_LOGO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + case EFC_EVT_ADISC_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + /* sm: / send ADISC acc */ + efc_send_adisc_acc(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_ABTS_RCVD: + /* sm: / process ABTS */ + efc_log_err(efc, "Unexpected event:%s\n", + efc_sm_event_name(evt)); + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + break; + + case EFC_EVT_NODE_REFOUND: + break; + + case EFC_EVT_NODE_MISSING: + if (node->nport->enable_rscn) + efc_node_transition(node, __efc_d_device_gone, NULL); + + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + /* T, or I+T, PRLI accept completed ok */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + break; + + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + /* T, or I+T, PRLI accept failed to complete */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + node_printf(node, "Failed to send PRLI LS_ACC\n"); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_device_gone(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + int rc = EFC_SCSI_CALL_COMPLETE; + int rc_2 = EFC_SCSI_CALL_COMPLETE; + static const char * const labels[] = { + "none", "initiator", "target", "initiator+target" + }; + + efc_log_info(efc, "[%s] missing (%s) WWPN %s WWNN %s\n", + node->display_name, + labels[(node->targ << 1) | (node->init)], + node->wwpn, node->wwnn); + + switch (efc_node_get_enable(node)) { + case EFC_NODE_ENABLE_T_TO_T: + case EFC_NODE_ENABLE_I_TO_T: + case EFC_NODE_ENABLE_IT_TO_T: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + case EFC_NODE_ENABLE_T_TO_I: + case EFC_NODE_ENABLE_I_TO_I: + case EFC_NODE_ENABLE_IT_TO_I: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + break; + + case EFC_NODE_ENABLE_T_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + break; + + case EFC_NODE_ENABLE_I_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + case EFC_NODE_ENABLE_IT_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + rc_2 = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + default: + rc = EFC_SCSI_CALL_COMPLETE; + break; + } + + if (rc == EFC_SCSI_CALL_COMPLETE && + rc_2 == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + + break; + } + case EFC_EVT_NODE_REFOUND: + /* two approaches, reauthenticate with PLOGI/PRLI, or ADISC */ + + /* reauthenticate with PLOGI/PRLI */ + /* efc_node_transition(node, __efc_d_discovered, NULL); */ + + /* reauthenticate with ADISC */ + /* sm: / send ADISC */ + efc_send_adisc(node); + efc_node_transition(node, __efc_d_wait_adisc_rsp, NULL); + break; + + case EFC_EVT_PLOGI_RCVD: { + /* sm: / save sparams, set send_plogi_acc, post implicit + * logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* + * Restart node attach with new service parameters, and send + * ACC + */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, + NULL); + break; + } + + case EFC_EVT_FCP_CMD_RCVD: { + /* most likely a stale frame (received prior to link down), + * if attempt to send LOGO, will probably timeout and eat + * up 20s; thus, drop FCP_CMND + */ + node_printf(node, "FCP_CMND received, drop\n"); + break; + } + case EFC_EVT_LOGO_RCVD: { + /* I, T, I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: + if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* received an LS_RJT, in this case, send shutdown + * (explicit logo) event which will unregister the node, + * and start over with PLOGI + */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / post explicit logout */ + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + + case EFC_EVT_LOGO_RCVD: { + /* In this case, we have the equivalent of an LS_RJT for + * the ADISC, so we need to abort the ADISC, and re-login + * with PLOGI + */ + /* sm: / request abort, send LOGO acc */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} diff --git a/drivers/scsi/elx/libefc/efc_device.h b/drivers/scsi/elx/libefc/efc_device.h new file mode 100644 index 000000000..3cf1d8c66 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_device.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Node state machine functions for remote device node sm + */ + +#ifndef __EFCT_DEVICE_H__ +#define __EFCT_DEVICE_H__ +void +efc_node_init_device(struct efc_node *node, bool send_plogi); +void +efc_process_prli_payload(struct efc_node *node, + void *prli); +void +efc_d_send_prli_rsp(struct efc_node *node, uint16_t ox_id); +void +efc_send_ls_acc_after_attach(struct efc_node *node, + struct fc_frame_header *hdr, + enum efc_node_send_ls_acc ls); +void +__efc_d_wait_loop(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_port_logged_in(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_device_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_device_gone(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +#endif /* __EFCT_DEVICE_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_domain.c b/drivers/scsi/elx/libefc/efc_domain.c new file mode 100644 index 000000000..ca9d7ff2c --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_domain.c @@ -0,0 +1,1088 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * domain_sm Domain State Machine: States + */ + +#include "efc.h" + +int +efc_domain_cb(void *arg, int event, void *data) +{ + struct efc *efc = arg; + struct efc_domain *domain = NULL; + int rc = 0; + unsigned long flags = 0; + + if (event != EFC_HW_DOMAIN_FOUND) + domain = data; + + /* Accept domain callback events from the user driver */ + spin_lock_irqsave(&efc->lock, flags); + switch (event) { + case EFC_HW_DOMAIN_FOUND: { + u64 fcf_wwn = 0; + struct efc_domain_record *drec = data; + + /* extract the fcf_wwn */ + fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn)); + + efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn); + + /* lookup domain, or allocate a new one */ + domain = efc->domain; + if (!domain) { + domain = efc_domain_alloc(efc, fcf_wwn); + if (!domain) { + efc_log_err(efc, "efc_domain_alloc() failed\n"); + rc = -1; + break; + } + efc_sm_transition(&domain->drvsm, __efc_domain_init, + NULL); + } + efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec); + break; + } + + case EFC_HW_DOMAIN_LOST: + domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n"); + efc->hold_frames = true; + efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL); + break; + + case EFC_HW_DOMAIN_ALLOC_OK: + domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n"); + efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL); + break; + + case EFC_HW_DOMAIN_ALLOC_FAIL: + domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n"); + efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL, + NULL); + break; + + case EFC_HW_DOMAIN_ATTACH_OK: + domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n"); + efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL); + break; + + case EFC_HW_DOMAIN_ATTACH_FAIL: + domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n"); + efc_domain_post_event(domain, + EFC_EVT_DOMAIN_ATTACH_FAIL, NULL); + break; + + case EFC_HW_DOMAIN_FREE_OK: + domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n"); + efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL); + break; + + case EFC_HW_DOMAIN_FREE_FAIL: + domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n"); + efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL); + break; + + default: + efc_log_warn(efc, "unsupported event %#x\n", event); + } + spin_unlock_irqrestore(&efc->lock, flags); + + if (efc->domain && domain->req_accept_frames) { + domain->req_accept_frames = false; + efc->hold_frames = false; + } + + return rc; +} + +static void +_efc_domain_free(struct kref *arg) +{ + struct efc_domain *domain = container_of(arg, struct efc_domain, ref); + struct efc *efc = domain->efc; + + if (efc->domain_free_cb) + (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg); + + kfree(domain); +} + +void +efc_domain_free(struct efc_domain *domain) +{ + struct efc *efc; + + efc = domain->efc; + + /* Hold frames to clear the domain pointer from the xport lookup */ + efc->hold_frames = false; + + efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn); + + xa_destroy(&domain->lookup); + efc->domain = NULL; + kref_put(&domain->ref, domain->release); +} + +struct efc_domain * +efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn) +{ + struct efc_domain *domain; + + domain = kzalloc(sizeof(*domain), GFP_ATOMIC); + if (!domain) + return NULL; + + domain->efc = efc; + domain->drvsm.app = domain; + + /* initialize refcount */ + kref_init(&domain->ref); + domain->release = _efc_domain_free; + + xa_init(&domain->lookup); + + INIT_LIST_HEAD(&domain->nport_list); + efc->domain = domain; + domain->fcf_wwn = fcf_wwn; + efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn); + + return domain; +} + +void +efc_register_domain_free_cb(struct efc *efc, + void (*callback)(struct efc *efc, void *arg), + void *arg) +{ + /* Register a callback to be called when the domain is freed */ + efc->domain_free_cb = callback; + efc->domain_free_cb_arg = arg; + if (!efc->domain && callback) + (*callback)(efc, arg); +} + +static void +__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_domain *domain = ctx->app; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* + * this can arise if an FLOGI fails on the NPORT, + * and the NPORT is shutdown + */ + break; + default: + efc_log_warn(domain->efc, "%-20s %-20s not handled\n", + funcname, efc_sm_event_name(evt)); + } +} + +static void +__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_domain *domain = ctx->app; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + break; + case EFC_EVT_DOMAIN_FOUND: + /* save drec, mark domain_found_pending */ + memcpy(&domain->pending_drec, arg, + sizeof(domain->pending_drec)); + domain->domain_found_pending = true; + break; + case EFC_EVT_DOMAIN_LOST: + /* unmark domain_found_pending */ + domain->domain_found_pending = false; + break; + + default: + efc_log_warn(domain->efc, "%-20s %-20s not handled\n", + funcname, efc_sm_event_name(evt)); + } +} + +#define std_domain_state_decl(...)\ + struct efc_domain *domain = NULL;\ + struct efc *efc = NULL;\ + \ + WARN_ON(!ctx || !ctx->app);\ + domain = ctx->app;\ + WARN_ON(!domain->efc);\ + efc = domain->efc + +void +__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_ENTER: + domain->attached = false; + break; + + case EFC_EVT_DOMAIN_FOUND: { + u32 i; + struct efc_domain_record *drec = arg; + struct efc_nport *nport; + + u64 my_wwnn = efc->req_wwnn; + u64 my_wwpn = efc->req_wwpn; + __be64 bewwpn; + + if (my_wwpn == 0 || my_wwnn == 0) { + efc_log_debug(efc, "using default hardware WWN config\n"); + my_wwpn = efc->def_wwpn; + my_wwnn = efc->def_wwnn; + } + + efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n", + my_wwpn, my_wwnn); + + /* Allocate a nport and transition to __efc_nport_allocated */ + nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX, + efc->enable_ini, efc->enable_tgt); + + if (!nport) { + efc_log_err(efc, "efc_nport_alloc() failed\n"); + break; + } + efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL); + + bewwpn = cpu_to_be64(nport->wwpn); + + /* allocate struct efc_nport object for local port + * Note: drec->fc_id is ALPA from read_topology only if loop + */ + if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) { + efc_log_err(efc, "Can't allocate port\n"); + efc_nport_free(nport); + break; + } + + domain->is_loop = drec->is_loop; + + /* + * If the loop position map includes ALPA == 0, + * then we are in a public loop (NL_PORT) + * Note that the first element of the loopmap[] + * contains the count of elements, and if + * ALPA == 0 is present, it will occupy the first + * location after the count. + */ + domain->is_nlport = drec->map.loop[1] == 0x00; + + if (!domain->is_loop) { + /* Initiate HW domain alloc */ + if (efc_cmd_domain_alloc(efc, domain, drec->index)) { + efc_log_err(efc, + "Failed to initiate HW domain allocation\n"); + break; + } + efc_sm_transition(ctx, __efc_domain_wait_alloc, arg); + break; + } + + efc_log_debug(efc, "%s fc_id=%#x speed=%d\n", + drec->is_loop ? + (domain->is_nlport ? + "public-loop" : "loop") : "other", + drec->fc_id, drec->speed); + + nport->fc_id = drec->fc_id; + nport->topology = EFC_NPORT_TOPO_FC_AL; + snprintf(nport->display_name, sizeof(nport->display_name), + "s%06x", drec->fc_id); + + if (efc->enable_ini) { + u32 count = drec->map.loop[0]; + + efc_log_debug(efc, "%d position map entries\n", + count); + for (i = 1; i <= count; i++) { + if (drec->map.loop[i] != drec->fc_id) { + struct efc_node *node; + + efc_log_debug(efc, "%#x -> %#x\n", + drec->fc_id, + drec->map.loop[i]); + node = efc_node_alloc(nport, + drec->map.loop[i], + false, true); + if (!node) { + efc_log_err(efc, + "efc_node_alloc() failed\n"); + break; + } + efc_node_transition(node, + __efc_d_wait_loop, + NULL); + } + } + } + + /* Initiate HW domain alloc */ + if (efc_cmd_domain_alloc(efc, domain, drec->index)) { + efc_log_err(efc, + "Failed to initiate HW domain allocation\n"); + break; + } + efc_sm_transition(ctx, __efc_domain_wait_alloc, arg); + break; + } + default: + __efc_domain_common(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_wait_alloc(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_DOMAIN_ALLOC_OK: { + struct fc_els_flogi *sp; + struct efc_nport *nport; + + nport = domain->nport; + if (WARN_ON(!nport)) + return; + + sp = (struct fc_els_flogi *)nport->service_params; + + /* Save the domain service parameters */ + memcpy(domain->service_params + 4, domain->dma.virt, + sizeof(struct fc_els_flogi) - 4); + memcpy(nport->service_params + 4, domain->dma.virt, + sizeof(struct fc_els_flogi) - 4); + + /* + * Update the nport's service parameters, + * user might have specified non-default names + */ + sp->fl_wwpn = cpu_to_be64(nport->wwpn); + sp->fl_wwnn = cpu_to_be64(nport->wwnn); + + /* + * Take the loop topology path, + * unless we are an NL_PORT (public loop) + */ + if (domain->is_loop && !domain->is_nlport) { + /* + * For loop, we already have our FC ID + * and don't need fabric login. + * Transition to the allocated state and + * post an event to attach to + * the domain. Note that this breaks the + * normal action/transition + * pattern here to avoid a race with the + * domain attach callback. + */ + /* sm: is_loop / domain_attach */ + efc_sm_transition(ctx, __efc_domain_allocated, NULL); + __efc_domain_attach_internal(domain, nport->fc_id); + break; + } + { + struct efc_node *node; + + /* alloc fabric node, send FLOGI */ + node = efc_node_find(nport, FC_FID_FLOGI); + if (node) { + efc_log_err(efc, + "Fabric Controller node already exists\n"); + break; + } + node = efc_node_alloc(nport, FC_FID_FLOGI, + false, false); + if (!node) { + efc_log_err(efc, + "Error: efc_node_alloc() failed\n"); + } else { + efc_node_transition(node, + __efc_fabric_init, NULL); + } + /* Accept frames */ + domain->req_accept_frames = true; + } + /* sm: / start fabric logins */ + efc_sm_transition(ctx, __efc_domain_allocated, NULL); + break; + } + + case EFC_EVT_DOMAIN_ALLOC_FAIL: + efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;", + efc_sm_event_name(evt)); + efc_log_err(efc, "shutting down domain\n"); + domain->req_domain_free = true; + break; + + case EFC_EVT_DOMAIN_FOUND: + /* Should not happen */ + break; + + case EFC_EVT_DOMAIN_LOST: + efc_log_debug(efc, + "%s received while waiting for hw_domain_alloc()\n", + efc_sm_event_name(evt)); + efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL); + break; + + default: + __efc_domain_common(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_DOMAIN_REQ_ATTACH: { + int rc = 0; + u32 fc_id; + + if (WARN_ON(!arg)) + return; + + fc_id = *((u32 *)arg); + efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n", + fc_id); + /* Update nport lookup */ + rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport, + GFP_ATOMIC)); + if (rc) { + efc_log_err(efc, "Sport lookup store failed: %d\n", rc); + return; + } + + /* Update display name for the nport */ + efc_node_fcid_display(fc_id, domain->nport->display_name, + sizeof(domain->nport->display_name)); + + /* Issue domain attach call */ + rc = efc_cmd_domain_attach(efc, domain, fc_id); + if (rc) { + efc_log_err(efc, "efc_hw_domain_attach failed: %d\n", + rc); + return; + } + /* sm: / domain_attach */ + efc_sm_transition(ctx, __efc_domain_wait_attach, NULL); + break; + } + + case EFC_EVT_DOMAIN_FOUND: + /* Should not happen */ + efc_log_err(efc, "%s: evt: %d should not happen\n", + __func__, evt); + break; + + case EFC_EVT_DOMAIN_LOST: { + efc_log_debug(efc, + "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n", + efc_sm_event_name(evt)); + if (!list_empty(&domain->nport_list)) { + /* + * if there are nports, transition to + * wait state and send shutdown to each + * nport + */ + struct efc_nport *nport = NULL, *nport_next = NULL; + + efc_sm_transition(ctx, __efc_domain_wait_nports_free, + NULL); + list_for_each_entry_safe(nport, nport_next, + &domain->nport_list, + list_entry) { + efc_sm_post_event(&nport->sm, + EFC_EVT_SHUTDOWN, NULL); + } + } else { + /* no nports exist, free domain */ + efc_sm_transition(ctx, __efc_domain_wait_shutdown, + NULL); + if (efc_cmd_domain_free(efc, domain)) + efc_log_err(efc, "hw_domain_free failed\n"); + } + + break; + } + + default: + __efc_domain_common(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_wait_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_DOMAIN_ATTACH_OK: { + struct efc_node *node = NULL; + struct efc_nport *nport, *next_nport; + unsigned long index; + + /* + * Set domain notify pending state to avoid + * duplicate domain event post + */ + domain->domain_notify_pend = true; + + /* Mark as attached */ + domain->attached = true; + + /* Transition to ready */ + /* sm: / forward event to all nports and nodes */ + efc_sm_transition(ctx, __efc_domain_ready, NULL); + + /* We have an FCFI, so we can accept frames */ + domain->req_accept_frames = true; + + /* + * Notify all nodes that the domain attach request + * has completed + * Note: nport will have already received notification + * of nport attached as a result of the HW's port attach. + */ + list_for_each_entry_safe(nport, next_nport, + &domain->nport_list, list_entry) { + xa_for_each(&nport->lookup, index, node) { + efc_node_post_event(node, + EFC_EVT_DOMAIN_ATTACH_OK, + NULL); + } + } + domain->domain_notify_pend = false; + break; + } + + case EFC_EVT_DOMAIN_ATTACH_FAIL: + efc_log_debug(efc, + "%s received while waiting for hw attach\n", + efc_sm_event_name(evt)); + break; + + case EFC_EVT_DOMAIN_FOUND: + /* Should not happen */ + efc_log_err(efc, "%s: evt: %d should not happen\n", + __func__, evt); + break; + + case EFC_EVT_DOMAIN_LOST: + /* + * Domain lost while waiting for an attach to complete, + * go to a state that waits for the domain attach to + * complete, then handle domain lost + */ + efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL); + break; + + case EFC_EVT_DOMAIN_REQ_ATTACH: + /* + * In P2P we can get an attach request from + * the other FLOGI path, so drop this one + */ + break; + + default: + __efc_domain_common(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_ENTER: { + /* start any pending vports */ + if (efc_vport_start(domain)) { + efc_log_debug(domain->efc, + "efc_vport_start didn't start vports\n"); + } + break; + } + case EFC_EVT_DOMAIN_LOST: { + if (!list_empty(&domain->nport_list)) { + /* + * if there are nports, transition to wait state + * and send shutdown to each nport + */ + struct efc_nport *nport = NULL, *nport_next = NULL; + + efc_sm_transition(ctx, __efc_domain_wait_nports_free, + NULL); + list_for_each_entry_safe(nport, nport_next, + &domain->nport_list, + list_entry) { + efc_sm_post_event(&nport->sm, + EFC_EVT_SHUTDOWN, NULL); + } + } else { + /* no nports exist, free domain */ + efc_sm_transition(ctx, __efc_domain_wait_shutdown, + NULL); + if (efc_cmd_domain_free(efc, domain)) + efc_log_err(efc, "hw_domain_free failed\n"); + } + break; + } + + case EFC_EVT_DOMAIN_FOUND: + /* Should not happen */ + efc_log_err(efc, "%s: evt: %d should not happen\n", + __func__, evt); + break; + + case EFC_EVT_DOMAIN_REQ_ATTACH: { + /* can happen during p2p */ + u32 fc_id; + + fc_id = *((u32 *)arg); + + /* Assume that the domain is attached */ + WARN_ON(!domain->attached); + + /* + * Verify that the requested FC_ID + * is the same as the one we're working with + */ + WARN_ON(domain->nport->fc_id != fc_id); + break; + } + + default: + __efc_domain_common(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + /* Wait for nodes to free prior to the domain shutdown */ + switch (evt) { + case EFC_EVT_ALL_CHILD_NODES_FREE: { + int rc; + + /* sm: / efc_hw_domain_free */ + efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL); + + /* Request efc_hw_domain_free and wait for completion */ + rc = efc_cmd_domain_free(efc, domain); + if (rc) { + efc_log_err(efc, "efc_hw_domain_free() failed: %d\n", + rc); + } + break; + } + default: + __efc_domain_common_shutdown(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + switch (evt) { + case EFC_EVT_DOMAIN_FREE_OK: + /* sm: / domain_free */ + if (domain->domain_found_pending) { + /* + * save fcf_wwn and drec from this domain, + * free current domain and allocate + * a new one with the same fcf_wwn + * could use a SLI-4 "re-register VPI" + * operation here? + */ + u64 fcf_wwn = domain->fcf_wwn; + struct efc_domain_record drec = domain->pending_drec; + + efc_log_debug(efc, "Reallocating domain\n"); + domain->req_domain_free = true; + domain = efc_domain_alloc(efc, fcf_wwn); + + if (!domain) { + efc_log_err(efc, + "efc_domain_alloc() failed\n"); + return; + } + /* + * got a new domain; at this point, + * there are at least two domains + * once the req_domain_free flag is processed, + * the associated domain will be removed. + */ + efc_sm_transition(&domain->drvsm, __efc_domain_init, + NULL); + efc_sm_post_event(&domain->drvsm, + EFC_EVT_DOMAIN_FOUND, &drec); + } else { + domain->req_domain_free = true; + } + break; + default: + __efc_domain_common_shutdown(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + std_domain_state_decl(); + + domain_sm_trace(domain); + + /* + * Wait for the domain alloc/attach completion + * after receiving a domain lost. + */ + switch (evt) { + case EFC_EVT_DOMAIN_ALLOC_OK: + case EFC_EVT_DOMAIN_ATTACH_OK: { + if (!list_empty(&domain->nport_list)) { + /* + * if there are nports, transition to + * wait state and send shutdown to each nport + */ + struct efc_nport *nport = NULL, *nport_next = NULL; + + efc_sm_transition(ctx, __efc_domain_wait_nports_free, + NULL); + list_for_each_entry_safe(nport, nport_next, + &domain->nport_list, + list_entry) { + efc_sm_post_event(&nport->sm, + EFC_EVT_SHUTDOWN, NULL); + } + } else { + /* no nports exist, free domain */ + efc_sm_transition(ctx, __efc_domain_wait_shutdown, + NULL); + if (efc_cmd_domain_free(efc, domain)) + efc_log_err(efc, "hw_domain_free() failed\n"); + } + break; + } + case EFC_EVT_DOMAIN_ALLOC_FAIL: + case EFC_EVT_DOMAIN_ATTACH_FAIL: + efc_log_err(efc, "[domain] %-20s: failed\n", + efc_sm_event_name(evt)); + break; + + default: + __efc_domain_common_shutdown(__func__, ctx, evt, arg); + } +} + +void +__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id) +{ + memcpy(domain->dma.virt, + ((uint8_t *)domain->flogi_service_params) + 4, + sizeof(struct fc_els_flogi) - 4); + (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH, + &s_id); +} + +void +efc_domain_attach(struct efc_domain *domain, u32 s_id) +{ + __efc_domain_attach_internal(domain, s_id); +} + +int +efc_domain_post_event(struct efc_domain *domain, + enum efc_sm_event event, void *arg) +{ + int rc; + bool req_domain_free; + + rc = efc_sm_post_event(&domain->drvsm, event, arg); + + req_domain_free = domain->req_domain_free; + domain->req_domain_free = false; + + if (req_domain_free) + efc_domain_free(domain); + + return rc; +} + +static void +efct_domain_process_pending(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + struct efc_hw_sequence *seq = NULL; + u32 processed = 0; + unsigned long flags = 0; + + for (;;) { + /* need to check for hold frames condition after each frame + * processed because any given frame could cause a transition + * to a state that holds frames + */ + if (efc->hold_frames) + break; + + /* Get next frame/sequence */ + spin_lock_irqsave(&efc->pend_frames_lock, flags); + + if (!list_empty(&efc->pend_frames)) { + seq = list_first_entry(&efc->pend_frames, + struct efc_hw_sequence, list_entry); + list_del(&seq->list_entry); + } + + if (!seq) { + processed = efc->pend_frames_processed; + efc->pend_frames_processed = 0; + spin_unlock_irqrestore(&efc->pend_frames_lock, flags); + break; + } + efc->pend_frames_processed++; + + spin_unlock_irqrestore(&efc->pend_frames_lock, flags); + + /* now dispatch frame(s) to dispatch function */ + if (efc_domain_dispatch_frame(domain, seq)) + efc->tt.hw_seq_free(efc, seq); + + seq = NULL; + } + + if (processed != 0) + efc_log_debug(efc, "%u domain frames held and processed\n", + processed); +} + +void +efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq) +{ + struct efc_domain *domain = efc->domain; + + /* + * If we are holding frames or the domain is not yet registered or + * there's already frames on the pending list, + * then add the new frame to pending list + */ + if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) { + unsigned long flags = 0; + + spin_lock_irqsave(&efc->pend_frames_lock, flags); + INIT_LIST_HEAD(&seq->list_entry); + list_add_tail(&seq->list_entry, &efc->pend_frames); + spin_unlock_irqrestore(&efc->pend_frames_lock, flags); + + if (domain) { + /* immediately process pending frames */ + efct_domain_process_pending(domain); + } + } else { + /* + * We are not holding frames and pending list is empty, + * just process frame. A non-zero return means the frame + * was not handled - so cleanup + */ + if (efc_domain_dispatch_frame(domain, seq)) + efc->tt.hw_seq_free(efc, seq); + } +} + +int +efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq) +{ + struct efc_domain *domain = (struct efc_domain *)arg; + struct efc *efc = domain->efc; + struct fc_frame_header *hdr; + struct efc_node *node = NULL; + struct efc_nport *nport = NULL; + unsigned long flags = 0; + u32 s_id, d_id, rc = EFC_HW_SEQ_FREE; + + if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) { + efc_log_err(efc, "Sequence header or payload is null\n"); + return rc; + } + + hdr = seq->header->dma.virt; + + /* extract the s_id and d_id */ + s_id = ntoh24(hdr->fh_s_id); + d_id = ntoh24(hdr->fh_d_id); + + spin_lock_irqsave(&efc->lock, flags); + + nport = efc_nport_find(domain, d_id); + if (!nport) { + if (hdr->fh_type == FC_TYPE_FCP) { + /* Drop frame */ + efc_log_warn(efc, "FCP frame with invalid d_id x%x\n", + d_id); + goto out; + } + + /* p2p will use this case */ + nport = domain->nport; + if (!nport || !kref_get_unless_zero(&nport->ref)) { + efc_log_err(efc, "Physical nport is NULL\n"); + goto out; + } + } + + /* Lookup the node given the remote s_id */ + node = efc_node_find(nport, s_id); + + /* If not found, then create a new node */ + if (!node) { + /* + * If this is solicited data or control based on R_CTL and + * there is no node context, then we can drop the frame + */ + if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) || + (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) { + efc_log_debug(efc, "sol data/ctrl frame without node\n"); + goto out_release; + } + + node = efc_node_alloc(nport, s_id, false, false); + if (!node) { + efc_log_err(efc, "efc_node_alloc() failed\n"); + goto out_release; + } + /* don't send PLOGI on efc_d_init entry */ + efc_node_init_device(node, false); + } + + if (node->hold_frames || !list_empty(&node->pend_frames)) { + /* add frame to node's pending list */ + spin_lock(&node->pend_frames_lock); + INIT_LIST_HEAD(&seq->list_entry); + list_add_tail(&seq->list_entry, &node->pend_frames); + spin_unlock(&node->pend_frames_lock); + rc = EFC_HW_SEQ_HOLD; + goto out_release; + } + + /* now dispatch frame to the node frame handler */ + efc_node_dispatch_frame(node, seq); + +out_release: + kref_put(&nport->ref, nport->release); +out: + spin_unlock_irqrestore(&efc->lock, flags); + return rc; +} + +void +efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq) +{ + struct fc_frame_header *hdr = seq->header->dma.virt; + u32 port_id; + struct efc_node *node = (struct efc_node *)arg; + struct efc *efc = node->efc; + + port_id = ntoh24(hdr->fh_s_id); + + if (WARN_ON(port_id != node->rnode.fc_id)) + return; + + if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) || + !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) { + node_printf(node, + "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n", + cpu_to_be32(((u32 *)hdr)[0]), + cpu_to_be32(((u32 *)hdr)[1]), + cpu_to_be32(((u32 *)hdr)[2]), + cpu_to_be32(((u32 *)hdr)[3]), + cpu_to_be32(((u32 *)hdr)[4]), + cpu_to_be32(((u32 *)hdr)[5])); + return; + } + + switch (hdr->fh_r_ctl) { + case FC_RCTL_ELS_REQ: + case FC_RCTL_ELS_REP: + efc_node_recv_els_frame(node, seq); + break; + + case FC_RCTL_BA_ABTS: + case FC_RCTL_BA_ACC: + case FC_RCTL_BA_RJT: + case FC_RCTL_BA_NOP: + efc_log_err(efc, "Received ABTS:\n"); + break; + + case FC_RCTL_DD_UNSOL_CMD: + case FC_RCTL_DD_UNSOL_CTL: + switch (hdr->fh_type) { + case FC_TYPE_FCP: + if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) { + if (!node->fcp_enabled) { + efc_node_recv_fcp_cmd(node, seq); + break; + } + efc_log_err(efc, "Recvd FCP CMD. Drop IO\n"); + } else if ((hdr->fh_r_ctl & 0xf) == + FC_RCTL_DD_SOL_DATA) { + node_printf(node, + "solicited data recvd. Drop IO\n"); + } + break; + + case FC_TYPE_CT: + efc_node_recv_ct_frame(node, seq); + break; + default: + break; + } + break; + default: + efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl); + } +} diff --git a/drivers/scsi/elx/libefc/efc_domain.h b/drivers/scsi/elx/libefc/efc_domain.h new file mode 100644 index 000000000..5468ea7ab --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_domain.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Declare driver's domain handler exported interface + */ + +#ifndef __EFCT_DOMAIN_H__ +#define __EFCT_DOMAIN_H__ + +struct efc_domain * +efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn); +void +efc_domain_free(struct efc_domain *domain); + +void +__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_domain_wait_alloc(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +__efc_domain_allocated(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +__efc_domain_wait_attach(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg); +void +efc_domain_attach(struct efc_domain *domain, u32 s_id); +int +efc_domain_post_event(struct efc_domain *domain, enum efc_sm_event event, + void *arg); +void +__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id); + +int +efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq); +void +efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq); + +#endif /* __EFCT_DOMAIN_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_els.c b/drivers/scsi/elx/libefc/efc_els.c new file mode 100644 index 000000000..84bc81d7c --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_els.c @@ -0,0 +1,1094 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Functions to build and send ELS/CT/BLS commands and responses. + */ + +#include "efc.h" +#include "efc_els.h" +#include "../libefc_sli/sli4.h" + +#define EFC_LOG_ENABLE_ELS_TRACE(efc) \ + (((efc) != NULL) ? (((efc)->logmask & (1U << 1)) != 0) : 0) + +#define node_els_trace() \ + do { \ + if (EFC_LOG_ENABLE_ELS_TRACE(efc)) \ + efc_log_info(efc, "[%s] %-20s\n", \ + node->display_name, __func__); \ + } while (0) + +#define els_io_printf(els, fmt, ...) \ + efc_log_err((struct efc *)els->node->efc,\ + "[%s] %-8s " fmt, \ + els->node->display_name,\ + els->display_name, ##__VA_ARGS__) + +#define EFC_ELS_RSP_LEN 1024 +#define EFC_ELS_GID_PT_RSP_LEN 8096 + +struct efc_els_io_req * +efc_els_io_alloc(struct efc_node *node, u32 reqlen) +{ + return efc_els_io_alloc_size(node, reqlen, EFC_ELS_RSP_LEN); +} + +struct efc_els_io_req * +efc_els_io_alloc_size(struct efc_node *node, u32 reqlen, u32 rsplen) +{ + struct efc *efc; + struct efc_els_io_req *els; + unsigned long flags = 0; + + efc = node->efc; + + if (!node->els_io_enabled) { + efc_log_err(efc, "els io alloc disabled\n"); + return NULL; + } + + els = mempool_alloc(efc->els_io_pool, GFP_ATOMIC); + if (!els) { + atomic_add_return(1, &efc->els_io_alloc_failed_count); + return NULL; + } + + /* initialize refcount */ + kref_init(&els->ref); + els->release = _efc_els_io_free; + + /* populate generic io fields */ + els->node = node; + + /* now allocate DMA for request and response */ + els->io.req.size = reqlen; + els->io.req.virt = dma_alloc_coherent(&efc->pci->dev, els->io.req.size, + &els->io.req.phys, GFP_KERNEL); + if (!els->io.req.virt) { + mempool_free(els, efc->els_io_pool); + return NULL; + } + + els->io.rsp.size = rsplen; + els->io.rsp.virt = dma_alloc_coherent(&efc->pci->dev, els->io.rsp.size, + &els->io.rsp.phys, GFP_KERNEL); + if (!els->io.rsp.virt) { + dma_free_coherent(&efc->pci->dev, els->io.req.size, + els->io.req.virt, els->io.req.phys); + mempool_free(els, efc->els_io_pool); + els = NULL; + } + + if (els) { + /* initialize fields */ + els->els_retries_remaining = EFC_FC_ELS_DEFAULT_RETRIES; + + /* add els structure to ELS IO list */ + INIT_LIST_HEAD(&els->list_entry); + spin_lock_irqsave(&node->els_ios_lock, flags); + list_add_tail(&els->list_entry, &node->els_ios_list); + spin_unlock_irqrestore(&node->els_ios_lock, flags); + } + + return els; +} + +void +efc_els_io_free(struct efc_els_io_req *els) +{ + kref_put(&els->ref, els->release); +} + +void +_efc_els_io_free(struct kref *arg) +{ + struct efc_els_io_req *els = + container_of(arg, struct efc_els_io_req, ref); + struct efc *efc; + struct efc_node *node; + int send_empty_event = false; + unsigned long flags = 0; + + node = els->node; + efc = node->efc; + + spin_lock_irqsave(&node->els_ios_lock, flags); + + list_del(&els->list_entry); + /* Send list empty event if the IO allocator + * is disabled, and the list is empty + * If node->els_io_enabled was not checked, + * the event would be posted continually + */ + send_empty_event = (!node->els_io_enabled && + list_empty(&node->els_ios_list)); + + spin_unlock_irqrestore(&node->els_ios_lock, flags); + + /* free ELS request and response buffers */ + dma_free_coherent(&efc->pci->dev, els->io.rsp.size, + els->io.rsp.virt, els->io.rsp.phys); + dma_free_coherent(&efc->pci->dev, els->io.req.size, + els->io.req.virt, els->io.req.phys); + + mempool_free(els, efc->els_io_pool); + + if (send_empty_event) + efc_scsi_io_list_empty(node->efc, node); +} + +static void +efc_els_retry(struct efc_els_io_req *els); + +static void +efc_els_delay_timer_cb(struct timer_list *t) +{ + struct efc_els_io_req *els = from_timer(els, t, delay_timer); + + /* Retry delay timer expired, retry the ELS request */ + efc_els_retry(els); +} + +static int +efc_els_req_cb(void *arg, u32 length, int status, u32 ext_status) +{ + struct efc_els_io_req *els; + struct efc_node *node; + struct efc *efc; + struct efc_node_cb cbdata; + u32 reason_code; + + els = arg; + node = els->node; + efc = node->efc; + + if (status) + els_io_printf(els, "status x%x ext x%x\n", status, ext_status); + + /* set the response len element of els->rsp */ + els->io.rsp.len = length; + + cbdata.status = status; + cbdata.ext_status = ext_status; + cbdata.header = NULL; + cbdata.els_rsp = els->io.rsp; + + /* set the response len element of els->rsp */ + cbdata.rsp_len = length; + + /* FW returns the number of bytes received on the link in + * the WCQE, not the amount placed in the buffer; use this info to + * check if there was an overrun. + */ + if (length > els->io.rsp.size) { + efc_log_warn(efc, + "ELS response returned len=%d > buflen=%zu\n", + length, els->io.rsp.size); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata); + return 0; + } + + /* Post event to ELS IO object */ + switch (status) { + case SLI4_FC_WCQE_STATUS_SUCCESS: + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_OK, &cbdata); + break; + + case SLI4_FC_WCQE_STATUS_LS_RJT: + reason_code = (ext_status >> 16) & 0xff; + + /* delay and retry if reason code is Logical Busy */ + switch (reason_code) { + case ELS_RJT_BUSY: + els->node->els_req_cnt--; + els_io_printf(els, + "LS_RJT Logical Busy, delay and retry\n"); + timer_setup(&els->delay_timer, + efc_els_delay_timer_cb, 0); + mod_timer(&els->delay_timer, + jiffies + msecs_to_jiffies(5000)); + break; + default: + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_RJT, + &cbdata); + break; + } + break; + + case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: + switch (ext_status) { + case SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT: + efc_els_retry(els); + break; + default: + efc_log_err(efc, "LOCAL_REJECT with ext status:%x\n", + ext_status); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, + &cbdata); + break; + } + break; + default: /* Other error */ + efc_log_warn(efc, "els req failed status x%x, ext_status x%x\n", + status, ext_status); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata); + break; + } + + return 0; +} + +void efc_disc_io_complete(struct efc_disc_io *io, u32 len, u32 status, + u32 ext_status) +{ + struct efc_els_io_req *els = + container_of(io, struct efc_els_io_req, io); + + WARN_ON_ONCE(!els->cb); + + ((efc_hw_srrs_cb_t)els->cb) (els, len, status, ext_status); +} + +static int efc_els_send_req(struct efc_node *node, struct efc_els_io_req *els, + enum efc_disc_io_type io_type) +{ + int rc = 0; + struct efc *efc = node->efc; + struct efc_node_cb cbdata; + + /* update ELS request counter */ + els->node->els_req_cnt++; + + /* Prepare the IO request details */ + els->io.io_type = io_type; + els->io.xmit_len = els->io.req.size; + els->io.rsp_len = els->io.rsp.size; + els->io.rpi = node->rnode.indicator; + els->io.vpi = node->nport->indicator; + els->io.s_id = node->nport->fc_id; + els->io.d_id = node->rnode.fc_id; + + if (node->rnode.attached) + els->io.rpi_registered = true; + + els->cb = efc_els_req_cb; + + rc = efc->tt.send_els(efc, &els->io); + if (!rc) + return rc; + + cbdata.status = EFC_STATUS_INVALID; + cbdata.ext_status = EFC_STATUS_INVALID; + cbdata.els_rsp = els->io.rsp; + efc_log_err(efc, "efc_els_send failed: %d\n", rc); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata); + + return rc; +} + +static void +efc_els_retry(struct efc_els_io_req *els) +{ + struct efc *efc; + struct efc_node_cb cbdata; + u32 rc; + + efc = els->node->efc; + cbdata.status = EFC_STATUS_INVALID; + cbdata.ext_status = EFC_STATUS_INVALID; + cbdata.els_rsp = els->io.rsp; + + if (els->els_retries_remaining) { + els->els_retries_remaining--; + rc = efc->tt.send_els(efc, &els->io); + } else { + rc = -EIO; + } + + if (rc) { + efc_log_err(efc, "ELS retries exhausted\n"); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata); + } +} + +static int +efc_els_acc_cb(void *arg, u32 length, int status, u32 ext_status) +{ + struct efc_els_io_req *els; + struct efc_node *node; + struct efc *efc; + struct efc_node_cb cbdata; + + els = arg; + node = els->node; + efc = node->efc; + + cbdata.status = status; + cbdata.ext_status = ext_status; + cbdata.header = NULL; + cbdata.els_rsp = els->io.rsp; + + /* Post node event */ + switch (status) { + case SLI4_FC_WCQE_STATUS_SUCCESS: + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_OK, &cbdata); + break; + + default: /* Other error */ + efc_log_warn(efc, "[%s] %-8s failed status x%x, ext x%x\n", + node->display_name, els->display_name, + status, ext_status); + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_FAIL, &cbdata); + break; + } + + return 0; +} + +static int +efc_els_send_rsp(struct efc_els_io_req *els, u32 rsplen) +{ + int rc = 0; + struct efc_node_cb cbdata; + struct efc_node *node = els->node; + struct efc *efc = node->efc; + + /* increment ELS completion counter */ + node->els_cmpl_cnt++; + + els->io.io_type = EFC_DISC_IO_ELS_RESP; + els->cb = efc_els_acc_cb; + + /* Prepare the IO request details */ + els->io.xmit_len = rsplen; + els->io.rsp_len = els->io.rsp.size; + els->io.rpi = node->rnode.indicator; + els->io.vpi = node->nport->indicator; + if (node->nport->fc_id != U32_MAX) + els->io.s_id = node->nport->fc_id; + else + els->io.s_id = els->io.iparam.els.s_id; + els->io.d_id = node->rnode.fc_id; + + if (node->attached) + els->io.rpi_registered = true; + + rc = efc->tt.send_els(efc, &els->io); + if (!rc) + return rc; + + cbdata.status = EFC_STATUS_INVALID; + cbdata.ext_status = EFC_STATUS_INVALID; + cbdata.els_rsp = els->io.rsp; + efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_FAIL, &cbdata); + + return rc; +} + +int +efc_send_plogi(struct efc_node *node) +{ + struct efc_els_io_req *els; + struct efc *efc = node->efc; + struct fc_els_flogi *plogi; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*plogi)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + els->display_name = "plogi"; + + /* Build PLOGI request */ + plogi = els->io.req.virt; + + memcpy(plogi, node->nport->service_params, sizeof(*plogi)); + + plogi->fl_cmd = ELS_PLOGI; + memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd)); + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_flogi(struct efc_node *node) +{ + struct efc_els_io_req *els; + struct efc *efc; + struct fc_els_flogi *flogi; + + efc = node->efc; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*flogi)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "flogi"; + + /* Build FLOGI request */ + flogi = els->io.req.virt; + + memcpy(flogi, node->nport->service_params, sizeof(*flogi)); + flogi->fl_cmd = ELS_FLOGI; + memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd)); + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_fdisc(struct efc_node *node) +{ + struct efc_els_io_req *els; + struct efc *efc; + struct fc_els_flogi *fdisc; + + efc = node->efc; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*fdisc)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "fdisc"; + + /* Build FDISC request */ + fdisc = els->io.req.virt; + + memcpy(fdisc, node->nport->service_params, sizeof(*fdisc)); + fdisc->fl_cmd = ELS_FDISC; + memset(fdisc->_fl_resvd, 0, sizeof(fdisc->_fl_resvd)); + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_prli(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els; + struct { + struct fc_els_prli prli; + struct fc_els_spp spp; + } *pp; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*pp)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "prli"; + + /* Build PRLI request */ + pp = els->io.req.virt; + + memset(pp, 0, sizeof(*pp)); + + pp->prli.prli_cmd = ELS_PRLI; + pp->prli.prli_spp_len = 16; + pp->prli.prli_len = cpu_to_be16(sizeof(*pp)); + pp->spp.spp_type = FC_TYPE_FCP; + pp->spp.spp_type_ext = 0; + pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR; + pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS | + (node->nport->enable_ini ? + FCP_SPPF_INIT_FCN : 0) | + (node->nport->enable_tgt ? + FCP_SPPF_TARG_FCN : 0)); + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_logo(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els; + struct fc_els_logo *logo; + struct fc_els_flogi *sparams; + + node_els_trace(); + + sparams = (struct fc_els_flogi *)node->nport->service_params; + + els = efc_els_io_alloc(node, sizeof(*logo)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "logo"; + + /* Build LOGO request */ + + logo = els->io.req.virt; + + memset(logo, 0, sizeof(*logo)); + logo->fl_cmd = ELS_LOGO; + hton24(logo->fl_n_port_id, node->rnode.nport->fc_id); + logo->fl_n_port_wwn = sparams->fl_wwpn; + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_adisc(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els; + struct fc_els_adisc *adisc; + struct fc_els_flogi *sparams; + struct efc_nport *nport = node->nport; + + node_els_trace(); + + sparams = (struct fc_els_flogi *)node->nport->service_params; + + els = efc_els_io_alloc(node, sizeof(*adisc)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "adisc"; + + /* Build ADISC request */ + + adisc = els->io.req.virt; + + memset(adisc, 0, sizeof(*adisc)); + adisc->adisc_cmd = ELS_ADISC; + hton24(adisc->adisc_hard_addr, nport->fc_id); + adisc->adisc_wwpn = sparams->fl_wwpn; + adisc->adisc_wwnn = sparams->fl_wwnn; + hton24(adisc->adisc_port_id, node->rnode.nport->fc_id); + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_scr(struct efc_node *node) +{ + struct efc_els_io_req *els; + struct efc *efc = node->efc; + struct fc_els_scr *req; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*req)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->display_name = "scr"; + + req = els->io.req.virt; + + memset(req, 0, sizeof(*req)); + req->scr_cmd = ELS_SCR; + req->scr_reg_func = ELS_SCRF_FULL; + + return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ); +} + +int +efc_send_ls_rjt(struct efc_node *node, u32 ox_id, u32 reason_code, + u32 reason_code_expl, u32 vendor_unique) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct fc_els_ls_rjt *rjt; + + els = efc_els_io_alloc(node, sizeof(*rjt)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + node_els_trace(); + + els->display_name = "ls_rjt"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + rjt = els->io.req.virt; + memset(rjt, 0, sizeof(*rjt)); + + rjt->er_cmd = ELS_LS_RJT; + rjt->er_reason = reason_code; + rjt->er_explan = reason_code_expl; + + return efc_els_send_rsp(els, sizeof(*rjt)); +} + +int +efc_send_plogi_acc(struct efc_node *node, u32 ox_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct fc_els_flogi *plogi; + struct fc_els_flogi *req = (struct fc_els_flogi *)node->service_params; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*plogi)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "plogi_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + plogi = els->io.req.virt; + + /* copy our port's service parameters to payload */ + memcpy(plogi, node->nport->service_params, sizeof(*plogi)); + plogi->fl_cmd = ELS_LS_ACC; + memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd)); + + /* Set Application header support bit if requested */ + if (req->fl_csp.sp_features & cpu_to_be16(FC_SP_FT_BCAST)) + plogi->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_BCAST); + + return efc_els_send_rsp(els, sizeof(*plogi)); +} + +int +efc_send_flogi_p2p_acc(struct efc_node *node, u32 ox_id, u32 s_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct fc_els_flogi *flogi; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*flogi)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "flogi_p2p_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + els->io.iparam.els.s_id = s_id; + + flogi = els->io.req.virt; + + /* copy our port's service parameters to payload */ + memcpy(flogi, node->nport->service_params, sizeof(*flogi)); + flogi->fl_cmd = ELS_LS_ACC; + memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd)); + + memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp)); + + return efc_els_send_rsp(els, sizeof(*flogi)); +} + +int +efc_send_prli_acc(struct efc_node *node, u32 ox_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct { + struct fc_els_prli prli; + struct fc_els_spp spp; + } *pp; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*pp)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "prli_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + pp = els->io.req.virt; + memset(pp, 0, sizeof(*pp)); + + pp->prli.prli_cmd = ELS_LS_ACC; + pp->prli.prli_spp_len = 0x10; + pp->prli.prli_len = cpu_to_be16(sizeof(*pp)); + pp->spp.spp_type = FC_TYPE_FCP; + pp->spp.spp_type_ext = 0; + pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR | FC_SPP_RESP_ACK; + + pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS | + (node->nport->enable_ini ? + FCP_SPPF_INIT_FCN : 0) | + (node->nport->enable_tgt ? + FCP_SPPF_TARG_FCN : 0)); + + return efc_els_send_rsp(els, sizeof(*pp)); +} + +int +efc_send_prlo_acc(struct efc_node *node, u32 ox_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct { + struct fc_els_prlo prlo; + struct fc_els_spp spp; + } *pp; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*pp)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "prlo_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + pp = els->io.req.virt; + memset(pp, 0, sizeof(*pp)); + pp->prlo.prlo_cmd = ELS_LS_ACC; + pp->prlo.prlo_obs = 0x10; + pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp)); + + pp->spp.spp_type = FC_TYPE_FCP; + pp->spp.spp_type_ext = 0; + pp->spp.spp_flags = FC_SPP_RESP_ACK; + + return efc_els_send_rsp(els, sizeof(*pp)); +} + +int +efc_send_ls_acc(struct efc_node *node, u32 ox_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct fc_els_ls_acc *acc; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*acc)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "ls_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + acc = els->io.req.virt; + memset(acc, 0, sizeof(*acc)); + + acc->la_cmd = ELS_LS_ACC; + + return efc_els_send_rsp(els, sizeof(*acc)); +} + +int +efc_send_logo_acc(struct efc_node *node, u32 ox_id) +{ + struct efc_els_io_req *els = NULL; + struct efc *efc = node->efc; + struct fc_els_ls_acc *logo; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*logo)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "logo_acc"; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + logo = els->io.req.virt; + memset(logo, 0, sizeof(*logo)); + + logo->la_cmd = ELS_LS_ACC; + + return efc_els_send_rsp(els, sizeof(*logo)); +} + +int +efc_send_adisc_acc(struct efc_node *node, u32 ox_id) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els = NULL; + struct fc_els_adisc *adisc; + struct fc_els_flogi *sparams; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*adisc)); + if (!els) { + efc_log_err(efc, "els IO alloc failed\n"); + return -EIO; + } + + els->display_name = "adisc_acc"; + + /* Go ahead and send the ELS_ACC */ + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + els->io.iparam.els.ox_id = ox_id; + + sparams = (struct fc_els_flogi *)node->nport->service_params; + adisc = els->io.req.virt; + memset(adisc, 0, sizeof(*adisc)); + adisc->adisc_cmd = ELS_LS_ACC; + adisc->adisc_wwpn = sparams->fl_wwpn; + adisc->adisc_wwnn = sparams->fl_wwnn; + hton24(adisc->adisc_port_id, node->rnode.nport->fc_id); + + return efc_els_send_rsp(els, sizeof(*adisc)); +} + +static inline void +fcct_build_req_header(struct fc_ct_hdr *hdr, u16 cmd, u16 max_size) +{ + hdr->ct_rev = FC_CT_REV; + hdr->ct_fs_type = FC_FST_DIR; + hdr->ct_fs_subtype = FC_NS_SUBTYPE; + hdr->ct_options = 0; + hdr->ct_cmd = cpu_to_be16(cmd); + /* words */ + hdr->ct_mr_size = cpu_to_be16(max_size / (sizeof(u32))); + hdr->ct_reason = 0; + hdr->ct_explan = 0; + hdr->ct_vendor = 0; +} + +int +efc_ns_send_rftid(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els; + struct { + struct fc_ct_hdr hdr; + struct fc_ns_rft_id rftid; + } *ct; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*ct)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ; + els->io.iparam.ct.type = FC_TYPE_CT; + els->io.iparam.ct.df_ctl = 0; + els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT; + + els->display_name = "rftid"; + + ct = els->io.req.virt; + memset(ct, 0, sizeof(*ct)); + fcct_build_req_header(&ct->hdr, FC_NS_RFT_ID, + sizeof(struct fc_ns_rft_id)); + + hton24(ct->rftid.fr_fid.fp_fid, node->rnode.nport->fc_id); + ct->rftid.fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] = + cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW)); + + return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ); +} + +int +efc_ns_send_rffid(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_els_io_req *els; + struct { + struct fc_ct_hdr hdr; + struct fc_ns_rff_id rffid; + } *ct; + + node_els_trace(); + + els = efc_els_io_alloc(node, sizeof(*ct)); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ; + els->io.iparam.ct.type = FC_TYPE_CT; + els->io.iparam.ct.df_ctl = 0; + els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT; + + els->display_name = "rffid"; + ct = els->io.req.virt; + + memset(ct, 0, sizeof(*ct)); + fcct_build_req_header(&ct->hdr, FC_NS_RFF_ID, + sizeof(struct fc_ns_rff_id)); + + hton24(ct->rffid.fr_fid.fp_fid, node->rnode.nport->fc_id); + if (node->nport->enable_ini) + ct->rffid.fr_feat |= FCP_FEAT_INIT; + if (node->nport->enable_tgt) + ct->rffid.fr_feat |= FCP_FEAT_TARG; + ct->rffid.fr_type = FC_TYPE_FCP; + + return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ); +} + +int +efc_ns_send_gidpt(struct efc_node *node) +{ + struct efc_els_io_req *els = NULL; + struct efc *efc = node->efc; + struct { + struct fc_ct_hdr hdr; + struct fc_ns_gid_pt gidpt; + } *ct; + + node_els_trace(); + + els = efc_els_io_alloc_size(node, sizeof(*ct), EFC_ELS_GID_PT_RSP_LEN); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ; + els->io.iparam.ct.type = FC_TYPE_CT; + els->io.iparam.ct.df_ctl = 0; + els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT; + + els->display_name = "gidpt"; + + ct = els->io.req.virt; + + memset(ct, 0, sizeof(*ct)); + fcct_build_req_header(&ct->hdr, FC_NS_GID_PT, + sizeof(struct fc_ns_gid_pt)); + + ct->gidpt.fn_pt_type = FC_TYPE_FCP; + + return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ); +} + +void +efc_els_io_cleanup(struct efc_els_io_req *els, int evt, void *arg) +{ + /* don't want further events that could come; e.g. abort requests + * from the node state machine; thus, disable state machine + */ + els->els_req_free = true; + efc_node_post_els_resp(els->node, evt, arg); + + efc_els_io_free(els); +} + +static int +efc_ct_acc_cb(void *arg, u32 length, int status, u32 ext_status) +{ + struct efc_els_io_req *els = arg; + + efc_els_io_free(els); + + return 0; +} + +int +efc_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id, + struct fc_ct_hdr *ct_hdr, u32 cmd_rsp_code, + u32 reason_code, u32 reason_code_explanation) +{ + struct efc_els_io_req *els = NULL; + struct fc_ct_hdr *rsp = NULL; + + els = efc_els_io_alloc(node, 256); + if (!els) { + efc_log_err(efc, "IO alloc failed\n"); + return -EIO; + } + + rsp = els->io.rsp.virt; + + *rsp = *ct_hdr; + + fcct_build_req_header(rsp, cmd_rsp_code, 0); + rsp->ct_reason = reason_code; + rsp->ct_explan = reason_code_explanation; + + els->display_name = "ct_rsp"; + els->cb = efc_ct_acc_cb; + + /* Prepare the IO request details */ + els->io.io_type = EFC_DISC_IO_CT_RESP; + els->io.xmit_len = sizeof(*rsp); + + els->io.rpi = node->rnode.indicator; + els->io.d_id = node->rnode.fc_id; + + memset(&els->io.iparam, 0, sizeof(els->io.iparam)); + + els->io.iparam.ct.ox_id = ox_id; + els->io.iparam.ct.r_ctl = 3; + els->io.iparam.ct.type = FC_TYPE_CT; + els->io.iparam.ct.df_ctl = 0; + els->io.iparam.ct.timeout = 5; + + if (efc->tt.send_els(efc, &els->io)) { + efc_els_io_free(els); + return -EIO; + } + return 0; +} + +int +efc_send_bls_acc(struct efc_node *node, struct fc_frame_header *hdr) +{ + struct sli_bls_params bls; + struct fc_ba_acc *acc; + struct efc *efc = node->efc; + + memset(&bls, 0, sizeof(bls)); + bls.ox_id = be16_to_cpu(hdr->fh_ox_id); + bls.rx_id = be16_to_cpu(hdr->fh_rx_id); + bls.s_id = ntoh24(hdr->fh_d_id); + bls.d_id = node->rnode.fc_id; + bls.rpi = node->rnode.indicator; + bls.vpi = node->nport->indicator; + + acc = (void *)bls.payload; + acc->ba_ox_id = cpu_to_be16(bls.ox_id); + acc->ba_rx_id = cpu_to_be16(bls.rx_id); + acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX); + + return efc->tt.send_bls(efc, FC_RCTL_BA_ACC, &bls); +} diff --git a/drivers/scsi/elx/libefc/efc_els.h b/drivers/scsi/elx/libefc/efc_els.h new file mode 100644 index 000000000..3c4f820f6 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_els.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFC_ELS_H__ +#define __EFC_ELS_H__ + +#define EFC_STATUS_INVALID INT_MAX +#define EFC_ELS_IO_POOL_SZ 1024 + +struct efc_els_io_req { + struct list_head list_entry; + struct kref ref; + void (*release)(struct kref *arg); + struct efc_node *node; + void *cb; + u32 els_retries_remaining; + bool els_req_free; + struct timer_list delay_timer; + + const char *display_name; + + struct efc_disc_io io; +}; + +typedef int(*efc_hw_srrs_cb_t)(void *arg, u32 length, int status, + u32 ext_status); + +void _efc_els_io_free(struct kref *arg); +struct efc_els_io_req * +efc_els_io_alloc(struct efc_node *node, u32 reqlen); +struct efc_els_io_req * +efc_els_io_alloc_size(struct efc_node *node, u32 reqlen, u32 rsplen); +void efc_els_io_free(struct efc_els_io_req *els); + +/* ELS command send */ +typedef void (*els_cb_t)(struct efc_node *node, + struct efc_node_cb *cbdata, void *arg); +int +efc_send_plogi(struct efc_node *node); +int +efc_send_flogi(struct efc_node *node); +int +efc_send_fdisc(struct efc_node *node); +int +efc_send_prli(struct efc_node *node); +int +efc_send_prlo(struct efc_node *node); +int +efc_send_logo(struct efc_node *node); +int +efc_send_adisc(struct efc_node *node); +int +efc_send_pdisc(struct efc_node *node); +int +efc_send_scr(struct efc_node *node); +int +efc_ns_send_rftid(struct efc_node *node); +int +efc_ns_send_rffid(struct efc_node *node); +int +efc_ns_send_gidpt(struct efc_node *node); +void +efc_els_io_cleanup(struct efc_els_io_req *els, int evt, void *arg); + +/* ELS acc send */ +int +efc_send_ls_acc(struct efc_node *node, u32 ox_id); +int +efc_send_ls_rjt(struct efc_node *node, u32 ox_id, u32 reason_cod, + u32 reason_code_expl, u32 vendor_unique); +int +efc_send_flogi_p2p_acc(struct efc_node *node, u32 ox_id, u32 s_id); +int +efc_send_flogi_acc(struct efc_node *node, u32 ox_id, u32 is_fport); +int +efc_send_plogi_acc(struct efc_node *node, u32 ox_id); +int +efc_send_prli_acc(struct efc_node *node, u32 ox_id); +int +efc_send_logo_acc(struct efc_node *node, u32 ox_id); +int +efc_send_prlo_acc(struct efc_node *node, u32 ox_id); +int +efc_send_adisc_acc(struct efc_node *node, u32 ox_id); + +int +efc_bls_send_acc_hdr(struct efc *efc, struct efc_node *node, + struct fc_frame_header *hdr); +int +efc_bls_send_rjt_hdr(struct efc_els_io_req *io, struct fc_frame_header *hdr); + +int +efc_els_io_list_empty(struct efc_node *node, struct list_head *list); + +/* CT */ +int +efc_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id, + struct fc_ct_hdr *ct_hdr, u32 cmd_rsp_code, u32 reason_code, + u32 reason_code_explanation); + +int +efc_send_bls_acc(struct efc_node *node, struct fc_frame_header *hdr); + +#endif /* __EFC_ELS_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_fabric.c b/drivers/scsi/elx/libefc/efc_fabric.c new file mode 100644 index 000000000..9661eea93 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_fabric.c @@ -0,0 +1,1563 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * This file implements remote node state machines for: + * - Fabric logins. + * - Fabric controller events. + * - Name/directory services interaction. + * - Point-to-point logins. + */ + +/* + * fabric_sm Node State Machine: Fabric States + * ns_sm Node State Machine: Name/Directory Services States + * p2p_sm Node State Machine: Point-to-Point Node States + */ + +#include "efc.h" + +static void +efc_fabric_initiate_shutdown(struct efc_node *node) +{ + struct efc *efc = node->efc; + + node->els_io_enabled = false; + + if (node->attached) { + int rc; + + /* issue hw node free; don't care if succeeds right away + * or sometime later, will check node->attached later in + * shutdown process + */ + rc = efc_cmd_node_detach(efc, &node->rnode); + if (rc < 0) { + node_printf(node, "Failed freeing HW node, rc=%d\n", + rc); + } + } + /* + * node has either been detached or is in the process of being detached, + * call common node's initiate cleanup function + */ + efc_node_initiate_cleanup(node); +} + +static void +__efc_fabric_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = NULL; + + node = ctx->app; + + switch (evt) { + case EFC_EVT_DOMAIN_ATTACH_OK: + break; + case EFC_EVT_SHUTDOWN: + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + + default: + /* call default event handler common to all nodes */ + __efc_node_common(funcname, ctx, evt, arg); + } +} + +void +__efc_fabric_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_REENTER: + efc_log_debug(efc, ">>> reenter !!\n"); + fallthrough; + + case EFC_EVT_ENTER: + /* send FLOGI */ + efc_send_flogi(node); + efc_node_transition(node, __efc_fabric_flogi_wait_rsp, NULL); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +efc_fabric_set_topology(struct efc_node *node, + enum efc_nport_topology topology) +{ + node->nport->topology = topology; +} + +void +efc_fabric_notify_topology(struct efc_node *node) +{ + struct efc_node *tmp_node; + unsigned long index; + + /* + * now loop through the nodes in the nport + * and send topology notification + */ + xa_for_each(&node->nport->lookup, index, tmp_node) { + if (tmp_node != node) { + efc_node_post_event(tmp_node, + EFC_EVT_NPORT_TOPOLOGY_NOTIFY, + &node->nport->topology); + } + } +} + +static bool efc_rnode_is_nport(struct fc_els_flogi *rsp) +{ + return !(ntohs(rsp->fl_csp.sp_features) & FC_SP_FT_FPORT); +} + +void +__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + + memcpy(node->nport->domain->flogi_service_params, + cbdata->els_rsp.virt, + sizeof(struct fc_els_flogi)); + + /* Check to see if the fabric is an F_PORT or and N_PORT */ + if (!efc_rnode_is_nport(cbdata->els_rsp.virt)) { + /* sm: if not nport / efc_domain_attach */ + /* ext_status has the fc_id, attach domain */ + efc_fabric_set_topology(node, EFC_NPORT_TOPO_FABRIC); + efc_fabric_notify_topology(node); + WARN_ON(node->nport->domain->attached); + efc_domain_attach(node->nport->domain, + cbdata->ext_status); + efc_node_transition(node, + __efc_fabric_wait_domain_attach, + NULL); + break; + } + + /* sm: if nport and p2p_winner / efc_domain_attach */ + efc_fabric_set_topology(node, EFC_NPORT_TOPO_P2P); + if (efc_p2p_setup(node->nport)) { + node_printf(node, + "p2p setup failed, shutting down node\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + } + + if (node->nport->p2p_winner) { + efc_node_transition(node, + __efc_p2p_wait_domain_attach, + NULL); + if (node->nport->domain->attached && + !node->nport->domain->domain_notify_pend) { + /* + * already attached, + * just send ATTACH_OK + */ + node_printf(node, + "p2p winner, domain already attached\n"); + efc_node_post_event(node, + EFC_EVT_DOMAIN_ATTACH_OK, + NULL); + } + } else { + /* + * peer is p2p winner; + * PLOGI will be received on the + * remote SID=1 node; + * this node has served its purpose + */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + } + + break; + } + + case EFC_EVT_ELS_REQ_ABORTED: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_SRRS_ELS_REQ_FAIL: { + struct efc_nport *nport = node->nport; + /* + * with these errors, we have no recovery, + * so shutdown the nport, leave the link + * up and the domain ready + */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI, + __efc_fabric_common, __func__)) { + return; + } + node_printf(node, + "FLOGI failed evt=%s, shutting down nport [%s]\n", + efc_sm_event_name(evt), nport->display_name); + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL); + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_vport_fabric_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* sm: / send FDISC */ + efc_send_fdisc(node); + efc_node_transition(node, __efc_fabric_fdisc_wait_rsp, NULL); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + /* fc_id is in ext_status */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC, + __efc_fabric_common, __func__)) { + return; + } + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / efc_nport_attach */ + efc_nport_attach(node->nport, cbdata->ext_status); + efc_node_transition(node, __efc_fabric_wait_domain_attach, + NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_SRRS_ELS_REQ_FAIL: { + if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_log_err(node->efc, "FDISC failed, shutting down nport\n"); + /* sm: / shutdown nport */ + efc_sm_post_event(&node->nport->sm, EFC_EVT_SHUTDOWN, NULL); + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +static int +efc_start_ns_node(struct efc_nport *nport) +{ + struct efc_node *ns; + + /* Instantiate a name services node */ + ns = efc_node_find(nport, FC_FID_DIR_SERV); + if (!ns) { + ns = efc_node_alloc(nport, FC_FID_DIR_SERV, false, false); + if (!ns) + return -EIO; + } + /* + * for found ns, should we be transitioning from here? + * breaks transition only + * 1. from within state machine or + * 2. if after alloc + */ + if (ns->efc->nodedb_mask & EFC_NODEDB_PAUSE_NAMESERVER) + efc_node_pause(ns, __efc_ns_init); + else + efc_node_transition(ns, __efc_ns_init, NULL); + return 0; +} + +static int +efc_start_fabctl_node(struct efc_nport *nport) +{ + struct efc_node *fabctl; + + fabctl = efc_node_find(nport, FC_FID_FCTRL); + if (!fabctl) { + fabctl = efc_node_alloc(nport, FC_FID_FCTRL, + false, false); + if (!fabctl) + return -EIO; + } + /* + * for found ns, should we be transitioning from here? + * breaks transition only + * 1. from within state machine or + * 2. if after alloc + */ + efc_node_transition(fabctl, __efc_fabctl_init, NULL); + return 0; +} + +void +__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + case EFC_EVT_NPORT_ATTACH_OK: { + int rc; + + rc = efc_start_ns_node(node->nport); + if (rc) + return; + + /* sm: if enable_ini / start fabctl node */ + /* Instantiate the fabric controller (sends SCR) */ + if (node->nport->enable_rscn) { + rc = efc_start_fabctl_node(node->nport); + if (rc) + return; + } + efc_node_transition(node, __efc_fabric_idle, NULL); + break; + } + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabric_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_DOMAIN_ATTACH_OK: + break; + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* sm: / send PLOGI */ + efc_send_plogi(node); + efc_node_transition(node, __efc_ns_plogi_wait_rsp, NULL); + break; + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + int rc; + + /* Save service parameters */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_ns_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, + NULL); + break; + } + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + /* sm: / send RFTID */ + efc_ns_send_rftid(node); + efc_node_transition(node, __efc_ns_rftid_wait_rsp, NULL); + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "Node attach failed\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + + case EFC_EVT_SHUTDOWN: + node_printf(node, "Shutdown event received\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, + __efc_fabric_wait_attach_evt_shutdown, + NULL); + break; + + /* + * if receive RSCN just ignore, + * we haven't sent GID_PT yet (ACC sent by fabctl node) + */ + case EFC_EVT_RSCN_RCVD: + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + /* wait for any of these attach events and then shutdown */ + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_fabric_initiate_shutdown(node); + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + node->attached = false; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_fabric_initiate_shutdown(node); + break; + + /* ignore shutdown event as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + node_printf(node, "Shutdown event received\n"); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: + if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFT_ID, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / send RFFID */ + efc_ns_send_rffid(node); + efc_node_transition(node, __efc_ns_rffid_wait_rsp, NULL); + break; + + /* + * if receive RSCN just ignore, + * we haven't sent GID_PT yet (ACC sent by fabctl node) + */ + case EFC_EVT_RSCN_RCVD: + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * Waits for an RFFID response event; + * if rscn enabled, a GIDPT name services request is issued. + */ + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFF_ID, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + if (node->nport->enable_rscn) { + /* sm: if enable_rscn / send GIDPT */ + efc_ns_send_gidpt(node); + + efc_node_transition(node, __efc_ns_gidpt_wait_rsp, + NULL); + } else { + /* if 'T' only, we're done, go to idle */ + efc_node_transition(node, __efc_ns_idle, NULL); + } + break; + } + /* + * if receive RSCN just ignore, + * we haven't sent GID_PT yet (ACC sent by fabctl node) + */ + case EFC_EVT_RSCN_RCVD: + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +static int +efc_process_gidpt_payload(struct efc_node *node, + void *data, u32 gidpt_len) +{ + u32 i, j; + struct efc_node *newnode; + struct efc_nport *nport = node->nport; + struct efc *efc = node->efc; + u32 port_id = 0, port_count, plist_count; + struct efc_node *n; + struct efc_node **active_nodes; + int residual; + struct { + struct fc_ct_hdr hdr; + struct fc_gid_pn_resp pn_rsp; + } *rsp; + struct fc_gid_pn_resp *gidpt; + unsigned long index; + + rsp = data; + gidpt = &rsp->pn_rsp; + residual = be16_to_cpu(rsp->hdr.ct_mr_size); + + if (residual != 0) + efc_log_debug(node->efc, "residual is %u words\n", residual); + + if (be16_to_cpu(rsp->hdr.ct_cmd) == FC_FS_RJT) { + node_printf(node, + "GIDPT request failed: rsn x%x rsn_expl x%x\n", + rsp->hdr.ct_reason, rsp->hdr.ct_explan); + return -EIO; + } + + plist_count = (gidpt_len - sizeof(struct fc_ct_hdr)) / sizeof(*gidpt); + + /* Count the number of nodes */ + port_count = 0; + xa_for_each(&nport->lookup, index, n) { + port_count++; + } + + /* Allocate a buffer for all nodes */ + active_nodes = kcalloc(port_count, sizeof(*active_nodes), GFP_ATOMIC); + if (!active_nodes) { + node_printf(node, "efc_malloc failed\n"); + return -EIO; + } + + /* Fill buffer with fc_id of active nodes */ + i = 0; + xa_for_each(&nport->lookup, index, n) { + port_id = n->rnode.fc_id; + switch (port_id) { + case FC_FID_FLOGI: + case FC_FID_FCTRL: + case FC_FID_DIR_SERV: + break; + default: + if (port_id != FC_FID_DOM_MGR) + active_nodes[i++] = n; + break; + } + } + + /* update the active nodes buffer */ + for (i = 0; i < plist_count; i++) { + hton24(gidpt[i].fp_fid, port_id); + + for (j = 0; j < port_count; j++) { + if (active_nodes[j] && + port_id == active_nodes[j]->rnode.fc_id) { + active_nodes[j] = NULL; + } + } + + if (gidpt[i].fp_resvd & FC_NS_FID_LAST) + break; + } + + /* Those remaining in the active_nodes[] are now gone ! */ + for (i = 0; i < port_count; i++) { + /* + * if we're an initiator and the remote node + * is a target, then post the node missing event. + * if we're target and we have enabled + * target RSCN, then post the node missing event. + */ + if (!active_nodes[i]) + continue; + + if ((node->nport->enable_ini && active_nodes[i]->targ) || + (node->nport->enable_tgt && enable_target_rscn(efc))) { + efc_node_post_event(active_nodes[i], + EFC_EVT_NODE_MISSING, NULL); + } else { + node_printf(node, + "GID_PT: skipping non-tgt port_id x%06x\n", + active_nodes[i]->rnode.fc_id); + } + } + kfree(active_nodes); + + for (i = 0; i < plist_count; i++) { + hton24(gidpt[i].fp_fid, port_id); + + /* Don't create node for ourselves */ + if (port_id == node->rnode.nport->fc_id) { + if (gidpt[i].fp_resvd & FC_NS_FID_LAST) + break; + continue; + } + + newnode = efc_node_find(nport, port_id); + if (!newnode) { + if (!node->nport->enable_ini) + continue; + + newnode = efc_node_alloc(nport, port_id, false, false); + if (!newnode) { + efc_log_err(efc, "efc_node_alloc() failed\n"); + return -EIO; + } + /* + * send PLOGI automatically + * if initiator + */ + efc_node_init_device(newnode, true); + } + + if (node->nport->enable_ini && newnode->targ) { + efc_node_post_event(newnode, EFC_EVT_NODE_REFOUND, + NULL); + } + + if (gidpt[i].fp_resvd & FC_NS_FID_LAST) + break; + } + return 0; +} + +void +__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + /* + * Wait for a GIDPT response from the name server. Process the FC_IDs + * that are reported by creating new remote ports, as needed. + */ + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_GID_PT, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / process GIDPT payload */ + efc_process_gidpt_payload(node, cbdata->els_rsp.virt, + cbdata->els_rsp.len); + efc_node_transition(node, __efc_ns_idle, NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_FAIL: { + /* not much we can do; will retry with the next RSCN */ + node_printf(node, "GID_PT failed to complete\n"); + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_transition(node, __efc_ns_idle, NULL); + break; + } + + /* if receive RSCN here, queue up another discovery processing */ + case EFC_EVT_RSCN_RCVD: { + node_printf(node, "RSCN received during GID_PT processing\n"); + node->rscn_pending = true; + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * Wait for RSCN received events (posted from the fabric controller) + * and restart the GIDPT name services query and processing. + */ + + switch (evt) { + case EFC_EVT_ENTER: + if (!node->rscn_pending) + break; + + node_printf(node, "RSCN pending, restart discovery\n"); + node->rscn_pending = false; + fallthrough; + + case EFC_EVT_RSCN_RCVD: { + /* sm: / send GIDPT */ + /* + * If target RSCN processing is enabled, + * and this is target only (not initiator), + * and tgt_rscn_delay is non-zero, + * then we delay issuing the GID_PT + */ + if (efc->tgt_rscn_delay_msec != 0 && + !node->nport->enable_ini && node->nport->enable_tgt && + enable_target_rscn(efc)) { + efc_node_transition(node, __efc_ns_gidpt_delay, NULL); + } else { + efc_ns_send_gidpt(node); + efc_node_transition(node, __efc_ns_gidpt_wait_rsp, + NULL); + } + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +static void +gidpt_delay_timer_cb(struct timer_list *t) +{ + struct efc_node *node = from_timer(node, t, gidpt_delay_timer); + + del_timer(&node->gidpt_delay_timer); + + efc_node_post_event(node, EFC_EVT_GIDPT_DELAY_EXPIRED, NULL); +} + +void +__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + u64 delay_msec, tmp; + + /* + * Compute the delay time. + * Set to tgt_rscn_delay, if the time since last GIDPT + * is less than tgt_rscn_period, then use tgt_rscn_period. + */ + delay_msec = efc->tgt_rscn_delay_msec; + tmp = jiffies_to_msecs(jiffies) - node->time_last_gidpt_msec; + if (tmp < efc->tgt_rscn_period_msec) + delay_msec = efc->tgt_rscn_period_msec; + + timer_setup(&node->gidpt_delay_timer, &gidpt_delay_timer_cb, + 0); + mod_timer(&node->gidpt_delay_timer, + jiffies + msecs_to_jiffies(delay_msec)); + + break; + } + + case EFC_EVT_GIDPT_DELAY_EXPIRED: + node->time_last_gidpt_msec = jiffies_to_msecs(jiffies); + + efc_ns_send_gidpt(node); + efc_node_transition(node, __efc_ns_gidpt_wait_rsp, NULL); + break; + + case EFC_EVT_RSCN_RCVD: { + efc_log_debug(efc, + "RSCN received while in GIDPT delay - no action\n"); + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabctl_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* no need to login to fabric controller, just send SCR */ + efc_send_scr(node); + efc_node_transition(node, __efc_fabctl_wait_scr_rsp, NULL); + break; + + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * Fabric controller node state machine: + * Wait for an SCR response from the fabric controller. + */ + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: + if (efc_node_check_els_req(ctx, evt, arg, ELS_SCR, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_transition(node, __efc_fabctl_ready, NULL); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +static void +efc_process_rscn(struct efc_node *node, struct efc_node_cb *cbdata) +{ + struct efc *efc = node->efc; + struct efc_nport *nport = node->nport; + struct efc_node *ns; + + /* Forward this event to the name-services node */ + ns = efc_node_find(nport, FC_FID_DIR_SERV); + if (ns) + efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, cbdata); + else + efc_log_warn(efc, "can't find name server node\n"); +} + +void +__efc_fabctl_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * Fabric controller node state machine: Ready. + * In this state, the fabric controller sends a RSCN, which is received + * by this node and is forwarded to the name services node object; and + * the RSCN LS_ACC is sent. + */ + switch (evt) { + case EFC_EVT_RSCN_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + /* + * sm: / process RSCN (forward to name services node), + * send LS_ACC + */ + efc_process_rscn(node, cbdata); + efc_send_ls_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_fabctl_wait_ls_acc_cmpl, + NULL); + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + efc_node_transition(node, __efc_fabctl_ready, NULL); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +static uint64_t +efc_get_wwpn(struct fc_els_flogi *sp) +{ + return be64_to_cpu(sp->fl_wwnn); +} + +static int +efc_rnode_is_winner(struct efc_nport *nport) +{ + struct fc_els_flogi *remote_sp; + u64 remote_wwpn; + u64 local_wwpn = nport->wwpn; + u64 wwn_bump = 0; + + remote_sp = (struct fc_els_flogi *)nport->domain->flogi_service_params; + remote_wwpn = efc_get_wwpn(remote_sp); + + local_wwpn ^= wwn_bump; + + efc_log_debug(nport->efc, "r: %llx\n", + be64_to_cpu(remote_sp->fl_wwpn)); + efc_log_debug(nport->efc, "l: %llx\n", local_wwpn); + + if (remote_wwpn == local_wwpn) { + efc_log_warn(nport->efc, + "WWPN of remote node [%08x %08x] matches local WWPN\n", + (u32)(local_wwpn >> 32ll), + (u32)local_wwpn); + return -1; + } + + return (remote_wwpn > local_wwpn); +} + +void +__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: { + struct efc_nport *nport = node->nport; + struct efc_node *rnode; + + /* + * this transient node (SID=0 (recv'd FLOGI) + * or DID=fabric (sent FLOGI)) + * is the p2p winner, will use a separate node + * to send PLOGI to peer + */ + WARN_ON(!node->nport->p2p_winner); + + rnode = efc_node_find(nport, node->nport->p2p_remote_port_id); + if (rnode) { + /* + * the "other" transient p2p node has + * already kicked off the + * new node from which PLOGI is sent + */ + node_printf(node, + "Node with fc_id x%x already exists\n", + rnode->rnode.fc_id); + } else { + /* + * create new node (SID=1, DID=2) + * from which to send PLOGI + */ + rnode = efc_node_alloc(nport, + nport->p2p_remote_port_id, + false, false); + if (!rnode) { + efc_log_err(efc, "node alloc failed\n"); + return; + } + + efc_fabric_notify_topology(node); + /* sm: / allocate p2p remote node */ + efc_node_transition(rnode, __efc_p2p_rnode_init, + NULL); + } + + /* + * the transient node (SID=0 or DID=fabric) + * has served its purpose + */ + if (node->rnode.fc_id == 0) { + /* + * if this is the SID=0 node, + * move to the init state in case peer + * has restarted FLOGI discovery and FLOGI is pending + */ + /* don't send PLOGI on efc_d_init entry */ + efc_node_init_device(node, false); + } else { + /* + * if this is the DID=fabric node + * (we initiated FLOGI), shut it down + */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + } + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_p2p_rnode_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* sm: / send PLOGI */ + efc_send_plogi(node); + efc_node_transition(node, __efc_p2p_wait_plogi_rsp, NULL); + break; + + case EFC_EVT_ABTS_RCVD: + /* sm: send BA_ACC */ + efc_send_bls_acc(node, cbdata->header->dma.virt); + + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + + /* sm: if p2p_winner / domain_attach */ + if (node->nport->p2p_winner) { + efc_node_transition(node, + __efc_p2p_wait_domain_attach, + NULL); + if (!node->nport->domain->attached) { + node_printf(node, "Domain not attached\n"); + efc_domain_attach(node->nport->domain, + node->nport->p2p_port_id); + } else { + node_printf(node, "Domain already attached\n"); + efc_node_post_event(node, + EFC_EVT_DOMAIN_ATTACH_OK, + NULL); + } + } else { + /* this node has served its purpose; + * we'll expect a PLOGI on a separate + * node (remote SID=0x1); return this node + * to init state in case peer + * restarts discovery -- it may already + * have (pending frames may exist). + */ + /* don't send PLOGI on efc_d_init entry */ + efc_node_init_device(node, false); + } + break; + + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + /* + * LS_ACC failed, possibly due to link down; + * shutdown node and wait + * for FLOGI discovery to restart + */ + node_printf(node, "FLOGI LS_ACC failed, shutting down\n"); + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + + case EFC_EVT_ABTS_RCVD: { + /* sm: / send BA_ACC */ + efc_send_bls_acc(node, cbdata->header->dma.virt); + break; + } + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: { + int rc; + + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_p2p_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, + NULL); + break; + } + case EFC_EVT_SRRS_ELS_REQ_FAIL: { + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_fabric_common, __func__)) { + return; + } + node_printf(node, "PLOGI failed, shutting down\n"); + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + } + + case EFC_EVT_PLOGI_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + /* if we're in external loopback mode, just send LS_ACC */ + if (node->efc->external_loopback) { + efc_send_plogi_acc(node, be16_to_cpu(hdr->fh_ox_id)); + } else { + /* + * if this isn't external loopback, + * pass to default handler + */ + __efc_fabric_common(__func__, ctx, evt, arg); + } + break; + } + case EFC_EVT_PRLI_RCVD: + /* I, or I+T */ + /* sent PLOGI and before completion was seen, received the + * PRLI from the remote node (WCQEs and RCQEs come in on + * different queues and order of processing cannot be assumed) + * Save OXID so PRLI can be sent after the attach and continue + * to wait for PLOGI response + */ + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PRLI); + efc_node_transition(node, __efc_p2p_wait_plogi_rsp_recvd_prli, + NULL); + break; + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* + * Since we've received a PRLI, we have a port login and will + * just need to wait for the PLOGI response to do the node + * attach and then we can send the LS_ACC for the PRLI. If, + * during this time, we receive FCP_CMNDs (which is possible + * since we've already sent a PRLI and our peer may have + * accepted). + * At this time, we are not waiting on any other unsolicited + * frames to continue with the login process. Thus, it will not + * hurt to hold frames here. + */ + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: { /* PLOGI response received */ + int rc; + + /* Completion from PLOGI sent */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_p2p_wait_node_attach, NULL); + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, + NULL); + break; + } + case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* PLOGI failed, shutdown the node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_fabric_common, __func__)) { + return; + } + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +void +__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + switch (node->send_ls_acc) { + case EFC_NODE_SEND_LS_ACC_PRLI: { + efc_d_send_prli_rsp(node->ls_acc_io, + node->ls_acc_oxid); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + break; + } + case EFC_NODE_SEND_LS_ACC_PLOGI: /* Can't happen in P2P */ + case EFC_NODE_SEND_LS_ACC_NONE: + default: + /* Normal case for I */ + /* sm: send_plogi_acc is not set / send PLOGI acc */ + efc_node_transition(node, __efc_d_port_logged_in, + NULL); + break; + } + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "Node attach failed\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_fabric_initiate_shutdown(node); + break; + + case EFC_EVT_SHUTDOWN: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, + __efc_fabric_wait_attach_evt_shutdown, + NULL); + break; + case EFC_EVT_PRLI_RCVD: + node_printf(node, "%s: PRLI received before node is attached\n", + efc_sm_event_name(evt)); + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PRLI); + break; + + default: + __efc_fabric_common(__func__, ctx, evt, arg); + } +} + +int +efc_p2p_setup(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + int rnode_winner; + + rnode_winner = efc_rnode_is_winner(nport); + + /* set nport flags to indicate p2p "winner" */ + if (rnode_winner == 1) { + nport->p2p_remote_port_id = 0; + nport->p2p_port_id = 0; + nport->p2p_winner = false; + } else if (rnode_winner == 0) { + nport->p2p_remote_port_id = 2; + nport->p2p_port_id = 1; + nport->p2p_winner = true; + } else { + /* no winner; only okay if external loopback enabled */ + if (nport->efc->external_loopback) { + /* + * External loopback mode enabled; + * local nport and remote node + * will be registered with an NPortID = 1; + */ + efc_log_debug(efc, + "External loopback mode enabled\n"); + nport->p2p_remote_port_id = 1; + nport->p2p_port_id = 1; + nport->p2p_winner = true; + } else { + efc_log_warn(efc, + "failed to determine p2p winner\n"); + return rnode_winner; + } + } + return 0; +} diff --git a/drivers/scsi/elx/libefc/efc_fabric.h b/drivers/scsi/elx/libefc/efc_fabric.h new file mode 100644 index 000000000..b0947ae6f --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_fabric.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Declarations for the interface exported by efc_fabric + */ + +#ifndef __EFCT_FABRIC_H__ +#define __EFCT_FABRIC_H__ +#include "scsi/fc/fc_els.h" +#include "scsi/fc/fc_fs.h" +#include "scsi/fc/fc_ns.h" + +void +__efc_fabric_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_domain_attach_wait(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +void +__efc_vport_fabric_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_wait_nport_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +void +__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_ns_logo_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event, void *arg); +void +__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabctl_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabctl_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabctl_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_fabric_idle(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +void +__efc_p2p_rnode_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_domain_attach_wait(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +int +efc_p2p_setup(struct efc_nport *nport); +void +efc_fabric_set_topology(struct efc_node *node, + enum efc_nport_topology topology); +void efc_fabric_notify_topology(struct efc_node *node); + +#endif /* __EFCT_FABRIC_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_node.c b/drivers/scsi/elx/libefc/efc_node.c new file mode 100644 index 000000000..a1b4ce6a2 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_node.c @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efc.h" + +int +efc_remote_node_cb(void *arg, int event, void *data) +{ + struct efc *efc = arg; + struct efc_remote_node *rnode = data; + struct efc_node *node = rnode->node; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, event, NULL); + spin_unlock_irqrestore(&efc->lock, flags); + + return 0; +} + +struct efc_node * +efc_node_find(struct efc_nport *nport, u32 port_id) +{ + /* Find an FC node structure given the FC port ID */ + return xa_load(&nport->lookup, port_id); +} + +static void +_efc_node_free(struct kref *arg) +{ + struct efc_node *node = container_of(arg, struct efc_node, ref); + struct efc *efc = node->efc; + struct efc_dma *dma; + + dma = &node->sparm_dma_buf; + dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); + memset(dma, 0, sizeof(struct efc_dma)); + mempool_free(node, efc->node_pool); +} + +struct efc_node *efc_node_alloc(struct efc_nport *nport, + u32 port_id, bool init, bool targ) +{ + int rc; + struct efc_node *node = NULL; + struct efc *efc = nport->efc; + struct efc_dma *dma; + + if (nport->shutting_down) { + efc_log_debug(efc, "node allocation when shutting down %06x", + port_id); + return NULL; + } + + node = mempool_alloc(efc->node_pool, GFP_ATOMIC); + if (!node) { + efc_log_err(efc, "node allocation failed %06x", port_id); + return NULL; + } + memset(node, 0, sizeof(*node)); + + dma = &node->sparm_dma_buf; + dma->size = NODE_SPARAMS_SIZE; + dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys); + if (!dma->virt) { + efc_log_err(efc, "node dma alloc failed\n"); + goto dma_fail; + } + node->rnode.indicator = U32_MAX; + node->nport = nport; + + node->efc = efc; + node->init = init; + node->targ = targ; + + spin_lock_init(&node->pend_frames_lock); + INIT_LIST_HEAD(&node->pend_frames); + spin_lock_init(&node->els_ios_lock); + INIT_LIST_HEAD(&node->els_ios_list); + node->els_io_enabled = true; + + rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport); + if (rc) { + efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc); + goto hw_alloc_fail; + } + + node->rnode.node = node; + node->sm.app = node; + node->evtdepth = 0; + + efc_node_update_display_name(node); + + rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC)); + if (rc) { + efc_log_err(efc, "Node lookup store failed: %d\n", rc); + goto xa_fail; + } + + /* initialize refcount */ + kref_init(&node->ref); + node->release = _efc_node_free; + kref_get(&nport->ref); + + return node; + +xa_fail: + efc_node_free_resources(efc, &node->rnode); +hw_alloc_fail: + dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); +dma_fail: + mempool_free(node, efc->node_pool); + return NULL; +} + +void +efc_node_free(struct efc_node *node) +{ + struct efc_nport *nport; + struct efc *efc; + int rc = 0; + struct efc_node *ns = NULL; + + nport = node->nport; + efc = node->efc; + + node_printf(node, "Free'd\n"); + + if (node->refound) { + /* + * Save the name server node. We will send fake RSCN event at + * the end to handle ignored RSCN event during node deletion + */ + ns = efc_node_find(node->nport, FC_FID_DIR_SERV); + } + + if (!node->nport) { + efc_log_err(efc, "Node already Freed\n"); + return; + } + + /* Free HW resources */ + rc = efc_node_free_resources(efc, &node->rnode); + if (rc < 0) + efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); + + /* if the gidpt_delay_timer is still running, then delete it */ + if (timer_pending(&node->gidpt_delay_timer)) + del_timer(&node->gidpt_delay_timer); + + xa_erase(&nport->lookup, node->rnode.fc_id); + + /* + * If the node_list is empty, + * then post a ALL_CHILD_NODES_FREE event to the nport, + * after the lock is released. + * The nport may be free'd as a result of the event. + */ + if (xa_empty(&nport->lookup)) + efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE, + NULL); + + node->nport = NULL; + node->sm.current_state = NULL; + + kref_put(&nport->ref, nport->release); + kref_put(&node->ref, node->release); + + if (ns) { + /* sending fake RSCN event to name server node */ + efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL); + } +} + +static void +efc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length) +{ + if (!dma || !buffer || !buffer_length) + return; + + if (buffer_length > dma->size) + buffer_length = dma->size; + + memcpy(dma->virt, buffer, buffer_length); + dma->len = buffer_length; +} + +int +efc_node_attach(struct efc_node *node) +{ + int rc = 0; + struct efc_nport *nport = node->nport; + struct efc_domain *domain = nport->domain; + struct efc *efc = node->efc; + + if (!domain->attached) { + efc_log_err(efc, "Warning: unattached domain\n"); + return -EIO; + } + /* Update node->wwpn/wwnn */ + + efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn), + efc_node_get_wwpn(node)); + efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn), + efc_node_get_wwnn(node)); + + efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4, + sizeof(node->service_params) - 4); + + /* take lock to protect node->rnode.attached */ + rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf); + if (rc < 0) + efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc); + + return rc; +} + +void +efc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length) +{ + switch (fc_id) { + case FC_FID_FLOGI: + snprintf(buffer, buffer_length, "fabric"); + break; + case FC_FID_FCTRL: + snprintf(buffer, buffer_length, "fabctl"); + break; + case FC_FID_DIR_SERV: + snprintf(buffer, buffer_length, "nserve"); + break; + default: + if (fc_id == FC_FID_DOM_MGR) { + snprintf(buffer, buffer_length, "dctl%02x", + (fc_id & 0x0000ff)); + } else { + snprintf(buffer, buffer_length, "%06x", fc_id); + } + break; + } +} + +void +efc_node_update_display_name(struct efc_node *node) +{ + u32 port_id = node->rnode.fc_id; + struct efc_nport *nport = node->nport; + char portid_display[16]; + + efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); + + snprintf(node->display_name, sizeof(node->display_name), "%s.%s", + nport->display_name, portid_display); +} + +void +efc_node_send_ls_io_cleanup(struct efc_node *node) +{ + if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) { + efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n", + node->display_name, node->ls_acc_oxid); + + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + } +} + +static void efc_node_handle_implicit_logo(struct efc_node *node) +{ + int rc; + + /* + * currently, only case for implicit logo is PLOGI + * recvd. Thus, node's ELS IO pending list won't be + * empty (PLOGI will be on it) + */ + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); + node_printf(node, "Reason: implicit logout, re-authenticate\n"); + + /* Re-attach node with the same HW node resources */ + node->req_free = false; + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + node->els_io_enabled = true; + + if (rc < 0) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL); +} + +static void efc_node_handle_explicit_logo(struct efc_node *node) +{ + s8 pend_frames_empty; + unsigned long flags = 0; + + /* cleanup any pending LS_ACC ELSs */ + efc_node_send_ls_io_cleanup(node); + + spin_lock_irqsave(&node->pend_frames_lock, flags); + pend_frames_empty = list_empty(&node->pend_frames); + spin_unlock_irqrestore(&node->pend_frames_lock, flags); + + /* + * there are two scenarios where we want to keep + * this node alive: + * 1. there are pending frames that need to be + * processed or + * 2. we're an initiator and the remote node is + * a target and we need to re-authenticate + */ + node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty); + node_printf(node, "nport.ini=%d node.tgt=%d\n", + node->nport->enable_ini, node->targ); + if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) { + u8 send_plogi = false; + + if (node->nport->enable_ini && node->targ) { + /* + * we're an initiator and + * node shutting down is a target; + * we'll need to re-authenticate in + * initial state + */ + send_plogi = true; + } + + /* + * transition to __efc_d_init + * (will retain HW node resources) + */ + node->els_io_enabled = true; + node->req_free = false; + + /* + * either pending frames exist or we are re-authenticating + * with PLOGI (or both); in either case, return to initial + * state + */ + efc_node_init_device(node, send_plogi); + } + /* else: let node shutdown occur */ +} + +static void +efc_node_purge_pending(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_hw_sequence *frame, *next; + unsigned long flags = 0; + + spin_lock_irqsave(&node->pend_frames_lock, flags); + + list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) { + list_del(&frame->list_entry); + efc->tt.hw_seq_free(efc, frame); + } + + spin_unlock_irqrestore(&node->pend_frames_lock, flags); +} + +void +__efc_node_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + efc_node_hold_frames(node); + WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); + /* by default, we will be freeing node after we unwind */ + node->req_free = true; + + switch (node->shutdown_reason) { + case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO: + /* Node shutdown b/c of PLOGI received when node + * already logged in. We have PLOGI service + * parameters, so submit node attach; we won't be + * freeing this node + */ + + efc_node_handle_implicit_logo(node); + break; + + case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: + efc_node_handle_explicit_logo(node); + break; + + case EFC_NODE_SHUTDOWN_DEFAULT: + default: { + /* + * shutdown due to link down, + * node going away (xport event) or + * nport shutdown, purge pending and + * proceed to cleanup node + */ + + /* cleanup any pending LS_ACC ELSs */ + efc_node_send_ls_io_cleanup(node); + + node_printf(node, + "Shutdown reason: default, purge pending\n"); + efc_node_purge_pending(node); + break; + } + } + + break; + } + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +static bool +efc_node_check_els_quiesced(struct efc_node *node) +{ + /* check to see if ELS requests, completions are quiesced */ + if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 && + efc_els_io_list_empty(node, &node->els_ios_list)) { + if (!node->attached) { + /* hw node detach already completed, proceed */ + node_printf(node, "HW node not attached\n"); + efc_node_transition(node, + __efc_node_wait_ios_shutdown, + NULL); + } else { + /* + * hw node detach hasn't completed, + * transition and wait + */ + node_printf(node, "HW node still attached\n"); + efc_node_transition(node, __efc_node_wait_node_free, + NULL); + } + return true; + } + return false; +} + +void +efc_node_initiate_cleanup(struct efc_node *node) +{ + /* + * if ELS's have already been quiesced, will move to next state + * if ELS's have not been quiesced, abort them + */ + if (!efc_node_check_els_quiesced(node)) { + efc_node_hold_frames(node); + efc_node_transition(node, __efc_node_wait_els_shutdown, NULL); + } +} + +void +__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + bool check_quiesce = false; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + /* Node state machine: Wait for all ELSs to complete */ + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + if (efc_els_io_list_empty(node, &node->els_ios_list)) { + node_printf(node, "All ELS IOs complete\n"); + check_quiesce = true; + } + break; + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_ELS_REQ_ABORTED: + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + check_quiesce = true; + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + if (WARN_ON(!node->els_cmpl_cnt)) + break; + node->els_cmpl_cnt--; + check_quiesce = true; + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* all ELS IO's complete */ + node_printf(node, "All ELS IOs complete\n"); + WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); + check_quiesce = true; + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + check_quiesce = true; + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } + + if (check_quiesce) + efc_node_check_els_quiesced(node); +} + +void +__efc_node_wait_node_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_FREE_OK: + /* node is officially no longer attached */ + node->attached = false; + efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL); + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + /* As IOs and ELS IO's complete we expect to get these events */ + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + + /* first check to see if no ELS IOs are outstanding */ + if (efc_els_io_list_empty(node, &node->els_ios_list)) + /* If there are any active IOS, Free them. */ + efc_node_transition(node, __efc_node_shutdown, NULL); + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + if (efc_els_io_list_empty(node, &node->els_ios_list)) + efc_node_transition(node, __efc_node_shutdown, NULL); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s\n", node->display_name, + efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = NULL; + struct efc *efc = NULL; + struct efc_node_cb *cbdata = arg; + + node = ctx->app; + efc = node->efc; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + case EFC_EVT_NPORT_TOPOLOGY_NOTIFY: + case EFC_EVT_NODE_MISSING: + case EFC_EVT_FCP_CMD_RCVD: + break; + + case EFC_EVT_NODE_REFOUND: + node->refound = true; + break; + + /* + * node->attached must be set appropriately + * for all node attach/detach events + */ + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + break; + + case EFC_EVT_NODE_FREE_OK: + case EFC_EVT_NODE_ATTACH_FAIL: + node->attached = false; + break; + + /* + * handle any ELS completions that + * other states either didn't care about + * or forgot about + */ + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + if (WARN_ON(!node->els_cmpl_cnt)) + break; + node->els_cmpl_cnt--; + break; + + /* + * handle any ELS request completions that + * other states either didn't care about + * or forgot about + */ + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_ELS_REQ_ABORTED: + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + break; + + case EFC_EVT_ELS_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + /* + * Unsupported ELS was received, + * send LS_RJT, command not supported + */ + efc_log_debug(efc, + "[%s] (%s) ELS x%02x, LS_RJT not supported\n", + node->display_name, funcname, + ((u8 *)cbdata->payload->dma.virt)[0]); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNSUP, ELS_EXPL_NONE, 0); + break; + } + + case EFC_EVT_PLOGI_RCVD: + case EFC_EVT_FLOGI_RCVD: + case EFC_EVT_LOGO_RCVD: + case EFC_EVT_PRLI_RCVD: + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_FDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: + case EFC_EVT_SCR_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + /* sm: / send ELS_RJT */ + efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + /* if we didn't catch this in a state, send generic LS_RJT */ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_NONE, 0); + break; + } + case EFC_EVT_ABTS_RCVD: { + efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + + /* sm: / send BA_ACC */ + efc_send_bls_acc(node, cbdata->header->dma.virt); + break; + } + + default: + efc_log_debug(node->efc, "[%s] %-20s %-20s not handled\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + } +} + +void +efc_node_save_sparms(struct efc_node *node, void *payload) +{ + memcpy(node->service_params, payload, sizeof(node->service_params)); +} + +void +efc_node_post_event(struct efc_node *node, + enum efc_sm_event evt, void *arg) +{ + bool free_node = false; + + node->evtdepth++; + + efc_sm_post_event(&node->sm, evt, arg); + + /* If our event call depth is one and + * we're not holding frames + * then we can dispatch any pending frames. + * We don't want to allow the efc_process_node_pending() + * call to recurse. + */ + if (!node->hold_frames && node->evtdepth == 1) + efc_process_node_pending(node); + + node->evtdepth--; + + /* + * Free the node object if so requested, + * and we're at an event call depth of zero + */ + if (node->evtdepth == 0 && node->req_free) + free_node = true; + + if (free_node) + efc_node_free(node); +} + +void +efc_node_transition(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *), void *data) +{ + struct efc_sm_ctx *ctx = &node->sm; + + if (ctx->current_state == state) { + efc_node_post_event(node, EFC_EVT_REENTER, data); + } else { + efc_node_post_event(node, EFC_EVT_EXIT, data); + ctx->current_state = state; + efc_node_post_event(node, EFC_EVT_ENTER, data); + } +} + +void +efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name) +{ + memset(buf, 0, buf_len); + + snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name); +} + +u64 +efc_node_get_wwpn(struct efc_node *node) +{ + struct fc_els_flogi *sp = + (struct fc_els_flogi *)node->service_params; + + return be64_to_cpu(sp->fl_wwpn); +} + +u64 +efc_node_get_wwnn(struct efc_node *node) +{ + struct fc_els_flogi *sp = + (struct fc_els_flogi *)node->service_params; + + return be64_to_cpu(sp->fl_wwnn); +} + +int +efc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, + u8 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname) +{ + return 0; +} + +int +efc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, + u16 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname) +{ + return 0; +} + +int +efc_els_io_list_empty(struct efc_node *node, struct list_head *list) +{ + int empty; + unsigned long flags = 0; + + spin_lock_irqsave(&node->els_ios_lock, flags); + empty = list_empty(list); + spin_unlock_irqrestore(&node->els_ios_lock, flags); + return empty; +} + +void +efc_node_pause(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *)) + +{ + node->nodedb_state = state; + efc_node_transition(node, __efc_node_paused, NULL); +} + +void +__efc_node_paused(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * This state is entered when a state is "paused". When resumed, the + * node is transitioned to a previously saved state (node->ndoedb_state) + */ + switch (evt) { + case EFC_EVT_ENTER: + node_printf(node, "Paused\n"); + break; + + case EFC_EVT_RESUME: { + void (*pf)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + + pf = node->nodedb_state; + + node->nodedb_state = NULL; + efc_node_transition(node, pf, NULL); + break; + } + + case EFC_EVT_DOMAIN_ATTACH_OK: + break; + + case EFC_EVT_SHUTDOWN: + node->req_free = true; + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +efc_node_recv_els_frame(struct efc_node *node, + struct efc_hw_sequence *seq) +{ + u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp); + struct { + u32 cmd; + enum efc_sm_event evt; + u32 payload_size; + } els_cmd_list[] = { + {ELS_PLOGI, EFC_EVT_PLOGI_RCVD, sizeof(struct fc_els_flogi)}, + {ELS_FLOGI, EFC_EVT_FLOGI_RCVD, sizeof(struct fc_els_flogi)}, + {ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)}, + {ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size}, + {ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size}, + {ELS_PDISC, EFC_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_FDISC, EFC_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_ADISC, EFC_EVT_ADISC_RCVD, sizeof(struct fc_els_adisc)}, + {ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD}, + }; + struct efc_node_cb cbdata; + u8 *buf = seq->payload->dma.virt; + enum efc_sm_event evt = EFC_EVT_ELS_RCVD; + u32 i; + + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.header = seq->header; + cbdata.payload = seq->payload; + + /* find a matching event for the ELS command */ + for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) { + if (els_cmd_list[i].cmd == buf[0]) { + evt = els_cmd_list[i].evt; + break; + } + } + + efc_node_post_event(node, evt, &cbdata); +} + +void +efc_node_recv_ct_frame(struct efc_node *node, + struct efc_hw_sequence *seq) +{ + struct fc_ct_hdr *iu = seq->payload->dma.virt; + struct fc_frame_header *hdr = seq->header->dma.virt; + struct efc *efc = node->efc; + u16 gscmd = be16_to_cpu(iu->ct_cmd); + + efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n", + node->display_name, gscmd); + efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu, + FC_FS_RJT, FC_FS_RJT_UNSUP, 0); +} + +void +efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq) +{ + struct efc_node_cb cbdata; + + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.header = seq->header; + cbdata.payload = seq->payload; + + efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata); +} + +void +efc_process_node_pending(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_hw_sequence *seq = NULL; + u32 pend_frames_processed = 0; + unsigned long flags = 0; + + for (;;) { + /* need to check for hold frames condition after each frame + * processed because any given frame could cause a transition + * to a state that holds frames + */ + if (node->hold_frames) + break; + + seq = NULL; + /* Get next frame/sequence */ + spin_lock_irqsave(&node->pend_frames_lock, flags); + + if (!list_empty(&node->pend_frames)) { + seq = list_first_entry(&node->pend_frames, + struct efc_hw_sequence, list_entry); + list_del(&seq->list_entry); + } + spin_unlock_irqrestore(&node->pend_frames_lock, flags); + + if (!seq) { + pend_frames_processed = node->pend_frames_processed; + node->pend_frames_processed = 0; + break; + } + node->pend_frames_processed++; + + /* now dispatch frame(s) to dispatch function */ + efc_node_dispatch_frame(node, seq); + efc->tt.hw_seq_free(efc, seq); + } + + if (pend_frames_processed != 0) + efc_log_debug(efc, "%u node frames held and processed\n", + pend_frames_processed); +} + +void +efc_scsi_sess_reg_complete(struct efc_node *node, u32 status) +{ + unsigned long flags = 0; + enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK; + struct efc *efc = node->efc; + + if (status) + evt = EFC_EVT_NODE_SESS_REG_FAIL; + + spin_lock_irqsave(&efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, evt, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void +efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void +efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void +efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg) +{ + struct efc *efc = node->efc; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, evt, arg); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void efc_node_post_shutdown(struct efc_node *node, void *arg) +{ + unsigned long flags = 0; + struct efc *efc = node->efc; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg); + spin_unlock_irqrestore(&efc->lock, flags); +} diff --git a/drivers/scsi/elx/libefc/efc_node.h b/drivers/scsi/elx/libefc/efc_node.h new file mode 100644 index 000000000..e9c600ac4 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_node.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#if !defined(__EFC_NODE_H__) +#define __EFC_NODE_H__ +#include "scsi/fc/fc_ns.h" + +#define EFC_NODEDB_PAUSE_FABRIC_LOGIN (1 << 0) +#define EFC_NODEDB_PAUSE_NAMESERVER (1 << 1) +#define EFC_NODEDB_PAUSE_NEW_NODES (1 << 2) + +#define MAX_ACC_REJECT_PAYLOAD sizeof(struct fc_els_ls_rjt) + +#define scsi_io_printf(io, fmt, ...) \ + efc_log_debug(io->efc, "[%s] [%04x][i:%04x t:%04x h:%04x]" fmt, \ + io->node->display_name, io->instance_index, io->init_task_tag, \ + io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__) + +static inline void +efc_node_evt_set(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + const char *handler) +{ + struct efc_node *node = ctx->app; + + if (evt == EFC_EVT_ENTER) { + strncpy(node->current_state_name, handler, + sizeof(node->current_state_name)); + } else if (evt == EFC_EVT_EXIT) { + strncpy(node->prev_state_name, node->current_state_name, + sizeof(node->prev_state_name)); + strncpy(node->current_state_name, "invalid", + sizeof(node->current_state_name)); + } + node->prev_evt = node->current_evt; + node->current_evt = evt; +} + +/** + * hold frames in pending frame list + * + * Unsolicited receive frames are held on the node pending frame list, + * rather than being processed. + */ + +static inline void +efc_node_hold_frames(struct efc_node *node) +{ + node->hold_frames = true; +} + +/** + * accept frames + * + * Unsolicited receive frames processed rather than being held on the node + * pending frame list. + */ + +static inline void +efc_node_accept_frames(struct efc_node *node) +{ + node->hold_frames = false; +} + +/* + * Node initiator/target enable defines + * All combinations of the SLI port (nport) initiator/target enable, + * and remote node initiator/target enable are enumerated. + * ex: EFC_NODE_ENABLE_T_TO_IT decodes to target mode is enabled on SLI port + * and I+T is enabled on remote node. + */ +enum efc_node_enable { + EFC_NODE_ENABLE_x_TO_x, + EFC_NODE_ENABLE_x_TO_T, + EFC_NODE_ENABLE_x_TO_I, + EFC_NODE_ENABLE_x_TO_IT, + EFC_NODE_ENABLE_T_TO_x, + EFC_NODE_ENABLE_T_TO_T, + EFC_NODE_ENABLE_T_TO_I, + EFC_NODE_ENABLE_T_TO_IT, + EFC_NODE_ENABLE_I_TO_x, + EFC_NODE_ENABLE_I_TO_T, + EFC_NODE_ENABLE_I_TO_I, + EFC_NODE_ENABLE_I_TO_IT, + EFC_NODE_ENABLE_IT_TO_x, + EFC_NODE_ENABLE_IT_TO_T, + EFC_NODE_ENABLE_IT_TO_I, + EFC_NODE_ENABLE_IT_TO_IT, +}; + +static inline enum efc_node_enable +efc_node_get_enable(struct efc_node *node) +{ + u32 retval = 0; + + if (node->nport->enable_ini) + retval |= (1U << 3); + if (node->nport->enable_tgt) + retval |= (1U << 2); + if (node->init) + retval |= (1U << 1); + if (node->targ) + retval |= (1U << 0); + return (enum efc_node_enable)retval; +} + +int +efc_node_check_els_req(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg, + u8 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname); +int +efc_node_check_ns_req(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg, + u16 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname); +int +efc_node_attach(struct efc_node *node); +struct efc_node * +efc_node_alloc(struct efc_nport *nport, u32 port_id, + bool init, bool targ); +void +efc_node_free(struct efc_node *efc); +void +efc_node_update_display_name(struct efc_node *node); +void efc_node_post_event(struct efc_node *node, enum efc_sm_event evt, + void *arg); + +void +__efc_node_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_node_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +efc_node_save_sparms(struct efc_node *node, void *payload); +void +efc_node_transition(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, enum efc_sm_event, + void *), void *data); +void +__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +void +efc_node_initiate_cleanup(struct efc_node *node); + +void +efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name); + +void +efc_node_pause(struct efc_node *node, + void (*state)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg)); +void +__efc_node_paused(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +int +efc_node_active_ios_empty(struct efc_node *node); +void +efc_node_send_ls_io_cleanup(struct efc_node *node); + +int +efc_els_io_list_empty(struct efc_node *node, struct list_head *list); + +void +efc_process_node_pending(struct efc_node *domain); + +u64 efc_node_get_wwnn(struct efc_node *node); +struct efc_node * +efc_node_find(struct efc_nport *nport, u32 id); +void +efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg); +void +efc_node_recv_els_frame(struct efc_node *node, struct efc_hw_sequence *s); +void +efc_node_recv_ct_frame(struct efc_node *node, struct efc_hw_sequence *seq); +void +efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq); + +#endif /* __EFC_NODE_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_nport.c b/drivers/scsi/elx/libefc/efc_nport.c new file mode 100644 index 000000000..2e83a6679 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_nport.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * NPORT + * + * Port object for physical port and NPIV ports. + */ + +/* + * NPORT REFERENCE COUNTING + * + * A nport reference should be taken when: + * - an nport is allocated + * - a vport populates associated nport + * - a remote node is allocated + * - a unsolicited frame is processed + * The reference should be dropped when: + * - the unsolicited frame processesing is done + * - the remote node is removed + * - the vport is removed + * - the nport is removed + */ + +#include "efc.h" + +void +efc_nport_cb(void *arg, int event, void *data) +{ + struct efc *efc = arg; + struct efc_nport *nport = data; + unsigned long flags = 0; + + efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event)); + + spin_lock_irqsave(&efc->lock, flags); + efc_sm_post_event(&nport->sm, event, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +static struct efc_nport * +efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn) +{ + struct efc_nport *nport = NULL; + + /* Find a nport, given the WWNN and WWPN */ + list_for_each_entry(nport, &domain->nport_list, list_entry) { + if (nport->wwnn == wwnn && nport->wwpn == wwpn) + return nport; + } + return NULL; +} + +static void +_efc_nport_free(struct kref *arg) +{ + struct efc_nport *nport = container_of(arg, struct efc_nport, ref); + + kfree(nport); +} + +struct efc_nport * +efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool enable_ini, bool enable_tgt) +{ + struct efc_nport *nport; + + if (domain->efc->enable_ini) + enable_ini = 0; + + /* Return a failure if this nport has already been allocated */ + if ((wwpn != 0) || (wwnn != 0)) { + nport = efc_nport_find_wwn(domain, wwnn, wwpn); + if (nport) { + efc_log_err(domain->efc, + "NPORT %016llX %016llX already allocated\n", + wwnn, wwpn); + return NULL; + } + } + + nport = kzalloc(sizeof(*nport), GFP_ATOMIC); + if (!nport) + return nport; + + /* initialize refcount */ + kref_init(&nport->ref); + nport->release = _efc_nport_free; + + nport->efc = domain->efc; + snprintf(nport->display_name, sizeof(nport->display_name), "------"); + nport->domain = domain; + xa_init(&nport->lookup); + nport->instance_index = domain->nport_count++; + nport->sm.app = nport; + nport->enable_ini = enable_ini; + nport->enable_tgt = enable_tgt; + nport->enable_rscn = (nport->enable_ini || + (nport->enable_tgt && enable_target_rscn(nport->efc))); + + /* Copy service parameters from domain */ + memcpy(nport->service_params, domain->service_params, + sizeof(struct fc_els_flogi)); + + /* Update requested fc_id */ + nport->fc_id = fc_id; + + /* Update the nport's service parameters for the new wwn's */ + nport->wwpn = wwpn; + nport->wwnn = wwnn; + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX", + (unsigned long long)wwnn); + + /* + * if this is the "first" nport of the domain, + * then make it the "phys" nport + */ + if (list_empty(&domain->nport_list)) + domain->nport = nport; + + INIT_LIST_HEAD(&nport->list_entry); + list_add_tail(&nport->list_entry, &domain->nport_list); + + kref_get(&domain->ref); + + efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name); + + return nport; +} + +void +efc_nport_free(struct efc_nport *nport) +{ + struct efc_domain *domain; + + if (!nport) + return; + + domain = nport->domain; + efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name); + list_del(&nport->list_entry); + /* + * if this is the physical nport, + * then clear it out of the domain + */ + if (nport == domain->nport) + domain->nport = NULL; + + xa_destroy(&nport->lookup); + xa_erase(&domain->lookup, nport->fc_id); + + if (list_empty(&domain->nport_list)) + efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE, + NULL); + + kref_put(&domain->ref, domain->release); + kref_put(&nport->ref, nport->release); +} + +struct efc_nport * +efc_nport_find(struct efc_domain *domain, u32 d_id) +{ + struct efc_nport *nport; + + /* Find a nport object, given an FC_ID */ + nport = xa_load(&domain->lookup, d_id); + if (!nport || !kref_get_unless_zero(&nport->ref)) + return NULL; + + return nport; +} + +int +efc_nport_attach(struct efc_nport *nport, u32 fc_id) +{ + int rc; + struct efc_node *node; + struct efc *efc = nport->efc; + unsigned long index; + + /* Set our lookup */ + rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC)); + if (rc) { + efc_log_err(efc, "Sport lookup store failed: %d\n", rc); + return rc; + } + + /* Update our display_name */ + efc_node_fcid_display(fc_id, nport->display_name, + sizeof(nport->display_name)); + + xa_for_each(&nport->lookup, index, node) { + efc_node_update_display_name(node); + } + + efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n", + nport->display_name, fc_id); + + /* Register a nport, given an FC_ID */ + rc = efc_cmd_nport_attach(efc, nport, fc_id); + if (rc < 0) { + efc_log_err(nport->efc, + "efc_hw_port_attach failed: %d\n", rc); + return -EIO; + } + return 0; +} + +static void +efc_nport_shutdown(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_node *node; + unsigned long index; + + xa_for_each(&nport->lookup, index, node) { + if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) { + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + continue; + } + + /* + * If this is a vport, logout of the fabric + * controller so that it deletes the vport + * on the switch. + */ + /* if link is down, don't send logo */ + if (efc->link_status == EFC_LINK_STATUS_DOWN) { + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + continue; + } + + efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n", + node->display_name); + + if (!efc_send_logo(node)) { + /* sent LOGO, wait for response */ + efc_node_transition(node, __efc_d_wait_logo_rsp, NULL); + continue; + } + + /* + * failed to send LOGO, + * go ahead and cleanup node anyways + */ + node_printf(node, "Failed to send LOGO\n"); + efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + } +} + +static void +efc_vport_link_down(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_vport *vport; + + /* Clear the nport reference in the vport specification */ + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if (vport->nport == nport) { + kref_put(&nport->ref, nport->release); + vport->nport = NULL; + break; + } + } +} + +static void +__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + struct efc *efc = nport->efc; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + case EFC_EVT_ALL_CHILD_NODES_FREE: + break; + case EFC_EVT_NPORT_ATTACH_OK: + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + case EFC_EVT_SHUTDOWN: + /* Flag this nport as shutting down */ + nport->shutting_down = true; + + if (nport->is_vport) + efc_vport_link_down(nport); + + if (xa_empty(&nport->lookup)) { + /* Remove the nport from the domain's lookup table */ + xa_erase(&domain->lookup, nport->fc_id); + efc_sm_transition(ctx, __efc_nport_wait_port_free, + NULL); + if (efc_cmd_nport_free(efc, nport)) { + efc_log_debug(nport->efc, + "efc_hw_port_free failed\n"); + /* Not much we can do, free the nport anyways */ + efc_nport_free(nport); + } + } else { + /* sm: node list is not empty / shutdown nodes */ + efc_sm_transition(ctx, + __efc_nport_wait_shutdown, NULL); + efc_nport_shutdown(nport); + } + break; + default: + efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n", + nport->display_name, funcname, + efc_sm_event_name(evt)); + } +} + +void +__efc_nport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + + nport_sm_trace(nport); + + switch (evt) { + /* the physical nport is attached */ + case EFC_EVT_NPORT_ATTACH_OK: + WARN_ON(nport != domain->nport); + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + + case EFC_EVT_NPORT_ALLOC_OK: + /* ignore */ + break; + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_ENTER: { + __be64 be_wwpn = cpu_to_be64(nport->wwpn); + + if (nport->wwpn == 0) + efc_log_debug(efc, "vport: letting f/w select WWN\n"); + + if (nport->fc_id != U32_MAX) { + efc_log_debug(efc, "vport: hard coding port id: %x\n", + nport->fc_id); + } + + efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL); + /* If wwpn is zero, then we'll let the f/w assign wwpn*/ + if (efc_cmd_nport_alloc(efc, nport, nport->domain, + nport->wwpn == 0 ? NULL : + (uint8_t *)&be_wwpn)) { + efc_log_err(efc, "Can't allocate port\n"); + break; + } + + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_NPORT_ALLOC_OK: { + struct fc_els_flogi *sp; + + sp = (struct fc_els_flogi *)nport->service_params; + + if (nport->wwnn == 0) { + nport->wwnn = be64_to_cpu(nport->sli_wwnn); + nport->wwpn = be64_to_cpu(nport->sli_wwpn); + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), + "%016llX", nport->wwpn); + } + + /* Update the nport's service parameters */ + sp->fl_wwpn = cpu_to_be64(nport->wwpn); + sp->fl_wwnn = cpu_to_be64(nport->wwnn); + + /* + * if nport->fc_id is uninitialized, + * then request that the fabric node use FDISC + * to find an fc_id. + * Otherwise we're restoring vports, or we're in + * fabric emulation mode, so attach the fc_id + */ + if (nport->fc_id == U32_MAX) { + struct efc_node *fabric; + + fabric = efc_node_alloc(nport, FC_FID_FLOGI, false, + false); + if (!fabric) { + efc_log_err(efc, "efc_node_alloc() failed\n"); + return; + } + efc_node_transition(fabric, __efc_vport_fabric_init, + NULL); + } else { + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), + "%016llX", nport->wwpn); + efc_nport_attach(nport, nport->fc_id); + } + efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + /* + * This state is entered after the nport is allocated; + * it then waits for a fabric node + * FDISC to complete, which requests a nport attach. + * The nport attach complete is handled in this state. + */ + switch (evt) { + case EFC_EVT_NPORT_ATTACH_OK: { + struct efc_node *node; + + /* Find our fabric node, and forward this event */ + node = efc_node_find(nport, FC_FID_FLOGI); + if (!node) { + efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI); + break; + } + /* sm: / forward nport attach to fabric node */ + efc_node_post_event(node, evt, NULL); + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +static void +efc_vport_update_spec(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_vport *vport; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if (vport->nport == nport) { + vport->wwnn = nport->wwnn; + vport->wwpn = nport->wwpn; + vport->tgt_data = nport->tgt_data; + vport->ini_data = nport->ini_data; + break; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); +} + +void +__efc_nport_attached(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_ENTER: { + struct efc_node *node; + unsigned long index; + + efc_log_debug(efc, + "[%s] NPORT attached WWPN %016llX WWNN %016llX\n", + nport->display_name, + nport->wwpn, nport->wwnn); + + xa_for_each(&nport->lookup, index, node) + efc_node_update_display_name(node); + + efc->tt.new_nport(efc, nport); + + /* + * Update the vport (if its not the physical nport) + * parameters + */ + if (nport->is_vport) + efc_vport_update_spec(nport); + break; + } + + case EFC_EVT_EXIT: + efc_log_debug(efc, + "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n", + nport->display_name, + nport->wwpn, nport->wwnn); + + efc->tt.del_nport(efc, nport); + break; + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_NPORT_ALLOC_OK: + case EFC_EVT_NPORT_ALLOC_FAIL: + case EFC_EVT_NPORT_ATTACH_OK: + case EFC_EVT_NPORT_ATTACH_FAIL: + /* ignore these events - just wait for the all free event */ + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: { + /* + * Remove the nport from the domain's + * sparse vector lookup table + */ + xa_erase(&domain->lookup, nport->fc_id); + efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL); + if (efc_cmd_nport_free(efc, nport)) { + efc_log_err(nport->efc, "efc_hw_port_free failed\n"); + /* Not much we can do, free the nport anyways */ + efc_nport_free(nport); + } + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_wait_port_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_NPORT_ATTACH_OK: + /* Ignore as we are waiting for the free CB */ + break; + case EFC_EVT_NPORT_FREE_OK: { + /* All done, free myself */ + efc_nport_free(nport); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +static int +efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport) +{ + struct efc_nport *nport; + + lockdep_assert_held(&domain->efc->lock); + + nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id, + vport->enable_ini, vport->enable_tgt); + vport->nport = nport; + if (!nport) + return -EIO; + + kref_get(&nport->ref); + nport->is_vport = true; + nport->tgt_data = vport->tgt_data; + nport->ini_data = vport->ini_data; + + efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL); + + return 0; +} + +int +efc_vport_start(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + struct efc_vport *vport; + struct efc_vport *next; + int rc = 0; + unsigned long flags = 0; + + /* Use the vport spec to find the associated vports and start them */ + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + if (!vport->nport) { + if (efc_vport_nport_alloc(domain, vport)) + rc = -EIO; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); + + return rc; +} + +int +efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool ini, bool tgt, void *tgt_data, + void *ini_data) +{ + struct efc *efc = domain->efc; + struct efc_vport *vport; + int rc = 0; + unsigned long flags = 0; + + if (ini && domain->efc->enable_ini == 0) { + efc_log_debug(efc, "driver initiator mode not enabled\n"); + return -EIO; + } + + if (tgt && domain->efc->enable_tgt == 0) { + efc_log_debug(efc, "driver target mode not enabled\n"); + return -EIO; + } + + /* + * Create a vport spec if we need to recreate + * this vport after a link up event + */ + vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt, + tgt_data, ini_data); + if (!vport) { + efc_log_err(efc, "failed to create vport object entry\n"); + return -EIO; + } + + spin_lock_irqsave(&efc->lock, flags); + rc = efc_vport_nport_alloc(domain, vport); + spin_unlock_irqrestore(&efc->lock, flags); + + return rc; +} + +int +efc_nport_vport_del(struct efc *efc, struct efc_domain *domain, + u64 wwpn, uint64_t wwnn) +{ + struct efc_nport *nport; + struct efc_vport *vport; + struct efc_vport *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + /* walk the efc_vport_list and remove from there */ + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + if (vport->wwpn == wwpn && vport->wwnn == wwnn) { + list_del(&vport->list_entry); + kfree(vport); + break; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); + + if (!domain) { + /* No domain means no nport to look for */ + return 0; + } + + spin_lock_irqsave(&efc->lock, flags); + list_for_each_entry(nport, &domain->nport_list, list_entry) { + if (nport->wwpn == wwpn && nport->wwnn == wwnn) { + kref_put(&nport->ref, nport->release); + /* Shutdown this NPORT */ + efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL); + break; + } + } + + spin_unlock_irqrestore(&efc->lock, flags); + return 0; +} + +void +efc_vport_del_all(struct efc *efc) +{ + struct efc_vport *vport; + struct efc_vport *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + list_del(&vport->list_entry); + kfree(vport); + } + spin_unlock_irqrestore(&efc->vport_lock, flags); +} + +struct efc_vport * +efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn, + u32 fc_id, bool enable_ini, + bool enable_tgt, void *tgt_data, void *ini_data) +{ + struct efc_vport *vport; + unsigned long flags = 0; + + /* + * walk the efc_vport_list and return failure + * if a valid(vport with non zero WWPN and WWNN) vport entry + * is already created + */ + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if ((wwpn && vport->wwpn == wwpn) && + (wwnn && vport->wwnn == wwnn)) { + efc_log_err(efc, + "VPORT %016llX %016llX already allocated\n", + wwnn, wwpn); + spin_unlock_irqrestore(&efc->vport_lock, flags); + return NULL; + } + } + + vport = kzalloc(sizeof(*vport), GFP_ATOMIC); + if (!vport) { + spin_unlock_irqrestore(&efc->vport_lock, flags); + return NULL; + } + + vport->wwnn = wwnn; + vport->wwpn = wwpn; + vport->fc_id = fc_id; + vport->enable_tgt = enable_tgt; + vport->enable_ini = enable_ini; + vport->tgt_data = tgt_data; + vport->ini_data = ini_data; + + INIT_LIST_HEAD(&vport->list_entry); + list_add_tail(&vport->list_entry, &efc->vport_list); + spin_unlock_irqrestore(&efc->vport_lock, flags); + return vport; +} diff --git a/drivers/scsi/elx/libefc/efc_nport.h b/drivers/scsi/elx/libefc/efc_nport.h new file mode 100644 index 000000000..b575ea205 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_nport.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/** + * EFC FC port (NPORT) exported declarations + * + */ + +#ifndef __EFC_NPORT_H__ +#define __EFC_NPORT_H__ + +struct efc_nport * +efc_nport_find(struct efc_domain *domain, u32 d_id); +struct efc_nport * +efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool enable_ini, bool enable_tgt); +void +efc_nport_free(struct efc_nport *nport); +int +efc_nport_attach(struct efc_nport *nport, u32 fc_id); + +void +__efc_nport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_wait_port_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_attached(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +int +efc_vport_start(struct efc_domain *domain); + +#endif /* __EFC_NPORT_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_sm.c b/drivers/scsi/elx/libefc/efc_sm.c new file mode 100644 index 000000000..afd963782 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_sm.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Generic state machine framework. + */ +#include "efc.h" +#include "efc_sm.h" + +/** + * efc_sm_post_event() - Post an event to a context. + * + * @ctx: State machine context + * @evt: Event to post + * @data: Event-specific data (if any) + */ +int +efc_sm_post_event(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *data) +{ + if (!ctx->current_state) + return -EIO; + + ctx->current_state(ctx, evt, data); + return 0; +} + +void +efc_sm_transition(struct efc_sm_ctx *ctx, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *), void *data) + +{ + if (ctx->current_state == state) { + efc_sm_post_event(ctx, EFC_EVT_REENTER, data); + } else { + efc_sm_post_event(ctx, EFC_EVT_EXIT, data); + ctx->current_state = state; + efc_sm_post_event(ctx, EFC_EVT_ENTER, data); + } +} + +static char *event_name[] = EFC_SM_EVENT_NAME; + +const char *efc_sm_event_name(enum efc_sm_event evt) +{ + if (evt > EFC_EVT_LAST) + return "unknown"; + + return event_name[evt]; +} diff --git a/drivers/scsi/elx/libefc/efc_sm.h b/drivers/scsi/elx/libefc/efc_sm.h new file mode 100644 index 000000000..e26867b4d --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_sm.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + */ + +/** + * Generic state machine framework declarations. + */ + +#ifndef _EFC_SM_H +#define _EFC_SM_H + +struct efc_sm_ctx; + +/* State Machine events */ +enum efc_sm_event { + /* Common Events */ + EFC_EVT_ENTER, + EFC_EVT_REENTER, + EFC_EVT_EXIT, + EFC_EVT_SHUTDOWN, + EFC_EVT_ALL_CHILD_NODES_FREE, + EFC_EVT_RESUME, + EFC_EVT_TIMER_EXPIRED, + + /* Domain Events */ + EFC_EVT_RESPONSE, + EFC_EVT_ERROR, + + EFC_EVT_DOMAIN_FOUND, + EFC_EVT_DOMAIN_ALLOC_OK, + EFC_EVT_DOMAIN_ALLOC_FAIL, + EFC_EVT_DOMAIN_REQ_ATTACH, + EFC_EVT_DOMAIN_ATTACH_OK, + EFC_EVT_DOMAIN_ATTACH_FAIL, + EFC_EVT_DOMAIN_LOST, + EFC_EVT_DOMAIN_FREE_OK, + EFC_EVT_DOMAIN_FREE_FAIL, + EFC_EVT_HW_DOMAIN_REQ_ATTACH, + EFC_EVT_HW_DOMAIN_REQ_FREE, + + /* Sport Events */ + EFC_EVT_NPORT_ALLOC_OK, + EFC_EVT_NPORT_ALLOC_FAIL, + EFC_EVT_NPORT_ATTACH_OK, + EFC_EVT_NPORT_ATTACH_FAIL, + EFC_EVT_NPORT_FREE_OK, + EFC_EVT_NPORT_FREE_FAIL, + EFC_EVT_NPORT_TOPOLOGY_NOTIFY, + EFC_EVT_HW_PORT_ALLOC_OK, + EFC_EVT_HW_PORT_ALLOC_FAIL, + EFC_EVT_HW_PORT_ATTACH_OK, + EFC_EVT_HW_PORT_REQ_ATTACH, + EFC_EVT_HW_PORT_REQ_FREE, + EFC_EVT_HW_PORT_FREE_OK, + + /* Login Events */ + EFC_EVT_SRRS_ELS_REQ_OK, + EFC_EVT_SRRS_ELS_CMPL_OK, + EFC_EVT_SRRS_ELS_REQ_FAIL, + EFC_EVT_SRRS_ELS_CMPL_FAIL, + EFC_EVT_SRRS_ELS_REQ_RJT, + EFC_EVT_NODE_ATTACH_OK, + EFC_EVT_NODE_ATTACH_FAIL, + EFC_EVT_NODE_FREE_OK, + EFC_EVT_NODE_FREE_FAIL, + EFC_EVT_ELS_FRAME, + EFC_EVT_ELS_REQ_TIMEOUT, + EFC_EVT_ELS_REQ_ABORTED, + /* request an ELS IO be aborted */ + EFC_EVT_ABORT_ELS, + /* ELS abort process complete */ + EFC_EVT_ELS_ABORT_CMPL, + + EFC_EVT_ABTS_RCVD, + + /* node is not in the GID_PT payload */ + EFC_EVT_NODE_MISSING, + /* node is allocated and in the GID_PT payload */ + EFC_EVT_NODE_REFOUND, + /* node shutting down due to PLOGI recvd (implicit logo) */ + EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, + /* node shutting down due to LOGO recvd/sent (explicit logo) */ + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + + EFC_EVT_PLOGI_RCVD, + EFC_EVT_FLOGI_RCVD, + EFC_EVT_LOGO_RCVD, + EFC_EVT_PRLI_RCVD, + EFC_EVT_PRLO_RCVD, + EFC_EVT_PDISC_RCVD, + EFC_EVT_FDISC_RCVD, + EFC_EVT_ADISC_RCVD, + EFC_EVT_RSCN_RCVD, + EFC_EVT_SCR_RCVD, + EFC_EVT_ELS_RCVD, + + EFC_EVT_FCP_CMD_RCVD, + + EFC_EVT_GIDPT_DELAY_EXPIRED, + + /* SCSI Target Server events */ + EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, + EFC_EVT_NODE_DEL_INI_COMPLETE, + EFC_EVT_NODE_DEL_TGT_COMPLETE, + EFC_EVT_NODE_SESS_REG_OK, + EFC_EVT_NODE_SESS_REG_FAIL, + + /* Must be last */ + EFC_EVT_LAST +}; + +/* State Machine event name lookup array */ +#define EFC_SM_EVENT_NAME { \ + [EFC_EVT_ENTER] = "EFC_EVT_ENTER", \ + [EFC_EVT_REENTER] = "EFC_EVT_REENTER", \ + [EFC_EVT_EXIT] = "EFC_EVT_EXIT", \ + [EFC_EVT_SHUTDOWN] = "EFC_EVT_SHUTDOWN", \ + [EFC_EVT_ALL_CHILD_NODES_FREE] = "EFC_EVT_ALL_CHILD_NODES_FREE",\ + [EFC_EVT_RESUME] = "EFC_EVT_RESUME", \ + [EFC_EVT_TIMER_EXPIRED] = "EFC_EVT_TIMER_EXPIRED", \ + [EFC_EVT_RESPONSE] = "EFC_EVT_RESPONSE", \ + [EFC_EVT_ERROR] = "EFC_EVT_ERROR", \ + [EFC_EVT_DOMAIN_FOUND] = "EFC_EVT_DOMAIN_FOUND", \ + [EFC_EVT_DOMAIN_ALLOC_OK] = "EFC_EVT_DOMAIN_ALLOC_OK", \ + [EFC_EVT_DOMAIN_ALLOC_FAIL] = "EFC_EVT_DOMAIN_ALLOC_FAIL", \ + [EFC_EVT_DOMAIN_REQ_ATTACH] = "EFC_EVT_DOMAIN_REQ_ATTACH", \ + [EFC_EVT_DOMAIN_ATTACH_OK] = "EFC_EVT_DOMAIN_ATTACH_OK", \ + [EFC_EVT_DOMAIN_ATTACH_FAIL] = "EFC_EVT_DOMAIN_ATTACH_FAIL", \ + [EFC_EVT_DOMAIN_LOST] = "EFC_EVT_DOMAIN_LOST", \ + [EFC_EVT_DOMAIN_FREE_OK] = "EFC_EVT_DOMAIN_FREE_OK", \ + [EFC_EVT_DOMAIN_FREE_FAIL] = "EFC_EVT_DOMAIN_FREE_FAIL", \ + [EFC_EVT_HW_DOMAIN_REQ_ATTACH] = "EFC_EVT_HW_DOMAIN_REQ_ATTACH",\ + [EFC_EVT_HW_DOMAIN_REQ_FREE] = "EFC_EVT_HW_DOMAIN_REQ_FREE", \ + [EFC_EVT_NPORT_ALLOC_OK] = "EFC_EVT_NPORT_ALLOC_OK", \ + [EFC_EVT_NPORT_ALLOC_FAIL] = "EFC_EVT_NPORT_ALLOC_FAIL", \ + [EFC_EVT_NPORT_ATTACH_OK] = "EFC_EVT_NPORT_ATTACH_OK", \ + [EFC_EVT_NPORT_ATTACH_FAIL] = "EFC_EVT_NPORT_ATTACH_FAIL", \ + [EFC_EVT_NPORT_FREE_OK] = "EFC_EVT_NPORT_FREE_OK", \ + [EFC_EVT_NPORT_FREE_FAIL] = "EFC_EVT_NPORT_FREE_FAIL", \ + [EFC_EVT_NPORT_TOPOLOGY_NOTIFY] = "EFC_EVT_NPORT_TOPOLOGY_NOTIFY",\ + [EFC_EVT_HW_PORT_ALLOC_OK] = "EFC_EVT_HW_PORT_ALLOC_OK", \ + [EFC_EVT_HW_PORT_ALLOC_FAIL] = "EFC_EVT_HW_PORT_ALLOC_FAIL", \ + [EFC_EVT_HW_PORT_ATTACH_OK] = "EFC_EVT_HW_PORT_ATTACH_OK", \ + [EFC_EVT_HW_PORT_REQ_ATTACH] = "EFC_EVT_HW_PORT_REQ_ATTACH", \ + [EFC_EVT_HW_PORT_REQ_FREE] = "EFC_EVT_HW_PORT_REQ_FREE", \ + [EFC_EVT_HW_PORT_FREE_OK] = "EFC_EVT_HW_PORT_FREE_OK", \ + [EFC_EVT_SRRS_ELS_REQ_OK] = "EFC_EVT_SRRS_ELS_REQ_OK", \ + [EFC_EVT_SRRS_ELS_CMPL_OK] = "EFC_EVT_SRRS_ELS_CMPL_OK", \ + [EFC_EVT_SRRS_ELS_REQ_FAIL] = "EFC_EVT_SRRS_ELS_REQ_FAIL", \ + [EFC_EVT_SRRS_ELS_CMPL_FAIL] = "EFC_EVT_SRRS_ELS_CMPL_FAIL", \ + [EFC_EVT_SRRS_ELS_REQ_RJT] = "EFC_EVT_SRRS_ELS_REQ_RJT", \ + [EFC_EVT_NODE_ATTACH_OK] = "EFC_EVT_NODE_ATTACH_OK", \ + [EFC_EVT_NODE_ATTACH_FAIL] = "EFC_EVT_NODE_ATTACH_FAIL", \ + [EFC_EVT_NODE_FREE_OK] = "EFC_EVT_NODE_FREE_OK", \ + [EFC_EVT_NODE_FREE_FAIL] = "EFC_EVT_NODE_FREE_FAIL", \ + [EFC_EVT_ELS_FRAME] = "EFC_EVT_ELS_FRAME", \ + [EFC_EVT_ELS_REQ_TIMEOUT] = "EFC_EVT_ELS_REQ_TIMEOUT", \ + [EFC_EVT_ELS_REQ_ABORTED] = "EFC_EVT_ELS_REQ_ABORTED", \ + [EFC_EVT_ABORT_ELS] = "EFC_EVT_ABORT_ELS", \ + [EFC_EVT_ELS_ABORT_CMPL] = "EFC_EVT_ELS_ABORT_CMPL", \ + [EFC_EVT_ABTS_RCVD] = "EFC_EVT_ABTS_RCVD", \ + [EFC_EVT_NODE_MISSING] = "EFC_EVT_NODE_MISSING", \ + [EFC_EVT_NODE_REFOUND] = "EFC_EVT_NODE_REFOUND", \ + [EFC_EVT_SHUTDOWN_IMPLICIT_LOGO] = "EFC_EVT_SHUTDOWN_IMPLICIT_LOGO",\ + [EFC_EVT_SHUTDOWN_EXPLICIT_LOGO] = "EFC_EVT_SHUTDOWN_EXPLICIT_LOGO",\ + [EFC_EVT_PLOGI_RCVD] = "EFC_EVT_PLOGI_RCVD", \ + [EFC_EVT_FLOGI_RCVD] = "EFC_EVT_FLOGI_RCVD", \ + [EFC_EVT_LOGO_RCVD] = "EFC_EVT_LOGO_RCVD", \ + [EFC_EVT_PRLI_RCVD] = "EFC_EVT_PRLI_RCVD", \ + [EFC_EVT_PRLO_RCVD] = "EFC_EVT_PRLO_RCVD", \ + [EFC_EVT_PDISC_RCVD] = "EFC_EVT_PDISC_RCVD", \ + [EFC_EVT_FDISC_RCVD] = "EFC_EVT_FDISC_RCVD", \ + [EFC_EVT_ADISC_RCVD] = "EFC_EVT_ADISC_RCVD", \ + [EFC_EVT_RSCN_RCVD] = "EFC_EVT_RSCN_RCVD", \ + [EFC_EVT_SCR_RCVD] = "EFC_EVT_SCR_RCVD", \ + [EFC_EVT_ELS_RCVD] = "EFC_EVT_ELS_RCVD", \ + [EFC_EVT_FCP_CMD_RCVD] = "EFC_EVT_FCP_CMD_RCVD", \ + [EFC_EVT_NODE_DEL_INI_COMPLETE] = "EFC_EVT_NODE_DEL_INI_COMPLETE",\ + [EFC_EVT_NODE_DEL_TGT_COMPLETE] = "EFC_EVT_NODE_DEL_TGT_COMPLETE",\ + [EFC_EVT_LAST] = "EFC_EVT_LAST", \ +} + +int +efc_sm_post_event(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *data); +void +efc_sm_transition(struct efc_sm_ctx *ctx, + void (*state)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg), + void *data); +void efc_sm_disable(struct efc_sm_ctx *ctx); +const char *efc_sm_event_name(enum efc_sm_event evt); + +#endif /* ! _EFC_SM_H */ diff --git a/drivers/scsi/elx/libefc/efclib.c b/drivers/scsi/elx/libefc/efclib.c new file mode 100644 index 000000000..dd3e3d0a4 --- /dev/null +++ b/drivers/scsi/elx/libefc/efclib.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * LIBEFC LOCKING + * + * The critical sections protected by the efc's spinlock are quite broad and + * may be improved upon in the future. The libefc code and its locking doesn't + * influence the I/O path, so excessive locking doesn't impact I/O performance. + * + * The strategy is to lock whenever processing a request from user driver. This + * means that the entry points into the libefc library are protected by efc + * lock. So all the state machine transitions are protected. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include "efc.h" + +int efcport_init(struct efc *efc) +{ + u32 rc = 0; + + spin_lock_init(&efc->lock); + INIT_LIST_HEAD(&efc->vport_list); + efc->hold_frames = false; + spin_lock_init(&efc->pend_frames_lock); + INIT_LIST_HEAD(&efc->pend_frames); + + /* Create Node pool */ + efc->node_pool = mempool_create_kmalloc_pool(EFC_MAX_REMOTE_NODES, + sizeof(struct efc_node)); + if (!efc->node_pool) { + efc_log_err(efc, "Can't allocate node pool\n"); + return -ENOMEM; + } + + efc->node_dma_pool = dma_pool_create("node_dma_pool", &efc->pci->dev, + NODE_SPARAMS_SIZE, 0, 0); + if (!efc->node_dma_pool) { + efc_log_err(efc, "Can't allocate node dma pool\n"); + mempool_destroy(efc->node_pool); + return -ENOMEM; + } + + efc->els_io_pool = mempool_create_kmalloc_pool(EFC_ELS_IO_POOL_SZ, + sizeof(struct efc_els_io_req)); + if (!efc->els_io_pool) { + efc_log_err(efc, "Can't allocate els io pool\n"); + return -ENOMEM; + } + + return rc; +} + +static void +efc_purge_pending(struct efc *efc) +{ + struct efc_hw_sequence *frame, *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->pend_frames_lock, flags); + + list_for_each_entry_safe(frame, next, &efc->pend_frames, list_entry) { + list_del(&frame->list_entry); + efc->tt.hw_seq_free(efc, frame); + } + + spin_unlock_irqrestore(&efc->pend_frames_lock, flags); +} + +void efcport_destroy(struct efc *efc) +{ + efc_purge_pending(efc); + mempool_destroy(efc->els_io_pool); + mempool_destroy(efc->node_pool); + dma_pool_destroy(efc->node_dma_pool); +} diff --git a/drivers/scsi/elx/libefc/efclib.h b/drivers/scsi/elx/libefc/efclib.h new file mode 100644 index 000000000..57e338612 --- /dev/null +++ b/drivers/scsi/elx/libefc/efclib.h @@ -0,0 +1,623 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFCLIB_H__ +#define __EFCLIB_H__ + +#include "scsi/fc/fc_els.h" +#include "scsi/fc/fc_fs.h" +#include "scsi/fc/fc_ns.h" +#include "scsi/fc/fc_gs.h" +#include "scsi/fc_frame.h" +#include "../include/efc_common.h" +#include "../libefc_sli/sli4.h" + +#define EFC_SERVICE_PARMS_LENGTH 120 +#define EFC_NAME_LENGTH 32 +#define EFC_SM_NAME_LENGTH 64 +#define EFC_DISPLAY_BUS_INFO_LENGTH 16 + +#define EFC_WWN_LENGTH 32 + +#define EFC_FC_ELS_DEFAULT_RETRIES 3 + +/* Timeouts */ +#define EFC_FC_ELS_SEND_DEFAULT_TIMEOUT 0 +#define EFC_FC_FLOGI_TIMEOUT_SEC 5 +#define EFC_SHUTDOWN_TIMEOUT_USEC 30000000 + +/* Return values for calls from base driver to libefc */ +#define EFC_SCSI_CALL_COMPLETE 0 +#define EFC_SCSI_CALL_ASYNC 1 + +/* Local port topology */ +enum efc_nport_topology { + EFC_NPORT_TOPO_UNKNOWN = 0, + EFC_NPORT_TOPO_FABRIC, + EFC_NPORT_TOPO_P2P, + EFC_NPORT_TOPO_FC_AL, +}; + +#define enable_target_rscn(efc) 1 + +enum efc_node_shutd_rsn { + EFC_NODE_SHUTDOWN_DEFAULT = 0, + EFC_NODE_SHUTDOWN_EXPLICIT_LOGO, + EFC_NODE_SHUTDOWN_IMPLICIT_LOGO, +}; + +enum efc_node_send_ls_acc { + EFC_NODE_SEND_LS_ACC_NONE = 0, + EFC_NODE_SEND_LS_ACC_PLOGI, + EFC_NODE_SEND_LS_ACC_PRLI, +}; + +#define EFC_LINK_STATUS_UP 0 +#define EFC_LINK_STATUS_DOWN 1 + +enum efc_sm_event; + +/* State machine context header */ +struct efc_sm_ctx { + void (*current_state)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + + const char *description; + void *app; +}; + +/* Description of discovered Fabric Domain */ +struct efc_domain_record { + u32 index; + u32 priority; + u8 address[6]; + u8 wwn[8]; + union { + u8 vlan[512]; + u8 loop[128]; + } map; + u32 speed; + u32 fc_id; + bool is_loop; + bool is_nport; +}; + +/* Domain events */ +enum efc_hw_domain_event { + EFC_HW_DOMAIN_ALLOC_OK, + EFC_HW_DOMAIN_ALLOC_FAIL, + EFC_HW_DOMAIN_ATTACH_OK, + EFC_HW_DOMAIN_ATTACH_FAIL, + EFC_HW_DOMAIN_FREE_OK, + EFC_HW_DOMAIN_FREE_FAIL, + EFC_HW_DOMAIN_LOST, + EFC_HW_DOMAIN_FOUND, + EFC_HW_DOMAIN_CHANGED, +}; + +/** + * Fibre Channel port object + * + * @list_entry: nport list entry + * @ref: reference count, each node takes a reference + * @release: function to free nport object + * @efc: pointer back to efc + * @instance_index: unique instance index value + * @display_name: port display name + * @is_vport: Is NPIV port + * @free_req_pending: pending request to free resources + * @attached: mark attached if reg VPI succeeds + * @p2p_winner: TRUE if we're the point-to-point winner + * @domain: pointer back to domain + * @wwpn: port wwpn + * @wwnn: port wwnn + * @tgt_data: target backend private port data + * @ini_data: initiator backend private port data + * @indicator: VPI + * @fc_id: port FC address + * @dma: memory for Service Parameters + * @wwnn_str: wwpn string + * @sli_wwpn: SLI provided wwpn + * @sli_wwnn: SLI provided wwnn + * @sm: nport state machine context + * @lookup: fc_id to node lookup object + * @enable_ini: SCSI initiator enabled for this port + * @enable_tgt: SCSI target enabled for this port + * @enable_rscn: port will be expecting RSCN + * @shutting_down: nport in process of shutting down + * @p2p_port_id: our port id for point-to-point + * @topology: topology: fabric/p2p/unknown + * @service_params: login parameters + * @p2p_remote_port_id: remote node's port id for point-to-point + */ + +struct efc_nport { + struct list_head list_entry; + struct kref ref; + void (*release)(struct kref *arg); + struct efc *efc; + u32 instance_index; + char display_name[EFC_NAME_LENGTH]; + bool is_vport; + bool free_req_pending; + bool attached; + bool attaching; + bool p2p_winner; + struct efc_domain *domain; + u64 wwpn; + u64 wwnn; + void *tgt_data; + void *ini_data; + + u32 indicator; + u32 fc_id; + struct efc_dma dma; + + u8 wwnn_str[EFC_WWN_LENGTH]; + __be64 sli_wwpn; + __be64 sli_wwnn; + + struct efc_sm_ctx sm; + struct xarray lookup; + bool enable_ini; + bool enable_tgt; + bool enable_rscn; + bool shutting_down; + u32 p2p_port_id; + enum efc_nport_topology topology; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + u32 p2p_remote_port_id; +}; + +/** + * Fibre Channel domain object + * + * This object is a container for the various SLI components needed + * to connect to the domain of a FC or FCoE switch + * @efc: pointer back to efc + * @instance_index: unique instance index value + * @display_name: Node display name + * @nport_list: linked list of nports associated with this domain + * @ref: Reference count, each nport takes a reference + * @release: Function to free domain object + * @ini_domain: initiator backend private domain data + * @tgt_domain: target backend private domain data + * @sm: state machine context + * @fcf: FC Forwarder table index + * @fcf_indicator: FCFI + * @indicator: VFI + * @nport_count: Number of nports allocated + * @dma: memory for Service Parameters + * @fcf_wwn: WWN for FCF/switch + * @drvsm: driver domain sm context + * @attached: set true after attach completes + * @is_fc: is FC + * @is_loop: is loop topology + * @is_nlport: is public loop + * @domain_found_pending:A domain found is pending, drec is updated + * @req_domain_free: True if domain object should be free'd + * @req_accept_frames: set in domain state machine to enable frames + * @domain_notify_pend: Set in domain SM to avoid duplicate node event post + * @pending_drec: Pending drec if a domain found is pending + * @service_params: any nports service parameters + * @flogi_service_params:Fabric/P2p service parameters from FLOGI + * @lookup: d_id to node lookup object + * @nport: Pointer to first (physical) SLI port + */ +struct efc_domain { + struct efc *efc; + char display_name[EFC_NAME_LENGTH]; + struct list_head nport_list; + struct kref ref; + void (*release)(struct kref *arg); + void *ini_domain; + void *tgt_domain; + + /* Declarations private to HW/SLI */ + u32 fcf; + u32 fcf_indicator; + u32 indicator; + u32 nport_count; + struct efc_dma dma; + + /* Declarations private to FC trannport */ + u64 fcf_wwn; + struct efc_sm_ctx drvsm; + bool attached; + bool is_fc; + bool is_loop; + bool is_nlport; + bool domain_found_pending; + bool req_domain_free; + bool req_accept_frames; + bool domain_notify_pend; + + struct efc_domain_record pending_drec; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + u8 flogi_service_params[EFC_SERVICE_PARMS_LENGTH]; + + struct xarray lookup; + + struct efc_nport *nport; +}; + +/** + * Remote Node object + * + * This object represents a connection between the SLI port and another + * Nx_Port on the fabric. Note this can be either a well known port such + * as a F_Port (i.e. ff:ff:fe) or another N_Port. + * @indicator: RPI + * @fc_id: FC address + * @attached: true if attached + * @nport: associated SLI port + * @node: associated node + */ +struct efc_remote_node { + u32 indicator; + u32 index; + u32 fc_id; + + bool attached; + + struct efc_nport *nport; + void *node; +}; + +/** + * FC Node object + * @efc: pointer back to efc structure + * @display_name: Node display name + * @nort: Assosiated nport pointer. + * @hold_frames: hold incoming frames if true + * @els_io_enabled: Enable allocating els ios for this node + * @els_ios_lock: lock to protect the els ios list + * @els_ios_list: ELS I/O's for this node + * @ini_node: backend initiator private node data + * @tgt_node: backend target private node data + * @rnode: Remote node + * @sm: state machine context + * @evtdepth: current event posting nesting depth + * @req_free: this node is to be free'd + * @attached: node is attached (REGLOGIN complete) + * @fcp_enabled: node is enabled to handle FCP + * @rscn_pending: for name server node RSCN is pending + * @send_plogi: send PLOGI accept, upon completion of node attach + * @send_plogi_acc: TRUE if io_alloc() is enabled. + * @send_ls_acc: type of LS acc to send + * @ls_acc_io: SCSI IO for LS acc + * @ls_acc_oxid: OX_ID for pending accept + * @ls_acc_did: D_ID for pending accept + * @shutdown_reason: reason for node shutdown + * @sparm_dma_buf: service parameters buffer + * @service_params: plogi/acc frame from remote device + * @pend_frames_lock: lock for inbound pending frames list + * @pend_frames: inbound pending frames list + * @pend_frames_processed:count of frames processed in hold frames interval + * @ox_id_in_use: used to verify one at a time us of ox_id + * @els_retries_remaining:for ELS, number of retries remaining + * @els_req_cnt: number of outstanding ELS requests + * @els_cmpl_cnt: number of outstanding ELS completions + * @abort_cnt: Abort counter for debugging purpos + * @current_state_name: current node state + * @prev_state_name: previous node state + * @current_evt: current event + * @prev_evt: previous event + * @targ: node is target capable + * @init: node is init capable + * @refound: Handle node refound case when node is being deleted + * @els_io_pend_list: list of pending (not yet processed) ELS IOs + * @els_io_active_list: list of active (processed) ELS IOs + * @nodedb_state: Node debugging, saved state + * @gidpt_delay_timer: GIDPT delay timer + * @time_last_gidpt_msec:Start time of last target RSCN GIDPT + * @wwnn: remote port WWNN + * @wwpn: remote port WWPN + */ +struct efc_node { + struct efc *efc; + char display_name[EFC_NAME_LENGTH]; + struct efc_nport *nport; + struct kref ref; + void (*release)(struct kref *arg); + bool hold_frames; + bool els_io_enabled; + bool send_plogi_acc; + bool send_plogi; + bool rscn_pending; + bool fcp_enabled; + bool attached; + bool req_free; + + spinlock_t els_ios_lock; + struct list_head els_ios_list; + void *ini_node; + void *tgt_node; + + struct efc_remote_node rnode; + /* Declarations private to FC trannport */ + struct efc_sm_ctx sm; + u32 evtdepth; + + enum efc_node_send_ls_acc send_ls_acc; + void *ls_acc_io; + u32 ls_acc_oxid; + u32 ls_acc_did; + enum efc_node_shutd_rsn shutdown_reason; + bool targ; + bool init; + bool refound; + struct efc_dma sparm_dma_buf; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + spinlock_t pend_frames_lock; + struct list_head pend_frames; + u32 pend_frames_processed; + u32 ox_id_in_use; + u32 els_retries_remaining; + u32 els_req_cnt; + u32 els_cmpl_cnt; + u32 abort_cnt; + + char current_state_name[EFC_SM_NAME_LENGTH]; + char prev_state_name[EFC_SM_NAME_LENGTH]; + int current_evt; + int prev_evt; + + void (*nodedb_state)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + struct timer_list gidpt_delay_timer; + u64 time_last_gidpt_msec; + + char wwnn[EFC_WWN_LENGTH]; + char wwpn[EFC_WWN_LENGTH]; +}; + +/** + * NPIV port + * + * Collection of the information required to restore a virtual port across + * link events + * @wwnn: node name + * @wwpn: port name + * @fc_id: port id + * @tgt_data: target backend pointer + * @ini_data: initiator backend pointe + * @nport: Used to match record after attaching for update + * + */ + +struct efc_vport { + struct list_head list_entry; + u64 wwnn; + u64 wwpn; + u32 fc_id; + bool enable_tgt; + bool enable_ini; + void *tgt_data; + void *ini_data; + struct efc_nport *nport; +}; + +#define node_printf(node, fmt, args...) \ + efc_log_info(node->efc, "[%s] " fmt, node->display_name, ##args) + +/* Node SM IO Context Callback structure */ +struct efc_node_cb { + int status; + int ext_status; + struct efc_hw_rq_buffer *header; + struct efc_hw_rq_buffer *payload; + struct efc_dma els_rsp; + + /* Actual length of data received */ + int rsp_len; +}; + +struct efc_hw_rq_buffer { + u16 rqindex; + struct efc_dma dma; +}; + +/** + * FC sequence object + * + * Defines a general FC sequence object + * @hw: HW that owns this sequence + * @fcfi: FCFI associated with sequence + * @header: Received frame header + * @payload: Received frame header + * @hw_priv: HW private context + */ +struct efc_hw_sequence { + struct list_head list_entry; + void *hw; + u8 fcfi; + struct efc_hw_rq_buffer *header; + struct efc_hw_rq_buffer *payload; + void *hw_priv; +}; + +enum efc_disc_io_type { + EFC_DISC_IO_ELS_REQ, + EFC_DISC_IO_ELS_RESP, + EFC_DISC_IO_CT_REQ, + EFC_DISC_IO_CT_RESP +}; + +struct efc_io_els_params { + u32 s_id; + u16 ox_id; + u8 timeout; +}; + +struct efc_io_ct_params { + u8 r_ctl; + u8 type; + u8 df_ctl; + u8 timeout; + u16 ox_id; +}; + +union efc_disc_io_param { + struct efc_io_els_params els; + struct efc_io_ct_params ct; +}; + +struct efc_disc_io { + struct efc_dma req; /* send buffer */ + struct efc_dma rsp; /* receive buffer */ + enum efc_disc_io_type io_type; /* EFC_DISC_IO_TYPE enum*/ + u16 xmit_len; /* Length of els request*/ + u16 rsp_len; /* Max length of rsps to be rcvd */ + u32 rpi; /* Registered RPI */ + u32 vpi; /* VPI for this nport */ + u32 s_id; + u32 d_id; + bool rpi_registered; /* if false, use tmp RPI */ + union efc_disc_io_param iparam; +}; + +/* Return value indiacating the sequence can not be freed */ +#define EFC_HW_SEQ_HOLD 0 +/* Return value indiacating the sequence can be freed */ +#define EFC_HW_SEQ_FREE 1 + +struct libefc_function_template { + /*Sport*/ + int (*new_nport)(struct efc *efc, struct efc_nport *sp); + void (*del_nport)(struct efc *efc, struct efc_nport *sp); + + /*Scsi Node*/ + int (*scsi_new_node)(struct efc *efc, struct efc_node *n); + int (*scsi_del_node)(struct efc *efc, struct efc_node *n, int reason); + + int (*issue_mbox_rqst)(void *efct, void *buf, void *cb, void *arg); + /*Send ELS IO*/ + int (*send_els)(struct efc *efc, struct efc_disc_io *io); + /*Send BLS IO*/ + int (*send_bls)(struct efc *efc, u32 type, struct sli_bls_params *bls); + /*Free HW frame*/ + int (*hw_seq_free)(struct efc *efc, struct efc_hw_sequence *seq); +}; + +#define EFC_LOG_LIB 0x01 +#define EFC_LOG_NODE 0x02 +#define EFC_LOG_PORT 0x04 +#define EFC_LOG_DOMAIN 0x08 +#define EFC_LOG_ELS 0x10 +#define EFC_LOG_DOMAIN_SM 0x20 +#define EFC_LOG_SM 0x40 + +/* efc library port structure */ +struct efc { + void *base; + struct pci_dev *pci; + struct sli4 *sli; + u32 fcfi; + u64 req_wwpn; + u64 req_wwnn; + + u64 def_wwpn; + u64 def_wwnn; + u64 max_xfer_size; + mempool_t *node_pool; + struct dma_pool *node_dma_pool; + u32 nodes_count; + + u32 link_status; + + struct list_head vport_list; + /* lock to protect the vport list */ + spinlock_t vport_lock; + + struct libefc_function_template tt; + /* lock to protect the discovery library. + * Refer to efclib.c for more details. + */ + spinlock_t lock; + + bool enable_ini; + bool enable_tgt; + + u32 log_level; + + struct efc_domain *domain; + void (*domain_free_cb)(struct efc *efc, void *arg); + void *domain_free_cb_arg; + + u64 tgt_rscn_delay_msec; + u64 tgt_rscn_period_msec; + + bool external_loopback; + u32 nodedb_mask; + u32 logmask; + mempool_t *els_io_pool; + atomic_t els_io_alloc_failed_count; + + /* hold pending frames */ + bool hold_frames; + /* lock to protect pending frames list access */ + spinlock_t pend_frames_lock; + struct list_head pend_frames; + /* count of pending frames that were processed */ + u32 pend_frames_processed; + +}; + +/* + * EFC library registration + * **********************************/ +int efcport_init(struct efc *efc); +void efcport_destroy(struct efc *efc); +/* + * EFC Domain + * **********************************/ +int efc_domain_cb(void *arg, int event, void *data); +void +efc_register_domain_free_cb(struct efc *efc, + void (*callback)(struct efc *efc, void *arg), + void *arg); + +/* + * EFC nport + * **********************************/ +void efc_nport_cb(void *arg, int event, void *data); +struct efc_vport * +efc_vport_create_spec(struct efc *efc, u64 wwnn, u64 wwpn, u32 fc_id, + bool enable_ini, bool enable_tgt, + void *tgt_data, void *ini_data); +int efc_nport_vport_new(struct efc_domain *domain, u64 wwpn, + u64 wwnn, u32 fc_id, bool ini, bool tgt, + void *tgt_data, void *ini_data); +int efc_nport_vport_del(struct efc *efc, struct efc_domain *domain, + u64 wwpn, u64 wwnn); + +void efc_vport_del_all(struct efc *efc); + +/* + * EFC Node + * **********************************/ +int efc_remote_node_cb(void *arg, int event, void *data); +void efc_node_fcid_display(u32 fc_id, char *buffer, u32 buf_len); +void efc_node_post_shutdown(struct efc_node *node, void *arg); +u64 efc_node_get_wwpn(struct efc_node *node); + +/* + * EFC FCP/ELS/CT interface + * **********************************/ +void efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq); +void efc_disc_io_complete(struct efc_disc_io *io, u32 len, u32 status, + u32 ext_status); + +/* + * EFC SCSI INTERACTION LAYER + * **********************************/ +void efc_scsi_sess_reg_complete(struct efc_node *node, u32 status); +void efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node); +void efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node); +void efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node); + +#endif /* __EFCLIB_H__ */ diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c new file mode 100644 index 000000000..b8c048cdb --- /dev/null +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -0,0 +1,5159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/** + * All common (i.e. transport-independent) SLI-4 functions are implemented + * in this file. + */ +#include "sli4.h" + +static struct sli4_asic_entry_t sli4_asic_table[] = { + { SLI4_ASIC_REV_B0, SLI4_ASIC_GEN_5}, + { SLI4_ASIC_REV_D0, SLI4_ASIC_GEN_5}, + { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A0, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_7}, + { SLI4_ASIC_REV_A0, SLI4_ASIC_GEN_7}, +}; + +/* Convert queue type enum (SLI_QTYPE_*) into a string */ +static char *SLI4_QNAME[] = { + "Event Queue", + "Completion Queue", + "Mailbox Queue", + "Work Queue", + "Receive Queue", + "Undefined" +}; + +/** + * sli_config_cmd_init() - Write a SLI_CONFIG command to the provided buffer. + * + * @sli4: SLI context pointer. + * @buf: Destination buffer for the command. + * @length: Length in bytes of attached command. + * @dma: DMA buffer for non-embedded commands. + * Return: Command payload buffer. + */ +static void * +sli_config_cmd_init(struct sli4 *sli4, void *buf, u32 length, + struct efc_dma *dma) +{ + struct sli4_cmd_sli_config *config; + u32 flags; + + if (length > sizeof(config->payload.embed) && !dma) { + efc_log_err(sli4, "Too big for an embedded cmd with len(%d)\n", + length); + return NULL; + } + + memset(buf, 0, SLI4_BMBX_SIZE); + + config = buf; + + config->hdr.command = SLI4_MBX_CMD_SLI_CONFIG; + if (!dma) { + flags = SLI4_SLICONF_EMB; + config->dw1_flags = cpu_to_le32(flags); + config->payload_len = cpu_to_le32(length); + return config->payload.embed; + } + + flags = SLI4_SLICONF_PMDCMD_VAL_1; + flags &= ~SLI4_SLICONF_EMB; + config->dw1_flags = cpu_to_le32(flags); + + config->payload.mem.addr.low = cpu_to_le32(lower_32_bits(dma->phys)); + config->payload.mem.addr.high = cpu_to_le32(upper_32_bits(dma->phys)); + config->payload.mem.length = + cpu_to_le32(dma->size & SLI4_SLICONF_PMD_LEN); + config->payload_len = cpu_to_le32(dma->size); + /* save pointer to DMA for BMBX dumping purposes */ + sli4->bmbx_non_emb_pmd = dma; + return dma->virt; +} + +/** + * sli_cmd_common_create_cq() - Write a COMMON_CREATE_CQ V2 command. + * + * @sli4: SLI context pointer. + * @buf: Destination buffer for the command. + * @qmem: DMA memory for queue. + * @eq_id: EQ id assosiated with this cq. + * Return: status -EIO/0. + */ +static int +sli_cmd_common_create_cq(struct sli4 *sli4, void *buf, struct efc_dma *qmem, + u16 eq_id) +{ + struct sli4_rqst_cmn_create_cq_v2 *cqv2 = NULL; + u32 p; + uintptr_t addr; + u32 num_pages = 0; + size_t cmd_size = 0; + u32 page_size = 0; + u32 n_cqe = 0; + u32 dw5_flags = 0; + u16 dw6w1_arm = 0; + __le32 len; + + /* First calculate number of pages and the mailbox cmd length */ + n_cqe = qmem->size / SLI4_CQE_BYTES; + switch (n_cqe) { + case 256: + case 512: + case 1024: + case 2048: + page_size = SZ_4K; + break; + case 4096: + page_size = SZ_8K; + break; + default: + return -EIO; + } + num_pages = sli_page_count(qmem->size, page_size); + + cmd_size = SLI4_RQST_CMDSZ(cmn_create_cq_v2) + + SZ_DMAADDR * num_pages; + + cqv2 = sli_config_cmd_init(sli4, buf, cmd_size, NULL); + if (!cqv2) + return -EIO; + + len = SLI4_RQST_PYLD_LEN_VAR(cmn_create_cq_v2, SZ_DMAADDR * num_pages); + sli_cmd_fill_hdr(&cqv2->hdr, SLI4_CMN_CREATE_CQ, SLI4_SUBSYSTEM_COMMON, + CMD_V2, len); + cqv2->page_size = page_size / SLI_PAGE_SIZE; + + /* valid values for number of pages: 1, 2, 4, 8 (sec 4.4.3) */ + cqv2->num_pages = cpu_to_le16(num_pages); + if (!num_pages || num_pages > SLI4_CREATE_CQV2_MAX_PAGES) + return -EIO; + + switch (num_pages) { + case 1: + dw5_flags |= SLI4_CQ_CNT_VAL(256); + break; + case 2: + dw5_flags |= SLI4_CQ_CNT_VAL(512); + break; + case 4: + dw5_flags |= SLI4_CQ_CNT_VAL(1024); + break; + case 8: + dw5_flags |= SLI4_CQ_CNT_VAL(LARGE); + cqv2->cqe_count = cpu_to_le16(n_cqe); + break; + default: + efc_log_err(sli4, "num_pages %d not valid\n", num_pages); + return -EIO; + } + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + dw5_flags |= SLI4_CREATE_CQV2_AUTOVALID; + + dw5_flags |= SLI4_CREATE_CQV2_EVT; + dw5_flags |= SLI4_CREATE_CQV2_VALID; + + cqv2->dw5_flags = cpu_to_le32(dw5_flags); + cqv2->dw6w1_arm = cpu_to_le16(dw6w1_arm); + cqv2->eq_id = cpu_to_le16(eq_id); + + for (p = 0, addr = qmem->phys; p < num_pages; p++, addr += page_size) { + cqv2->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr)); + cqv2->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr)); + } + + return 0; +} + +static int +sli_cmd_common_create_eq(struct sli4 *sli4, void *buf, struct efc_dma *qmem) +{ + struct sli4_rqst_cmn_create_eq *eq; + u32 p; + uintptr_t addr; + u16 num_pages; + u32 dw5_flags = 0; + u32 dw6_flags = 0, ver; + + eq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(cmn_create_eq), + NULL); + if (!eq) + return -EIO; + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + ver = CMD_V2; + else + ver = CMD_V0; + + sli_cmd_fill_hdr(&eq->hdr, SLI4_CMN_CREATE_EQ, SLI4_SUBSYSTEM_COMMON, + ver, SLI4_RQST_PYLD_LEN(cmn_create_eq)); + + /* valid values for number of pages: 1, 2, 4 (sec 4.4.3) */ + num_pages = qmem->size / SLI_PAGE_SIZE; + eq->num_pages = cpu_to_le16(num_pages); + + switch (num_pages) { + case 1: + dw5_flags |= SLI4_EQE_SIZE_4; + dw6_flags |= SLI4_EQ_CNT_VAL(1024); + break; + case 2: + dw5_flags |= SLI4_EQE_SIZE_4; + dw6_flags |= SLI4_EQ_CNT_VAL(2048); + break; + case 4: + dw5_flags |= SLI4_EQE_SIZE_4; + dw6_flags |= SLI4_EQ_CNT_VAL(4096); + break; + default: + efc_log_err(sli4, "num_pages %d not valid\n", num_pages); + return -EIO; + } + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + dw5_flags |= SLI4_CREATE_EQ_AUTOVALID; + + dw5_flags |= SLI4_CREATE_EQ_VALID; + dw6_flags &= (~SLI4_CREATE_EQ_ARM); + eq->dw5_flags = cpu_to_le32(dw5_flags); + eq->dw6_flags = cpu_to_le32(dw6_flags); + eq->dw7_delaymulti = cpu_to_le32(SLI4_CREATE_EQ_DELAYMULTI); + + for (p = 0, addr = qmem->phys; p < num_pages; + p++, addr += SLI_PAGE_SIZE) { + eq->page_address[p].low = cpu_to_le32(lower_32_bits(addr)); + eq->page_address[p].high = cpu_to_le32(upper_32_bits(addr)); + } + + return 0; +} + +static int +sli_cmd_common_create_mq_ext(struct sli4 *sli4, void *buf, struct efc_dma *qmem, + u16 cq_id) +{ + struct sli4_rqst_cmn_create_mq_ext *mq; + u32 p; + uintptr_t addr; + u32 num_pages; + u16 dw6w1_flags = 0; + + mq = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_create_mq_ext), NULL); + if (!mq) + return -EIO; + + sli_cmd_fill_hdr(&mq->hdr, SLI4_CMN_CREATE_MQ_EXT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_create_mq_ext)); + + /* valid values for number of pages: 1, 2, 4, 8 (sec 4.4.12) */ + num_pages = qmem->size / SLI_PAGE_SIZE; + mq->num_pages = cpu_to_le16(num_pages); + switch (num_pages) { + case 1: + dw6w1_flags |= SLI4_MQE_SIZE_16; + break; + case 2: + dw6w1_flags |= SLI4_MQE_SIZE_32; + break; + case 4: + dw6w1_flags |= SLI4_MQE_SIZE_64; + break; + case 8: + dw6w1_flags |= SLI4_MQE_SIZE_128; + break; + default: + efc_log_info(sli4, "num_pages %d not valid\n", num_pages); + return -EIO; + } + + mq->async_event_bitmap = cpu_to_le32(SLI4_ASYNC_EVT_FC_ALL); + + if (sli4->params.mq_create_version) { + mq->cq_id_v1 = cpu_to_le16(cq_id); + mq->hdr.dw3_version = cpu_to_le32(CMD_V1); + } else { + dw6w1_flags |= (cq_id << SLI4_CREATE_MQEXT_CQID_SHIFT); + } + mq->dw7_val = cpu_to_le32(SLI4_CREATE_MQEXT_VAL); + + mq->dw6w1_flags = cpu_to_le16(dw6w1_flags); + for (p = 0, addr = qmem->phys; p < num_pages; + p++, addr += SLI_PAGE_SIZE) { + mq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr)); + mq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr)); + } + + return 0; +} + +int +sli_cmd_wq_create(struct sli4 *sli4, void *buf, struct efc_dma *qmem, u16 cq_id) +{ + struct sli4_rqst_wq_create *wq; + u32 p; + uintptr_t addr; + u32 page_size = 0; + u32 n_wqe = 0; + u16 num_pages; + + wq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(wq_create), + NULL); + if (!wq) + return -EIO; + + sli_cmd_fill_hdr(&wq->hdr, SLI4_OPC_WQ_CREATE, SLI4_SUBSYSTEM_FC, + CMD_V1, SLI4_RQST_PYLD_LEN(wq_create)); + n_wqe = qmem->size / sli4->wqe_size; + + switch (qmem->size) { + case 4096: + case 8192: + case 16384: + case 32768: + page_size = SZ_4K; + break; + case 65536: + page_size = SZ_8K; + break; + case 131072: + page_size = SZ_16K; + break; + case 262144: + page_size = SZ_32K; + break; + case 524288: + page_size = SZ_64K; + break; + default: + return -EIO; + } + + /* valid values for number of pages(num_pages): 1-8 */ + num_pages = sli_page_count(qmem->size, page_size); + wq->num_pages = cpu_to_le16(num_pages); + if (!num_pages || num_pages > SLI4_WQ_CREATE_MAX_PAGES) + return -EIO; + + wq->cq_id = cpu_to_le16(cq_id); + + wq->page_size = page_size / SLI_PAGE_SIZE; + + if (sli4->wqe_size == SLI4_WQE_EXT_BYTES) + wq->wqe_size_byte |= SLI4_WQE_EXT_SIZE; + else + wq->wqe_size_byte |= SLI4_WQE_SIZE; + + wq->wqe_count = cpu_to_le16(n_wqe); + + for (p = 0, addr = qmem->phys; p < num_pages; p++, addr += page_size) { + wq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr)); + wq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr)); + } + + return 0; +} + +static int +sli_cmd_rq_create_v1(struct sli4 *sli4, void *buf, struct efc_dma *qmem, + u16 cq_id, u16 buffer_size) +{ + struct sli4_rqst_rq_create_v1 *rq; + u32 p; + uintptr_t addr; + u32 num_pages; + + rq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(rq_create_v1), + NULL); + if (!rq) + return -EIO; + + sli_cmd_fill_hdr(&rq->hdr, SLI4_OPC_RQ_CREATE, SLI4_SUBSYSTEM_FC, + CMD_V1, SLI4_RQST_PYLD_LEN(rq_create_v1)); + /* Disable "no buffer warnings" to avoid Lancer bug */ + rq->dim_dfd_dnb |= SLI4_RQ_CREATE_V1_DNB; + + /* valid values for number of pages: 1-8 (sec 4.5.6) */ + num_pages = sli_page_count(qmem->size, SLI_PAGE_SIZE); + rq->num_pages = cpu_to_le16(num_pages); + if (!num_pages || + num_pages > SLI4_RQ_CREATE_V1_MAX_PAGES) { + efc_log_info(sli4, "num_pages %d not valid, max %d\n", + num_pages, SLI4_RQ_CREATE_V1_MAX_PAGES); + return -EIO; + } + + /* + * RQE count is the total number of entries (note not lg2(# entries)) + */ + rq->rqe_count = cpu_to_le16(qmem->size / SLI4_RQE_SIZE); + + rq->rqe_size_byte |= SLI4_RQE_SIZE_8; + + rq->page_size = SLI4_RQ_PAGE_SIZE_4096; + + if (buffer_size < sli4->rq_min_buf_size || + buffer_size > sli4->rq_max_buf_size) { + efc_log_err(sli4, "buffer_size %d out of range (%d-%d)\n", + buffer_size, sli4->rq_min_buf_size, + sli4->rq_max_buf_size); + return -EIO; + } + rq->buffer_size = cpu_to_le32(buffer_size); + + rq->cq_id = cpu_to_le16(cq_id); + + for (p = 0, addr = qmem->phys; + p < num_pages; + p++, addr += SLI_PAGE_SIZE) { + rq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr)); + rq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr)); + } + + return 0; +} + +static int +sli_cmd_rq_create_v2(struct sli4 *sli4, u32 num_rqs, + struct sli4_queue *qs[], u32 base_cq_id, + u32 header_buffer_size, + u32 payload_buffer_size, struct efc_dma *dma) +{ + struct sli4_rqst_rq_create_v2 *req = NULL; + u32 i, p, offset = 0; + u32 payload_size, page_count; + uintptr_t addr; + u32 num_pages; + __le32 len; + + page_count = sli_page_count(qs[0]->dma.size, SLI_PAGE_SIZE) * num_rqs; + + /* Payload length must accommodate both request and response */ + payload_size = max(SLI4_RQST_CMDSZ(rq_create_v2) + + SZ_DMAADDR * page_count, + sizeof(struct sli4_rsp_cmn_create_queue_set)); + + dma->size = payload_size; + dma->virt = dma_alloc_coherent(&sli4->pci->dev, dma->size, + &dma->phys, GFP_KERNEL); + if (!dma->virt) + return -EIO; + + memset(dma->virt, 0, payload_size); + + req = sli_config_cmd_init(sli4, sli4->bmbx.virt, payload_size, dma); + if (!req) + return -EIO; + + len = SLI4_RQST_PYLD_LEN_VAR(rq_create_v2, SZ_DMAADDR * page_count); + sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_RQ_CREATE, SLI4_SUBSYSTEM_FC, + CMD_V2, len); + /* Fill Payload fields */ + req->dim_dfd_dnb |= SLI4_RQCREATEV2_DNB; + num_pages = sli_page_count(qs[0]->dma.size, SLI_PAGE_SIZE); + req->num_pages = cpu_to_le16(num_pages); + req->rqe_count = cpu_to_le16(qs[0]->dma.size / SLI4_RQE_SIZE); + req->rqe_size_byte |= SLI4_RQE_SIZE_8; + req->page_size = SLI4_RQ_PAGE_SIZE_4096; + req->rq_count = num_rqs; + req->base_cq_id = cpu_to_le16(base_cq_id); + req->hdr_buffer_size = cpu_to_le16(header_buffer_size); + req->payload_buffer_size = cpu_to_le16(payload_buffer_size); + + for (i = 0; i < num_rqs; i++) { + for (p = 0, addr = qs[i]->dma.phys; p < num_pages; + p++, addr += SLI_PAGE_SIZE) { + req->page_phys_addr[offset].low = + cpu_to_le32(lower_32_bits(addr)); + req->page_phys_addr[offset].high = + cpu_to_le32(upper_32_bits(addr)); + offset++; + } + } + + return 0; +} + +static void +__sli_queue_destroy(struct sli4 *sli4, struct sli4_queue *q) +{ + if (!q->dma.size) + return; + + dma_free_coherent(&sli4->pci->dev, q->dma.size, + q->dma.virt, q->dma.phys); + memset(&q->dma, 0, sizeof(struct efc_dma)); +} + +int +__sli_queue_init(struct sli4 *sli4, struct sli4_queue *q, u32 qtype, + size_t size, u32 n_entries, u32 align) +{ + if (q->dma.virt) { + efc_log_err(sli4, "%s failed\n", __func__); + return -EIO; + } + + memset(q, 0, sizeof(struct sli4_queue)); + + q->dma.size = size * n_entries; + q->dma.virt = dma_alloc_coherent(&sli4->pci->dev, q->dma.size, + &q->dma.phys, GFP_KERNEL); + if (!q->dma.virt) { + memset(&q->dma, 0, sizeof(struct efc_dma)); + efc_log_err(sli4, "%s allocation failed\n", SLI4_QNAME[qtype]); + return -EIO; + } + + memset(q->dma.virt, 0, size * n_entries); + + spin_lock_init(&q->lock); + + q->type = qtype; + q->size = size; + q->length = n_entries; + + if (q->type == SLI4_QTYPE_EQ || q->type == SLI4_QTYPE_CQ) { + /* For prism, phase will be flipped after + * a sweep through eq and cq + */ + q->phase = 1; + } + + /* Limit to hwf the queue size per interrupt */ + q->proc_limit = n_entries / 2; + + if (q->type == SLI4_QTYPE_EQ) + q->posted_limit = q->length / 2; + else + q->posted_limit = 64; + + return 0; +} + +int +sli_fc_rq_alloc(struct sli4 *sli4, struct sli4_queue *q, + u32 n_entries, u32 buffer_size, + struct sli4_queue *cq, bool is_hdr) +{ + if (__sli_queue_init(sli4, q, SLI4_QTYPE_RQ, SLI4_RQE_SIZE, + n_entries, SLI_PAGE_SIZE)) + return -EIO; + + if (sli_cmd_rq_create_v1(sli4, sli4->bmbx.virt, &q->dma, cq->id, + buffer_size)) + goto error; + + if (__sli_create_queue(sli4, q)) + goto error; + + if (is_hdr && q->id & 1) { + efc_log_info(sli4, "bad header RQ_ID %d\n", q->id); + goto error; + } else if (!is_hdr && (q->id & 1) == 0) { + efc_log_info(sli4, "bad data RQ_ID %d\n", q->id); + goto error; + } + + if (is_hdr) + q->u.flag |= SLI4_QUEUE_FLAG_HDR; + else + q->u.flag &= ~SLI4_QUEUE_FLAG_HDR; + + return 0; + +error: + __sli_queue_destroy(sli4, q); + return -EIO; +} + +int +sli_fc_rq_set_alloc(struct sli4 *sli4, u32 num_rq_pairs, + struct sli4_queue *qs[], u32 base_cq_id, + u32 n_entries, u32 header_buffer_size, + u32 payload_buffer_size) +{ + u32 i; + struct efc_dma dma = {0}; + struct sli4_rsp_cmn_create_queue_set *rsp = NULL; + void __iomem *db_regaddr = NULL; + u32 num_rqs = num_rq_pairs * 2; + + for (i = 0; i < num_rqs; i++) { + if (__sli_queue_init(sli4, qs[i], SLI4_QTYPE_RQ, + SLI4_RQE_SIZE, n_entries, + SLI_PAGE_SIZE)) { + goto error; + } + } + + if (sli_cmd_rq_create_v2(sli4, num_rqs, qs, base_cq_id, + header_buffer_size, payload_buffer_size, + &dma)) { + goto error; + } + + if (sli_bmbx_command(sli4)) { + efc_log_err(sli4, "bootstrap mailbox write failed RQSet\n"); + goto error; + } + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + db_regaddr = sli4->reg[1] + SLI4_IF6_RQ_DB_REG; + else + db_regaddr = sli4->reg[0] + SLI4_RQ_DB_REG; + + rsp = dma.virt; + if (rsp->hdr.status) { + efc_log_err(sli4, "bad create RQSet status=%#x addl=%#x\n", + rsp->hdr.status, rsp->hdr.additional_status); + goto error; + } + + for (i = 0; i < num_rqs; i++) { + qs[i]->id = i + le16_to_cpu(rsp->q_id); + if ((qs[i]->id & 1) == 0) + qs[i]->u.flag |= SLI4_QUEUE_FLAG_HDR; + else + qs[i]->u.flag &= ~SLI4_QUEUE_FLAG_HDR; + + qs[i]->db_regaddr = db_regaddr; + } + + dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, dma.phys); + + return 0; + +error: + for (i = 0; i < num_rqs; i++) + __sli_queue_destroy(sli4, qs[i]); + + if (dma.virt) + dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, + dma.phys); + + return -EIO; +} + +static int +sli_res_sli_config(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_sli_config *sli_config = buf; + + /* sanity check */ + if (!buf || sli_config->hdr.command != + SLI4_MBX_CMD_SLI_CONFIG) { + efc_log_err(sli4, "bad parameter buf=%p cmd=%#x\n", buf, + buf ? sli_config->hdr.command : -1); + return -EIO; + } + + if (le16_to_cpu(sli_config->hdr.status)) + return le16_to_cpu(sli_config->hdr.status); + + if (le32_to_cpu(sli_config->dw1_flags) & SLI4_SLICONF_EMB) + return sli_config->payload.embed[4]; + + efc_log_info(sli4, "external buffers not supported\n"); + return -EIO; +} + +int +__sli_create_queue(struct sli4 *sli4, struct sli4_queue *q) +{ + struct sli4_rsp_cmn_create_queue *res_q = NULL; + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail %s\n", + SLI4_QNAME[q->type]); + return -EIO; + } + if (sli_res_sli_config(sli4, sli4->bmbx.virt)) { + efc_log_err(sli4, "bad status create %s\n", + SLI4_QNAME[q->type]); + return -EIO; + } + res_q = (void *)((u8 *)sli4->bmbx.virt + + offsetof(struct sli4_cmd_sli_config, payload)); + + if (res_q->hdr.status) { + efc_log_err(sli4, "bad create %s status=%#x addl=%#x\n", + SLI4_QNAME[q->type], res_q->hdr.status, + res_q->hdr.additional_status); + return -EIO; + } + q->id = le16_to_cpu(res_q->q_id); + switch (q->type) { + case SLI4_QTYPE_EQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + q->db_regaddr = sli4->reg[1] + SLI4_IF6_EQ_DB_REG; + else + q->db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG; + break; + case SLI4_QTYPE_CQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + q->db_regaddr = sli4->reg[1] + SLI4_IF6_CQ_DB_REG; + else + q->db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG; + break; + case SLI4_QTYPE_MQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + q->db_regaddr = sli4->reg[1] + SLI4_IF6_MQ_DB_REG; + else + q->db_regaddr = sli4->reg[0] + SLI4_MQ_DB_REG; + break; + case SLI4_QTYPE_RQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + q->db_regaddr = sli4->reg[1] + SLI4_IF6_RQ_DB_REG; + else + q->db_regaddr = sli4->reg[0] + SLI4_RQ_DB_REG; + break; + case SLI4_QTYPE_WQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + q->db_regaddr = sli4->reg[1] + SLI4_IF6_WQ_DB_REG; + else + q->db_regaddr = sli4->reg[0] + SLI4_IO_WQ_DB_REG; + break; + default: + break; + } + + return 0; +} + +int +sli_get_queue_entry_size(struct sli4 *sli4, u32 qtype) +{ + u32 size = 0; + + switch (qtype) { + case SLI4_QTYPE_EQ: + size = sizeof(u32); + break; + case SLI4_QTYPE_CQ: + size = 16; + break; + case SLI4_QTYPE_MQ: + size = 256; + break; + case SLI4_QTYPE_WQ: + size = sli4->wqe_size; + break; + case SLI4_QTYPE_RQ: + size = SLI4_RQE_SIZE; + break; + default: + efc_log_info(sli4, "unknown queue type %d\n", qtype); + return -1; + } + return size; +} + +int +sli_queue_alloc(struct sli4 *sli4, u32 qtype, + struct sli4_queue *q, u32 n_entries, + struct sli4_queue *assoc) +{ + int size; + u32 align = 0; + + /* get queue size */ + size = sli_get_queue_entry_size(sli4, qtype); + if (size < 0) + return -EIO; + align = SLI_PAGE_SIZE; + + if (__sli_queue_init(sli4, q, qtype, size, n_entries, align)) + return -EIO; + + switch (qtype) { + case SLI4_QTYPE_EQ: + if (!sli_cmd_common_create_eq(sli4, sli4->bmbx.virt, &q->dma) && + !__sli_create_queue(sli4, q)) + return 0; + + break; + case SLI4_QTYPE_CQ: + if (!sli_cmd_common_create_cq(sli4, sli4->bmbx.virt, &q->dma, + assoc ? assoc->id : 0) && + !__sli_create_queue(sli4, q)) + return 0; + + break; + case SLI4_QTYPE_MQ: + assoc->u.flag |= SLI4_QUEUE_FLAG_MQ; + if (!sli_cmd_common_create_mq_ext(sli4, sli4->bmbx.virt, + &q->dma, assoc->id) && + !__sli_create_queue(sli4, q)) + return 0; + + break; + case SLI4_QTYPE_WQ: + if (!sli_cmd_wq_create(sli4, sli4->bmbx.virt, &q->dma, + assoc ? assoc->id : 0) && + !__sli_create_queue(sli4, q)) + return 0; + + break; + default: + efc_log_info(sli4, "unknown queue type %d\n", qtype); + } + + __sli_queue_destroy(sli4, q); + return -EIO; +} + +static int sli_cmd_cq_set_create(struct sli4 *sli4, + struct sli4_queue *qs[], u32 num_cqs, + struct sli4_queue *eqs[], + struct efc_dma *dma) +{ + struct sli4_rqst_cmn_create_cq_set_v0 *req = NULL; + uintptr_t addr; + u32 i, offset = 0, page_bytes = 0, payload_size; + u32 p = 0, page_size = 0, n_cqe = 0, num_pages_cq; + u32 dw5_flags = 0; + u16 dw6w1_flags = 0; + __le32 req_len; + + n_cqe = qs[0]->dma.size / SLI4_CQE_BYTES; + switch (n_cqe) { + case 256: + case 512: + case 1024: + case 2048: + page_size = 1; + break; + case 4096: + page_size = 2; + break; + default: + return -EIO; + } + + page_bytes = page_size * SLI_PAGE_SIZE; + num_pages_cq = sli_page_count(qs[0]->dma.size, page_bytes); + payload_size = max(SLI4_RQST_CMDSZ(cmn_create_cq_set_v0) + + (SZ_DMAADDR * num_pages_cq * num_cqs), + sizeof(struct sli4_rsp_cmn_create_queue_set)); + + dma->size = payload_size; + dma->virt = dma_alloc_coherent(&sli4->pci->dev, dma->size, + &dma->phys, GFP_KERNEL); + if (!dma->virt) + return -EIO; + + memset(dma->virt, 0, payload_size); + + req = sli_config_cmd_init(sli4, sli4->bmbx.virt, payload_size, dma); + if (!req) + return -EIO; + + req_len = SLI4_RQST_PYLD_LEN_VAR(cmn_create_cq_set_v0, + SZ_DMAADDR * num_pages_cq * num_cqs); + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_CREATE_CQ_SET, SLI4_SUBSYSTEM_FC, + CMD_V0, req_len); + req->page_size = page_size; + + req->num_pages = cpu_to_le16(num_pages_cq); + switch (num_pages_cq) { + case 1: + dw5_flags |= SLI4_CQ_CNT_VAL(256); + break; + case 2: + dw5_flags |= SLI4_CQ_CNT_VAL(512); + break; + case 4: + dw5_flags |= SLI4_CQ_CNT_VAL(1024); + break; + case 8: + dw5_flags |= SLI4_CQ_CNT_VAL(LARGE); + dw6w1_flags |= (n_cqe & SLI4_CREATE_CQSETV0_CQE_COUNT); + break; + default: + efc_log_info(sli4, "num_pages %d not valid\n", num_pages_cq); + return -EIO; + } + + dw5_flags |= SLI4_CREATE_CQSETV0_EVT; + dw5_flags |= SLI4_CREATE_CQSETV0_VALID; + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + dw5_flags |= SLI4_CREATE_CQSETV0_AUTOVALID; + + dw6w1_flags &= ~SLI4_CREATE_CQSETV0_ARM; + + req->dw5_flags = cpu_to_le32(dw5_flags); + req->dw6w1_flags = cpu_to_le16(dw6w1_flags); + + req->num_cq_req = cpu_to_le16(num_cqs); + + /* Fill page addresses of all the CQs. */ + for (i = 0; i < num_cqs; i++) { + req->eq_id[i] = cpu_to_le16(eqs[i]->id); + for (p = 0, addr = qs[i]->dma.phys; p < num_pages_cq; + p++, addr += page_bytes) { + req->page_phys_addr[offset].low = + cpu_to_le32(lower_32_bits(addr)); + req->page_phys_addr[offset].high = + cpu_to_le32(upper_32_bits(addr)); + offset++; + } + } + + return 0; +} + +int +sli_cq_alloc_set(struct sli4 *sli4, struct sli4_queue *qs[], + u32 num_cqs, u32 n_entries, struct sli4_queue *eqs[]) +{ + u32 i; + struct efc_dma dma = {0}; + struct sli4_rsp_cmn_create_queue_set *res; + void __iomem *db_regaddr; + + /* Align the queue DMA memory */ + for (i = 0; i < num_cqs; i++) { + if (__sli_queue_init(sli4, qs[i], SLI4_QTYPE_CQ, SLI4_CQE_BYTES, + n_entries, SLI_PAGE_SIZE)) + goto error; + } + + if (sli_cmd_cq_set_create(sli4, qs, num_cqs, eqs, &dma)) + goto error; + + if (sli_bmbx_command(sli4)) + goto error; + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + db_regaddr = sli4->reg[1] + SLI4_IF6_CQ_DB_REG; + else + db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG; + + res = dma.virt; + if (res->hdr.status) { + efc_log_err(sli4, "bad create CQSet status=%#x addl=%#x\n", + res->hdr.status, res->hdr.additional_status); + goto error; + } + + /* Check if we got all requested CQs. */ + if (le16_to_cpu(res->num_q_allocated) != num_cqs) { + efc_log_crit(sli4, "Requested count CQs doesn't match.\n"); + goto error; + } + /* Fill the resp cq ids. */ + for (i = 0; i < num_cqs; i++) { + qs[i]->id = le16_to_cpu(res->q_id) + i; + qs[i]->db_regaddr = db_regaddr; + } + + dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, dma.phys); + + return 0; + +error: + for (i = 0; i < num_cqs; i++) + __sli_queue_destroy(sli4, qs[i]); + + if (dma.virt) + dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, + dma.phys); + + return -EIO; +} + +static int +sli_cmd_common_destroy_q(struct sli4 *sli4, u8 opc, u8 subsystem, u16 q_id) +{ + struct sli4_rqst_cmn_destroy_q *req; + + /* Payload length must accommodate both request and response */ + req = sli_config_cmd_init(sli4, sli4->bmbx.virt, + SLI4_CFG_PYLD_LENGTH(cmn_destroy_q), NULL); + if (!req) + return -EIO; + + sli_cmd_fill_hdr(&req->hdr, opc, subsystem, + CMD_V0, SLI4_RQST_PYLD_LEN(cmn_destroy_q)); + req->q_id = cpu_to_le16(q_id); + + return 0; +} + +int +sli_queue_free(struct sli4 *sli4, struct sli4_queue *q, + u32 destroy_queues, u32 free_memory) +{ + int rc = 0; + u8 opcode, subsystem; + struct sli4_rsp_hdr *res; + + if (!q) { + efc_log_err(sli4, "bad parameter sli4=%p q=%p\n", sli4, q); + return -EIO; + } + + if (!destroy_queues) + goto free_mem; + + switch (q->type) { + case SLI4_QTYPE_EQ: + opcode = SLI4_CMN_DESTROY_EQ; + subsystem = SLI4_SUBSYSTEM_COMMON; + break; + case SLI4_QTYPE_CQ: + opcode = SLI4_CMN_DESTROY_CQ; + subsystem = SLI4_SUBSYSTEM_COMMON; + break; + case SLI4_QTYPE_MQ: + opcode = SLI4_CMN_DESTROY_MQ; + subsystem = SLI4_SUBSYSTEM_COMMON; + break; + case SLI4_QTYPE_WQ: + opcode = SLI4_OPC_WQ_DESTROY; + subsystem = SLI4_SUBSYSTEM_FC; + break; + case SLI4_QTYPE_RQ: + opcode = SLI4_OPC_RQ_DESTROY; + subsystem = SLI4_SUBSYSTEM_FC; + break; + default: + efc_log_info(sli4, "bad queue type %d\n", q->type); + rc = -EIO; + goto free_mem; + } + + rc = sli_cmd_common_destroy_q(sli4, opcode, subsystem, q->id); + if (rc) + goto free_mem; + + rc = sli_bmbx_command(sli4); + if (rc) + goto free_mem; + + rc = sli_res_sli_config(sli4, sli4->bmbx.virt); + if (rc) + goto free_mem; + + res = (void *)((u8 *)sli4->bmbx.virt + + offsetof(struct sli4_cmd_sli_config, payload)); + if (res->status) { + efc_log_err(sli4, "destroy %s st=%#x addl=%#x\n", + SLI4_QNAME[q->type], res->status, + res->additional_status); + rc = -EIO; + goto free_mem; + } + +free_mem: + if (free_memory) + __sli_queue_destroy(sli4, q); + + return rc; +} + +int +sli_queue_eq_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm) +{ + u32 val; + unsigned long flags = 0; + u32 a = arm ? SLI4_EQCQ_ARM : SLI4_EQCQ_UNARM; + + spin_lock_irqsave(&q->lock, flags); + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + val = sli_format_if6_eq_db_data(q->n_posted, q->id, a); + else + val = sli_format_eq_db_data(q->n_posted, q->id, a); + + writel(val, q->db_regaddr); + q->n_posted = 0; + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + +int +sli_queue_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm) +{ + u32 val = 0; + unsigned long flags = 0; + u32 a = arm ? SLI4_EQCQ_ARM : SLI4_EQCQ_UNARM; + + spin_lock_irqsave(&q->lock, flags); + + switch (q->type) { + case SLI4_QTYPE_EQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + val = sli_format_if6_eq_db_data(q->n_posted, q->id, a); + else + val = sli_format_eq_db_data(q->n_posted, q->id, a); + + writel(val, q->db_regaddr); + q->n_posted = 0; + break; + case SLI4_QTYPE_CQ: + if (sli4->if_type == SLI4_INTF_IF_TYPE_6) + val = sli_format_if6_cq_db_data(q->n_posted, q->id, a); + else + val = sli_format_cq_db_data(q->n_posted, q->id, a); + + writel(val, q->db_regaddr); + q->n_posted = 0; + break; + default: + efc_log_info(sli4, "should only be used for EQ/CQ, not %s\n", + SLI4_QNAME[q->type]); + } + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + +int +sli_wq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + u32 qindex; + u32 val = 0; + + qindex = q->index; + qe += q->index * q->size; + + if (sli4->params.perf_wq_id_association) + sli_set_wq_id_association(entry, q->id); + + memcpy(qe, entry, q->size); + val = sli_format_wq_db_data(q->id); + + writel(val, q->db_regaddr); + q->index = (q->index + 1) & (q->length - 1); + + return qindex; +} + +int +sli_mq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + u32 qindex; + u32 val = 0; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + qindex = q->index; + qe += q->index * q->size; + + memcpy(qe, entry, q->size); + val = sli_format_mq_db_data(q->id); + writel(val, q->db_regaddr); + q->index = (q->index + 1) & (q->length - 1); + spin_unlock_irqrestore(&q->lock, flags); + + return qindex; +} + +int +sli_rq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + u32 qindex; + u32 val = 0; + + qindex = q->index; + qe += q->index * q->size; + + memcpy(qe, entry, q->size); + + /* + * In RQ-pair, an RQ either contains the FC header + * (i.e. is_hdr == TRUE) or the payload. + * + * Don't ring doorbell for payload RQ + */ + if (!(q->u.flag & SLI4_QUEUE_FLAG_HDR)) + goto skip; + + val = sli_format_rq_db_data(q->id); + writel(val, q->db_regaddr); +skip: + q->index = (q->index + 1) & (q->length - 1); + + return qindex; +} + +int +sli_eq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + unsigned long flags = 0; + u16 wflags = 0; + + spin_lock_irqsave(&q->lock, flags); + + qe += q->index * q->size; + + /* Check if eqe is valid */ + wflags = le16_to_cpu(((struct sli4_eqe *)qe)->dw0w0_flags); + + if ((wflags & SLI4_EQE_VALID) != q->phase) { + spin_unlock_irqrestore(&q->lock, flags); + return -EIO; + } + + if (sli4->if_type != SLI4_INTF_IF_TYPE_6) { + wflags &= ~SLI4_EQE_VALID; + ((struct sli4_eqe *)qe)->dw0w0_flags = cpu_to_le16(wflags); + } + + memcpy(entry, qe, q->size); + q->index = (q->index + 1) & (q->length - 1); + q->n_posted++; + /* + * For prism, the phase value will be used + * to check the validity of eq/cq entries. + * The value toggles after a complete sweep + * through the queue. + */ + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6 && q->index == 0) + q->phase ^= (u16)0x1; + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + +int +sli_cq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + unsigned long flags = 0; + u32 dwflags = 0; + bool valid_bit_set; + + spin_lock_irqsave(&q->lock, flags); + + qe += q->index * q->size; + + /* Check if cqe is valid */ + dwflags = le32_to_cpu(((struct sli4_mcqe *)qe)->dw3_flags); + valid_bit_set = (dwflags & SLI4_MCQE_VALID) != 0; + + if (valid_bit_set != q->phase) { + spin_unlock_irqrestore(&q->lock, flags); + return -EIO; + } + + if (sli4->if_type != SLI4_INTF_IF_TYPE_6) { + dwflags &= ~SLI4_MCQE_VALID; + ((struct sli4_mcqe *)qe)->dw3_flags = cpu_to_le32(dwflags); + } + + memcpy(entry, qe, q->size); + q->index = (q->index + 1) & (q->length - 1); + q->n_posted++; + /* + * For prism, the phase value will be used + * to check the validity of eq/cq entries. + * The value toggles after a complete sweep + * through the queue. + */ + + if (sli4->if_type == SLI4_INTF_IF_TYPE_6 && q->index == 0) + q->phase ^= (u16)0x1; + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + +int +sli_mq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry) +{ + u8 *qe = q->dma.virt; + unsigned long flags = 0; + + spin_lock_irqsave(&q->lock, flags); + + qe += q->u.r_idx * q->size; + + /* Check if mqe is valid */ + if (q->index == q->u.r_idx) { + spin_unlock_irqrestore(&q->lock, flags); + return -EIO; + } + + memcpy(entry, qe, q->size); + q->u.r_idx = (q->u.r_idx + 1) & (q->length - 1); + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + +int +sli_eq_parse(struct sli4 *sli4, u8 *buf, u16 *cq_id) +{ + struct sli4_eqe *eqe = (void *)buf; + int rc = 0; + u16 flags = 0; + u16 majorcode; + u16 minorcode; + + if (!buf || !cq_id) { + efc_log_err(sli4, "bad parameters sli4=%p buf=%p cq_id=%p\n", + sli4, buf, cq_id); + return -EIO; + } + + flags = le16_to_cpu(eqe->dw0w0_flags); + majorcode = (flags & SLI4_EQE_MJCODE) >> 1; + minorcode = (flags & SLI4_EQE_MNCODE) >> 4; + switch (majorcode) { + case SLI4_MAJOR_CODE_STANDARD: + *cq_id = le16_to_cpu(eqe->resource_id); + break; + case SLI4_MAJOR_CODE_SENTINEL: + efc_log_info(sli4, "sentinel EQE\n"); + rc = SLI4_EQE_STATUS_EQ_FULL; + break; + default: + efc_log_info(sli4, "Unsupported EQE: major %x minor %x\n", + majorcode, minorcode); + rc = -EIO; + } + + return rc; +} + +int +sli_cq_parse(struct sli4 *sli4, struct sli4_queue *cq, u8 *cqe, + enum sli4_qentry *etype, u16 *q_id) +{ + int rc = 0; + + if (!cq || !cqe || !etype) { + efc_log_err(sli4, "bad params sli4=%p cq=%p cqe=%p etype=%p q_id=%p\n", + sli4, cq, cqe, etype, q_id); + return -EINVAL; + } + + /* Parse a CQ entry to retrieve the event type and the queue id */ + if (cq->u.flag & SLI4_QUEUE_FLAG_MQ) { + struct sli4_mcqe *mcqe = (void *)cqe; + + if (le32_to_cpu(mcqe->dw3_flags) & SLI4_MCQE_AE) { + *etype = SLI4_QENTRY_ASYNC; + } else { + *etype = SLI4_QENTRY_MQ; + rc = sli_cqe_mq(sli4, mcqe); + } + *q_id = -1; + } else { + rc = sli_fc_cqe_parse(sli4, cq, cqe, etype, q_id); + } + + return rc; +} + +int +sli_abort_wqe(struct sli4 *sli, void *buf, enum sli4_abort_type type, + bool send_abts, u32 ids, u32 mask, u16 tag, u16 cq_id) +{ + struct sli4_abort_wqe *abort = buf; + + memset(buf, 0, sli->wqe_size); + + switch (type) { + case SLI4_ABORT_XRI: + abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG; + if (mask) { + efc_log_warn(sli, "%#x aborting XRI %#x warning non-zero mask", + mask, ids); + mask = 0; + } + break; + case SLI4_ABORT_ABORT_ID: + abort->criteria = SLI4_ABORT_CRITERIA_ABORT_TAG; + break; + case SLI4_ABORT_REQUEST_ID: + abort->criteria = SLI4_ABORT_CRITERIA_REQUEST_TAG; + break; + default: + efc_log_info(sli, "unsupported type %#x\n", type); + return -EIO; + } + + abort->ia_ir_byte |= send_abts ? 0 : 1; + + /* Suppress ABTS retries */ + abort->ia_ir_byte |= SLI4_ABRT_WQE_IR; + + abort->t_mask = cpu_to_le32(mask); + abort->t_tag = cpu_to_le32(ids); + abort->command = SLI4_WQE_ABORT; + abort->request_tag = cpu_to_le16(tag); + + abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD); + + abort->cq_id = cpu_to_le16(cq_id); + abort->cmdtype_wqec_byte |= SLI4_CMD_ABORT_WQE; + + return 0; +} + +int +sli_els_request64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + struct sli_els_params *params) +{ + struct sli4_els_request64_wqe *els = buf; + struct sli4_sge *sge = sgl->virt; + bool is_fabric = false; + struct sli4_bde *bptr; + + memset(buf, 0, sli->wqe_size); + + bptr = &els->els_request_payload; + if (sli->params.sgl_pre_registered) { + els->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_REQ_WQE_XBL; + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->xmit_len & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + ((2 * sizeof(struct sli4_sge)) & + SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + } + + els->els_request_payload_length = cpu_to_le32(params->xmit_len); + els->max_response_payload_length = cpu_to_le32(params->rsp_len); + + els->xri_tag = cpu_to_le16(params->xri); + els->timer = params->timeout; + els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + els->command = SLI4_WQE_ELS_REQUEST64; + + els->request_tag = cpu_to_le16(params->tag); + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_IOD; + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_QOSD; + + /* figure out the ELS_ID value from the request buffer */ + + switch (params->cmd) { + case ELS_LOGO: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_LOGO << SLI4_REQ_WQE_ELSID_SHFT; + if (params->rpi_registered) { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->rpi); + } else { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + } + if (params->d_id == FC_FID_FLOGI) + is_fabric = true; + break; + case ELS_FDISC: + if (params->d_id == FC_FID_FLOGI) + is_fabric = true; + if (params->s_id == 0) { + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_FDISC << SLI4_REQ_WQE_ELSID_SHFT; + is_fabric = true; + } else { + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + } + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT); + break; + case ELS_FLOGI: + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + /* + * Set SP here ... we haven't done a REG_VPI yet + * need to maybe not set this when we have + * completed VFI/VPI registrations ... + * + * Use the FC_ID of the SPORT if it has been allocated, + * otherwise use an S_ID of zero. + */ + els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT); + if (params->s_id != U32_MAX) + els->sid_sp_dword |= cpu_to_le32(params->s_id); + break; + case ELS_PLOGI: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_PLOGI << SLI4_REQ_WQE_ELSID_SHFT; + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + break; + case ELS_SCR: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + break; + default: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + if (params->rpi_registered) { + els->ct_byte |= (SLI4_GENERIC_CONTEXT_RPI << + SLI4_REQ_WQE_CT_SHFT); + els->context_tag = cpu_to_le16(params->vpi); + } else { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + } + break; + } + + if (is_fabric) + els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_FABRIC; + else + els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_NON_FABRIC; + + els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) != + SLI4_GENERIC_CONTEXT_RPI) + els->remote_id_dword = cpu_to_le32(params->d_id); + + if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) == + SLI4_GENERIC_CONTEXT_VPI) + els->temporary_rpi = cpu_to_le16(params->rpi); + + return 0; +} + +int +sli_fcp_icmnd64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, u16 xri, + u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 timeout) +{ + struct sli4_fcp_icmnd64_wqe *icmnd = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + sge = sgl->virt; + bptr = &icmnd->bde; + if (sli->params.sgl_pre_registered) { + icmnd->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_ICMD_WQE_XBL; + + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + icmnd->payload_offset_length = cpu_to_le16(len); + icmnd->xri_tag = cpu_to_le16(xri); + icmnd->context_tag = cpu_to_le16(rpi); + icmnd->timer = timeout; + + /* WQE word 4 contains read transfer length */ + icmnd->class_pu_byte |= 2 << SLI4_ICMD_WQE_PU_SHFT; + icmnd->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + icmnd->command = SLI4_WQE_FCP_ICMND64; + icmnd->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_ICMD_WQE_CT_SHFT; + + icmnd->abort_tag = cpu_to_le32(xri); + + icmnd->request_tag = cpu_to_le16(tag); + icmnd->len_loc1_byte |= SLI4_ICMD_WQE_LEN_LOC_BIT1; + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_LEN_LOC_BIT2; + icmnd->cmd_type_byte |= SLI4_CMD_FCP_ICMND64_WQE; + icmnd->cq_id = cpu_to_le16(cq_id); + + return 0; +} + +int +sli_fcp_iread64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, u16 xri, u16 tag, + u16 cq_id, u32 rpi, u32 rnode_fcid, + u8 dif, u8 bs, u8 timeout) +{ + struct sli4_fcp_iread64_wqe *iread = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 sge_flags, len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + + sge = sgl->virt; + bptr = &iread->bde; + if (sli->params.sgl_pre_registered) { + iread->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IR_WQE_XBL; + + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = sge[0].buffer_address_low; + bptr->u.blp.high = sge[0].buffer_address_high; + } else { + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + + /* + * fill out fcp_cmnd buffer len and change resp buffer to be of + * type "skip" (note: response will still be written to sge[1] + * if necessary) + */ + len = le32_to_cpu(sge[0].buffer_length); + iread->fcp_cmd_buffer_length = cpu_to_le16(len); + + sge_flags = le32_to_cpu(sge[1].dw2_flags); + sge_flags &= (~SLI4_SGE_TYPE_MASK); + sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT); + sge[1].dw2_flags = cpu_to_le32(sge_flags); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + iread->payload_offset_length = cpu_to_le16(len); + iread->total_transfer_length = cpu_to_le32(xfer_len); + + iread->xri_tag = cpu_to_le16(xri); + iread->context_tag = cpu_to_le16(rpi); + + iread->timer = timeout; + + /* WQE word 4 contains read transfer length */ + iread->class_pu_byte |= 2 << SLI4_IR_WQE_PU_SHFT; + iread->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + iread->command = SLI4_WQE_FCP_IREAD64; + iread->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_IR_WQE_CT_SHFT; + iread->dif_ct_bs_byte |= dif; + iread->dif_ct_bs_byte |= bs << SLI4_IR_WQE_BS_SHFT; + + iread->abort_tag = cpu_to_le32(xri); + + iread->request_tag = cpu_to_le16(tag); + iread->len_loc1_byte |= SLI4_IR_WQE_LEN_LOC_BIT1; + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_LEN_LOC_BIT2; + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_IOD; + iread->cmd_type_byte |= SLI4_CMD_FCP_IREAD64_WQE; + iread->cq_id = cpu_to_le16(cq_id); + + if (sli->params.perf_hint) { + bptr = &iread->first_data_bde; + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = + sge[first_data_sge].buffer_address_low; + bptr->u.data.high = + sge[first_data_sge].buffer_address_high; + } + + return 0; +} + +int +sli_fcp_iwrite64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, + u32 first_burst, u16 xri, u16 tag, + u16 cq_id, u32 rpi, + u32 rnode_fcid, + u8 dif, u8 bs, u8 timeout) +{ + struct sli4_fcp_iwrite64_wqe *iwrite = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 sge_flags, min, len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + sge = sgl->virt; + bptr = &iwrite->bde; + if (sli->params.sgl_pre_registered) { + iwrite->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IWR_WQE_XBL; + + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_DBDE; + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_XBL; + + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + + /* + * fill out fcp_cmnd buffer len and change resp buffer to be of + * type "skip" (note: response will still be written to sge[1] + * if necessary) + */ + len = le32_to_cpu(sge[0].buffer_length); + iwrite->fcp_cmd_buffer_length = cpu_to_le16(len); + sge_flags = le32_to_cpu(sge[1].dw2_flags); + sge_flags &= ~SLI4_SGE_TYPE_MASK; + sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT); + sge[1].dw2_flags = cpu_to_le32(sge_flags); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + iwrite->payload_offset_length = cpu_to_le16(len); + iwrite->total_transfer_length = cpu_to_le16(xfer_len); + min = (xfer_len < first_burst) ? xfer_len : first_burst; + iwrite->initial_transfer_length = cpu_to_le16(min); + + iwrite->xri_tag = cpu_to_le16(xri); + iwrite->context_tag = cpu_to_le16(rpi); + + iwrite->timer = timeout; + /* WQE word 4 contains read transfer length */ + iwrite->class_pu_byte |= 2 << SLI4_IWR_WQE_PU_SHFT; + iwrite->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + iwrite->command = SLI4_WQE_FCP_IWRITE64; + iwrite->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_IWR_WQE_CT_SHFT; + iwrite->dif_ct_bs_byte |= dif; + iwrite->dif_ct_bs_byte |= bs << SLI4_IWR_WQE_BS_SHFT; + + iwrite->abort_tag = cpu_to_le32(xri); + + iwrite->request_tag = cpu_to_le16(tag); + iwrite->len_loc1_byte |= SLI4_IWR_WQE_LEN_LOC_BIT1; + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_LEN_LOC_BIT2; + iwrite->cmd_type_byte |= SLI4_CMD_FCP_IWRITE64_WQE; + iwrite->cq_id = cpu_to_le16(cq_id); + + if (sli->params.perf_hint) { + bptr = &iwrite->first_data_bde; + + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[first_data_sge].buffer_address_low; + bptr->u.data.high = sge[first_data_sge].buffer_address_high; + } + + return 0; +} + +int +sli_fcp_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_treceive64_wqe *trecv = buf; + struct sli4_fcp_128byte_wqe *trecv_128 = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + sge = sgl->virt; + bptr = &trecv->bde; + if (sli->params.sgl_pre_registered) { + trecv->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_TRCV_WQE_XBL; + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) + & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + + trecv->payload_offset_length = sge[0].buffer_length; + } else { + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_XBL; + + /* if data is a single physical address, use a BDE */ + if (!dif && + params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) { + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) + & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[2].buffer_address_low; + bptr->u.data.high = sge[2].buffer_address_high; + } else { + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + } + + trecv->relative_offset = cpu_to_le32(params->offset); + + if (params->flags & SLI4_IO_CONTINUATION) + trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_XC; + + trecv->xri_tag = cpu_to_le16(params->xri); + + trecv->context_tag = cpu_to_le16(params->rpi); + + /* WQE uses relative offset */ + trecv->class_ar_pu_byte |= 1 << SLI4_TRCV_WQE_PU_SHFT; + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) + trecv->class_ar_pu_byte |= SLI4_TRCV_WQE_AR; + + trecv->command = SLI4_WQE_FCP_TRECEIVE64; + trecv->class_ar_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + trecv->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_TRCV_WQE_CT_SHFT; + trecv->dif_ct_bs_byte |= bs << SLI4_TRCV_WQE_BS_SHFT; + + trecv->remote_xid = cpu_to_le16(params->ox_id); + + trecv->request_tag = cpu_to_le16(params->tag); + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_IOD; + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_LEN_LOC_BIT2; + + trecv->cmd_type_byte |= SLI4_CMD_FCP_TRECEIVE64_WQE; + + trecv->cq_id = cpu_to_le16(cq_id); + + trecv->fcp_data_receive_length = cpu_to_le32(params->xmit_len); + + if (sli->params.perf_hint) { + bptr = &trecv->first_data_bde; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[first_data_sge].buffer_address_low; + bptr->u.data.high = sge[first_data_sge].buffer_address_high; + } + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_CCPE; + trecv->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli->wqe_size == SLI4_WQE_EXT_BYTES && + !(trecv->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) { + trecv->lloc1_appid |= SLI4_TRCV_WQE_APPID; + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_WQES; + trecv_128->dw[31] = params->app_id; + } + return 0; +} + +int +sli_fcp_cont_treceive64_wqe(struct sli4 *sli, void *buf, + struct efc_dma *sgl, u32 first_data_sge, + u16 sec_xri, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + int rc; + + rc = sli_fcp_treceive64_wqe(sli, buf, sgl, first_data_sge, + cq_id, dif, bs, params); + if (!rc) { + struct sli4_fcp_treceive64_wqe *trecv = buf; + + trecv->command = SLI4_WQE_FCP_CONT_TRECEIVE64; + trecv->dword5.sec_xri_tag = cpu_to_le16(sec_xri); + } + return rc; +} + +int +sli_fcp_trsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u16 cq_id, u8 port_owned, struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_trsp64_wqe *trsp = buf; + struct sli4_fcp_128byte_wqe *trsp_128 = buf; + + memset(buf, 0, sli4->wqe_size); + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) { + trsp->class_ag_byte |= SLI4_TRSP_WQE_AG; + } else { + struct sli4_sge *sge = sgl->virt; + struct sli4_bde *bptr; + + if (sli4->params.sgl_pre_registered || port_owned) + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_DBDE; + else + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_XBL; + bptr = &trsp->bde; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + + trsp->fcp_response_length = cpu_to_le32(params->xmit_len); + } + + if (params->flags & SLI4_IO_CONTINUATION) + trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_XC; + + trsp->xri_tag = cpu_to_le16(params->xri); + trsp->rpi = cpu_to_le16(params->rpi); + + trsp->command = SLI4_WQE_FCP_TRSP64; + trsp->class_ag_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + trsp->remote_xid = cpu_to_le16(params->ox_id); + trsp->request_tag = cpu_to_le16(params->tag); + if (params->flags & SLI4_IO_DNRX) + trsp->ct_dnrx_byte |= SLI4_TRSP_WQE_DNRX; + else + trsp->ct_dnrx_byte &= ~SLI4_TRSP_WQE_DNRX; + + trsp->lloc1_appid |= 0x1; + trsp->cq_id = cpu_to_le16(cq_id); + trsp->cmd_type_byte = SLI4_CMD_FCP_TRSP64_WQE; + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_CCPE; + trsp->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES && + !(trsp->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) { + trsp->lloc1_appid |= SLI4_TRSP_WQE_APPID; + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_WQES; + trsp_128->dw[31] = params->app_id; + } + return 0; +} + +int +sli_fcp_tsend64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_tsend64_wqe *tsend = buf; + struct sli4_fcp_128byte_wqe *tsend_128 = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli4->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + sge = sgl->virt; + + bptr = &tsend->bde; + if (sli4->params.sgl_pre_registered) { + tsend->ll_qd_xbl_hlm_iod_dbde &= ~SLI4_TSEND_WQE_XBL; + + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) & + SLI4_BDE_LEN_MASK)); + + /* TSEND64_WQE specifies first two SGE are skipped (3rd is + * valid) + */ + bptr->u.data.low = sge[2].buffer_address_low; + bptr->u.data.high = sge[2].buffer_address_high; + } else { + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_XBL; + + /* if data is a single physical address, use a BDE */ + if (!dif && + params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) { + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) & + SLI4_BDE_LEN_MASK)); + /* + * TSEND64_WQE specifies first two SGE are skipped + * (i.e. 3rd is valid) + */ + bptr->u.data.low = + sge[2].buffer_address_low; + bptr->u.data.high = + sge[2].buffer_address_high; + } else { + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & + SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + } + + tsend->relative_offset = cpu_to_le32(params->offset); + + if (params->flags & SLI4_IO_CONTINUATION) + tsend->dw10byte2 |= SLI4_TSEND_XC; + + tsend->xri_tag = cpu_to_le16(params->xri); + + tsend->rpi = cpu_to_le16(params->rpi); + /* WQE uses relative offset */ + tsend->class_pu_ar_byte |= 1 << SLI4_TSEND_WQE_PU_SHFT; + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) + tsend->class_pu_ar_byte |= SLI4_TSEND_WQE_AR; + + tsend->command = SLI4_WQE_FCP_TSEND64; + tsend->class_pu_ar_byte |= SLI4_GENERIC_CLASS_CLASS_3; + tsend->ct_byte |= SLI4_GENERIC_CONTEXT_RPI << SLI4_TSEND_CT_SHFT; + tsend->ct_byte |= dif; + tsend->ct_byte |= bs << SLI4_TSEND_BS_SHFT; + + tsend->remote_xid = cpu_to_le16(params->ox_id); + + tsend->request_tag = cpu_to_le16(params->tag); + + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_LEN_LOC_BIT2; + + tsend->cq_id = cpu_to_le16(cq_id); + + tsend->cmd_type_byte |= SLI4_CMD_FCP_TSEND64_WQE; + + tsend->fcp_data_transmit_length = cpu_to_le32(params->xmit_len); + + if (sli4->params.perf_hint) { + bptr = &tsend->first_data_bde; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = + sge[first_data_sge].buffer_address_low; + bptr->u.data.high = + sge[first_data_sge].buffer_address_high; + } + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + tsend->dw10byte2 |= SLI4_TSEND_CCPE; + tsend->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES && + !(tsend->dw10byte2 & SLI4_TSEND_EAT)) { + tsend->dw10byte0 |= SLI4_TSEND_APPID_VALID; + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQES; + tsend_128->dw[31] = params->app_id; + } + return 0; +} + +int +sli_gen_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + struct sli_ct_params *params) +{ + struct sli4_gen_request64_wqe *gen = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli4->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return -EIO; + } + sge = sgl->virt; + bptr = &gen->bde; + + if (sli4->params.sgl_pre_registered) { + gen->dw10flags1 &= ~SLI4_GEN_REQ64_WQE_XBL; + + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->xmit_len & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + ((2 * sizeof(struct sli4_sge)) & + SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + + gen->request_payload_length = cpu_to_le32(params->xmit_len); + gen->max_response_payload_length = cpu_to_le32(params->rsp_len); + + gen->df_ctl = params->df_ctl; + gen->type = params->type; + gen->r_ctl = params->r_ctl; + + gen->xri_tag = cpu_to_le16(params->xri); + + gen->ct_byte = SLI4_GENERIC_CONTEXT_RPI << SLI4_GEN_REQ64_CT_SHFT; + gen->context_tag = cpu_to_le16(params->rpi); + + gen->class_byte = SLI4_GENERIC_CLASS_CLASS_3; + + gen->command = SLI4_WQE_GEN_REQUEST64; + + gen->timer = params->timeout; + + gen->request_tag = cpu_to_le16(params->tag); + + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_IOD; + + gen->dw10flags0 |= SLI4_GEN_REQ64_WQE_QOSD; + + gen->cmd_type_byte = SLI4_CMD_GEN_REQUEST64_WQE; + + gen->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + return 0; +} + +int +sli_send_frame_wqe(struct sli4 *sli, void *buf, u8 sof, u8 eof, u32 *hdr, + struct efc_dma *payload, u32 req_len, u8 timeout, u16 xri, + u16 req_tag) +{ + struct sli4_send_frame_wqe *sf = buf; + + memset(buf, 0, sli->wqe_size); + + sf->dw10flags1 |= SLI4_SF_WQE_DBDE; + sf->bde.bde_type_buflen = cpu_to_le32(req_len & + SLI4_BDE_LEN_MASK); + sf->bde.u.data.low = cpu_to_le32(lower_32_bits(payload->phys)); + sf->bde.u.data.high = cpu_to_le32(upper_32_bits(payload->phys)); + + /* Copy FC header */ + sf->fc_header_0_1[0] = cpu_to_le32(hdr[0]); + sf->fc_header_0_1[1] = cpu_to_le32(hdr[1]); + sf->fc_header_2_5[0] = cpu_to_le32(hdr[2]); + sf->fc_header_2_5[1] = cpu_to_le32(hdr[3]); + sf->fc_header_2_5[2] = cpu_to_le32(hdr[4]); + sf->fc_header_2_5[3] = cpu_to_le32(hdr[5]); + + sf->frame_length = cpu_to_le32(req_len); + + sf->xri_tag = cpu_to_le16(xri); + sf->dw7flags0 &= ~SLI4_SF_PU; + sf->context_tag = 0; + + sf->ct_byte &= ~SLI4_SF_CT; + sf->command = SLI4_WQE_SEND_FRAME; + sf->dw7flags0 |= SLI4_GENERIC_CLASS_CLASS_3; + sf->timer = timeout; + + sf->request_tag = cpu_to_le16(req_tag); + sf->eof = eof; + sf->sof = sof; + + sf->dw10flags1 &= ~SLI4_SF_QOSD; + sf->dw10flags0 |= SLI4_SF_LEN_LOC_BIT1; + sf->dw10flags2 &= ~SLI4_SF_XC; + + sf->dw10flags1 |= SLI4_SF_XBL; + + sf->cmd_type_byte |= SLI4_CMD_SEND_FRAME_WQE; + sf->cq_id = cpu_to_le16(0xffff); + + return 0; +} + +int +sli_xmit_bls_rsp64_wqe(struct sli4 *sli, void *buf, + struct sli_bls_payload *payload, + struct sli_bls_params *params) +{ + struct sli4_xmit_bls_rsp_wqe *bls = buf; + u32 dw_ridflags = 0; + + /* + * Callers can either specify RPI or S_ID, but not both + */ + if (params->rpi_registered && params->s_id != U32_MAX) { + efc_log_info(sli, "S_ID specified for attached remote node %d\n", + params->rpi); + return -EIO; + } + + memset(buf, 0, sli->wqe_size); + + if (payload->type == SLI4_SLI_BLS_ACC) { + bls->payload_word0 = + cpu_to_le32((payload->u.acc.seq_id_last << 16) | + (payload->u.acc.seq_id_validity << 24)); + bls->high_seq_cnt = payload->u.acc.high_seq_cnt; + bls->low_seq_cnt = payload->u.acc.low_seq_cnt; + } else if (payload->type == SLI4_SLI_BLS_RJT) { + bls->payload_word0 = + cpu_to_le32(*((u32 *)&payload->u.rjt)); + dw_ridflags |= SLI4_BLS_RSP_WQE_AR; + } else { + efc_log_info(sli, "bad BLS type %#x\n", payload->type); + return -EIO; + } + + bls->ox_id = payload->ox_id; + bls->rx_id = payload->rx_id; + + if (params->rpi_registered) { + bls->dw8flags0 |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_BLS_RSP_WQE_CT_SHFT; + bls->context_tag = cpu_to_le16(params->rpi); + } else { + bls->dw8flags0 |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_BLS_RSP_WQE_CT_SHFT; + bls->context_tag = cpu_to_le16(params->vpi); + + if (params->s_id != U32_MAX) + bls->local_n_port_id_dword |= + cpu_to_le32(params->s_id & 0x00ffffff); + else + bls->local_n_port_id_dword |= + cpu_to_le32(params->s_id & 0x00ffffff); + + dw_ridflags = (dw_ridflags & ~SLI4_BLS_RSP_RID) | + (params->d_id & SLI4_BLS_RSP_RID); + + bls->temporary_rpi = cpu_to_le16(params->rpi); + } + + bls->xri_tag = cpu_to_le16(params->xri); + + bls->dw8flags1 |= SLI4_GENERIC_CLASS_CLASS_3; + + bls->command = SLI4_WQE_XMIT_BLS_RSP; + + bls->request_tag = cpu_to_le16(params->tag); + + bls->dw11flags1 |= SLI4_BLS_RSP_WQE_QOSD; + + bls->remote_id_dword = cpu_to_le32(dw_ridflags); + bls->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + bls->dw12flags0 |= SLI4_CMD_XMIT_BLS_RSP64_WQE; + + return 0; +} + +int +sli_xmit_els_rsp64_wqe(struct sli4 *sli, void *buf, struct efc_dma *rsp, + struct sli_els_params *params) +{ + struct sli4_xmit_els_rsp64_wqe *els = buf; + + memset(buf, 0, sli->wqe_size); + + if (sli->params.sgl_pre_registered) + els->flags2 |= SLI4_ELS_DBDE; + else + els->flags2 |= SLI4_ELS_XBL; + + els->els_response_payload.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->rsp_len & SLI4_BDE_LEN_MASK)); + els->els_response_payload.u.data.low = + cpu_to_le32(lower_32_bits(rsp->phys)); + els->els_response_payload.u.data.high = + cpu_to_le32(upper_32_bits(rsp->phys)); + + els->els_response_payload_length = cpu_to_le32(params->rsp_len); + + els->xri_tag = cpu_to_le16(params->xri); + + els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + els->command = SLI4_WQE_ELS_RSP64; + + els->request_tag = cpu_to_le16(params->tag); + + els->ox_id = cpu_to_le16(params->ox_id); + + els->flags2 |= SLI4_ELS_QOSD; + + els->cmd_type_wqec = SLI4_ELS_REQUEST64_CMD_GEN; + + els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + if (params->rpi_registered) { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_ELS_CT_OFFSET; + els->context_tag = cpu_to_le16(params->rpi); + return 0; + } + + els->ct_byte |= SLI4_GENERIC_CONTEXT_VPI << SLI4_ELS_CT_OFFSET; + els->context_tag = cpu_to_le16(params->vpi); + els->rid_dw = cpu_to_le32(params->d_id & SLI4_ELS_RID); + els->temporary_rpi = cpu_to_le16(params->rpi); + if (params->s_id != U32_MAX) { + els->sid_dw |= + cpu_to_le32(SLI4_ELS_SP | (params->s_id & SLI4_ELS_SID)); + } + + return 0; +} + +int +sli_xmit_sequence64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *payload, + struct sli_ct_params *params) +{ + struct sli4_xmit_sequence64_wqe *xmit = buf; + + memset(buf, 0, sli4->wqe_size); + + if (!payload || !payload->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + payload, payload ? payload->virt : NULL); + return -EIO; + } + + if (sli4->params.sgl_pre_registered) + xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_DBDE); + else + xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_XBL); + + xmit->bde.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->rsp_len & SLI4_BDE_LEN_MASK)); + xmit->bde.u.data.low = + cpu_to_le32(lower_32_bits(payload->phys)); + xmit->bde.u.data.high = + cpu_to_le32(upper_32_bits(payload->phys)); + xmit->sequence_payload_len = cpu_to_le32(params->rsp_len); + + xmit->remote_n_port_id_dword |= cpu_to_le32(params->d_id & 0x00ffffff); + + xmit->relative_offset = 0; + + /* sequence initiative - this matches what is seen from + * FC switches in response to FCGS commands + */ + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_SI); + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_FT);/* force transmit */ + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_XO);/* exchange responder */ + xmit->dw5flags0 |= SLI4_SEQ_WQE_LS;/* last in seqence */ + xmit->df_ctl = params->df_ctl; + xmit->type = params->type; + xmit->r_ctl = params->r_ctl; + + xmit->xri_tag = cpu_to_le16(params->xri); + xmit->context_tag = cpu_to_le16(params->rpi); + + xmit->dw7flags0 &= ~SLI4_SEQ_WQE_DIF; + xmit->dw7flags0 |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_SEQ_WQE_CT_SHIFT; + xmit->dw7flags0 &= ~SLI4_SEQ_WQE_BS; + + xmit->command = SLI4_WQE_XMIT_SEQUENCE64; + xmit->dw7flags1 |= SLI4_GENERIC_CLASS_CLASS_3; + xmit->dw7flags1 &= ~SLI4_SEQ_WQE_PU; + xmit->timer = params->timeout; + + xmit->abort_tag = 0; + xmit->request_tag = cpu_to_le16(params->tag); + xmit->remote_xid = cpu_to_le16(params->ox_id); + + xmit->dw10w0 |= + cpu_to_le16(SLI4_ELS_REQUEST64_DIR_READ << SLI4_SEQ_WQE_IOD_SHIFT); + + xmit->cmd_type_wqec_byte |= SLI4_CMD_XMIT_SEQUENCE64_WQE; + + xmit->dw10w0 |= cpu_to_le16(2 << SLI4_SEQ_WQE_LEN_LOC_SHIFT); + + xmit->cq_id = cpu_to_le16(0xFFFF); + + return 0; +} + +int +sli_requeue_xri_wqe(struct sli4 *sli4, void *buf, u16 xri, u16 tag, u16 cq_id) +{ + struct sli4_requeue_xri_wqe *requeue = buf; + + memset(buf, 0, sli4->wqe_size); + + requeue->command = SLI4_WQE_REQUEUE_XRI; + requeue->xri_tag = cpu_to_le16(xri); + requeue->request_tag = cpu_to_le16(tag); + requeue->flags2 |= cpu_to_le16(SLI4_REQU_XRI_WQE_XC); + requeue->flags1 |= cpu_to_le16(SLI4_REQU_XRI_WQE_QOSD); + requeue->cq_id = cpu_to_le16(cq_id); + requeue->cmd_type_wqec_byte = SLI4_CMD_REQUEUE_XRI_WQE; + return 0; +} + +int +sli_fc_process_link_attention(struct sli4 *sli4, void *acqe) +{ + struct sli4_link_attention *link_attn = acqe; + struct sli4_link_event event = { 0 }; + + efc_log_info(sli4, "link=%d attn_type=%#x top=%#x speed=%#x pfault=%#x\n", + link_attn->link_number, link_attn->attn_type, + link_attn->topology, link_attn->port_speed, + link_attn->port_fault); + efc_log_info(sli4, "shared_lnk_status=%#x logl_lnk_speed=%#x evttag=%#x\n", + link_attn->shared_link_status, + le16_to_cpu(link_attn->logical_link_speed), + le32_to_cpu(link_attn->event_tag)); + + if (!sli4->link) + return -EIO; + + event.medium = SLI4_LINK_MEDIUM_FC; + + switch (link_attn->attn_type) { + case SLI4_LNK_ATTN_TYPE_LINK_UP: + event.status = SLI4_LINK_STATUS_UP; + break; + case SLI4_LNK_ATTN_TYPE_LINK_DOWN: + event.status = SLI4_LINK_STATUS_DOWN; + break; + case SLI4_LNK_ATTN_TYPE_NO_HARD_ALPA: + efc_log_info(sli4, "attn_type: no hard alpa\n"); + event.status = SLI4_LINK_STATUS_NO_ALPA; + break; + default: + efc_log_info(sli4, "attn_type: unknown\n"); + break; + } + + switch (link_attn->event_type) { + case SLI4_EVENT_LINK_ATTENTION: + break; + case SLI4_EVENT_SHARED_LINK_ATTENTION: + efc_log_info(sli4, "event_type: FC shared link event\n"); + break; + default: + efc_log_info(sli4, "event_type: unknown\n"); + break; + } + + switch (link_attn->topology) { + case SLI4_LNK_ATTN_P2P: + event.topology = SLI4_LINK_TOPO_NON_FC_AL; + break; + case SLI4_LNK_ATTN_FC_AL: + event.topology = SLI4_LINK_TOPO_FC_AL; + break; + case SLI4_LNK_ATTN_INTERNAL_LOOPBACK: + efc_log_info(sli4, "topology Internal loopback\n"); + event.topology = SLI4_LINK_TOPO_LOOPBACK_INTERNAL; + break; + case SLI4_LNK_ATTN_SERDES_LOOPBACK: + efc_log_info(sli4, "topology serdes loopback\n"); + event.topology = SLI4_LINK_TOPO_LOOPBACK_EXTERNAL; + break; + default: + efc_log_info(sli4, "topology: unknown\n"); + break; + } + + event.speed = link_attn->port_speed * 1000; + + sli4->link(sli4->link_arg, (void *)&event); + + return 0; +} + +int +sli_fc_cqe_parse(struct sli4 *sli4, struct sli4_queue *cq, + u8 *cqe, enum sli4_qentry *etype, u16 *r_id) +{ + u8 code = cqe[SLI4_CQE_CODE_OFFSET]; + int rc; + + switch (code) { + case SLI4_CQE_CODE_WORK_REQUEST_COMPLETION: + { + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + *etype = SLI4_QENTRY_WQ; + *r_id = le16_to_cpu(wcqe->request_tag); + rc = wcqe->status; + + /* Flag errors except for FCP_RSP_FAILURE */ + if (rc && rc != SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE) { + efc_log_info(sli4, "WCQE: status=%#x hw_status=%#x tag=%#x\n", + wcqe->status, wcqe->hw_status, + le16_to_cpu(wcqe->request_tag)); + efc_log_info(sli4, "w1=%#x w2=%#x xb=%d\n", + le32_to_cpu(wcqe->wqe_specific_1), + le32_to_cpu(wcqe->wqe_specific_2), + (wcqe->flags & SLI4_WCQE_XB)); + efc_log_info(sli4, " %08X %08X %08X %08X\n", + ((u32 *)cqe)[0], ((u32 *)cqe)[1], + ((u32 *)cqe)[2], ((u32 *)cqe)[3]); + } + + break; + } + case SLI4_CQE_CODE_RQ_ASYNC: + { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID; + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_RQ_ASYNC_V1: + { + struct sli4_fc_async_rcqe_v1 *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->rq_id); + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD: + { + struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe; + + *etype = SLI4_QENTRY_OPT_WRITE_CMD; + *r_id = le16_to_cpu(optcqe->rq_id); + rc = optcqe->status; + break; + } + case SLI4_CQE_CODE_OPTIMIZED_WRITE_DATA: + { + struct sli4_fc_optimized_write_data_cqe *dcqe = (void *)cqe; + + *etype = SLI4_QENTRY_OPT_WRITE_DATA; + *r_id = le16_to_cpu(dcqe->xri); + rc = dcqe->status; + + /* Flag errors */ + if (rc != SLI4_FC_WCQE_STATUS_SUCCESS) { + efc_log_info(sli4, "Optimized DATA CQE: status=%#x\n", + dcqe->status); + efc_log_info(sli4, "hstat=%#x xri=%#x dpl=%#x w3=%#x xb=%d\n", + dcqe->hw_status, le16_to_cpu(dcqe->xri), + le32_to_cpu(dcqe->total_data_placed), + ((u32 *)cqe)[3], + (dcqe->flags & SLI4_OCQE_XB)); + } + break; + } + case SLI4_CQE_CODE_RQ_COALESCING: + { + struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->rq_id); + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_XRI_ABORTED: + { + struct sli4_fc_xri_aborted_cqe *xa = (void *)cqe; + + *etype = SLI4_QENTRY_XABT; + *r_id = le16_to_cpu(xa->xri); + rc = 0; + break; + } + case SLI4_CQE_CODE_RELEASE_WQE: + { + struct sli4_fc_wqec *wqec = (void *)cqe; + + *etype = SLI4_QENTRY_WQ_RELEASE; + *r_id = le16_to_cpu(wqec->wq_id); + rc = 0; + break; + } + default: + efc_log_info(sli4, "CQE completion code %d not handled\n", + code); + *etype = SLI4_QENTRY_MAX; + *r_id = U16_MAX; + rc = -EINVAL; + } + + return rc; +} + +u32 +sli_fc_response_length(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + return le32_to_cpu(wcqe->wqe_specific_1); +} + +u32 +sli_fc_io_length(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + return le32_to_cpu(wcqe->wqe_specific_1); +} + +int +sli_fc_els_did(struct sli4 *sli4, u8 *cqe, u32 *d_id) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + *d_id = 0; + + if (wcqe->status) + return -EIO; + *d_id = le32_to_cpu(wcqe->wqe_specific_2) & 0x00ffffff; + return 0; +} + +u32 +sli_fc_ext_status(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + u32 mask; + + switch (wcqe->status) { + case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE: + mask = U32_MAX; + break; + case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: + case SLI4_FC_WCQE_STATUS_CMD_REJECT: + mask = 0xff; + break; + case SLI4_FC_WCQE_STATUS_NPORT_RJT: + case SLI4_FC_WCQE_STATUS_FABRIC_RJT: + case SLI4_FC_WCQE_STATUS_NPORT_BSY: + case SLI4_FC_WCQE_STATUS_FABRIC_BSY: + case SLI4_FC_WCQE_STATUS_LS_RJT: + mask = U32_MAX; + break; + case SLI4_FC_WCQE_STATUS_DI_ERROR: + mask = U32_MAX; + break; + default: + mask = 0; + } + + return le32_to_cpu(wcqe->wqe_specific_2) & mask; +} + +int +sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index) +{ + int rc = -EIO; + u8 code = 0; + u16 rq_element_index; + + *rq_id = 0; + *index = U32_MAX; + + code = cqe[SLI4_CQE_CODE_OFFSET]; + + /* Retrieve the RQ index from the completion */ + if (code == SLI4_CQE_CODE_RQ_ASYNC) { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *rq_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID; + rq_element_index = + le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX; + *index = rq_element_index; + if (rcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = 0; + } else { + rc = rcqe->status; + efc_log_info(sli4, "status=%02x (%s) rq_id=%d\n", + rcqe->status, + sli_fc_get_status_string(rcqe->status), + le16_to_cpu(rcqe->fcfi_rq_id_word) & + SLI4_RACQE_RQ_ID); + + efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n", + le16_to_cpu(rcqe->data_placement_length), + rcqe->sof_byte, rcqe->eof_byte, + rcqe->hdpl_byte & SLI4_RACQE_HDPL); + } + } else if (code == SLI4_CQE_CODE_RQ_ASYNC_V1) { + struct sli4_fc_async_rcqe_v1 *rcqe_v1 = (void *)cqe; + + *rq_id = le16_to_cpu(rcqe_v1->rq_id); + rq_element_index = + (le16_to_cpu(rcqe_v1->rq_elmt_indx_word) & + SLI4_RACQE_RQ_EL_INDX); + *index = rq_element_index; + if (rcqe_v1->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = 0; + } else { + rc = rcqe_v1->status; + efc_log_info(sli4, "status=%02x (%s) rq_id=%d, index=%x\n", + rcqe_v1->status, + sli_fc_get_status_string(rcqe_v1->status), + le16_to_cpu(rcqe_v1->rq_id), rq_element_index); + + efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n", + le16_to_cpu(rcqe_v1->data_placement_length), + rcqe_v1->sof_byte, rcqe_v1->eof_byte, + rcqe_v1->hdpl_byte & SLI4_RACQE_HDPL); + } + } else if (code == SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD) { + struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe; + + *rq_id = le16_to_cpu(optcqe->rq_id); + *index = le16_to_cpu(optcqe->w1) & SLI4_OCQE_RQ_EL_INDX; + if (optcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = 0; + } else { + rc = optcqe->status; + efc_log_info(sli4, "stat=%02x (%s) rqid=%d, idx=%x pdpl=%x\n", + optcqe->status, + sli_fc_get_status_string(optcqe->status), + le16_to_cpu(optcqe->rq_id), *index, + le16_to_cpu(optcqe->data_placement_length)); + + efc_log_info(sli4, "hdpl=%x oox=%d agxr=%d xri=0x%x rpi=%x\n", + (optcqe->hdpl_vld & SLI4_OCQE_HDPL), + (optcqe->flags1 & SLI4_OCQE_OOX), + (optcqe->flags1 & SLI4_OCQE_AGXR), + optcqe->xri, le16_to_cpu(optcqe->rpi)); + } + } else if (code == SLI4_CQE_CODE_RQ_COALESCING) { + struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe; + + rq_element_index = (le16_to_cpu(rcqe->rq_elmt_indx_word) & + SLI4_RCQE_RQ_EL_INDX); + + *rq_id = le16_to_cpu(rcqe->rq_id); + if (rcqe->status == SLI4_FC_COALESCE_RQ_SUCCESS) { + *index = rq_element_index; + rc = 0; + } else { + *index = U32_MAX; + rc = rcqe->status; + + efc_log_info(sli4, "stat=%02x (%s) rq_id=%d, idx=%x\n", + rcqe->status, + sli_fc_get_status_string(rcqe->status), + le16_to_cpu(rcqe->rq_id), rq_element_index); + efc_log_info(sli4, "rq_id=%#x sdpl=%x\n", + le16_to_cpu(rcqe->rq_id), + le16_to_cpu(rcqe->seq_placement_length)); + } + } else { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *index = U32_MAX; + rc = rcqe->status; + + efc_log_info(sli4, "status=%02x rq_id=%d, index=%x pdpl=%x\n", + rcqe->status, + le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID, + (le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX), + le16_to_cpu(rcqe->data_placement_length)); + efc_log_info(sli4, "sof=%02x eof=%02x hdpl=%x\n", + rcqe->sof_byte, rcqe->eof_byte, + rcqe->hdpl_byte & SLI4_RACQE_HDPL); + } + + return rc; +} + +static int +sli_bmbx_wait(struct sli4 *sli4, u32 msec) +{ + u32 val; + unsigned long end; + + /* Wait for the bootstrap mailbox to report "ready" */ + end = jiffies + msecs_to_jiffies(msec); + do { + val = readl(sli4->reg[0] + SLI4_BMBX_REG); + if (val & SLI4_BMBX_RDY) + return 0; + + usleep_range(1000, 2000); + } while (time_before(jiffies, end)); + + return -EIO; +} + +static int +sli_bmbx_write(struct sli4 *sli4) +{ + u32 val; + + /* write buffer location to bootstrap mailbox register */ + val = sli_bmbx_write_hi(sli4->bmbx.phys); + writel(val, (sli4->reg[0] + SLI4_BMBX_REG)); + + if (sli_bmbx_wait(sli4, SLI4_BMBX_DELAY_US)) { + efc_log_crit(sli4, "BMBX WRITE_HI failed\n"); + return -EIO; + } + val = sli_bmbx_write_lo(sli4->bmbx.phys); + writel(val, (sli4->reg[0] + SLI4_BMBX_REG)); + + /* wait for SLI Port to set ready bit */ + return sli_bmbx_wait(sli4, SLI4_BMBX_TIMEOUT_MSEC); +} + +int +sli_bmbx_command(struct sli4 *sli4) +{ + void *cqe = (u8 *)sli4->bmbx.virt + SLI4_BMBX_SIZE; + + if (sli_fw_error_status(sli4) > 0) { + efc_log_crit(sli4, "Chip is in an error state -Mailbox command rejected"); + efc_log_crit(sli4, " status=%#x error1=%#x error2=%#x\n", + sli_reg_read_status(sli4), + sli_reg_read_err1(sli4), + sli_reg_read_err2(sli4)); + return -EIO; + } + + /* Submit a command to the bootstrap mailbox and check the status */ + if (sli_bmbx_write(sli4)) { + efc_log_crit(sli4, "bmbx write fail phys=%pad reg=%#x\n", + &sli4->bmbx.phys, readl(sli4->reg[0] + SLI4_BMBX_REG)); + return -EIO; + } + + /* check completion queue entry status */ + if (le32_to_cpu(((struct sli4_mcqe *)cqe)->dw3_flags) & + SLI4_MCQE_VALID) { + return sli_cqe_mq(sli4, cqe); + } + efc_log_crit(sli4, "invalid or wrong type\n"); + return -EIO; +} + +int +sli_cmd_config_link(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_config_link *config_link = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + config_link->hdr.command = SLI4_MBX_CMD_CONFIG_LINK; + + /* Port interprets zero in a field as "use default value" */ + + return 0; +} + +int +sli_cmd_down_link(struct sli4 *sli4, void *buf) +{ + struct sli4_mbox_command_header *hdr = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + hdr->command = SLI4_MBX_CMD_DOWN_LINK; + + /* Port interprets zero in a field as "use default value" */ + + return 0; +} + +int +sli_cmd_dump_type4(struct sli4 *sli4, void *buf, u16 wki) +{ + struct sli4_cmd_dump4 *cmd = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_DUMP; + cmd->type_dword = cpu_to_le32(0x4); + cmd->wki_selection = cpu_to_le16(wki); + return 0; +} + +int +sli_cmd_common_read_transceiver_data(struct sli4 *sli4, void *buf, u32 page_num, + struct efc_dma *dma) +{ + struct sli4_rqst_cmn_read_transceiver_data *req = NULL; + u32 psize; + + if (!dma) + psize = SLI4_CFG_PYLD_LENGTH(cmn_read_transceiver_data); + else + psize = dma->size; + + req = sli_config_cmd_init(sli4, buf, psize, dma); + if (!req) + return -EIO; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_READ_TRANS_DATA, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_read_transceiver_data)); + + req->page_number = cpu_to_le32(page_num); + req->port = cpu_to_le32(sli4->port_number); + + return 0; +} + +int +sli_cmd_read_link_stats(struct sli4 *sli4, void *buf, u8 req_ext_counters, + u8 clear_overflow_flags, + u8 clear_all_counters) +{ + struct sli4_cmd_read_link_stats *cmd = buf; + u32 flags; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_READ_LNK_STAT; + + flags = 0; + if (req_ext_counters) + flags |= SLI4_READ_LNKSTAT_REC; + if (clear_all_counters) + flags |= SLI4_READ_LNKSTAT_CLRC; + if (clear_overflow_flags) + flags |= SLI4_READ_LNKSTAT_CLOF; + + cmd->dw1_flags = cpu_to_le32(flags); + return 0; +} + +int +sli_cmd_read_status(struct sli4 *sli4, void *buf, u8 clear_counters) +{ + struct sli4_cmd_read_status *cmd = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_READ_STATUS; + if (clear_counters) + flags |= SLI4_READSTATUS_CLEAR_COUNTERS; + else + flags &= ~SLI4_READSTATUS_CLEAR_COUNTERS; + + cmd->dw1_flags = cpu_to_le32(flags); + return 0; +} + +int +sli_cmd_init_link(struct sli4 *sli4, void *buf, u32 speed, u8 reset_alpa) +{ + struct sli4_cmd_init_link *init_link = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_link->hdr.command = SLI4_MBX_CMD_INIT_LINK; + + init_link->sel_reset_al_pa_dword = + cpu_to_le32(reset_alpa); + flags &= ~SLI4_INIT_LINK_F_LOOPBACK; + + init_link->link_speed_sel_code = cpu_to_le32(speed); + switch (speed) { + case SLI4_LINK_SPEED_1G: + case SLI4_LINK_SPEED_2G: + case SLI4_LINK_SPEED_4G: + case SLI4_LINK_SPEED_8G: + case SLI4_LINK_SPEED_16G: + case SLI4_LINK_SPEED_32G: + case SLI4_LINK_SPEED_64G: + flags |= SLI4_INIT_LINK_F_FIXED_SPEED; + break; + case SLI4_LINK_SPEED_10G: + efc_log_info(sli4, "unsupported FC speed %d\n", speed); + init_link->flags0 = cpu_to_le32(flags); + return -EIO; + } + + switch (sli4->topology) { + case SLI4_READ_CFG_TOPO_FC: + /* Attempt P2P but failover to FC-AL */ + flags |= SLI4_INIT_LINK_F_FAIL_OVER; + flags |= SLI4_INIT_LINK_F_P2P_FAIL_OVER; + break; + case SLI4_READ_CFG_TOPO_FC_AL: + flags |= SLI4_INIT_LINK_F_FCAL_ONLY; + if (speed == SLI4_LINK_SPEED_16G || + speed == SLI4_LINK_SPEED_32G) { + efc_log_info(sli4, "unsupported FC-AL speed %d\n", + speed); + init_link->flags0 = cpu_to_le32(flags); + return -EIO; + } + break; + case SLI4_READ_CFG_TOPO_NON_FC_AL: + flags |= SLI4_INIT_LINK_F_P2P_ONLY; + break; + default: + + efc_log_info(sli4, "unsupported topology %#x\n", sli4->topology); + + init_link->flags0 = cpu_to_le32(flags); + return -EIO; + } + + flags &= ~SLI4_INIT_LINK_F_UNFAIR; + flags &= ~SLI4_INIT_LINK_F_NO_LIRP; + flags &= ~SLI4_INIT_LINK_F_LOOP_VALID_CHK; + flags &= ~SLI4_INIT_LINK_F_NO_LISA; + flags &= ~SLI4_INIT_LINK_F_PICK_HI_ALPA; + init_link->flags0 = cpu_to_le32(flags); + + return 0; +} + +int +sli_cmd_init_vfi(struct sli4 *sli4, void *buf, u16 vfi, u16 fcfi, u16 vpi) +{ + struct sli4_cmd_init_vfi *init_vfi = buf; + u16 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_vfi->hdr.command = SLI4_MBX_CMD_INIT_VFI; + init_vfi->vfi = cpu_to_le16(vfi); + init_vfi->fcfi = cpu_to_le16(fcfi); + + /* + * If the VPI is valid, initialize it at the same time as + * the VFI + */ + if (vpi != U16_MAX) { + flags |= SLI4_INIT_VFI_FLAG_VP; + init_vfi->flags0_word = cpu_to_le16(flags); + init_vfi->vpi = cpu_to_le16(vpi); + } + + return 0; +} + +int +sli_cmd_init_vpi(struct sli4 *sli4, void *buf, u16 vpi, u16 vfi) +{ + struct sli4_cmd_init_vpi *init_vpi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_vpi->hdr.command = SLI4_MBX_CMD_INIT_VPI; + init_vpi->vpi = cpu_to_le16(vpi); + init_vpi->vfi = cpu_to_le16(vfi); + + return 0; +} + +int +sli_cmd_post_xri(struct sli4 *sli4, void *buf, u16 xri_base, u16 xri_count) +{ + struct sli4_cmd_post_xri *post_xri = buf; + u16 xri_count_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + post_xri->hdr.command = SLI4_MBX_CMD_POST_XRI; + post_xri->xri_base = cpu_to_le16(xri_base); + xri_count_flags = xri_count & SLI4_POST_XRI_COUNT; + xri_count_flags |= SLI4_POST_XRI_FLAG_ENX; + xri_count_flags |= SLI4_POST_XRI_FLAG_VAL; + post_xri->xri_count_flags = cpu_to_le16(xri_count_flags); + + return 0; +} + +int +sli_cmd_release_xri(struct sli4 *sli4, void *buf, u8 num_xri) +{ + struct sli4_cmd_release_xri *release_xri = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + release_xri->hdr.command = SLI4_MBX_CMD_RELEASE_XRI; + release_xri->xri_count_word = cpu_to_le16(num_xri & + SLI4_RELEASE_XRI_COUNT); + + return 0; +} + +static int +sli_cmd_read_config(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_read_config *read_config = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_config->hdr.command = SLI4_MBX_CMD_READ_CONFIG; + + return 0; +} + +int +sli_cmd_read_nvparms(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_read_nvparms *read_nvparms = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_nvparms->hdr.command = SLI4_MBX_CMD_READ_NVPARMS; + + return 0; +} + +int +sli_cmd_write_nvparms(struct sli4 *sli4, void *buf, u8 *wwpn, u8 *wwnn, + u8 hard_alpa, u32 preferred_d_id) +{ + struct sli4_cmd_write_nvparms *write_nvparms = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + write_nvparms->hdr.command = SLI4_MBX_CMD_WRITE_NVPARMS; + memcpy(write_nvparms->wwpn, wwpn, 8); + memcpy(write_nvparms->wwnn, wwnn, 8); + + write_nvparms->hard_alpa_d_id = + cpu_to_le32((preferred_d_id << 8) | hard_alpa); + return 0; +} + +static int +sli_cmd_read_rev(struct sli4 *sli4, void *buf, struct efc_dma *vpd) +{ + struct sli4_cmd_read_rev *read_rev = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_rev->hdr.command = SLI4_MBX_CMD_READ_REV; + + if (vpd && vpd->size) { + read_rev->flags0_word |= cpu_to_le16(SLI4_READ_REV_FLAG_VPD); + + read_rev->available_length_dword = + cpu_to_le32(vpd->size & + SLI4_READ_REV_AVAILABLE_LENGTH); + + read_rev->hostbuf.low = + cpu_to_le32(lower_32_bits(vpd->phys)); + read_rev->hostbuf.high = + cpu_to_le32(upper_32_bits(vpd->phys)); + } + + return 0; +} + +int +sli_cmd_read_sparm64(struct sli4 *sli4, void *buf, struct efc_dma *dma, u16 vpi) +{ + struct sli4_cmd_read_sparm64 *read_sparm64 = buf; + + if (vpi == U16_MAX) { + efc_log_err(sli4, "special VPI not supported!!!\n"); + return -EIO; + } + + if (!dma || !dma->phys) { + efc_log_err(sli4, "bad DMA buffer\n"); + return -EIO; + } + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_sparm64->hdr.command = SLI4_MBX_CMD_READ_SPARM64; + + read_sparm64->bde_64.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (dma->size & SLI4_BDE_LEN_MASK)); + read_sparm64->bde_64.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + read_sparm64->bde_64.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + read_sparm64->vpi = cpu_to_le16(vpi); + + return 0; +} + +int +sli_cmd_read_topology(struct sli4 *sli4, void *buf, struct efc_dma *dma) +{ + struct sli4_cmd_read_topology *read_topo = buf; + + if (!dma || !dma->size) + return -EIO; + + if (dma->size < SLI4_MIN_LOOP_MAP_BYTES) { + efc_log_err(sli4, "loop map buffer too small %zx\n", dma->size); + return -EIO; + } + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_topo->hdr.command = SLI4_MBX_CMD_READ_TOPOLOGY; + + memset(dma->virt, 0, dma->size); + + read_topo->bde_loop_map.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (dma->size & SLI4_BDE_LEN_MASK)); + read_topo->bde_loop_map.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + read_topo->bde_loop_map.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + return 0; +} + +int +sli_cmd_reg_fcfi(struct sli4 *sli4, void *buf, u16 index, + struct sli4_cmd_rq_cfg *rq_cfg) +{ + struct sli4_cmd_reg_fcfi *reg_fcfi = buf; + u32 i; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_fcfi->hdr.command = SLI4_MBX_CMD_REG_FCFI; + + reg_fcfi->fcf_index = cpu_to_le16(index); + + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + switch (i) { + case 0: + reg_fcfi->rqid0 = rq_cfg[0].rq_id; + break; + case 1: + reg_fcfi->rqid1 = rq_cfg[1].rq_id; + break; + case 2: + reg_fcfi->rqid2 = rq_cfg[2].rq_id; + break; + case 3: + reg_fcfi->rqid3 = rq_cfg[3].rq_id; + break; + } + reg_fcfi->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask; + reg_fcfi->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match; + reg_fcfi->rq_cfg[i].type_mask = rq_cfg[i].type_mask; + reg_fcfi->rq_cfg[i].type_match = rq_cfg[i].type_match; + } + + return 0; +} + +int +sli_cmd_reg_fcfi_mrq(struct sli4 *sli4, void *buf, u8 mode, u16 fcf_index, + u8 rq_selection_policy, u8 mrq_bit_mask, u16 num_mrqs, + struct sli4_cmd_rq_cfg *rq_cfg) +{ + struct sli4_cmd_reg_fcfi_mrq *reg_fcfi_mrq = buf; + u32 i; + u32 mrq_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_fcfi_mrq->hdr.command = SLI4_MBX_CMD_REG_FCFI_MRQ; + if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) { + reg_fcfi_mrq->fcf_index = cpu_to_le16(fcf_index); + goto done; + } + + reg_fcfi_mrq->dw8_vlan = cpu_to_le32(SLI4_REGFCFI_MRQ_MODE); + + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + reg_fcfi_mrq->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask; + reg_fcfi_mrq->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match; + reg_fcfi_mrq->rq_cfg[i].type_mask = rq_cfg[i].type_mask; + reg_fcfi_mrq->rq_cfg[i].type_match = rq_cfg[i].type_match; + + switch (i) { + case 3: + reg_fcfi_mrq->rqid3 = rq_cfg[i].rq_id; + break; + case 2: + reg_fcfi_mrq->rqid2 = rq_cfg[i].rq_id; + break; + case 1: + reg_fcfi_mrq->rqid1 = rq_cfg[i].rq_id; + break; + case 0: + reg_fcfi_mrq->rqid0 = rq_cfg[i].rq_id; + break; + } + } + + mrq_flags = num_mrqs & SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS; + mrq_flags |= (mrq_bit_mask << 8); + mrq_flags |= (rq_selection_policy << 12); + reg_fcfi_mrq->dw9_mrqflags = cpu_to_le32(mrq_flags); +done: + return 0; +} + +int +sli_cmd_reg_rpi(struct sli4 *sli4, void *buf, u32 rpi, u32 vpi, u32 fc_id, + struct efc_dma *dma, u8 update, u8 enable_t10_pi) +{ + struct sli4_cmd_reg_rpi *reg_rpi = buf; + u32 rportid_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_rpi->hdr.command = SLI4_MBX_CMD_REG_RPI; + + reg_rpi->rpi = cpu_to_le16(rpi); + + rportid_flags = fc_id & SLI4_REGRPI_REMOTE_N_PORTID; + + if (update) + rportid_flags |= SLI4_REGRPI_UPD; + else + rportid_flags &= ~SLI4_REGRPI_UPD; + + if (enable_t10_pi) + rportid_flags |= SLI4_REGRPI_ETOW; + else + rportid_flags &= ~SLI4_REGRPI_ETOW; + + reg_rpi->dw2_rportid_flags = cpu_to_le32(rportid_flags); + + reg_rpi->bde_64.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK)); + reg_rpi->bde_64.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + reg_rpi->bde_64.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + reg_rpi->vpi = cpu_to_le16(vpi); + + return 0; +} + +int +sli_cmd_reg_vfi(struct sli4 *sli4, void *buf, size_t size, + u16 vfi, u16 fcfi, struct efc_dma dma, + u16 vpi, __be64 sli_wwpn, u32 fc_id) +{ + struct sli4_cmd_reg_vfi *reg_vfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_vfi->hdr.command = SLI4_MBX_CMD_REG_VFI; + + reg_vfi->vfi = cpu_to_le16(vfi); + + reg_vfi->fcfi = cpu_to_le16(fcfi); + + reg_vfi->sparm.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK)); + reg_vfi->sparm.u.data.low = + cpu_to_le32(lower_32_bits(dma.phys)); + reg_vfi->sparm.u.data.high = + cpu_to_le32(upper_32_bits(dma.phys)); + + reg_vfi->e_d_tov = cpu_to_le32(sli4->e_d_tov); + reg_vfi->r_a_tov = cpu_to_le32(sli4->r_a_tov); + + reg_vfi->dw0w1_flags |= cpu_to_le16(SLI4_REGVFI_VP); + reg_vfi->vpi = cpu_to_le16(vpi); + memcpy(reg_vfi->wwpn, &sli_wwpn, sizeof(reg_vfi->wwpn)); + reg_vfi->dw10_lportid_flags = cpu_to_le32(fc_id); + + return 0; +} + +int +sli_cmd_reg_vpi(struct sli4 *sli4, void *buf, u32 fc_id, __be64 sli_wwpn, + u16 vpi, u16 vfi, bool update) +{ + struct sli4_cmd_reg_vpi *reg_vpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_vpi->hdr.command = SLI4_MBX_CMD_REG_VPI; + + flags = (fc_id & SLI4_REGVPI_LOCAL_N_PORTID); + if (update) + flags |= SLI4_REGVPI_UPD; + else + flags &= ~SLI4_REGVPI_UPD; + + reg_vpi->dw2_lportid_flags = cpu_to_le32(flags); + memcpy(reg_vpi->wwpn, &sli_wwpn, sizeof(reg_vpi->wwpn)); + reg_vpi->vpi = cpu_to_le16(vpi); + reg_vpi->vfi = cpu_to_le16(vfi); + + return 0; +} + +static int +sli_cmd_request_features(struct sli4 *sli4, void *buf, u32 features_mask, + bool query) +{ + struct sli4_cmd_request_features *req_features = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + req_features->hdr.command = SLI4_MBX_CMD_RQST_FEATURES; + + if (query) + req_features->dw1_qry = cpu_to_le32(SLI4_REQFEAT_QRY); + + req_features->cmd = cpu_to_le32(features_mask); + + return 0; +} + +int +sli_cmd_unreg_fcfi(struct sli4 *sli4, void *buf, u16 indicator) +{ + struct sli4_cmd_unreg_fcfi *unreg_fcfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_fcfi->hdr.command = SLI4_MBX_CMD_UNREG_FCFI; + unreg_fcfi->fcfi = cpu_to_le16(indicator); + + return 0; +} + +int +sli_cmd_unreg_rpi(struct sli4 *sli4, void *buf, u16 indicator, + enum sli4_resource which, u32 fc_id) +{ + struct sli4_cmd_unreg_rpi *unreg_rpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_rpi->hdr.command = SLI4_MBX_CMD_UNREG_RPI; + switch (which) { + case SLI4_RSRC_RPI: + flags |= SLI4_UNREG_RPI_II_RPI; + if (fc_id == U32_MAX) + break; + + flags |= SLI4_UNREG_RPI_DP; + unreg_rpi->dw2_dest_n_portid = + cpu_to_le32(fc_id & SLI4_UNREG_RPI_DEST_N_PORTID_MASK); + break; + case SLI4_RSRC_VPI: + flags |= SLI4_UNREG_RPI_II_VPI; + break; + case SLI4_RSRC_VFI: + flags |= SLI4_UNREG_RPI_II_VFI; + break; + case SLI4_RSRC_FCFI: + flags |= SLI4_UNREG_RPI_II_FCFI; + break; + default: + efc_log_info(sli4, "unknown type %#x\n", which); + return -EIO; + } + + unreg_rpi->dw1w1_flags = cpu_to_le16(flags); + unreg_rpi->index = cpu_to_le16(indicator); + + return 0; +} + +int +sli_cmd_unreg_vfi(struct sli4 *sli4, void *buf, u16 index, u32 which) +{ + struct sli4_cmd_unreg_vfi *unreg_vfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_vfi->hdr.command = SLI4_MBX_CMD_UNREG_VFI; + switch (which) { + case SLI4_UNREG_TYPE_DOMAIN: + unreg_vfi->index = cpu_to_le16(index); + break; + case SLI4_UNREG_TYPE_FCF: + unreg_vfi->index = cpu_to_le16(index); + break; + case SLI4_UNREG_TYPE_ALL: + unreg_vfi->index = cpu_to_le16(U32_MAX); + break; + default: + return -EIO; + } + + if (which != SLI4_UNREG_TYPE_DOMAIN) + unreg_vfi->dw2_flags = cpu_to_le16(SLI4_UNREG_VFI_II_FCFI); + + return 0; +} + +int +sli_cmd_unreg_vpi(struct sli4 *sli4, void *buf, u16 indicator, u32 which) +{ + struct sli4_cmd_unreg_vpi *unreg_vpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_vpi->hdr.command = SLI4_MBX_CMD_UNREG_VPI; + unreg_vpi->index = cpu_to_le16(indicator); + switch (which) { + case SLI4_UNREG_TYPE_PORT: + flags |= SLI4_UNREG_VPI_II_VPI; + break; + case SLI4_UNREG_TYPE_DOMAIN: + flags |= SLI4_UNREG_VPI_II_VFI; + break; + case SLI4_UNREG_TYPE_FCF: + flags |= SLI4_UNREG_VPI_II_FCFI; + break; + case SLI4_UNREG_TYPE_ALL: + /* override indicator */ + unreg_vpi->index = cpu_to_le16(U32_MAX); + flags |= SLI4_UNREG_VPI_II_FCFI; + break; + default: + return -EIO; + } + + unreg_vpi->dw2w0_flags = cpu_to_le16(flags); + return 0; +} + +static int +sli_cmd_common_modify_eq_delay(struct sli4 *sli4, void *buf, + struct sli4_queue *q, int num_q, u32 shift, + u32 delay_mult) +{ + struct sli4_rqst_cmn_modify_eq_delay *req = NULL; + int i; + + req = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_modify_eq_delay), NULL); + if (!req) + return -EIO; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_MODIFY_EQ_DELAY, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_modify_eq_delay)); + req->num_eq = cpu_to_le32(num_q); + + for (i = 0; i < num_q; i++) { + req->eq_delay_record[i].eq_id = cpu_to_le32(q[i].id); + req->eq_delay_record[i].phase = cpu_to_le32(shift); + req->eq_delay_record[i].delay_multiplier = + cpu_to_le32(delay_mult); + } + + return 0; +} + +void +sli4_cmd_lowlevel_set_watchdog(struct sli4 *sli4, void *buf, + size_t size, u16 timeout) +{ + struct sli4_rqst_lowlevel_set_watchdog *req = NULL; + + req = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(lowlevel_set_watchdog), NULL); + if (!req) + return; + + sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_LOWLEVEL_SET_WATCHDOG, + SLI4_SUBSYSTEM_LOWLEVEL, CMD_V0, + SLI4_RQST_PYLD_LEN(lowlevel_set_watchdog)); + req->watchdog_timeout = cpu_to_le16(timeout); +} + +static int +sli_cmd_common_get_cntl_attributes(struct sli4 *sli4, void *buf, + struct efc_dma *dma) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma); + if (!hdr) + return -EIO; + + hdr->opcode = SLI4_CMN_GET_CNTL_ATTRIBUTES; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = cpu_to_le32(dma->size); + + return 0; +} + +static int +sli_cmd_common_get_cntl_addl_attributes(struct sli4 *sli4, void *buf, + struct efc_dma *dma) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma); + if (!hdr) + return -EIO; + + hdr->opcode = SLI4_CMN_GET_CNTL_ADDL_ATTRS; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = cpu_to_le32(dma->size); + + return 0; +} + +int +sli_cmd_common_nop(struct sli4 *sli4, void *buf, uint64_t context) +{ + struct sli4_rqst_cmn_nop *nop = NULL; + + nop = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(cmn_nop), + NULL); + if (!nop) + return -EIO; + + sli_cmd_fill_hdr(&nop->hdr, SLI4_CMN_NOP, SLI4_SUBSYSTEM_COMMON, + CMD_V0, SLI4_RQST_PYLD_LEN(cmn_nop)); + + memcpy(&nop->context, &context, sizeof(context)); + + return 0; +} + +int +sli_cmd_common_get_resource_extent_info(struct sli4 *sli4, void *buf, u16 rtype) +{ + struct sli4_rqst_cmn_get_resource_extent_info *ext = NULL; + + ext = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_get_resource_extent_info), NULL); + if (!ext) + return -EIO; + + sli_cmd_fill_hdr(&ext->hdr, SLI4_CMN_GET_RSC_EXTENT_INFO, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_get_resource_extent_info)); + + ext->resource_type = cpu_to_le16(rtype); + + return 0; +} + +int +sli_cmd_common_get_sli4_parameters(struct sli4 *sli4, void *buf) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_get_sli4_params), NULL); + if (!hdr) + return -EIO; + + hdr->opcode = SLI4_CMN_GET_SLI4_PARAMS; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = SLI4_RQST_PYLD_LEN(cmn_get_sli4_params); + + return 0; +} + +static int +sli_cmd_common_get_port_name(struct sli4 *sli4, void *buf) +{ + struct sli4_rqst_cmn_get_port_name *pname; + + pname = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_get_port_name), NULL); + if (!pname) + return -EIO; + + sli_cmd_fill_hdr(&pname->hdr, SLI4_CMN_GET_PORT_NAME, + SLI4_SUBSYSTEM_COMMON, CMD_V1, + SLI4_RQST_PYLD_LEN(cmn_get_port_name)); + + /* Set the port type value (ethernet=0, FC=1) for V1 commands */ + pname->port_type = SLI4_PORT_TYPE_FC; + + return 0; +} + +int +sli_cmd_common_write_object(struct sli4 *sli4, void *buf, u16 noc, + u16 eof, u32 desired_write_length, + u32 offset, char *obj_name, + struct efc_dma *dma) +{ + struct sli4_rqst_cmn_write_object *wr_obj = NULL; + struct sli4_bde *bde; + u32 dwflags = 0; + + wr_obj = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_write_object) + sizeof(*bde), NULL); + if (!wr_obj) + return -EIO; + + sli_cmd_fill_hdr(&wr_obj->hdr, SLI4_CMN_WRITE_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN_VAR(cmn_write_object, sizeof(*bde))); + + if (noc) + dwflags |= SLI4_RQ_DES_WRITE_LEN_NOC; + if (eof) + dwflags |= SLI4_RQ_DES_WRITE_LEN_EOF; + dwflags |= (desired_write_length & SLI4_RQ_DES_WRITE_LEN); + + wr_obj->desired_write_len_dword = cpu_to_le32(dwflags); + + wr_obj->write_offset = cpu_to_le32(offset); + strncpy(wr_obj->object_name, obj_name, sizeof(wr_obj->object_name) - 1); + wr_obj->host_buffer_descriptor_count = cpu_to_le32(1); + + bde = (struct sli4_bde *)wr_obj->host_buffer_descriptor; + + /* Setup to transfer xfer_size bytes to device */ + bde->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (desired_write_length & SLI4_BDE_LEN_MASK)); + bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys)); + bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys)); + + return 0; +} + +int +sli_cmd_common_delete_object(struct sli4 *sli4, void *buf, char *obj_name) +{ + struct sli4_rqst_cmn_delete_object *req = NULL; + + req = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_delete_object), NULL); + if (!req) + return -EIO; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_DELETE_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_delete_object)); + + strncpy(req->object_name, obj_name, sizeof(req->object_name) - 1); + return 0; +} + +int +sli_cmd_common_read_object(struct sli4 *sli4, void *buf, u32 desired_read_len, + u32 offset, char *obj_name, struct efc_dma *dma) +{ + struct sli4_rqst_cmn_read_object *rd_obj = NULL; + struct sli4_bde *bde; + + rd_obj = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_read_object) + sizeof(*bde), NULL); + if (!rd_obj) + return -EIO; + + sli_cmd_fill_hdr(&rd_obj->hdr, SLI4_CMN_READ_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN_VAR(cmn_read_object, sizeof(*bde))); + rd_obj->desired_read_length_dword = + cpu_to_le32(desired_read_len & SLI4_REQ_DESIRE_READLEN); + + rd_obj->read_offset = cpu_to_le32(offset); + strncpy(rd_obj->object_name, obj_name, sizeof(rd_obj->object_name) - 1); + rd_obj->host_buffer_descriptor_count = cpu_to_le32(1); + + bde = (struct sli4_bde *)rd_obj->host_buffer_descriptor; + + /* Setup to transfer xfer_size bytes to device */ + bde->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (desired_read_len & SLI4_BDE_LEN_MASK)); + if (dma) { + bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys)); + bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys)); + } else { + bde->u.data.low = 0; + bde->u.data.high = 0; + } + + return 0; +} + +int +sli_cmd_dmtf_exec_clp_cmd(struct sli4 *sli4, void *buf, struct efc_dma *cmd, + struct efc_dma *resp) +{ + struct sli4_rqst_dmtf_exec_clp_cmd *clp_cmd = NULL; + + clp_cmd = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(dmtf_exec_clp_cmd), NULL); + if (!clp_cmd) + return -EIO; + + sli_cmd_fill_hdr(&clp_cmd->hdr, DMTF_EXEC_CLP_CMD, SLI4_SUBSYSTEM_DMTF, + CMD_V0, SLI4_RQST_PYLD_LEN(dmtf_exec_clp_cmd)); + + clp_cmd->cmd_buf_length = cpu_to_le32(cmd->size); + clp_cmd->cmd_buf_addr_low = cpu_to_le32(lower_32_bits(cmd->phys)); + clp_cmd->cmd_buf_addr_high = cpu_to_le32(upper_32_bits(cmd->phys)); + clp_cmd->resp_buf_length = cpu_to_le32(resp->size); + clp_cmd->resp_buf_addr_low = cpu_to_le32(lower_32_bits(resp->phys)); + clp_cmd->resp_buf_addr_high = cpu_to_le32(upper_32_bits(resp->phys)); + return 0; +} + +int +sli_cmd_common_set_dump_location(struct sli4 *sli4, void *buf, bool query, + bool is_buffer_list, + struct efc_dma *buffer, u8 fdb) +{ + struct sli4_rqst_cmn_set_dump_location *set_dump_loc = NULL; + u32 buffer_length_flag = 0; + + set_dump_loc = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_set_dump_location), NULL); + if (!set_dump_loc) + return -EIO; + + sli_cmd_fill_hdr(&set_dump_loc->hdr, SLI4_CMN_SET_DUMP_LOCATION, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_set_dump_location)); + + if (is_buffer_list) + buffer_length_flag |= SLI4_CMN_SET_DUMP_BLP; + + if (query) + buffer_length_flag |= SLI4_CMN_SET_DUMP_QRY; + + if (fdb) + buffer_length_flag |= SLI4_CMN_SET_DUMP_FDB; + + if (buffer) { + set_dump_loc->buf_addr_low = + cpu_to_le32(lower_32_bits(buffer->phys)); + set_dump_loc->buf_addr_high = + cpu_to_le32(upper_32_bits(buffer->phys)); + + buffer_length_flag |= + buffer->len & SLI4_CMN_SET_DUMP_BUFFER_LEN; + } else { + set_dump_loc->buf_addr_low = 0; + set_dump_loc->buf_addr_high = 0; + set_dump_loc->buffer_length_dword = 0; + } + set_dump_loc->buffer_length_dword = cpu_to_le32(buffer_length_flag); + return 0; +} + +int +sli_cmd_common_set_features(struct sli4 *sli4, void *buf, u32 feature, + u32 param_len, void *parameter) +{ + struct sli4_rqst_cmn_set_features *cmd = NULL; + + cmd = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_set_features), NULL); + if (!cmd) + return -EIO; + + sli_cmd_fill_hdr(&cmd->hdr, SLI4_CMN_SET_FEATURES, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_set_features)); + + cmd->feature = cpu_to_le32(feature); + cmd->param_len = cpu_to_le32(param_len); + memcpy(cmd->params, parameter, param_len); + + return 0; +} + +int +sli_cqe_mq(struct sli4 *sli4, void *buf) +{ + struct sli4_mcqe *mcqe = buf; + u32 dwflags = le32_to_cpu(mcqe->dw3_flags); + /* + * Firmware can split mbx completions into two MCQEs: first with only + * the "consumed" bit set and a second with the "complete" bit set. + * Thus, ignore MCQE unless "complete" is set. + */ + if (!(dwflags & SLI4_MCQE_COMPLETED)) + return SLI4_MCQE_STATUS_NOT_COMPLETED; + + if (le16_to_cpu(mcqe->completion_status)) { + efc_log_info(sli4, "status(st=%#x ext=%#x con=%d cmp=%d ae=%d val=%d)\n", + le16_to_cpu(mcqe->completion_status), + le16_to_cpu(mcqe->extended_status), + (dwflags & SLI4_MCQE_CONSUMED), + (dwflags & SLI4_MCQE_COMPLETED), + (dwflags & SLI4_MCQE_AE), + (dwflags & SLI4_MCQE_VALID)); + } + + return le16_to_cpu(mcqe->completion_status); +} + +int +sli_cqe_async(struct sli4 *sli4, void *buf) +{ + struct sli4_acqe *acqe = buf; + int rc = -EIO; + + if (!buf) { + efc_log_err(sli4, "bad parameter sli4=%p buf=%p\n", sli4, buf); + return -EIO; + } + + switch (acqe->event_code) { + case SLI4_ACQE_EVENT_CODE_LINK_STATE: + efc_log_info(sli4, "Unsupported by FC link, evt code:%#x\n", + acqe->event_code); + break; + case SLI4_ACQE_EVENT_CODE_GRP_5: + efc_log_info(sli4, "ACQE GRP5\n"); + break; + case SLI4_ACQE_EVENT_CODE_SLI_PORT_EVENT: + efc_log_info(sli4, "ACQE SLI Port, type=0x%x, data1,2=0x%08x,0x%08x\n", + acqe->event_type, + le32_to_cpu(acqe->event_data[0]), + le32_to_cpu(acqe->event_data[1])); + break; + case SLI4_ACQE_EVENT_CODE_FC_LINK_EVENT: + rc = sli_fc_process_link_attention(sli4, buf); + break; + default: + efc_log_info(sli4, "ACQE unknown=%#x\n", acqe->event_code); + } + + return rc; +} + +bool +sli_fw_ready(struct sli4 *sli4) +{ + u32 val; + + /* Determine if the chip FW is in a ready state */ + val = sli_reg_read_status(sli4); + return (val & SLI4_PORT_STATUS_RDY) ? 1 : 0; +} + +static bool +sli_wait_for_fw_ready(struct sli4 *sli4, u32 timeout_ms) +{ + unsigned long end; + + end = jiffies + msecs_to_jiffies(timeout_ms); + + do { + if (sli_fw_ready(sli4)) + return true; + + usleep_range(1000, 2000); + } while (time_before(jiffies, end)); + + return false; +} + +static bool +sli_sliport_reset(struct sli4 *sli4) +{ + bool rc; + u32 val; + + val = SLI4_PORT_CTRL_IP; + /* Initialize port, endian */ + writel(val, (sli4->reg[0] + SLI4_PORT_CTRL_REG)); + + rc = sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC); + if (!rc) + efc_log_crit(sli4, "port failed to become ready after initialization\n"); + + return rc; +} + +static bool +sli_fw_init(struct sli4 *sli4) +{ + /* + * Is firmware ready for operation? + */ + if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) { + efc_log_crit(sli4, "FW status is NOT ready\n"); + return false; + } + + /* + * Reset port to a known state + */ + return sli_sliport_reset(sli4); +} + +static int +sli_request_features(struct sli4 *sli4, u32 *features, bool query) +{ + struct sli4_cmd_request_features *req_features = sli4->bmbx.virt; + + if (sli_cmd_request_features(sli4, sli4->bmbx.virt, *features, query)) { + efc_log_err(sli4, "bad REQUEST_FEATURES write\n"); + return -EIO; + } + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail\n"); + return -EIO; + } + + if (le16_to_cpu(req_features->hdr.status)) { + efc_log_err(sli4, "REQUEST_FEATURES bad status %#x\n", + le16_to_cpu(req_features->hdr.status)); + return -EIO; + } + + *features = le32_to_cpu(req_features->resp); + return 0; +} + +void +sli_calc_max_qentries(struct sli4 *sli4) +{ + enum sli4_qtype q; + u32 qentries; + + for (q = SLI4_QTYPE_EQ; q < SLI4_QTYPE_MAX; q++) { + sli4->qinfo.max_qentries[q] = + sli_convert_mask_to_count(sli4->qinfo.count_method[q], + sli4->qinfo.count_mask[q]); + } + + /* single, contiguous DMA allocations will be called for each queue + * of size (max_qentries * queue entry size); since these can be large, + * check against the OS max DMA allocation size + */ + for (q = SLI4_QTYPE_EQ; q < SLI4_QTYPE_MAX; q++) { + qentries = sli4->qinfo.max_qentries[q]; + + efc_log_info(sli4, "[%s]: max_qentries from %d to %d\n", + SLI4_QNAME[q], + sli4->qinfo.max_qentries[q], qentries); + sli4->qinfo.max_qentries[q] = qentries; + } +} + +static int +sli_get_read_config(struct sli4 *sli4) +{ + struct sli4_rsp_read_config *conf = sli4->bmbx.virt; + u32 i, total; + u32 *base; + + if (sli_cmd_read_config(sli4, sli4->bmbx.virt)) { + efc_log_err(sli4, "bad READ_CONFIG write\n"); + return -EIO; + } + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox fail (READ_CONFIG)\n"); + return -EIO; + } + + if (le16_to_cpu(conf->hdr.status)) { + efc_log_err(sli4, "READ_CONFIG bad status %#x\n", + le16_to_cpu(conf->hdr.status)); + return -EIO; + } + + sli4->params.has_extents = + le32_to_cpu(conf->ext_dword) & SLI4_READ_CFG_RESP_RESOURCE_EXT; + if (sli4->params.has_extents) { + efc_log_err(sli4, "extents not supported\n"); + return -EIO; + } + + base = sli4->ext[0].base; + if (!base) { + int size = SLI4_RSRC_MAX * sizeof(u32); + + base = kzalloc(size, GFP_KERNEL); + if (!base) + return -EIO; + } + + for (i = 0; i < SLI4_RSRC_MAX; i++) { + sli4->ext[i].number = 1; + sli4->ext[i].n_alloc = 0; + sli4->ext[i].base = &base[i]; + } + + sli4->ext[SLI4_RSRC_VFI].base[0] = le16_to_cpu(conf->vfi_base); + sli4->ext[SLI4_RSRC_VFI].size = le16_to_cpu(conf->vfi_count); + + sli4->ext[SLI4_RSRC_VPI].base[0] = le16_to_cpu(conf->vpi_base); + sli4->ext[SLI4_RSRC_VPI].size = le16_to_cpu(conf->vpi_count); + + sli4->ext[SLI4_RSRC_RPI].base[0] = le16_to_cpu(conf->rpi_base); + sli4->ext[SLI4_RSRC_RPI].size = le16_to_cpu(conf->rpi_count); + + sli4->ext[SLI4_RSRC_XRI].base[0] = le16_to_cpu(conf->xri_base); + sli4->ext[SLI4_RSRC_XRI].size = le16_to_cpu(conf->xri_count); + + sli4->ext[SLI4_RSRC_FCFI].base[0] = 0; + sli4->ext[SLI4_RSRC_FCFI].size = le16_to_cpu(conf->fcfi_count); + + for (i = 0; i < SLI4_RSRC_MAX; i++) { + total = sli4->ext[i].number * sli4->ext[i].size; + sli4->ext[i].use_map = bitmap_zalloc(total, GFP_KERNEL); + if (!sli4->ext[i].use_map) { + efc_log_err(sli4, "bitmap memory allocation failed %d\n", + i); + return -EIO; + } + sli4->ext[i].map_size = total; + } + + sli4->topology = (le32_to_cpu(conf->topology_dword) & + SLI4_READ_CFG_RESP_TOPOLOGY) >> 24; + switch (sli4->topology) { + case SLI4_READ_CFG_TOPO_FC: + efc_log_info(sli4, "FC (unknown)\n"); + break; + case SLI4_READ_CFG_TOPO_NON_FC_AL: + efc_log_info(sli4, "FC (direct attach)\n"); + break; + case SLI4_READ_CFG_TOPO_FC_AL: + efc_log_info(sli4, "FC (arbitrated loop)\n"); + break; + default: + efc_log_info(sli4, "bad topology %#x\n", sli4->topology); + } + + sli4->e_d_tov = le16_to_cpu(conf->e_d_tov); + sli4->r_a_tov = le16_to_cpu(conf->r_a_tov); + + sli4->link_module_type = le16_to_cpu(conf->lmt); + + sli4->qinfo.max_qcount[SLI4_QTYPE_EQ] = le16_to_cpu(conf->eq_count); + sli4->qinfo.max_qcount[SLI4_QTYPE_CQ] = le16_to_cpu(conf->cq_count); + sli4->qinfo.max_qcount[SLI4_QTYPE_WQ] = le16_to_cpu(conf->wq_count); + sli4->qinfo.max_qcount[SLI4_QTYPE_RQ] = le16_to_cpu(conf->rq_count); + + /* + * READ_CONFIG doesn't give the max number of MQ. Applications + * will typically want 1, but we may need another at some future + * date. Dummy up a "max" MQ count here. + */ + sli4->qinfo.max_qcount[SLI4_QTYPE_MQ] = SLI4_USER_MQ_COUNT; + return 0; +} + +static int +sli_get_sli4_parameters(struct sli4 *sli4) +{ + struct sli4_rsp_cmn_get_sli4_params *parms; + u32 dw_loopback; + u32 dw_eq_pg_cnt; + u32 dw_cq_pg_cnt; + u32 dw_mq_pg_cnt; + u32 dw_wq_pg_cnt; + u32 dw_rq_pg_cnt; + u32 dw_sgl_pg_cnt; + + if (sli_cmd_common_get_sli4_parameters(sli4, sli4->bmbx.virt)) + return -EIO; + + parms = (struct sli4_rsp_cmn_get_sli4_params *) + (((u8 *)sli4->bmbx.virt) + + offsetof(struct sli4_cmd_sli_config, payload.embed)); + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail\n"); + return -EIO; + } + + if (parms->hdr.status) { + efc_log_err(sli4, "COMMON_GET_SLI4_PARAMETERS bad status %#x", + parms->hdr.status); + efc_log_err(sli4, "additional status %#x\n", + parms->hdr.additional_status); + return -EIO; + } + + dw_loopback = le32_to_cpu(parms->dw16_loopback_scope); + dw_eq_pg_cnt = le32_to_cpu(parms->dw6_eq_page_cnt); + dw_cq_pg_cnt = le32_to_cpu(parms->dw8_cq_page_cnt); + dw_mq_pg_cnt = le32_to_cpu(parms->dw10_mq_page_cnt); + dw_wq_pg_cnt = le32_to_cpu(parms->dw12_wq_page_cnt); + dw_rq_pg_cnt = le32_to_cpu(parms->dw14_rq_page_cnt); + + sli4->params.auto_reg = (dw_loopback & SLI4_PARAM_AREG); + sli4->params.auto_xfer_rdy = (dw_loopback & SLI4_PARAM_AGXF); + sli4->params.hdr_template_req = (dw_loopback & SLI4_PARAM_HDRR); + sli4->params.t10_dif_inline_capable = (dw_loopback & SLI4_PARAM_TIMM); + sli4->params.t10_dif_separate_capable = (dw_loopback & SLI4_PARAM_TSMM); + + sli4->params.mq_create_version = GET_Q_CREATE_VERSION(dw_mq_pg_cnt); + sli4->params.cq_create_version = GET_Q_CREATE_VERSION(dw_cq_pg_cnt); + + sli4->rq_min_buf_size = le16_to_cpu(parms->min_rq_buffer_size); + sli4->rq_max_buf_size = le32_to_cpu(parms->max_rq_buffer_size); + + sli4->qinfo.qpage_count[SLI4_QTYPE_EQ] = + (dw_eq_pg_cnt & SLI4_PARAM_EQ_PAGE_CNT_MASK); + sli4->qinfo.qpage_count[SLI4_QTYPE_CQ] = + (dw_cq_pg_cnt & SLI4_PARAM_CQ_PAGE_CNT_MASK); + sli4->qinfo.qpage_count[SLI4_QTYPE_MQ] = + (dw_mq_pg_cnt & SLI4_PARAM_MQ_PAGE_CNT_MASK); + sli4->qinfo.qpage_count[SLI4_QTYPE_WQ] = + (dw_wq_pg_cnt & SLI4_PARAM_WQ_PAGE_CNT_MASK); + sli4->qinfo.qpage_count[SLI4_QTYPE_RQ] = + (dw_rq_pg_cnt & SLI4_PARAM_RQ_PAGE_CNT_MASK); + + /* save count methods and masks for each queue type */ + + sli4->qinfo.count_mask[SLI4_QTYPE_EQ] = + le16_to_cpu(parms->eqe_count_mask); + sli4->qinfo.count_method[SLI4_QTYPE_EQ] = + GET_Q_CNT_METHOD(dw_eq_pg_cnt); + + sli4->qinfo.count_mask[SLI4_QTYPE_CQ] = + le16_to_cpu(parms->cqe_count_mask); + sli4->qinfo.count_method[SLI4_QTYPE_CQ] = + GET_Q_CNT_METHOD(dw_cq_pg_cnt); + + sli4->qinfo.count_mask[SLI4_QTYPE_MQ] = + le16_to_cpu(parms->mqe_count_mask); + sli4->qinfo.count_method[SLI4_QTYPE_MQ] = + GET_Q_CNT_METHOD(dw_mq_pg_cnt); + + sli4->qinfo.count_mask[SLI4_QTYPE_WQ] = + le16_to_cpu(parms->wqe_count_mask); + sli4->qinfo.count_method[SLI4_QTYPE_WQ] = + GET_Q_CNT_METHOD(dw_wq_pg_cnt); + + sli4->qinfo.count_mask[SLI4_QTYPE_RQ] = + le16_to_cpu(parms->rqe_count_mask); + sli4->qinfo.count_method[SLI4_QTYPE_RQ] = + GET_Q_CNT_METHOD(dw_rq_pg_cnt); + + /* now calculate max queue entries */ + sli_calc_max_qentries(sli4); + + dw_sgl_pg_cnt = le32_to_cpu(parms->dw18_sgl_page_cnt); + + /* max # of pages */ + sli4->max_sgl_pages = (dw_sgl_pg_cnt & SLI4_PARAM_SGL_PAGE_CNT_MASK); + + /* bit map of available sizes */ + sli4->sgl_page_sizes = (dw_sgl_pg_cnt & + SLI4_PARAM_SGL_PAGE_SZS_MASK) >> 8; + /* ignore HLM here. Use value from REQUEST_FEATURES */ + sli4->sge_supported_length = le32_to_cpu(parms->sge_supported_length); + sli4->params.sgl_pre_reg_required = (dw_loopback & SLI4_PARAM_SGLR); + /* default to using pre-registered SGL's */ + sli4->params.sgl_pre_registered = true; + + sli4->params.perf_hint = dw_loopback & SLI4_PARAM_PHON; + sli4->params.perf_wq_id_association = (dw_loopback & SLI4_PARAM_PHWQ); + + sli4->rq_batch = (le16_to_cpu(parms->dw15w1_rq_db_window) & + SLI4_PARAM_RQ_DB_WINDOW_MASK) >> 12; + + /* Use the highest available WQE size. */ + if (((dw_wq_pg_cnt & SLI4_PARAM_WQE_SZS_MASK) >> 8) & + SLI4_128BYTE_WQE_SUPPORT) + sli4->wqe_size = SLI4_WQE_EXT_BYTES; + else + sli4->wqe_size = SLI4_WQE_BYTES; + + return 0; +} + +static int +sli_get_ctrl_attributes(struct sli4 *sli4) +{ + struct sli4_rsp_cmn_get_cntl_attributes *attr; + struct sli4_rsp_cmn_get_cntl_addl_attributes *add_attr; + struct efc_dma data; + u32 psize; + + /* + * Issue COMMON_GET_CNTL_ATTRIBUTES to get port_number. Temporarily + * uses VPD DMA buffer as the response won't fit in the embedded + * buffer. + */ + memset(sli4->vpd_data.virt, 0, sli4->vpd_data.size); + if (sli_cmd_common_get_cntl_attributes(sli4, sli4->bmbx.virt, + &sli4->vpd_data)) { + efc_log_err(sli4, "bad COMMON_GET_CNTL_ATTRIBUTES write\n"); + return -EIO; + } + + attr = sli4->vpd_data.virt; + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail\n"); + return -EIO; + } + + if (attr->hdr.status) { + efc_log_err(sli4, "COMMON_GET_CNTL_ATTRIBUTES bad status %#x", + attr->hdr.status); + efc_log_err(sli4, "additional status %#x\n", + attr->hdr.additional_status); + return -EIO; + } + + sli4->port_number = attr->port_num_type_flags & SLI4_CNTL_ATTR_PORTNUM; + + memcpy(sli4->bios_version_string, attr->bios_version_str, + sizeof(sli4->bios_version_string)); + + /* get additional attributes */ + psize = sizeof(struct sli4_rsp_cmn_get_cntl_addl_attributes); + data.size = psize; + data.virt = dma_alloc_coherent(&sli4->pci->dev, data.size, + &data.phys, GFP_KERNEL); + if (!data.virt) { + memset(&data, 0, sizeof(struct efc_dma)); + efc_log_err(sli4, "Failed to allocate memory for GET_CNTL_ADDL_ATTR\n"); + return -EIO; + } + + if (sli_cmd_common_get_cntl_addl_attributes(sli4, sli4->bmbx.virt, + &data)) { + efc_log_err(sli4, "bad GET_CNTL_ADDL_ATTR write\n"); + dma_free_coherent(&sli4->pci->dev, data.size, + data.virt, data.phys); + return -EIO; + } + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "mailbox fail (GET_CNTL_ADDL_ATTR)\n"); + dma_free_coherent(&sli4->pci->dev, data.size, + data.virt, data.phys); + return -EIO; + } + + add_attr = data.virt; + if (add_attr->hdr.status) { + efc_log_err(sli4, "GET_CNTL_ADDL_ATTR bad status %#x\n", + add_attr->hdr.status); + dma_free_coherent(&sli4->pci->dev, data.size, + data.virt, data.phys); + return -EIO; + } + + memcpy(sli4->ipl_name, add_attr->ipl_file_name, sizeof(sli4->ipl_name)); + + efc_log_info(sli4, "IPL:%s\n", (char *)sli4->ipl_name); + + dma_free_coherent(&sli4->pci->dev, data.size, data.virt, + data.phys); + memset(&data, 0, sizeof(struct efc_dma)); + return 0; +} + +static int +sli_get_fw_rev(struct sli4 *sli4) +{ + struct sli4_cmd_read_rev *read_rev = sli4->bmbx.virt; + + if (sli_cmd_read_rev(sli4, sli4->bmbx.virt, &sli4->vpd_data)) + return -EIO; + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail (READ_REV)\n"); + return -EIO; + } + + if (le16_to_cpu(read_rev->hdr.status)) { + efc_log_err(sli4, "READ_REV bad status %#x\n", + le16_to_cpu(read_rev->hdr.status)); + return -EIO; + } + + sli4->fw_rev[0] = le32_to_cpu(read_rev->first_fw_id); + memcpy(sli4->fw_name[0], read_rev->first_fw_name, + sizeof(sli4->fw_name[0])); + + sli4->fw_rev[1] = le32_to_cpu(read_rev->second_fw_id); + memcpy(sli4->fw_name[1], read_rev->second_fw_name, + sizeof(sli4->fw_name[1])); + + sli4->hw_rev[0] = le32_to_cpu(read_rev->first_hw_rev); + sli4->hw_rev[1] = le32_to_cpu(read_rev->second_hw_rev); + sli4->hw_rev[2] = le32_to_cpu(read_rev->third_hw_rev); + + efc_log_info(sli4, "FW1:%s (%08x) / FW2:%s (%08x)\n", + read_rev->first_fw_name, le32_to_cpu(read_rev->first_fw_id), + read_rev->second_fw_name, le32_to_cpu(read_rev->second_fw_id)); + + efc_log_info(sli4, "HW1: %08x / HW2: %08x\n", + le32_to_cpu(read_rev->first_hw_rev), + le32_to_cpu(read_rev->second_hw_rev)); + + /* Check that all VPD data was returned */ + if (le32_to_cpu(read_rev->returned_vpd_length) != + le32_to_cpu(read_rev->actual_vpd_length)) { + efc_log_info(sli4, "VPD length: avail=%d return=%d actual=%d\n", + le32_to_cpu(read_rev->available_length_dword) & + SLI4_READ_REV_AVAILABLE_LENGTH, + le32_to_cpu(read_rev->returned_vpd_length), + le32_to_cpu(read_rev->actual_vpd_length)); + } + sli4->vpd_length = le32_to_cpu(read_rev->returned_vpd_length); + return 0; +} + +static int +sli_get_config(struct sli4 *sli4) +{ + struct sli4_rsp_cmn_get_port_name *port_name; + struct sli4_cmd_read_nvparms *read_nvparms; + + /* + * Read the device configuration + */ + if (sli_get_read_config(sli4)) + return -EIO; + + if (sli_get_sli4_parameters(sli4)) + return -EIO; + + if (sli_get_ctrl_attributes(sli4)) + return -EIO; + + if (sli_cmd_common_get_port_name(sli4, sli4->bmbx.virt)) + return -EIO; + + port_name = (struct sli4_rsp_cmn_get_port_name *) + (((u8 *)sli4->bmbx.virt) + + offsetof(struct sli4_cmd_sli_config, payload.embed)); + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox fail (GET_PORT_NAME)\n"); + return -EIO; + } + + sli4->port_name[0] = port_name->port_name[sli4->port_number]; + sli4->port_name[1] = '\0'; + + if (sli_get_fw_rev(sli4)) + return -EIO; + + if (sli_cmd_read_nvparms(sli4, sli4->bmbx.virt)) { + efc_log_err(sli4, "bad READ_NVPARMS write\n"); + return -EIO; + } + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox fail (READ_NVPARMS)\n"); + return -EIO; + } + + read_nvparms = sli4->bmbx.virt; + if (le16_to_cpu(read_nvparms->hdr.status)) { + efc_log_err(sli4, "READ_NVPARMS bad status %#x\n", + le16_to_cpu(read_nvparms->hdr.status)); + return -EIO; + } + + memcpy(sli4->wwpn, read_nvparms->wwpn, sizeof(sli4->wwpn)); + memcpy(sli4->wwnn, read_nvparms->wwnn, sizeof(sli4->wwnn)); + + efc_log_info(sli4, "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + sli4->wwpn[0], sli4->wwpn[1], sli4->wwpn[2], sli4->wwpn[3], + sli4->wwpn[4], sli4->wwpn[5], sli4->wwpn[6], sli4->wwpn[7]); + efc_log_info(sli4, "WWNN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + sli4->wwnn[0], sli4->wwnn[1], sli4->wwnn[2], sli4->wwnn[3], + sli4->wwnn[4], sli4->wwnn[5], sli4->wwnn[6], sli4->wwnn[7]); + + return 0; +} + +int +sli_setup(struct sli4 *sli4, void *os, struct pci_dev *pdev, + void __iomem *reg[]) +{ + u32 intf = U32_MAX; + u32 pci_class_rev = 0; + u32 rev_id = 0; + u32 family = 0; + u32 asic_id = 0; + u32 i; + struct sli4_asic_entry_t *asic; + + memset(sli4, 0, sizeof(struct sli4)); + + sli4->os = os; + sli4->pci = pdev; + + for (i = 0; i < 6; i++) + sli4->reg[i] = reg[i]; + /* + * Read the SLI_INTF register to discover the register layout + * and other capability information + */ + if (pci_read_config_dword(pdev, SLI4_INTF_REG, &intf)) + return -EIO; + + if ((intf & SLI4_INTF_VALID_MASK) != (u32)SLI4_INTF_VALID_VALUE) { + efc_log_err(sli4, "SLI_INTF is not valid\n"); + return -EIO; + } + + /* driver only support SLI-4 */ + if ((intf & SLI4_INTF_REV_MASK) != SLI4_INTF_REV_S4) { + efc_log_err(sli4, "Unsupported SLI revision (intf=%#x)\n", intf); + return -EIO; + } + + sli4->sli_family = intf & SLI4_INTF_FAMILY_MASK; + + sli4->if_type = intf & SLI4_INTF_IF_TYPE_MASK; + efc_log_info(sli4, "status=%#x error1=%#x error2=%#x\n", + sli_reg_read_status(sli4), + sli_reg_read_err1(sli4), + sli_reg_read_err2(sli4)); + + /* + * set the ASIC type and revision + */ + if (pci_read_config_dword(pdev, PCI_CLASS_REVISION, &pci_class_rev)) + return -EIO; + + rev_id = pci_class_rev & 0xff; + family = sli4->sli_family; + if (family == SLI4_FAMILY_CHECK_ASIC_TYPE) { + if (!pci_read_config_dword(pdev, SLI4_ASIC_ID_REG, &asic_id)) + family = asic_id & SLI4_ASIC_GEN_MASK; + } + + for (i = 0, asic = sli4_asic_table; i < ARRAY_SIZE(sli4_asic_table); + i++, asic++) { + if (rev_id == asic->rev_id && family == asic->family) { + sli4->asic_type = family; + sli4->asic_rev = rev_id; + break; + } + } + /* Fail if no matching asic type/rev was found */ + if (!sli4->asic_type) { + efc_log_err(sli4, "no matching asic family/rev found: %02x/%02x\n", + family, rev_id); + return -EIO; + } + + /* + * The bootstrap mailbox is equivalent to a MQ with a single 256 byte + * entry, a CQ with a single 16 byte entry, and no event queue. + * Alignment must be 16 bytes as the low order address bits in the + * address register are also control / status. + */ + sli4->bmbx.size = SLI4_BMBX_SIZE + sizeof(struct sli4_mcqe); + sli4->bmbx.virt = dma_alloc_coherent(&pdev->dev, sli4->bmbx.size, + &sli4->bmbx.phys, GFP_KERNEL); + if (!sli4->bmbx.virt) { + memset(&sli4->bmbx, 0, sizeof(struct efc_dma)); + efc_log_err(sli4, "bootstrap mailbox allocation failed\n"); + return -EIO; + } + + if (sli4->bmbx.phys & SLI4_BMBX_MASK_LO) { + efc_log_err(sli4, "bad alignment for bootstrap mailbox\n"); + return -EIO; + } + + efc_log_info(sli4, "bmbx v=%p p=0x%x %08x s=%zd\n", sli4->bmbx.virt, + upper_32_bits(sli4->bmbx.phys), + lower_32_bits(sli4->bmbx.phys), sli4->bmbx.size); + + /* 4096 is arbitrary. What should this value actually be? */ + sli4->vpd_data.size = 4096; + sli4->vpd_data.virt = dma_alloc_coherent(&pdev->dev, + sli4->vpd_data.size, + &sli4->vpd_data.phys, + GFP_KERNEL); + if (!sli4->vpd_data.virt) { + memset(&sli4->vpd_data, 0, sizeof(struct efc_dma)); + /* Note that failure isn't fatal in this specific case */ + efc_log_info(sli4, "VPD buffer allocation failed\n"); + } + + if (!sli_fw_init(sli4)) { + efc_log_err(sli4, "FW initialization failed\n"); + return -EIO; + } + + /* + * Set one of fcpi(initiator), fcpt(target), fcpc(combined) to true + * in addition to any other desired features + */ + sli4->features = (SLI4_REQFEAT_IAAB | SLI4_REQFEAT_NPIV | + SLI4_REQFEAT_DIF | SLI4_REQFEAT_VF | + SLI4_REQFEAT_FCPC | SLI4_REQFEAT_IAAR | + SLI4_REQFEAT_HLM | SLI4_REQFEAT_PERFH | + SLI4_REQFEAT_RXSEQ | SLI4_REQFEAT_RXRI | + SLI4_REQFEAT_MRQP); + + /* use performance hints if available */ + if (sli4->params.perf_hint) + sli4->features |= SLI4_REQFEAT_PERFH; + + if (sli_request_features(sli4, &sli4->features, true)) + return -EIO; + + if (sli_get_config(sli4)) + return -EIO; + + return 0; +} + +int +sli_init(struct sli4 *sli4) +{ + if (sli4->params.has_extents) { + efc_log_info(sli4, "extend allocation not supported\n"); + return -EIO; + } + + sli4->features &= (~SLI4_REQFEAT_HLM); + sli4->features &= (~SLI4_REQFEAT_RXSEQ); + sli4->features &= (~SLI4_REQFEAT_RXRI); + + if (sli_request_features(sli4, &sli4->features, false)) + return -EIO; + + return 0; +} + +int +sli_reset(struct sli4 *sli4) +{ + u32 i; + + if (!sli_fw_init(sli4)) { + efc_log_crit(sli4, "FW initialization failed\n"); + return -EIO; + } + + kfree(sli4->ext[0].base); + sli4->ext[0].base = NULL; + + for (i = 0; i < SLI4_RSRC_MAX; i++) { + bitmap_free(sli4->ext[i].use_map); + sli4->ext[i].use_map = NULL; + sli4->ext[i].base = NULL; + } + + return sli_get_config(sli4); +} + +int +sli_fw_reset(struct sli4 *sli4) +{ + /* + * Firmware must be ready before issuing the reset. + */ + if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) { + efc_log_crit(sli4, "FW status is NOT ready\n"); + return -EIO; + } + + /* Lancer uses PHYDEV_CONTROL */ + writel(SLI4_PHYDEV_CTRL_FRST, (sli4->reg[0] + SLI4_PHYDEV_CTRL_REG)); + + /* wait for the FW to become ready after the reset */ + if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) { + efc_log_crit(sli4, "Failed to be ready after firmware reset\n"); + return -EIO; + } + return 0; +} + +void +sli_teardown(struct sli4 *sli4) +{ + u32 i; + + kfree(sli4->ext[0].base); + sli4->ext[0].base = NULL; + + for (i = 0; i < SLI4_RSRC_MAX; i++) { + sli4->ext[i].base = NULL; + + bitmap_free(sli4->ext[i].use_map); + sli4->ext[i].use_map = NULL; + } + + if (!sli_sliport_reset(sli4)) + efc_log_err(sli4, "FW deinitialization failed\n"); + + dma_free_coherent(&sli4->pci->dev, sli4->vpd_data.size, + sli4->vpd_data.virt, sli4->vpd_data.phys); + memset(&sli4->vpd_data, 0, sizeof(struct efc_dma)); + + dma_free_coherent(&sli4->pci->dev, sli4->bmbx.size, + sli4->bmbx.virt, sli4->bmbx.phys); + memset(&sli4->bmbx, 0, sizeof(struct efc_dma)); +} + +int +sli_callback(struct sli4 *sli4, enum sli4_callback which, + void *func, void *arg) +{ + if (!func) { + efc_log_err(sli4, "bad parameter sli4=%p which=%#x func=%p\n", + sli4, which, func); + return -EIO; + } + + switch (which) { + case SLI4_CB_LINK: + sli4->link = func; + sli4->link_arg = arg; + break; + default: + efc_log_info(sli4, "unknown callback %#x\n", which); + return -EIO; + } + + return 0; +} + +int +sli_eq_modify_delay(struct sli4 *sli4, struct sli4_queue *eq, + u32 num_eq, u32 shift, u32 delay_mult) +{ + sli_cmd_common_modify_eq_delay(sli4, sli4->bmbx.virt, eq, num_eq, + shift, delay_mult); + + if (sli_bmbx_command(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail (MODIFY EQ DELAY)\n"); + return -EIO; + } + if (sli_res_sli_config(sli4, sli4->bmbx.virt)) { + efc_log_err(sli4, "bad status MODIFY EQ DELAY\n"); + return -EIO; + } + + return 0; +} + +int +sli_resource_alloc(struct sli4 *sli4, enum sli4_resource rtype, + u32 *rid, u32 *index) +{ + int rc = 0; + u32 size; + u32 ext_idx; + u32 item_idx; + u32 position; + + *rid = U32_MAX; + *index = U32_MAX; + + switch (rtype) { + case SLI4_RSRC_VFI: + case SLI4_RSRC_VPI: + case SLI4_RSRC_RPI: + case SLI4_RSRC_XRI: + position = + find_first_zero_bit(sli4->ext[rtype].use_map, + sli4->ext[rtype].map_size); + if (position >= sli4->ext[rtype].map_size) { + efc_log_err(sli4, "out of resource %d (alloc=%d)\n", + rtype, sli4->ext[rtype].n_alloc); + rc = -EIO; + break; + } + set_bit(position, sli4->ext[rtype].use_map); + *index = position; + + size = sli4->ext[rtype].size; + + ext_idx = *index / size; + item_idx = *index % size; + + *rid = sli4->ext[rtype].base[ext_idx] + item_idx; + + sli4->ext[rtype].n_alloc++; + break; + default: + rc = -EIO; + } + + return rc; +} + +int +sli_resource_free(struct sli4 *sli4, enum sli4_resource rtype, u32 rid) +{ + int rc = -EIO; + u32 x; + u32 size, *base; + + switch (rtype) { + case SLI4_RSRC_VFI: + case SLI4_RSRC_VPI: + case SLI4_RSRC_RPI: + case SLI4_RSRC_XRI: + /* + * Figure out which extent contains the resource ID. I.e. find + * the extent such that + * extent->base <= resource ID < extent->base + extent->size + */ + base = sli4->ext[rtype].base; + size = sli4->ext[rtype].size; + + /* + * In the case of FW reset, this may be cleared + * but the force_free path will still attempt to + * free the resource. Prevent a NULL pointer access. + */ + if (!base) + break; + + for (x = 0; x < sli4->ext[rtype].number; x++) { + if ((rid < base[x] || (rid >= (base[x] + size)))) + continue; + + rid -= base[x]; + clear_bit((x * size) + rid, sli4->ext[rtype].use_map); + rc = 0; + break; + } + break; + default: + break; + } + + return rc; +} + +int +sli_resource_reset(struct sli4 *sli4, enum sli4_resource rtype) +{ + int rc = -EIO; + u32 i; + + switch (rtype) { + case SLI4_RSRC_VFI: + case SLI4_RSRC_VPI: + case SLI4_RSRC_RPI: + case SLI4_RSRC_XRI: + for (i = 0; i < sli4->ext[rtype].map_size; i++) + clear_bit(i, sli4->ext[rtype].use_map); + rc = 0; + break; + default: + break; + } + + return rc; +} + +int sli_raise_ue(struct sli4 *sli4, u8 dump) +{ + u32 val = 0; + + if (dump == SLI4_FUNC_DESC_DUMP) { + val = SLI4_PORT_CTRL_FDD | SLI4_PORT_CTRL_IP; + writel(val, (sli4->reg[0] + SLI4_PORT_CTRL_REG)); + } else { + val = SLI4_PHYDEV_CTRL_FRST; + + if (dump == SLI4_CHIP_LEVEL_DUMP) + val |= SLI4_PHYDEV_CTRL_DD; + writel(val, (sli4->reg[0] + SLI4_PHYDEV_CTRL_REG)); + } + + return 0; +} + +int sli_dump_is_ready(struct sli4 *sli4) +{ + int rc = SLI4_DUMP_READY_STATUS_NOT_READY; + u32 port_val; + u32 bmbx_val; + + /* + * Ensure that the port is ready AND the mailbox is + * ready before signaling that the dump is ready to go. + */ + port_val = sli_reg_read_status(sli4); + bmbx_val = readl(sli4->reg[0] + SLI4_BMBX_REG); + + if ((bmbx_val & SLI4_BMBX_RDY) && + (port_val & SLI4_PORT_STATUS_RDY)) { + if (port_val & SLI4_PORT_STATUS_DIP) + rc = SLI4_DUMP_READY_STATUS_DD_PRESENT; + else if (port_val & SLI4_PORT_STATUS_FDP) + rc = SLI4_DUMP_READY_STATUS_FDB_PRESENT; + } + + return rc; +} + +bool sli_reset_required(struct sli4 *sli4) +{ + u32 val; + + val = sli_reg_read_status(sli4); + return (val & SLI4_PORT_STATUS_RN); +} + +int +sli_cmd_post_sgl_pages(struct sli4 *sli4, void *buf, u16 xri, + u32 xri_count, struct efc_dma *page0[], + struct efc_dma *page1[], struct efc_dma *dma) +{ + struct sli4_rqst_post_sgl_pages *post = NULL; + u32 i; + __le32 req_len; + + post = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(post_sgl_pages), dma); + if (!post) + return -EIO; + + /* payload size calculation */ + /* 4 = xri_start + xri_count */ + /* xri_count = # of XRI's registered */ + /* sizeof(uint64_t) = physical address size */ + /* 2 = # of physical addresses per page set */ + req_len = cpu_to_le32(4 + (xri_count * (sizeof(uint64_t) * 2))); + sli_cmd_fill_hdr(&post->hdr, SLI4_OPC_POST_SGL_PAGES, SLI4_SUBSYSTEM_FC, + CMD_V0, req_len); + post->xri_start = cpu_to_le16(xri); + post->xri_count = cpu_to_le16(xri_count); + + for (i = 0; i < xri_count; i++) { + post->page_set[i].page0_low = + cpu_to_le32(lower_32_bits(page0[i]->phys)); + post->page_set[i].page0_high = + cpu_to_le32(upper_32_bits(page0[i]->phys)); + } + + if (page1) { + for (i = 0; i < xri_count; i++) { + post->page_set[i].page1_low = + cpu_to_le32(lower_32_bits(page1[i]->phys)); + post->page_set[i].page1_high = + cpu_to_le32(upper_32_bits(page1[i]->phys)); + } + } + + return 0; +} + +int +sli_cmd_post_hdr_templates(struct sli4 *sli4, void *buf, struct efc_dma *dma, + u16 rpi, struct efc_dma *payload_dma) +{ + struct sli4_rqst_post_hdr_templates *req = NULL; + uintptr_t phys = 0; + u32 i = 0; + u32 page_count, payload_size; + + page_count = sli_page_count(dma->size, SLI_PAGE_SIZE); + + payload_size = ((sizeof(struct sli4_rqst_post_hdr_templates) + + (page_count * SZ_DMAADDR)) - sizeof(struct sli4_rqst_hdr)); + + if (page_count > 16) { + /* + * We can't fit more than 16 descriptors into an embedded mbox + * command, it has to be non-embedded + */ + payload_dma->size = payload_size; + payload_dma->virt = dma_alloc_coherent(&sli4->pci->dev, + payload_dma->size, + &payload_dma->phys, GFP_KERNEL); + if (!payload_dma->virt) { + memset(payload_dma, 0, sizeof(struct efc_dma)); + efc_log_err(sli4, "mbox payload memory allocation fail\n"); + return -EIO; + } + req = sli_config_cmd_init(sli4, buf, payload_size, payload_dma); + } else { + req = sli_config_cmd_init(sli4, buf, payload_size, NULL); + } + + if (!req) + return -EIO; + + if (rpi == U16_MAX) + rpi = sli4->ext[SLI4_RSRC_RPI].base[0]; + + sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_POST_HDR_TEMPLATES, + SLI4_SUBSYSTEM_FC, CMD_V0, + SLI4_RQST_PYLD_LEN(post_hdr_templates)); + + req->rpi_offset = cpu_to_le16(rpi); + req->page_count = cpu_to_le16(page_count); + phys = dma->phys; + for (i = 0; i < page_count; i++) { + req->page_descriptor[i].low = cpu_to_le32(lower_32_bits(phys)); + req->page_descriptor[i].high = cpu_to_le32(upper_32_bits(phys)); + + phys += SLI_PAGE_SIZE; + } + + return 0; +} + +u32 +sli_fc_get_rpi_requirements(struct sli4 *sli4, u32 n_rpi) +{ + u32 bytes = 0; + + /* Check if header templates needed */ + if (sli4->params.hdr_template_req) + /* round up to a page */ + bytes = round_up(n_rpi * SLI4_HDR_TEMPLATE_SIZE, SLI_PAGE_SIZE); + + return bytes; +} + +const char * +sli_fc_get_status_string(u32 status) +{ + static struct { + u32 code; + const char *label; + } lookup[] = { + {SLI4_FC_WCQE_STATUS_SUCCESS, "SUCCESS"}, + {SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE, "FCP_RSP_FAILURE"}, + {SLI4_FC_WCQE_STATUS_REMOTE_STOP, "REMOTE_STOP"}, + {SLI4_FC_WCQE_STATUS_LOCAL_REJECT, "LOCAL_REJECT"}, + {SLI4_FC_WCQE_STATUS_NPORT_RJT, "NPORT_RJT"}, + {SLI4_FC_WCQE_STATUS_FABRIC_RJT, "FABRIC_RJT"}, + {SLI4_FC_WCQE_STATUS_NPORT_BSY, "NPORT_BSY"}, + {SLI4_FC_WCQE_STATUS_FABRIC_BSY, "FABRIC_BSY"}, + {SLI4_FC_WCQE_STATUS_LS_RJT, "LS_RJT"}, + {SLI4_FC_WCQE_STATUS_CMD_REJECT, "CMD_REJECT"}, + {SLI4_FC_WCQE_STATUS_FCP_TGT_LENCHECK, "FCP_TGT_LENCHECK"}, + {SLI4_FC_WCQE_STATUS_RQ_BUF_LEN_EXCEEDED, "BUF_LEN_EXCEEDED"}, + {SLI4_FC_WCQE_STATUS_RQ_INSUFF_BUF_NEEDED, + "RQ_INSUFF_BUF_NEEDED"}, + {SLI4_FC_WCQE_STATUS_RQ_INSUFF_FRM_DISC, "RQ_INSUFF_FRM_DESC"}, + {SLI4_FC_WCQE_STATUS_RQ_DMA_FAILURE, "RQ_DMA_FAILURE"}, + {SLI4_FC_WCQE_STATUS_FCP_RSP_TRUNCATE, "FCP_RSP_TRUNCATE"}, + {SLI4_FC_WCQE_STATUS_DI_ERROR, "DI_ERROR"}, + {SLI4_FC_WCQE_STATUS_BA_RJT, "BA_RJT"}, + {SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_NEEDED, + "RQ_INSUFF_XRI_NEEDED"}, + {SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_DISC, "INSUFF_XRI_DISC"}, + {SLI4_FC_WCQE_STATUS_RX_ERROR_DETECT, "RX_ERROR_DETECT"}, + {SLI4_FC_WCQE_STATUS_RX_ABORT_REQUEST, "RX_ABORT_REQUEST"}, + }; + u32 i; + + for (i = 0; i < ARRAY_SIZE(lookup); i++) { + if (status == lookup[i].code) + return lookup[i].label; + } + return "unknown"; +} diff --git a/drivers/scsi/elx/libefc_sli/sli4.h b/drivers/scsi/elx/libefc_sli/sli4.h new file mode 100644 index 000000000..38af166cc --- /dev/null +++ b/drivers/scsi/elx/libefc_sli/sli4.h @@ -0,0 +1,4132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + */ + +/* + * All common SLI-4 structures and function prototypes. + */ + +#ifndef _SLI4_H +#define _SLI4_H + +#include <linux/pci.h> +#include <linux/delay.h> +#include "scsi/fc/fc_els.h" +#include "scsi/fc/fc_fs.h" +#include "../include/efc_common.h" + +/************************************************************************* + * Common SLI-4 register offsets and field definitions + */ + +/* SLI_INTF - SLI Interface Definition Register */ +#define SLI4_INTF_REG 0x0058 +enum sli4_intf { + SLI4_INTF_REV_SHIFT = 4, + SLI4_INTF_REV_MASK = 0xf0, + + SLI4_INTF_REV_S3 = 0x30, + SLI4_INTF_REV_S4 = 0x40, + + SLI4_INTF_FAMILY_SHIFT = 8, + SLI4_INTF_FAMILY_MASK = 0x0f00, + + SLI4_FAMILY_CHECK_ASIC_TYPE = 0x0f00, + + SLI4_INTF_IF_TYPE_SHIFT = 12, + SLI4_INTF_IF_TYPE_MASK = 0xf000, + + SLI4_INTF_IF_TYPE_2 = 0x2000, + SLI4_INTF_IF_TYPE_6 = 0x6000, + + SLI4_INTF_VALID_SHIFT = 29, + SLI4_INTF_VALID_MASK = 0xe0000000, + + SLI4_INTF_VALID_VALUE = 0xc0000000, +}; + +/* ASIC_ID - SLI ASIC Type and Revision Register */ +#define SLI4_ASIC_ID_REG 0x009c +enum sli4_asic { + SLI4_ASIC_GEN_SHIFT = 8, + SLI4_ASIC_GEN_MASK = 0xff00, + SLI4_ASIC_GEN_5 = 0x0b00, + SLI4_ASIC_GEN_6 = 0x0c00, + SLI4_ASIC_GEN_7 = 0x0d00, +}; + +enum sli4_acic_revisions { + SLI4_ASIC_REV_A0 = 0x00, + SLI4_ASIC_REV_A1 = 0x01, + SLI4_ASIC_REV_A2 = 0x02, + SLI4_ASIC_REV_A3 = 0x03, + SLI4_ASIC_REV_B0 = 0x10, + SLI4_ASIC_REV_B1 = 0x11, + SLI4_ASIC_REV_B2 = 0x12, + SLI4_ASIC_REV_C0 = 0x20, + SLI4_ASIC_REV_C1 = 0x21, + SLI4_ASIC_REV_C2 = 0x22, + SLI4_ASIC_REV_D0 = 0x30, +}; + +struct sli4_asic_entry_t { + u32 rev_id; + u32 family; +}; + +/* BMBX - Bootstrap Mailbox Register */ +#define SLI4_BMBX_REG 0x0160 +enum sli4_bmbx { + SLI4_BMBX_MASK_HI = 0x3, + SLI4_BMBX_MASK_LO = 0xf, + SLI4_BMBX_RDY = 1 << 0, + SLI4_BMBX_HI = 1 << 1, + SLI4_BMBX_SIZE = 256, +}; + +static inline u32 +sli_bmbx_write_hi(u64 addr) { + u32 val; + + val = upper_32_bits(addr) & ~SLI4_BMBX_MASK_HI; + val |= SLI4_BMBX_HI; + + return val; +} + +static inline u32 +sli_bmbx_write_lo(u64 addr) { + u32 val; + + val = (upper_32_bits(addr) & SLI4_BMBX_MASK_HI) << 30; + val |= ((addr) & ~SLI4_BMBX_MASK_LO) >> 2; + + return val; +} + +/* SLIPORT_CONTROL - SLI Port Control Register */ +#define SLI4_PORT_CTRL_REG 0x0408 +enum sli4_port_ctrl { + SLI4_PORT_CTRL_IP = 1u << 27, + SLI4_PORT_CTRL_IDIS = 1u << 22, + SLI4_PORT_CTRL_FDD = 1u << 31, +}; + +/* SLI4_SLIPORT_ERROR - SLI Port Error Register */ +#define SLI4_PORT_ERROR1 0x040c +#define SLI4_PORT_ERROR2 0x0410 + +/* EQCQ_DOORBELL - EQ and CQ Doorbell Register */ +#define SLI4_EQCQ_DB_REG 0x120 +enum sli4_eqcq_e { + SLI4_EQ_ID_LO_MASK = 0x01ff, + + SLI4_CQ_ID_LO_MASK = 0x03ff, + + SLI4_EQCQ_CI_EQ = 0x0200, + + SLI4_EQCQ_QT_EQ = 0x00000400, + SLI4_EQCQ_QT_CQ = 0x00000000, + + SLI4_EQCQ_ID_HI_SHIFT = 11, + SLI4_EQCQ_ID_HI_MASK = 0xf800, + + SLI4_EQCQ_NUM_SHIFT = 16, + SLI4_EQCQ_NUM_MASK = 0x1fff0000, + + SLI4_EQCQ_ARM = 0x20000000, + SLI4_EQCQ_UNARM = 0x00000000, +}; + +static inline u32 +sli_format_eq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = (id & SLI4_EQ_ID_LO_MASK) | SLI4_EQCQ_QT_EQ; + reg |= (((id) >> 9) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK; + reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK; + reg |= arm | SLI4_EQCQ_CI_EQ; + + return reg; +} + +static inline u32 +sli_format_cq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = ((id) & SLI4_CQ_ID_LO_MASK) | SLI4_EQCQ_QT_CQ; + reg |= (((id) >> 10) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK; + reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* EQ_DOORBELL - EQ Doorbell Register for IF_TYPE = 6*/ +#define SLI4_IF6_EQ_DB_REG 0x120 +enum sli4_eq_e { + SLI4_IF6_EQ_ID_MASK = 0x0fff, + + SLI4_IF6_EQ_NUM_SHIFT = 16, + SLI4_IF6_EQ_NUM_MASK = 0x1fff0000, +}; + +static inline u32 +sli_format_if6_eq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = id & SLI4_IF6_EQ_ID_MASK; + reg |= (num_popped << SLI4_IF6_EQ_NUM_SHIFT) & SLI4_IF6_EQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* CQ_DOORBELL - CQ Doorbell Register for IF_TYPE = 6 */ +#define SLI4_IF6_CQ_DB_REG 0xc0 +enum sli4_cq_e { + SLI4_IF6_CQ_ID_MASK = 0xffff, + + SLI4_IF6_CQ_NUM_SHIFT = 16, + SLI4_IF6_CQ_NUM_MASK = 0x1fff0000, +}; + +static inline u32 +sli_format_if6_cq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = id & SLI4_IF6_CQ_ID_MASK; + reg |= ((num_popped) << SLI4_IF6_CQ_NUM_SHIFT) & SLI4_IF6_CQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* MQ_DOORBELL - MQ Doorbell Register */ +#define SLI4_MQ_DB_REG 0x0140 +#define SLI4_IF6_MQ_DB_REG 0x0160 +enum sli4_mq_e { + SLI4_MQ_ID_MASK = 0xffff, + + SLI4_MQ_NUM_SHIFT = 16, + SLI4_MQ_NUM_MASK = 0x3fff0000, +}; + +static inline u32 +sli_format_mq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_MQ_ID_MASK; + reg |= (1 << SLI4_MQ_NUM_SHIFT) & SLI4_MQ_NUM_MASK; + + return reg; +} + +/* RQ_DOORBELL - RQ Doorbell Register */ +#define SLI4_RQ_DB_REG 0x0a0 +#define SLI4_IF6_RQ_DB_REG 0x0080 +enum sli4_rq_e { + SLI4_RQ_DB_ID_MASK = 0xffff, + + SLI4_RQ_DB_NUM_SHIFT = 16, + SLI4_RQ_DB_NUM_MASK = 0x3fff0000, +}; + +static inline u32 +sli_format_rq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_RQ_DB_ID_MASK; + reg |= (1 << SLI4_RQ_DB_NUM_SHIFT) & SLI4_RQ_DB_NUM_MASK; + + return reg; +} + +/* WQ_DOORBELL - WQ Doorbell Register */ +#define SLI4_IO_WQ_DB_REG 0x040 +#define SLI4_IF6_WQ_DB_REG 0x040 +enum sli4_wq_e { + SLI4_WQ_ID_MASK = 0xffff, + + SLI4_WQ_IDX_SHIFT = 16, + SLI4_WQ_IDX_MASK = 0xff0000, + + SLI4_WQ_NUM_SHIFT = 24, + SLI4_WQ_NUM_MASK = 0x0ff00000, +}; + +static inline u32 +sli_format_wq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_WQ_ID_MASK; + reg |= (1 << SLI4_WQ_NUM_SHIFT) & SLI4_WQ_NUM_MASK; + + return reg; +} + +/* SLIPORT_STATUS - SLI Port Status Register */ +#define SLI4_PORT_STATUS_REGOFF 0x0404 +enum sli4_port_status { + SLI4_PORT_STATUS_FDP = 1u << 21, + SLI4_PORT_STATUS_RDY = 1u << 23, + SLI4_PORT_STATUS_RN = 1u << 24, + SLI4_PORT_STATUS_DIP = 1u << 25, + SLI4_PORT_STATUS_OTI = 1u << 29, + SLI4_PORT_STATUS_ERR = 1u << 31, +}; + +#define SLI4_PHYDEV_CTRL_REG 0x0414 +#define SLI4_PHYDEV_CTRL_FRST (1 << 1) +#define SLI4_PHYDEV_CTRL_DD (1 << 2) + +/* Register name enums */ +enum sli4_regname_en { + SLI4_REG_BMBX, + SLI4_REG_EQ_DOORBELL, + SLI4_REG_CQ_DOORBELL, + SLI4_REG_RQ_DOORBELL, + SLI4_REG_IO_WQ_DOORBELL, + SLI4_REG_MQ_DOORBELL, + SLI4_REG_PHYSDEV_CONTROL, + SLI4_REG_PORT_CONTROL, + SLI4_REG_PORT_ERROR1, + SLI4_REG_PORT_ERROR2, + SLI4_REG_PORT_SEMAPHORE, + SLI4_REG_PORT_STATUS, + SLI4_REG_UNKWOWN /* must be last */ +}; + +struct sli4_reg { + u32 rset; + u32 off; +}; + +struct sli4_dmaaddr { + __le32 low; + __le32 high; +}; + +/* + * a 3-word Buffer Descriptor Entry with + * address 1st 2 words, length last word + */ +struct sli4_bufptr { + struct sli4_dmaaddr addr; + __le32 length; +}; + +/* Buffer Descriptor Entry (BDE) */ +enum sli4_bde_e { + SLI4_BDE_LEN_MASK = 0x00ffffff, + SLI4_BDE_TYPE_MASK = 0xff000000, +}; + +struct sli4_bde { + __le32 bde_type_buflen; + union { + struct sli4_dmaaddr data; + struct { + __le32 offset; + __le32 rsvd2; + } imm; + struct sli4_dmaaddr blp; + } u; +}; + +/* Buffer Descriptors */ +enum sli4_bde_type { + SLI4_BDE_TYPE_SHIFT = 24, + SLI4_BDE_TYPE_64 = 0x00, /* Generic 64-bit data */ + SLI4_BDE_TYPE_IMM = 0x01, /* Immediate data */ + SLI4_BDE_TYPE_BLP = 0x40, /* Buffer List Pointer */ +}; + +#define SLI4_BDE_TYPE_VAL(type) \ + (SLI4_BDE_TYPE_##type << SLI4_BDE_TYPE_SHIFT) + +/* Scatter-Gather Entry (SGE) */ +#define SLI4_SGE_MAX_RESERVED 3 + +enum sli4_sge_type { + /* DW2 */ + SLI4_SGE_DATA_OFFSET_MASK = 0x07ffffff, + /*DW2W1*/ + SLI4_SGE_TYPE_SHIFT = 27, + SLI4_SGE_TYPE_MASK = 0x78000000, + /*SGE Types*/ + SLI4_SGE_TYPE_DATA = 0x00, + SLI4_SGE_TYPE_DIF = 0x04, /* Data Integrity Field */ + SLI4_SGE_TYPE_LSP = 0x05, /* List Segment Pointer */ + SLI4_SGE_TYPE_PEDIF = 0x06, /* Post Encryption Engine DIF */ + SLI4_SGE_TYPE_PESEED = 0x07, /* Post Encryption DIF Seed */ + SLI4_SGE_TYPE_DISEED = 0x08, /* DIF Seed */ + SLI4_SGE_TYPE_ENC = 0x09, /* Encryption */ + SLI4_SGE_TYPE_ATM = 0x0a, /* DIF Application Tag Mask */ + SLI4_SGE_TYPE_SKIP = 0x0c, /* SKIP */ + + SLI4_SGE_LAST = 1u << 31, +}; + +struct sli4_sge { + __le32 buffer_address_high; + __le32 buffer_address_low; + __le32 dw2_flags; + __le32 buffer_length; +}; + +/* T10 DIF Scatter-Gather Entry (SGE) */ +struct sli4_dif_sge { + __le32 buffer_address_high; + __le32 buffer_address_low; + __le32 dw2_flags; + __le32 rsvd12; +}; + +/* Data Integrity Seed (DISEED) SGE */ +enum sli4_diseed_sge_flags { + /* DW2W1 */ + SLI4_DISEED_SGE_HS = 1 << 2, + SLI4_DISEED_SGE_WS = 1 << 3, + SLI4_DISEED_SGE_IC = 1 << 4, + SLI4_DISEED_SGE_ICS = 1 << 5, + SLI4_DISEED_SGE_ATRT = 1 << 6, + SLI4_DISEED_SGE_AT = 1 << 7, + SLI4_DISEED_SGE_FAT = 1 << 8, + SLI4_DISEED_SGE_NA = 1 << 9, + SLI4_DISEED_SGE_HI = 1 << 10, + + /* DW3W1 */ + SLI4_DISEED_SGE_BS_MASK = 0x0007, + SLI4_DISEED_SGE_AI = 1 << 3, + SLI4_DISEED_SGE_ME = 1 << 4, + SLI4_DISEED_SGE_RE = 1 << 5, + SLI4_DISEED_SGE_CE = 1 << 6, + SLI4_DISEED_SGE_NR = 1 << 7, + + SLI4_DISEED_SGE_OP_RX_SHIFT = 8, + SLI4_DISEED_SGE_OP_RX_MASK = 0x0f00, + SLI4_DISEED_SGE_OP_TX_SHIFT = 12, + SLI4_DISEED_SGE_OP_TX_MASK = 0xf000, +}; + +/* Opcode values */ +enum sli4_diseed_sge_opcodes { + SLI4_DISEED_SGE_OP_IN_NODIF_OUT_CRC, + SLI4_DISEED_SGE_OP_IN_CRC_OUT_NODIF, + SLI4_DISEED_SGE_OP_IN_NODIF_OUT_CSUM, + SLI4_DISEED_SGE_OP_IN_CSUM_OUT_NODIF, + SLI4_DISEED_SGE_OP_IN_CRC_OUT_CRC, + SLI4_DISEED_SGE_OP_IN_CSUM_OUT_CSUM, + SLI4_DISEED_SGE_OP_IN_CRC_OUT_CSUM, + SLI4_DISEED_SGE_OP_IN_CSUM_OUT_CRC, + SLI4_DISEED_SGE_OP_IN_RAW_OUT_RAW, +}; + +#define SLI4_DISEED_SGE_OP_RX_VALUE(stype) \ + (SLI4_DISEED_SGE_OP_##stype << SLI4_DISEED_SGE_OP_RX_SHIFT) +#define SLI4_DISEED_SGE_OP_TX_VALUE(stype) \ + (SLI4_DISEED_SGE_OP_##stype << SLI4_DISEED_SGE_OP_TX_SHIFT) + +struct sli4_diseed_sge { + __le32 ref_tag_cmp; + __le32 ref_tag_repl; + __le16 app_tag_repl; + __le16 dw2w1_flags; + __le16 app_tag_cmp; + __le16 dw3w1_flags; +}; + +/* List Segment Pointer Scatter-Gather Entry (SGE) */ +#define SLI4_LSP_SGE_SEGLEN 0x00ffffff + +struct sli4_lsp_sge { + __le32 buffer_address_high; + __le32 buffer_address_low; + __le32 dw2_flags; + __le32 dw3_seglen; +}; + +enum sli4_eqe_e { + SLI4_EQE_VALID = 1, + SLI4_EQE_MJCODE = 0xe, + SLI4_EQE_MNCODE = 0xfff0, +}; + +struct sli4_eqe { + __le16 dw0w0_flags; + __le16 resource_id; +}; + +#define SLI4_MAJOR_CODE_STANDARD 0 +#define SLI4_MAJOR_CODE_SENTINEL 1 + +/* Sentinel EQE indicating the EQ is full */ +#define SLI4_EQE_STATUS_EQ_FULL 2 + +enum sli4_mcqe_e { + SLI4_MCQE_CONSUMED = 1u << 27, + SLI4_MCQE_COMPLETED = 1u << 28, + SLI4_MCQE_AE = 1u << 30, + SLI4_MCQE_VALID = 1u << 31, +}; + +/* Entry was consumed but not completed */ +#define SLI4_MCQE_STATUS_NOT_COMPLETED -2 + +struct sli4_mcqe { + __le16 completion_status; + __le16 extended_status; + __le32 mqe_tag_low; + __le32 mqe_tag_high; + __le32 dw3_flags; +}; + +enum sli4_acqe_e { + SLI4_ACQE_AE = 1 << 6, /* async event - this is an ACQE */ + SLI4_ACQE_VAL = 1 << 7, /* valid - contents of CQE are valid */ +}; + +struct sli4_acqe { + __le32 event_data[3]; + u8 rsvd12; + u8 event_code; + u8 event_type; + u8 ae_val; +}; + +enum sli4_acqe_event_code { + SLI4_ACQE_EVENT_CODE_LINK_STATE = 0x01, + SLI4_ACQE_EVENT_CODE_FIP = 0x02, + SLI4_ACQE_EVENT_CODE_DCBX = 0x03, + SLI4_ACQE_EVENT_CODE_ISCSI = 0x04, + SLI4_ACQE_EVENT_CODE_GRP_5 = 0x05, + SLI4_ACQE_EVENT_CODE_FC_LINK_EVENT = 0x10, + SLI4_ACQE_EVENT_CODE_SLI_PORT_EVENT = 0x11, + SLI4_ACQE_EVENT_CODE_VF_EVENT = 0x12, + SLI4_ACQE_EVENT_CODE_MR_EVENT = 0x13, +}; + +enum sli4_qtype { + SLI4_QTYPE_EQ, + SLI4_QTYPE_CQ, + SLI4_QTYPE_MQ, + SLI4_QTYPE_WQ, + SLI4_QTYPE_RQ, + SLI4_QTYPE_MAX, /* must be last */ +}; + +#define SLI4_USER_MQ_COUNT 1 +#define SLI4_MAX_CQ_SET_COUNT 16 +#define SLI4_MAX_RQ_SET_COUNT 16 + +enum sli4_qentry { + SLI4_QENTRY_ASYNC, + SLI4_QENTRY_MQ, + SLI4_QENTRY_RQ, + SLI4_QENTRY_WQ, + SLI4_QENTRY_WQ_RELEASE, + SLI4_QENTRY_OPT_WRITE_CMD, + SLI4_QENTRY_OPT_WRITE_DATA, + SLI4_QENTRY_XABT, + SLI4_QENTRY_MAX /* must be last */ +}; + +enum sli4_queue_flags { + SLI4_QUEUE_FLAG_MQ = 1 << 0, /* CQ has MQ/Async completion */ + SLI4_QUEUE_FLAG_HDR = 1 << 1, /* RQ for packet headers */ + SLI4_QUEUE_FLAG_RQBATCH = 1 << 2, /* RQ index increment by 8 */ +}; + +/* Generic Command Request header */ +enum sli4_cmd_version { + CMD_V0, + CMD_V1, + CMD_V2, +}; + +struct sli4_rqst_hdr { + u8 opcode; + u8 subsystem; + __le16 rsvd2; + __le32 timeout; + __le32 request_length; + __le32 dw3_version; +}; + +/* Generic Command Response header */ +struct sli4_rsp_hdr { + u8 opcode; + u8 subsystem; + __le16 rsvd2; + u8 status; + u8 additional_status; + __le16 rsvd6; + __le32 response_length; + __le32 actual_response_length; +}; + +#define SLI4_QUEUE_RQ_BATCH 8 + +#define SZ_DMAADDR sizeof(struct sli4_dmaaddr) +#define SLI4_RQST_CMDSZ(stype) sizeof(struct sli4_rqst_##stype) + +#define SLI4_RQST_PYLD_LEN(stype) \ + cpu_to_le32(sizeof(struct sli4_rqst_##stype) - \ + sizeof(struct sli4_rqst_hdr)) + +#define SLI4_RQST_PYLD_LEN_VAR(stype, varpyld) \ + cpu_to_le32((sizeof(struct sli4_rqst_##stype) + \ + varpyld) - sizeof(struct sli4_rqst_hdr)) + +#define SLI4_CFG_PYLD_LENGTH(stype) \ + max(sizeof(struct sli4_rqst_##stype), \ + sizeof(struct sli4_rsp_##stype)) + +enum sli4_create_cqv2_e { + /* DW5_flags values*/ + SLI4_CREATE_CQV2_CLSWM_MASK = 0x00003000, + SLI4_CREATE_CQV2_NODELAY = 0x00004000, + SLI4_CREATE_CQV2_AUTOVALID = 0x00008000, + SLI4_CREATE_CQV2_CQECNT_MASK = 0x18000000, + SLI4_CREATE_CQV2_VALID = 0x20000000, + SLI4_CREATE_CQV2_EVT = 0x80000000, + /* DW6W1_flags values*/ + SLI4_CREATE_CQV2_ARM = 0x8000, +}; + +struct sli4_rqst_cmn_create_cq_v2 { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + u8 page_size; + u8 rsvd19; + __le32 dw5_flags; + __le16 eq_id; + __le16 dw6w1_arm; + __le16 cqe_count; + __le16 rsvd30; + __le32 rsvd32; + struct sli4_dmaaddr page_phys_addr[]; +}; + +enum sli4_create_cqset_e { + /* DW5_flags values*/ + SLI4_CREATE_CQSETV0_CLSWM_MASK = 0x00003000, + SLI4_CREATE_CQSETV0_NODELAY = 0x00004000, + SLI4_CREATE_CQSETV0_AUTOVALID = 0x00008000, + SLI4_CREATE_CQSETV0_CQECNT_MASK = 0x18000000, + SLI4_CREATE_CQSETV0_VALID = 0x20000000, + SLI4_CREATE_CQSETV0_EVT = 0x80000000, + /* DW5W1_flags values */ + SLI4_CREATE_CQSETV0_CQE_COUNT = 0x7fff, + SLI4_CREATE_CQSETV0_ARM = 0x8000, +}; + +struct sli4_rqst_cmn_create_cq_set_v0 { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + u8 page_size; + u8 rsvd19; + __le32 dw5_flags; + __le16 num_cq_req; + __le16 dw6w1_flags; + __le16 eq_id[16]; + struct sli4_dmaaddr page_phys_addr[]; +}; + +/* CQE count */ +enum sli4_cq_cnt { + SLI4_CQ_CNT_256, + SLI4_CQ_CNT_512, + SLI4_CQ_CNT_1024, + SLI4_CQ_CNT_LARGE, +}; + +#define SLI4_CQ_CNT_SHIFT 27 +#define SLI4_CQ_CNT_VAL(type) (SLI4_CQ_CNT_##type << SLI4_CQ_CNT_SHIFT) + +#define SLI4_CQE_BYTES (4 * sizeof(u32)) + +#define SLI4_CREATE_CQV2_MAX_PAGES 8 + +/* Generic Common Create EQ/CQ/MQ/WQ/RQ Queue completion */ +struct sli4_rsp_cmn_create_queue { + struct sli4_rsp_hdr hdr; + __le16 q_id; + u8 rsvd18; + u8 ulp; + __le32 db_offset; + __le16 db_rs; + __le16 db_fmt; +}; + +struct sli4_rsp_cmn_create_queue_set { + struct sli4_rsp_hdr hdr; + __le16 q_id; + __le16 num_q_allocated; +}; + +/* Common Destroy Queue */ +struct sli4_rqst_cmn_destroy_q { + struct sli4_rqst_hdr hdr; + __le16 q_id; + __le16 rsvd; +}; + +struct sli4_rsp_cmn_destroy_q { + struct sli4_rsp_hdr hdr; +}; + +/* Modify the delay multiplier for EQs */ +struct sli4_eqdelay_rec { + __le32 eq_id; + __le32 phase; + __le32 delay_multiplier; +}; + +struct sli4_rqst_cmn_modify_eq_delay { + struct sli4_rqst_hdr hdr; + __le32 num_eq; + struct sli4_eqdelay_rec eq_delay_record[8]; +}; + +struct sli4_rsp_cmn_modify_eq_delay { + struct sli4_rsp_hdr hdr; +}; + +enum sli4_create_cq_e { + /* DW5 */ + SLI4_CREATE_EQ_AUTOVALID = 1u << 28, + SLI4_CREATE_EQ_VALID = 1u << 29, + SLI4_CREATE_EQ_EQESZ = 1u << 31, + /* DW6 */ + SLI4_CREATE_EQ_COUNT = 7 << 26, + SLI4_CREATE_EQ_ARM = 1u << 31, + /* DW7 */ + SLI4_CREATE_EQ_DELAYMULTI_SHIFT = 13, + SLI4_CREATE_EQ_DELAYMULTI_MASK = 0x007fe000, + SLI4_CREATE_EQ_DELAYMULTI = 0x00040000, +}; + +struct sli4_rqst_cmn_create_eq { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + __le16 rsvd18; + __le32 dw5_flags; + __le32 dw6_flags; + __le32 dw7_delaymulti; + __le32 rsvd32; + struct sli4_dmaaddr page_address[8]; +}; + +struct sli4_rsp_cmn_create_eq { + struct sli4_rsp_cmn_create_queue q_rsp; +}; + +/* EQ count */ +enum sli4_eq_cnt { + SLI4_EQ_CNT_256, + SLI4_EQ_CNT_512, + SLI4_EQ_CNT_1024, + SLI4_EQ_CNT_2048, + SLI4_EQ_CNT_4096 = 3, +}; + +#define SLI4_EQ_CNT_SHIFT 26 +#define SLI4_EQ_CNT_VAL(type) (SLI4_EQ_CNT_##type << SLI4_EQ_CNT_SHIFT) + +#define SLI4_EQE_SIZE_4 0 +#define SLI4_EQE_SIZE_16 1 + +/* Create a Mailbox Queue; accommodate v0 and v1 forms. */ +enum sli4_create_mq_flags { + /* DW6W1 */ + SLI4_CREATE_MQEXT_RINGSIZE = 0xf, + SLI4_CREATE_MQEXT_CQID_SHIFT = 6, + SLI4_CREATE_MQEXT_CQIDV0_MASK = 0xffc0, + /* DW7 */ + SLI4_CREATE_MQEXT_VAL = 1u << 31, + /* DW8 */ + SLI4_CREATE_MQEXT_ACQV = 1u << 0, + SLI4_CREATE_MQEXT_ASYNC_CQIDV0 = 0x7fe, +}; + +struct sli4_rqst_cmn_create_mq_ext { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + __le16 cq_id_v1; + __le32 async_event_bitmap; + __le16 async_cq_id_v1; + __le16 dw6w1_flags; + __le32 dw7_val; + __le32 dw8_flags; + __le32 rsvd36; + struct sli4_dmaaddr page_phys_addr[]; +}; + +struct sli4_rsp_cmn_create_mq_ext { + struct sli4_rsp_cmn_create_queue q_rsp; +}; + +enum sli4_mqe_size { + SLI4_MQE_SIZE_16 = 0x05, + SLI4_MQE_SIZE_32, + SLI4_MQE_SIZE_64, + SLI4_MQE_SIZE_128, +}; + +enum sli4_async_evt { + SLI4_ASYNC_EVT_LINK_STATE = 1 << 1, + SLI4_ASYNC_EVT_FIP = 1 << 2, + SLI4_ASYNC_EVT_GRP5 = 1 << 5, + SLI4_ASYNC_EVT_FC = 1 << 16, + SLI4_ASYNC_EVT_SLI_PORT = 1 << 17, +}; + +#define SLI4_ASYNC_EVT_FC_ALL \ + (SLI4_ASYNC_EVT_LINK_STATE | \ + SLI4_ASYNC_EVT_FIP | \ + SLI4_ASYNC_EVT_GRP5 | \ + SLI4_ASYNC_EVT_FC | \ + SLI4_ASYNC_EVT_SLI_PORT) + +/* Create a Completion Queue. */ +struct sli4_rqst_cmn_create_cq_v0 { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + __le16 rsvd18; + __le32 dw5_flags; + __le32 dw6_flags; + __le32 rsvd28; + __le32 rsvd32; + struct sli4_dmaaddr page_phys_addr[]; +}; + +enum sli4_create_rq_e { + SLI4_RQ_CREATE_DUA = 0x1, + SLI4_RQ_CREATE_BQU = 0x2, + + SLI4_RQE_SIZE = 8, + SLI4_RQE_SIZE_8 = 0x2, + SLI4_RQE_SIZE_16 = 0x3, + SLI4_RQE_SIZE_32 = 0x4, + SLI4_RQE_SIZE_64 = 0x5, + SLI4_RQE_SIZE_128 = 0x6, + + SLI4_RQ_PAGE_SIZE_4096 = 0x1, + SLI4_RQ_PAGE_SIZE_8192 = 0x2, + SLI4_RQ_PAGE_SIZE_16384 = 0x4, + SLI4_RQ_PAGE_SIZE_32768 = 0x8, + SLI4_RQ_PAGE_SIZE_64536 = 0x10, + + SLI4_RQ_CREATE_V0_MAX_PAGES = 8, + SLI4_RQ_CREATE_V0_MIN_BUF_SIZE = 128, + SLI4_RQ_CREATE_V0_MAX_BUF_SIZE = 2048, +}; + +struct sli4_rqst_rq_create { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + u8 dua_bqu_byte; + u8 ulp; + __le16 rsvd16; + u8 rqe_count_byte; + u8 rsvd19; + __le32 rsvd20; + __le16 buffer_size; + __le16 cq_id; + __le32 rsvd28; + struct sli4_dmaaddr page_phys_addr[SLI4_RQ_CREATE_V0_MAX_PAGES]; +}; + +struct sli4_rsp_rq_create { + struct sli4_rsp_cmn_create_queue rsp; +}; + +enum sli4_create_rqv1_e { + SLI4_RQ_CREATE_V1_DNB = 0x80, + SLI4_RQ_CREATE_V1_MAX_PAGES = 8, + SLI4_RQ_CREATE_V1_MIN_BUF_SIZE = 64, + SLI4_RQ_CREATE_V1_MAX_BUF_SIZE = 2048, +}; + +struct sli4_rqst_rq_create_v1 { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + u8 rsvd14; + u8 dim_dfd_dnb; + u8 page_size; + u8 rqe_size_byte; + __le16 rqe_count; + __le32 rsvd20; + __le16 rsvd24; + __le16 cq_id; + __le32 buffer_size; + struct sli4_dmaaddr page_phys_addr[SLI4_RQ_CREATE_V1_MAX_PAGES]; +}; + +struct sli4_rsp_rq_create_v1 { + struct sli4_rsp_cmn_create_queue rsp; +}; + +#define SLI4_RQCREATEV2_DNB 0x80 + +struct sli4_rqst_rq_create_v2 { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + u8 rq_count; + u8 dim_dfd_dnb; + u8 page_size; + u8 rqe_size_byte; + __le16 rqe_count; + __le16 hdr_buffer_size; + __le16 payload_buffer_size; + __le16 base_cq_id; + __le16 rsvd26; + __le32 rsvd42; + struct sli4_dmaaddr page_phys_addr[]; +}; + +struct sli4_rsp_rq_create_v2 { + struct sli4_rsp_cmn_create_queue rsp; +}; + +#define SLI4_CQE_CODE_OFFSET 14 + +enum sli4_cqe_code { + SLI4_CQE_CODE_WORK_REQUEST_COMPLETION = 0x01, + SLI4_CQE_CODE_RELEASE_WQE, + SLI4_CQE_CODE_RSVD, + SLI4_CQE_CODE_RQ_ASYNC, + SLI4_CQE_CODE_XRI_ABORTED, + SLI4_CQE_CODE_RQ_COALESCING, + SLI4_CQE_CODE_RQ_CONSUMPTION, + SLI4_CQE_CODE_MEASUREMENT_REPORTING, + SLI4_CQE_CODE_RQ_ASYNC_V1, + SLI4_CQE_CODE_RQ_COALESCING_V1, + SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD, + SLI4_CQE_CODE_OPTIMIZED_WRITE_DATA, +}; + +#define SLI4_WQ_CREATE_MAX_PAGES 8 + +struct sli4_rqst_wq_create { + struct sli4_rqst_hdr hdr; + __le16 num_pages; + __le16 cq_id; + u8 page_size; + u8 wqe_size_byte; + __le16 wqe_count; + __le32 rsvd; + struct sli4_dmaaddr page_phys_addr[SLI4_WQ_CREATE_MAX_PAGES]; +}; + +struct sli4_rsp_wq_create { + struct sli4_rsp_cmn_create_queue rsp; +}; + +enum sli4_link_attention_flags { + SLI4_LNK_ATTN_TYPE_LINK_UP = 0x01, + SLI4_LNK_ATTN_TYPE_LINK_DOWN = 0x02, + SLI4_LNK_ATTN_TYPE_NO_HARD_ALPA = 0x03, + + SLI4_LNK_ATTN_P2P = 0x01, + SLI4_LNK_ATTN_FC_AL = 0x02, + SLI4_LNK_ATTN_INTERNAL_LOOPBACK = 0x03, + SLI4_LNK_ATTN_SERDES_LOOPBACK = 0x04, +}; + +struct sli4_link_attention { + u8 link_number; + u8 attn_type; + u8 topology; + u8 port_speed; + u8 port_fault; + u8 shared_link_status; + __le16 logical_link_speed; + __le32 event_tag; + u8 rsvd12; + u8 event_code; + u8 event_type; + u8 flags; +}; + +enum sli4_link_event_type { + SLI4_EVENT_LINK_ATTENTION = 0x01, + SLI4_EVENT_SHARED_LINK_ATTENTION = 0x02, +}; + +enum sli4_wcqe_flags { + SLI4_WCQE_XB = 0x10, + SLI4_WCQE_QX = 0x80, +}; + +struct sli4_fc_wcqe { + u8 hw_status; + u8 status; + __le16 request_tag; + __le32 wqe_specific_1; + __le32 wqe_specific_2; + u8 rsvd12; + u8 qx_byte; + u8 code; + u8 flags; +}; + +/* FC WQ consumed CQ queue entry */ +struct sli4_fc_wqec { + __le32 rsvd0; + __le32 rsvd1; + __le16 wqe_index; + __le16 wq_id; + __le16 rsvd12; + u8 code; + u8 vld_byte; +}; + +/* FC Completion Status Codes. */ +enum sli4_wcqe_status { + SLI4_FC_WCQE_STATUS_SUCCESS, + SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE, + SLI4_FC_WCQE_STATUS_REMOTE_STOP, + SLI4_FC_WCQE_STATUS_LOCAL_REJECT, + SLI4_FC_WCQE_STATUS_NPORT_RJT, + SLI4_FC_WCQE_STATUS_FABRIC_RJT, + SLI4_FC_WCQE_STATUS_NPORT_BSY, + SLI4_FC_WCQE_STATUS_FABRIC_BSY, + SLI4_FC_WCQE_STATUS_RSVD, + SLI4_FC_WCQE_STATUS_LS_RJT, + SLI4_FC_WCQE_STATUS_RX_BUF_OVERRUN, + SLI4_FC_WCQE_STATUS_CMD_REJECT, + SLI4_FC_WCQE_STATUS_FCP_TGT_LENCHECK, + SLI4_FC_WCQE_STATUS_RSVD1, + SLI4_FC_WCQE_STATUS_ELS_CMPLT_NO_AUTOREG, + SLI4_FC_WCQE_STATUS_RSVD2, + SLI4_FC_WCQE_STATUS_RQ_SUCCESS, + SLI4_FC_WCQE_STATUS_RQ_BUF_LEN_EXCEEDED, + SLI4_FC_WCQE_STATUS_RQ_INSUFF_BUF_NEEDED, + SLI4_FC_WCQE_STATUS_RQ_INSUFF_FRM_DISC, + SLI4_FC_WCQE_STATUS_RQ_DMA_FAILURE, + SLI4_FC_WCQE_STATUS_FCP_RSP_TRUNCATE, + SLI4_FC_WCQE_STATUS_DI_ERROR, + SLI4_FC_WCQE_STATUS_BA_RJT, + SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_NEEDED, + SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_DISC, + SLI4_FC_WCQE_STATUS_RX_ERROR_DETECT, + SLI4_FC_WCQE_STATUS_RX_ABORT_REQUEST, + + /* driver generated status codes */ + SLI4_FC_WCQE_STATUS_DISPATCH_ERROR = 0xfd, + SLI4_FC_WCQE_STATUS_SHUTDOWN = 0xfe, + SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT = 0xff, +}; + +/* DI_ERROR Extended Status */ +enum sli4_fc_di_error_status { + SLI4_FC_DI_ERROR_GE = 1 << 0, + SLI4_FC_DI_ERROR_AE = 1 << 1, + SLI4_FC_DI_ERROR_RE = 1 << 2, + SLI4_FC_DI_ERROR_TDPV = 1 << 3, + SLI4_FC_DI_ERROR_UDB = 1 << 4, + SLI4_FC_DI_ERROR_EDIR = 1 << 5, +}; + +/* WQE DIF field contents */ +enum sli4_dif_fields { + SLI4_DIF_DISABLED, + SLI4_DIF_PASS_THROUGH, + SLI4_DIF_STRIP, + SLI4_DIF_INSERT, +}; + +/* Work Queue Entry (WQE) types */ +enum sli4_wqe_types { + SLI4_WQE_ABORT = 0x0f, + SLI4_WQE_ELS_REQUEST64 = 0x8a, + SLI4_WQE_FCP_IBIDIR64 = 0xac, + SLI4_WQE_FCP_IREAD64 = 0x9a, + SLI4_WQE_FCP_IWRITE64 = 0x98, + SLI4_WQE_FCP_ICMND64 = 0x9c, + SLI4_WQE_FCP_TRECEIVE64 = 0xa1, + SLI4_WQE_FCP_CONT_TRECEIVE64 = 0xe5, + SLI4_WQE_FCP_TRSP64 = 0xa3, + SLI4_WQE_FCP_TSEND64 = 0x9f, + SLI4_WQE_GEN_REQUEST64 = 0xc2, + SLI4_WQE_SEND_FRAME = 0xe1, + SLI4_WQE_XMIT_BCAST64 = 0x84, + SLI4_WQE_XMIT_BLS_RSP = 0x97, + SLI4_WQE_ELS_RSP64 = 0x95, + SLI4_WQE_XMIT_SEQUENCE64 = 0x82, + SLI4_WQE_REQUEUE_XRI = 0x93, +}; + +/* WQE command types */ +enum sli4_wqe_cmds { + SLI4_CMD_FCP_IREAD64_WQE = 0x00, + SLI4_CMD_FCP_ICMND64_WQE = 0x00, + SLI4_CMD_FCP_IWRITE64_WQE = 0x01, + SLI4_CMD_FCP_TRECEIVE64_WQE = 0x02, + SLI4_CMD_FCP_TRSP64_WQE = 0x03, + SLI4_CMD_FCP_TSEND64_WQE = 0x07, + SLI4_CMD_GEN_REQUEST64_WQE = 0x08, + SLI4_CMD_XMIT_BCAST64_WQE = 0x08, + SLI4_CMD_XMIT_BLS_RSP64_WQE = 0x08, + SLI4_CMD_ABORT_WQE = 0x08, + SLI4_CMD_XMIT_SEQUENCE64_WQE = 0x08, + SLI4_CMD_REQUEUE_XRI_WQE = 0x0a, + SLI4_CMD_SEND_FRAME_WQE = 0x0a, +}; + +#define SLI4_WQE_SIZE 0x05 +#define SLI4_WQE_EXT_SIZE 0x06 + +#define SLI4_WQE_BYTES (16 * sizeof(u32)) +#define SLI4_WQE_EXT_BYTES (32 * sizeof(u32)) + +/* Mask for ccp (CS_CTL) */ +#define SLI4_MASK_CCP 0xfe + +/* Generic WQE */ +enum sli4_gen_wqe_flags { + SLI4_GEN_WQE_EBDECNT = 0xf, + SLI4_GEN_WQE_LEN_LOC = 0x3 << 7, + SLI4_GEN_WQE_QOSD = 1 << 9, + SLI4_GEN_WQE_XBL = 1 << 11, + SLI4_GEN_WQE_HLM = 1 << 12, + SLI4_GEN_WQE_IOD = 1 << 13, + SLI4_GEN_WQE_DBDE = 1 << 14, + SLI4_GEN_WQE_WQES = 1 << 15, + + SLI4_GEN_WQE_PRI = 0x7, + SLI4_GEN_WQE_PV = 1 << 3, + SLI4_GEN_WQE_EAT = 1 << 4, + SLI4_GEN_WQE_XC = 1 << 5, + SLI4_GEN_WQE_CCPE = 1 << 7, + + SLI4_GEN_WQE_CMDTYPE = 0xf, + SLI4_GEN_WQE_WQEC = 1 << 7, +}; + +struct sli4_generic_wqe { + __le32 cmd_spec0_5[6]; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 rsvd34; + __le16 dw10w0_flags; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmdtype_wqec_byte; + u8 rsvd41; + __le16 cq_id; +}; + +/* WQE used to abort exchanges. */ +enum sli4_abort_wqe_flags { + SLI4_ABRT_WQE_IR = 0x02, + + SLI4_ABRT_WQE_EBDECNT = 0xf, + SLI4_ABRT_WQE_LEN_LOC = 0x3 << 7, + SLI4_ABRT_WQE_QOSD = 1 << 9, + SLI4_ABRT_WQE_XBL = 1 << 11, + SLI4_ABRT_WQE_IOD = 1 << 13, + SLI4_ABRT_WQE_DBDE = 1 << 14, + SLI4_ABRT_WQE_WQES = 1 << 15, + + SLI4_ABRT_WQE_PRI = 0x7, + SLI4_ABRT_WQE_PV = 1 << 3, + SLI4_ABRT_WQE_EAT = 1 << 4, + SLI4_ABRT_WQE_XC = 1 << 5, + SLI4_ABRT_WQE_CCPE = 1 << 7, + + SLI4_ABRT_WQE_CMDTYPE = 0xf, + SLI4_ABRT_WQE_WQEC = 1 << 7, +}; + +struct sli4_abort_wqe { + __le32 rsvd0; + __le32 rsvd4; + __le32 ext_t_tag; + u8 ia_ir_byte; + u8 criteria; + __le16 rsvd10; + __le32 ext_t_mask; + __le32 t_mask; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 t_tag; + __le16 request_tag; + __le16 rsvd34; + __le16 dw10w0_flags; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmdtype_wqec_byte; + u8 rsvd41; + __le16 cq_id; +}; + +enum sli4_abort_criteria { + SLI4_ABORT_CRITERIA_XRI_TAG = 0x01, + SLI4_ABORT_CRITERIA_ABORT_TAG, + SLI4_ABORT_CRITERIA_REQUEST_TAG, + SLI4_ABORT_CRITERIA_EXT_ABORT_TAG, +}; + +enum sli4_abort_type { + SLI4_ABORT_XRI, + SLI4_ABORT_ABORT_ID, + SLI4_ABORT_REQUEST_ID, + SLI4_ABORT_MAX, /* must be last */ +}; + +/* WQE used to create an ELS request. */ +enum sli4_els_req_wqe_flags { + SLI4_REQ_WQE_QOSD = 0x2, + SLI4_REQ_WQE_DBDE = 0x40, + SLI4_REQ_WQE_XBL = 0x8, + SLI4_REQ_WQE_XC = 0x20, + SLI4_REQ_WQE_IOD = 0x20, + SLI4_REQ_WQE_HLM = 0x10, + SLI4_REQ_WQE_CCPE = 0x80, + SLI4_REQ_WQE_EAT = 0x10, + SLI4_REQ_WQE_WQES = 0x80, + SLI4_REQ_WQE_PU_SHFT = 4, + SLI4_REQ_WQE_CT_SHFT = 2, + SLI4_REQ_WQE_CT = 0xc, + SLI4_REQ_WQE_ELSID_SHFT = 4, + SLI4_REQ_WQE_SP_SHFT = 24, + SLI4_REQ_WQE_LEN_LOC_BIT1 = 0x80, + SLI4_REQ_WQE_LEN_LOC_BIT2 = 0x1, +}; + +struct sli4_els_request64_wqe { + struct sli4_bde els_request_payload; + __le32 els_request_payload_length; + __le32 sid_sp_dword; + __le32 remote_id_dword; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 temporary_rpi; + u8 len_loc1_byte; + u8 qosd_xbl_hlm_iod_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmdtype_elsid_byte; + u8 rsvd41; + __le16 cq_id; + struct sli4_bde els_response_payload_bde; + __le32 max_response_payload_length; +}; + +/* WQE used to create an FCP initiator no data command. */ +enum sli4_icmd_wqe_flags { + SLI4_ICMD_WQE_DBDE = 0x40, + SLI4_ICMD_WQE_XBL = 0x8, + SLI4_ICMD_WQE_XC = 0x20, + SLI4_ICMD_WQE_IOD = 0x20, + SLI4_ICMD_WQE_HLM = 0x10, + SLI4_ICMD_WQE_CCPE = 0x80, + SLI4_ICMD_WQE_EAT = 0x10, + SLI4_ICMD_WQE_APPID = 0x10, + SLI4_ICMD_WQE_WQES = 0x80, + SLI4_ICMD_WQE_PU_SHFT = 4, + SLI4_ICMD_WQE_CT_SHFT = 2, + SLI4_ICMD_WQE_BS_SHFT = 4, + SLI4_ICMD_WQE_LEN_LOC_BIT1 = 0x80, + SLI4_ICMD_WQE_LEN_LOC_BIT2 = 0x1, +}; + +struct sli4_fcp_icmnd64_wqe { + struct sli4_bde bde; + __le16 payload_offset_length; + __le16 fcp_cmd_buffer_length; + __le32 rsvd12; + __le32 remote_n_port_id_dword; + __le16 xri_tag; + __le16 context_tag; + u8 dif_ct_bs_byte; + u8 command; + u8 class_pu_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 rsvd34; + u8 len_loc1_byte; + u8 qosd_xbl_hlm_iod_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 rsvd44; + __le32 rsvd48; + __le32 rsvd52; + __le32 rsvd56; +}; + +/* WQE used to create an FCP initiator read. */ +enum sli4_ir_wqe_flags { + SLI4_IR_WQE_DBDE = 0x40, + SLI4_IR_WQE_XBL = 0x8, + SLI4_IR_WQE_XC = 0x20, + SLI4_IR_WQE_IOD = 0x20, + SLI4_IR_WQE_HLM = 0x10, + SLI4_IR_WQE_CCPE = 0x80, + SLI4_IR_WQE_EAT = 0x10, + SLI4_IR_WQE_APPID = 0x10, + SLI4_IR_WQE_WQES = 0x80, + SLI4_IR_WQE_PU_SHFT = 4, + SLI4_IR_WQE_CT_SHFT = 2, + SLI4_IR_WQE_BS_SHFT = 4, + SLI4_IR_WQE_LEN_LOC_BIT1 = 0x80, + SLI4_IR_WQE_LEN_LOC_BIT2 = 0x1, +}; + +struct sli4_fcp_iread64_wqe { + struct sli4_bde bde; + __le16 payload_offset_length; + __le16 fcp_cmd_buffer_length; + + __le32 total_transfer_length; + + __le32 remote_n_port_id_dword; + + __le16 xri_tag; + __le16 context_tag; + + u8 dif_ct_bs_byte; + u8 command; + u8 class_pu_byte; + u8 timer; + + __le32 abort_tag; + + __le16 request_tag; + __le16 rsvd34; + + u8 len_loc1_byte; + u8 qosd_xbl_hlm_iod_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + + __le32 rsvd44; + struct sli4_bde first_data_bde; +}; + +/* WQE used to create an FCP initiator write. */ +enum sli4_iwr_wqe_flags { + SLI4_IWR_WQE_DBDE = 0x40, + SLI4_IWR_WQE_XBL = 0x8, + SLI4_IWR_WQE_XC = 0x20, + SLI4_IWR_WQE_IOD = 0x20, + SLI4_IWR_WQE_HLM = 0x10, + SLI4_IWR_WQE_DNRX = 0x10, + SLI4_IWR_WQE_CCPE = 0x80, + SLI4_IWR_WQE_EAT = 0x10, + SLI4_IWR_WQE_APPID = 0x10, + SLI4_IWR_WQE_WQES = 0x80, + SLI4_IWR_WQE_PU_SHFT = 4, + SLI4_IWR_WQE_CT_SHFT = 2, + SLI4_IWR_WQE_BS_SHFT = 4, + SLI4_IWR_WQE_LEN_LOC_BIT1 = 0x80, + SLI4_IWR_WQE_LEN_LOC_BIT2 = 0x1, +}; + +struct sli4_fcp_iwrite64_wqe { + struct sli4_bde bde; + __le16 payload_offset_length; + __le16 fcp_cmd_buffer_length; + __le16 total_transfer_length; + __le16 initial_transfer_length; + __le16 xri_tag; + __le16 context_tag; + u8 dif_ct_bs_byte; + u8 command; + u8 class_pu_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 rsvd34; + u8 len_loc1_byte; + u8 qosd_xbl_hlm_iod_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 remote_n_port_id_dword; + struct sli4_bde first_data_bde; +}; + +struct sli4_fcp_128byte_wqe { + u32 dw[32]; +}; + +/* WQE used to create an FCP target receive */ +enum sli4_trcv_wqe_flags { + SLI4_TRCV_WQE_DBDE = 0x40, + SLI4_TRCV_WQE_XBL = 0x8, + SLI4_TRCV_WQE_AR = 0x8, + SLI4_TRCV_WQE_XC = 0x20, + SLI4_TRCV_WQE_IOD = 0x20, + SLI4_TRCV_WQE_HLM = 0x10, + SLI4_TRCV_WQE_DNRX = 0x10, + SLI4_TRCV_WQE_CCPE = 0x80, + SLI4_TRCV_WQE_EAT = 0x10, + SLI4_TRCV_WQE_APPID = 0x10, + SLI4_TRCV_WQE_WQES = 0x80, + SLI4_TRCV_WQE_PU_SHFT = 4, + SLI4_TRCV_WQE_CT_SHFT = 2, + SLI4_TRCV_WQE_BS_SHFT = 4, + SLI4_TRCV_WQE_LEN_LOC_BIT2 = 0x1, +}; + +struct sli4_fcp_treceive64_wqe { + struct sli4_bde bde; + __le32 payload_offset_length; + __le32 relative_offset; + union { + __le16 sec_xri_tag; + __le16 rsvd; + __le32 dword; + } dword5; + __le16 xri_tag; + __le16 context_tag; + u8 dif_ct_bs_byte; + u8 command; + u8 class_ar_pu_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 remote_xid; + u8 lloc1_appid; + u8 qosd_xbl_hlm_iod_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 fcp_data_receive_length; + struct sli4_bde first_data_bde; +}; + +/* WQE used to create an FCP target response */ +enum sli4_trsp_wqe_flags { + SLI4_TRSP_WQE_AG = 0x8, + SLI4_TRSP_WQE_DBDE = 0x40, + SLI4_TRSP_WQE_XBL = 0x8, + SLI4_TRSP_WQE_XC = 0x20, + SLI4_TRSP_WQE_HLM = 0x10, + SLI4_TRSP_WQE_DNRX = 0x10, + SLI4_TRSP_WQE_CCPE = 0x80, + SLI4_TRSP_WQE_EAT = 0x10, + SLI4_TRSP_WQE_APPID = 0x10, + SLI4_TRSP_WQE_WQES = 0x80, +}; + +struct sli4_fcp_trsp64_wqe { + struct sli4_bde bde; + __le32 fcp_response_length; + __le32 rsvd12; + __le32 dword5; + __le16 xri_tag; + __le16 rpi; + u8 ct_dnrx_byte; + u8 command; + u8 class_ag_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 remote_xid; + u8 lloc1_appid; + u8 qosd_xbl_hlm_dbde_wqes; + u8 eat_xc_ccpe; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 rsvd44; + __le32 rsvd48; + __le32 rsvd52; + __le32 rsvd56; +}; + +/* WQE used to create an FCP target send (DATA IN). */ +enum sli4_tsend_wqe_flags { + SLI4_TSEND_WQE_XBL = 0x8, + SLI4_TSEND_WQE_DBDE = 0x40, + SLI4_TSEND_WQE_IOD = 0x20, + SLI4_TSEND_WQE_QOSD = 0x2, + SLI4_TSEND_WQE_HLM = 0x10, + SLI4_TSEND_WQE_PU_SHFT = 4, + SLI4_TSEND_WQE_AR = 0x8, + SLI4_TSEND_CT_SHFT = 2, + SLI4_TSEND_BS_SHFT = 4, + SLI4_TSEND_LEN_LOC_BIT2 = 0x1, + SLI4_TSEND_CCPE = 0x80, + SLI4_TSEND_APPID_VALID = 0x20, + SLI4_TSEND_WQES = 0x80, + SLI4_TSEND_XC = 0x20, + SLI4_TSEND_EAT = 0x10, +}; + +struct sli4_fcp_tsend64_wqe { + struct sli4_bde bde; + __le32 payload_offset_length; + __le32 relative_offset; + __le32 dword5; + __le16 xri_tag; + __le16 rpi; + u8 ct_byte; + u8 command; + u8 class_pu_ar_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 remote_xid; + u8 dw10byte0; + u8 ll_qd_xbl_hlm_iod_dbde; + u8 dw10byte2; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd45; + __le16 cq_id; + __le32 fcp_data_transmit_length; + struct sli4_bde first_data_bde; +}; + +/* WQE used to create a general request. */ +enum sli4_gen_req_wqe_flags { + SLI4_GEN_REQ64_WQE_XBL = 0x8, + SLI4_GEN_REQ64_WQE_DBDE = 0x40, + SLI4_GEN_REQ64_WQE_IOD = 0x20, + SLI4_GEN_REQ64_WQE_QOSD = 0x2, + SLI4_GEN_REQ64_WQE_HLM = 0x10, + SLI4_GEN_REQ64_CT_SHFT = 2, +}; + +struct sli4_gen_request64_wqe { + struct sli4_bde bde; + __le32 request_payload_length; + __le32 relative_offset; + u8 rsvd17; + u8 df_ctl; + u8 type; + u8 r_ctl; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 rsvd34; + u8 dw10flags0; + u8 dw10flags1; + u8 dw10flags2; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 remote_n_port_id_dword; + __le32 rsvd48; + __le32 rsvd52; + __le32 max_response_payload_length; +}; + +/* WQE used to create a send frame request */ +enum sli4_sf_wqe_flags { + SLI4_SF_WQE_DBDE = 0x40, + SLI4_SF_PU = 0x30, + SLI4_SF_CT = 0xc, + SLI4_SF_QOSD = 0x2, + SLI4_SF_LEN_LOC_BIT1 = 0x80, + SLI4_SF_LEN_LOC_BIT2 = 0x1, + SLI4_SF_XC = 0x20, + SLI4_SF_XBL = 0x8, +}; + +struct sli4_send_frame_wqe { + struct sli4_bde bde; + __le32 frame_length; + __le32 fc_header_0_1[2]; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 dw7flags0; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + u8 eof; + u8 sof; + u8 dw10flags0; + u8 dw10flags1; + u8 dw10flags2; + u8 ccp; + u8 cmd_type_byte; + u8 rsvd41; + __le16 cq_id; + __le32 fc_header_2_5[4]; +}; + +/* WQE used to create a transmit sequence */ +enum sli4_seq_wqe_flags { + SLI4_SEQ_WQE_DBDE = 0x4000, + SLI4_SEQ_WQE_XBL = 0x800, + SLI4_SEQ_WQE_SI = 0x4, + SLI4_SEQ_WQE_FT = 0x8, + SLI4_SEQ_WQE_XO = 0x40, + SLI4_SEQ_WQE_LS = 0x80, + SLI4_SEQ_WQE_DIF = 0x3, + SLI4_SEQ_WQE_BS = 0x70, + SLI4_SEQ_WQE_PU = 0x30, + SLI4_SEQ_WQE_HLM = 0x1000, + SLI4_SEQ_WQE_IOD_SHIFT = 13, + SLI4_SEQ_WQE_CT_SHIFT = 2, + SLI4_SEQ_WQE_LEN_LOC_SHIFT = 7, +}; + +struct sli4_xmit_sequence64_wqe { + struct sli4_bde bde; + __le32 remote_n_port_id_dword; + __le32 relative_offset; + u8 dw5flags0; + u8 df_ctl; + u8 type; + u8 r_ctl; + __le16 xri_tag; + __le16 context_tag; + u8 dw7flags0; + u8 command; + u8 dw7flags1; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 remote_xid; + __le16 dw10w0; + u8 dw10flags0; + u8 ccp; + u8 cmd_type_wqec_byte; + u8 rsvd45; + __le16 cq_id; + __le32 sequence_payload_len; + __le32 rsvd48; + __le32 rsvd52; + __le32 rsvd56; +}; + +/* + * WQE used unblock the specified XRI and to release + * it to the SLI Port's free pool. + */ +enum sli4_requeue_wqe_flags { + SLI4_REQU_XRI_WQE_XC = 0x20, + SLI4_REQU_XRI_WQE_QOSD = 0x2, +}; + +struct sli4_requeue_xri_wqe { + __le32 rsvd0; + __le32 rsvd4; + __le32 rsvd8; + __le32 rsvd12; + __le32 rsvd16; + __le32 rsvd20; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 rsvd32; + __le16 request_tag; + __le16 rsvd34; + __le16 flags0; + __le16 flags1; + __le16 flags2; + u8 ccp; + u8 cmd_type_wqec_byte; + u8 rsvd42; + __le16 cq_id; + __le32 rsvd44; + __le32 rsvd48; + __le32 rsvd52; + __le32 rsvd56; +}; + +/* WQE used to create a BLS response */ +enum sli4_bls_rsp_wqe_flags { + SLI4_BLS_RSP_RID = 0xffffff, + SLI4_BLS_RSP_WQE_AR = 0x40000000, + SLI4_BLS_RSP_WQE_CT_SHFT = 2, + SLI4_BLS_RSP_WQE_QOSD = 0x2, + SLI4_BLS_RSP_WQE_HLM = 0x10, +}; + +struct sli4_xmit_bls_rsp_wqe { + __le32 payload_word0; + __le16 rx_id; + __le16 ox_id; + __le16 high_seq_cnt; + __le16 low_seq_cnt; + __le32 rsvd12; + __le32 local_n_port_id_dword; + __le32 remote_id_dword; + __le16 xri_tag; + __le16 context_tag; + u8 dw8flags0; + u8 command; + u8 dw8flags1; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 rsvd38; + u8 dw11flags0; + u8 dw11flags1; + u8 dw11flags2; + u8 ccp; + u8 dw12flags0; + u8 rsvd45; + __le16 cq_id; + __le16 temporary_rpi; + u8 rsvd50; + u8 rsvd51; + __le32 rsvd52; + __le32 rsvd56; + __le32 rsvd60; +}; + +enum sli_bls_type { + SLI4_SLI_BLS_ACC, + SLI4_SLI_BLS_RJT, + SLI4_SLI_BLS_MAX +}; + +struct sli_bls_payload { + enum sli_bls_type type; + __le16 ox_id; + __le16 rx_id; + union { + struct { + u8 seq_id_validity; + u8 seq_id_last; + u8 rsvd2; + u8 rsvd3; + u16 ox_id; + u16 rx_id; + __le16 low_seq_cnt; + __le16 high_seq_cnt; + } acc; + struct { + u8 vendor_unique; + u8 reason_explanation; + u8 reason_code; + u8 rsvd3; + } rjt; + } u; +}; + +/* WQE used to create an ELS response */ + +enum sli4_els_rsp_flags { + SLI4_ELS_SID = 0xffffff, + SLI4_ELS_RID = 0xffffff, + SLI4_ELS_DBDE = 0x40, + SLI4_ELS_XBL = 0x8, + SLI4_ELS_IOD = 0x20, + SLI4_ELS_QOSD = 0x2, + SLI4_ELS_XC = 0x20, + SLI4_ELS_CT_OFFSET = 0X2, + SLI4_ELS_SP = 0X1000000, + SLI4_ELS_HLM = 0X10, +}; + +struct sli4_xmit_els_rsp64_wqe { + struct sli4_bde els_response_payload; + __le32 els_response_payload_length; + __le32 sid_dw; + __le32 rid_dw; + __le16 xri_tag; + __le16 context_tag; + u8 ct_byte; + u8 command; + u8 class_byte; + u8 timer; + __le32 abort_tag; + __le16 request_tag; + __le16 ox_id; + u8 flags1; + u8 flags2; + u8 flags3; + u8 flags4; + u8 cmd_type_wqec; + u8 rsvd34; + __le16 cq_id; + __le16 temporary_rpi; + __le16 rsvd38; + u32 rsvd40; + u32 rsvd44; + u32 rsvd48; +}; + +/* Local Reject Reason Codes */ +enum sli4_fc_local_rej_codes { + SLI4_FC_LOCAL_REJECT_UNKNOWN, + SLI4_FC_LOCAL_REJECT_MISSING_CONTINUE, + SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT, + SLI4_FC_LOCAL_REJECT_INTERNAL_ERROR, + SLI4_FC_LOCAL_REJECT_INVALID_RPI, + SLI4_FC_LOCAL_REJECT_NO_XRI, + SLI4_FC_LOCAL_REJECT_ILLEGAL_COMMAND, + SLI4_FC_LOCAL_REJECT_XCHG_DROPPED, + SLI4_FC_LOCAL_REJECT_ILLEGAL_FIELD, + SLI4_FC_LOCAL_REJECT_RPI_SUSPENDED, + SLI4_FC_LOCAL_REJECT_RSVD, + SLI4_FC_LOCAL_REJECT_RSVD1, + SLI4_FC_LOCAL_REJECT_NO_ABORT_MATCH, + SLI4_FC_LOCAL_REJECT_TX_DMA_FAILED, + SLI4_FC_LOCAL_REJECT_RX_DMA_FAILED, + SLI4_FC_LOCAL_REJECT_ILLEGAL_FRAME, + SLI4_FC_LOCAL_REJECT_RSVD2, + SLI4_FC_LOCAL_REJECT_NO_RESOURCES, //0x11 + SLI4_FC_LOCAL_REJECT_FCP_CONF_FAILURE, + SLI4_FC_LOCAL_REJECT_ILLEGAL_LENGTH, + SLI4_FC_LOCAL_REJECT_UNSUPPORTED_FEATURE, + SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS, + SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED, + SLI4_FC_LOCAL_REJECT_RCV_BUFFER_TIMEOUT, + SLI4_FC_LOCAL_REJECT_LOOP_OPEN_FAILURE, + SLI4_FC_LOCAL_REJECT_RSVD3, + SLI4_FC_LOCAL_REJECT_LINK_DOWN, + SLI4_FC_LOCAL_REJECT_CORRUPTED_DATA, + SLI4_FC_LOCAL_REJECT_CORRUPTED_RPI, + SLI4_FC_LOCAL_REJECT_OUTOFORDER_DATA, + SLI4_FC_LOCAL_REJECT_OUTOFORDER_ACK, + SLI4_FC_LOCAL_REJECT_DUP_FRAME, + SLI4_FC_LOCAL_REJECT_LINK_CONTROL_FRAME, //0x20 + SLI4_FC_LOCAL_REJECT_BAD_HOST_ADDRESS, + SLI4_FC_LOCAL_REJECT_RSVD4, + SLI4_FC_LOCAL_REJECT_MISSING_HDR_BUFFER, + SLI4_FC_LOCAL_REJECT_MSEQ_CHAIN_CORRUPTED, + SLI4_FC_LOCAL_REJECT_ABORTMULT_REQUESTED, + SLI4_FC_LOCAL_REJECT_BUFFER_SHORTAGE = 0x28, + SLI4_FC_LOCAL_REJECT_RCV_XRIBUF_WAITING, + SLI4_FC_LOCAL_REJECT_INVALID_VPI = 0x2e, + SLI4_FC_LOCAL_REJECT_NO_FPORT_DETECTED, + SLI4_FC_LOCAL_REJECT_MISSING_XRIBUF, + SLI4_FC_LOCAL_REJECT_RSVD5, + SLI4_FC_LOCAL_REJECT_INVALID_XRI, + SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET = 0x40, + SLI4_FC_LOCAL_REJECT_MISSING_RELOFFSET, + SLI4_FC_LOCAL_REJECT_INSUFF_BUFFERSPACE, + SLI4_FC_LOCAL_REJECT_MISSING_SI, + SLI4_FC_LOCAL_REJECT_MISSING_ES, + SLI4_FC_LOCAL_REJECT_INCOMPLETE_XFER, + SLI4_FC_LOCAL_REJECT_SLER_FAILURE, + SLI4_FC_LOCAL_REJECT_SLER_CMD_RCV_FAILURE, + SLI4_FC_LOCAL_REJECT_SLER_REC_RJT_ERR, + SLI4_FC_LOCAL_REJECT_SLER_REC_SRR_RETRY_ERR, + SLI4_FC_LOCAL_REJECT_SLER_SRR_RJT_ERR, + SLI4_FC_LOCAL_REJECT_RSVD6, + SLI4_FC_LOCAL_REJECT_SLER_RRQ_RJT_ERR, + SLI4_FC_LOCAL_REJECT_SLER_RRQ_RETRY_ERR, + SLI4_FC_LOCAL_REJECT_SLER_ABTS_ERR, +}; + +enum sli4_async_rcqe_flags { + SLI4_RACQE_RQ_EL_INDX = 0xfff, + SLI4_RACQE_FCFI = 0x3f, + SLI4_RACQE_HDPL = 0x3f, + SLI4_RACQE_RQ_ID = 0xffc0, +}; + +struct sli4_fc_async_rcqe { + u8 rsvd0; + u8 status; + __le16 rq_elmt_indx_word; + __le32 rsvd4; + __le16 fcfi_rq_id_word; + __le16 data_placement_length; + u8 sof_byte; + u8 eof_byte; + u8 code; + u8 hdpl_byte; +}; + +struct sli4_fc_async_rcqe_v1 { + u8 rsvd0; + u8 status; + __le16 rq_elmt_indx_word; + u8 fcfi_byte; + u8 rsvd5; + __le16 rsvd6; + __le16 rq_id; + __le16 data_placement_length; + u8 sof_byte; + u8 eof_byte; + u8 code; + u8 hdpl_byte; +}; + +enum sli4_fc_async_rq_status { + SLI4_FC_ASYNC_RQ_SUCCESS = 0x10, + SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED, + SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED, + SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC, + SLI4_FC_ASYNC_RQ_DMA_FAILURE, +}; + +#define SLI4_RCQE_RQ_EL_INDX 0xfff + +struct sli4_fc_coalescing_rcqe { + u8 rsvd0; + u8 status; + __le16 rq_elmt_indx_word; + __le32 rsvd4; + __le16 rq_id; + __le16 seq_placement_length; + __le16 rsvd14; + u8 code; + u8 vld_byte; +}; + +#define SLI4_FC_COALESCE_RQ_SUCCESS 0x10 +#define SLI4_FC_COALESCE_RQ_INSUFF_XRI_NEEDED 0x18 + +enum sli4_optimized_write_cmd_cqe_flags { + SLI4_OCQE_RQ_EL_INDX = 0x7f, /* DW0 bits 16:30 */ + SLI4_OCQE_FCFI = 0x3f, /* DW1 bits 0:6 */ + SLI4_OCQE_OOX = 1 << 6, /* DW1 bit 15 */ + SLI4_OCQE_AGXR = 1 << 7, /* DW1 bit 16 */ + SLI4_OCQE_HDPL = 0x3f, /* DW3 bits 24:29*/ +}; + +struct sli4_fc_optimized_write_cmd_cqe { + u8 rsvd0; + u8 status; + __le16 w1; + u8 flags0; + u8 flags1; + __le16 xri; + __le16 rq_id; + __le16 data_placement_length; + __le16 rpi; + u8 code; + u8 hdpl_vld; +}; + +#define SLI4_OCQE_XB 0x10 + +struct sli4_fc_optimized_write_data_cqe { + u8 hw_status; + u8 status; + __le16 xri; + __le32 total_data_placed; + __le32 extended_status; + __le16 rsvd12; + u8 code; + u8 flags; +}; + +struct sli4_fc_xri_aborted_cqe { + u8 rsvd0; + u8 status; + __le16 rsvd2; + __le32 extended_status; + __le16 xri; + __le16 remote_xid; + __le16 rsvd12; + u8 code; + u8 flags; +}; + +enum sli4_generic_ctx { + SLI4_GENERIC_CONTEXT_RPI, + SLI4_GENERIC_CONTEXT_VPI, + SLI4_GENERIC_CONTEXT_VFI, + SLI4_GENERIC_CONTEXT_FCFI, +}; + +#define SLI4_GENERIC_CLASS_CLASS_2 0x1 +#define SLI4_GENERIC_CLASS_CLASS_3 0x2 + +#define SLI4_ELS_REQUEST64_DIR_WRITE 0x0 +#define SLI4_ELS_REQUEST64_DIR_READ 0x1 + +enum sli4_els_request { + SLI4_ELS_REQUEST64_OTHER, + SLI4_ELS_REQUEST64_LOGO, + SLI4_ELS_REQUEST64_FDISC, + SLI4_ELS_REQUEST64_FLOGIN, + SLI4_ELS_REQUEST64_PLOGI, +}; + +enum sli4_els_cmd_type { + SLI4_ELS_REQUEST64_CMD_GEN = 0x08, + SLI4_ELS_REQUEST64_CMD_NON_FABRIC = 0x0c, + SLI4_ELS_REQUEST64_CMD_FABRIC = 0x0d, +}; + +#define SLI_PAGE_SIZE SZ_4K + +#define SLI4_BMBX_TIMEOUT_MSEC 30000 +#define SLI4_FW_READY_TIMEOUT_MSEC 30000 + +#define SLI4_BMBX_DELAY_US 1000 /* 1 ms */ +#define SLI4_INIT_PORT_DELAY_US 10000 /* 10 ms */ + +static inline u32 +sli_page_count(size_t bytes, u32 page_size) +{ + if (!page_size) + return 0; + + return (bytes + (page_size - 1)) >> __ffs(page_size); +} + +/************************************************************************* + * SLI-4 mailbox command formats and definitions + */ + +struct sli4_mbox_command_header { + u8 resvd0; + u8 command; + __le16 status; /* Port writes to indicate success/fail */ +}; + +enum sli4_mbx_cmd_value { + SLI4_MBX_CMD_CONFIG_LINK = 0x07, + SLI4_MBX_CMD_DUMP = 0x17, + SLI4_MBX_CMD_DOWN_LINK = 0x06, + SLI4_MBX_CMD_INIT_LINK = 0x05, + SLI4_MBX_CMD_INIT_VFI = 0xa3, + SLI4_MBX_CMD_INIT_VPI = 0xa4, + SLI4_MBX_CMD_POST_XRI = 0xa7, + SLI4_MBX_CMD_RELEASE_XRI = 0xac, + SLI4_MBX_CMD_READ_CONFIG = 0x0b, + SLI4_MBX_CMD_READ_STATUS = 0x0e, + SLI4_MBX_CMD_READ_NVPARMS = 0x02, + SLI4_MBX_CMD_READ_REV = 0x11, + SLI4_MBX_CMD_READ_LNK_STAT = 0x12, + SLI4_MBX_CMD_READ_SPARM64 = 0x8d, + SLI4_MBX_CMD_READ_TOPOLOGY = 0x95, + SLI4_MBX_CMD_REG_FCFI = 0xa0, + SLI4_MBX_CMD_REG_FCFI_MRQ = 0xaf, + SLI4_MBX_CMD_REG_RPI = 0x93, + SLI4_MBX_CMD_REG_RX_RQ = 0xa6, + SLI4_MBX_CMD_REG_VFI = 0x9f, + SLI4_MBX_CMD_REG_VPI = 0x96, + SLI4_MBX_CMD_RQST_FEATURES = 0x9d, + SLI4_MBX_CMD_SLI_CONFIG = 0x9b, + SLI4_MBX_CMD_UNREG_FCFI = 0xa2, + SLI4_MBX_CMD_UNREG_RPI = 0x14, + SLI4_MBX_CMD_UNREG_VFI = 0xa1, + SLI4_MBX_CMD_UNREG_VPI = 0x97, + SLI4_MBX_CMD_WRITE_NVPARMS = 0x03, + SLI4_MBX_CMD_CFG_AUTO_XFER_RDY = 0xad, +}; + +enum sli4_mbx_status { + SLI4_MBX_STATUS_SUCCESS = 0x0000, + SLI4_MBX_STATUS_FAILURE = 0x0001, + SLI4_MBX_STATUS_RPI_NOT_REG = 0x1400, +}; + +/* CONFIG_LINK - configure link-oriented parameters, + * such as default N_Port_ID address and various timers + */ +enum sli4_cmd_config_link_flags { + SLI4_CFG_LINK_BBSCN = 0xf00, + SLI4_CFG_LINK_CSCN = 0x1000, +}; + +struct sli4_cmd_config_link { + struct sli4_mbox_command_header hdr; + u8 maxbbc; + u8 rsvd5; + u8 rsvd6; + u8 rsvd7; + u8 alpa; + __le16 n_port_id; + u8 rsvd11; + __le32 rsvd12; + __le32 e_d_tov; + __le32 lp_tov; + __le32 r_a_tov; + __le32 r_t_tov; + __le32 al_tov; + __le32 rsvd36; + __le32 bbscn_dword; +}; + +#define SLI4_DUMP4_TYPE 0xf + +#define SLI4_WKI_TAG_SAT_TEM 0x1040 + +struct sli4_cmd_dump4 { + struct sli4_mbox_command_header hdr; + __le32 type_dword; + __le16 wki_selection; + __le16 rsvd10; + __le32 rsvd12; + __le32 returned_byte_cnt; + __le32 resp_data[59]; +}; + +/* INIT_LINK - initialize the link for a FC port */ +enum sli4_init_link_flags { + SLI4_INIT_LINK_F_LOOPBACK = 1 << 0, + + SLI4_INIT_LINK_F_P2P_ONLY = 1 << 1, + SLI4_INIT_LINK_F_FCAL_ONLY = 2 << 1, + SLI4_INIT_LINK_F_FCAL_FAIL_OVER = 0 << 1, + SLI4_INIT_LINK_F_P2P_FAIL_OVER = 1 << 1, + + SLI4_INIT_LINK_F_UNFAIR = 1 << 6, + SLI4_INIT_LINK_F_NO_LIRP = 1 << 7, + SLI4_INIT_LINK_F_LOOP_VALID_CHK = 1 << 8, + SLI4_INIT_LINK_F_NO_LISA = 1 << 9, + SLI4_INIT_LINK_F_FAIL_OVER = 1 << 10, + SLI4_INIT_LINK_F_FIXED_SPEED = 1 << 11, + SLI4_INIT_LINK_F_PICK_HI_ALPA = 1 << 15, + +}; + +enum sli4_fc_link_speed { + SLI4_LINK_SPEED_1G = 1, + SLI4_LINK_SPEED_2G, + SLI4_LINK_SPEED_AUTO_1_2, + SLI4_LINK_SPEED_4G, + SLI4_LINK_SPEED_AUTO_4_1, + SLI4_LINK_SPEED_AUTO_4_2, + SLI4_LINK_SPEED_AUTO_4_2_1, + SLI4_LINK_SPEED_8G, + SLI4_LINK_SPEED_AUTO_8_1, + SLI4_LINK_SPEED_AUTO_8_2, + SLI4_LINK_SPEED_AUTO_8_2_1, + SLI4_LINK_SPEED_AUTO_8_4, + SLI4_LINK_SPEED_AUTO_8_4_1, + SLI4_LINK_SPEED_AUTO_8_4_2, + SLI4_LINK_SPEED_10G, + SLI4_LINK_SPEED_16G, + SLI4_LINK_SPEED_AUTO_16_8_4, + SLI4_LINK_SPEED_AUTO_16_8, + SLI4_LINK_SPEED_32G, + SLI4_LINK_SPEED_AUTO_32_16_8, + SLI4_LINK_SPEED_AUTO_32_16, + SLI4_LINK_SPEED_64G, + SLI4_LINK_SPEED_AUTO_64_32_16, + SLI4_LINK_SPEED_AUTO_64_32, + SLI4_LINK_SPEED_128G, + SLI4_LINK_SPEED_AUTO_128_64_32, + SLI4_LINK_SPEED_AUTO_128_64, +}; + +struct sli4_cmd_init_link { + struct sli4_mbox_command_header hdr; + __le32 sel_reset_al_pa_dword; + __le32 flags0; + __le32 link_speed_sel_code; +}; + +/* INIT_VFI - initialize the VFI resource */ +enum sli4_init_vfi_flags { + SLI4_INIT_VFI_FLAG_VP = 0x1000, + SLI4_INIT_VFI_FLAG_VF = 0x2000, + SLI4_INIT_VFI_FLAG_VT = 0x4000, + SLI4_INIT_VFI_FLAG_VR = 0x8000, + + SLI4_INIT_VFI_VFID = 0x1fff, + SLI4_INIT_VFI_PRI = 0xe000, + + SLI4_INIT_VFI_HOP_COUNT = 0xff000000, +}; + +struct sli4_cmd_init_vfi { + struct sli4_mbox_command_header hdr; + __le16 vfi; + __le16 flags0_word; + __le16 fcfi; + __le16 vpi; + __le32 vf_id_pri_dword; + __le32 hop_cnt_dword; +}; + +/* INIT_VPI - initialize the VPI resource */ +struct sli4_cmd_init_vpi { + struct sli4_mbox_command_header hdr; + __le16 vpi; + __le16 vfi; +}; + +/* POST_XRI - post XRI resources to the SLI Port */ +enum sli4_post_xri_flags { + SLI4_POST_XRI_COUNT = 0xfff, + SLI4_POST_XRI_FLAG_ENX = 0x1000, + SLI4_POST_XRI_FLAG_DL = 0x2000, + SLI4_POST_XRI_FLAG_DI = 0x4000, + SLI4_POST_XRI_FLAG_VAL = 0x8000, +}; + +struct sli4_cmd_post_xri { + struct sli4_mbox_command_header hdr; + __le16 xri_base; + __le16 xri_count_flags; +}; + +/* RELEASE_XRI - Release XRI resources from the SLI Port */ +enum sli4_release_xri_flags { + SLI4_RELEASE_XRI_REL_XRI_CNT = 0x1f, + SLI4_RELEASE_XRI_COUNT = 0x1f, +}; + +struct sli4_cmd_release_xri { + struct sli4_mbox_command_header hdr; + __le16 rel_xri_count_word; + __le16 xri_count_word; + + struct { + __le16 xri_tag0; + __le16 xri_tag1; + } xri_tbl[62]; +}; + +/* READ_CONFIG - read SLI port configuration parameters */ +struct sli4_cmd_read_config { + struct sli4_mbox_command_header hdr; +}; + +enum sli4_read_cfg_resp_flags { + SLI4_READ_CFG_RESP_RESOURCE_EXT = 0x80000000, /* DW1 */ + SLI4_READ_CFG_RESP_TOPOLOGY = 0xff000000, /* DW2 */ +}; + +enum sli4_read_cfg_topo { + SLI4_READ_CFG_TOPO_FC = 0x1, /* FC topology unknown */ + SLI4_READ_CFG_TOPO_NON_FC_AL = 0x2, /* FC point-to-point or fabric */ + SLI4_READ_CFG_TOPO_FC_AL = 0x3, /* FC-AL topology */ +}; + +/* Link Module Type */ +enum sli4_read_cfg_lmt { + SLI4_LINK_MODULE_TYPE_1GB = 0x0004, + SLI4_LINK_MODULE_TYPE_2GB = 0x0008, + SLI4_LINK_MODULE_TYPE_4GB = 0x0040, + SLI4_LINK_MODULE_TYPE_8GB = 0x0080, + SLI4_LINK_MODULE_TYPE_16GB = 0x0200, + SLI4_LINK_MODULE_TYPE_32GB = 0x0400, + SLI4_LINK_MODULE_TYPE_64GB = 0x0800, + SLI4_LINK_MODULE_TYPE_128GB = 0x1000, +}; + +struct sli4_rsp_read_config { + struct sli4_mbox_command_header hdr; + __le32 ext_dword; + __le32 topology_dword; + __le32 resvd8; + __le16 e_d_tov; + __le16 resvd14; + __le32 resvd16; + __le16 r_a_tov; + __le16 resvd22; + __le32 resvd24; + __le32 resvd28; + __le16 lmt; + __le16 resvd34; + __le32 resvd36; + __le32 resvd40; + __le16 xri_base; + __le16 xri_count; + __le16 rpi_base; + __le16 rpi_count; + __le16 vpi_base; + __le16 vpi_count; + __le16 vfi_base; + __le16 vfi_count; + __le16 resvd60; + __le16 fcfi_count; + __le16 rq_count; + __le16 eq_count; + __le16 wq_count; + __le16 cq_count; + __le32 pad[45]; +}; + +/* READ_NVPARMS - read SLI port configuration parameters */ +enum sli4_read_nvparms_flags { + SLI4_READ_NVPARAMS_HARD_ALPA = 0xff, + SLI4_READ_NVPARAMS_PREFERRED_D_ID = 0xffffff00, +}; + +struct sli4_cmd_read_nvparms { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + __le32 resvd8; + __le32 resvd12; + u8 wwpn[8]; + u8 wwnn[8]; + __le32 hard_alpa_d_id; +}; + +/* WRITE_NVPARMS - write SLI port configuration parameters */ +struct sli4_cmd_write_nvparms { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + __le32 resvd8; + __le32 resvd12; + u8 wwpn[8]; + u8 wwnn[8]; + __le32 hard_alpa_d_id; +}; + +/* READ_REV - read the Port revision levels */ +enum { + SLI4_READ_REV_FLAG_SLI_LEVEL = 0xf, + SLI4_READ_REV_FLAG_FCOEM = 0x10, + SLI4_READ_REV_FLAG_CEEV = 0x60, + SLI4_READ_REV_FLAG_VPD = 0x2000, + + SLI4_READ_REV_AVAILABLE_LENGTH = 0xffffff, +}; + +struct sli4_cmd_read_rev { + struct sli4_mbox_command_header hdr; + __le16 resvd0; + __le16 flags0_word; + __le32 first_hw_rev; + __le32 second_hw_rev; + __le32 resvd12; + __le32 third_hw_rev; + u8 fc_ph_low; + u8 fc_ph_high; + u8 feature_level_low; + u8 feature_level_high; + __le32 resvd24; + __le32 first_fw_id; + u8 first_fw_name[16]; + __le32 second_fw_id; + u8 second_fw_name[16]; + __le32 rsvd18[30]; + __le32 available_length_dword; + struct sli4_dmaaddr hostbuf; + __le32 returned_vpd_length; + __le32 actual_vpd_length; +}; + +/* READ_SPARM64 - read the Port service parameters */ +#define SLI4_READ_SPARM64_WWPN_OFFSET (4 * sizeof(u32)) +#define SLI4_READ_SPARM64_WWNN_OFFSET (6 * sizeof(u32)) + +struct sli4_cmd_read_sparm64 { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + struct sli4_bde bde_64; + __le16 vpi; + __le16 resvd22; + __le16 port_name_start; + __le16 port_name_len; + __le16 node_name_start; + __le16 node_name_len; +}; + +/* READ_TOPOLOGY - read the link event information */ +enum sli4_read_topo_e { + SLI4_READTOPO_ATTEN_TYPE = 0xff, + SLI4_READTOPO_FLAG_IL = 0x100, + SLI4_READTOPO_FLAG_PB_RECVD = 0x200, + + SLI4_READTOPO_LINKSTATE_RECV = 0x3, + SLI4_READTOPO_LINKSTATE_TRANS = 0xc, + SLI4_READTOPO_LINKSTATE_MACHINE = 0xf0, + SLI4_READTOPO_LINKSTATE_SPEED = 0xff00, + SLI4_READTOPO_LINKSTATE_TF = 0x40000000, + SLI4_READTOPO_LINKSTATE_LU = 0x80000000, + + SLI4_READTOPO_SCN_BBSCN = 0xf, + SLI4_READTOPO_SCN_CBBSCN = 0xf0, + + SLI4_READTOPO_R_T_TOV = 0x1ff, + SLI4_READTOPO_AL_TOV = 0xf000, + + SLI4_READTOPO_PB_FLAG = 0x80, + + SLI4_READTOPO_INIT_N_PORTID = 0xffffff, +}; + +#define SLI4_MIN_LOOP_MAP_BYTES 128 + +struct sli4_cmd_read_topology { + struct sli4_mbox_command_header hdr; + __le32 event_tag; + __le32 dw2_attentype; + u8 topology; + u8 lip_type; + u8 lip_al_ps; + u8 al_pa_granted; + struct sli4_bde bde_loop_map; + __le32 linkdown_state; + __le32 currlink_state; + u8 max_bbc; + u8 init_bbc; + u8 scn_flags; + u8 rsvd39; + __le16 dw10w0_al_rt_tov; + __le16 lp_tov; + u8 acquired_al_pa; + u8 pb_flags; + __le16 specified_al_pa; + __le32 dw12_init_n_port_id; +}; + +enum sli4_read_topo_link { + SLI4_READ_TOPOLOGY_LINK_UP = 0x1, + SLI4_READ_TOPOLOGY_LINK_DOWN, + SLI4_READ_TOPOLOGY_LINK_NO_ALPA, +}; + +enum sli4_read_topo { + SLI4_READ_TOPO_UNKNOWN = 0x0, + SLI4_READ_TOPO_NON_FC_AL, + SLI4_READ_TOPO_FC_AL, +}; + +enum sli4_read_topo_speed { + SLI4_READ_TOPOLOGY_SPEED_NONE = 0x00, + SLI4_READ_TOPOLOGY_SPEED_1G = 0x04, + SLI4_READ_TOPOLOGY_SPEED_2G = 0x08, + SLI4_READ_TOPOLOGY_SPEED_4G = 0x10, + SLI4_READ_TOPOLOGY_SPEED_8G = 0x20, + SLI4_READ_TOPOLOGY_SPEED_10G = 0x40, + SLI4_READ_TOPOLOGY_SPEED_16G = 0x80, + SLI4_READ_TOPOLOGY_SPEED_32G = 0x90, + SLI4_READ_TOPOLOGY_SPEED_64G = 0xa0, + SLI4_READ_TOPOLOGY_SPEED_128G = 0xb0, +}; + +/* REG_FCFI - activate a FC Forwarder */ +struct sli4_cmd_reg_fcfi_rq_cfg { + u8 r_ctl_mask; + u8 r_ctl_match; + u8 type_mask; + u8 type_match; +}; + +enum sli4_regfcfi_tag { + SLI4_REGFCFI_VLAN_TAG = 0xfff, + SLI4_REGFCFI_VLANTAG_VALID = 0x1000, +}; + +#define SLI4_CMD_REG_FCFI_NUM_RQ_CFG 4 +struct sli4_cmd_reg_fcfi { + struct sli4_mbox_command_header hdr; + __le16 fcf_index; + __le16 fcfi; + __le16 rqid1; + __le16 rqid0; + __le16 rqid3; + __le16 rqid2; + struct sli4_cmd_reg_fcfi_rq_cfg + rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]; + __le32 dw8_vlan; +}; + +#define SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG 4 +#define SLI4_CMD_REG_FCFI_MRQ_MAX_NUM_RQ 32 +#define SLI4_CMD_REG_FCFI_SET_FCFI_MODE 0 +#define SLI4_CMD_REG_FCFI_SET_MRQ_MODE 1 + +enum sli4_reg_fcfi_mrq { + SLI4_REGFCFI_MRQ_VLAN_TAG = 0xfff, + SLI4_REGFCFI_MRQ_VLANTAG_VALID = 0x1000, + SLI4_REGFCFI_MRQ_MODE = 0x2000, + + SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS = 0xff, + SLI4_REGFCFI_MRQ_FILTER_BITMASK = 0xf00, + SLI4_REGFCFI_MRQ_RQ_SEL_POLICY = 0xf000, +}; + +struct sli4_cmd_reg_fcfi_mrq { + struct sli4_mbox_command_header hdr; + __le16 fcf_index; + __le16 fcfi; + __le16 rqid1; + __le16 rqid0; + __le16 rqid3; + __le16 rqid2; + struct sli4_cmd_reg_fcfi_rq_cfg + rq_cfg[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG]; + __le32 dw8_vlan; + __le32 dw9_mrqflags; +}; + +struct sli4_cmd_rq_cfg { + __le16 rq_id; + u8 r_ctl_mask; + u8 r_ctl_match; + u8 type_mask; + u8 type_match; +}; + +/* REG_RPI - register a Remote Port Indicator */ +enum sli4_reg_rpi { + SLI4_REGRPI_REMOTE_N_PORTID = 0xffffff, /* DW2 */ + SLI4_REGRPI_UPD = 0x1000000, + SLI4_REGRPI_ETOW = 0x8000000, + SLI4_REGRPI_TERP = 0x20000000, + SLI4_REGRPI_CI = 0x80000000, +}; + +struct sli4_cmd_reg_rpi { + struct sli4_mbox_command_header hdr; + __le16 rpi; + __le16 rsvd2; + __le32 dw2_rportid_flags; + struct sli4_bde bde_64; + __le16 vpi; + __le16 rsvd26; +}; + +#define SLI4_REG_RPI_BUF_LEN 0x70 + +/* REG_VFI - register a Virtual Fabric Indicator */ +enum sli_reg_vfi { + SLI4_REGVFI_VP = 0x1000, /* DW1 */ + SLI4_REGVFI_UPD = 0x2000, + + SLI4_REGVFI_LOCAL_N_PORTID = 0xffffff, /* DW10 */ +}; + +struct sli4_cmd_reg_vfi { + struct sli4_mbox_command_header hdr; + __le16 vfi; + __le16 dw0w1_flags; + __le16 fcfi; + __le16 vpi; + u8 wwpn[8]; + struct sli4_bde sparm; + __le32 e_d_tov; + __le32 r_a_tov; + __le32 dw10_lportid_flags; +}; + +/* REG_VPI - register a Virtual Port Indicator */ +enum sli4_reg_vpi { + SLI4_REGVPI_LOCAL_N_PORTID = 0xffffff, + SLI4_REGVPI_UPD = 0x1000000, +}; + +struct sli4_cmd_reg_vpi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 dw2_lportid_flags; + u8 wwpn[8]; + __le32 rsvd12; + __le16 vpi; + __le16 vfi; +}; + +/* REQUEST_FEATURES - request / query SLI features */ +enum sli4_req_features_flags { + SLI4_REQFEAT_QRY = 0x1, /* Dw1 */ + + SLI4_REQFEAT_IAAB = 1 << 0, /* DW2 & DW3 */ + SLI4_REQFEAT_NPIV = 1 << 1, + SLI4_REQFEAT_DIF = 1 << 2, + SLI4_REQFEAT_VF = 1 << 3, + SLI4_REQFEAT_FCPI = 1 << 4, + SLI4_REQFEAT_FCPT = 1 << 5, + SLI4_REQFEAT_FCPC = 1 << 6, + SLI4_REQFEAT_RSVD = 1 << 7, + SLI4_REQFEAT_RQD = 1 << 8, + SLI4_REQFEAT_IAAR = 1 << 9, + SLI4_REQFEAT_HLM = 1 << 10, + SLI4_REQFEAT_PERFH = 1 << 11, + SLI4_REQFEAT_RXSEQ = 1 << 12, + SLI4_REQFEAT_RXRI = 1 << 13, + SLI4_REQFEAT_DCL2 = 1 << 14, + SLI4_REQFEAT_RSCO = 1 << 15, + SLI4_REQFEAT_MRQP = 1 << 16, +}; + +struct sli4_cmd_request_features { + struct sli4_mbox_command_header hdr; + __le32 dw1_qry; + __le32 cmd; + __le32 resp; +}; + +/* + * SLI_CONFIG - submit a configuration command to Port + * + * Command is either embedded as part of the payload (embed) or located + * in a separate memory buffer (mem) + */ +enum sli4_sli_config { + SLI4_SLICONF_EMB = 0x1, /* DW1 */ + SLI4_SLICONF_PMDCMD_SHIFT = 3, + SLI4_SLICONF_PMDCMD_MASK = 0xf8, + SLI4_SLICONF_PMDCMD_VAL_1 = 8, + SLI4_SLICONF_PMDCNT = 0xf8, + + SLI4_SLICONF_PMD_LEN = 0x00ffffff, +}; + +struct sli4_cmd_sli_config { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 payload_len; + __le32 rsvd12[3]; + union { + u8 embed[58 * sizeof(u32)]; + struct sli4_bufptr mem; + } payload; +}; + +/* READ_STATUS - read tx/rx status of a particular port */ +#define SLI4_READSTATUS_CLEAR_COUNTERS 0x1 + +struct sli4_cmd_read_status { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 rsvd4; + __le32 trans_kbyte_cnt; + __le32 recv_kbyte_cnt; + __le32 trans_frame_cnt; + __le32 recv_frame_cnt; + __le32 trans_seq_cnt; + __le32 recv_seq_cnt; + __le32 tot_exchanges_orig; + __le32 tot_exchanges_resp; + __le32 recv_p_bsy_cnt; + __le32 recv_f_bsy_cnt; + __le32 no_rq_buf_dropped_frames_cnt; + __le32 empty_rq_timeout_cnt; + __le32 no_xri_dropped_frames_cnt; + __le32 empty_xri_pool_cnt; +}; + +/* READ_LNK_STAT - read link status of a particular port */ +enum sli4_read_link_stats_flags { + SLI4_READ_LNKSTAT_REC = 1u << 0, + SLI4_READ_LNKSTAT_GEC = 1u << 1, + SLI4_READ_LNKSTAT_W02OF = 1u << 2, + SLI4_READ_LNKSTAT_W03OF = 1u << 3, + SLI4_READ_LNKSTAT_W04OF = 1u << 4, + SLI4_READ_LNKSTAT_W05OF = 1u << 5, + SLI4_READ_LNKSTAT_W06OF = 1u << 6, + SLI4_READ_LNKSTAT_W07OF = 1u << 7, + SLI4_READ_LNKSTAT_W08OF = 1u << 8, + SLI4_READ_LNKSTAT_W09OF = 1u << 9, + SLI4_READ_LNKSTAT_W10OF = 1u << 10, + SLI4_READ_LNKSTAT_W11OF = 1u << 11, + SLI4_READ_LNKSTAT_W12OF = 1u << 12, + SLI4_READ_LNKSTAT_W13OF = 1u << 13, + SLI4_READ_LNKSTAT_W14OF = 1u << 14, + SLI4_READ_LNKSTAT_W15OF = 1u << 15, + SLI4_READ_LNKSTAT_W16OF = 1u << 16, + SLI4_READ_LNKSTAT_W17OF = 1u << 17, + SLI4_READ_LNKSTAT_W18OF = 1u << 18, + SLI4_READ_LNKSTAT_W19OF = 1u << 19, + SLI4_READ_LNKSTAT_W20OF = 1u << 20, + SLI4_READ_LNKSTAT_W21OF = 1u << 21, + SLI4_READ_LNKSTAT_CLRC = 1u << 30, + SLI4_READ_LNKSTAT_CLOF = 1u << 31, +}; + +struct sli4_cmd_read_link_stats { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 linkfail_errcnt; + __le32 losssync_errcnt; + __le32 losssignal_errcnt; + __le32 primseq_errcnt; + __le32 inval_txword_errcnt; + __le32 crc_errcnt; + __le32 primseq_eventtimeout_cnt; + __le32 elastic_bufoverrun_errcnt; + __le32 arbit_fc_al_timeout_cnt; + __le32 adv_rx_buftor_to_buf_credit; + __le32 curr_rx_buf_to_buf_credit; + __le32 adv_tx_buf_to_buf_credit; + __le32 curr_tx_buf_to_buf_credit; + __le32 rx_eofa_cnt; + __le32 rx_eofdti_cnt; + __le32 rx_eofni_cnt; + __le32 rx_soff_cnt; + __le32 rx_dropped_no_aer_cnt; + __le32 rx_dropped_no_avail_rpi_rescnt; + __le32 rx_dropped_no_avail_xri_rescnt; +}; + +/* Format a WQE with WQ_ID Association performance hint */ +static inline void +sli_set_wq_id_association(void *entry, u16 q_id) +{ + u32 *wqe = entry; + + /* + * Set Word 10, bit 0 to zero + * Set Word 10, bits 15:1 to the WQ ID + */ + wqe[10] &= ~0xffff; + wqe[10] |= q_id << 1; +} + +/* UNREG_FCFI - unregister a FCFI */ +struct sli4_cmd_unreg_fcfi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 fcfi; + __le16 rsvd6; +}; + +/* UNREG_RPI - unregister one or more RPI */ +enum sli4_unreg_rpi { + SLI4_UNREG_RPI_DP = 0x2000, + SLI4_UNREG_RPI_II_SHIFT = 14, + SLI4_UNREG_RPI_II_MASK = 0xc000, + SLI4_UNREG_RPI_II_RPI = 0x0000, + SLI4_UNREG_RPI_II_VPI = 0x4000, + SLI4_UNREG_RPI_II_VFI = 0x8000, + SLI4_UNREG_RPI_II_FCFI = 0xc000, + + SLI4_UNREG_RPI_DEST_N_PORTID_MASK = 0x00ffffff, +}; + +struct sli4_cmd_unreg_rpi { + struct sli4_mbox_command_header hdr; + __le16 index; + __le16 dw1w1_flags; + __le32 dw2_dest_n_portid; +}; + +/* UNREG_VFI - unregister one or more VFI */ +enum sli4_unreg_vfi { + SLI4_UNREG_VFI_II_SHIFT = 14, + SLI4_UNREG_VFI_II_MASK = 0xc000, + SLI4_UNREG_VFI_II_VFI = 0x0000, + SLI4_UNREG_VFI_II_FCFI = 0xc000, +}; + +struct sli4_cmd_unreg_vfi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 index; + __le16 dw2_flags; +}; + +enum sli4_unreg_type { + SLI4_UNREG_TYPE_PORT, + SLI4_UNREG_TYPE_DOMAIN, + SLI4_UNREG_TYPE_FCF, + SLI4_UNREG_TYPE_ALL +}; + +/* UNREG_VPI - unregister one or more VPI */ +enum sli4_unreg_vpi { + SLI4_UNREG_VPI_II_SHIFT = 14, + SLI4_UNREG_VPI_II_MASK = 0xc000, + SLI4_UNREG_VPI_II_VPI = 0x0000, + SLI4_UNREG_VPI_II_VFI = 0x8000, + SLI4_UNREG_VPI_II_FCFI = 0xc000, +}; + +struct sli4_cmd_unreg_vpi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 index; + __le16 dw2w0_flags; +}; + +/* AUTO_XFER_RDY - Configure the auto-generate XFER-RDY feature */ +struct sli4_cmd_config_auto_xfer_rdy { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 max_burst_len; +}; + +#define SLI4_CONFIG_AUTO_XFERRDY_BLKSIZE 0xffff + +struct sli4_cmd_config_auto_xfer_rdy_hp { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 max_burst_len; + __le32 dw3_esoc_flags; + __le16 block_size; + __le16 rsvd14; +}; + +/************************************************************************* + * SLI-4 common configuration command formats and definitions + */ + +/* + * Subsystem values. + */ +enum sli4_subsystem { + SLI4_SUBSYSTEM_COMMON = 0x01, + SLI4_SUBSYSTEM_LOWLEVEL = 0x0b, + SLI4_SUBSYSTEM_FC = 0x0c, + SLI4_SUBSYSTEM_DMTF = 0x11, +}; + +#define SLI4_OPC_LOWLEVEL_SET_WATCHDOG 0X36 + +/* + * Common opcode (OPC) values. + */ +enum sli4_cmn_opcode { + SLI4_CMN_FUNCTION_RESET = 0x3d, + SLI4_CMN_CREATE_CQ = 0x0c, + SLI4_CMN_CREATE_CQ_SET = 0x1d, + SLI4_CMN_DESTROY_CQ = 0x36, + SLI4_CMN_MODIFY_EQ_DELAY = 0x29, + SLI4_CMN_CREATE_EQ = 0x0d, + SLI4_CMN_DESTROY_EQ = 0x37, + SLI4_CMN_CREATE_MQ_EXT = 0x5a, + SLI4_CMN_DESTROY_MQ = 0x35, + SLI4_CMN_GET_CNTL_ATTRIBUTES = 0x20, + SLI4_CMN_NOP = 0x21, + SLI4_CMN_GET_RSC_EXTENT_INFO = 0x9a, + SLI4_CMN_GET_SLI4_PARAMS = 0xb5, + SLI4_CMN_QUERY_FW_CONFIG = 0x3a, + SLI4_CMN_GET_PORT_NAME = 0x4d, + + SLI4_CMN_WRITE_FLASHROM = 0x07, + /* TRANSCEIVER Data */ + SLI4_CMN_READ_TRANS_DATA = 0x49, + SLI4_CMN_GET_CNTL_ADDL_ATTRS = 0x79, + SLI4_CMN_GET_FUNCTION_CFG = 0xa0, + SLI4_CMN_GET_PROFILE_CFG = 0xa4, + SLI4_CMN_SET_PROFILE_CFG = 0xa5, + SLI4_CMN_GET_PROFILE_LIST = 0xa6, + SLI4_CMN_GET_ACTIVE_PROFILE = 0xa7, + SLI4_CMN_SET_ACTIVE_PROFILE = 0xa8, + SLI4_CMN_READ_OBJECT = 0xab, + SLI4_CMN_WRITE_OBJECT = 0xac, + SLI4_CMN_DELETE_OBJECT = 0xae, + SLI4_CMN_READ_OBJECT_LIST = 0xad, + SLI4_CMN_SET_DUMP_LOCATION = 0xb8, + SLI4_CMN_SET_FEATURES = 0xbf, + SLI4_CMN_GET_RECFG_LINK_INFO = 0xc9, + SLI4_CMN_SET_RECNG_LINK_ID = 0xca, +}; + +/* DMTF opcode (OPC) values */ +#define DMTF_EXEC_CLP_CMD 0x01 + +/* + * COMMON_FUNCTION_RESET + * + * Resets the Port, returning it to a power-on state. This configuration + * command does not have a payload and should set/expect the lengths to + * be zero. + */ +struct sli4_rqst_cmn_function_reset { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_function_reset { + struct sli4_rsp_hdr hdr; +}; + +/* + * COMMON_GET_CNTL_ATTRIBUTES + * + * Query for information about the SLI Port + */ +enum sli4_cntrl_attr_flags { + SLI4_CNTL_ATTR_PORTNUM = 0x3f, + SLI4_CNTL_ATTR_PORTTYPE = 0xc0, +}; + +struct sli4_rsp_cmn_get_cntl_attributes { + struct sli4_rsp_hdr hdr; + u8 version_str[32]; + u8 manufacturer_name[32]; + __le32 supported_modes; + u8 eprom_version_lo; + u8 eprom_version_hi; + __le16 rsvd17; + __le32 mbx_ds_version; + __le32 ep_fw_ds_version; + u8 ncsi_version_str[12]; + __le32 def_extended_timeout; + u8 model_number[32]; + u8 description[64]; + u8 serial_number[32]; + u8 ip_version_str[32]; + u8 fw_version_str[32]; + u8 bios_version_str[32]; + u8 redboot_version_str[32]; + u8 driver_version_str[32]; + u8 fw_on_flash_version_str[32]; + __le32 functionalities_supported; + __le16 max_cdb_length; + u8 asic_revision; + u8 generational_guid0; + __le32 generational_guid1_12[3]; + __le16 generational_guid13_14; + u8 generational_guid15; + u8 hba_port_count; + __le16 default_link_down_timeout; + u8 iscsi_version_min_max; + u8 multifunctional_device; + u8 cache_valid; + u8 hba_status; + u8 max_domains_supported; + u8 port_num_type_flags; + __le32 firmware_post_status; + __le32 hba_mtu; + u8 iscsi_features; + u8 rsvd121[3]; + __le16 pci_vendor_id; + __le16 pci_device_id; + __le16 pci_sub_vendor_id; + __le16 pci_sub_system_id; + u8 pci_bus_number; + u8 pci_device_number; + u8 pci_function_number; + u8 interface_type; + __le64 unique_identifier; + u8 number_of_netfilters; + u8 rsvd122[3]; +}; + +/* + * COMMON_GET_CNTL_ATTRIBUTES + * + * This command queries the controller information from the Flash ROM. + */ +struct sli4_rqst_cmn_get_cntl_addl_attributes { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_cntl_addl_attributes { + struct sli4_rsp_hdr hdr; + __le16 ipl_file_number; + u8 ipl_file_version; + u8 rsvd4; + u8 on_die_temperature; + u8 rsvd5[3]; + __le32 driver_advanced_features_supported; + __le32 rsvd7[4]; + char universal_bios_version[32]; + char x86_bios_version[32]; + char efi_bios_version[32]; + char fcode_version[32]; + char uefi_bios_version[32]; + char uefi_nic_version[32]; + char uefi_fcode_version[32]; + char uefi_iscsi_version[32]; + char iscsi_x86_bios_version[32]; + char pxe_x86_bios_version[32]; + u8 default_wwpn[8]; + u8 ext_phy_version[32]; + u8 fc_universal_bios_version[32]; + u8 fc_x86_bios_version[32]; + u8 fc_efi_bios_version[32]; + u8 fc_fcode_version[32]; + u8 ext_phy_crc_label[8]; + u8 ipl_file_name[16]; + u8 rsvd139[72]; +}; + +/* + * COMMON_NOP + * + * This command does not do anything; it only returns + * the payload in the completion. + */ +struct sli4_rqst_cmn_nop { + struct sli4_rqst_hdr hdr; + __le32 context[2]; +}; + +struct sli4_rsp_cmn_nop { + struct sli4_rsp_hdr hdr; + __le32 context[2]; +}; + +struct sli4_rqst_cmn_get_resource_extent_info { + struct sli4_rqst_hdr hdr; + __le16 resource_type; + __le16 rsvd16; +}; + +enum sli4_rsc_type { + SLI4_RSC_TYPE_VFI = 0x20, + SLI4_RSC_TYPE_VPI = 0x21, + SLI4_RSC_TYPE_RPI = 0x22, + SLI4_RSC_TYPE_XRI = 0x23, +}; + +struct sli4_rsp_cmn_get_resource_extent_info { + struct sli4_rsp_hdr hdr; + __le16 resource_extent_count; + __le16 resource_extent_size; +}; + +#define SLI4_128BYTE_WQE_SUPPORT 0x02 + +#define GET_Q_CNT_METHOD(m) \ + (((m) & SLI4_PARAM_Q_CNT_MTHD_MASK) >> SLI4_PARAM_Q_CNT_MTHD_SHFT) +#define GET_Q_CREATE_VERSION(v) \ + (((v) & SLI4_PARAM_QV_MASK) >> SLI4_PARAM_QV_SHIFT) + +enum sli4_rsp_get_params_e { + /*GENERIC*/ + SLI4_PARAM_Q_CNT_MTHD_SHFT = 24, + SLI4_PARAM_Q_CNT_MTHD_MASK = 0xf << 24, + SLI4_PARAM_QV_SHIFT = 14, + SLI4_PARAM_QV_MASK = 3 << 14, + + /* DW4 */ + SLI4_PARAM_PROTO_TYPE_MASK = 0xff, + /* DW5 */ + SLI4_PARAM_FT = 1 << 0, + SLI4_PARAM_SLI_REV_MASK = 0xf << 4, + SLI4_PARAM_SLI_FAM_MASK = 0xf << 8, + SLI4_PARAM_IF_TYPE_MASK = 0xf << 12, + SLI4_PARAM_SLI_HINT1_MASK = 0xff << 16, + SLI4_PARAM_SLI_HINT2_MASK = 0x1f << 24, + /* DW6 */ + SLI4_PARAM_EQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_EQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_EQ_PAGE_SZS_MASK = 0xff << 16, + /* DW8 */ + SLI4_PARAM_CQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_CQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_CQ_PAGE_SZS_MASK = 0xff << 16, + /* DW10 */ + SLI4_PARAM_MQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_MQ_PAGE_SZS_MASK = 0xff << 16, + /* DW12 */ + SLI4_PARAM_WQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_WQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_WQ_PAGE_SZS_MASK = 0xff << 16, + /* DW14 */ + SLI4_PARAM_RQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_RQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_RQ_PAGE_SZS_MASK = 0xff << 16, + /* DW15W1*/ + SLI4_PARAM_RQ_DB_WINDOW_MASK = 0xf000, + /* DW16 */ + SLI4_PARAM_FC = 1 << 0, + SLI4_PARAM_EXT = 1 << 1, + SLI4_PARAM_HDRR = 1 << 2, + SLI4_PARAM_SGLR = 1 << 3, + SLI4_PARAM_FBRR = 1 << 4, + SLI4_PARAM_AREG = 1 << 5, + SLI4_PARAM_TGT = 1 << 6, + SLI4_PARAM_TERP = 1 << 7, + SLI4_PARAM_ASSI = 1 << 8, + SLI4_PARAM_WCHN = 1 << 9, + SLI4_PARAM_TCCA = 1 << 10, + SLI4_PARAM_TRTY = 1 << 11, + SLI4_PARAM_TRIR = 1 << 12, + SLI4_PARAM_PHOFF = 1 << 13, + SLI4_PARAM_PHON = 1 << 14, + SLI4_PARAM_PHWQ = 1 << 15, + SLI4_PARAM_BOUND_4GA = 1 << 16, + SLI4_PARAM_RXC = 1 << 17, + SLI4_PARAM_HLM = 1 << 18, + SLI4_PARAM_IPR = 1 << 19, + SLI4_PARAM_RXRI = 1 << 20, + SLI4_PARAM_SGLC = 1 << 21, + SLI4_PARAM_TIMM = 1 << 22, + SLI4_PARAM_TSMM = 1 << 23, + SLI4_PARAM_OAS = 1 << 25, + SLI4_PARAM_LC = 1 << 26, + SLI4_PARAM_AGXF = 1 << 27, + SLI4_PARAM_LOOPBACK_MASK = 0xf << 28, + /* DW18 */ + SLI4_PARAM_SGL_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_SGL_PAGE_SZS_MASK = 0xff << 8, + SLI4_PARAM_SGL_PP_ALIGN_MASK = 0xff << 16, +}; + +struct sli4_rqst_cmn_get_sli4_params { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_sli4_params { + struct sli4_rsp_hdr hdr; + __le32 dw4_protocol_type; + __le32 dw5_sli; + __le32 dw6_eq_page_cnt; + __le16 eqe_count_mask; + __le16 rsvd26; + __le32 dw8_cq_page_cnt; + __le16 cqe_count_mask; + __le16 rsvd34; + __le32 dw10_mq_page_cnt; + __le16 mqe_count_mask; + __le16 rsvd42; + __le32 dw12_wq_page_cnt; + __le16 wqe_count_mask; + __le16 rsvd50; + __le32 dw14_rq_page_cnt; + __le16 rqe_count_mask; + __le16 dw15w1_rq_db_window; + __le32 dw16_loopback_scope; + __le32 sge_supported_length; + __le32 dw18_sgl_page_cnt; + __le16 min_rq_buffer_size; + __le16 rsvd75; + __le32 max_rq_buffer_size; + __le16 physical_xri_max; + __le16 physical_rpi_max; + __le16 physical_vpi_max; + __le16 physical_vfi_max; + __le32 rsvd88; + __le16 frag_num_field_offset; + __le16 frag_num_field_size; + __le16 sgl_index_field_offset; + __le16 sgl_index_field_size; + __le32 chain_sge_initial_value_lo; + __le32 chain_sge_initial_value_hi; +}; + +/*Port Types*/ +enum sli4_port_types { + SLI4_PORT_TYPE_ETH = 0, + SLI4_PORT_TYPE_FC = 1, +}; + +struct sli4_rqst_cmn_get_port_name { + struct sli4_rqst_hdr hdr; + u8 port_type; + u8 rsvd4[3]; +}; + +struct sli4_rsp_cmn_get_port_name { + struct sli4_rsp_hdr hdr; + char port_name[4]; +}; + +struct sli4_rqst_cmn_write_flashrom { + struct sli4_rqst_hdr hdr; + __le32 flash_rom_access_opcode; + __le32 flash_rom_access_operation_type; + __le32 data_buffer_size; + __le32 offset; + u8 data_buffer[4]; +}; + +/* + * COMMON_READ_TRANSCEIVER_DATA + * + * This command reads SFF transceiver data(Format is defined + * by the SFF-8472 specification). + */ +struct sli4_rqst_cmn_read_transceiver_data { + struct sli4_rqst_hdr hdr; + __le32 page_number; + __le32 port; +}; + +struct sli4_rsp_cmn_read_transceiver_data { + struct sli4_rsp_hdr hdr; + __le32 page_number; + __le32 port; + u8 page_data[128]; + u8 page_data_2[128]; +}; + +#define SLI4_REQ_DESIRE_READLEN 0xffffff + +struct sli4_rqst_cmn_read_object { + struct sli4_rqst_hdr hdr; + __le32 desired_read_length_dword; + __le32 read_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[]; +}; + +#define RSP_COM_READ_OBJ_EOF 0x80000000 + +struct sli4_rsp_cmn_read_object { + struct sli4_rsp_hdr hdr; + __le32 actual_read_length; + __le32 eof_dword; +}; + +enum sli4_rqst_write_object_flags { + SLI4_RQ_DES_WRITE_LEN = 0xffffff, + SLI4_RQ_DES_WRITE_LEN_NOC = 0x40000000, + SLI4_RQ_DES_WRITE_LEN_EOF = 0x80000000, +}; + +struct sli4_rqst_cmn_write_object { + struct sli4_rqst_hdr hdr; + __le32 desired_write_len_dword; + __le32 write_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[]; +}; + +#define RSP_CHANGE_STATUS 0xff + +struct sli4_rsp_cmn_write_object { + struct sli4_rsp_hdr hdr; + __le32 actual_write_length; + __le32 change_status_dword; +}; + +struct sli4_rqst_cmn_delete_object { + struct sli4_rqst_hdr hdr; + __le32 rsvd4; + __le32 rsvd5; + u8 object_name[104]; +}; + +#define SLI4_RQ_OBJ_LIST_READ_LEN 0xffffff + +struct sli4_rqst_cmn_read_object_list { + struct sli4_rqst_hdr hdr; + __le32 desired_read_length_dword; + __le32 read_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[]; +}; + +enum sli4_rqst_set_dump_flags { + SLI4_CMN_SET_DUMP_BUFFER_LEN = 0xffffff, + SLI4_CMN_SET_DUMP_FDB = 0x20000000, + SLI4_CMN_SET_DUMP_BLP = 0x40000000, + SLI4_CMN_SET_DUMP_QRY = 0x80000000, +}; + +struct sli4_rqst_cmn_set_dump_location { + struct sli4_rqst_hdr hdr; + __le32 buffer_length_dword; + __le32 buf_addr_low; + __le32 buf_addr_high; +}; + +struct sli4_rsp_cmn_set_dump_location { + struct sli4_rsp_hdr hdr; + __le32 buffer_length_dword; +}; + +enum sli4_dump_level { + SLI4_DUMP_LEVEL_NONE, + SLI4_CHIP_LEVEL_DUMP, + SLI4_FUNC_DESC_DUMP, +}; + +enum sli4_dump_state { + SLI4_DUMP_STATE_NONE, + SLI4_CHIP_DUMP_STATE_VALID, + SLI4_FUNC_DUMP_STATE_VALID, +}; + +enum sli4_dump_status { + SLI4_DUMP_READY_STATUS_NOT_READY, + SLI4_DUMP_READY_STATUS_DD_PRESENT, + SLI4_DUMP_READY_STATUS_FDB_PRESENT, + SLI4_DUMP_READY_STATUS_SKIP_DUMP, + SLI4_DUMP_READY_STATUS_FAILED = -1, +}; + +enum sli4_set_features { + SLI4_SET_FEATURES_DIF_SEED = 0x01, + SLI4_SET_FEATURES_XRI_TIMER = 0x03, + SLI4_SET_FEATURES_MAX_PCIE_SPEED = 0x04, + SLI4_SET_FEATURES_FCTL_CHECK = 0x05, + SLI4_SET_FEATURES_FEC = 0x06, + SLI4_SET_FEATURES_PCIE_RECV_DETECT = 0x07, + SLI4_SET_FEATURES_DIF_MEMORY_MODE = 0x08, + SLI4_SET_FEATURES_DISABLE_SLI_PORT_PAUSE_STATE = 0x09, + SLI4_SET_FEATURES_ENABLE_PCIE_OPTIONS = 0x0a, + SLI4_SET_FEAT_CFG_AUTO_XFER_RDY_T10PI = 0x0c, + SLI4_SET_FEATURES_ENABLE_MULTI_RECEIVE_QUEUE = 0x0d, + SLI4_SET_FEATURES_SET_FTD_XFER_HINT = 0x0f, + SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK = 0x11, +}; + +struct sli4_rqst_cmn_set_features { + struct sli4_rqst_hdr hdr; + __le32 feature; + __le32 param_len; + __le32 params[8]; +}; + +struct sli4_rqst_cmn_set_features_dif_seed { + __le16 seed; + __le16 rsvd16; +}; + +enum sli4_rqst_set_mrq_features { + SLI4_RQ_MULTIRQ_ISR = 0x1, + SLI4_RQ_MULTIRQ_AUTOGEN_XFER_RDY = 0x2, + + SLI4_RQ_MULTIRQ_NUM_RQS = 0xff, + SLI4_RQ_MULTIRQ_RQ_SELECT = 0xf00, +}; + +struct sli4_rqst_cmn_set_features_multirq { + __le32 auto_gen_xfer_dword; + __le32 num_rqs_dword; +}; + +enum sli4_rqst_health_check_flags { + SLI4_RQ_HEALTH_CHECK_ENABLE = 0x1, + SLI4_RQ_HEALTH_CHECK_QUERY = 0x2, +}; + +struct sli4_rqst_cmn_set_features_health_check { + __le32 health_check_dword; +}; + +struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint { + __le32 fdt_xfer_hint; +}; + +struct sli4_rqst_dmtf_exec_clp_cmd { + struct sli4_rqst_hdr hdr; + __le32 cmd_buf_length; + __le32 resp_buf_length; + __le32 cmd_buf_addr_low; + __le32 cmd_buf_addr_high; + __le32 resp_buf_addr_low; + __le32 resp_buf_addr_high; +}; + +struct sli4_rsp_dmtf_exec_clp_cmd { + struct sli4_rsp_hdr hdr; + __le32 rsvd4; + __le32 resp_length; + __le32 rsvd6; + __le32 rsvd7; + __le32 rsvd8; + __le32 rsvd9; + __le32 clp_status; + __le32 clp_detailed_status; +}; + +#define SLI4_PROTOCOL_FC 0x10 +#define SLI4_PROTOCOL_DEFAULT 0xff + +struct sli4_rspource_descriptor_v1 { + u8 descriptor_type; + u8 descriptor_length; + __le16 rsvd16; + __le32 type_specific[]; +}; + +enum sli4_pcie_desc_flags { + SLI4_PCIE_DESC_IMM = 0x4000, + SLI4_PCIE_DESC_NOSV = 0x8000, + + SLI4_PCIE_DESC_PF_NO = 0x3ff0000, + + SLI4_PCIE_DESC_MISSN_ROLE = 0xff, + SLI4_PCIE_DESC_PCHG = 0x8000000, + SLI4_PCIE_DESC_SCHG = 0x10000000, + SLI4_PCIE_DESC_XCHG = 0x20000000, + SLI4_PCIE_DESC_XROM = 0xc0000000 +}; + +struct sli4_pcie_resource_descriptor_v1 { + u8 descriptor_type; + u8 descriptor_length; + __le16 imm_nosv_dword; + __le32 pf_number_dword; + __le32 rsvd3; + u8 sriov_state; + u8 pf_state; + u8 pf_type; + u8 rsvd4; + __le16 number_of_vfs; + __le16 rsvd5; + __le32 mission_roles_dword; + __le32 rsvd7[16]; +}; + +struct sli4_rqst_cmn_get_function_config { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_function_config { + struct sli4_rsp_hdr hdr; + __le32 desc_count; + __le32 desc[54]; +}; + +/* Link Config Descriptor for link config functions */ +struct sli4_link_config_descriptor { + u8 link_config_id; + u8 rsvd1[3]; + __le32 config_description[8]; +}; + +#define MAX_LINK_DES 10 + +struct sli4_rqst_cmn_get_reconfig_link_info { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_reconfig_link_info { + struct sli4_rsp_hdr hdr; + u8 active_link_config_id; + u8 rsvd17; + u8 next_link_config_id; + u8 rsvd19; + __le32 link_configuration_descriptor_count; + struct sli4_link_config_descriptor + desc[MAX_LINK_DES]; +}; + +enum sli4_set_reconfig_link_flags { + SLI4_SET_RECONFIG_LINKID_NEXT = 0xff, + SLI4_SET_RECONFIG_LINKID_FD = 1u << 31, +}; + +struct sli4_rqst_cmn_set_reconfig_link_id { + struct sli4_rqst_hdr hdr; + __le32 dw4_flags; +}; + +struct sli4_rsp_cmn_set_reconfig_link_id { + struct sli4_rsp_hdr hdr; +}; + +struct sli4_rqst_lowlevel_set_watchdog { + struct sli4_rqst_hdr hdr; + __le16 watchdog_timeout; + __le16 rsvd18; +}; + +struct sli4_rsp_lowlevel_set_watchdog { + struct sli4_rsp_hdr hdr; + __le32 rsvd; +}; + +/* FC opcode (OPC) values */ +enum sli4_fc_opcodes { + SLI4_OPC_WQ_CREATE = 0x1, + SLI4_OPC_WQ_DESTROY = 0x2, + SLI4_OPC_POST_SGL_PAGES = 0x3, + SLI4_OPC_RQ_CREATE = 0x5, + SLI4_OPC_RQ_DESTROY = 0x6, + SLI4_OPC_READ_FCF_TABLE = 0x8, + SLI4_OPC_POST_HDR_TEMPLATES = 0xb, + SLI4_OPC_REDISCOVER_FCF = 0x10, +}; + +/* Use the default CQ associated with the WQ */ +#define SLI4_CQ_DEFAULT 0xffff + +/* + * POST_SGL_PAGES + * + * Register the scatter gather list (SGL) memory and + * associate it with an XRI. + */ +struct sli4_rqst_post_sgl_pages { + struct sli4_rqst_hdr hdr; + __le16 xri_start; + __le16 xri_count; + struct { + __le32 page0_low; + __le32 page0_high; + __le32 page1_low; + __le32 page1_high; + } page_set[10]; +}; + +struct sli4_rsp_post_sgl_pages { + struct sli4_rsp_hdr hdr; +}; + +struct sli4_rqst_post_hdr_templates { + struct sli4_rqst_hdr hdr; + __le16 rpi_offset; + __le16 page_count; + struct sli4_dmaaddr page_descriptor[]; +}; + +#define SLI4_HDR_TEMPLATE_SIZE 64 + +enum sli4_io_flags { +/* The XRI associated with this IO is already active */ + SLI4_IO_CONTINUATION = 1 << 0, +/* Automatically generate a good RSP frame */ + SLI4_IO_AUTO_GOOD_RESPONSE = 1 << 1, + SLI4_IO_NO_ABORT = 1 << 2, +/* Set the DNRX bit because no auto xref rdy buffer is posted */ + SLI4_IO_DNRX = 1 << 3, +}; + +enum sli4_callback { + SLI4_CB_LINK, + SLI4_CB_MAX, +}; + +enum sli4_link_status { + SLI4_LINK_STATUS_UP, + SLI4_LINK_STATUS_DOWN, + SLI4_LINK_STATUS_NO_ALPA, + SLI4_LINK_STATUS_MAX, +}; + +enum sli4_link_topology { + SLI4_LINK_TOPO_NON_FC_AL = 1, + SLI4_LINK_TOPO_FC_AL, + SLI4_LINK_TOPO_LOOPBACK_INTERNAL, + SLI4_LINK_TOPO_LOOPBACK_EXTERNAL, + SLI4_LINK_TOPO_NONE, + SLI4_LINK_TOPO_MAX, +}; + +enum sli4_link_medium { + SLI4_LINK_MEDIUM_ETHERNET, + SLI4_LINK_MEDIUM_FC, + SLI4_LINK_MEDIUM_MAX, +}; +/******Driver specific structures******/ + +struct sli4_queue { + /* Common to all queue types */ + struct efc_dma dma; + spinlock_t lock; /* Lock to protect the doorbell register + * writes and queue reads + */ + u32 index; /* current host entry index */ + u16 size; /* entry size */ + u16 length; /* number of entries */ + u16 n_posted; /* number entries posted for CQ, EQ */ + u16 id; /* Port assigned xQ_ID */ + u8 type; /* queue type ie EQ, CQ, ... */ + void __iomem *db_regaddr; /* register address for the doorbell */ + u16 phase; /* For if_type = 6, this value toggle + * for each iteration of the queue, + * a queue entry is valid when a cqe + * valid bit matches this value + */ + u32 proc_limit; /* limit CQE processed per iteration */ + u32 posted_limit; /* CQE/EQE process before ring db */ + u32 max_num_processed; + u64 max_process_time; + union { + u32 r_idx; /* "read" index (MQ only) */ + u32 flag; + } u; +}; + +/* Parameters used to populate WQE*/ +struct sli_bls_params { + u32 s_id; + u32 d_id; + u16 ox_id; + u16 rx_id; + u32 rpi; + u32 vpi; + bool rpi_registered; + u8 payload[12]; + u16 xri; + u16 tag; +}; + +struct sli_els_params { + u32 s_id; + u32 d_id; + u16 ox_id; + u32 rpi; + u32 vpi; + bool rpi_registered; + u32 xmit_len; + u32 rsp_len; + u8 timeout; + u8 cmd; + u16 xri; + u16 tag; +}; + +struct sli_ct_params { + u8 r_ctl; + u8 type; + u8 df_ctl; + u8 timeout; + u16 ox_id; + u32 d_id; + u32 rpi; + u32 vpi; + bool rpi_registered; + u32 xmit_len; + u32 rsp_len; + u16 xri; + u16 tag; +}; + +struct sli_fcp_tgt_params { + u32 s_id; + u32 d_id; + u32 rpi; + u32 vpi; + u32 offset; + u16 ox_id; + u16 flags; + u8 cs_ctl; + u8 timeout; + u32 app_id; + u32 xmit_len; + u16 xri; + u16 tag; +}; + +struct sli4_link_event { + enum sli4_link_status status; + enum sli4_link_topology topology; + enum sli4_link_medium medium; + u32 speed; + u8 *loop_map; + u32 fc_id; +}; + +enum sli4_resource { + SLI4_RSRC_VFI, + SLI4_RSRC_VPI, + SLI4_RSRC_RPI, + SLI4_RSRC_XRI, + SLI4_RSRC_FCFI, + SLI4_RSRC_MAX, +}; + +struct sli4_extent { + u32 number; + u32 size; + u32 n_alloc; + u32 *base; + unsigned long *use_map; + u32 map_size; +}; + +struct sli4_queue_info { + u16 max_qcount[SLI4_QTYPE_MAX]; + u32 max_qentries[SLI4_QTYPE_MAX]; + u16 count_mask[SLI4_QTYPE_MAX]; + u16 count_method[SLI4_QTYPE_MAX]; + u32 qpage_count[SLI4_QTYPE_MAX]; +}; + +struct sli4_params { + u8 has_extents; + u8 auto_reg; + u8 auto_xfer_rdy; + u8 hdr_template_req; + u8 perf_hint; + u8 perf_wq_id_association; + u8 cq_create_version; + u8 mq_create_version; + u8 high_login_mode; + u8 sgl_pre_registered; + u8 sgl_pre_reg_required; + u8 t10_dif_inline_capable; + u8 t10_dif_separate_capable; +}; + +struct sli4 { + void *os; + struct pci_dev *pci; + void __iomem *reg[PCI_STD_NUM_BARS]; + + u32 sli_rev; + u32 sli_family; + u32 if_type; + + u16 asic_type; + u16 asic_rev; + + u16 e_d_tov; + u16 r_a_tov; + struct sli4_queue_info qinfo; + u16 link_module_type; + u8 rq_batch; + u8 port_number; + char port_name[2]; + u16 rq_min_buf_size; + u32 rq_max_buf_size; + u8 topology; + u8 wwpn[8]; + u8 wwnn[8]; + u32 fw_rev[2]; + u8 fw_name[2][16]; + char ipl_name[16]; + u32 hw_rev[3]; + char modeldesc[64]; + char bios_version_string[32]; + u32 wqe_size; + u32 vpd_length; + /* + * Tracks the port resources using extents metaphor. For + * devices that don't implement extents (i.e. + * has_extents == FALSE), the code models each resource as + * a single large extent. + */ + struct sli4_extent ext[SLI4_RSRC_MAX]; + u32 features; + struct sli4_params params; + u32 sge_supported_length; + u32 sgl_page_sizes; + u32 max_sgl_pages; + + /* + * Callback functions + */ + int (*link)(void *ctx, void *event); + void *link_arg; + + struct efc_dma bmbx; + + /* Save pointer to physical memory descriptor for non-embedded + * SLI_CONFIG commands for BMBX dumping purposes + */ + struct efc_dma *bmbx_non_emb_pmd; + + struct efc_dma vpd_data; +}; + +static inline void +sli_cmd_fill_hdr(struct sli4_rqst_hdr *hdr, u8 opc, u8 sub, u32 ver, __le32 len) +{ + hdr->opcode = opc; + hdr->subsystem = sub; + hdr->dw3_version = cpu_to_le32(ver); + hdr->request_length = len; +} + +/** + * Get / set parameter functions + */ + +static inline u32 +sli_get_max_sge(struct sli4 *sli4) +{ + return sli4->sge_supported_length; +} + +static inline u32 +sli_get_max_sgl(struct sli4 *sli4) +{ + if (sli4->sgl_page_sizes != 1) { + efc_log_err(sli4, "unsupported SGL page sizes %#x\n", + sli4->sgl_page_sizes); + return 0; + } + + return (sli4->max_sgl_pages * SLI_PAGE_SIZE) / sizeof(struct sli4_sge); +} + +static inline enum sli4_link_medium +sli_get_medium(struct sli4 *sli4) +{ + switch (sli4->topology) { + case SLI4_READ_CFG_TOPO_FC: + case SLI4_READ_CFG_TOPO_FC_AL: + case SLI4_READ_CFG_TOPO_NON_FC_AL: + return SLI4_LINK_MEDIUM_FC; + default: + return SLI4_LINK_MEDIUM_MAX; + } +} + +static inline u32 +sli_get_lmt(struct sli4 *sli4) +{ + return sli4->link_module_type; +} + +static inline int +sli_set_topology(struct sli4 *sli4, u32 value) +{ + int rc = 0; + + switch (value) { + case SLI4_READ_CFG_TOPO_FC: + case SLI4_READ_CFG_TOPO_FC_AL: + case SLI4_READ_CFG_TOPO_NON_FC_AL: + sli4->topology = value; + break; + default: + efc_log_err(sli4, "unsupported topology %#x\n", value); + rc = -1; + } + + return rc; +} + +static inline u32 +sli_convert_mask_to_count(u32 method, u32 mask) +{ + u32 count = 0; + + if (method) { + count = 1 << (31 - __builtin_clz(mask)); + count *= 16; + } else { + count = mask; + } + + return count; +} + +static inline u32 +sli_reg_read_status(struct sli4 *sli) +{ + return readl(sli->reg[0] + SLI4_PORT_STATUS_REGOFF); +} + +static inline int +sli_fw_error_status(struct sli4 *sli4) +{ + return (sli_reg_read_status(sli4) & SLI4_PORT_STATUS_ERR) ? 1 : 0; +} + +static inline u32 +sli_reg_read_err1(struct sli4 *sli) +{ + return readl(sli->reg[0] + SLI4_PORT_ERROR1); +} + +static inline u32 +sli_reg_read_err2(struct sli4 *sli) +{ + return readl(sli->reg[0] + SLI4_PORT_ERROR2); +} + +static inline int +sli_fc_rqe_length(struct sli4 *sli4, void *cqe, u32 *len_hdr, + u32 *len_data) +{ + struct sli4_fc_async_rcqe *rcqe = cqe; + + *len_hdr = *len_data = 0; + + if (rcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + *len_hdr = rcqe->hdpl_byte & SLI4_RACQE_HDPL; + *len_data = le16_to_cpu(rcqe->data_placement_length); + return 0; + } else { + return -1; + } +} + +static inline u8 +sli_fc_rqe_fcfi(struct sli4 *sli4, void *cqe) +{ + u8 code = ((u8 *)cqe)[SLI4_CQE_CODE_OFFSET]; + u8 fcfi = U8_MAX; + + switch (code) { + case SLI4_CQE_CODE_RQ_ASYNC: { + struct sli4_fc_async_rcqe *rcqe = cqe; + + fcfi = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_FCFI; + break; + } + case SLI4_CQE_CODE_RQ_ASYNC_V1: { + struct sli4_fc_async_rcqe_v1 *rcqev1 = cqe; + + fcfi = rcqev1->fcfi_byte & SLI4_RACQE_FCFI; + break; + } + case SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD: { + struct sli4_fc_optimized_write_cmd_cqe *opt_wr = cqe; + + fcfi = opt_wr->flags0 & SLI4_OCQE_FCFI; + break; + } + } + + return fcfi; +} + +/**************************************************************************** + * Function prototypes + */ +int +sli_cmd_config_link(struct sli4 *sli4, void *buf); +int +sli_cmd_down_link(struct sli4 *sli4, void *buf); +int +sli_cmd_dump_type4(struct sli4 *sli4, void *buf, u16 wki); +int +sli_cmd_common_read_transceiver_data(struct sli4 *sli4, void *buf, + u32 page_num, struct efc_dma *dma); +int +sli_cmd_read_link_stats(struct sli4 *sli4, void *buf, u8 req_stats, + u8 clear_overflow_flags, u8 clear_all_counters); +int +sli_cmd_read_status(struct sli4 *sli4, void *buf, u8 clear); +int +sli_cmd_init_link(struct sli4 *sli4, void *buf, u32 speed, + u8 reset_alpa); +int +sli_cmd_init_vfi(struct sli4 *sli4, void *buf, u16 vfi, u16 fcfi, + u16 vpi); +int +sli_cmd_init_vpi(struct sli4 *sli4, void *buf, u16 vpi, u16 vfi); +int +sli_cmd_post_xri(struct sli4 *sli4, void *buf, u16 base, u16 cnt); +int +sli_cmd_release_xri(struct sli4 *sli4, void *buf, u8 num_xri); +int +sli_cmd_read_sparm64(struct sli4 *sli4, void *buf, + struct efc_dma *dma, u16 vpi); +int +sli_cmd_read_topology(struct sli4 *sli4, void *buf, struct efc_dma *dma); +int +sli_cmd_read_nvparms(struct sli4 *sli4, void *buf); +int +sli_cmd_write_nvparms(struct sli4 *sli4, void *buf, u8 *wwpn, + u8 *wwnn, u8 hard_alpa, u32 preferred_d_id); +int +sli_cmd_reg_fcfi(struct sli4 *sli4, void *buf, u16 index, + struct sli4_cmd_rq_cfg *rq_cfg); +int +sli_cmd_reg_fcfi_mrq(struct sli4 *sli4, void *buf, u8 mode, u16 index, + u8 rq_selection_policy, u8 mrq_bit_mask, u16 num_mrqs, + struct sli4_cmd_rq_cfg *rq_cfg); +int +sli_cmd_reg_rpi(struct sli4 *sli4, void *buf, u32 rpi, u32 vpi, u32 fc_id, + struct efc_dma *dma, u8 update, u8 enable_t10_pi); +int +sli_cmd_unreg_fcfi(struct sli4 *sli4, void *buf, u16 indicator); +int +sli_cmd_unreg_rpi(struct sli4 *sli4, void *buf, u16 indicator, + enum sli4_resource which, u32 fc_id); +int +sli_cmd_reg_vpi(struct sli4 *sli4, void *buf, u32 fc_id, + __be64 sli_wwpn, u16 vpi, u16 vfi, bool update); +int +sli_cmd_reg_vfi(struct sli4 *sli4, void *buf, size_t size, + u16 vfi, u16 fcfi, struct efc_dma dma, + u16 vpi, __be64 sli_wwpn, u32 fc_id); +int +sli_cmd_unreg_vpi(struct sli4 *sli4, void *buf, u16 id, u32 type); +int +sli_cmd_unreg_vfi(struct sli4 *sli4, void *buf, u16 idx, u32 type); +int +sli_cmd_common_nop(struct sli4 *sli4, void *buf, uint64_t context); +int +sli_cmd_common_get_resource_extent_info(struct sli4 *sli4, void *buf, + u16 rtype); +int +sli_cmd_common_get_sli4_parameters(struct sli4 *sli4, void *buf); +int +sli_cmd_common_write_object(struct sli4 *sli4, void *buf, u16 noc, + u16 eof, u32 len, u32 offset, char *name, struct efc_dma *dma); +int +sli_cmd_common_delete_object(struct sli4 *sli4, void *buf, char *object_name); +int +sli_cmd_common_read_object(struct sli4 *sli4, void *buf, + u32 length, u32 offset, char *name, struct efc_dma *dma); +int +sli_cmd_dmtf_exec_clp_cmd(struct sli4 *sli4, void *buf, + struct efc_dma *cmd, struct efc_dma *resp); +int +sli_cmd_common_set_dump_location(struct sli4 *sli4, void *buf, + bool query, bool is_buffer_list, struct efc_dma *dma, u8 fdb); +int +sli_cmd_common_set_features(struct sli4 *sli4, void *buf, + u32 feature, u32 param_len, void *parameter); + +int sli_cqe_mq(struct sli4 *sli4, void *buf); +int sli_cqe_async(struct sli4 *sli4, void *buf); + +int +sli_setup(struct sli4 *sli4, void *os, struct pci_dev *pdev, void __iomem *r[]); +void sli_calc_max_qentries(struct sli4 *sli4); +int sli_init(struct sli4 *sli4); +int sli_reset(struct sli4 *sli4); +int sli_fw_reset(struct sli4 *sli4); +void sli_teardown(struct sli4 *sli4); +int +sli_callback(struct sli4 *sli4, enum sli4_callback cb, void *func, void *arg); +int +sli_bmbx_command(struct sli4 *sli4); +int +__sli_queue_init(struct sli4 *sli4, struct sli4_queue *q, u32 qtype, + size_t size, u32 n_entries, u32 align); +int +__sli_create_queue(struct sli4 *sli4, struct sli4_queue *q); +int +sli_eq_modify_delay(struct sli4 *sli4, struct sli4_queue *eq, u32 num_eq, + u32 shift, u32 delay_mult); +int +sli_queue_alloc(struct sli4 *sli4, u32 qtype, struct sli4_queue *q, + u32 n_entries, struct sli4_queue *assoc); +int +sli_cq_alloc_set(struct sli4 *sli4, struct sli4_queue *qs[], u32 num_cqs, + u32 n_entries, struct sli4_queue *eqs[]); +int +sli_get_queue_entry_size(struct sli4 *sli4, u32 qtype); +int +sli_queue_free(struct sli4 *sli4, struct sli4_queue *q, u32 destroy_queues, + u32 free_memory); +int +sli_queue_eq_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm); +int +sli_queue_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm); + +int +sli_wq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_mq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_rq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_eq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_cq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_mq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry); +int +sli_resource_alloc(struct sli4 *sli4, enum sli4_resource rtype, u32 *rid, + u32 *index); +int +sli_resource_free(struct sli4 *sli4, enum sli4_resource rtype, u32 rid); +int +sli_resource_reset(struct sli4 *sli4, enum sli4_resource rtype); +int +sli_eq_parse(struct sli4 *sli4, u8 *buf, u16 *cq_id); +int +sli_cq_parse(struct sli4 *sli4, struct sli4_queue *cq, u8 *cqe, + enum sli4_qentry *etype, u16 *q_id); + +int sli_raise_ue(struct sli4 *sli4, u8 dump); +int sli_dump_is_ready(struct sli4 *sli4); +bool sli_reset_required(struct sli4 *sli4); +bool sli_fw_ready(struct sli4 *sli4); + +int +sli_fc_process_link_attention(struct sli4 *sli4, void *acqe); +int +sli_fc_cqe_parse(struct sli4 *sli4, struct sli4_queue *cq, + u8 *cqe, enum sli4_qentry *etype, + u16 *rid); +u32 sli_fc_response_length(struct sli4 *sli4, u8 *cqe); +u32 sli_fc_io_length(struct sli4 *sli4, u8 *cqe); +int sli_fc_els_did(struct sli4 *sli4, u8 *cqe, u32 *d_id); +u32 sli_fc_ext_status(struct sli4 *sli4, u8 *cqe); +int +sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index); +int +sli_cmd_wq_create(struct sli4 *sli4, void *buf, + struct efc_dma *qmem, u16 cq_id); +int sli_cmd_post_sgl_pages(struct sli4 *sli4, void *buf, u16 xri, + u32 xri_count, struct efc_dma *page0[], struct efc_dma *page1[], + struct efc_dma *dma); +int +sli_cmd_post_hdr_templates(struct sli4 *sli4, void *buf, + struct efc_dma *dma, u16 rpi, struct efc_dma *payload_dma); +int +sli_fc_rq_alloc(struct sli4 *sli4, struct sli4_queue *q, u32 n_entries, + u32 buffer_size, struct sli4_queue *cq, bool is_hdr); +int +sli_fc_rq_set_alloc(struct sli4 *sli4, u32 num_rq_pairs, struct sli4_queue *q[], + u32 base_cq_id, u32 num, u32 hdr_buf_size, u32 data_buf_size); +u32 sli_fc_get_rpi_requirements(struct sli4 *sli4, u32 n_rpi); +int +sli_abort_wqe(struct sli4 *sli4, void *buf, enum sli4_abort_type type, + bool send_abts, u32 ids, u32 mask, u16 tag, u16 cq_id); + +int +sli_send_frame_wqe(struct sli4 *sli4, void *buf, u8 sof, u8 eof, + u32 *hdr, struct efc_dma *payload, u32 req_len, u8 timeout, + u16 xri, u16 req_tag); + +int +sli_xmit_els_rsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *rsp, + struct sli_els_params *params); + +int +sli_els_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + struct sli_els_params *params); + +int +sli_fcp_icmnd64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, u16 xri, + u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 timeout); + +int +sli_fcp_iread64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, u16 xri, + u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 dif, u8 bs, + u8 timeout); + +int +sli_fcp_iwrite64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, + u32 first_burst, u16 xri, u16 tag, u16 cq_id, u32 rpi, + u32 rnode_fcid, u8 dif, u8 bs, u8 timeout); + +int +sli_fcp_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params); +int +sli_fcp_cont_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 sec_xri, u16 cq_id, u8 dif, + u8 bs, struct sli_fcp_tgt_params *params); + +int +sli_fcp_trsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u16 cq_id, u8 port_owned, struct sli_fcp_tgt_params *params); + +int +sli_fcp_tsend64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params); +int +sli_gen_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + struct sli_ct_params *params); + +int +sli_xmit_bls_rsp64_wqe(struct sli4 *sli4, void *buf, + struct sli_bls_payload *payload, struct sli_bls_params *params); + +int +sli_xmit_sequence64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *payload, + struct sli_ct_params *params); + +int +sli_requeue_xri_wqe(struct sli4 *sli4, void *buf, u16 xri, u16 tag, u16 cq_id); +void +sli4_cmd_lowlevel_set_watchdog(struct sli4 *sli4, void *buf, size_t size, + u16 timeout); + +const char *sli_fc_get_status_string(u32 status); + +#endif /* !_SLI4_H */ |