diff options
Diffstat (limited to 'sys/dev/twa/tw_cl_init.c')
-rw-r--r-- | sys/dev/twa/tw_cl_init.c | 1104 |
1 files changed, 1104 insertions, 0 deletions
diff --git a/sys/dev/twa/tw_cl_init.c b/sys/dev/twa/tw_cl_init.c new file mode 100644 index 0000000..11d848d --- /dev/null +++ b/sys/dev/twa/tw_cl_init.c @@ -0,0 +1,1104 @@ +/* + * 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 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)) + return(TW_CL_TRUE); + return(TW_CL_FALSE); +} + + + +/* + * 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 + * 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 + * flash_dma_mem_size -- # of bytes of DMA'able memory + * needed for firmware flash, if applicable + * 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 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 +#ifdef TW_OSL_FLASH_FIRMWARE + , TW_UINT32 *flash_dma_mem_size +#endif /* TW_OSL_FLASH_FIRMWARE */ +#ifdef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + , TW_UINT32 *per_req_dma_mem_size +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + , TW_UINT32 *per_req_non_dma_mem_size +#endif /* TW_OSL_N0N_DMA_MEM_ALLOC_PER_REQUEST */ + ) +{ + 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; + *sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR; + + /* + * 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. + */ +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + + *non_dma_mem_size = sizeof(struct tw_cli_ctlr_context) + + (sizeof(struct tw_cli_req_context)) + + (sizeof(struct tw_cl_event_packet) * max_aens); + *per_req_non_dma_mem_size = sizeof(struct tw_cli_req_context); + +#else /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + + *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); + +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + + /* + * 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. + */ +#ifdef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + + *dma_mem_size = sizeof(struct tw_cl_command_packet) + + TW_CLI_SECTOR_SIZE; + *per_req_dma_mem_size = sizeof(struct tw_cl_command_packet); + +#else /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + + *dma_mem_size = (sizeof(struct tw_cl_command_packet) * + (max_simult_reqs + 1)) + (TW_CLI_SECTOR_SIZE); + +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + + +#ifdef TW_OSL_FLASH_FIRMWARE + + /* Memory needed to hold the firmware image while flashing. */ + *flash_dma_mem_size = + ((tw_cli_fw_img_size / TW_CLI_NUM_FW_IMAGE_CHUNKS) + + (TWA_SG_ELEMENT_SIZE_FACTOR - 1)) & + ~(TWA_SG_ELEMENT_SIZE_FACTOR - 1); + +#endif /* TW_OSL_FLASH_FIRMWARE */ + + 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 + * 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 + * flash_dma_mem -- ptr to allocated DMA'able memory + * needed for firmware flash, if applicable + * flash_dma_mem_phys -- physical address of flash_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 max_simult_reqs, TW_INT32 max_aens, TW_VOID *non_dma_mem, + TW_VOID *dma_mem, TW_UINT64 dma_mem_phys +#ifdef TW_OSL_FLASH_FIRMWARE + , TW_VOID *flash_dma_mem, + TW_UINT64 flash_dma_mem_phys +#endif /* TW_OSL_FLASH_FIRMWARE */ + ) +{ + 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) +#ifdef TW_OSL_FLASH_FIRMWARE + || ((flags & TW_CL_FLASH_FIRMWARE) ? + (flash_dma_mem == TW_CL_NULL) : TW_CL_FALSE) +#endif /* TW_OSL_FLASH_FIRMWARE */ + ) { + 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); + } + +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + tw_osl_memzero(non_dma_mem, sizeof(struct tw_cli_ctlr_context) + + sizeof(struct tw_cli_req_context) + + (sizeof(struct tw_cl_event_packet) * max_aens)); +#else /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + 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)); +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + +#ifdef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + tw_osl_memzero(dma_mem, + sizeof(struct tw_cl_command_packet) + + TW_CLI_SECTOR_SIZE); +#else /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + tw_osl_memzero(dma_mem, + (sizeof(struct tw_cl_command_packet) * + (max_simult_reqs + 1)) + + TW_CLI_SECTOR_SIZE); +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + + + 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->max_simult_reqs = max_simult_reqs + 1; + ctlr->max_aens_supported = max_aens; + ctlr->flags = flags; + +#ifdef TW_OSL_FLASH_FIRMWARE + ctlr->flash_dma_mem = flash_dma_mem; + ctlr->flash_dma_mem_phys = flash_dma_mem_phys; +#endif /* TW_OSL_FLASH_FIRMWARE */ + + /* 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 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 + * only use one lock (io_lock) by making io_lock & intr_lock one and + * the same. + */ + if (ctlr->flags & TW_CL_64BIT_ADDRESSES) + 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) * + ( +#ifndef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + max_simult_reqs + +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + 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 + + ( +#ifndef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + max_simult_reqs + +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + 1)); + ctlr->internal_req_data_phys = ctlr->cmd_pkt_phys + + (sizeof(struct tw_cl_command_packet) * + ( +#ifndef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + max_simult_reqs + +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + 1)); + + for (i = 0; + i < ( +#ifndef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + max_simult_reqs + +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + 1); i++) { + req = &(ctlr->req_ctxt_buf[i]); + +#ifndef TW_OSL_DMA_MEM_ALLOC_PER_REQUEST + + req->cmd_pkt = &(ctlr->cmd_pkt_buf[i]); + req->cmd_pkt_phys = ctlr->cmd_pkt_phys + + (i * sizeof(struct tw_cl_command_packet)); + +#endif /* TW_OSL_DMA_MEM_ALLOC_PER_REQUEST */ + + req->request_id = i; + req->ctlr = ctlr; + +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + req->flags |= TW_CLI_REQ_FLAGS_INTERNAL; +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + + /* Insert request into the free queue. */ + tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); + } + + +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + + ctlr->free_req_head = i - 1; + ctlr->free_req_tail = i - 1; + + for (; i < (max_simult_reqs + 1); i++) + ctlr->free_req_ids[i - 1] = i; + + ctlr->num_free_req_ids = max_simult_reqs; + +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + + + /* 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); +} + + + +#ifdef TW_OSL_FLASH_FIRMWARE +/* + * Function name: tw_cli_flash_firmware + * Description: Flashes bundled firmware image onto controller. + * + * Input: ctlr -- ptr to per ctlr structure + * Output: None + * Return value: 0 -- success + * non-zero-- failure + */ +TW_INT32 +tw_cli_flash_firmware(struct tw_cli_ctlr_context *ctlr) +{ + struct tw_cli_req_context *req; + struct tw_cl_command_header *cmd_hdr; + struct tw_cl_command_download_firmware *cmd; + TW_UINT32 fw_img_chunk_size; + TW_UINT32 num_chunks; + TW_UINT32 this_chunk_size = 0; + TW_INT32 remaining_img_size = 0; + TW_INT32 hard_reset_needed = TW_CL_FALSE; + TW_INT32 error = TW_OSL_EGENFAILURE; + TW_UINT32 i; + + tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); + 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) { + /* No free request packets available. Can't proceed. */ + error = TW_OSL_EBUSY; + goto out; + } + +#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; + + /* + * Determine amount of memory needed to hold a chunk of the + * firmware image. + */ + fw_img_chunk_size = ((tw_cli_fw_img_size / TW_CLI_NUM_FW_IMAGE_CHUNKS) + + (TWA_SG_ELEMENT_SIZE_FACTOR - 1)) & + ~(TWA_SG_ELEMENT_SIZE_FACTOR - 1); + + /* Calculate the actual number of chunks needed. */ + num_chunks = (tw_cli_fw_img_size / fw_img_chunk_size) + + ((tw_cli_fw_img_size % fw_img_chunk_size) ? 1 : 0); + + req->data = ctlr->flash_dma_mem; + req->data_phys = ctlr->flash_dma_mem_phys; + + remaining_img_size = tw_cli_fw_img_size; + + cmd_hdr = &(req->cmd_pkt->cmd_hdr); + cmd = &(req->cmd_pkt->command.cmd_pkt_7k.download_fw); + + for (i = 0; i < num_chunks; i++) { + /* Build a cmd pkt for downloading firmware. */ + tw_osl_memzero(req->cmd_pkt, + sizeof(struct tw_cl_command_packet)); + + cmd_hdr->header_desc.size_header = 128; + + /* sgl_offset (offset in dwords, to sg list) is 2. */ + cmd->sgl_off__opcode = + BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_DOWNLOAD_FIRMWARE); + cmd->request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id)); + cmd->unit = 0; + cmd->status = 0; + cmd->flags = 0; + cmd->param = TW_CL_SWAP16(8); /* prom image */ + + if (i != (num_chunks - 1)) + this_chunk_size = fw_img_chunk_size; + else /* last chunk */ + this_chunk_size = remaining_img_size; + + remaining_img_size -= this_chunk_size; + + tw_osl_memcpy(req->data, tw_cli_fw_img + (i * fw_img_chunk_size), + this_chunk_size); + + /* + * The next line will effect only the last chunk. + */ + req->length = (this_chunk_size + + (TWA_SG_ELEMENT_SIZE_FACTOR - 1)) & + ~(TWA_SG_ELEMENT_SIZE_FACTOR - 1); + + if (ctlr->flags & TW_CL_64BIT_ADDRESSES) { + ((struct tw_cl_sg_desc64 *)(cmd->sgl))[0].address = + TW_CL_SWAP64(req->data_phys); + ((struct tw_cl_sg_desc64 *)(cmd->sgl))[0].length = + TW_CL_SWAP32(req->length); + cmd->size = 2 + 3; + } else { + ((struct tw_cl_sg_desc32 *)(cmd->sgl))[0].address = + TW_CL_SWAP32(req->data_phys); + ((struct tw_cl_sg_desc32 *)(cmd->sgl))[0].length = + TW_CL_SWAP32(req->length); + cmd->size = 2 + 2; + } + + error = tw_cli_submit_and_poll_request(req, + TW_CLI_REQUEST_TIMEOUT_PERIOD); + if (error) { + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1005, 0x1, TW_CL_SEVERITY_ERROR_STRING, + "Firmware flash request could not be posted", + "error = %d\n", error); + if (error == TW_OSL_ETIMEDOUT) + /* clean-up done by tw_cli_submit_and_poll_request */ + return(error); + break; + } + error = cmd->status; + + if (((i == (num_chunks - 1)) && (error)) || + ((i != (num_chunks - 1)) && + ((error = cmd_hdr->status_block.error) != + TWA_ERROR_MORE_DATA))) { + /* + * It's either that download of the last chunk + * failed, or the download of one of the earlier + * chunks failed with an error other than + * TWA_ERROR_MORE_DATA. Report the error. + */ + tw_cli_create_ctlr_event(ctlr, + TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, + cmd_hdr); + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1006, 0x1, TW_CL_SEVERITY_ERROR_STRING, + "Firmware flash failed", + "cmd = 0x%x, chunk # %d, cmd status = %d", + GET_OPCODE(cmd->sgl_off__opcode), + i, cmd->status); + /* + * Make a note to hard reset the controller, + * so that it doesn't wait for the remaining + * chunks. Don't call the hard reset function + * right here, since we have committed to having + * only 1 active internal request at a time, and + * this request has not yet been freed. + */ + hard_reset_needed = TW_CL_TRUE; + break; + } + } /* for */ + +out: + if (req) + tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); + + if (hard_reset_needed) + tw_cli_hard_reset(ctlr); + + return(error); +} + + + +/* + * Function name: tw_cli_hard_reset + * Description: Hard resets the controller. + * + * Input: ctlr -- ptr to per ctlr structure + * Output: None + * Return value: 0 -- success + * non-zero-- failure + */ +TW_INT32 +tw_cli_hard_reset(struct tw_cli_ctlr_context *ctlr) +{ + struct tw_cli_req_context *req; + struct tw_cl_command_reset_firmware *cmd; + TW_INT32 error; + + tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); + + 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) + return(TW_OSL_EBUSY); + +#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; + + /* Build a cmd pkt for sending down the hard reset command. */ + req->cmd_pkt->cmd_hdr.header_desc.size_header = 128; + + cmd = &(req->cmd_pkt->command.cmd_pkt_7k.reset_fw); + cmd->res1__opcode = + BUILD_RES__OPCODE(0, TWA_FW_CMD_HARD_RESET_FIRMWARE); + cmd->size = 2; + cmd->request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id)); + cmd->unit = 0; + cmd->status = 0; + cmd->flags = 0; + cmd->param = 0; /* don't reload FPGA logic */ + + req->data = TW_CL_NULL; + req->length = 0; + + error = tw_cli_submit_and_poll_request(req, + TW_CLI_REQUEST_TIMEOUT_PERIOD); + if (error) { + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1007, 0x1, TW_CL_SEVERITY_ERROR_STRING, + "Hard reset request could not be posted", + "error = %d", error); + if (error == TW_OSL_ETIMEDOUT) + /* clean-up done by tw_cli_submit_and_poll_request */ + return(error); + goto out; + } + if ((error = cmd->status)) { + tw_cli_create_ctlr_event(ctlr, + TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, + &(req->cmd_pkt->cmd_hdr)); + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1008, 0x1, TW_CL_SEVERITY_ERROR_STRING, + "Hard reset request failed", + "error = %d", error); + } + +out: + if (req) + tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); + return(error); +} + +#endif /* TW_OSL_FLASH_FIRMWARE */ + + + +/* + * Function name: tw_cli_start_ctlr + * Description: Establishes a logical connection with the controller. + * If bundled with firmware, determines whether or not + * to flash firmware, based on arch_id, fw SRL (Spec. + * Revision Level), branch & build #'s. Also 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; +#ifdef TW_OSL_FLASH_FIRMWARE + TW_INT8 fw_flashed = TW_CL_FALSE; + TW_INT8 fw_flash_failed = TW_CL_FALSE; +#endif /* TW_OSL_FLASH_FIRMWARE */ + + 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, + TWA_9000_ARCH_ID, TWA_CURRENT_FW_BRANCH, + TWA_CURRENT_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, + 0x100B, 0x2, TW_CL_SEVERITY_WARNING_STRING, + "Can't initialize connection in current mode", + "error = %d", error); + return(error); + } + +#ifdef TW_OSL_FLASH_FIRMWARE + + if ((ctlr->flags & TW_CL_FLASH_FIRMWARE) && + (init_connect_result & TWA_BUNDLED_FW_SAFE_TO_FLASH) && + (init_connect_result & TWA_CTLR_FW_RECOMMENDS_FLASH)) { + /* + * The bundled firmware is safe to flash, and the firmware + * on the controller recommends a flash. So, flash! + */ + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x100C, 0x3, TW_CL_SEVERITY_INFO_STRING, + "Flashing bundled firmware...", + " "); + if ((error = tw_cli_flash_firmware(ctlr))) { + fw_flash_failed = TW_CL_TRUE; + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x100D, 0x2, TW_CL_SEVERITY_WARNING_STRING, + "Unable to flash bundled firmware. " + "Attempting to work with fw on ctlr...", + " "); + } else { + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x100E, 0x3, TW_CL_SEVERITY_INFO_STRING, + "Successfully flashed bundled firmware", + " "); + fw_flashed = TW_CL_TRUE; + } + } + + if (fw_flashed) { + /* The firmware was flashed. Have the new image loaded */ + error = tw_cli_hard_reset(ctlr); + if (error) + tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x100F, 0x1, TW_CL_SEVERITY_ERROR_STRING, + "Could not reset controller after flash!", + " "); + else /* Go through initialization again. */ + error = tw_cli_start_ctlr(ctlr); + /* + * If hard reset of controller failed, we need to return. + * Otherwise, the above recursive call to tw_cli_start_ctlr + * will have completed the rest of the initialization (starting + * from tw_cli_drain_aen_queue below). Don't do it again. + * Just return. + */ + return(error); + } else +#endif /* TW_OSL_FLASH_FIRMWARE */ + { + /* + * Either we are not bundled with a firmware image, or + * the bundled firmware is not safe to flash, + * or flash failed for some reason. 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->working_build = TWA_CURRENT_FW_BUILD; + } 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. We should never come here + * if firmware has just been flashed. + */ + 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, TWA_9000_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... + */ +#ifdef TW_OSL_FLASH_FIRMWARE + if (fw_flash_failed) + tw_cl_create_event(ctlr->ctlr_handle, + TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1012, 0x1, + TW_CL_SEVERITY_ERROR_STRING, + "Incompatible firmware on controller" + "...and could not flash bundled " + "firmware", + " "); + else + tw_cl_create_event(ctlr->ctlr_handle, + TW_CL_FALSE, + TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, + 0x1013, 0x1, + TW_CL_SEVERITY_ERROR_STRING, + "Incompatible firmware on controller" + "...and bundled firmware not safe to " + "flash", + " "); +#endif /* TW_OSL_FLASH_FIRMWARE */ + 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; + } + } + + /* 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 +#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST + , TW_CL_NULL +#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */ + )) == TW_CL_NULL) + goto out; + +#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; + + /* 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 |= 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); +} + + |