It includes: 1)Newly added TMF feature. 2)Added newly Huawei & Inspur PCI ID's 3)Fixed smartpqi driver hangs in Z-Pool while running on FreeBSD12.1 4)Fixed flooding dmesg in kernel while the controller is offline during in ioctls. 5)Avoided unnecessary host memory allocation for rcb sg buffers. 6)Fixed race conditions while accessing internal rcb structure. 7)Fixed where Logical volumes exposing two different names to the OS it's due to the system memory is overwritten with DMA stale data. 8)Fixed dynamically unloading a smartpqi driver. 9)Added device_shutdown callback instead of deprecated shutdown_final kernel event in smartpqi driver. 10)Fixed where Os is crashed during physical drive hot removal during heavy IO. 11)Fixed OS crash during controller lockup/offline during heavy IO. 12)Fixed coverity issues in smartpqi driver 13)Fixed system crash while creating and deleting logical volume in a continuous loop. 14)Fixed where the volume size is not exposing to OS when it expands. 15)Added HC3 pci id's. Reviewed by: Scott Benesh (microsemi), Murthy Bhat (microsemi), imp Differential Revision: https://reviews.freebsd.org/D30182 Sponsored by: Netflix
1200 lines
33 KiB
C
1200 lines
33 KiB
C
/*-
|
|
* 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.
|
|
*/
|
|
|
|
/* $FreeBSD$ */
|
|
|
|
#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;
|
|
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 runnning");
|
|
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");
|
|
}
|