1537078d8f
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
1442 lines
43 KiB
C
1442 lines
43 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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 I/O 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"
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_start_io
|
|
* Description: Interface to OS Layer for accepting SCSI requests.
|
|
*
|
|
* Input: ctlr_handle -- controller handle
|
|
* req_pkt -- OSL built request packet
|
|
* req_handle -- request handle
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_start_io(struct tw_cl_ctlr_handle *ctlr_handle,
|
|
struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr;
|
|
struct tw_cli_req_context *req;
|
|
struct tw_cl_command_9k *cmd;
|
|
struct tw_cl_scsi_req_packet *scsi_req;
|
|
TW_INT32 error = TW_CL_ERR_REQ_SUCCESS;
|
|
|
|
tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
|
|
/*
|
|
* If working with a firmware version that does not support multiple
|
|
* luns, and this request is directed at a non-zero lun, error it
|
|
* back right away.
|
|
*/
|
|
if ((req_pkt->gen_req_pkt.scsi_req.lun) &&
|
|
(ctlr->working_srl < TWA_MULTI_LUN_FW_SRL)) {
|
|
req_pkt->status |= (TW_CL_ERR_REQ_INVALID_LUN |
|
|
TW_CL_ERR_REQ_SCSI_ERROR);
|
|
req_pkt->tw_osl_callback(req_handle);
|
|
return(TW_CL_ERR_REQ_SUCCESS);
|
|
}
|
|
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL) {
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
|
|
"Out of request context packets: returning busy");
|
|
return(TW_OSL_EBUSY);
|
|
}
|
|
|
|
req_handle->cl_req_ctxt = req;
|
|
req->req_handle = req_handle;
|
|
req->orig_req = req_pkt;
|
|
req->tw_cli_callback = tw_cli_complete_io;
|
|
|
|
req->flags |= TW_CLI_REQ_FLAGS_EXTERNAL;
|
|
req->flags |= TW_CLI_REQ_FLAGS_9K;
|
|
|
|
scsi_req = &(req_pkt->gen_req_pkt.scsi_req);
|
|
|
|
/* Build the cmd pkt. */
|
|
cmd = &(req->cmd_pkt->command.cmd_pkt_9k);
|
|
|
|
req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
cmd->res__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
|
|
cmd->unit = (TW_UINT8)(scsi_req->unit);
|
|
cmd->lun_l4__req_id = TW_CL_SWAP16(
|
|
BUILD_LUN_L4__REQ_ID(scsi_req->lun, req->request_id));
|
|
cmd->status = 0;
|
|
cmd->sgl_offset = 16; /* offset from end of hdr = max cdb len */
|
|
tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len);
|
|
|
|
if (req_pkt->flags & TW_CL_REQ_CALLBACK_FOR_SGLIST) {
|
|
TW_UINT32 num_sgl_entries;
|
|
|
|
req_pkt->tw_osl_sgl_callback(req_handle, cmd->sg_list,
|
|
&num_sgl_entries);
|
|
cmd->lun_h4__sgl_entries =
|
|
TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
|
|
num_sgl_entries));
|
|
} else {
|
|
cmd->lun_h4__sgl_entries =
|
|
TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
|
|
scsi_req->sgl_entries));
|
|
tw_cli_fill_sg_list(ctlr, scsi_req->sg_list,
|
|
cmd->sg_list, scsi_req->sgl_entries);
|
|
}
|
|
|
|
if (((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL) ||
|
|
(ctlr->reset_in_progress)) {
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
|
|
TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
|
|
} else if ((error = tw_cli_submit_cmd(req))) {
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
|
|
"Could not start request. request = %p, error = %d",
|
|
req, error);
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_submit_cmd
|
|
* Description: Submits a cmd to firmware.
|
|
*
|
|
* Input: req -- ptr to CL internal request context
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_submit_cmd(struct tw_cli_req_context *req)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr = req->ctlr;
|
|
struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle;
|
|
TW_UINT32 status_reg;
|
|
TW_INT32 error = 0;
|
|
|
|
tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Serialize access to the controller cmd queue. */
|
|
tw_osl_get_lock(ctlr_handle, ctlr->io_lock);
|
|
|
|
/* For 9650SE first write low 4 bytes */
|
|
if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
|
|
(ctlr->device_id == TW_CL_DEVICE_ID_9K_SA))
|
|
tw_osl_write_reg(ctlr_handle,
|
|
TWA_COMMAND_QUEUE_OFFSET_LOW,
|
|
(TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
|
|
|
|
status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
|
|
if (status_reg & TWA_STATUS_COMMAND_QUEUE_FULL) {
|
|
struct tw_cl_req_packet *req_pkt =
|
|
(struct tw_cl_req_packet *)(req->orig_req);
|
|
|
|
tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(),
|
|
"Cmd queue full");
|
|
|
|
if ((req->flags & TW_CLI_REQ_FLAGS_INTERNAL)
|
|
|| ((req_pkt) &&
|
|
(req_pkt->flags & TW_CL_REQ_RETRY_ON_BUSY))
|
|
) {
|
|
if (req->state != TW_CLI_REQ_STATE_PENDING) {
|
|
tw_cli_dbg_printf(2, ctlr_handle,
|
|
tw_osl_cur_func(),
|
|
"pending internal/ioctl request");
|
|
req->state = TW_CLI_REQ_STATE_PENDING;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
|
|
/* Unmask command interrupt. */
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
|
|
TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
|
|
} else
|
|
error = TW_OSL_EBUSY;
|
|
} else {
|
|
error = TW_OSL_EBUSY;
|
|
}
|
|
} else {
|
|
tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
|
|
"Submitting command");
|
|
|
|
/* Insert command into busy queue */
|
|
req->state = TW_CLI_REQ_STATE_BUSY;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_BUSY_Q);
|
|
|
|
if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
|
|
(ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) {
|
|
/* Now write the high 4 bytes */
|
|
tw_osl_write_reg(ctlr_handle,
|
|
TWA_COMMAND_QUEUE_OFFSET_HIGH,
|
|
(TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
|
|
} else {
|
|
if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
|
|
/* First write the low 4 bytes, then the high 4. */
|
|
tw_osl_write_reg(ctlr_handle,
|
|
TWA_COMMAND_QUEUE_OFFSET_LOW,
|
|
(TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
|
|
tw_osl_write_reg(ctlr_handle,
|
|
TWA_COMMAND_QUEUE_OFFSET_HIGH,
|
|
(TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
|
|
} else
|
|
tw_osl_write_reg(ctlr_handle,
|
|
TWA_COMMAND_QUEUE_OFFSET,
|
|
(TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
|
|
}
|
|
}
|
|
|
|
tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
|
|
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_fw_passthru
|
|
* Description: Interface to OS Layer for accepting firmware
|
|
* passthru requests.
|
|
* Input: ctlr_handle -- controller handle
|
|
* req_pkt -- OSL built request packet
|
|
* req_handle -- request handle
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_fw_passthru(struct tw_cl_ctlr_handle *ctlr_handle,
|
|
struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr;
|
|
struct tw_cli_req_context *req;
|
|
union tw_cl_command_7k *cmd_7k;
|
|
struct tw_cl_command_9k *cmd_9k;
|
|
struct tw_cl_passthru_req_packet *pt_req;
|
|
TW_UINT8 opcode;
|
|
TW_UINT8 sgl_offset;
|
|
TW_VOID *sgl = TW_CL_NULL;
|
|
TW_INT32 error = TW_CL_ERR_REQ_SUCCESS;
|
|
|
|
tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL) {
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
|
|
"Out of request context packets: returning busy");
|
|
return(TW_OSL_EBUSY);
|
|
}
|
|
|
|
req_handle->cl_req_ctxt = req;
|
|
req->req_handle = req_handle;
|
|
req->orig_req = req_pkt;
|
|
req->tw_cli_callback = tw_cli_complete_io;
|
|
|
|
req->flags |= TW_CLI_REQ_FLAGS_PASSTHRU;
|
|
|
|
pt_req = &(req_pkt->gen_req_pkt.pt_req);
|
|
|
|
tw_osl_memcpy(req->cmd_pkt, pt_req->cmd_pkt,
|
|
pt_req->cmd_pkt_length);
|
|
/* Build the cmd pkt. */
|
|
if ((opcode = GET_OPCODE(((TW_UINT8 *)
|
|
(pt_req->cmd_pkt))[sizeof(struct tw_cl_command_header)]))
|
|
== TWA_FW_CMD_EXECUTE_SCSI) {
|
|
TW_UINT16 lun_l4, lun_h4;
|
|
|
|
tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
|
|
"passthru: 9k cmd pkt");
|
|
req->flags |= TW_CLI_REQ_FLAGS_9K;
|
|
cmd_9k = &(req->cmd_pkt->command.cmd_pkt_9k);
|
|
lun_l4 = GET_LUN_L4(cmd_9k->lun_l4__req_id);
|
|
lun_h4 = GET_LUN_H4(cmd_9k->lun_h4__sgl_entries);
|
|
cmd_9k->lun_l4__req_id = TW_CL_SWAP16(
|
|
BUILD_LUN_L4__REQ_ID(lun_l4, req->request_id));
|
|
if (pt_req->sgl_entries) {
|
|
cmd_9k->lun_h4__sgl_entries =
|
|
TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(lun_h4,
|
|
pt_req->sgl_entries));
|
|
sgl = (TW_VOID *)(cmd_9k->sg_list);
|
|
}
|
|
} else {
|
|
tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
|
|
"passthru: 7k cmd pkt");
|
|
cmd_7k = &(req->cmd_pkt->command.cmd_pkt_7k);
|
|
cmd_7k->generic.request_id =
|
|
(TW_UINT8)(TW_CL_SWAP16(req->request_id));
|
|
if ((sgl_offset =
|
|
GET_SGL_OFF(cmd_7k->generic.sgl_off__opcode))) {
|
|
if (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)
|
|
sgl = (((TW_UINT32 *)cmd_7k) + cmd_7k->generic.size);
|
|
else
|
|
sgl = (((TW_UINT32 *)cmd_7k) + sgl_offset);
|
|
cmd_7k->generic.size += pt_req->sgl_entries *
|
|
((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
|
|
}
|
|
}
|
|
|
|
if (sgl)
|
|
tw_cli_fill_sg_list(ctlr, pt_req->sg_list,
|
|
sgl, pt_req->sgl_entries);
|
|
|
|
if (((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL) ||
|
|
(ctlr->reset_in_progress)) {
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
|
|
TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
|
|
} else if ((error = tw_cli_submit_cmd(req))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1100, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Failed to start passthru command",
|
|
"error = %d", error);
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_ioctl
|
|
* Description: Handler of CL supported ioctl cmds.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* cmd -- ioctl cmd
|
|
* buf -- ptr to buffer in kernel memory, which is
|
|
* a copy of the input buffer in user-space
|
|
* Output: buf -- ptr to buffer in kernel memory, which will
|
|
* need to be copied to the output buffer in
|
|
* user-space
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_ioctl(struct tw_cl_ctlr_handle *ctlr_handle, u_long cmd, TW_VOID *buf)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
struct tw_cl_ioctl_packet *user_buf =
|
|
(struct tw_cl_ioctl_packet *)buf;
|
|
struct tw_cl_event_packet event_buf;
|
|
TW_INT32 event_index;
|
|
TW_INT32 start_index;
|
|
TW_INT32 error = TW_OSL_ESUCCESS;
|
|
|
|
tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Serialize access to the AEN queue and the ioctl lock. */
|
|
tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);
|
|
|
|
switch (cmd) {
|
|
case TW_CL_IOCTL_GET_FIRST_EVENT:
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get First Event");
|
|
|
|
if (ctlr->aen_q_wrapped) {
|
|
if (ctlr->aen_q_overflow) {
|
|
/*
|
|
* The aen queue has wrapped, even before some
|
|
* events have been retrieved. Let the caller
|
|
* know that he missed out on some AEN's.
|
|
*/
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_OVERFLOW;
|
|
ctlr->aen_q_overflow = TW_CL_FALSE;
|
|
} else
|
|
user_buf->driver_pkt.status = 0;
|
|
event_index = ctlr->aen_head;
|
|
} else {
|
|
if (ctlr->aen_head == ctlr->aen_tail) {
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
user_buf->driver_pkt.status = 0;
|
|
event_index = ctlr->aen_tail; /* = 0 */
|
|
}
|
|
tw_osl_memcpy(user_buf->data_buf,
|
|
&(ctlr->aen_queue[event_index]),
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
|
|
|
|
break;
|
|
|
|
|
|
case TW_CL_IOCTL_GET_LAST_EVENT:
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get Last Event");
|
|
|
|
if (ctlr->aen_q_wrapped) {
|
|
if (ctlr->aen_q_overflow) {
|
|
/*
|
|
* The aen queue has wrapped, even before some
|
|
* events have been retrieved. Let the caller
|
|
* know that he missed out on some AEN's.
|
|
*/
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_OVERFLOW;
|
|
ctlr->aen_q_overflow = TW_CL_FALSE;
|
|
} else
|
|
user_buf->driver_pkt.status = 0;
|
|
} else {
|
|
if (ctlr->aen_head == ctlr->aen_tail) {
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
user_buf->driver_pkt.status = 0;
|
|
}
|
|
event_index = (ctlr->aen_head - 1 + ctlr->max_aens_supported) %
|
|
ctlr->max_aens_supported;
|
|
|
|
tw_osl_memcpy(user_buf->data_buf,
|
|
&(ctlr->aen_queue[event_index]),
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
|
|
|
|
break;
|
|
|
|
|
|
case TW_CL_IOCTL_GET_NEXT_EVENT:
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get Next Event");
|
|
|
|
user_buf->driver_pkt.status = 0;
|
|
if (ctlr->aen_q_wrapped) {
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get Next Event: wrapped");
|
|
if (ctlr->aen_q_overflow) {
|
|
/*
|
|
* The aen queue has wrapped, even before some
|
|
* events have been retrieved. Let the caller
|
|
* know that he missed out on some AEN's.
|
|
*/
|
|
tw_cli_dbg_printf(2, ctlr_handle,
|
|
tw_osl_cur_func(),
|
|
"Get Next Event: overflow");
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_OVERFLOW;
|
|
ctlr->aen_q_overflow = TW_CL_FALSE;
|
|
}
|
|
start_index = ctlr->aen_head;
|
|
} else {
|
|
if (ctlr->aen_head == ctlr->aen_tail) {
|
|
tw_cli_dbg_printf(3, ctlr_handle,
|
|
tw_osl_cur_func(),
|
|
"Get Next Event: empty queue");
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
start_index = ctlr->aen_tail; /* = 0 */
|
|
}
|
|
tw_osl_memcpy(&event_buf, user_buf->data_buf,
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
event_index = (start_index + event_buf.sequence_id -
|
|
ctlr->aen_queue[start_index].sequence_id + 1) %
|
|
ctlr->max_aens_supported;
|
|
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get Next Event: si = %x, ei = %x, ebsi = %x, "
|
|
"sisi = %x, eisi = %x",
|
|
start_index, event_index, event_buf.sequence_id,
|
|
ctlr->aen_queue[start_index].sequence_id,
|
|
ctlr->aen_queue[event_index].sequence_id);
|
|
|
|
if (! (ctlr->aen_queue[event_index].sequence_id >
|
|
event_buf.sequence_id)) {
|
|
/*
|
|
* We don't have any event matching the criterion. So,
|
|
* we have to report TW_CL_ERROR_NO_EVENTS. If we also
|
|
* encountered an overflow condition above, we cannot
|
|
* report both conditions during this call. We choose
|
|
* to report NO_EVENTS this time, and an overflow the
|
|
* next time we are called.
|
|
*/
|
|
if (user_buf->driver_pkt.status ==
|
|
TW_CL_ERROR_AEN_OVERFLOW) {
|
|
/*
|
|
* Make a note so we report the overflow
|
|
* next time.
|
|
*/
|
|
ctlr->aen_q_overflow = TW_CL_TRUE;
|
|
}
|
|
user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
/* Copy the event -- even if there has been an overflow. */
|
|
tw_osl_memcpy(user_buf->data_buf,
|
|
&(ctlr->aen_queue[event_index]),
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
|
|
|
|
break;
|
|
|
|
|
|
case TW_CL_IOCTL_GET_PREVIOUS_EVENT:
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get Previous Event");
|
|
|
|
user_buf->driver_pkt.status = 0;
|
|
if (ctlr->aen_q_wrapped) {
|
|
if (ctlr->aen_q_overflow) {
|
|
/*
|
|
* The aen queue has wrapped, even before some
|
|
* events have been retrieved. Let the caller
|
|
* know that he missed out on some AEN's.
|
|
*/
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_OVERFLOW;
|
|
ctlr->aen_q_overflow = TW_CL_FALSE;
|
|
}
|
|
start_index = ctlr->aen_head;
|
|
} else {
|
|
if (ctlr->aen_head == ctlr->aen_tail) {
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
start_index = ctlr->aen_tail; /* = 0 */
|
|
}
|
|
tw_osl_memcpy(&event_buf, user_buf->data_buf,
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
event_index = (start_index + event_buf.sequence_id -
|
|
ctlr->aen_queue[start_index].sequence_id - 1) %
|
|
ctlr->max_aens_supported;
|
|
|
|
if (! (ctlr->aen_queue[event_index].sequence_id <
|
|
event_buf.sequence_id)) {
|
|
/*
|
|
* We don't have any event matching the criterion. So,
|
|
* we have to report TW_CL_ERROR_NO_EVENTS. If we also
|
|
* encountered an overflow condition above, we cannot
|
|
* report both conditions during this call. We choose
|
|
* to report NO_EVENTS this time, and an overflow the
|
|
* next time we are called.
|
|
*/
|
|
if (user_buf->driver_pkt.status ==
|
|
TW_CL_ERROR_AEN_OVERFLOW) {
|
|
/*
|
|
* Make a note so we report the overflow
|
|
* next time.
|
|
*/
|
|
ctlr->aen_q_overflow = TW_CL_TRUE;
|
|
}
|
|
user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
|
|
break;
|
|
}
|
|
/* Copy the event -- even if there has been an overflow. */
|
|
tw_osl_memcpy(user_buf->data_buf,
|
|
&(ctlr->aen_queue[event_index]),
|
|
sizeof(struct tw_cl_event_packet));
|
|
|
|
ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
|
|
|
|
break;
|
|
|
|
|
|
case TW_CL_IOCTL_GET_LOCK:
|
|
{
|
|
struct tw_cl_lock_packet lock_pkt;
|
|
TW_TIME cur_time;
|
|
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get ioctl lock");
|
|
|
|
cur_time = tw_osl_get_local_time();
|
|
tw_osl_memcpy(&lock_pkt, user_buf->data_buf,
|
|
sizeof(struct tw_cl_lock_packet));
|
|
|
|
if ((ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) ||
|
|
(lock_pkt.force_flag) ||
|
|
(cur_time >= ctlr->ioctl_lock.timeout)) {
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"GET_LOCK: Getting lock!");
|
|
ctlr->ioctl_lock.lock = TW_CLI_LOCK_HELD;
|
|
ctlr->ioctl_lock.timeout =
|
|
cur_time + (lock_pkt.timeout_msec / 1000);
|
|
lock_pkt.time_remaining_msec = lock_pkt.timeout_msec;
|
|
user_buf->driver_pkt.status = 0;
|
|
} else {
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
|
|
"GET_LOCK: Lock already held!");
|
|
lock_pkt.time_remaining_msec = (TW_UINT32)(
|
|
(ctlr->ioctl_lock.timeout - cur_time) * 1000);
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_IOCTL_LOCK_ALREADY_HELD;
|
|
}
|
|
tw_osl_memcpy(user_buf->data_buf, &lock_pkt,
|
|
sizeof(struct tw_cl_lock_packet));
|
|
break;
|
|
}
|
|
|
|
|
|
case TW_CL_IOCTL_RELEASE_LOCK:
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Release ioctl lock");
|
|
|
|
if (ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) {
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
|
|
"twa_ioctl: RELEASE_LOCK: Lock not held!");
|
|
user_buf->driver_pkt.status =
|
|
TW_CL_ERROR_IOCTL_LOCK_NOT_HELD;
|
|
} else {
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"RELEASE_LOCK: Releasing lock!");
|
|
ctlr->ioctl_lock.lock = TW_CLI_LOCK_FREE;
|
|
user_buf->driver_pkt.status = 0;
|
|
}
|
|
break;
|
|
|
|
|
|
case TW_CL_IOCTL_GET_COMPATIBILITY_INFO:
|
|
{
|
|
struct tw_cl_compatibility_packet comp_pkt;
|
|
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Get compatibility info");
|
|
|
|
tw_osl_memcpy(comp_pkt.driver_version,
|
|
TW_OSL_DRIVER_VERSION_STRING,
|
|
sizeof(TW_OSL_DRIVER_VERSION_STRING));
|
|
comp_pkt.working_srl = ctlr->working_srl;
|
|
comp_pkt.working_branch = ctlr->working_branch;
|
|
comp_pkt.working_build = ctlr->working_build;
|
|
comp_pkt.driver_srl_high = TWA_CURRENT_FW_SRL;
|
|
comp_pkt.driver_branch_high =
|
|
TWA_CURRENT_FW_BRANCH(ctlr->arch_id);
|
|
comp_pkt.driver_build_high =
|
|
TWA_CURRENT_FW_BUILD(ctlr->arch_id);
|
|
comp_pkt.driver_srl_low = TWA_BASE_FW_SRL;
|
|
comp_pkt.driver_branch_low = TWA_BASE_FW_BRANCH;
|
|
comp_pkt.driver_build_low = TWA_BASE_FW_BUILD;
|
|
comp_pkt.fw_on_ctlr_srl = ctlr->fw_on_ctlr_srl;
|
|
comp_pkt.fw_on_ctlr_branch = ctlr->fw_on_ctlr_branch;
|
|
comp_pkt.fw_on_ctlr_build = ctlr->fw_on_ctlr_build;
|
|
user_buf->driver_pkt.status = 0;
|
|
|
|
/* Copy compatibility information to user space. */
|
|
tw_osl_memcpy(user_buf->data_buf, &comp_pkt,
|
|
(sizeof(struct tw_cl_compatibility_packet) <
|
|
user_buf->driver_pkt.buffer_length) ?
|
|
sizeof(struct tw_cl_compatibility_packet) :
|
|
user_buf->driver_pkt.buffer_length);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* Unknown opcode. */
|
|
tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
|
|
"Unknown ioctl cmd 0x%x", cmd);
|
|
error = TW_OSL_ENOTTY;
|
|
}
|
|
|
|
tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_get_param
|
|
* Description: Get a firmware parameter.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* table_id -- parameter table #
|
|
* param_id -- index of the parameter in the table
|
|
* param_size -- size of the parameter in bytes
|
|
* callback -- ptr to function, if any, to be called
|
|
* back on completion; TW_CL_NULL if no callback.
|
|
* Output: param_data -- param value
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_get_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
|
|
TW_INT32 param_id, TW_VOID *param_data, TW_INT32 param_size,
|
|
TW_VOID (* callback)(struct tw_cli_req_context *req))
|
|
{
|
|
struct tw_cli_req_context *req;
|
|
union tw_cl_command_7k *cmd;
|
|
struct tw_cl_param_9k *param = TW_CL_NULL;
|
|
TW_INT32 error = TW_OSL_EBUSY;
|
|
|
|
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Get a request packet. */
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL)
|
|
goto out;
|
|
|
|
/* Make sure this is the only CL internal request at this time. */
|
|
if (ctlr->internal_req_busy) {
|
|
error = TW_OSL_EBUSY;
|
|
goto out;
|
|
}
|
|
ctlr->internal_req_busy = TW_CL_TRUE;
|
|
req->data = ctlr->internal_req_data;
|
|
req->data_phys = ctlr->internal_req_data_phys;
|
|
req->length = TW_CLI_SECTOR_SIZE;
|
|
req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
|
|
|
|
/* Initialize memory to read data into. */
|
|
param = (struct tw_cl_param_9k *)(req->data);
|
|
tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
|
|
|
|
/* Build the cmd pkt. */
|
|
cmd = &(req->cmd_pkt->command.cmd_pkt_7k);
|
|
|
|
req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
cmd->param.sgl_off__opcode =
|
|
BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_GET_PARAM);
|
|
cmd->param.request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id));
|
|
cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
|
|
cmd->param.param_count = TW_CL_SWAP16(1);
|
|
|
|
if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
|
|
((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
|
|
TW_CL_SWAP64(req->data_phys);
|
|
((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
cmd->param.size = 2 + 3;
|
|
} else {
|
|
((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
|
|
TW_CL_SWAP32(req->data_phys);
|
|
((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
cmd->param.size = 2 + 2;
|
|
}
|
|
|
|
/* Specify which parameter we need. */
|
|
param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
|
|
param->parameter_id = (TW_UINT8)(param_id);
|
|
param->parameter_size_bytes = TW_CL_SWAP16(param_size);
|
|
|
|
/* Submit the command. */
|
|
if (callback == TW_CL_NULL) {
|
|
/* There's no call back; wait till the command completes. */
|
|
error = tw_cli_submit_and_poll_request(req,
|
|
TW_CLI_REQUEST_TIMEOUT_PERIOD);
|
|
if (error)
|
|
goto out;
|
|
if ((error = cmd->param.status)) {
|
|
#if 0
|
|
tw_cli_create_ctlr_event(ctlr,
|
|
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
|
|
&(req->cmd_pkt->cmd_hdr));
|
|
#endif // 0
|
|
goto out;
|
|
}
|
|
tw_osl_memcpy(param_data, param->data, param_size);
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
} else {
|
|
/* There's a call back. Simply submit the command. */
|
|
req->tw_cli_callback = callback;
|
|
if ((error = tw_cli_submit_cmd(req)))
|
|
goto out;
|
|
}
|
|
return(0);
|
|
|
|
out:
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1101, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"get_param failed",
|
|
"error = %d", error);
|
|
if (param)
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
if (req)
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
return(1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_set_param
|
|
* Description: Set a firmware parameter.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* table_id -- parameter table #
|
|
* param_id -- index of the parameter in the table
|
|
* param_size -- size of the parameter in bytes
|
|
* callback -- ptr to function, if any, to be called
|
|
* back on completion; TW_CL_NULL if no callback.
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_set_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
|
|
TW_INT32 param_id, TW_INT32 param_size, TW_VOID *data,
|
|
TW_VOID (* callback)(struct tw_cli_req_context *req))
|
|
{
|
|
struct tw_cli_req_context *req;
|
|
union tw_cl_command_7k *cmd;
|
|
struct tw_cl_param_9k *param = TW_CL_NULL;
|
|
TW_INT32 error = TW_OSL_EBUSY;
|
|
|
|
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Get a request packet. */
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL)
|
|
goto out;
|
|
|
|
/* Make sure this is the only CL internal request at this time. */
|
|
if (ctlr->internal_req_busy) {
|
|
error = TW_OSL_EBUSY;
|
|
goto out;
|
|
}
|
|
ctlr->internal_req_busy = TW_CL_TRUE;
|
|
req->data = ctlr->internal_req_data;
|
|
req->data_phys = ctlr->internal_req_data_phys;
|
|
req->length = TW_CLI_SECTOR_SIZE;
|
|
req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
|
|
|
|
/* Initialize memory to send data using. */
|
|
param = (struct tw_cl_param_9k *)(req->data);
|
|
tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
|
|
|
|
/* Build the cmd pkt. */
|
|
cmd = &(req->cmd_pkt->command.cmd_pkt_7k);
|
|
|
|
req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
cmd->param.sgl_off__opcode =
|
|
BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_SET_PARAM);
|
|
cmd->param.request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id));
|
|
cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
|
|
cmd->param.param_count = TW_CL_SWAP16(1);
|
|
|
|
if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
|
|
((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
|
|
TW_CL_SWAP64(req->data_phys);
|
|
((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
cmd->param.size = 2 + 3;
|
|
} else {
|
|
((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
|
|
TW_CL_SWAP32(req->data_phys);
|
|
((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
cmd->param.size = 2 + 2;
|
|
}
|
|
|
|
/* Specify which parameter we want to set. */
|
|
param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
|
|
param->parameter_id = (TW_UINT8)(param_id);
|
|
param->parameter_size_bytes = TW_CL_SWAP16(param_size);
|
|
tw_osl_memcpy(param->data, data, param_size);
|
|
|
|
/* Submit the command. */
|
|
if (callback == TW_CL_NULL) {
|
|
/* There's no call back; wait till the command completes. */
|
|
error = tw_cli_submit_and_poll_request(req,
|
|
TW_CLI_REQUEST_TIMEOUT_PERIOD);
|
|
if (error)
|
|
goto out;
|
|
if ((error = cmd->param.status)) {
|
|
#if 0
|
|
tw_cli_create_ctlr_event(ctlr,
|
|
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
|
|
&(req->cmd_pkt->cmd_hdr));
|
|
#endif // 0
|
|
goto out;
|
|
}
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
} else {
|
|
/* There's a call back. Simply submit the command. */
|
|
req->tw_cli_callback = callback;
|
|
if ((error = tw_cli_submit_cmd(req)))
|
|
goto out;
|
|
}
|
|
return(error);
|
|
|
|
out:
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1102, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"set_param failed",
|
|
"error = %d", error);
|
|
if (param)
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
if (req)
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_submit_and_poll_request
|
|
* Description: Sends down a firmware cmd, and waits for the completion
|
|
* in a tight loop.
|
|
*
|
|
* Input: req -- ptr to request pkt
|
|
* timeout -- max # of seconds to wait before giving up
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_submit_and_poll_request(struct tw_cli_req_context *req,
|
|
TW_UINT32 timeout)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr = req->ctlr;
|
|
TW_TIME end_time;
|
|
TW_INT32 error;
|
|
|
|
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/*
|
|
* If the cmd queue is full, tw_cli_submit_cmd will queue this
|
|
* request in the pending queue, since this is an internal request.
|
|
*/
|
|
if ((error = tw_cli_submit_cmd(req))) {
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1103, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Failed to start internal request",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
|
|
/*
|
|
* Poll for the response until the command gets completed, or there's
|
|
* a timeout.
|
|
*/
|
|
end_time = tw_osl_get_local_time() + timeout;
|
|
do {
|
|
if ((error = req->error_code))
|
|
/*
|
|
* This will take care of completion due to a reset,
|
|
* or a failure in tw_cli_submit_pending_queue.
|
|
* The caller should do the clean-up.
|
|
*/
|
|
return(error);
|
|
|
|
/* See if the command completed. */
|
|
tw_cli_process_resp_intr(ctlr);
|
|
|
|
if ((req->state != TW_CLI_REQ_STATE_BUSY) &&
|
|
(req->state != TW_CLI_REQ_STATE_PENDING))
|
|
return(req->state != TW_CLI_REQ_STATE_COMPLETE);
|
|
} while (tw_osl_get_local_time() <= end_time);
|
|
|
|
/* Time out! */
|
|
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x1104, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Internal request timed out",
|
|
"request = %p", req);
|
|
|
|
/*
|
|
* We will reset the controller only if the request has already been
|
|
* submitted, so as to not lose the request packet. If a busy request
|
|
* timed out, the reset will take care of freeing resources. If a
|
|
* pending request timed out, we will free resources for that request,
|
|
* right here, thereby avoiding a reset. So, the caller is expected
|
|
* to NOT cleanup when TW_OSL_ETIMEDOUT is returned.
|
|
*/
|
|
|
|
/*
|
|
* We have to make sure that this timed out request, if it were in the
|
|
* pending queue, doesn't get submitted while we are here, from
|
|
* tw_cli_submit_pending_queue. There could be a race in that case.
|
|
* Need to revisit.
|
|
*/
|
|
if (req->state == TW_CLI_REQ_STATE_PENDING) {
|
|
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(),
|
|
"Removing request from pending queue");
|
|
/*
|
|
* Request was never submitted. Clean up. Note that we did
|
|
* not do a reset. So, we have to remove the request ourselves
|
|
* from the pending queue (as against tw_cli_drain_pendinq_queue
|
|
* taking care of it).
|
|
*/
|
|
tw_cli_req_q_remove_item(req, TW_CLI_PENDING_Q);
|
|
if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL)
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
|
|
TWA_CONTROL_MASK_COMMAND_INTERRUPT);
|
|
if (req->data)
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
}
|
|
|
|
return(TW_OSL_ETIMEDOUT);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cl_reset_ctlr
|
|
* Description: Soft resets and then initializes the controller;
|
|
* drains any incomplete requests.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* req_handle -- ptr to request handle
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cl_reset_ctlr(struct tw_cl_ctlr_handle *ctlr_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
struct twa_softc *sc = ctlr_handle->osl_ctlr_ctxt;
|
|
struct tw_cli_req_context *req;
|
|
TW_INT32 reset_attempt = 1;
|
|
TW_INT32 error = 0;
|
|
|
|
tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
ctlr->reset_in_progress = TW_CL_TRUE;
|
|
twa_teardown_intr(sc);
|
|
|
|
|
|
/*
|
|
* Error back all requests in the complete, busy, and pending queues.
|
|
* If any request is already on its way to getting submitted, it's in
|
|
* none of these queues and so, will not be completed. That request
|
|
* will continue its course and get submitted to the controller after
|
|
* the reset is done (and io_lock is released).
|
|
*/
|
|
tw_cli_drain_complete_queue(ctlr);
|
|
tw_cli_drain_busy_queue(ctlr);
|
|
tw_cli_drain_pending_queue(ctlr);
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
ctlr->get_more_aens = TW_CL_FALSE;
|
|
|
|
/* Soft reset the controller. */
|
|
while (reset_attempt <= TW_CLI_MAX_RESET_ATTEMPTS) {
|
|
if ((error = tw_cli_soft_reset(ctlr))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x1105, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Controller reset failed",
|
|
"error = %d; attempt %d", error, reset_attempt++);
|
|
reset_attempt++;
|
|
continue;
|
|
}
|
|
|
|
/* Re-establish logical connection with the controller. */
|
|
if ((error = tw_cli_init_connection(ctlr,
|
|
(TW_UINT16)(ctlr->max_simult_reqs),
|
|
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_EVENT,
|
|
0x1106, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't initialize connection after reset",
|
|
"error = %d", error);
|
|
reset_attempt++;
|
|
continue;
|
|
}
|
|
|
|
#ifdef TW_OSL_DEBUG
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x1107, 0x3, TW_CL_SEVERITY_INFO_STRING,
|
|
"Controller reset done!", " ");
|
|
#endif /* TW_OSL_DEBUG */
|
|
break;
|
|
} /* End of while */
|
|
|
|
/* Move commands from the reset queue to the pending queue. */
|
|
while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_RESET_Q)) != TW_CL_NULL) {
|
|
tw_osl_timeout(req->req_handle);
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
|
|
}
|
|
|
|
twa_setup_intr(sc);
|
|
tw_cli_enable_interrupts(ctlr);
|
|
if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL)
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
|
|
TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
|
|
ctlr->reset_in_progress = TW_CL_FALSE;
|
|
ctlr->reset_needed = TW_CL_FALSE;
|
|
|
|
/* Request for a bus re-scan. */
|
|
tw_osl_scan_bus(ctlr_handle);
|
|
|
|
return(error);
|
|
}
|
|
|
|
TW_VOID
|
|
tw_cl_set_reset_needed(struct tw_cl_ctlr_handle *ctlr_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
|
|
ctlr->reset_needed = TW_CL_TRUE;
|
|
}
|
|
|
|
TW_INT32
|
|
tw_cl_is_reset_needed(struct tw_cl_ctlr_handle *ctlr_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
|
|
|
|
return(ctlr->reset_needed);
|
|
}
|
|
|
|
TW_INT32
|
|
tw_cl_is_active(struct tw_cl_ctlr_handle *ctlr_handle)
|
|
{
|
|
struct tw_cli_ctlr_context *ctlr =
|
|
(struct tw_cli_ctlr_context *)
|
|
(ctlr_handle->cl_ctlr_ctxt);
|
|
|
|
return(ctlr->active);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_soft_reset
|
|
* Description: Does the actual soft reset.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_soft_reset(struct tw_cli_ctlr_context *ctlr)
|
|
{
|
|
struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle;
|
|
int found;
|
|
int loop_count;
|
|
TW_UINT32 error;
|
|
|
|
tw_cli_dbg_printf(1, ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x1108, 0x3, TW_CL_SEVERITY_INFO_STRING,
|
|
"Resetting controller...",
|
|
" ");
|
|
|
|
/* Don't let any new commands get submitted to the controller. */
|
|
tw_osl_get_lock(ctlr_handle, ctlr->io_lock);
|
|
|
|
TW_CLI_SOFT_RESET(ctlr_handle);
|
|
|
|
if ((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)) {
|
|
/*
|
|
* There's a hardware bug in the G133 ASIC, which can lead to
|
|
* PCI parity errors and hangs, if the host accesses any
|
|
* registers when the firmware is resetting the hardware, as
|
|
* part of a hard/soft reset. The window of time when the
|
|
* problem can occur is about 10 ms. Here, we will handshake
|
|
* with the firmware to find out when the firmware is pulling
|
|
* down the hardware reset pin, and wait for about 500 ms to
|
|
* make sure we don't access any hardware registers (for
|
|
* polling) during that window.
|
|
*/
|
|
ctlr->reset_phase1_in_progress = TW_CL_TRUE;
|
|
loop_count = 0;
|
|
do {
|
|
found = (tw_cli_find_response(ctlr, TWA_RESET_PHASE1_NOTIFICATION_RESPONSE) == TW_OSL_ESUCCESS);
|
|
tw_osl_delay(10);
|
|
loop_count++;
|
|
error = 0x7888;
|
|
} while (!found && (loop_count < 6000000)); /* Loop for no more than 60 seconds */
|
|
|
|
if (!found) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Missed firmware handshake after soft-reset",
|
|
"error = %d", error);
|
|
tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
|
|
return(error);
|
|
}
|
|
|
|
tw_osl_delay(TWA_RESET_PHASE1_WAIT_TIME_MS * 1000);
|
|
ctlr->reset_phase1_in_progress = TW_CL_FALSE;
|
|
}
|
|
|
|
if ((error = tw_cli_poll_status(ctlr,
|
|
TWA_STATUS_MICROCONTROLLER_READY |
|
|
TWA_STATUS_ATTENTION_INTERRUPT,
|
|
TW_CLI_RESET_TIMEOUT_PERIOD))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Micro-ctlr not ready/No attn intr after reset",
|
|
"error = %d", error);
|
|
tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
|
|
return(error);
|
|
}
|
|
|
|
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
|
|
TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);
|
|
|
|
if ((error = tw_cli_drain_response_queue(ctlr))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x110A, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't drain response queue after reset",
|
|
"error = %d", error);
|
|
tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
|
|
return(error);
|
|
}
|
|
|
|
tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
|
|
|
|
if ((error = tw_cli_drain_aen_queue(ctlr))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
|
|
0x110B, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Can't drain AEN queue after reset",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
|
|
if ((error = tw_cli_find_aen(ctlr, TWA_AEN_SOFT_RESET))) {
|
|
tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
|
|
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
|
|
0x110C, 0x1, TW_CL_SEVERITY_ERROR_STRING,
|
|
"Reset not reported by controller",
|
|
"error = %d", error);
|
|
return(error);
|
|
}
|
|
|
|
return(TW_OSL_ESUCCESS);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_send_scsi_cmd
|
|
* Description: Sends down a scsi cmd to fw.
|
|
*
|
|
* Input: req -- ptr to request pkt
|
|
* cmd -- opcode of scsi cmd to send
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_send_scsi_cmd(struct tw_cli_req_context *req, TW_INT32 cmd)
|
|
{
|
|
struct tw_cl_command_packet *cmdpkt;
|
|
struct tw_cl_command_9k *cmd9k;
|
|
struct tw_cli_ctlr_context *ctlr;
|
|
TW_INT32 error;
|
|
|
|
ctlr = req->ctlr;
|
|
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
/* Make sure this is the only CL internal request at this time. */
|
|
if (ctlr->internal_req_busy)
|
|
return(TW_OSL_EBUSY);
|
|
ctlr->internal_req_busy = TW_CL_TRUE;
|
|
req->data = ctlr->internal_req_data;
|
|
req->data_phys = ctlr->internal_req_data_phys;
|
|
tw_osl_memzero(req->data, TW_CLI_SECTOR_SIZE);
|
|
req->length = TW_CLI_SECTOR_SIZE;
|
|
|
|
/* Build the cmd pkt. */
|
|
cmdpkt = req->cmd_pkt;
|
|
|
|
cmdpkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
cmd9k = &(cmdpkt->command.cmd_pkt_9k);
|
|
|
|
cmd9k->res__opcode =
|
|
BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
|
|
cmd9k->unit = 0;
|
|
cmd9k->lun_l4__req_id = TW_CL_SWAP16(req->request_id);
|
|
cmd9k->status = 0;
|
|
cmd9k->sgl_offset = 16; /* offset from end of hdr = max cdb len */
|
|
cmd9k->lun_h4__sgl_entries = TW_CL_SWAP16(1);
|
|
|
|
if (req->ctlr->flags & TW_CL_64BIT_ADDRESSES) {
|
|
((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].address =
|
|
TW_CL_SWAP64(req->data_phys);
|
|
((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
} else {
|
|
((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].address =
|
|
TW_CL_SWAP32(req->data_phys);
|
|
((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].length =
|
|
TW_CL_SWAP32(req->length);
|
|
}
|
|
|
|
cmd9k->cdb[0] = (TW_UINT8)cmd;
|
|
cmd9k->cdb[4] = 128;
|
|
|
|
if ((error = tw_cli_submit_cmd(req)))
|
|
if (error != TW_OSL_EBUSY) {
|
|
tw_cli_dbg_printf(1, ctlr->ctlr_handle,
|
|
tw_osl_cur_func(),
|
|
"Failed to start SCSI command",
|
|
"request = %p, error = %d", req, error);
|
|
return(TW_OSL_EIO);
|
|
}
|
|
return(TW_OSL_ESUCCESS);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_get_aen
|
|
* Description: Sends down a Request Sense cmd to fw to fetch an AEN.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
TW_INT32
|
|
tw_cli_get_aen(struct tw_cli_ctlr_context *ctlr)
|
|
{
|
|
struct tw_cli_req_context *req;
|
|
TW_INT32 error;
|
|
|
|
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
if ((req = tw_cli_get_request(ctlr
|
|
)) == TW_CL_NULL)
|
|
return(TW_OSL_EBUSY);
|
|
|
|
req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
|
|
req->flags |= TW_CLI_REQ_FLAGS_9K;
|
|
req->tw_cli_callback = tw_cli_aen_callback;
|
|
if ((error = tw_cli_send_scsi_cmd(req, 0x03 /* REQUEST_SENSE */))) {
|
|
tw_cli_dbg_printf(1, ctlr->ctlr_handle, tw_osl_cur_func(),
|
|
"Could not send SCSI command",
|
|
"request = %p, error = %d", req, error);
|
|
if (req->data)
|
|
ctlr->internal_req_busy = TW_CL_FALSE;
|
|
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: tw_cli_fill_sg_list
|
|
* Description: Fills in the scatter/gather list.
|
|
*
|
|
* Input: ctlr -- ptr to per ctlr structure
|
|
* sgl_src -- ptr to fill the sg list from
|
|
* sgl_dest-- ptr to sg list
|
|
* nsegments--# of segments
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
TW_VOID
|
|
tw_cli_fill_sg_list(struct tw_cli_ctlr_context *ctlr, TW_VOID *sgl_src,
|
|
TW_VOID *sgl_dest, TW_INT32 num_sgl_entries)
|
|
{
|
|
TW_INT32 i;
|
|
|
|
tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
|
|
|
|
if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
|
|
struct tw_cl_sg_desc64 *sgl_s =
|
|
(struct tw_cl_sg_desc64 *)sgl_src;
|
|
struct tw_cl_sg_desc64 *sgl_d =
|
|
(struct tw_cl_sg_desc64 *)sgl_dest;
|
|
|
|
tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
|
|
"64 bit addresses");
|
|
for (i = 0; i < num_sgl_entries; i++) {
|
|
sgl_d[i].address = TW_CL_SWAP64(sgl_s->address);
|
|
sgl_d[i].length = TW_CL_SWAP32(sgl_s->length);
|
|
sgl_s++;
|
|
if (ctlr->flags & TW_CL_64BIT_SG_LENGTH)
|
|
sgl_s = (struct tw_cl_sg_desc64 *)
|
|
(((TW_INT8 *)(sgl_s)) + 4);
|
|
}
|
|
} else {
|
|
struct tw_cl_sg_desc32 *sgl_s =
|
|
(struct tw_cl_sg_desc32 *)sgl_src;
|
|
struct tw_cl_sg_desc32 *sgl_d =
|
|
(struct tw_cl_sg_desc32 *)sgl_dest;
|
|
|
|
tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
|
|
"32 bit addresses");
|
|
for (i = 0; i < num_sgl_entries; i++) {
|
|
sgl_d[i].address = TW_CL_SWAP32(sgl_s[i].address);
|
|
sgl_d[i].length = TW_CL_SWAP32(sgl_s[i].length);
|
|
}
|
|
}
|
|
}
|
|
|