Index: head/sys/dev/twa/tw_cl_init.c =================================================================== --- head/sys/dev/twa/tw_cl_init.c (revision 190328) +++ head/sys/dev/twa/tw_cl_init.c (revision 190329) @@ -1,744 +1,744 @@ /* * Copyright (c) 2004-07 Applied Micro Circuits Corporation. * Copyright (c) 2004-05 Vinod Kashyap * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * AMCC'S 3ware driver for 9000 series storage controllers. * * Author: Vinod Kashyap * Modifications by: Adam Radford * Modifications by: Manjunath Ranganathaiah */ /* * Common Layer initialization functions. */ #include "tw_osl_share.h" #include "tw_cl_share.h" #include "tw_cl_fwif.h" #include "tw_cl_ioctl.h" #include "tw_cl.h" #include "tw_cl_externs.h" #include "tw_osl_ioctl.h" /* * Function name: tw_cl_ctlr_supported * Description: Determines if a controller is supported. * * Input: vendor_id -- vendor id of the controller * device_id -- device id of the controller * Output: None * Return value: TW_CL_TRUE-- controller supported * TW_CL_FALSE-- controller not supported */ TW_INT32 tw_cl_ctlr_supported(TW_INT32 vendor_id, TW_INT32 device_id) { if ((vendor_id == TW_CL_VENDOR_ID) && ((device_id == TW_CL_DEVICE_ID_9K) || (device_id == TW_CL_DEVICE_ID_9K_X) || (device_id == TW_CL_DEVICE_ID_9K_E) || (device_id == TW_CL_DEVICE_ID_9K_SA))) return(TW_CL_TRUE); return(TW_CL_FALSE); } /* * Function name: tw_cl_get_pci_bar_info * Description: Returns PCI BAR info. * * Input: device_id -- device id of the controller * bar_type -- type of PCI BAR in question * Output: bar_num -- PCI BAR number corresponding to bar_type * bar0_offset -- byte offset from BAR 0 (0x10 in * PCI config space) * bar_size -- size, in bytes, of the BAR in question * Return value: 0 -- success * non-zero -- failure */ TW_INT32 tw_cl_get_pci_bar_info(TW_INT32 device_id, TW_INT32 bar_type, TW_INT32 *bar_num, TW_INT32 *bar0_offset, TW_INT32 *bar_size) { TW_INT32 error = TW_OSL_ESUCCESS; switch(device_id) { case TW_CL_DEVICE_ID_9K: switch(bar_type) { case TW_CL_BAR_TYPE_IO: *bar_num = 0; *bar0_offset = 0; *bar_size = 4; break; case TW_CL_BAR_TYPE_MEM: *bar_num = 1; *bar0_offset = 0x4; *bar_size = 8; break; case TW_CL_BAR_TYPE_SBUF: *bar_num = 2; *bar0_offset = 0xC; *bar_size = 8; break; } break; case TW_CL_DEVICE_ID_9K_X: case TW_CL_DEVICE_ID_9K_E: case TW_CL_DEVICE_ID_9K_SA: switch(bar_type) { case TW_CL_BAR_TYPE_IO: *bar_num = 2; *bar0_offset = 0x10; *bar_size = 4; break; case TW_CL_BAR_TYPE_MEM: *bar_num = 1; *bar0_offset = 0x8; *bar_size = 8; break; case TW_CL_BAR_TYPE_SBUF: *bar_num = 0; *bar0_offset = 0; *bar_size = 8; break; } break; default: error = TW_OSL_ENOTTY; break; } return(error); } /* * Function name: tw_cl_get_mem_requirements * Description: Provides info about Common Layer requirements for a * controller, given the controller type (in 'flags'). * Input: ctlr_handle -- controller handle * flags -- more info passed by the OS Layer * device_id -- device id of the controller * max_simult_reqs -- maximum # of simultaneous * requests that the OS Layer expects * the Common Layer to support * max_aens -- maximun # of AEN's needed to be supported * Output: alignment -- alignment needed for all DMA'able * buffers * sg_size_factor -- every SG element should have a size * that's a multiple of this number * non_dma_mem_size -- # of bytes of memory needed for * non-DMA purposes * dma_mem_size -- # of bytes of DMA'able memory needed * per_req_dma_mem_size -- # of bytes of DMA'able memory * needed per request, if applicable * per_req_non_dma_mem_size -- # of bytes of memory needed * per request for non-DMA purposes, * if applicable * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cl_get_mem_requirements(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags, TW_INT32 device_id, TW_INT32 max_simult_reqs, TW_INT32 max_aens, TW_UINT32 *alignment, TW_UINT32 *sg_size_factor, TW_UINT32 *non_dma_mem_size, TW_UINT32 *dma_mem_size ) { if (device_id == 0) device_id = TW_CL_DEVICE_ID_9K; if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Too many simultaneous requests to support!", "requested = %d, supported = %d, error = %d\n", max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS, TW_OSL_EBIG); return(TW_OSL_EBIG); } *alignment = TWA_ALIGNMENT(device_id); *sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id); /* * Total non-DMA memory needed is the sum total of memory needed for * the controller context, request packets (including the 1 needed for * CL internal requests), and event packets. */ *non_dma_mem_size = sizeof(struct tw_cli_ctlr_context) + (sizeof(struct tw_cli_req_context) * (max_simult_reqs + 1)) + (sizeof(struct tw_cl_event_packet) * max_aens); /* * Total DMA'able memory needed is the sum total of memory needed for * all command packets (including the 1 needed for CL internal * requests), and memory needed to hold the payload for internal * requests. */ *dma_mem_size = (sizeof(struct tw_cl_command_packet) * (max_simult_reqs + 1)) + (TW_CLI_SECTOR_SIZE); return(0); } /* * Function name: tw_cl_init_ctlr * Description: Initializes driver data structures for the controller. * * Input: ctlr_handle -- controller handle * flags -- more info passed by the OS Layer * device_id -- device id of the controller * max_simult_reqs -- maximum # of simultaneous requests * that the OS Layer expects the Common * Layer to support * max_aens -- maximun # of AEN's needed to be supported * non_dma_mem -- ptr to allocated non-DMA memory * dma_mem -- ptr to allocated DMA'able memory * dma_mem_phys -- physical address of dma_mem * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cl_init_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags, TW_INT32 device_id, TW_INT32 max_simult_reqs, TW_INT32 max_aens, TW_VOID *non_dma_mem, TW_VOID *dma_mem, TW_UINT64 dma_mem_phys ) { struct tw_cli_ctlr_context *ctlr; struct tw_cli_req_context *req; TW_UINT8 *free_non_dma_mem; TW_INT32 error = TW_OSL_ESUCCESS; TW_INT32 i; tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered"); if (flags & TW_CL_START_CTLR_ONLY) { ctlr = (struct tw_cli_ctlr_context *) (ctlr_handle->cl_ctlr_ctxt); goto start_ctlr; } if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Too many simultaneous requests to support!", "requested = %d, supported = %d, error = %d\n", max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS, TW_OSL_EBIG); return(TW_OSL_EBIG); } if ((non_dma_mem == TW_CL_NULL) || (dma_mem == TW_CL_NULL) ) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1001, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Insufficient memory for Common Layer's internal usage", "error = %d\n", TW_OSL_ENOMEM); return(TW_OSL_ENOMEM); } tw_osl_memzero(non_dma_mem, sizeof(struct tw_cli_ctlr_context) + (sizeof(struct tw_cli_req_context) * (max_simult_reqs + 1)) + (sizeof(struct tw_cl_event_packet) * max_aens)); tw_osl_memzero(dma_mem, (sizeof(struct tw_cl_command_packet) * (max_simult_reqs + 1)) + TW_CLI_SECTOR_SIZE); free_non_dma_mem = (TW_UINT8 *)non_dma_mem; ctlr = (struct tw_cli_ctlr_context *)free_non_dma_mem; free_non_dma_mem += sizeof(struct tw_cli_ctlr_context); ctlr_handle->cl_ctlr_ctxt = ctlr; ctlr->ctlr_handle = ctlr_handle; ctlr->device_id = (TW_UINT32)device_id; ctlr->arch_id = TWA_ARCH_ID(device_id); ctlr->flags = flags; ctlr->sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id); ctlr->max_simult_reqs = max_simult_reqs + 1; ctlr->max_aens_supported = max_aens; /* Initialize queues of CL internal request context packets. */ tw_cli_req_q_init(ctlr, TW_CLI_FREE_Q); tw_cli_req_q_init(ctlr, TW_CLI_BUSY_Q); tw_cli_req_q_init(ctlr, TW_CLI_PENDING_Q); tw_cli_req_q_init(ctlr, TW_CLI_COMPLETE_Q); /* Initialize all locks used by CL. */ ctlr->gen_lock = &(ctlr->gen_lock_handle); tw_osl_init_lock(ctlr_handle, "tw_cl_gen_lock", ctlr->gen_lock); ctlr->io_lock = &(ctlr->io_lock_handle); tw_osl_init_lock(ctlr_handle, "tw_cl_io_lock", ctlr->io_lock); /* * If 64 bit cmd pkt addresses are used, we will need to serialize * writes to the hardware (across registers), since existing (G66) * hardware will get confused if, for example, we wrote the low 32 bits * of the cmd pkt address, followed by a response interrupt mask to the * control register, followed by the high 32 bits of the cmd pkt * address. It will then interpret the value written to the control * register as the low cmd pkt address. So, for this case, we will * make a note that we will need to synchronize control register writes * with command register writes. */ if ((ctlr->flags & TW_CL_64BIT_ADDRESSES) && ((ctlr->device_id == TW_CL_DEVICE_ID_9K) || (ctlr->device_id == TW_CL_DEVICE_ID_9K_X) || (ctlr->device_id == TW_CL_DEVICE_ID_9K_E) || (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA))) { ctlr->state |= TW_CLI_CTLR_STATE_G66_WORKAROUND_NEEDED; ctlr->intr_lock = ctlr->io_lock; } else { ctlr->intr_lock = &(ctlr->intr_lock_handle); tw_osl_init_lock(ctlr_handle, "tw_cl_intr_lock", ctlr->intr_lock); } /* Initialize CL internal request context packets. */ ctlr->req_ctxt_buf = (struct tw_cli_req_context *)free_non_dma_mem; free_non_dma_mem += (sizeof(struct tw_cli_req_context) * ( max_simult_reqs + 1)); ctlr->cmd_pkt_buf = (struct tw_cl_command_packet *)dma_mem; ctlr->cmd_pkt_phys = dma_mem_phys; ctlr->internal_req_data = (TW_UINT8 *) (ctlr->cmd_pkt_buf + ( max_simult_reqs + 1)); ctlr->internal_req_data_phys = ctlr->cmd_pkt_phys + (sizeof(struct tw_cl_command_packet) * ( max_simult_reqs + 1)); for (i = 0; i < ( max_simult_reqs + 1); i++) { req = &(ctlr->req_ctxt_buf[i]); req->cmd_pkt = &(ctlr->cmd_pkt_buf[i]); req->cmd_pkt_phys = ctlr->cmd_pkt_phys + (i * sizeof(struct tw_cl_command_packet)); req->request_id = i; req->ctlr = ctlr; /* Insert request into the free queue. */ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); } /* Initialize the AEN queue. */ ctlr->aen_queue = (struct tw_cl_event_packet *)free_non_dma_mem; start_ctlr: /* * Disable interrupts. Interrupts will be enabled in tw_cli_start_ctlr * (only) if initialization succeeded. */ tw_cli_disable_interrupts(ctlr); /* Initialize the controller. */ if ((error = tw_cli_start_ctlr(ctlr))) { /* Soft reset the controller, and try one more time. */ tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1002, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Controller initialization failed. Retrying...", "error = %d\n", error); if ((error = tw_cli_soft_reset(ctlr))) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1003, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Controller soft reset failed", "error = %d\n", error); return(error); } else if ((error = tw_cli_start_ctlr(ctlr))) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1004, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Controller initialization retry failed", "error = %d\n", error); return(error); } } /* Notify some info about the controller to the OSL. */ tw_cli_notify_ctlr_info(ctlr); /* Mark the controller as active. */ ctlr->state |= TW_CLI_CTLR_STATE_ACTIVE; return(error); } /* * Function name: tw_cli_start_ctlr * Description: Establishes a logical connection with the controller. * Determines whether or not the driver is compatible * with the firmware on the controller, before proceeding * to work with it. * * Input: ctlr -- ptr to per ctlr structure * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cli_start_ctlr(struct tw_cli_ctlr_context *ctlr) { TW_UINT16 fw_on_ctlr_srl = 0; TW_UINT16 fw_on_ctlr_arch_id = 0; TW_UINT16 fw_on_ctlr_branch = 0; TW_UINT16 fw_on_ctlr_build = 0; TW_UINT32 init_connect_result = 0; TW_INT32 error = TW_OSL_ESUCCESS; tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); /* Wait for the controller to become ready. */ if ((error = tw_cli_poll_status(ctlr, TWA_STATUS_MICROCONTROLLER_READY, TW_CLI_REQUEST_TIMEOUT_PERIOD))) { tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1009, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Microcontroller not ready", "error = %d", error); return(error); } /* Drain the response queue. */ if ((error = tw_cli_drain_response_queue(ctlr))) { tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x100A, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Can't drain response queue", "error = %d", error); return(error); } /* Establish a logical connection with the controller. */ if ((error = tw_cli_init_connection(ctlr, (TW_UINT16)(ctlr->max_simult_reqs), TWA_EXTENDED_INIT_CONNECT, TWA_CURRENT_FW_SRL, (TW_UINT16)(ctlr->arch_id), TWA_CURRENT_FW_BRANCH(ctlr->arch_id), TWA_CURRENT_FW_BUILD(ctlr->arch_id), &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result))) { tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x100B, 0x2, TW_CL_SEVERITY_WARNING_STRING, "Can't initialize connection in current mode", "error = %d", error); return(error); } { /* See if we can at least work with the firmware on the * controller in the current mode. */ if (init_connect_result & TWA_CTLR_FW_COMPATIBLE) { /* Yes, we can. Make note of the operating mode. */ if (init_connect_result & TWA_CTLR_FW_SAME_OR_NEWER) { ctlr->working_srl = TWA_CURRENT_FW_SRL; ctlr->working_branch = TWA_CURRENT_FW_BRANCH(ctlr->arch_id); ctlr->working_build = TWA_CURRENT_FW_BUILD(ctlr->arch_id); } else { ctlr->working_srl = fw_on_ctlr_srl; ctlr->working_branch = fw_on_ctlr_branch; ctlr->working_build = fw_on_ctlr_build; } } else { /* * No, we can't. See if we can at least work with * it in the base mode. */ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1010, 0x2, TW_CL_SEVERITY_WARNING_STRING, "Driver/Firmware mismatch. " "Negotiating for base level...", " "); if ((error = tw_cli_init_connection(ctlr, (TW_UINT16)(ctlr->max_simult_reqs), TWA_EXTENDED_INIT_CONNECT, TWA_BASE_FW_SRL, (TW_UINT16)(ctlr->arch_id), TWA_BASE_FW_BRANCH, TWA_BASE_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result))) { tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1011, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Can't initialize connection in " "base mode", " "); return(error); } if (!(init_connect_result & TWA_CTLR_FW_COMPATIBLE)) { /* * The firmware on the controller is not even * compatible with our base mode. We cannot * work with it. Bail... */ return(1); } /* * We can work with this firmware, but only in * base mode. */ ctlr->working_srl = TWA_BASE_FW_SRL; ctlr->working_branch = TWA_BASE_FW_BRANCH; ctlr->working_build = TWA_BASE_FW_BUILD; ctlr->operating_mode = TWA_BASE_MODE; } ctlr->fw_on_ctlr_srl = fw_on_ctlr_srl; ctlr->fw_on_ctlr_branch = fw_on_ctlr_branch; ctlr->fw_on_ctlr_build = fw_on_ctlr_build; } /* Drain the AEN queue */ if ((error = tw_cli_drain_aen_queue(ctlr))) /* * We will just print that we couldn't drain the AEN queue. * There's no need to bail out. */ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1014, 0x2, TW_CL_SEVERITY_WARNING_STRING, "Can't drain AEN queue", "error = %d", error); /* Enable interrupts. */ tw_cli_enable_interrupts(ctlr); return(TW_OSL_ESUCCESS); } /* * Function name: tw_cl_shutdown_ctlr * Description: Closes logical connection with the controller. * * Input: ctlr -- ptr to per ctlr structure * flags -- more info passed by the OS Layer * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cl_shutdown_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags) { struct tw_cli_ctlr_context *ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); TW_INT32 error; tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered"); /* * Mark the controller as inactive, disable any further interrupts, * and notify the controller that we are going down. */ ctlr->state &= ~TW_CLI_CTLR_STATE_ACTIVE; tw_cli_disable_interrupts(ctlr); /* Let the controller know that we are going down. */ if ((error = tw_cli_init_connection(ctlr, TWA_SHUTDOWN_MESSAGE_CREDITS, 0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL))) tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1015, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Can't close connection with controller", "error = %d", error); if (flags & TW_CL_STOP_CTLR_ONLY) goto ret; /* Destroy all locks used by CL. */ tw_osl_destroy_lock(ctlr_handle, ctlr->gen_lock); tw_osl_destroy_lock(ctlr_handle, ctlr->io_lock); if (!(ctlr->flags & TW_CL_64BIT_ADDRESSES)) tw_osl_destroy_lock(ctlr_handle, ctlr->intr_lock); ret: return(error); } /* * Function name: tw_cli_init_connection * Description: Sends init_connection cmd to firmware * * Input: ctlr -- ptr to per ctlr structure * message_credits -- max # of requests that we might send * down simultaneously. This will be * typically set to 256 at init-time or * after a reset, and to 1 at shutdown-time * set_features -- indicates if we intend to use 64-bit * sg, also indicates if we want to do a * basic or an extended init_connection; * * Note: The following input/output parameters are valid, only in case of an * extended init_connection: * * current_fw_srl -- srl of fw we are bundled * with, if any; 0 otherwise * current_fw_arch_id -- arch_id of fw we are bundled * with, if any; 0 otherwise * current_fw_branch -- branch # of fw we are bundled * with, if any; 0 otherwise * current_fw_build -- build # of fw we are bundled * with, if any; 0 otherwise * Output: fw_on_ctlr_srl -- srl of fw on ctlr * fw_on_ctlr_arch_id -- arch_id of fw on ctlr * fw_on_ctlr_branch -- branch # of fw on ctlr * fw_on_ctlr_build -- build # of fw on ctlr * init_connect_result -- result bitmap of fw response * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cli_init_connection(struct tw_cli_ctlr_context *ctlr, TW_UINT16 message_credits, TW_UINT32 set_features, TW_UINT16 current_fw_srl, TW_UINT16 current_fw_arch_id, TW_UINT16 current_fw_branch, TW_UINT16 current_fw_build, TW_UINT16 *fw_on_ctlr_srl, TW_UINT16 *fw_on_ctlr_arch_id, TW_UINT16 *fw_on_ctlr_branch, TW_UINT16 *fw_on_ctlr_build, TW_UINT32 *init_connect_result) { struct tw_cli_req_context *req; struct tw_cl_command_init_connect *init_connect; TW_INT32 error = TW_OSL_EBUSY; tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); /* Get a request packet. */ if ((req = tw_cli_get_request(ctlr )) == TW_CL_NULL) goto out; req->flags |= TW_CLI_REQ_FLAGS_INTERNAL; /* Build the cmd pkt. */ init_connect = &(req->cmd_pkt->command.cmd_pkt_7k.init_connect); req->cmd_pkt->cmd_hdr.header_desc.size_header = 128; init_connect->res1__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_INIT_CONNECTION); init_connect->request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id)); init_connect->message_credits = TW_CL_SWAP16(message_credits); init_connect->features = TW_CL_SWAP32(set_features); if (ctlr->flags & TW_CL_64BIT_ADDRESSES) - init_connect->features |= TWA_64BIT_SG_ADDRESSES; + init_connect->features |= TW_CL_SWAP32(TWA_64BIT_SG_ADDRESSES); if (set_features & TWA_EXTENDED_INIT_CONNECT) { /* * Fill in the extra fields needed for an extended * init_connect. */ init_connect->size = 6; init_connect->fw_srl = TW_CL_SWAP16(current_fw_srl); init_connect->fw_arch_id = TW_CL_SWAP16(current_fw_arch_id); init_connect->fw_branch = TW_CL_SWAP16(current_fw_branch); init_connect->fw_build = TW_CL_SWAP16(current_fw_build); } else init_connect->size = 3; /* Submit the command, and wait for it to complete. */ error = tw_cli_submit_and_poll_request(req, TW_CLI_REQUEST_TIMEOUT_PERIOD); if (error == TW_OSL_ETIMEDOUT) /* Clean-up done by tw_cli_submit_and_poll_request. */ return(error); if (error) goto out; if ((error = init_connect->status)) { tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, &(req->cmd_pkt->cmd_hdr)); goto out; } if (set_features & TWA_EXTENDED_INIT_CONNECT) { *fw_on_ctlr_srl = TW_CL_SWAP16(init_connect->fw_srl); *fw_on_ctlr_arch_id = TW_CL_SWAP16(init_connect->fw_arch_id); *fw_on_ctlr_branch = TW_CL_SWAP16(init_connect->fw_branch); *fw_on_ctlr_build = TW_CL_SWAP16(init_connect->fw_build); *init_connect_result = TW_CL_SWAP32(init_connect->result); } tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); return(error); out: tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1016, 0x1, TW_CL_SEVERITY_ERROR_STRING, "init_connection failed", "error = %d", error); if (req) tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); return(error); } Index: head/sys/dev/twa/tw_osl.h =================================================================== --- head/sys/dev/twa/tw_osl.h (revision 190328) +++ head/sys/dev/twa/tw_osl.h (revision 190329) @@ -1,316 +1,322 @@ /* * 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 */ #ifndef TW_OSL_H #define TW_OSL_H /* * OS Layer internal macros, structures and functions. */ #define TW_OSLI_DEVICE_NAME "3ware 9000 series Storage Controller" #define TW_OSLI_MALLOC_CLASS M_TWA #define TW_OSLI_MAX_NUM_IOS TW_CL_MAX_SIMULTANEOUS_REQUESTS #define TW_OSLI_MAX_NUM_AENS 0x100 /* Disabled, doesn't work yet. #define TW_OSLI_DEFERRED_INTR_USED */ +#ifdef PAE +#define TW_OSLI_DMA_BOUNDARY (1u << 31) +#else +#define TW_OSLI_DMA_BOUNDARY ((bus_size_t)((uint64_t)1 << 32)) +#endif + /* Possible values of req->state. */ #define TW_OSLI_REQ_STATE_INIT 0x0 /* being initialized */ #define TW_OSLI_REQ_STATE_BUSY 0x1 /* submitted to CL */ #define TW_OSLI_REQ_STATE_PENDING 0x2 /* in pending queue */ #define TW_OSLI_REQ_STATE_COMPLETE 0x3 /* completed by CL */ /* Possible values of req->flags. */ #define TW_OSLI_REQ_FLAGS_DATA_IN (1<<0) /* read request */ #define TW_OSLI_REQ_FLAGS_DATA_OUT (1<<1) /* write request */ #define TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED (1<<2)/* data in ccb is misaligned, have to copy to/from private buffer */ #define TW_OSLI_REQ_FLAGS_MAPPED (1<<3) /* request has been mapped */ #define TW_OSLI_REQ_FLAGS_IN_PROGRESS (1<<4) /* bus_dmamap_load returned EINPROGRESS */ #define TW_OSLI_REQ_FLAGS_PASSTHRU (1<<5) /* pass through request */ #define TW_OSLI_REQ_FLAGS_SLEEPING (1<<6) /* owner sleeping on this cmd */ /* Possible values of sc->state. */ #define TW_OSLI_CTLR_STATE_OPEN (1<<0) /* control device is open */ #define TW_OSLI_CTLR_STATE_SIMQ_FROZEN (1<<1) /* simq frozen */ #ifdef TW_OSL_DEBUG struct tw_osli_q_stats { TW_UINT32 cur_len; /* current # of items in q */ TW_UINT32 max_len; /* max value reached by q_length */ }; #endif /* TW_OSL_DEBUG */ /* Queues of OSL internal request context packets. */ #define TW_OSLI_FREE_Q 0 /* free q */ #define TW_OSLI_BUSY_Q 1 /* q of reqs submitted to CL */ #define TW_OSLI_Q_COUNT 2 /* total number of queues */ /* Driver's request packet. */ struct tw_osli_req_context { struct tw_cl_req_handle req_handle;/* tag to track req b/w OSL & CL */ struct twa_softc *ctlr; /* ptr to OSL's controller context */ TW_VOID *data; /* ptr to data being passed to CL */ TW_UINT32 length; /* length of buf being passed to CL */ /* * ptr to, and length of data passed to us from above, in case a buffer * copy was done due to non-compliance to alignment requirements */ TW_VOID *real_data; TW_UINT32 real_length; TW_UINT32 state; /* request state */ TW_UINT32 flags; /* request flags */ /* error encountered before request submission to CL */ TW_UINT32 error_code; /* ptr to orig req for use during callback */ TW_VOID *orig_req; struct tw_cl_link link; /* to link this request in a list */ bus_dmamap_t dma_map;/* DMA map for data */ struct tw_cl_req_packet req_pkt;/* req pkt understood by CL */ }; /* Per-controller structure. */ struct twa_softc { struct tw_cl_ctlr_handle ctlr_handle; struct tw_osli_req_context *req_ctxt_buf; /* Controller state. */ TW_UINT32 state; TW_UINT32 flags; TW_INT32 device_id; TW_UINT32 alignment; TW_UINT32 sg_size_factor; TW_VOID *non_dma_mem; TW_VOID *dma_mem; TW_UINT64 dma_mem_phys; /* Request queues and arrays. */ struct tw_cl_link req_q_head[TW_OSLI_Q_COUNT]; struct task deferred_intr_callback;/* taskqueue function */ struct mtx io_lock_handle;/* general purpose lock */ struct mtx *io_lock;/* ptr to general purpose lock */ struct mtx q_lock_handle; /* queue manipulation lock */ struct mtx *q_lock;/* ptr to queue manipulation lock */ struct mtx sim_lock_handle;/* sim lock shared with cam */ struct mtx *sim_lock;/* ptr to sim lock */ #ifdef TW_OSL_DEBUG struct tw_osli_q_stats q_stats[TW_OSLI_Q_COUNT];/* queue statistics */ #endif /* TW_OSL_DEBUG */ device_t bus_dev; /* bus device */ struct cdev *ctrl_dev; /* control device */ struct resource *reg_res; /* register interface window */ TW_INT32 reg_res_id; /* register resource id */ bus_space_handle_t bus_handle; /* bus space handle */ bus_space_tag_t bus_tag; /* bus space tag */ bus_dma_tag_t parent_tag; /* parent DMA tag */ bus_dma_tag_t cmd_tag; /* DMA tag for CL's DMA'able mem */ bus_dma_tag_t dma_tag; /* data buffer DMA tag */ bus_dma_tag_t ioctl_tag; /* ioctl data buffer DMA tag */ bus_dmamap_t cmd_map; /* DMA map for CL's DMA'able mem */ bus_dmamap_t ioctl_map; /* DMA map for ioctl data buffers */ struct resource *irq_res; /* interrupt resource */ TW_INT32 irq_res_id; /* register resource id */ TW_VOID *intr_handle; /* interrupt handle */ struct sysctl_ctx_list sysctl_ctxt; /* sysctl context */ struct sysctl_oid *sysctl_tree; /* sysctl oid */ struct cam_sim *sim; /* sim for this controller */ struct cam_path *path; /* peripheral, path, tgt, lun associated with this controller */ }; /* * Queue primitives. */ #ifdef TW_OSL_DEBUG #define TW_OSLI_Q_INIT(sc, q_type) do { \ (sc)->q_stats[q_type].cur_len = 0; \ (sc)->q_stats[q_type].max_len = 0; \ } while(0) #define TW_OSLI_Q_INSERT(sc, q_type) do { \ struct tw_osli_q_stats *q_stats = &((sc)->q_stats[q_type]); \ \ if (++(q_stats->cur_len) > q_stats->max_len) \ q_stats->max_len = q_stats->cur_len; \ } while(0) #define TW_OSLI_Q_REMOVE(sc, q_type) \ (sc)->q_stats[q_type].cur_len-- #else /* TW_OSL_DEBUG */ #define TW_OSLI_Q_INIT(sc, q_index) #define TW_OSLI_Q_INSERT(sc, q_index) #define TW_OSLI_Q_REMOVE(sc, q_index) #endif /* TW_OSL_DEBUG */ /* Initialize a queue of requests. */ static __inline TW_VOID tw_osli_req_q_init(struct twa_softc *sc, TW_UINT8 q_type) { TW_CL_Q_INIT(&(sc->req_q_head[q_type])); TW_OSLI_Q_INIT(sc, q_type); } /* Insert the given request at the head of the given queue (q_type). */ static __inline TW_VOID tw_osli_req_q_insert_head(struct tw_osli_req_context *req, TW_UINT8 q_type) { mtx_lock_spin(req->ctlr->q_lock); TW_CL_Q_INSERT_HEAD(&(req->ctlr->req_q_head[q_type]), &(req->link)); TW_OSLI_Q_INSERT(req->ctlr, q_type); mtx_unlock_spin(req->ctlr->q_lock); } /* Insert the given request at the tail of the given queue (q_type). */ static __inline TW_VOID tw_osli_req_q_insert_tail(struct tw_osli_req_context *req, TW_UINT8 q_type) { mtx_lock_spin(req->ctlr->q_lock); TW_CL_Q_INSERT_TAIL(&(req->ctlr->req_q_head[q_type]), &(req->link)); TW_OSLI_Q_INSERT(req->ctlr, q_type); mtx_unlock_spin(req->ctlr->q_lock); } /* Remove and return the request at the head of the given queue (q_type). */ static __inline struct tw_osli_req_context * tw_osli_req_q_remove_head(struct twa_softc *sc, TW_UINT8 q_type) { struct tw_osli_req_context *req = NULL; struct tw_cl_link *link; mtx_lock_spin(sc->q_lock); if ((link = TW_CL_Q_FIRST_ITEM(&(sc->req_q_head[q_type]))) != TW_CL_NULL) { req = TW_CL_STRUCT_HEAD(link, struct tw_osli_req_context, link); TW_CL_Q_REMOVE_ITEM(&(sc->req_q_head[q_type]), &(req->link)); TW_OSLI_Q_REMOVE(sc, q_type); } mtx_unlock_spin(sc->q_lock); return(req); } /* Remove the given request from the given queue (q_type). */ static __inline TW_VOID tw_osli_req_q_remove_item(struct tw_osli_req_context *req, TW_UINT8 q_type) { mtx_lock_spin(req->ctlr->q_lock); TW_CL_Q_REMOVE_ITEM(&(req->ctlr->req_q_head[q_type]), &(req->link)); TW_OSLI_Q_REMOVE(req->ctlr, q_type); mtx_unlock_spin(req->ctlr->q_lock); } #ifdef TW_OSL_DEBUG extern TW_INT32 TW_DEBUG_LEVEL_FOR_OSL; #define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...) \ if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL) \ device_printf(sc->bus_dev, "%s: " fmt "\n", \ __func__, ##args) #define tw_osli_dbg_printf(dbg_level, fmt, args...) \ if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL) \ printf("%s: " fmt "\n", __func__, ##args) #else /* TW_OSL_DEBUG */ #define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...) #define tw_osli_dbg_printf(dbg_level, fmt, args...) #endif /* TW_OSL_DEBUG */ /* For regular printing. */ #define twa_printf(sc, fmt, args...) \ device_printf(((struct twa_softc *)(sc))->bus_dev, fmt, ##args) /* For printing in the "consistent error reporting" format. */ #define tw_osli_printf(sc, err_specific_desc, args...) \ device_printf((sc)->bus_dev, \ "%s: (0x%02X: 0x%04X): %s: " err_specific_desc "\n", ##args) #endif /* TW_OSL_H */ Index: head/sys/dev/twa/tw_osl_freebsd.c =================================================================== --- head/sys/dev/twa/tw_osl_freebsd.c (revision 190328) +++ head/sys/dev/twa/tw_osl_freebsd.c (revision 190329) @@ -1,1663 +1,1663 @@ /* * Copyright (c) 2004-07 Applied Micro Circuits Corporation. * Copyright (c) 2004-05 Vinod Kashyap. * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * 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 */ /* * FreeBSD specific functions not related to CAM, and other * miscellaneous functions. */ #include #include #include #include #ifdef TW_OSL_DEBUG TW_INT32 TW_DEBUG_LEVEL_FOR_OSL = TW_OSL_DEBUG; TW_INT32 TW_OSL_DEBUG_LEVEL_FOR_CL = TW_OSL_DEBUG; #endif /* TW_OSL_DEBUG */ MALLOC_DEFINE(TW_OSLI_MALLOC_CLASS, "twa_commands", "twa commands"); static d_open_t twa_open; static d_close_t twa_close; static d_ioctl_t twa_ioctl; static struct cdevsw twa_cdevsw = { .d_version = D_VERSION, .d_open = twa_open, .d_close = twa_close, .d_ioctl = twa_ioctl, .d_name = "twa", }; static devclass_t twa_devclass; /* * Function name: twa_open * Description: Called when the controller is opened. * Simply marks the controller as open. * * Input: dev -- control device corresponding to the ctlr * flags -- mode of open * fmt -- device type (character/block etc.) * proc -- current process * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_open(struct cdev *dev, TW_INT32 flags, TW_INT32 fmt, d_thread_t *proc) { TW_INT32 unit = dev2unit(dev); struct twa_softc *sc = devclass_get_softc(twa_devclass, unit); tw_osli_dbg_dprintf(5, sc, "entered"); sc->state |= TW_OSLI_CTLR_STATE_OPEN; return(0); } /* * Function name: twa_close * Description: Called when the controller is closed. * Simply marks the controller as not open. * * Input: dev -- control device corresponding to the ctlr * flags -- mode of corresponding open * fmt -- device type (character/block etc.) * proc -- current process * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_close(struct cdev *dev, TW_INT32 flags, TW_INT32 fmt, d_thread_t *proc) { TW_INT32 unit = dev2unit(dev); struct twa_softc *sc = devclass_get_softc(twa_devclass, unit); tw_osli_dbg_dprintf(5, sc, "entered"); sc->state &= ~TW_OSLI_CTLR_STATE_OPEN; return(0); } /* * Function name: twa_ioctl * Description: Called when an ioctl is posted to the controller. * Handles any OS Layer specific cmds, passes the rest * on to the Common Layer. * * Input: dev -- control device corresponding to the ctlr * cmd -- ioctl cmd * buf -- ptr to buffer in kernel memory, which is * a copy of the input buffer in user-space * flags -- mode of corresponding open * proc -- current process * Output: buf -- ptr to buffer in kernel memory, which will * be copied to the output buffer in user-space * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, TW_INT32 flags, d_thread_t *proc) { struct twa_softc *sc = (struct twa_softc *)(dev->si_drv1); TW_INT32 error; tw_osli_dbg_dprintf(5, sc, "entered"); switch (cmd) { case TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH: tw_osli_dbg_dprintf(6, sc, "ioctl: fw_passthru"); error = tw_osli_fw_passthru(sc, (TW_INT8 *)buf); break; case TW_OSL_IOCTL_SCAN_BUS: /* Request CAM for a bus scan. */ tw_osli_dbg_dprintf(6, sc, "ioctl: scan bus"); error = tw_osli_request_bus_scan(sc); break; default: tw_osli_dbg_dprintf(6, sc, "ioctl: 0x%lx", cmd); error = tw_cl_ioctl(&sc->ctlr_handle, cmd, buf); break; } return(error); } static TW_INT32 twa_probe(device_t dev); static TW_INT32 twa_attach(device_t dev); static TW_INT32 twa_detach(device_t dev); static TW_INT32 twa_shutdown(device_t dev); static TW_VOID twa_busdma_lock(TW_VOID *lock_arg, bus_dma_lock_op_t op); #ifdef TW_OSLI_DEFERRED_INTR_USED static int twa_pci_intr_fast(TW_VOID *arg); static TW_VOID twa_deferred_intr(TW_VOID *context, TW_INT32 pending); #else static TW_VOID twa_pci_intr(TW_VOID *arg); #endif /* TW_OSLI_DEFERRED_INTR_USED */ static TW_INT32 tw_osli_alloc_mem(struct twa_softc *sc); static TW_VOID tw_osli_free_resources(struct twa_softc *sc); static TW_VOID twa_map_load_data_callback(TW_VOID *arg, bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error); static TW_VOID twa_map_load_callback(TW_VOID *arg, bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error); static device_method_t twa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, twa_probe), DEVMETHOD(device_attach, twa_attach), DEVMETHOD(device_detach, twa_detach), DEVMETHOD(device_shutdown, twa_shutdown), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), {0, 0} }; static driver_t twa_pci_driver = { "twa", twa_methods, sizeof(struct twa_softc) }; DRIVER_MODULE(twa, pci, twa_pci_driver, twa_devclass, 0, 0); MODULE_DEPEND(twa, cam, 1, 1, 1); MODULE_DEPEND(twa, pci, 1, 1, 1); /* * Function name: twa_probe * Description: Called at driver load time. Claims 9000 ctlrs. * * Input: dev -- bus device corresponding to the ctlr * Output: None * Return value: <= 0 -- success * > 0 -- failure */ static TW_INT32 twa_probe(device_t dev) { static TW_UINT8 first_ctlr = 1; tw_osli_dbg_printf(3, "entered"); if (tw_cl_ctlr_supported(pci_get_vendor(dev), pci_get_device(dev))) { device_set_desc(dev, TW_OSLI_DEVICE_NAME); /* Print the driver version only once. */ if (first_ctlr) { printf("3ware device driver for 9000 series storage " "controllers, version: %s\n", TW_OSL_DRIVER_VERSION_STRING); first_ctlr = 0; } return(0); } return(ENXIO); } /* * Function name: twa_attach * Description: Allocates pci resources; updates sc; adds a node to the * sysctl tree to expose the driver version; makes calls * (to the Common Layer) to initialize ctlr, and to * attach to CAM. * * Input: dev -- bus device corresponding to the ctlr * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_attach(device_t dev) { struct twa_softc *sc = device_get_softc(dev); TW_UINT32 command; TW_INT32 bar_num; TW_INT32 bar0_offset; TW_INT32 bar_size; TW_INT32 error; tw_osli_dbg_dprintf(3, sc, "entered"); sc->ctlr_handle.osl_ctlr_ctxt = sc; /* Initialize the softc structure. */ sc->bus_dev = dev; sc->device_id = pci_get_device(dev); /* Initialize the mutexes right here. */ sc->io_lock = &(sc->io_lock_handle); mtx_init(sc->io_lock, "tw_osl_io_lock", NULL, MTX_SPIN); sc->q_lock = &(sc->q_lock_handle); mtx_init(sc->q_lock, "tw_osl_q_lock", NULL, MTX_SPIN); sc->sim_lock = &(sc->sim_lock_handle); mtx_init(sc->sim_lock, "tw_osl_sim_lock", NULL, MTX_DEF | MTX_RECURSE); sysctl_ctx_init(&sc->sysctl_ctxt); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctxt, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0, ""); if (sc->sysctl_tree == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2000, "Cannot add sysctl tree node", ENXIO); return(ENXIO); } SYSCTL_ADD_STRING(&sc->sysctl_ctxt, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "driver_version", CTLFLAG_RD, TW_OSL_DRIVER_VERSION_STRING, 0, "TWA driver version"); /* Make sure we are going to be able to talk to this board. */ command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_PORTEN) == 0) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2001, "Register window not available", ENXIO); tw_osli_free_resources(sc); return(ENXIO); } /* Force the busmaster enable bit on, in case the BIOS forgot. */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); /* Allocate the PCI register window. */ if ((error = tw_cl_get_pci_bar_info(sc->device_id, TW_CL_BAR_TYPE_MEM, &bar_num, &bar0_offset, &bar_size))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201F, "Can't get PCI BAR info", error); tw_osli_free_resources(sc); return(error); } sc->reg_res_id = PCIR_BARS + bar0_offset; if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE)) == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2002, "Can't allocate register window", ENXIO); tw_osli_free_resources(sc); return(ENXIO); } sc->bus_tag = rman_get_bustag(sc->reg_res); sc->bus_handle = rman_get_bushandle(sc->reg_res); /* Allocate and register our interrupt. */ sc->irq_res_id = 0; if ((sc->irq_res = bus_alloc_resource(sc->bus_dev, SYS_RES_IRQ, &(sc->irq_res_id), 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2003, "Can't allocate interrupt", ENXIO); tw_osli_free_resources(sc); return(ENXIO); } if ((error = bus_setup_intr(sc->bus_dev, sc->irq_res, #ifdef TW_OSLI_DEFERRED_INTR_USED INTR_TYPE_CAM | INTR_FAST, twa_pci_intr_fast, NULL, #else INTR_TYPE_CAM | INTR_MPSAFE, NULL, twa_pci_intr, #endif sc, &sc->intr_handle))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2004, "Can't set up interrupt", error); tw_osli_free_resources(sc); return(error); } #ifdef TW_OSLI_DEFERRED_INTR_USED TASK_INIT(&sc->deferred_intr_callback, 0, twa_deferred_intr, sc); #endif /* TW_OSLI_DEFERRED_INTR_USED */ if ((error = tw_osli_alloc_mem(sc))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2005, "Memory allocation failure", error); tw_osli_free_resources(sc); return(error); } /* Initialize the Common Layer for this controller. */ if ((error = tw_cl_init_ctlr(&sc->ctlr_handle, sc->flags, sc->device_id, TW_OSLI_MAX_NUM_IOS, TW_OSLI_MAX_NUM_AENS, sc->non_dma_mem, sc->dma_mem, sc->dma_mem_phys ))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2006, "Failed to initialize Common Layer/controller", error); tw_osli_free_resources(sc); return(error); } /* Create the control device. */ sc->ctrl_dev = make_dev(&twa_cdevsw, device_get_unit(sc->bus_dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "twa%d", device_get_unit(sc->bus_dev)); sc->ctrl_dev->si_drv1 = sc; if ((error = tw_osli_cam_attach(sc))) { tw_osli_free_resources(sc); tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2007, "Failed to initialize CAM", error); return(error); } return(0); } /* * Function name: tw_osli_alloc_mem * Description: Allocates memory needed both by CL and OSL. * * Input: sc -- OSL internal controller context * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 tw_osli_alloc_mem(struct twa_softc *sc) { struct tw_osli_req_context *req; TW_UINT32 max_sg_elements; TW_UINT32 non_dma_mem_size; TW_UINT32 dma_mem_size; TW_INT32 error; TW_INT32 i; tw_osli_dbg_dprintf(3, sc, "entered"); sc->flags |= (sizeof(bus_addr_t) == 8) ? TW_CL_64BIT_ADDRESSES : 0; sc->flags |= (sizeof(bus_size_t) == 8) ? TW_CL_64BIT_SG_LENGTH : 0; #ifdef TW_OSLI_DEFERRED_INTR_USED sc->flags |= TW_CL_DEFERRED_INTR_USED; #endif /* TW_OSLI_DEFERRED_INTR_USED */ max_sg_elements = (sizeof(bus_addr_t) == 8) ? TW_CL_MAX_64BIT_SG_ELEMENTS : TW_CL_MAX_32BIT_SG_ELEMENTS; if ((error = tw_cl_get_mem_requirements(&sc->ctlr_handle, sc->flags, sc->device_id, TW_OSLI_MAX_NUM_IOS, TW_OSLI_MAX_NUM_AENS, &(sc->alignment), &(sc->sg_size_factor), &non_dma_mem_size, &dma_mem_size ))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2008, "Can't get Common Layer's memory requirements", error); return(error); } if ((sc->non_dma_mem = malloc(non_dma_mem_size, TW_OSLI_MALLOC_CLASS, M_WAITOK)) == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2009, "Can't allocate non-dma memory", ENOMEM); return(ENOMEM); } /* Create the parent dma tag. */ if (bus_dma_tag_create(NULL, /* parent */ sc->alignment, /* alignment */ - 0, /* boundary */ - BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + TW_OSLI_DMA_BOUNDARY, /* boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TW_CL_MAX_IO_SIZE, /* maxsize */ max_sg_elements, /* nsegments */ TW_CL_MAX_IO_SIZE, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &sc->parent_tag /* tag */)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x200A, "Can't allocate parent DMA tag", ENOMEM); return(ENOMEM); } /* Create a dma tag for Common Layer's DMA'able memory (dma_mem). */ if (bus_dma_tag_create(sc->parent_tag, /* parent */ sc->alignment, /* alignment */ 0, /* boundary */ - BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ dma_mem_size, /* maxsize */ 1, /* nsegments */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &sc->cmd_tag /* tag */)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x200B, "Can't allocate DMA tag for Common Layer's " "DMA'able memory", ENOMEM); return(ENOMEM); } if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, BUS_DMA_NOWAIT, &sc->cmd_map)) { /* Try a second time. */ if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, BUS_DMA_NOWAIT, &sc->cmd_map)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x200C, "Can't allocate DMA'able memory for the" "Common Layer", ENOMEM); return(ENOMEM); } } bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem, dma_mem_size, twa_map_load_callback, &sc->dma_mem_phys, 0); /* * Create a dma tag for data buffers; size will be the maximum * possible I/O size (128kB). */ if (bus_dma_tag_create(sc->parent_tag, /* parent */ sc->alignment, /* alignment */ 0, /* boundary */ - BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TW_CL_MAX_IO_SIZE, /* maxsize */ max_sg_elements, /* nsegments */ TW_CL_MAX_IO_SIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ twa_busdma_lock, /* lockfunc */ sc->io_lock, /* lockfuncarg */ &sc->dma_tag /* tag */)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x200F, "Can't allocate DMA tag for data buffers", ENOMEM); return(ENOMEM); } /* * Create a dma tag for ioctl data buffers; size will be the maximum * possible I/O size (128kB). */ if (bus_dma_tag_create(sc->parent_tag, /* parent */ sc->alignment, /* alignment */ 0, /* boundary */ - BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TW_CL_MAX_IO_SIZE, /* maxsize */ max_sg_elements, /* nsegments */ TW_CL_MAX_IO_SIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ twa_busdma_lock, /* lockfunc */ sc->io_lock, /* lockfuncarg */ &sc->ioctl_tag /* tag */)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2010, "Can't allocate DMA tag for ioctl data buffers", ENOMEM); return(ENOMEM); } /* Create just one map for all ioctl request data buffers. */ if (bus_dmamap_create(sc->ioctl_tag, 0, &sc->ioctl_map)) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2011, "Can't create ioctl map", ENOMEM); return(ENOMEM); } /* Initialize request queues. */ tw_osli_req_q_init(sc, TW_OSLI_FREE_Q); tw_osli_req_q_init(sc, TW_OSLI_BUSY_Q); if ((sc->req_ctxt_buf = (struct tw_osli_req_context *) malloc((sizeof(struct tw_osli_req_context) * TW_OSLI_MAX_NUM_IOS), TW_OSLI_MALLOC_CLASS, M_WAITOK)) == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2012, "Failed to allocate request packets", ENOMEM); return(ENOMEM); } bzero(sc->req_ctxt_buf, sizeof(struct tw_osli_req_context) * TW_OSLI_MAX_NUM_IOS); for (i = 0; i < TW_OSLI_MAX_NUM_IOS; i++) { req = &(sc->req_ctxt_buf[i]); req->ctlr = sc; if (bus_dmamap_create(sc->dma_tag, 0, &req->dma_map)) { tw_osli_printf(sc, "request # = %d, error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2013, "Can't create dma map", i, ENOMEM); return(ENOMEM); } /* Insert request into the free queue. */ tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); } return(0); } /* * Function name: tw_osli_free_resources * Description: Performs clean-up at the time of going down. * * Input: sc -- ptr to OSL internal ctlr context * Output: None * Return value: None */ static TW_VOID tw_osli_free_resources(struct twa_softc *sc) { struct tw_osli_req_context *req; TW_INT32 error = 0; tw_osli_dbg_dprintf(3, sc, "entered"); /* Detach from CAM */ tw_osli_cam_detach(sc); if (sc->req_ctxt_buf) while ((req = tw_osli_req_q_remove_head(sc, TW_OSLI_FREE_Q)) != NULL) if ((error = bus_dmamap_destroy(sc->dma_tag, req->dma_map))) tw_osli_dbg_dprintf(1, sc, "dmamap_destroy(dma) returned %d", error); if ((sc->ioctl_tag) && (sc->ioctl_map)) if ((error = bus_dmamap_destroy(sc->ioctl_tag, sc->ioctl_map))) tw_osli_dbg_dprintf(1, sc, "dmamap_destroy(ioctl) returned %d", error); /* Free all memory allocated so far. */ if (sc->req_ctxt_buf) free(sc->req_ctxt_buf, TW_OSLI_MALLOC_CLASS); if (sc->non_dma_mem) free(sc->non_dma_mem, TW_OSLI_MALLOC_CLASS); if (sc->dma_mem) { bus_dmamap_unload(sc->cmd_tag, sc->cmd_map); bus_dmamem_free(sc->cmd_tag, sc->dma_mem, sc->cmd_map); } if (sc->cmd_tag) if ((error = bus_dma_tag_destroy(sc->cmd_tag))) tw_osli_dbg_dprintf(1, sc, "dma_tag_destroy(cmd) returned %d", error); if (sc->dma_tag) if ((error = bus_dma_tag_destroy(sc->dma_tag))) tw_osli_dbg_dprintf(1, sc, "dma_tag_destroy(dma) returned %d", error); if (sc->ioctl_tag) if ((error = bus_dma_tag_destroy(sc->ioctl_tag))) tw_osli_dbg_dprintf(1, sc, "dma_tag_destroy(ioctl) returned %d", error); if (sc->parent_tag) if ((error = bus_dma_tag_destroy(sc->parent_tag))) tw_osli_dbg_dprintf(1, sc, "dma_tag_destroy(parent) returned %d", error); /* Disconnect the interrupt handler. */ if (sc->intr_handle) if ((error = bus_teardown_intr(sc->bus_dev, sc->irq_res, sc->intr_handle))) tw_osli_dbg_dprintf(1, sc, "teardown_intr returned %d", error); if (sc->irq_res != NULL) if ((error = bus_release_resource(sc->bus_dev, SYS_RES_IRQ, sc->irq_res_id, sc->irq_res))) tw_osli_dbg_dprintf(1, sc, "release_resource(irq) returned %d", error); /* Release the register window mapping. */ if (sc->reg_res != NULL) if ((error = bus_release_resource(sc->bus_dev, SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))) tw_osli_dbg_dprintf(1, sc, "release_resource(io) returned %d", error); /* Destroy the control device. */ if (sc->ctrl_dev != (struct cdev *)NULL) destroy_dev(sc->ctrl_dev); if ((error = sysctl_ctx_free(&sc->sysctl_ctxt))) tw_osli_dbg_dprintf(1, sc, "sysctl_ctx_free returned %d", error); } /* * Function name: twa_detach * Description: Called when the controller is being detached from * the pci bus. * * Input: dev -- bus device corresponding to the ctlr * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_detach(device_t dev) { struct twa_softc *sc = device_get_softc(dev); TW_INT32 error; tw_osli_dbg_dprintf(3, sc, "entered"); error = EBUSY; if (sc->state & TW_OSLI_CTLR_STATE_OPEN) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2014, "Device open", error); goto out; } /* Shut the controller down. */ if ((error = twa_shutdown(dev))) goto out; /* Free all resources associated with this controller. */ tw_osli_free_resources(sc); error = 0; out: return(error); } /* * Function name: twa_shutdown * Description: Called at unload/shutdown time. Lets the controller * know that we are going down. * * Input: dev -- bus device corresponding to the ctlr * Output: None * Return value: 0 -- success * non-zero-- failure */ static TW_INT32 twa_shutdown(device_t dev) { struct twa_softc *sc = device_get_softc(dev); TW_INT32 error = 0; tw_osli_dbg_dprintf(3, sc, "entered"); /* Disconnect from the controller. */ if ((error = tw_cl_shutdown_ctlr(&(sc->ctlr_handle), 0))) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2015, "Failed to shutdown Common Layer/controller", error); } return(error); } /* * Function name: twa_busdma_lock * Description: Function to provide synchronization during busdma_swi. * * Input: lock_arg -- lock mutex sent as argument * op -- operation (lock/unlock) expected of the function * Output: None * Return value: None */ TW_VOID twa_busdma_lock(TW_VOID *lock_arg, bus_dma_lock_op_t op) { struct mtx *lock; lock = (struct mtx *)lock_arg; switch (op) { case BUS_DMA_LOCK: mtx_lock_spin(lock); break; case BUS_DMA_UNLOCK: mtx_unlock_spin(lock); break; default: panic("Unknown operation 0x%x for twa_busdma_lock!", op); } } #ifdef TW_OSLI_DEFERRED_INTR_USED /* * Function name: twa_pci_intr_fast * Description: Interrupt handler. Wrapper for twa_interrupt. * * Input: arg -- ptr to OSL internal ctlr context * Output: FILTER_HANDLED or FILTER_STRAY * Return value: None */ static int twa_pci_intr_fast(TW_VOID *arg) { struct twa_softc *sc = (struct twa_softc *)arg; tw_osli_dbg_dprintf(10, sc, "entered"); if (tw_cl_interrupt(&(sc->ctlr_handle))) { taskqueue_enqueue_fast(taskqueue_fast, &(sc->deferred_intr_callback)); return(FILTER_HANDLED); } return(FILTER_STRAY); } #else /* * Function name: twa_pci_intr * Description: Interrupt handler. Wrapper for twa_interrupt. * * Input: arg -- ptr to OSL internal ctlr context * Output: None * Return value: None */ static TW_VOID twa_pci_intr(TW_VOID *arg) { struct twa_softc *sc = (struct twa_softc *)arg; tw_osli_dbg_dprintf(10, sc, "entered"); if (tw_cl_interrupt(&(sc->ctlr_handle))) tw_cl_deferred_interrupt(&(sc->ctlr_handle)); } #endif #ifdef TW_OSLI_DEFERRED_INTR_USED /* * Function name: twa_deferred_intr * Description: Deferred interrupt handler. * * Input: context -- ptr to OSL internal ctlr context * pending -- not used * Output: None * Return value: None */ static TW_VOID twa_deferred_intr(TW_VOID *context, TW_INT32 pending) { struct twa_softc *sc = (struct twa_softc *)context; tw_osli_dbg_dprintf(10, sc, "entered"); tw_cl_deferred_interrupt(&(sc->ctlr_handle)); } #endif /* TW_OSLI_DEFERRED_INTR_USED */ /* * Function name: tw_osli_fw_passthru * Description: Builds a fw passthru cmd pkt, and submits it to CL. * * Input: sc -- ptr to OSL internal ctlr context * buf -- ptr to ioctl pkt understood by CL * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_osli_fw_passthru(struct twa_softc *sc, TW_INT8 *buf) { struct tw_osli_req_context *req; struct tw_osli_ioctl_no_data_buf *user_buf = (struct tw_osli_ioctl_no_data_buf *)buf; TW_TIME end_time; TW_UINT32 timeout = 60; TW_UINT32 data_buf_size_adjusted; struct tw_cl_req_packet *req_pkt; struct tw_cl_passthru_req_packet *pt_req; TW_INT32 error; tw_osli_dbg_dprintf(5, sc, "ioctl: passthru"); if ((req = tw_osli_get_request(sc)) == NULL) return(EBUSY); req->req_handle.osl_req_ctxt = req; req->orig_req = buf; req->flags |= TW_OSLI_REQ_FLAGS_PASSTHRU; req_pkt = &(req->req_pkt); req_pkt->status = 0; req_pkt->tw_osl_callback = tw_osl_complete_passthru; /* Let the Common Layer retry the request on cmd queue full. */ req_pkt->flags |= TW_CL_REQ_RETRY_ON_BUSY; pt_req = &(req_pkt->gen_req_pkt.pt_req); /* * Make sure that the data buffer sent to firmware is a * 512 byte multiple in size. */ data_buf_size_adjusted = (user_buf->driver_pkt.buffer_length + (sc->sg_size_factor - 1)) & ~(sc->sg_size_factor - 1); if ((req->length = data_buf_size_adjusted)) { if ((req->data = malloc(data_buf_size_adjusted, TW_OSLI_MALLOC_CLASS, M_WAITOK)) == NULL) { error = ENOMEM; tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2016, "Could not alloc mem for " "fw_passthru data_buf", error); goto fw_passthru_err; } /* Copy the payload. */ if ((error = copyin((TW_VOID *)(user_buf->pdata), req->data, user_buf->driver_pkt.buffer_length)) != 0) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2017, "Could not copyin fw_passthru data_buf", error); goto fw_passthru_err; } pt_req->sgl_entries = 1; /* will be updated during mapping */ req->flags |= (TW_OSLI_REQ_FLAGS_DATA_IN | TW_OSLI_REQ_FLAGS_DATA_OUT); } else pt_req->sgl_entries = 0; /* no payload */ pt_req->cmd_pkt = (TW_VOID *)(&(user_buf->cmd_pkt)); pt_req->cmd_pkt_length = sizeof(struct tw_cl_command_packet); if ((error = tw_osli_map_request(req))) goto fw_passthru_err; end_time = tw_osl_get_local_time() + timeout; while (req->state != TW_OSLI_REQ_STATE_COMPLETE) { req->flags |= TW_OSLI_REQ_FLAGS_SLEEPING; error = tsleep(req, PRIBIO, "twa_passthru", timeout * hz); if (!(req->flags & TW_OSLI_REQ_FLAGS_SLEEPING)) error = 0; req->flags &= ~TW_OSLI_REQ_FLAGS_SLEEPING; if (! error) { if (((error = req->error_code)) || ((error = (req->state != TW_OSLI_REQ_STATE_COMPLETE))) || ((error = req_pkt->status))) goto fw_passthru_err; break; } if (req_pkt->status) { error = req_pkt->status; goto fw_passthru_err; } if (error == EWOULDBLOCK) { /* Time out! */ tw_osli_printf(sc, "request = %p", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2018, "Passthru request timed out!", req); /* * Should I check here if the timeout happened * because of yet another reset, and not do a * second reset? */ tw_cl_reset_ctlr(&sc->ctlr_handle); /* * Don't touch req after a reset. It (and any * associated data) will already have been * unmapped by the callback. */ user_buf->driver_pkt.os_status = error; error = ETIMEDOUT; goto fw_passthru_err; } /* * Either the request got completed, or we were woken up by a * signal. Calculate the new timeout, in case it was the latter. */ timeout = (end_time - tw_osl_get_local_time()); } /* If there was a payload, copy it back. */ if ((!error) && (req->length)) if ((error = copyout(req->data, user_buf->pdata, user_buf->driver_pkt.buffer_length))) tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2019, "Could not copyout fw_passthru data_buf", error); fw_passthru_err: /* * Print the failure message. For some reason, on certain OS versions, * printing this error message during reset hangs the display (although * the rest of the system is running fine. So, don't print it if the * failure was due to a reset. */ if ((error) && (error != TW_CL_ERR_REQ_BUS_RESET)) tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201A, "Firmware passthru failed!", error); user_buf->driver_pkt.os_status = error; /* Free resources. */ if (req->data) free(req->data, TW_OSLI_MALLOC_CLASS); tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); return(error); } /* * Function name: tw_osl_complete_passthru * Description: Called to complete passthru requests. * * Input: req_handle -- ptr to request handle * Output: None * Return value: None */ TW_VOID tw_osl_complete_passthru(struct tw_cl_req_handle *req_handle) { struct tw_osli_req_context *req = req_handle->osl_req_ctxt; struct twa_softc *sc = req->ctlr; tw_osli_dbg_dprintf(5, sc, "entered"); if (req->state != TW_OSLI_REQ_STATE_BUSY) { tw_osli_printf(sc, "request = %p, status = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201B, "Unposted command completed!!", req, req->state); } /* * Remove request from the busy queue. Just mark it complete. * There's no need to move it into the complete queue as we are * going to be done with it right now. */ req->state = TW_OSLI_REQ_STATE_COMPLETE; tw_osli_req_q_remove_item(req, TW_OSLI_BUSY_Q); tw_osli_unmap_request(req); /* * Don't do a wake up if there was an error even before the request * was sent down to the Common Layer, and we hadn't gotten an * EINPROGRESS. The request originator will then be returned an * error, and he can do the clean-up. */ if ((req->error_code) && (!(req->state & TW_OSLI_REQ_FLAGS_IN_PROGRESS))) return; if (req->flags & TW_OSLI_REQ_FLAGS_PASSTHRU) { if (req->flags & TW_OSLI_REQ_FLAGS_SLEEPING) { /* Wake up the sleeping command originator. */ tw_osli_dbg_dprintf(5, sc, "Waking up originator of request %p", req); req->flags &= ~TW_OSLI_REQ_FLAGS_SLEEPING; wakeup_one(req); } else { /* * If the request completed even before tsleep * was called, simply return. */ if (req->flags & TW_OSLI_REQ_FLAGS_MAPPED) return; tw_osli_printf(sc, "request = %p", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201C, "Passthru callback called, " "and caller not sleeping", req); } } else { tw_osli_printf(sc, "request = %p", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201D, "Passthru callback called for non-passthru request", req); } } /* * Function name: tw_osli_get_request * Description: Gets a request pkt from the free queue. * * Input: sc -- ptr to OSL internal ctlr context * Output: None * Return value: ptr to request pkt -- success * NULL -- failure */ struct tw_osli_req_context * tw_osli_get_request(struct twa_softc *sc) { struct tw_osli_req_context *req; tw_osli_dbg_dprintf(4, sc, "entered"); /* Get a free request packet. */ req = tw_osli_req_q_remove_head(sc, TW_OSLI_FREE_Q); /* Initialize some fields to their defaults. */ if (req) { req->req_handle.osl_req_ctxt = NULL; req->req_handle.cl_req_ctxt = NULL; req->data = NULL; req->length = 0; req->real_data = NULL; req->real_length = 0; req->state = TW_OSLI_REQ_STATE_INIT;/* req being initialized */ req->flags = 0; req->error_code = 0; req->orig_req = NULL; bzero(&(req->req_pkt), sizeof(struct tw_cl_req_packet)); } return(req); } /* * Function name: twa_map_load_data_callback * Description: Callback of bus_dmamap_load for the buffer associated * with data. Updates the cmd pkt (size/sgl_entries * fields, as applicable) to reflect the number of sg * elements. * * Input: arg -- ptr to OSL internal request context * segs -- ptr to a list of segment descriptors * nsegments--# of segments * error -- 0 if no errors encountered before callback, * non-zero if errors were encountered * Output: None * Return value: None */ static TW_VOID twa_map_load_data_callback(TW_VOID *arg, bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error) { struct tw_osli_req_context *req = (struct tw_osli_req_context *)arg; struct twa_softc *sc = req->ctlr; struct tw_cl_req_packet *req_pkt = &(req->req_pkt); tw_osli_dbg_dprintf(10, sc, "entered"); /* Mark the request as currently being processed. */ req->state = TW_OSLI_REQ_STATE_BUSY; /* Move the request into the busy queue. */ tw_osli_req_q_insert_tail(req, TW_OSLI_BUSY_Q); req->flags |= TW_OSLI_REQ_FLAGS_MAPPED; if (req->flags & TW_OSLI_REQ_FLAGS_IN_PROGRESS) tw_osli_allow_new_requests(sc, (TW_VOID *)(req->orig_req)); if (error == EFBIG) { req->error_code = error; goto out; } if (req->flags & TW_OSLI_REQ_FLAGS_PASSTHRU) { struct tw_cl_passthru_req_packet *pt_req; if (req->flags & TW_OSLI_REQ_FLAGS_DATA_IN) bus_dmamap_sync(sc->ioctl_tag, sc->ioctl_map, BUS_DMASYNC_PREREAD); if (req->flags & TW_OSLI_REQ_FLAGS_DATA_OUT) { /* * If we're using an alignment buffer, and we're * writing data, copy the real data out. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) bcopy(req->real_data, req->data, req->real_length); bus_dmamap_sync(sc->ioctl_tag, sc->ioctl_map, BUS_DMASYNC_PREWRITE); } pt_req = &(req_pkt->gen_req_pkt.pt_req); pt_req->sg_list = (TW_UINT8 *)segs; pt_req->sgl_entries += (nsegments - 1); error = tw_cl_fw_passthru(&(sc->ctlr_handle), req_pkt, &(req->req_handle)); } else { struct tw_cl_scsi_req_packet *scsi_req; if (req->flags & TW_OSLI_REQ_FLAGS_DATA_IN) bus_dmamap_sync(sc->dma_tag, req->dma_map, BUS_DMASYNC_PREREAD); if (req->flags & TW_OSLI_REQ_FLAGS_DATA_OUT) { /* * If we're using an alignment buffer, and we're * writing data, copy the real data out. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) bcopy(req->real_data, req->data, req->real_length); bus_dmamap_sync(sc->dma_tag, req->dma_map, BUS_DMASYNC_PREWRITE); } scsi_req = &(req_pkt->gen_req_pkt.scsi_req); scsi_req->sg_list = (TW_UINT8 *)segs; scsi_req->sgl_entries += (nsegments - 1); error = tw_cl_start_io(&(sc->ctlr_handle), req_pkt, &(req->req_handle)); } out: if (error) { req->error_code = error; req_pkt->tw_osl_callback(&(req->req_handle)); /* * If the caller had been returned EINPROGRESS, and he has * registered a callback for handling completion, the callback * will never get called because we were unable to submit the * request. So, free up the request right here. */ if (req->flags & TW_OSLI_REQ_FLAGS_IN_PROGRESS) tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); } } /* * Function name: twa_map_load_callback * Description: Callback of bus_dmamap_load for the buffer associated * with a cmd pkt. * * Input: arg -- ptr to variable to hold phys addr * segs -- ptr to a list of segment descriptors * nsegments--# of segments * error -- 0 if no errors encountered before callback, * non-zero if errors were encountered * Output: None * Return value: None */ static TW_VOID twa_map_load_callback(TW_VOID *arg, bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error) { - *((bus_addr_t *)arg) = segs[0].ds_addr; + *((TW_UINT64 *)arg) = segs[0].ds_addr; } /* * Function name: tw_osli_map_request * Description: Maps a cmd pkt and data associated with it, into * DMA'able memory. * * Input: req -- ptr to request pkt * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_osli_map_request(struct tw_osli_req_context *req) { struct twa_softc *sc = req->ctlr; TW_INT32 error = 0; tw_osli_dbg_dprintf(10, sc, "entered"); /* If the command involves data, map that too. */ if (req->data != NULL) { /* * It's sufficient for the data pointer to be 4-byte aligned * to work with 9000. However, if 4-byte aligned addresses * are passed to bus_dmamap_load, we can get back sg elements * that are not 512-byte multiples in size. So, we will let * only those buffers that are 512-byte aligned to pass * through, and bounce the rest, so as to make sure that we * always get back sg elements that are 512-byte multiples * in size. */ if (((vm_offset_t)req->data % sc->sg_size_factor) || (req->length % sc->sg_size_factor)) { req->flags |= TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED; /* Save original data pointer and length. */ req->real_data = req->data; req->real_length = req->length; req->length = (req->length + (sc->sg_size_factor - 1)) & ~(sc->sg_size_factor - 1); req->data = malloc(req->length, TW_OSLI_MALLOC_CLASS, M_NOWAIT); if (req->data == NULL) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x201E, "Failed to allocate memory " "for bounce buffer", ENOMEM); /* Restore original data pointer and length. */ req->data = req->real_data; req->length = req->real_length; return(ENOMEM); } } /* * Map the data buffer into bus space and build the SG list. */ if (req->flags & TW_OSLI_REQ_FLAGS_PASSTHRU) { /* Lock against multiple simultaneous ioctl calls. */ mtx_lock_spin(sc->io_lock); error = bus_dmamap_load(sc->ioctl_tag, sc->ioctl_map, req->data, req->length, twa_map_load_data_callback, req, BUS_DMA_WAITOK); mtx_unlock_spin(sc->io_lock); } else { /* * There's only one CAM I/O thread running at a time. * So, there's no need to hold the io_lock. */ error = bus_dmamap_load(sc->dma_tag, req->dma_map, req->data, req->length, twa_map_load_data_callback, req, BUS_DMA_WAITOK); } if (!error) error = req->error_code; else { if (error == EINPROGRESS) { /* * Specifying sc->io_lock as the lockfuncarg * in ...tag_create should protect the access * of ...FLAGS_MAPPED from the callback. */ mtx_lock_spin(sc->io_lock); if (!(req->flags & TW_OSLI_REQ_FLAGS_MAPPED)) { req->flags |= TW_OSLI_REQ_FLAGS_IN_PROGRESS; tw_osli_disallow_new_requests(sc); } mtx_unlock_spin(sc->io_lock); error = 0; } else { /* Free alignment buffer if it was used. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) { free(req->data, TW_OSLI_MALLOC_CLASS); /* * Restore original data pointer * and length. */ req->data = req->real_data; req->length = req->real_length; } } } } else { /* Mark the request as currently being processed. */ req->state = TW_OSLI_REQ_STATE_BUSY; /* Move the request into the busy queue. */ tw_osli_req_q_insert_tail(req, TW_OSLI_BUSY_Q); if (req->flags & TW_OSLI_REQ_FLAGS_PASSTHRU) error = tw_cl_fw_passthru(&sc->ctlr_handle, &(req->req_pkt), &(req->req_handle)); else error = tw_cl_start_io(&sc->ctlr_handle, &(req->req_pkt), &(req->req_handle)); if (error) { req->error_code = error; req->req_pkt.tw_osl_callback(&(req->req_handle)); } } return(error); } /* * Function name: tw_osli_unmap_request * Description: Undoes the mapping done by tw_osli_map_request. * * Input: req -- ptr to request pkt * Output: None * Return value: None */ TW_VOID tw_osli_unmap_request(struct tw_osli_req_context *req) { struct twa_softc *sc = req->ctlr; tw_osli_dbg_dprintf(10, sc, "entered"); /* If the command involved data, unmap that too. */ if (req->data != NULL) { if (req->flags & TW_OSLI_REQ_FLAGS_PASSTHRU) { /* Lock against multiple simultaneous ioctl calls. */ mtx_lock_spin(sc->io_lock); if (req->flags & TW_OSLI_REQ_FLAGS_DATA_IN) { bus_dmamap_sync(sc->ioctl_tag, sc->ioctl_map, BUS_DMASYNC_POSTREAD); /* * If we are using a bounce buffer, and we are * reading data, copy the real data in. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) bcopy(req->data, req->real_data, req->real_length); } if (req->flags & TW_OSLI_REQ_FLAGS_DATA_OUT) bus_dmamap_sync(sc->ioctl_tag, sc->ioctl_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->ioctl_tag, sc->ioctl_map); mtx_unlock_spin(sc->io_lock); } else { if (req->flags & TW_OSLI_REQ_FLAGS_DATA_IN) { bus_dmamap_sync(sc->dma_tag, req->dma_map, BUS_DMASYNC_POSTREAD); /* * If we are using a bounce buffer, and we are * reading data, copy the real data in. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) bcopy(req->data, req->real_data, req->real_length); } if (req->flags & TW_OSLI_REQ_FLAGS_DATA_OUT) bus_dmamap_sync(sc->dma_tag, req->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->dma_tag, req->dma_map); } } /* Free alignment buffer if it was used. */ if (req->flags & TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED) { free(req->data, TW_OSLI_MALLOC_CLASS); /* Restore original data pointer and length. */ req->data = req->real_data; req->length = req->real_length; } } #ifdef TW_OSL_DEBUG TW_VOID twa_report_stats(TW_VOID); TW_VOID twa_reset_stats(TW_VOID); TW_VOID tw_osli_print_ctlr_stats(struct twa_softc *sc); TW_VOID twa_print_req_info(struct tw_osli_req_context *req); /* * Function name: twa_report_stats * Description: For being called from ddb. Calls functions that print * OSL and CL internal stats for the controller. * * Input: None * Output: None * Return value: None */ TW_VOID twa_report_stats(TW_VOID) { struct twa_softc *sc; TW_INT32 i; for (i = 0; (sc = devclass_get_softc(twa_devclass, i)) != NULL; i++) { tw_osli_print_ctlr_stats(sc); tw_cl_print_ctlr_stats(&sc->ctlr_handle); } } /* * Function name: tw_osli_print_ctlr_stats * Description: For being called from ddb. Prints OSL controller stats * * Input: sc -- ptr to OSL internal controller context * Output: None * Return value: None */ TW_VOID tw_osli_print_ctlr_stats(struct twa_softc *sc) { twa_printf(sc, "osl_ctlr_ctxt = %p\n", sc); twa_printf(sc, "OSLq type current max\n"); twa_printf(sc, "free %04d %04d\n", sc->q_stats[TW_OSLI_FREE_Q].cur_len, sc->q_stats[TW_OSLI_FREE_Q].max_len); twa_printf(sc, "busy %04d %04d\n", sc->q_stats[TW_OSLI_BUSY_Q].cur_len, sc->q_stats[TW_OSLI_BUSY_Q].max_len); } /* * Function name: twa_print_req_info * Description: For being called from ddb. Calls functions that print * OSL and CL internal details for the request. * * Input: req -- ptr to OSL internal request context * Output: None * Return value: None */ TW_VOID twa_print_req_info(struct tw_osli_req_context *req) { struct twa_softc *sc = req->ctlr; twa_printf(sc, "OSL details for request:\n"); twa_printf(sc, "osl_req_ctxt = %p, cl_req_ctxt = %p\n" "data = %p, length = 0x%x, real_data = %p, real_length = 0x%x\n" "state = 0x%x, flags = 0x%x, error = 0x%x, orig_req = %p\n" "next_req = %p, prev_req = %p, dma_map = %p\n", req->req_handle.osl_req_ctxt, req->req_handle.cl_req_ctxt, req->data, req->length, req->real_data, req->real_length, req->state, req->flags, req->error_code, req->orig_req, req->link.next, req->link.prev, req->dma_map); tw_cl_print_req_info(&(req->req_handle)); } /* * Function name: twa_reset_stats * Description: For being called from ddb. * Resets some OSL controller stats. * * Input: None * Output: None * Return value: None */ TW_VOID twa_reset_stats(TW_VOID) { struct twa_softc *sc; TW_INT32 i; for (i = 0; (sc = devclass_get_softc(twa_devclass, i)) != NULL; i++) { sc->q_stats[TW_OSLI_FREE_Q].max_len = 0; sc->q_stats[TW_OSLI_BUSY_Q].max_len = 0; tw_cl_reset_stats(&sc->ctlr_handle); } } #endif /* TW_OSL_DEBUG */