summaryrefslogtreecommitdiffstats
path: root/sys/dev/twa/tw_cl_misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/twa/tw_cl_misc.c')
-rw-r--r--sys/dev/twa/tw_cl_misc.c1053
1 files changed, 1053 insertions, 0 deletions
diff --git a/sys/dev/twa/tw_cl_misc.c b/sys/dev/twa/tw_cl_misc.c
new file mode 100644
index 0000000..8b01ff3
--- /dev/null
+++ b/sys/dev/twa/tw_cl_misc.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (c) 2004-05 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
+ */
+
+
+/*
+ * Common Layer miscellaneous 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"
+
+
+
+/* AEN severity table. */
+TW_INT8 *tw_cli_severity_string_table[] = {
+ "None",
+ TW_CL_SEVERITY_ERROR_STRING,
+ TW_CL_SEVERITY_WARNING_STRING,
+ TW_CL_SEVERITY_INFO_STRING,
+ TW_CL_SEVERITY_DEBUG_STRING,
+ ""
+};
+
+
+
+/*
+ * Function name: tw_cli_drain_complete_queue
+ * Description: This function gets called during a controller reset.
+ * It errors back to CAM, all those requests that are
+ * in the complete queue, at the time of the reset. Any
+ * CL internal requests will be simply freed.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_drain_complete_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+ struct tw_cl_req_packet *req_pkt;
+
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Walk the busy queue. */
+ while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q))) {
+ if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+ /*
+ * It's an internal request. Set the appropriate
+ * error and call the CL internal callback if there's
+ * one. If the request originator is polling for
+ * completion, he should be checking req->error to
+ * determine that the request did not go through.
+ * The request originators are responsible for the
+ * clean-up.
+ */
+ req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+ } else {
+ if ((req_pkt = req->orig_req)) {
+ /* It's a SCSI request. Complete it. */
+ tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "Completing complete request %p "
+ "on reset",
+ req);
+ req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+ req_pkt->tw_osl_callback(req->req_handle);
+ }
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ }
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_drain_busy_queue
+ * Description: This function gets called during a controller reset.
+ * It errors back to CAM, all those requests that were
+ * pending with the firmware, at the time of the reset.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_drain_busy_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+ struct tw_cl_req_packet *req_pkt;
+
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Walk the busy queue. */
+ while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_BUSY_Q))) {
+ if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+ /*
+ * It's an internal request. Set the appropriate
+ * error and call the CL internal callback if there's
+ * one. If the request originator is polling for
+ * completion, he should be checking req->error to
+ * determine that the request did not go through.
+ * The request originators are responsible for the
+ * clean-up.
+ */
+ req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+ } else {
+ if ((req_pkt = req->orig_req)) {
+ /* It's a SCSI request. Complete it. */
+ tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "Completing busy request %p on reset",
+ req);
+ req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+ req_pkt->tw_osl_callback(req->req_handle);
+ }
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ }
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_drain_pending_queue
+ * Description: This function gets called during a controller reset.
+ * It errors back to CAM, all those requests that were
+ * in the pending queue, at the time of the reset.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+
+TW_VOID
+tw_cli_drain_pending_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+ struct tw_cl_req_packet *req_pkt;
+
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * Pull requests off the pending queue, and complete them.
+ */
+ while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q))) {
+ if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+ /*
+ * It's an internal request. Set the appropriate
+ * error and call the CL internal callback if there's
+ * one. If the request originator is polling for
+ * completion, he should be checking req->error to
+ * determine that the request did not go through.
+ * The request originators are responsible for the
+ * clean-up.
+ */
+ req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+ } else {
+ if ((req_pkt = req->orig_req)) {
+ /* It's an external request. Complete it. */
+ tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "Completing pending request %p "
+ "on reset", req);
+ req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+ req_pkt->tw_osl_callback(req->req_handle);
+ }
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ }
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_drain_response_queue
+ * Description: Drain the controller response queue.
+ *
+ * Input: ctlr -- ptr to per ctlr structure
+ * Output: None
+ * Return value: 0 -- success
+ * non-zero-- failure
+ */
+TW_INT32
+tw_cli_drain_response_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ TW_UINT32 resp;
+ TW_UINT32 status_reg;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ for (;;) {
+ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+
+ if (tw_cli_check_ctlr_state(ctlr, status_reg))
+ return(TW_OSL_EGENFAILURE);
+
+ if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
+ return(TW_OSL_ESUCCESS); /* no more response queue entries */
+
+ resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_drain_aen_queue
+ * Description: Fetches all un-retrieved AEN's posted by fw.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: 0 -- success
+ * non-zero-- failure
+ */
+TW_INT32
+tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+ struct tw_cl_command_header *cmd_hdr;
+ TW_TIME end_time;
+ TW_UINT16 aen_code;
+ TW_INT32 error;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ for (;;) {
+ if ((req = tw_cli_get_request(ctlr
+#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+ , TW_CL_NULL
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+ )) == TW_CL_NULL) {
+ error = TW_OSL_EBUSY;
+ break;
+ }
+
+#ifdef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST
+
+ req->cmd_pkt = ctlr->cmd_pkt_buf;
+ req->cmd_pkt_phys = ctlr->cmd_pkt_phys;
+ tw_osl_memzero(req->cmd_pkt,
+ sizeof(struct tw_cl_command_header) +
+ 28 /* max bytes before sglist */);
+
+#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */
+
+ req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+ req->tw_cli_callback = TW_CL_NULL;
+ if ((error = tw_cli_send_scsi_cmd(req,
+ 0x03 /* REQUEST_SENSE */))) {
+ tw_cli_dbg_printf(1, ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "Cannot send command to fetch aen");
+ break;
+ }
+
+ end_time = tw_osl_get_local_time() +
+ TW_CLI_REQUEST_TIMEOUT_PERIOD;
+ 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.
+ */
+ goto out;
+
+ tw_cli_process_resp_intr(req->ctlr);
+
+ if ((req->state != TW_CLI_REQ_STATE_BUSY) &&
+ (req->state != TW_CLI_REQ_STATE_PENDING))
+ break;
+ } while (tw_osl_get_local_time() <= end_time);
+
+ if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
+ error = TW_OSL_ETIMEDOUT;
+ break;
+ }
+
+ if ((error = req->cmd_pkt->command.cmd_pkt_9k.status)) {
+ cmd_hdr = &req->cmd_pkt->cmd_hdr;
+ tw_cli_create_ctlr_event(ctlr,
+ TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ cmd_hdr);
+ break;
+ }
+
+ aen_code = tw_cli_manage_aen(ctlr, req);
+ if (aen_code == TWA_AEN_QUEUE_EMPTY)
+ break;
+ if (aen_code == TWA_AEN_SYNC_TIME_WITH_HOST)
+ continue;
+
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ }
+
+out:
+ if (req) {
+ if (req->data)
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ }
+ return(error);
+}
+
+
+
+/*
+ * Function name: tw_cli_find_aen
+ * Description: Reports whether a given AEN ever occurred.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * aen_code-- AEN to look for
+ * Output: None
+ * Return value: 0 -- success
+ * non-zero-- failure
+ */
+TW_INT32
+tw_cli_find_aen(struct tw_cli_ctlr_context *ctlr, TW_UINT16 aen_code)
+{
+ TW_UINT32 last_index;
+ TW_INT32 i;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ if (ctlr->aen_q_wrapped)
+ last_index = ctlr->aen_head;
+ else
+ last_index = 0;
+
+ i = ctlr->aen_head;
+ do {
+ i = (i + ctlr->max_aens_supported - 1) %
+ ctlr->max_aens_supported;
+ if (ctlr->aen_queue[i].aen_code == aen_code)
+ return(TW_OSL_ESUCCESS);
+ } while (i != last_index);
+
+ return(TW_OSL_EGENFAILURE);
+}
+
+
+
+/*
+ * Function name: tw_cli_poll_status
+ * Description: Poll for a given status to show up in the firmware
+ * status register.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * status -- status to look for
+ * timeout -- max # of seconds to wait before giving up
+ * Output: None
+ * Return value: 0 -- success
+ * non-zero-- failure
+ */
+TW_INT32
+tw_cli_poll_status(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status,
+ TW_UINT32 timeout)
+{
+ TW_TIME end_time;
+ TW_UINT32 status_reg;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ end_time = tw_osl_get_local_time() + timeout;
+ do {
+ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+ if ((status_reg & status) == status)
+ /* got the required bit(s) */
+ return(TW_OSL_ESUCCESS);
+
+ /*
+ * The OSL should not define TW_OSL_CAN_SLEEP if it calls
+ * tw_cl_deferred_interrupt from within the ISR and not a
+ * lower interrupt level, since, in that case, we might end
+ * up here, and try to sleep (within an ISR).
+ */
+#ifndef TW_OSL_CAN_SLEEP
+ /* OSL doesn't support sleeping; will spin. */
+ tw_osl_delay(1000);
+#else /* TW_OSL_CAN_SLEEP */
+#if 0
+ /* Will spin if initializing, sleep otherwise. */
+ if (!(ctlr->state & TW_CLI_CTLR_STATE_ACTIVE))
+ tw_osl_delay(1000);
+ else
+ tw_osl_sleep(ctlr->ctlr_handle,
+ &(ctlr->sleep_handle), 1 /* ms */);
+#else /* #if 0 */
+ /*
+ * Will always spin for now (since reset holds a spin lock).
+ * We could free io_lock after the call to TW_CLI_SOFT_RESET,
+ * so we could sleep here. To block new requests (since
+ * the lock will have been released) we could use the
+ * ...RESET_IN_PROGRESS flag. Need to revisit.
+ */
+ tw_osl_delay(1000);
+#endif /* #if 0 */
+#endif /* TW_OSL_CAN_SLEEP */
+ } while (tw_osl_get_local_time() <= end_time);
+
+ return(TW_OSL_ETIMEDOUT);
+}
+
+
+
+/*
+ * Function name: tw_cl_create_event
+ * Description: Creates and queues ctlr/CL/OSL AEN's to be
+ * supplied to user-space tools on request.
+ * Also notifies OS Layer.
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * queue_event-- TW_CL_TRUE --> queue event;
+ * TW_CL_FALSE--> don't queue event
+ * (simply notify OSL)
+ * event_src -- source of event
+ * event_code -- AEN/error code
+ * severity -- severity of event
+ * severity_str--Text description of severity
+ * event_desc -- standard string related to the event/error
+ * event_specific_desc -- format string for additional
+ * info about the event
+ * ... -- additional arguments conforming to the format
+ * specified by event_specific_desc
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cl_create_event(struct tw_cl_ctlr_handle *ctlr_handle,
+ TW_UINT8 queue_event, TW_UINT8 event_src, TW_UINT16 event_code,
+ TW_UINT8 severity, TW_UINT8 *severity_str, TW_UINT8 *event_desc,
+ TW_UINT8 *event_specific_desc, ...)
+{
+ struct tw_cli_ctlr_context *ctlr = ctlr_handle->cl_ctlr_ctxt;
+ struct tw_cl_event_packet event_pkt;
+ struct tw_cl_event_packet *event;
+ va_list ap;
+
+ tw_cli_dbg_printf(8, ctlr_handle, tw_osl_cur_func(), "entered");
+
+ if ((ctlr) && (queue_event)) {
+ /* Protect access to ctlr->aen_head. */
+ tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);
+
+ /* Queue the event. */
+ event = &(ctlr->aen_queue[ctlr->aen_head]);
+ tw_osl_memzero(event->parameter_data,
+ sizeof(event->parameter_data));
+
+ if (event->retrieved == TW_CL_AEN_NOT_RETRIEVED)
+ ctlr->aen_q_overflow = TW_CL_TRUE;
+ event->sequence_id = ++(ctlr->aen_cur_seq_id);
+ if ((ctlr->aen_head + 1) == ctlr->max_aens_supported) {
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle,
+ tw_osl_cur_func(), "AEN queue wrapped");
+ ctlr->aen_q_wrapped = TW_CL_TRUE;
+ }
+ } else {
+ event = &event_pkt;
+ tw_osl_memzero(event, sizeof(struct tw_cl_event_packet));
+ }
+
+ event->event_src = event_src;
+ event->time_stamp_sec = (TW_UINT32)tw_osl_get_local_time();
+ event->aen_code = event_code;
+ event->severity = severity;
+ tw_osl_strcpy(event->severity_str, severity_str);
+ event->retrieved = TW_CL_AEN_NOT_RETRIEVED;
+
+ va_start(ap, event_specific_desc);
+ tw_osl_vsprintf(event->parameter_data, event_specific_desc, ap);
+ va_end(ap);
+
+ event->parameter_len =
+ (TW_UINT8)(tw_osl_strlen(event->parameter_data));
+ tw_osl_strcpy(event->parameter_data + event->parameter_len + 1,
+ event_desc);
+ event->parameter_len += (1 + tw_osl_strlen(event_desc));
+
+ tw_cli_dbg_printf(4, ctlr_handle, tw_osl_cur_func(),
+ "event = %x %x %x %x %x %x %x\n %s",
+ event->sequence_id,
+ event->time_stamp_sec,
+ event->aen_code,
+ event->severity,
+ event->retrieved,
+ event->repeat_count,
+ event->parameter_len,
+ event->parameter_data);
+
+ if ((ctlr) && (queue_event)) {
+ ctlr->aen_head =
+ (ctlr->aen_head + 1) % ctlr->max_aens_supported;
+ /* Free access to ctlr->aen_head. */
+ tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
+ }
+ tw_osl_notify_event(ctlr_handle, event);
+}
+
+
+
+/*
+ * Function name: tw_cli_get_request
+ * Description: Gets a request pkt from the free queue.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * req_pkt -- ptr to OSL built req_pkt, if there's one
+ * Output: None
+ * Return value: ptr to request pkt -- success
+ * TW_CL_NULL -- failure
+ */
+struct tw_cli_req_context *
+tw_cli_get_request(struct tw_cli_ctlr_context *ctlr
+#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+ , struct tw_cl_req_packet *req_pkt
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+ )
+{
+ struct tw_cli_req_context *req;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+
+ if (req_pkt) {
+ if (ctlr->num_free_req_ids == 0) {
+ DbgPrint("Out of req_ids!!\n");
+ return(TW_CL_NULL);
+ }
+ ctlr->num_free_req_ids--;
+ req = (struct tw_cli_req_context *)(req_pkt->non_dma_mem);
+ req->ctlr = ctlr;
+ req->request_id = ctlr->free_req_ids[ctlr->free_req_head];
+ ctlr->busy_reqs[req->request_id] = req;
+ ctlr->free_req_head = (ctlr->free_req_head + 1) %
+ (ctlr->max_simult_reqs - 1);
+ } else
+
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+ {
+ /* Get a free request packet. */
+ req = tw_cli_req_q_remove_head(ctlr, TW_CLI_FREE_Q);
+ }
+
+ /* Initialize some fields to their defaults. */
+ if (req) {
+ req->req_handle = TW_CL_NULL;
+ req->data = TW_CL_NULL;
+ req->length = 0;
+ req->data_phys = 0;
+ req->state = TW_CLI_REQ_STATE_INIT; /* req being initialized */
+ req->flags = 0;
+ req->error_code = 0;
+ req->orig_req = TW_CL_NULL;
+ req->tw_cli_callback = TW_CL_NULL;
+
+#ifndef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST
+
+ /*
+ * Look at the status field in the command packet to see how
+ * it completed the last time it was used, and zero out only
+ * the portions that might have changed. Note that we don't
+ * care to zero out the sglist.
+ */
+ if (req->cmd_pkt->command.cmd_pkt_9k.status)
+ tw_osl_memzero(req->cmd_pkt,
+ sizeof(struct tw_cl_command_header) +
+ 28 /* max bytes before sglist */);
+ else
+ tw_osl_memzero(&(req->cmd_pkt->command),
+ 28 /* max bytes before sglist */);
+
+#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */
+ }
+ return(req);
+}
+
+
+
+/*
+ * Function name: tw_cli_dbg_printf
+ * Description: Calls OSL print function if dbg_level is appropriate
+ *
+ * Input: dbg_level -- Determines whether or not to print
+ * ctlr_handle -- controller handle
+ * cur_func -- text name of calling function
+ * fmt -- format string for the arguments to follow
+ * ... -- variable number of arguments, to be printed
+ * based on the fmt string
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_dbg_printf(TW_UINT8 dbg_level,
+ struct tw_cl_ctlr_handle *ctlr_handle, const TW_INT8 *cur_func,
+ TW_INT8 *fmt, ...)
+{
+#ifdef TW_OSL_DEBUG
+ TW_INT8 print_str[256];
+ va_list ap;
+
+ tw_osl_memzero(print_str, 256);
+ if (dbg_level <= TW_OSL_DEBUG_LEVEL_FOR_CL) {
+ tw_osl_sprintf(print_str, "%s: ", cur_func);
+
+ va_start(ap, fmt);
+ tw_osl_vsprintf(print_str + tw_osl_strlen(print_str), fmt, ap);
+ va_end(ap);
+
+ tw_osl_strcpy(print_str + tw_osl_strlen(print_str), "\n");
+ tw_osl_dbg_printf(ctlr_handle, print_str);
+ }
+#endif /* TW_OSL_DEBUG */
+}
+
+
+
+/*
+ * Function name: tw_cli_notify_ctlr_info
+ * Description: Notify OSL of controller info (fw/BIOS versions, etc.).
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_notify_ctlr_info(struct tw_cli_ctlr_context *ctlr)
+{
+ TW_INT8 fw_ver[16];
+ TW_INT8 bios_ver[16];
+ TW_INT32 error[2];
+ TW_UINT8 num_ports = 0;
+
+ tw_cli_dbg_printf(5, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Get the port count. */
+ error[0] = tw_cli_get_param(ctlr, TWA_PARAM_CONTROLLER_TABLE,
+ TWA_PARAM_CONTROLLER_PORT_COUNT, &num_ports,
+ 1, TW_CL_NULL);
+
+ /* Get the firmware and BIOS versions. */
+ error[0] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
+ TWA_PARAM_VERSION_FW, fw_ver, 16, TW_CL_NULL);
+ error[1] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
+ TWA_PARAM_VERSION_BIOS, bios_ver, 16, TW_CL_NULL);
+
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1300, 0x3, TW_CL_SEVERITY_INFO_STRING,
+ "Controller details:",
+ "%d ports, Firmware %.16s, BIOS %.16s",
+ num_ports,
+ error[0]?(TW_INT8 *)TW_CL_NULL:fw_ver,
+ error[1]?(TW_INT8 *)TW_CL_NULL:bios_ver);
+}
+
+
+
+/*
+ * Function name: tw_cli_check_ctlr_state
+ * Description: Makes sure that the fw status register reports a
+ * proper status.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * status_reg-- value in the status register
+ * Output: None
+ * Return value: 0 -- no errors
+ * non-zero-- errors
+ */
+TW_INT32
+tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status_reg)
+{
+ struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle;
+ TW_INT32 error = TW_OSL_ESUCCESS;
+
+ tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Check if the 'micro-controller ready' bit is not set. */
+ if ((status_reg & TWA_STATUS_EXPECTED_BITS) !=
+ TWA_STATUS_EXPECTED_BITS) {
+ TW_INT8 desc[200];
+
+ tw_osl_memzero(desc, 200);
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1301, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Missing expected status bit(s)",
+ "status reg = 0x%x; Missing bits: %s",
+ status_reg,
+ tw_cli_describe_bits (~status_reg &
+ TWA_STATUS_EXPECTED_BITS, desc));
+ error = TW_OSL_EGENFAILURE;
+ }
+
+ /* Check if any error bits are set. */
+ if ((status_reg & TWA_STATUS_UNEXPECTED_BITS) != 0) {
+ TW_INT8 desc[200];
+
+ tw_osl_memzero(desc, 200);
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1302, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Unexpected status bit(s)",
+ "status reg = 0x%x Unexpected bits: %s",
+ status_reg & TWA_STATUS_UNEXPECTED_BITS,
+ tw_cli_describe_bits(status_reg &
+ TWA_STATUS_UNEXPECTED_BITS, desc));
+
+ if (status_reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) {
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1303, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "PCI parity error: clearing... "
+ "Re-seat/move/replace card",
+ "status reg = 0x%x %s",
+ status_reg,
+ tw_cli_describe_bits(status_reg, desc));
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_CLEAR_PARITY_ERROR);
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+ tw_osl_write_pci_config(ctlr->ctlr_handle,
+ TW_CLI_PCI_CONFIG_STATUS_OFFSET,
+ TWA_PCI_CONFIG_CLEAR_PARITY_ERROR, 2);
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+ }
+
+ if (status_reg & TWA_STATUS_PCI_ABORT_INTERRUPT) {
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1304, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "PCI abort: clearing... ",
+ "status reg = 0x%x %s",
+ status_reg,
+ tw_cli_describe_bits(status_reg, desc));
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_CLEAR_PCI_ABORT);
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+ tw_osl_write_pci_config(ctlr->ctlr_handle,
+ TW_CLI_PCI_CONFIG_STATUS_OFFSET,
+ TWA_PCI_CONFIG_CLEAR_PCI_ABORT, 2);
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+ }
+
+ if (status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) {
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1305, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Controller queue error: clearing... ",
+ "status reg = 0x%x %s",
+ status_reg,
+ tw_cli_describe_bits(status_reg, desc));
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_CLEAR_QUEUE_ERROR);
+ }
+
+ if (status_reg & TWA_STATUS_SBUF_WRITE_ERROR) {
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1306, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "SBUF write error: clearing... ",
+ "status reg = 0x%x %s",
+ status_reg,
+ tw_cli_describe_bits(status_reg, desc));
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_CLEAR_SBUF_WRITE_ERROR);
+ }
+
+ if (status_reg & TWA_STATUS_MICROCONTROLLER_ERROR) {
+ tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+ 0x1307, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Micro-controller error! ",
+ "status reg = 0x%x %s",
+ status_reg,
+ tw_cli_describe_bits(status_reg, desc));
+ error = TW_OSL_EGENFAILURE;
+ }
+ }
+ return(error);
+}
+
+
+
+/*
+ * Function name: tw_cli_describe_bits
+ * Description: Given the value of the status register, returns a
+ * string describing the meaning of each set bit.
+ *
+ * Input: reg -- status register value
+ * Output: Pointer to a string describing each set bit
+ * Return value: Pointer to the string describing each set bit
+ */
+TW_INT8 *
+tw_cli_describe_bits(TW_UINT32 reg, TW_INT8 *str)
+{
+ tw_osl_strcpy(str, "[");
+
+ if (reg & TWA_STATUS_SBUF_WRITE_ERROR)
+ tw_osl_strcpy(str, "SBUF_WR_ERR,");
+ if (reg & TWA_STATUS_COMMAND_QUEUE_EMPTY)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_EMPTY,");
+ if (reg & TWA_STATUS_MICROCONTROLLER_READY)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_RDY,");
+ if (reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_Q_EMPTY,");
+ if (reg & TWA_STATUS_COMMAND_QUEUE_FULL)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_FULL,");
+ if (reg & TWA_STATUS_RESPONSE_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_INTR,");
+ if (reg & TWA_STATUS_COMMAND_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_INTR,");
+ if (reg & TWA_STATUS_ATTENTION_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "ATTN_INTR,");
+ if (reg & TWA_STATUS_HOST_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "HOST_INTR,");
+ if (reg & TWA_STATUS_PCI_ABORT_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_ABRT,");
+ if (reg & TWA_STATUS_MICROCONTROLLER_ERROR)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_ERR,");
+ if (reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "Q_ERR,");
+ if (reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT)
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_PERR");
+
+ tw_osl_strcpy(&str[tw_osl_strlen(str)], "]");
+ return(str);
+}
+
+
+
+#ifdef TW_OSL_DEBUG
+
+/*
+ * Function name: tw_cl_print_ctlr_stats
+ * Description: Prints the current status of the controller.
+ *
+ * Input: ctlr_handle-- controller handle
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cl_print_ctlr_stats(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+ struct tw_cli_ctlr_context *ctlr =
+ (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+ TW_UINT32 status_reg;
+ TW_INT8 desc[200];
+
+ tw_cli_dbg_printf(7, ctlr->ctlr_handle, "", "entered");
+
+ /* Print current controller details. */
+ tw_cli_dbg_printf(0, ctlr_handle, "", "cl_ctlr_ctxt = %p", ctlr);
+
+ tw_osl_memzero(desc, 200);
+ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+ tw_cli_dbg_printf(0, ctlr_handle, "", "status reg = 0x%x %s",
+ status_reg, tw_cli_describe_bits(status_reg, desc));
+
+ tw_cli_dbg_printf(0, ctlr_handle, "", "CLq type current max");
+ tw_cli_dbg_printf(0, ctlr_handle, "", "free %04d %04d",
+ ctlr->q_stats[TW_CLI_FREE_Q].cur_len,
+ ctlr->q_stats[TW_CLI_FREE_Q].max_len);
+ tw_cli_dbg_printf(0, ctlr_handle, "", "busy %04d %04d",
+ ctlr->q_stats[TW_CLI_BUSY_Q].cur_len,
+ ctlr->q_stats[TW_CLI_BUSY_Q].max_len);
+ tw_cli_dbg_printf(0, ctlr_handle, "", "pending %04d %04d",
+ ctlr->q_stats[TW_CLI_PENDING_Q].cur_len,
+ ctlr->q_stats[TW_CLI_PENDING_Q].max_len);
+ tw_cli_dbg_printf(0, ctlr_handle, "", "complete %04d %04d",
+ ctlr->q_stats[TW_CLI_COMPLETE_Q].cur_len,
+ ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len);
+ tw_cli_dbg_printf(0, ctlr_handle, "", "AEN queue head %d tail %d",
+ ctlr->aen_head, ctlr->aen_tail);
+}
+
+
+
+/*
+ * Function name: tw_cl_reset_stats
+ * Description: Resets CL maintained statistics for the controller.
+ *
+ * Input: ctlr_handle-- controller handle
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cl_reset_stats(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+ struct tw_cli_ctlr_context *ctlr =
+ (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+
+ tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(), "entered");
+ ctlr->q_stats[TW_CLI_FREE_Q].max_len = 0;
+ ctlr->q_stats[TW_CLI_BUSY_Q].max_len = 0;
+ ctlr->q_stats[TW_CLI_PENDING_Q].max_len = 0;
+ ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len = 0;
+}
+
+
+
+/*
+ * Function name: tw_cli_print_req_info
+ * Description: Prints CL internal details of a given request.
+ *
+ * Input: req -- ptr to CL internal request context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cl_print_req_info(struct tw_cl_req_handle *req_handle)
+{
+ struct tw_cli_req_context *req = req_handle->cl_req_ctxt;
+ struct tw_cli_ctlr_context *ctlr = req->ctlr;
+ struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle;
+ struct tw_cl_command_packet *cmd_pkt = req->cmd_pkt;
+ struct tw_cl_command_9k *cmd9k;
+ union tw_cl_command_7k *cmd7k;
+ TW_UINT8 *cdb;
+ TW_VOID *sgl;
+ TW_UINT32 sgl_entries;
+ TW_UINT32 i;
+
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "CL details for request:");
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "req_handle = %p, ctlr = %p,\n"
+ "cmd_pkt = %p, cmd_pkt_phys = 0x%llx,\n"
+ "data = %p, length = 0x%x, data_phys = 0x%llx,\n"
+ "state = 0x%x, flags = 0x%x, error = 0x%x,\n"
+ "orig_req = %p, callback = %p, req_id = 0x%x,\n"
+ "next_req = %p, prev_req = %p",
+ req_handle, ctlr,
+ cmd_pkt, req->cmd_pkt_phys,
+ req->data, req->length, req->data_phys,
+ req->state, req->flags, req->error_code,
+ req->orig_req, req->tw_cli_callback, req->request_id,
+ req->link.next, req->link.prev);
+
+ if (req->flags & TW_CLI_REQ_FLAGS_9K) {
+ cmd9k = &(cmd_pkt->command.cmd_pkt_9k);
+ sgl = cmd9k->sg_list;
+ sgl_entries = TW_CL_SWAP16(
+ GET_SGL_ENTRIES(cmd9k->lun_h4__sgl_entries));
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "9K cmd: opcode = 0x%x, unit = 0x%x, req_id = 0x%x,\n"
+ "status = 0x%x, sgl_offset = 0x%x, sgl_entries = 0x%x",
+ GET_OPCODE(cmd9k->res__opcode),
+ cmd9k->unit,
+ TW_CL_SWAP16(GET_REQ_ID(cmd9k->lun_l4__req_id)),
+ cmd9k->status,
+ cmd9k->sgl_offset,
+ sgl_entries);
+
+ cdb = (TW_UINT8 *)(cmd9k->cdb);
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "CDB: %x %x %x %x %x %x %x %x"
+ "%x %x %x %x %x %x %x %x",
+ cdb[0], cdb[1], cdb[2], cdb[3],
+ cdb[4], cdb[5], cdb[6], cdb[7],
+ cdb[8], cdb[9], cdb[10], cdb[11],
+ cdb[12], cdb[13], cdb[14], cdb[15]);
+ } else {
+ cmd7k = &(cmd_pkt->command.cmd_pkt_7k);
+ sgl = cmd7k->param.sgl;
+ sgl_entries = (cmd7k->generic.size -
+ GET_SGL_OFF(cmd7k->generic.sgl_off__opcode)) /
+ ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "7K cmd: opcode = 0x%x, sgl_offset = 0x%x,\n"
+ "size = 0x%x, req_id = 0x%x, unit = 0x%x,\n"
+ "status = 0x%x, flags = 0x%x, count = 0x%x",
+ GET_OPCODE(cmd7k->generic.sgl_off__opcode),
+ GET_SGL_OFF(cmd7k->generic.sgl_off__opcode),
+ cmd7k->generic.size,
+ TW_CL_SWAP16(cmd7k->generic.request_id),
+ GET_UNIT(cmd7k->generic.host_id__unit),
+ cmd7k->generic.status,
+ cmd7k->generic.flags,
+ TW_CL_SWAP16(cmd7k->generic.count));
+ }
+
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), "SG entries:");
+
+ if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+ struct tw_cl_sg_desc64 *sgl64 = (struct tw_cl_sg_desc64 *)sgl;
+
+ for (i = 0; i < sgl_entries; i++) {
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "0x%llx 0x%x",
+ sgl64[i].address, sgl64[i].length);
+ }
+ } else {
+ struct tw_cl_sg_desc32 *sgl32 = (struct tw_cl_sg_desc32 *)sgl;
+
+ for (i = 0; i < sgl_entries; i++) {
+ tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+ "0x%x 0x%x",
+ sgl32[i].address, sgl32[i].length);
+ }
+ }
+}
+
+#endif /* TW_OSL_DEBUG */
+
OpenPOWER on IntegriCloud