/*- * Copyright 2016-2021 Microchip Technology, Inc. and/or its subsidiaries. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "smartpqi_includes.h" /* 5 mins timeout for quiesce */ #define PQI_QUIESCE_TIMEOUT 300000 /* * Request the adapter to get PQI capabilities supported. */ static int pqisrc_report_pqi_capability(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; DBG_FUNC("IN\n"); gen_adm_req_iu_t admin_req; gen_adm_resp_iu_t admin_resp; dma_mem_t pqi_cap_dma_buf; pqi_dev_cap_t *capability = NULL; pqi_iu_layer_desc_t *iu_layer_desc = NULL; /* Allocate Non DMA memory */ capability = os_mem_alloc(softs, sizeof(*capability)); if (!capability) { DBG_ERR("Failed to allocate memory for capability\n"); ret = PQI_STATUS_FAILURE; goto err_out; } memset(&admin_req, 0, sizeof(admin_req)); memset(&admin_resp, 0, sizeof(admin_resp)); memset(&pqi_cap_dma_buf, 0, sizeof(struct dma_mem)); pqi_cap_dma_buf.tag = "pqi_cap_buf"; pqi_cap_dma_buf.size = REPORT_PQI_DEV_CAP_DATA_BUF_SIZE; pqi_cap_dma_buf.align = PQISRC_DEFAULT_DMA_ALIGN; ret = os_dma_mem_alloc(softs, &pqi_cap_dma_buf); if (ret) { DBG_ERR("Failed to allocate capability DMA buffer : %d\n", ret); goto err_dma_alloc; } admin_req.fn_code = PQI_FUNCTION_REPORT_DEV_CAP; admin_req.req_type.general_func.buf_size = pqi_cap_dma_buf.size; admin_req.req_type.general_func.sg_desc.length = pqi_cap_dma_buf.size; admin_req.req_type.general_func.sg_desc.addr = pqi_cap_dma_buf.dma_addr; admin_req.req_type.general_func.sg_desc.type = SGL_DESCRIPTOR_CODE_DATA_BLOCK; ret = pqisrc_submit_admin_req(softs, &admin_req, &admin_resp); if( PQI_STATUS_SUCCESS == ret) { memcpy(capability, pqi_cap_dma_buf.virt_addr, pqi_cap_dma_buf.size); } else { DBG_ERR("Failed to send admin req report pqi device capability\n"); goto err_admin_req; } softs->pqi_dev_cap.max_iqs = capability->max_iqs; softs->pqi_dev_cap.max_iq_elements = capability->max_iq_elements; softs->pqi_dev_cap.max_iq_elem_len = capability->max_iq_elem_len; softs->pqi_dev_cap.min_iq_elem_len = capability->min_iq_elem_len; softs->pqi_dev_cap.max_oqs = capability->max_oqs; softs->pqi_dev_cap.max_oq_elements = capability->max_oq_elements; softs->pqi_dev_cap.max_oq_elem_len = capability->max_oq_elem_len; softs->pqi_dev_cap.intr_coales_time_granularity = capability->intr_coales_time_granularity; iu_layer_desc = &capability->iu_layer_desc[PQI_PROTOCOL_SOP]; softs->max_ib_iu_length_per_fw = iu_layer_desc->max_ib_iu_len; softs->ib_spanning_supported = iu_layer_desc->ib_spanning_supported; softs->ob_spanning_supported = iu_layer_desc->ob_spanning_supported; DBG_INIT("softs->pqi_dev_cap.max_iqs: %d\n", softs->pqi_dev_cap.max_iqs); DBG_INIT("softs->pqi_dev_cap.max_iq_elements: %d\n", softs->pqi_dev_cap.max_iq_elements); DBG_INIT("softs->pqi_dev_cap.max_iq_elem_len: %d\n", softs->pqi_dev_cap.max_iq_elem_len); DBG_INIT("softs->pqi_dev_cap.min_iq_elem_len: %d\n", softs->pqi_dev_cap.min_iq_elem_len); DBG_INIT("softs->pqi_dev_cap.max_oqs: %d\n", softs->pqi_dev_cap.max_oqs); DBG_INIT("softs->pqi_dev_cap.max_oq_elements: %d\n", softs->pqi_dev_cap.max_oq_elements); DBG_INIT("softs->pqi_dev_cap.max_oq_elem_len: %d\n", softs->pqi_dev_cap.max_oq_elem_len); DBG_INIT("softs->pqi_dev_cap.intr_coales_time_granularity: %d\n", softs->pqi_dev_cap.intr_coales_time_granularity); DBG_INIT("softs->max_ib_iu_length_per_fw: %d\n", softs->max_ib_iu_length_per_fw); DBG_INIT("softs->ib_spanning_supported: %d\n", softs->ib_spanning_supported); DBG_INIT("softs->ob_spanning_supported: %d\n", softs->ob_spanning_supported); os_mem_free(softs, (void *)capability, REPORT_PQI_DEV_CAP_DATA_BUF_SIZE); os_dma_mem_free(softs, &pqi_cap_dma_buf); DBG_FUNC("OUT\n"); return ret; err_admin_req: os_dma_mem_free(softs, &pqi_cap_dma_buf); err_dma_alloc: if (capability) os_mem_free(softs, (void *)capability, REPORT_PQI_DEV_CAP_DATA_BUF_SIZE); err_out: DBG_FUNC("failed OUT\n"); return PQI_STATUS_FAILURE; } /* * Function used to deallocate the used rcb. */ void pqisrc_free_rcb(pqisrc_softstate_t *softs, int req_count) { uint32_t num_req; size_t size; int i; DBG_FUNC("IN\n"); num_req = softs->max_outstanding_io + 1; size = num_req * sizeof(rcb_t); for (i = 1; i < req_count; i++) os_dma_mem_free(softs, &softs->sg_dma_desc[i]); os_mem_free(softs, (void *)softs->rcb, size); softs->rcb = NULL; DBG_FUNC("OUT\n"); } /* * Allocate memory for rcb and SG descriptors. */ static int pqisrc_allocate_rcb(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; int i = 0; uint32_t num_req = 0; uint32_t sg_buf_size = 0; uint64_t alloc_size = 0; rcb_t *rcb = NULL; rcb_t *prcb = NULL; DBG_FUNC("IN\n"); /* Set maximum outstanding requests */ /* The valid tag values are from 1, 2, ..., softs->max_outstanding_io * The rcb will be accessed by using the tag as index * As 0 tag index is not used, we need to allocate one extra. */ softs->max_outstanding_io = softs->pqi_cap.max_outstanding_io; num_req = softs->max_outstanding_io + 1; DBG_INIT("Max Outstanding IO reset to %d\n", num_req); alloc_size = num_req * sizeof(rcb_t); /* Allocate Non DMA memory */ rcb = os_mem_alloc(softs, alloc_size); if (!rcb) { DBG_ERR("Failed to allocate memory for rcb\n"); ret = PQI_STATUS_FAILURE; goto err_out; } softs->rcb = rcb; /* Allocate sg dma memory for sg chain */ sg_buf_size = softs->pqi_cap.max_sg_elem * sizeof(sgt_t); prcb = &softs->rcb[1]; /* Initialize rcb */ for(i=1; i < num_req; i++) { char tag[15]; sprintf(tag, "sg_dma_buf%d", i); softs->sg_dma_desc[i].tag = tag; softs->sg_dma_desc[i].size = sg_buf_size; softs->sg_dma_desc[i].align = PQISRC_DEFAULT_DMA_ALIGN; ret = os_dma_mem_alloc(softs, &softs->sg_dma_desc[i]); if (ret) { DBG_ERR("Failed to Allocate sg desc %d\n", ret); ret = PQI_STATUS_FAILURE; goto error; } prcb->sg_chain_virt = (sgt_t *)(softs->sg_dma_desc[i].virt_addr); prcb->sg_chain_dma = (dma_addr_t)(softs->sg_dma_desc[i].dma_addr); prcb ++; } DBG_FUNC("OUT\n"); return ret; error: pqisrc_free_rcb(softs, i); err_out: DBG_FUNC("failed OUT\n"); return ret; } /* * Function used to decide the operational queue configuration params * - no of ibq/obq, shared/non-shared interrupt resource, IU spanning support */ void pqisrc_decide_opq_config(pqisrc_softstate_t *softs) { uint16_t total_iq_elements; DBG_FUNC("IN\n"); DBG_INIT("softs->intr_count : %d softs->num_cpus_online : %d", softs->intr_count, softs->num_cpus_online); if (softs->intr_count == 1 || softs->num_cpus_online == 1) { /* Share the event and Operational queue. */ softs->num_op_obq = 1; softs->share_opq_and_eventq = true; } else { /* Note : One OBQ (OBQ0) reserved for event queue */ softs->num_op_obq = MIN(softs->num_cpus_online, softs->intr_count) - 1; softs->share_opq_and_eventq = false; } /* If the available interrupt count is more than one, we dont need to share the interrupt for IO and event queue */ if (softs->intr_count > 1) softs->share_opq_and_eventq = false; DBG_INIT("softs->num_op_obq : %d\n",softs->num_op_obq); softs->num_op_raid_ibq = softs->num_op_obq; softs->num_op_aio_ibq = softs->num_op_raid_ibq; softs->ibq_elem_size = softs->pqi_dev_cap.max_iq_elem_len * 16; softs->obq_elem_size = softs->pqi_dev_cap.max_oq_elem_len * 16; if (softs->max_ib_iu_length_per_fw == 256 && softs->ob_spanning_supported) { /* older f/w that doesn't actually support spanning. */ softs->max_ib_iu_length = softs->ibq_elem_size; } else { /* max. inbound IU length is an multiple of our inbound element size. */ softs->max_ib_iu_length = (softs->max_ib_iu_length_per_fw / softs->ibq_elem_size) * softs->ibq_elem_size; } /* If Max. Outstanding IO came with Max. Spanning element count then, needed elements per IO are multiplication of Max.Outstanding IO and Max.Spanning element */ total_iq_elements = (softs->max_outstanding_io * (softs->max_ib_iu_length / softs->ibq_elem_size)); softs->num_elem_per_op_ibq = total_iq_elements / softs->num_op_raid_ibq; softs->num_elem_per_op_ibq = MIN(softs->num_elem_per_op_ibq, softs->pqi_dev_cap.max_iq_elements); softs->num_elem_per_op_obq = softs->max_outstanding_io / softs->num_op_obq; softs->num_elem_per_op_obq = MIN(softs->num_elem_per_op_obq, softs->pqi_dev_cap.max_oq_elements); softs->max_sg_per_iu = ((softs->max_ib_iu_length - softs->ibq_elem_size) / sizeof(sgt_t)) + MAX_EMBEDDED_SG_IN_FIRST_IU; DBG_INIT("softs->max_ib_iu_length: %d\n", softs->max_ib_iu_length); DBG_INIT("softs->num_elem_per_op_ibq: %d\n", softs->num_elem_per_op_ibq); DBG_INIT("softs->num_elem_per_op_obq: %d\n", softs->num_elem_per_op_obq); DBG_INIT("softs->max_sg_per_iu: %d\n", softs->max_sg_per_iu); DBG_FUNC("OUT\n"); } /* * Configure the operational queue parameters. */ int pqisrc_configure_op_queues(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; /* Get the PQI capability, REPORT PQI DEVICE CAPABILITY request */ ret = pqisrc_report_pqi_capability(softs); if (ret) { DBG_ERR("Failed to send report pqi dev capability request : %d\n", ret); goto err_out; } /* Reserve required no of slots for internal requests */ softs->max_io_for_scsi_ml = softs->max_outstanding_io - PQI_RESERVED_IO_SLOTS_CNT; /* Decide the Op queue configuration */ pqisrc_decide_opq_config(softs); DBG_FUNC("OUT\n"); return ret; err_out: DBG_FUNC("OUT failed\n"); return ret; } /* * Validate the PQI mode of adapter. */ int pqisrc_check_pqimode(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_FAILURE; int tmo = 0; uint64_t signature = 0; DBG_FUNC("IN\n"); /* Check the PQI device signature */ tmo = PQISRC_PQIMODE_READY_TIMEOUT; do { signature = LE_64(PCI_MEM_GET64(softs, &softs->pqi_reg->signature, PQI_SIGNATURE)); if (memcmp(&signature, PQISRC_PQI_DEVICE_SIGNATURE, sizeof(uint64_t)) == 0) { ret = PQI_STATUS_SUCCESS; break; } OS_SLEEP(PQISRC_MODE_READY_POLL_INTERVAL); } while (tmo--); PRINT_PQI_SIGNATURE(signature); if (tmo <= 0) { DBG_ERR("PQI Signature is invalid\n"); ret = PQI_STATUS_TIMEOUT; goto err_out; } tmo = PQISRC_PQIMODE_READY_TIMEOUT; /* Check function and status code for the device */ COND_WAIT((PCI_MEM_GET64(softs, &softs->pqi_reg->admin_q_config, PQI_ADMINQ_CONFIG) == PQI_ADMIN_QUEUE_CONF_FUNC_STATUS_IDLE), tmo); if (!tmo) { DBG_ERR("PQI device is not in IDLE state\n"); ret = PQI_STATUS_TIMEOUT; goto err_out; } tmo = PQISRC_PQIMODE_READY_TIMEOUT; /* Check the PQI device status register */ COND_WAIT(LE_32(PCI_MEM_GET32(softs, &softs->pqi_reg->pqi_dev_status, PQI_DEV_STATUS)) & PQI_DEV_STATE_AT_INIT, tmo); if (!tmo) { DBG_ERR("PQI Registers are not ready\n"); ret = PQI_STATUS_TIMEOUT; goto err_out; } DBG_FUNC("OUT\n"); return ret; err_out: DBG_FUNC("OUT failed\n"); return ret; } /* PQI Feature processing */ static int pqisrc_config_table_update(struct pqisrc_softstate *softs, uint16_t first_section, uint16_t last_section) { pqi_vendor_general_request_t request; int ret = PQI_STATUS_FAILURE; memset(&request, 0, sizeof(request)); request.header.iu_type = PQI_REQUEST_IU_VENDOR_GENERAL; request.header.iu_length = sizeof(request) - PQI_REQUEST_HEADER_LENGTH; request.function_code = PQI_VENDOR_GENERAL_CONFIG_TABLE_UPDATE; request.data.config_table_update.first_section = first_section; request.data.config_table_update.last_section = last_section; ret = pqisrc_build_send_vendor_request(softs, &request, NULL); if (ret != PQI_STATUS_SUCCESS) { DBG_ERR("Failed to submit vendor general request IU, Ret status: %d\n", ret); return PQI_STATUS_FAILURE; } return PQI_STATUS_SUCCESS; } static inline boolean_t pqi_is_firmware_feature_supported( struct pqi_conf_table_firmware_features *firmware_feature_list, unsigned int bit_position) { unsigned int byte_index; byte_index = bit_position / BITS_PER_BYTE; if (byte_index >= firmware_feature_list->num_elements) return false; return firmware_feature_list->features_supported[byte_index] & (1 << (bit_position % BITS_PER_BYTE)) ? true : false; } static inline boolean_t pqi_is_firmware_feature_enabled( struct pqi_conf_table_firmware_features *firmware_feature_list, uint8_t *firmware_features_addr, unsigned int bit_position) { unsigned int byte_index; uint8_t *feature_enabled_addr; byte_index = (bit_position / BITS_PER_BYTE) + (firmware_feature_list->num_elements * 2); feature_enabled_addr = firmware_features_addr + offsetof(struct pqi_conf_table_firmware_features, features_supported) + byte_index; return *feature_enabled_addr & (1 << (bit_position % BITS_PER_BYTE)) ? true : false; } static inline void pqi_request_firmware_feature( struct pqi_conf_table_firmware_features *firmware_feature_list, unsigned int bit_position) { unsigned int byte_index; byte_index = (bit_position / BITS_PER_BYTE) + firmware_feature_list->num_elements; firmware_feature_list->features_supported[byte_index] |= (1 << (bit_position % BITS_PER_BYTE)); } /* Update PQI config table firmware features section and inform the firmware */ static int pqisrc_set_host_requested_firmware_feature(pqisrc_softstate_t *softs, struct pqi_conf_table_firmware_features *firmware_feature_list) { uint8_t *request_feature_addr; void *request_feature_abs_addr; request_feature_addr = firmware_feature_list->features_supported + firmware_feature_list->num_elements; request_feature_abs_addr = softs->fw_features_section_abs_addr + (request_feature_addr - (uint8_t*)firmware_feature_list); os_io_memcpy(request_feature_abs_addr, request_feature_addr, firmware_feature_list->num_elements); return pqisrc_config_table_update(softs, PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES, PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES); } /* Check firmware has enabled the feature specified in the respective bit position. */ inline boolean_t pqisrc_is_firmware_feature_enabled(pqisrc_softstate_t *softs, struct pqi_conf_table_firmware_features *firmware_feature_list, uint16_t bit_position) { uint16_t byte_index; uint8_t *features_enabled_abs_addr; byte_index = (bit_position / BITS_PER_BYTE) + (firmware_feature_list->num_elements * 2); features_enabled_abs_addr = softs->fw_features_section_abs_addr + offsetof(struct pqi_conf_table_firmware_features,features_supported) + byte_index; return *features_enabled_abs_addr & (1 << (bit_position % BITS_PER_BYTE)) ? true : false; } static void pqi_firmware_feature_status(struct pqisrc_softstate *softs, struct pqi_firmware_feature *firmware_feature) { switch(firmware_feature->feature_bit) { case PQI_FIRMWARE_FEATURE_OFA: break; case PQI_FIRMWARE_FEATURE_TIMEOUT_IN_RAID_IU_SUPPORT: softs->timeout_in_passthrough = true; break; case PQI_FIRMWARE_FEATURE_TIMEOUT_IN_TMF_IU_SUPPORT: softs->timeout_in_tmf = true; break; default: DBG_NOTE("Nothing to do \n"); } } /* Firmware features supported by the driver */ static struct pqi_firmware_feature pqi_firmware_features[] = { { .feature_name = "Support timeout for pass-through commands", .feature_bit = PQI_FIRMWARE_FEATURE_TIMEOUT_IN_RAID_IU_SUPPORT, .feature_status = pqi_firmware_feature_status, }, { .feature_name = "Support timeout for LUN Reset TMF", .feature_bit = PQI_FIRMWARE_FEATURE_TIMEOUT_IN_TMF_IU_SUPPORT, .feature_status = pqi_firmware_feature_status, } }; static void pqisrc_process_firmware_features(pqisrc_softstate_t *softs) { int rc; struct pqi_conf_table_firmware_features *firmware_feature_list; unsigned int i; unsigned int num_features_requested; firmware_feature_list = (struct pqi_conf_table_firmware_features*) softs->fw_features_section_abs_addr; /* Check features and request those supported by firmware and driver.*/ for (i = 0, num_features_requested = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) { /* Firmware support it ? */ if (pqi_is_firmware_feature_supported(firmware_feature_list, pqi_firmware_features[i].feature_bit)) { pqi_request_firmware_feature(firmware_feature_list, pqi_firmware_features[i].feature_bit); pqi_firmware_features[i].supported = true; num_features_requested++; DBG_NOTE("%s supported by driver, requesting firmware to enable it\n", pqi_firmware_features[i].feature_name); } else { DBG_NOTE("%s supported by driver, but not by current firmware\n", pqi_firmware_features[i].feature_name); } } if (num_features_requested == 0) return; rc = pqisrc_set_host_requested_firmware_feature(softs, firmware_feature_list); if (rc) { DBG_ERR("Failed to update pqi config table\n"); return; } for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) { if (pqi_is_firmware_feature_enabled(firmware_feature_list, softs->fw_features_section_abs_addr, pqi_firmware_features[i].feature_bit)) { pqi_firmware_features[i].enabled = true; DBG_NOTE("Firmware feature %s enabled \n",pqi_firmware_features[i].feature_name); if(pqi_firmware_features[i].feature_status) pqi_firmware_features[i].feature_status(softs, &(pqi_firmware_features[i])); } } } /* * Get the PQI configuration table parameters. * Currently using for heart-beat counter scratch-pad register. */ int pqisrc_process_config_table(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_FAILURE; uint32_t config_table_size; uint32_t section_off; uint8_t *config_table_abs_addr __unused; struct pqi_conf_table *conf_table; struct pqi_conf_table_section_header *section_hdr; config_table_size = softs->pqi_cap.conf_tab_sz; if (config_table_size < sizeof(*conf_table) || config_table_size > PQI_CONF_TABLE_MAX_LEN) { DBG_ERR("Invalid PQI conf table length of %u\n", config_table_size); return ret; } conf_table = os_mem_alloc(softs, config_table_size); if (!conf_table) { DBG_ERR("Failed to allocate memory for PQI conf table\n"); return ret; } if (config_table_size < sizeof(conf_table) || config_table_size > PQI_CONF_TABLE_MAX_LEN) { DBG_ERR("Invalid PQI conf table length of %u\n", config_table_size); goto out; } config_table_abs_addr = (uint8_t *)(softs->pci_mem_base_vaddr + softs->pqi_cap.conf_tab_off); PCI_MEM_GET_BUF(softs, config_table_abs_addr, softs->pqi_cap.conf_tab_off, (uint8_t*)conf_table, config_table_size); if (memcmp(conf_table->sign, PQI_CONF_TABLE_SIGNATURE, sizeof(conf_table->sign)) != 0) { DBG_ERR("Invalid PQI config signature\n"); goto out; } section_off = LE_32(conf_table->first_section_off); while (section_off) { if (section_off+ sizeof(*section_hdr) >= config_table_size) { DBG_INFO("Reached end of PQI config table. Breaking off.\n"); break; } section_hdr = (struct pqi_conf_table_section_header *)((uint8_t *)conf_table + section_off); switch (LE_16(section_hdr->section_id)) { case PQI_CONF_TABLE_SECTION_GENERAL_INFO: case PQI_CONF_TABLE_SECTION_FIRMWARE_ERRATA: case PQI_CONF_TABLE_SECTION_DEBUG: break; case PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES: softs->fw_features_section_off = softs->pqi_cap.conf_tab_off + section_off; softs->fw_features_section_abs_addr = softs->pci_mem_base_vaddr + softs->fw_features_section_off; pqisrc_process_firmware_features(softs); break; case PQI_CONF_TABLE_SECTION_HEARTBEAT: softs->heartbeat_counter_off = softs->pqi_cap.conf_tab_off + section_off + offsetof(struct pqi_conf_table_heartbeat, heartbeat_counter); softs->heartbeat_counter_abs_addr = (uint64_t *)(softs->pci_mem_base_vaddr + softs->heartbeat_counter_off); ret = PQI_STATUS_SUCCESS; break; default: DBG_INFO("unrecognized PQI config table section ID: 0x%x\n", LE_16(section_hdr->section_id)); break; } section_off = LE_16(section_hdr->next_section_off); } out: os_mem_free(softs, (void *)conf_table,config_table_size); return ret; } /* Wait for PQI reset completion for the adapter*/ int pqisrc_wait_for_pqi_reset_completion(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; pqi_reset_reg_t reset_reg; int pqi_reset_timeout = 0; uint64_t val = 0; uint32_t max_timeout = 0; val = PCI_MEM_GET64(softs, &softs->pqi_reg->pqi_dev_adminq_cap, PQI_ADMINQ_CAP); max_timeout = (val & 0xFFFF00000000) >> 32; DBG_INIT("max_timeout for PQI reset completion in 100 msec units = %u\n", max_timeout); while(1) { if (pqi_reset_timeout++ == max_timeout) { return PQI_STATUS_TIMEOUT; } OS_SLEEP(PQI_RESET_POLL_INTERVAL);/* 100 msec */ reset_reg.all_bits = PCI_MEM_GET32(softs, &softs->pqi_reg->dev_reset, PQI_DEV_RESET); if (reset_reg.bits.reset_action == PQI_RESET_ACTION_COMPLETED) break; } return ret; } /* * Function used to perform PQI hard reset. */ int pqi_reset(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; uint32_t val = 0; pqi_reset_reg_t pqi_reset_reg; DBG_FUNC("IN\n"); if (true == softs->ctrl_in_pqi_mode) { if (softs->pqi_reset_quiesce_allowed) { val = PCI_MEM_GET32(softs, &softs->ioa_reg->host_to_ioa_db, LEGACY_SIS_IDBR); val |= SIS_PQI_RESET_QUIESCE; PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db, LEGACY_SIS_IDBR, LE_32(val)); ret = pqisrc_sis_wait_for_db_bit_to_clear(softs, SIS_PQI_RESET_QUIESCE); if (ret) { DBG_ERR("failed with error %d during quiesce\n", ret); return ret; } } pqi_reset_reg.all_bits = 0; pqi_reset_reg.bits.reset_type = PQI_RESET_TYPE_HARD_RESET; pqi_reset_reg.bits.reset_action = PQI_RESET_ACTION_RESET; PCI_MEM_PUT32(softs, &softs->pqi_reg->dev_reset, PQI_DEV_RESET, LE_32(pqi_reset_reg.all_bits)); ret = pqisrc_wait_for_pqi_reset_completion(softs); if (ret) { DBG_ERR("PQI reset timed out: ret = %d!\n", ret); return ret; } } softs->ctrl_in_pqi_mode = false; DBG_FUNC("OUT\n"); return ret; } /* * Initialize the adapter with supported PQI configuration. */ int pqisrc_pqi_init(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; DBG_FUNC("IN\n"); /* Check the PQI signature */ ret = pqisrc_check_pqimode(softs); if(ret) { DBG_ERR("failed to switch to pqi\n"); goto err_out; } PQI_SAVE_CTRL_MODE(softs, CTRL_PQI_MODE); softs->ctrl_in_pqi_mode = true; /* Get the No. of Online CPUs,NUMA/Processor config from OS */ ret = os_get_processor_config(softs); if (ret) { DBG_ERR("Failed to get processor config from OS %d\n", ret); goto err_out; } softs->intr_type = INTR_TYPE_NONE; /* Get the interrupt count, type, priority available from OS */ ret = os_get_intr_config(softs); if (ret) { DBG_ERR("Failed to get interrupt config from OS %d\n", ret); goto err_out; } /*Enable/Set Legacy INTx Interrupt mask clear pqi register, *if allocated interrupt is legacy type. */ if (INTR_TYPE_FIXED == softs->intr_type) { pqisrc_configure_legacy_intx(softs, true); sis_enable_intx(softs); } /* Create Admin Queue pair*/ ret = pqisrc_create_admin_queue(softs); if(ret) { DBG_ERR("Failed to configure admin queue\n"); goto err_admin_queue; } /* For creating event and IO operational queues we have to submit admin IU requests.So Allocate resources for submitting IUs */ /* Allocate the request container block (rcb) */ ret = pqisrc_allocate_rcb(softs); if (ret == PQI_STATUS_FAILURE) { DBG_ERR("Failed to allocate rcb \n"); goto err_rcb; } /* Allocate & initialize request id queue */ ret = pqisrc_init_taglist(softs,&softs->taglist, softs->max_outstanding_io); if (ret) { DBG_ERR("Failed to allocate memory for request id q : %d\n", ret); goto err_taglist; } ret = pqisrc_configure_op_queues(softs); if (ret) { DBG_ERR("Failed to configure op queue\n"); goto err_config_opq; } /* Create Operational queues */ ret = pqisrc_create_op_queues(softs); if(ret) { DBG_ERR("Failed to create op queue\n"); ret = PQI_STATUS_FAILURE; goto err_create_opq; } softs->ctrl_online = true; DBG_FUNC("OUT\n"); return ret; err_create_opq: err_config_opq: pqisrc_destroy_taglist(softs,&softs->taglist); err_taglist: pqisrc_free_rcb(softs, softs->max_outstanding_io + 1); err_rcb: pqisrc_destroy_admin_queue(softs); err_admin_queue: os_free_intr_config(softs); err_out: DBG_FUNC("OUT failed\n"); return PQI_STATUS_FAILURE; } int pqisrc_force_sis(pqisrc_softstate_t *softs) { int ret = PQI_STATUS_SUCCESS; if (SIS_IS_KERNEL_PANIC(softs)) { DBG_INIT("Controller FW is not running"); return PQI_STATUS_FAILURE; } if (PQI_GET_CTRL_MODE(softs) == CTRL_SIS_MODE) { return ret; } if (SIS_IS_KERNEL_UP(softs)) { PQI_SAVE_CTRL_MODE(softs, CTRL_SIS_MODE); return ret; } /* Disable interrupts ? */ sis_disable_interrupt(softs); /* reset pqi, this will delete queues */ ret = pqi_reset(softs); if (ret) { return ret; } /* Re enable SIS */ ret = pqisrc_reenable_sis(softs); if (ret) { return ret; } PQI_SAVE_CTRL_MODE(softs, CTRL_SIS_MODE); return ret; } static int pqisrc_wait_for_cmnd_complete(pqisrc_softstate_t *softs) { int count = 0; int ret = PQI_STATUS_SUCCESS; DBG_NOTE("softs->taglist.num_elem : %d",softs->taglist.num_elem); if (softs->taglist.num_elem == softs->max_outstanding_io) return ret; else { DBG_WARN("%d commands pending\n", softs->max_outstanding_io - softs->taglist.num_elem); while(1) { /* Since heartbeat timer stopped ,check for firmware status*/ if (SIS_IS_KERNEL_PANIC(softs)) { DBG_ERR("Controller FW is not running\n"); return PQI_STATUS_FAILURE; } if (softs->taglist.num_elem != softs->max_outstanding_io) { /* Sleep for 1 msec */ OS_SLEEP(1000); count++; if(count % 1000 == 0) { DBG_WARN("Waited for %d seconds", count/1000); } if (count >= PQI_QUIESCE_TIMEOUT) { return PQI_STATUS_FAILURE; } continue; } break; } } return ret; } static void pqisrc_complete_internal_cmds(pqisrc_softstate_t *softs) { int tag = 0; rcb_t *rcb; for (tag = 1; tag <= softs->max_outstanding_io; tag++) { rcb = &softs->rcb[tag]; if(rcb->req_pending && is_internal_req(rcb)) { rcb->status = REQUEST_FAILED; rcb->req_pending = false; } } } /* * Uninitialize the resources used during PQI initialization. */ void pqisrc_pqi_uninit(pqisrc_softstate_t *softs) { int i, ret; DBG_FUNC("IN\n"); /* Wait for any rescan to finish */ pqisrc_wait_for_rescan_complete(softs); /* Wait for commands to complete */ ret = pqisrc_wait_for_cmnd_complete(softs); /* disable and free the interrupt resources */ os_destroy_intr(softs); /* Complete all pending commands. */ if(ret != PQI_STATUS_SUCCESS) { pqisrc_complete_internal_cmds(softs); os_complete_outstanding_cmds_nodevice(softs); } if(softs->devlist_lockcreated==true){ os_uninit_spinlock(&softs->devlist_lock); softs->devlist_lockcreated = false; } for (i = 0; i < softs->num_op_raid_ibq; i++) { /* OP RAID IB Q */ if(softs->op_raid_ib_q[i].lockcreated==true){ OS_UNINIT_PQILOCK(&softs->op_raid_ib_q[i].lock); softs->op_raid_ib_q[i].lockcreated = false; } /* OP AIO IB Q */ if(softs->op_aio_ib_q[i].lockcreated==true){ OS_UNINIT_PQILOCK(&softs->op_aio_ib_q[i].lock); softs->op_aio_ib_q[i].lockcreated = false; } } /* Free Op queues */ os_dma_mem_free(softs, &softs->op_ibq_dma_mem); os_dma_mem_free(softs, &softs->op_obq_dma_mem); os_dma_mem_free(softs, &softs->event_q_dma_mem); /* Free rcb */ pqisrc_free_rcb(softs, softs->max_outstanding_io + 1); /* Free request id lists */ pqisrc_destroy_taglist(softs,&softs->taglist); if(softs->admin_ib_queue.lockcreated==true) { OS_UNINIT_PQILOCK(&softs->admin_ib_queue.lock); softs->admin_ib_queue.lockcreated = false; } /* Free Admin Queue */ os_dma_mem_free(softs, &softs->admin_queue_dma_mem); /* Switch back to SIS mode */ if (pqisrc_force_sis(softs)) { DBG_ERR("Failed to switch back the adapter to SIS mode!\n"); } DBG_FUNC("OUT\n"); } /* * Function to initialize the adapter settings. */ int pqisrc_init(pqisrc_softstate_t *softs) { int ret = 0; int i = 0, j = 0; DBG_FUNC("IN\n"); check_struct_sizes(); /* Init the Sync interface */ ret = pqisrc_sis_init(softs); if (ret) { DBG_ERR("SIS Init failed with error %d\n", ret); goto err_out; } ret = os_create_semaphore("scan_lock", 1, &softs->scan_lock); if(ret != PQI_STATUS_SUCCESS){ DBG_ERR(" Failed to initialize scan lock\n"); goto err_scan_lock; } /* Init the PQI interface */ ret = pqisrc_pqi_init(softs); if (ret) { DBG_ERR("PQI Init failed with error %d\n", ret); goto err_pqi; } /* Setup interrupt */ ret = os_setup_intr(softs); if (ret) { DBG_ERR("Interrupt setup failed with error %d\n", ret); goto err_intr; } /* Report event configuration */ ret = pqisrc_report_event_config(softs); if(ret){ DBG_ERR(" Failed to configure Report events\n"); goto err_event; } /* Set event configuration*/ ret = pqisrc_set_event_config(softs); if(ret){ DBG_ERR(" Failed to configure Set events\n"); goto err_event; } /* Check for For PQI spanning */ ret = pqisrc_get_ctrl_fw_version(softs); if(ret){ DBG_ERR(" Failed to get ctrl fw version\n"); goto err_fw_version; } /* update driver version in to FW */ ret = pqisrc_write_driver_version_to_host_wellness(softs); if (ret) { DBG_ERR(" Failed to update driver version in to FW"); goto err_host_wellness; } os_strlcpy(softs->devlist_lock_name, "devlist_lock", LOCKNAME_SIZE); ret = os_init_spinlock(softs, &softs->devlist_lock, softs->devlist_lock_name); if(ret){ DBG_ERR(" Failed to initialize devlist_lock\n"); softs->devlist_lockcreated=false; goto err_lock; } softs->devlist_lockcreated = true; /* Get the PQI configuration table to read heart-beat counter*/ ret = pqisrc_process_config_table(softs); if (ret) { DBG_ERR("Failed to process PQI configuration table %d\n", ret); goto err_config_tab; } softs->prev_heartbeat_count = CTRLR_HEARTBEAT_CNT(softs) - OS_FW_HEARTBEAT_TIMER_INTERVAL; /* Init device list */ for(i = 0; i < PQI_MAX_DEVICES; i++) for(j = 0; j < PQI_MAX_MULTILUN; j++) softs->device_list[i][j] = NULL; pqisrc_init_targetid_pool(softs); DBG_FUNC("OUT\n"); return ret; err_config_tab: if(softs->devlist_lockcreated==true){ os_uninit_spinlock(&softs->devlist_lock); softs->devlist_lockcreated = false; } err_lock: err_fw_version: err_event: err_host_wellness: err_intr: pqisrc_pqi_uninit(softs); err_pqi: os_destroy_semaphore(&softs->scan_lock); err_scan_lock: pqisrc_sis_uninit(softs); err_out: DBG_FUNC("OUT failed\n"); return ret; } /* * Write all data in the adapter's battery-backed cache to * storage. */ int pqisrc_flush_cache( pqisrc_softstate_t *softs, enum pqisrc_flush_cache_event_type event_type) { int rval = PQI_STATUS_SUCCESS; pqisrc_raid_req_t request; pqisrc_bmic_flush_cache_t *flush_buff = NULL; DBG_FUNC("IN\n"); if (pqisrc_ctrl_offline(softs)) return PQI_STATUS_FAILURE; flush_buff = os_mem_alloc(softs, sizeof(pqisrc_bmic_flush_cache_t)); if (!flush_buff) { DBG_ERR("Failed to allocate memory for flush cache params\n"); rval = PQI_STATUS_FAILURE; return rval; } flush_buff->halt_event = event_type; memset(&request, 0, sizeof(request)); rval = pqisrc_build_send_raid_request(softs, &request, flush_buff, sizeof(*flush_buff), SA_CACHE_FLUSH, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); if (rval) { DBG_ERR("error in build send raid req ret=%d\n", rval); } if (flush_buff) os_mem_free(softs, (void *)flush_buff, sizeof(pqisrc_bmic_flush_cache_t)); DBG_FUNC("OUT\n"); return rval; } /* * Uninitialize the adapter. */ void pqisrc_uninit(pqisrc_softstate_t *softs) { DBG_FUNC("IN\n"); pqisrc_pqi_uninit(softs); pqisrc_sis_uninit(softs); os_destroy_semaphore(&softs->scan_lock); pqisrc_cleanup_devices(softs); DBG_FUNC("OUT\n"); }