This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
NO WARRANTY THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
DISCLAIMER OF LIABILITY NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Bugs/Comments/Suggestions should be mailed to: aradford@gmail.com
Note: This version of the driver does not contain a bundled firmware image.
History ------- 2.26.02.000 - Driver cleanup for kernel submission. 2.26.02.001 - Replace schedule_timeout() calls with msleep(). 2.26.02.002 - Add support for PAE mode. Add lun support. Fix twa_remove() to free irq handler/unregister_chrdev() before shutting down card. Change to new 'change_queue_depth' api. Fix 'handled=1' ISR usage, remove bogus IRQ check. Remove un-needed eh_abort handler. Add support for embedded firmware error strings. 2.26.02.003 - Correctly handle single sgl's with use_sg=1. 2.26.02.004 - Add support for 9550SX controllers. 2.26.02.005 - Fix use_sg == 0 mapping on systems with 4GB or higher. 2.26.02.006 - Fix 9550SX pchip reset timeout. Add big endian support. 2.26.02.007 - Disable local interrupts during kmap/unmap_atomic(). 2.26.02.008 - Free irq handler in __twa_shutdown(). Serialize reset code. Add support for 9650SE controllers. 2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails. 2.26.02.010 - Add support for 9690SA controllers. 2.26.02.011 - Increase max AENs drained to 256. Add MSI support and "use_msi" module parameter. Fix bug in twa_get_param() on 4GB+. Use pci_resource_len() for ioremap(). 2.26.02.012 - Add power management support. 2.26.02.013 - Fix bug in twa_load_sgl(). 2.26.02.014 - Force 60 second timeout default.
*/
/* * The controllers use an inline buffer instead of a mapped SGL for small, * single entry buffers. Note that we treat a zero-length transfer like * a mapped SGL.
*/ staticbool twa_command_mapped(struct scsi_cmnd *cmd)
{ return scsi_sg_count(cmd) != 1 ||
scsi_bufflen(cmd) >= TW_MIN_SGL_LENGTH;
}
/* This function will complete an aen request from the isr */ staticint twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Command_Apache_Header *header; unsignedshort aen; int retval = 1;
/* First check for internal completion of set param for time sync */ if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) { /* Keep reading the queue in case there are more aen's */ if (twa_aen_read_queue(tw_dev, request_id)) goto out2; else {
retval = 0; goto out;
}
}
switch (aen) { case TW_AEN_QUEUE_EMPTY: /* Quit reading the queue if this is the last one */ break; case TW_AEN_SYNC_TIME_WITH_HOST:
twa_aen_sync_time(tw_dev, request_id);
retval = 0; goto out; default:
twa_aen_queue_event(tw_dev, header);
/* If there are more aen's, keep reading the queue */ if (twa_aen_read_queue(tw_dev, request_id)) goto out2; else {
retval = 0; goto out;
}
}
retval = 0;
out2:
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
out: return retval;
} /* End twa_aen_complete() */
/* This function will drain aen queue */ staticint twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{ int request_id = 0; unsignedchar cdb[TW_MAX_CDB_LEN];
TW_SG_Entry sglist[1]; int finished = 0, count = 0;
TW_Command_Full *full_command_packet;
TW_Command_Apache_Header *header; unsignedshort aen; int first_reset = 0, queue = 0, retval = 1;
if (no_check_reset)
first_reset = 0; else
first_reset = 1;
if (tw_dev->generic_buffer_phys[request_id] & TW_ALIGNMENT_9000_SGL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain"); goto out;
}
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
do { /* Send command to the board */ if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense"); goto out;
}
/* Now poll for completion */ if (twa_poll_response(tw_dev, request_id, 30)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "No valid response while draining AEN queue");
tw_dev->posted_request_count--; goto out;
}
/* This function will read the aen queue from the isr */ staticint twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar cdb[TW_MAX_CDB_LEN];
TW_SG_Entry sglist[1];
TW_Command_Full *full_command_packet; int retval = 1;
/* This function will sync firmware time with the host time */ staticvoid twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
time64_t local_time;
/* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime);
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command */
twa_post_command_packet(tw_dev, request_id, 1);
} /* End twa_aen_sync_time() */
/* This function will allocate memory and check if it is correctly aligned */ staticint twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{ int i;
dma_addr_t dma_handle; unsignedlong *cpu_addr; int retval = 1;
/* This function will check the srl and decide if we are compatible */ staticint twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
{ int retval = 1; unsignedshort fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; unsignedshort fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
u32 init_connect_result = 0;
/* Now copy down the entire ioctl */ if (copy_from_user(tw_ioctl, argp, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length)) goto out3;
/* See which ioctl we are doing */ switch (cmd) { case TW_IOCTL_FIRMWARE_PASS_THROUGH:
spin_lock_irqsave(tw_dev->host->host_lock, flags);
twa_get_request_id(tw_dev, &request_id);
/* Flag internal command */
tw_dev->srb[request_id] = NULL;
/* Flag chrdev ioctl */
tw_dev->chrdev_request_id = request_id;
/* Load request id and sglist for both command types */
twa_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
/* Now post the command packet to the controller */
twa_post_command_packet(tw_dev, request_id, 1);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
/* Now wait for command to complete */
timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
/* We timed out, and didn't get an interrupt */ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */
printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
tw_dev->host->host_no, TW_DRIVER, 0x37,
cmd);
retval = TW_IOCTL_ERROR_OS_EIO;
twa_reset_device_extension(tw_dev); goto out3;
}
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
/* Now copy the entire response to userspace */ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0)
retval = 0;
out3: /* Now free ioctl buf memory */
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Ioctl_Buf_Apache) + data_buffer_length_adjusted,
cpu_addr, dma_handle);
out2:
mutex_unlock(&tw_dev->ioctl_lock);
out:
mutex_unlock(&twa_chrdev_mutex); return retval;
} /* End twa_chrdev_ioctl() */
/* This function handles open for the character device */ /* NOTE that this function will race with remove. */ staticint twa_chrdev_open(struct inode *inode, struct file *file)
{ unsignedint minor_number; int retval = TW_IOCTL_ERROR_OS_ENODEV;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EACCES; goto out;
}
minor_number = iminor(inode); if (minor_number >= twa_device_extension_count) goto out;
retval = 0;
out: return retval;
} /* End twa_chrdev_open() */
/* This function will print readable messages from status register errors */ staticint twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
{ int retval = 1;
/* Check for various error conditions and handle them appropriately */ if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "PCI Parity Error: clearing");
writel(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
}
/* This function will empty the response queue */ staticint twa_empty_response_queue(TW_Device_Extension *tw_dev)
{
u32 status_reg_value; int count = 0, retval = 1;
/* This function will clear the pchip/response queue on 9550SX */ staticint twa_empty_response_queue_large(TW_Device_Extension *tw_dev)
{
u32 response_que_value = 0; unsignedlong before; int retval = 1;
if (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9000) {
before = jiffies; while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) {
response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev));
msleep(1); if (time_after(jiffies, before + HZ * 30)) goto out;
} /* P-chip settle time */
msleep(500);
retval = 0;
} else
retval = 0;
out: return retval;
} /* End twa_empty_response_queue_large() */
/* This function passes sense keys from firmware to scsi layer */ staticint twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
{
TW_Command_Full *full_command_packet; unsignedshort error; int retval = 1; char *error_str;
/* This function will free up device extension resources */ staticvoid twa_free_device_extension(TW_Device_Extension *tw_dev)
{ if (tw_dev->command_packet_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Command_Full) * TW_Q_LENGTH,
tw_dev->command_packet_virt[0],
tw_dev->command_packet_phys[0]);
if (tw_dev->generic_buffer_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev,
TW_SECTOR_SIZE * TW_Q_LENGTH,
tw_dev->generic_buffer_virt[0],
tw_dev->generic_buffer_phys[0]);
kfree(tw_dev->event_queue[0]);
} /* End twa_free_device_extension() */
/* This function will free a request id */ staticvoid twa_free_request_id(TW_Device_Extension *tw_dev, int request_id)
{
tw_dev->free_queue[tw_dev->free_tail] = request_id;
tw_dev->state[request_id] = TW_S_FINISHED;
tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
} /* End twa_free_request_id() */
/* This function will get parameter table entries from the firmware */ staticvoid *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param; void *retval = NULL;
/* Post the command packet to the board */
twa_post_command_packet(tw_dev, request_id, 1);
/* Poll for completion */ if (twa_poll_response(tw_dev, request_id, 30))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param") else
retval = (void *)&(param->data[0]);
/* This function will assign an available request id */ staticvoid twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
{
*request_id = tw_dev->free_queue[tw_dev->free_head];
tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
tw_dev->state[*request_id] = TW_S_STARTED;
} /* End twa_get_request_id() */
/* This function will send an initconnection command to controller */ staticint twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
u32 set_features, unsignedshort current_fw_srl, unsignedshort current_fw_arch_id, unsignedshort current_fw_branch, unsignedshort current_fw_build, unsignedshort *fw_on_ctlr_srl, unsignedshort *fw_on_ctlr_arch_id, unsignedshort *fw_on_ctlr_branch, unsignedshort *fw_on_ctlr_build,
u32 *init_connect_result)
{
TW_Command_Full *full_command_packet;
TW_Initconnect *tw_initconnect; int request_id = 0, retval = 1;
/* This function will initialize the fields of a device extension */ staticint twa_initialize_device_extension(TW_Device_Extension *tw_dev)
{ int i, retval = 1;
/* Handle command interrupt */ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
TW_MASK_COMMAND_INTERRUPT(tw_dev); /* Drain as many pending commands as we can */ while (tw_dev->pending_request_count > 0) {
request_id = tw_dev->pending_queue[tw_dev->pending_head]; if (tw_dev->state[request_id] != TW_S_PENDING) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
} if (twa_post_command_packet(tw_dev, request_id, 1)==0) {
tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;
tw_dev->pending_request_count--;
} else { /* If we get here, we will continue re-posting on the next command interrupt */ break;
}
}
}
/* Handle response interrupt */ if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
/* Drain the response queue from the board */ while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) { /* Complete the response */
response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_que.response_id);
full_command_packet = tw_dev->command_packet_virt[request_id];
error = 0; /* Check for command packet errors */ if (full_command_packet->command.newcommand.status != 0) { if (tw_dev->srb[request_id] != NULL) {
error = twa_fill_sense(tw_dev, request_id, 1, 1);
} else { /* Skip ioctl error prints */ if (request_id != tw_dev->chrdev_request_id) {
error = twa_fill_sense(tw_dev, request_id, 0, 1);
}
}
}
/* Check for correct state */ if (tw_dev->state[request_id] != TW_S_POSTED) { if (tw_dev->srb[request_id] != NULL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
}
}
/* Check for internal command completion */ if (tw_dev->srb[request_id] == NULL) { if (request_id != tw_dev->chrdev_request_id) { if (twa_aen_complete(tw_dev, request_id))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
wake_up(&tw_dev->ioctl_wqueue);
}
} else { struct scsi_cmnd *cmd;
cmd = tw_dev->srb[request_id];
twa_scsiop_execute_scsi_complete(tw_dev, request_id); /* If no error command was a success */ if (error == 0) {
cmd->result = (DID_OK << 16);
}
/* If error, command failed */ if (error == 1) { /* Ask for a host reset */
cmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
}
/* Report residual bytes for single sgl */ if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
u32 length = le32_to_cpu(full_command_packet->command.newcommand.sg_list[0].length);
if (length < scsi_bufflen(cmd))
scsi_set_resid(cmd, scsi_bufflen(cmd) - length);
}
/* Now complete the io */ if (twa_command_mapped(cmd))
scsi_dma_unmap(cmd);
scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
}
/* Check for valid status after each drain */
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); if (twa_check_bits(status_reg_value)) { if (twa_decode_bits(tw_dev, status_reg_value)) {
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
}
}
}
}
twa_interrupt_bail:
spin_unlock(tw_dev->host->host_lock); return IRQ_RETVAL(handled);
} /* End twa_interrupt() */
/* This function will load the request id and various sgls for ioctls */ staticvoid twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
{
TW_Command *oldcommand;
TW_Command_Apache *newcommand;
TW_SG_Entry *sgl; unsignedint pae = 0;
if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { /* Load the sg list */ if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)
sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry)/4) + pae); else
sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));
sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
sgl->length = cpu_to_le32(length);
oldcommand->size += pae;
}
}
} /* End twa_load_sgl() */
/* This function will poll for a response interrupt of a request */ staticint twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{ int retval = 1, found = 0, response_request_id;
TW_Response_Queue response_queue;
TW_Command_Full *full_command_packet = tw_dev->command_packet_virt[request_id];
if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {
response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
response_request_id = TW_RESID_OUT(response_queue.response_id); if (request_id != response_request_id) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response"); goto out;
} if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { if (full_command_packet->command.newcommand.status != 0) { /* bad response */
twa_fill_sense(tw_dev, request_id, 0, 0); goto out;
}
found = 1;
} else { if (full_command_packet->command.oldcommand.status != 0) { /* bad response */
twa_fill_sense(tw_dev, request_id, 0, 0); goto out;
}
found = 1;
}
}
if (found)
retval = 0;
out: return retval;
} /* End twa_poll_response() */
/* This function will poll the status register for a flag */ staticint twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
while ((status_reg_value & flag) != flag) {
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will poll the status register for disappearance of a flag */ staticint twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
while ((status_reg_value & flag) != 0) {
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will attempt to post a command packet to the board */ staticint twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal)
{
u32 status_reg_value;
dma_addr_t command_que_value; int retval = 1;
/* Only pend internal driver commands */ if (!internal) {
retval = SCSI_MLQUEUE_HOST_BUSY; goto out;
}
/* Couldn't post the command packet, so we do it later */ if (tw_dev->state[request_id] != TW_S_PENDING) {
tw_dev->state[request_id] = TW_S_PENDING;
tw_dev->pending_request_count++; if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
tw_dev->max_pending_request_count = tw_dev->pending_request_count;
}
tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;
}
TW_UNMASK_COMMAND_INTERRUPT(tw_dev); goto out;
} else { if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) ||
(tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { /* Now write upper 4 bytes */
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4);
} else { if (sizeof(dma_addr_t) > 4) {
command_que_value += TW_COMMAND_OFFSET;
writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4);
} else {
writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
}
}
tw_dev->state[request_id] = TW_S_POSTED;
tw_dev->posted_request_count++; if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
tw_dev->max_posted_request_count = tw_dev->posted_request_count;
}
}
retval = 0;
out: return retval;
} /* End twa_post_command_packet() */
/* This function will reset a device extension */ staticint twa_reset_device_extension(TW_Device_Extension *tw_dev)
{ int i = 0; int retval = 1; unsignedlong flags = 0;
/* Abort all requests that are in progress */ for (i = 0; i < TW_Q_LENGTH; i++) { if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) { if (tw_dev->srb[i]) { struct scsi_cmnd *cmd = tw_dev->srb[i];
/* This function will reset a controller */ staticint twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
{ int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;
while (tries < TW_MAX_RESET_TRIES) { if (do_soft_reset) {
TW_SOFT_RESET(tw_dev); /* Clear pchip/response queue on 9550SX */ if (twa_empty_response_queue_large(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x36, "Response queue (large) empty failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
}
/* Make sure controller is in a good state */ if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 60)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
/* Check for compatibility/flash */ if (twa_check_srl(tw_dev, &flashed)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Compatibility check failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
} else { if (flashed) {
tries++; continue;
}
}
/* Drain the AEN queue */ if (twa_aen_drain_queue(tw_dev, soft_reset)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x22, "AEN drain failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
/* If we got here, controller is in a good state */
retval = 0; goto out;
}
out: return retval;
} /* End twa_reset_sequence() */
/* This funciton returns unit geometry in cylinders/heads/sectors */ staticint twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
{ int heads, sectors, cylinders;
/* This is the new scsi eh reset function */ staticint twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
{
TW_Device_Extension *tw_dev = NULL; int retval = FAILED;
/* Make sure we are not issuing an ioctl or resetting from ioctl */
mutex_lock(&tw_dev->ioctl_lock);
/* Now reset the card and some of the device extension data */ if (twa_reset_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); goto out;
}
/* This is the main scsi queue function to handle scsi opcodes */ staticint twa_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{ void (*done)(struct scsi_cmnd *) = scsi_done; int request_id, retval;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
/* If we are resetting due to timed out ioctl, report as busy */ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
retval = SCSI_MLQUEUE_HOST_BUSY; goto out;
}
/* Check if this FW supports luns */ if ((SCpnt->device->lun != 0) && (tw_dev->tw_compat_info.working_srl < TW_FW_SRL_LUNS_SUPPORTED)) {
SCpnt->result = (DID_BAD_TARGET << 16);
done(SCpnt);
retval = 0; goto out;
}
/* Get a free request id */
twa_get_request_id(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL); switch (retval) { case SCSI_MLQUEUE_HOST_BUSY: if (twa_command_mapped(SCpnt))
scsi_dma_unmap(SCpnt);
twa_free_request_id(tw_dev, request_id); break; case 1:
SCpnt->result = (DID_ERROR << 16); if (twa_command_mapped(SCpnt))
scsi_dma_unmap(SCpnt);
done(SCpnt);
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
retval = 0;
}
out: return retval;
} /* End twa_scsi_queue() */
static DEF_SCSI_QCMD(twa_scsi_queue)
/* This function hands scsi cdb's to the firmware */ staticint twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, unsignedchar *cdb, int use_sg,
TW_SG_Entry *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet;
u32 num_sectors = 0x0; int i, sg_count; struct scsi_cmnd *srb = NULL; struct scatterlist *sg; int retval = 1;
if (tw_dev->srb[request_id])
srb = tw_dev->srb[request_id];
/* Update SG statistics */ if (srb) {
tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]); if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
tw_dev->max_sgl_entries = tw_dev->sgl_entries;
}
/* Now post the command to the board */ if (srb) {
retval = twa_post_command_packet(tw_dev, request_id, 0);
} else {
twa_post_command_packet(tw_dev, request_id, 1);
retval = 0;
}
out: return retval;
} /* End twa_scsiop_execute_scsi() */
/* This function completes an execute scsi operation */ staticvoid twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
{ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
if (!twa_command_mapped(cmd) &&
(cmd->sc_data_direction == DMA_FROM_DEVICE ||
cmd->sc_data_direction == DMA_BIDIRECTIONAL)) { if (scsi_sg_count(cmd) == 1) { void *buf = tw_dev->generic_buffer_virt[request_id];
scsi_sg_copy_from_buffer(cmd, buf, TW_SECTOR_SIZE);
}
}
} /* End twa_scsiop_execute_scsi_complete() */
/* This function tells the controller to shut down */ staticvoid __twa_shutdown(TW_Device_Extension *tw_dev)
{ /* Disable interrupts */
TW_DISABLE_INTERRUPTS(tw_dev);
/* Free up the IRQ */
free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
/* Tell the card we are shutting down */ if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x31, "Connection shutdown failed");
} else {
printk(KERN_WARNING "3w-9xxx: Shutdown complete.\n");
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.