f7c701d528
prevent individual transactions from crossing a 4GB address boundary. Due to bus_size_t type limitations, the driver uses a 2GB boundary in PAE kernels. Reviewed by: scottl MFC after: 1 week
745 lines
23 KiB
C
745 lines
23 KiB
C
/*
|
|
* Copyright (c) 2004-07 Applied Micro Circuits Corporation.
|
|
* Copyright (c) 2004-05 Vinod Kashyap
|
|
* All rights reserved.
|
|
*
|
|
* 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$
|
|
*/
|
|
|
|
/*
|
|
* AMCC'S 3ware driver for 9000 series storage controllers.
|
|
*
|
|
* Author: Vinod Kashyap
|
|
* Modifications by: Adam Radford
|
|
* Modifications by: Manjunath Ranganathaiah
|
|
*/
|
|
|
|
|
|
/*
|
|
* Common Layer initialization functions.
|
|
*/
|
|
|
|
|
|
#include "tw_osl_share.h"
|
|
#include "tw_cl_share.h"
|
|
#include "tw_cl_fwif.h"
|
|
#include "tw_cl_ioctl.h"
|
|
#include "tw_cl.h"
|
|
#include "tw_cl_externs.h"
|
|
#include "tw_osl_ioctl.h"
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_ctlr_supported
|
|
* Description: Determines if a controller is supported.
|
|
*
|
|
* Input: vendor_id -- vendor id of the controller
|
|
* device_id -- device id of the controller
|
|
* Output: None
|
|
* Return value: TW_CL_TRUE-- controller supported
|
|
* TW_CL_FALSE-- controller not supported
|
|
*/
|
|
TW_INT32
|
|
tw_cl_ctlr_supported(TW_INT32 vendor_id, TW_INT32 device_id)
|
|
{
|
|
if ((vendor_id == TW_CL_VENDOR_ID) &&
|
|
((device_id == TW_CL_DEVICE_ID_9K) ||
|
|
(device_id == TW_CL_DEVICE_ID_9K_X) ||
|
|
(device_id == TW_CL_DEVICE_ID_9K_E) ||
|
|
(device_id == TW_CL_DEVICE_ID_9K_SA)))
|
|
return(TW_CL_TRUE);
|
|
return(TW_CL_FALSE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_get_pci_bar_info
|
|
* Description: Returns PCI BAR info.
|
|
*
|
|
* Input: device_id -- device id of the controller
|
|
* bar_type -- type of PCI BAR in question
|
|
* Output: bar_num -- PCI BAR number corresponding to bar_type
|
|
* bar0_offset -- byte offset from BAR 0 (0x10 in
|
|
* PCI config space)
|
|
* bar_size -- size, in bytes, of the BAR in question
|
|
* Return value: 0 -- success
|
|
* non-zero -- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_get_pci_bar_info(TW_INT32 device_id, TW_INT32 bar_type,
|
|
TW_INT32 *bar_num, TW_INT32 *bar0_offset, TW_INT32 *bar_size)
|
|
{
|
|
TW_INT32 error = TW_OSL_ESUCCESS;
|
|
|
|
switch(device_id) {
|
|
case TW_CL_DEVICE_ID_9K:
|
|
switch(bar_type) {
|
|
case TW_CL_BAR_TYPE_IO:
|
|
*bar_num = 0;
|
|
*bar0_offset = 0;
|
|
*bar_size = 4;
|
|
break;
|
|
|
|
case TW_CL_BAR_TYPE_MEM:
|
|
*bar_num = 1;
|
|
*bar0_offset = 0x4;
|
|
*bar_size = 8;
|
|
break;
|
|
|
|
case TW_CL_BAR_TYPE_SBUF:
|
|
*bar_num = 2;
|
|
*bar0_offset = 0xC;
|
|
*bar_size = 8;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TW_CL_DEVICE_ID_9K_X:
|
|
case TW_CL_DEVICE_ID_9K_E:
|
|
case TW_CL_DEVICE_ID_9K_SA:
|
|
switch(bar_type) {
|
|
case TW_CL_BAR_TYPE_IO:
|
|
*bar_num = 2;
|
|
*bar0_offset = 0x10;
|
|
*bar_size = 4;
|
|
break;
|
|
|
|
case TW_CL_BAR_TYPE_MEM:
|
|
*bar_num = 1;
|
|
*bar0_offset = 0x8;
|
|
*bar_size = 8;
|
|
break;
|
|
|
|
case TW_CL_BAR_TYPE_SBUF:
|
|
*bar_num = 0;
|
|
*bar0_offset = 0;
|
|
*bar_size = 8;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error = TW_OSL_ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_get_mem_requirements
|
|
* Description: Provides info about Common Layer requirements for a
|
|
* controller, given the controller type (in 'flags').
|
|
* Input: ctlr_handle -- controller handle
|
|
* flags -- more info passed by the OS Layer
|
|
* device_id -- device id of the controller
|
|
* max_simult_reqs -- maximum # of simultaneous
|
|
* requests that the OS Layer expects
|
|
* the Common Layer to support
|
|
* max_aens -- maximun # of AEN's needed to be supported
|
|
* Output: alignment -- alignment needed for all DMA'able
|
|
* buffers
|
|
* sg_size_factor -- every SG element should have a size
|
|
* that's a multiple of this number
|
|
* non_dma_mem_size -- # of bytes of memory needed for
|
|
* non-DMA purposes
|
|
* dma_mem_size -- # of bytes of DMA'able memory needed
|
|
* per_req_dma_mem_size -- # of bytes of DMA'able memory
|
|
* needed per request, if applicable
|
|
* per_req_non_dma_mem_size -- # of bytes of memory needed
|
|
* per request for non-DMA purposes,
|
|
* if applicable
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_get_mem_requirements(struct tw_cl_ctlr_handle *ctlr_handle,
|
|
TW_UINT32 flags, TW_INT32 device_id, TW_INT32 max_simult_reqs,
|
|
TW_INT32 max_aens, TW_UINT32 *alignment, TW_UINT32 *sg_size_factor,
|
|
TW_UINT32 *non_dma_mem_size, TW_UINT32 *dma_mem_size
|
|
)
|
|
{
|
|
if (device_id == 0)
|
|
device_id = TW_CL_DEVICE_ID_9K;
|
|
|
|
if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Too many simultaneous requests to support!",
|
|
"requested = %d, supported = %d, error = %d\n",
|
|
max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS,
|
|
TW_OSL_EBIG);
|
|
return(TW_OSL_EBIG);
|
|
}
|
|
|
|
*alignment = TWA_ALIGNMENT(device_id);
|
|
*sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id);
|
|
|
|
/*
|
|
* Total non-DMA memory needed is the sum total of memory needed for
|
|
* the controller context, request packets (including the 1 needed for
|
|
* CL internal requests), and event packets.
|
|
*/
|
|
|
|
*non_dma_mem_size = sizeof(struct tw_cli_ctlr_context) +
|
|
(sizeof(struct tw_cli_req_context) * (max_simult_reqs + 1)) +
|
|
(sizeof(struct tw_cl_event_packet) * max_aens);
|
|
|
|
|
|
/*
|
|
* Total DMA'able memory needed is the sum total of memory needed for
|
|
* all command packets (including the 1 needed for CL internal
|
|
* requests), and memory needed to hold the payload for internal
|
|
* requests.
|
|
*/
|
|
|
|
*dma_mem_size = (sizeof(struct tw_cl_command_packet) *
|
|
(max_simult_reqs + 1)) + (TW_CLI_SECTOR_SIZE);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_init_ctlr
|
|
* Description: Initializes driver data structures for the controller.
|
|
*
|
|
* Input: ctlr_handle -- controller handle
|
|
* flags -- more info passed by the OS Layer
|
|
* device_id -- device id of the controller
|
|
* max_simult_reqs -- maximum # of simultaneous requests
|
|
* that the OS Layer expects the Common
|
|
* Layer to support
|
|
* max_aens -- maximun # of AEN's needed to be supported
|
|
* non_dma_mem -- ptr to allocated non-DMA memory
|
|
* dma_mem -- ptr to allocated DMA'able memory
|
|
* dma_mem_phys -- physical address of dma_mem
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_init_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags,
|
|
TW_INT32 device_id, TW_INT32 max_simult_reqs, TW_INT32 max_aens,
|
|
TW_VOID *non_dma_mem, TW_VOID *dma_mem, TW_UINT64 dma_mem_phys
|
|
)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr;
|
|
struct tw_cli_req_context *req;
|
|
TW_UINT8 *free_non_dma_mem;
|
|
TW_INT32 error = TW_OSL_ESUCCESS;
|
|
TW_INT32 i;
|
|
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
if (flags & TW_CL_START_CTLR_ONLY) {
|
|
ctlr = (struct tw_cli_ctlr_context *)
|
|
(ctlr_handle->cl_ctlr_ctxt);
|
|
goto start_ctlr;
|
|
}
|
|
|
|
if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Too many simultaneous requests to support!",
|
|
"requested = %d, supported = %d, error = %d\n",
|
|
max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS,
|
|
TW_OSL_EBIG);
|
|
return(TW_OSL_EBIG);
|
|
}
|
|
|
|
if ((non_dma_mem == TW_CL_NULL) || (dma_mem == TW_CL_NULL)
|
|
) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1001, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Insufficient memory for Common Layer's internal usage",
|
|
"error = %d\n", TW_OSL_ENOMEM);
|
|
return(TW_OSL_ENOMEM);
|
|
}
|
|
|
|
tw_osl_memzero(non_dma_mem, sizeof(struct tw_cli_ctlr_context) +
|
|
(sizeof(struct tw_cli_req_context) * (max_simult_reqs + 1)) +
|
|
(sizeof(struct tw_cl_event_packet) * max_aens));
|
|
|
|
tw_osl_memzero(dma_mem,
|
|
(sizeof(struct tw_cl_command_packet) *
|
|
(max_simult_reqs + 1)) +
|
|
TW_CLI_SECTOR_SIZE);
|
|
|
|
free_non_dma_mem = (TW_UINT8 *)non_dma_mem;
|
|
|
|
ctlr = (struct tw_cli_ctlr_context *)free_non_dma_mem;
|
|
free_non_dma_mem += sizeof(struct tw_cli_ctlr_context);
|
|
|
|
ctlr_handle->cl_ctlr_ctxt = ctlr;
|
|
ctlr->ctlr_handle = ctlr_handle;
|
|
|
|
ctlr->device_id = (TW_UINT32)device_id;
|
|
ctlr->arch_id = TWA_ARCH_ID(device_id);
|
|
ctlr->flags = flags;
|
|
ctlr->sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id);
|
|
ctlr->max_simult_reqs = max_simult_reqs + 1;
|
|
ctlr->max_aens_supported = max_aens;
|
|
|
|
/* Initialize queues of CL internal request context packets. */
|
|
tw_cli_req_q_init(ctlr, TW_CLI_FREE_Q);
|
|
tw_cli_req_q_init(ctlr, TW_CLI_BUSY_Q);
|
|
tw_cli_req_q_init(ctlr, TW_CLI_PENDING_Q);
|
|
tw_cli_req_q_init(ctlr, TW_CLI_COMPLETE_Q);
|
|
|
|
/* Initialize all locks used by CL. */
|
|
ctlr->gen_lock = &(ctlr->gen_lock_handle);
|
|
tw_osl_init_lock(ctlr_handle, "tw_cl_gen_lock", ctlr->gen_lock);
|
|
ctlr->io_lock = &(ctlr->io_lock_handle);
|
|
tw_osl_init_lock(ctlr_handle, "tw_cl_io_lock", ctlr->io_lock);
|
|
/*
|
|
* If 64 bit cmd pkt addresses are used, we will need to serialize
|
|
* writes to the hardware (across registers), since existing (G66)
|
|
* hardware will get confused if, for example, we wrote the low 32 bits
|
|
* of the cmd pkt address, followed by a response interrupt mask to the
|
|
* control register, followed by the high 32 bits of the cmd pkt
|
|
* address. It will then interpret the value written to the control
|
|
* register as the low cmd pkt address. So, for this case, we will
|
|
* make a note that we will need to synchronize control register writes
|
|
* with command register writes.
|
|
*/
|
|
if ((ctlr->flags & TW_CL_64BIT_ADDRESSES) &&
|
|
((ctlr->device_id == TW_CL_DEVICE_ID_9K) ||
|
|
(ctlr->device_id == TW_CL_DEVICE_ID_9K_X) ||
|
|
(ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
|
|
(ctlr->device_id == TW_CL_DEVICE_ID_9K_SA))) {
|
|
ctlr->state |= TW_CLI_CTLR_STATE_G66_WORKAROUND_NEEDED;
|
|
ctlr->intr_lock = ctlr->io_lock;
|
|
} else {
|
|
ctlr->intr_lock = &(ctlr->intr_lock_handle);
|
|
tw_osl_init_lock(ctlr_handle, "tw_cl_intr_lock",
|
|
ctlr->intr_lock);
|
|
}
|
|
|
|
/* Initialize CL internal request context packets. */
|
|
ctlr->req_ctxt_buf = (struct tw_cli_req_context *)free_non_dma_mem;
|
|
free_non_dma_mem += (sizeof(struct tw_cli_req_context) *
|
|
(
|
|
max_simult_reqs +
|
|
1));
|
|
|
|
ctlr->cmd_pkt_buf = (struct tw_cl_command_packet *)dma_mem;
|
|
ctlr->cmd_pkt_phys = dma_mem_phys;
|
|
|
|
ctlr->internal_req_data = (TW_UINT8 *)
|
|
(ctlr->cmd_pkt_buf +
|
|
(
|
|
max_simult_reqs +
|
|
1));
|
|
ctlr->internal_req_data_phys = ctlr->cmd_pkt_phys +
|
|
(sizeof(struct tw_cl_command_packet) *
|
|
(
|
|
max_simult_reqs +
|
|
1));
|
|
|
|
for (i = 0;
|
|
i < (
|
|
max_simult_reqs +
|
|
1); i++) {
|
|
req = &(ctlr->req_ctxt_buf[i]);
|
|
|
|
req->cmd_pkt = &(ctlr->cmd_pkt_buf[i]);
|
|
req->cmd_pkt_phys = ctlr->cmd_pkt_phys +
|
|
(i * sizeof(struct tw_cl_command_packet));
|
|
|
|
req->request_id = i;
|
|
req->ctlr = ctlr;
|
|
|
|
/* Insert request into the free queue. */
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
}
|
|
|
|
/* Initialize the AEN queue. */
|
|
ctlr->aen_queue = (struct tw_cl_event_packet *)free_non_dma_mem;
|
|
|
|
|
|
start_ctlr:
|
|
/*
|
|
* Disable interrupts. Interrupts will be enabled in tw_cli_start_ctlr
|
|
* (only) if initialization succeeded.
|
|
*/
|
|
tw_cli_disable_interrupts(ctlr);
|
|
|
|
/* Initialize the controller. */
|
|
if ((error = tw_cli_start_ctlr(ctlr))) {
|
|
/* Soft reset the controller, and try one more time. */
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1002, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Controller initialization failed. Retrying...",
|
|
"error = %d\n", error);
|
|
if ((error = tw_cli_soft_reset(ctlr))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1003, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Controller soft reset failed",
|
|
"error = %d\n", error);
|
|
return(error);
|
|
} else if ((error = tw_cli_start_ctlr(ctlr))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1004, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Controller initialization retry failed",
|
|
"error = %d\n", error);
|
|
return(error);
|
|
}
|
|
}
|
|
/* Notify some info about the controller to the OSL. */
|
|
tw_cli_notify_ctlr_info(ctlr);
|
|
|
|
/* Mark the controller as active. */
|
|
ctlr->state |= TW_CLI_CTLR_STATE_ACTIVE;
|
|
return(error);
|
|
}
|
|
|
|
/*
|
|
* Function name: tw_cli_start_ctlr
|
|
* Description: Establishes a logical connection with the controller.
|
|
* Determines whether or not the driver is compatible
|
|
* with the firmware on the controller, before proceeding
|
|
* to work with it.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_start_ctlr(struct tw_cli_ctlr_context *ctlr)
|
|
{
|
|
TW_UINT16 fw_on_ctlr_srl = 0;
|
|
TW_UINT16 fw_on_ctlr_arch_id = 0;
|
|
TW_UINT16 fw_on_ctlr_branch = 0;
|
|
TW_UINT16 fw_on_ctlr_build = 0;
|
|
TW_UINT32 init_connect_result = 0;
|
|
TW_INT32 error = TW_OSL_ESUCCESS;
|
|
|
|
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Wait for the controller to become ready. */
|
|
if ((error = tw_cli_poll_status(ctlr,
|
|
TWA_STATUS_MICROCONTROLLER_READY,
|
|
TW_CLI_REQUEST_TIMEOUT_PERIOD))) {
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1009, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Microcontroller not ready",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
/* Drain the response queue. */
|
|
if ((error = tw_cli_drain_response_queue(ctlr))) {
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x100A, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't drain response queue",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
/* Establish a logical connection with the controller. */
|
|
if ((error = tw_cli_init_connection(ctlr,
|
|
(TW_UINT16)(ctlr->max_simult_reqs),
|
|
TWA_EXTENDED_INIT_CONNECT, TWA_CURRENT_FW_SRL,
|
|
(TW_UINT16)(ctlr->arch_id),
|
|
TWA_CURRENT_FW_BRANCH(ctlr->arch_id),
|
|
TWA_CURRENT_FW_BUILD(ctlr->arch_id),
|
|
&fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
|
|
&fw_on_ctlr_branch, &fw_on_ctlr_build,
|
|
&init_connect_result))) {
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x100B, 0x2, TW_CL_SEVERITY_WARNING_STRING,
|
|
"Can't initialize connection in current mode",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
{
|
|
/* See if we can at least work with the firmware on the
|
|
* controller in the current mode.
|
|
*/
|
|
if (init_connect_result & TWA_CTLR_FW_COMPATIBLE) {
|
|
/* Yes, we can. Make note of the operating mode. */
|
|
if (init_connect_result & TWA_CTLR_FW_SAME_OR_NEWER) {
|
|
ctlr->working_srl = TWA_CURRENT_FW_SRL;
|
|
ctlr->working_branch =
|
|
TWA_CURRENT_FW_BRANCH(ctlr->arch_id);
|
|
ctlr->working_build =
|
|
TWA_CURRENT_FW_BUILD(ctlr->arch_id);
|
|
} else {
|
|
ctlr->working_srl = fw_on_ctlr_srl;
|
|
ctlr->working_branch = fw_on_ctlr_branch;
|
|
ctlr->working_build = fw_on_ctlr_build;
|
|
}
|
|
} else {
|
|
/*
|
|
* No, we can't. See if we can at least work with
|
|
* it in the base mode.
|
|
*/
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1010, 0x2, TW_CL_SEVERITY_WARNING_STRING,
|
|
"Driver/Firmware mismatch. "
|
|
"Negotiating for base level...",
|
|
" ");
|
|
if ((error = tw_cli_init_connection(ctlr,
|
|
(TW_UINT16)(ctlr->max_simult_reqs),
|
|
TWA_EXTENDED_INIT_CONNECT,
|
|
TWA_BASE_FW_SRL,
|
|
(TW_UINT16)(ctlr->arch_id),
|
|
TWA_BASE_FW_BRANCH, TWA_BASE_FW_BUILD,
|
|
&fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
|
|
&fw_on_ctlr_branch, &fw_on_ctlr_build,
|
|
&init_connect_result))) {
|
|
tw_cl_create_event(ctlr->ctlr_handle,
|
|
TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1011, 0x1,
|
|
TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't initialize connection in "
|
|
"base mode",
|
|
" ");
|
|
return(error);
|
|
}
|
|
if (!(init_connect_result & TWA_CTLR_FW_COMPATIBLE)) {
|
|
/*
|
|
* The firmware on the controller is not even
|
|
* compatible with our base mode. We cannot
|
|
* work with it. Bail...
|
|
*/
|
|
return(1);
|
|
}
|
|
/*
|
|
* We can work with this firmware, but only in
|
|
* base mode.
|
|
*/
|
|
ctlr->working_srl = TWA_BASE_FW_SRL;
|
|
ctlr->working_branch = TWA_BASE_FW_BRANCH;
|
|
ctlr->working_build = TWA_BASE_FW_BUILD;
|
|
ctlr->operating_mode = TWA_BASE_MODE;
|
|
}
|
|
ctlr->fw_on_ctlr_srl = fw_on_ctlr_srl;
|
|
ctlr->fw_on_ctlr_branch = fw_on_ctlr_branch;
|
|
ctlr->fw_on_ctlr_build = fw_on_ctlr_build;
|
|
}
|
|
|
|
/* Drain the AEN queue */
|
|
if ((error = tw_cli_drain_aen_queue(ctlr)))
|
|
/*
|
|
* We will just print that we couldn't drain the AEN queue.
|
|
* There's no need to bail out.
|
|
*/
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1014, 0x2, TW_CL_SEVERITY_WARNING_STRING,
|
|
"Can't drain AEN queue",
|
|
"error = %d", error);
|
|
|
|
/* Enable interrupts. */
|
|
tw_cli_enable_interrupts(ctlr);
|
|
|
|
return(TW_OSL_ESUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_shutdown_ctlr
|
|
* Description: Closes logical connection with the controller.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* flags -- more info passed by the OS Layer
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_shutdown_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
TW_INT32 error;
|
|
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
/*
|
|
* Mark the controller as inactive, disable any further interrupts,
|
|
* and notify the controller that we are going down.
|
|
*/
|
|
ctlr->state &= ~TW_CLI_CTLR_STATE_ACTIVE;
|
|
|
|
tw_cli_disable_interrupts(ctlr);
|
|
|
|
/* Let the controller know that we are going down. */
|
|
if ((error = tw_cli_init_connection(ctlr, TWA_SHUTDOWN_MESSAGE_CREDITS,
|
|
0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL,
|
|
TW_CL_NULL, TW_CL_NULL)))
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1015, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't close connection with controller",
|
|
"error = %d", error);
|
|
|
|
if (flags & TW_CL_STOP_CTLR_ONLY)
|
|
goto ret;
|
|
|
|
/* Destroy all locks used by CL. */
|
|
tw_osl_destroy_lock(ctlr_handle, ctlr->gen_lock);
|
|
tw_osl_destroy_lock(ctlr_handle, ctlr->io_lock);
|
|
if (!(ctlr->flags & TW_CL_64BIT_ADDRESSES))
|
|
tw_osl_destroy_lock(ctlr_handle, ctlr->intr_lock);
|
|
|
|
ret:
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_init_connection
|
|
* Description: Sends init_connection cmd to firmware
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* message_credits -- max # of requests that we might send
|
|
* down simultaneously. This will be
|
|
* typically set to 256 at init-time or
|
|
* after a reset, and to 1 at shutdown-time
|
|
* set_features -- indicates if we intend to use 64-bit
|
|
* sg, also indicates if we want to do a
|
|
* basic or an extended init_connection;
|
|
*
|
|
* Note: The following input/output parameters are valid, only in case of an
|
|
* extended init_connection:
|
|
*
|
|
* current_fw_srl -- srl of fw we are bundled
|
|
* with, if any; 0 otherwise
|
|
* current_fw_arch_id -- arch_id of fw we are bundled
|
|
* with, if any; 0 otherwise
|
|
* current_fw_branch -- branch # of fw we are bundled
|
|
* with, if any; 0 otherwise
|
|
* current_fw_build -- build # of fw we are bundled
|
|
* with, if any; 0 otherwise
|
|
* Output: fw_on_ctlr_srl -- srl of fw on ctlr
|
|
* fw_on_ctlr_arch_id -- arch_id of fw on ctlr
|
|
* fw_on_ctlr_branch -- branch # of fw on ctlr
|
|
* fw_on_ctlr_build -- build # of fw on ctlr
|
|
* init_connect_result -- result bitmap of fw response
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_init_connection(struct tw_cli_ctlr_context *ctlr,
|
|
TW_UINT16 message_credits, TW_UINT32 set_features,
|
|
TW_UINT16 current_fw_srl, TW_UINT16 current_fw_arch_id,
|
|
TW_UINT16 current_fw_branch, TW_UINT16 current_fw_build,
|
|
TW_UINT16 *fw_on_ctlr_srl, TW_UINT16 *fw_on_ctlr_arch_id,
|
|
TW_UINT16 *fw_on_ctlr_branch, TW_UINT16 *fw_on_ctlr_build,
|
|
TW_UINT32 *init_connect_result)
|
|
{
|
|
struct tw_cli_req_context *req;
|
|
struct tw_cl_command_init_connect *init_connect;
|
|
TW_INT32 error = TW_OSL_EBUSY;
|
|
|
|
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Get a request packet. */
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL)
|
|
goto out;
|
|
|
|
req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
|
|
|
|
/* Build the cmd pkt. */
|
|
init_connect = &(req->cmd_pkt->command.cmd_pkt_7k.init_connect);
|
|
|
|
req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
init_connect->res1__opcode =
|
|
BUILD_RES__OPCODE(0, TWA_FW_CMD_INIT_CONNECTION);
|
|
init_connect->request_id =
|
|
(TW_UINT8)(TW_CL_SWAP16(req->request_id));
|
|
init_connect->message_credits = TW_CL_SWAP16(message_credits);
|
|
init_connect->features = TW_CL_SWAP32(set_features);
|
|
if (ctlr->flags & TW_CL_64BIT_ADDRESSES)
|
|
init_connect->features |= TW_CL_SWAP32(TWA_64BIT_SG_ADDRESSES);
|
|
if (set_features & TWA_EXTENDED_INIT_CONNECT) {
|
|
/*
|
|
* Fill in the extra fields needed for an extended
|
|
* init_connect.
|
|
*/
|
|
init_connect->size = 6;
|
|
init_connect->fw_srl = TW_CL_SWAP16(current_fw_srl);
|
|
init_connect->fw_arch_id = TW_CL_SWAP16(current_fw_arch_id);
|
|
init_connect->fw_branch = TW_CL_SWAP16(current_fw_branch);
|
|
init_connect->fw_build = TW_CL_SWAP16(current_fw_build);
|
|
} else
|
|
init_connect->size = 3;
|
|
|
|
/* Submit the command, and wait for it to complete. */
|
|
error = tw_cli_submit_and_poll_request(req,
|
|
TW_CLI_REQUEST_TIMEOUT_PERIOD);
|
|
if (error == TW_OSL_ETIMEDOUT)
|
|
/* Clean-up done by tw_cli_submit_and_poll_request. */
|
|
return(error);
|
|
if (error)
|
|
goto out;
|
|
if ((error = init_connect->status)) {
|
|
tw_cli_create_ctlr_event(ctlr,
|
|
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
|
|
&(req->cmd_pkt->cmd_hdr));
|
|
goto out;
|
|
}
|
|
if (set_features & TWA_EXTENDED_INIT_CONNECT) {
|
|
*fw_on_ctlr_srl = TW_CL_SWAP16(init_connect->fw_srl);
|
|
*fw_on_ctlr_arch_id = TW_CL_SWAP16(init_connect->fw_arch_id);
|
|
*fw_on_ctlr_branch = TW_CL_SWAP16(init_connect->fw_branch);
|
|
*fw_on_ctlr_build = TW_CL_SWAP16(init_connect->fw_build);
|
|
*init_connect_result = TW_CL_SWAP32(init_connect->result);
|
|
}
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
return(error);
|
|
|
|
out:
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1016, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"init_connection failed",
|
|
"error = %d", error);
|
|
if (req)
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
return(error);
|
|
}
|
|
|
|
|