diff options
Diffstat (limited to 'drivers/scsi/3w-9xxx.c')
| -rw-r--r-- | drivers/scsi/3w-9xxx.c | 684 |
1 files changed, 407 insertions, 277 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 31c49754227..0a7325361d2 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -1,9 +1,11 @@ /* 3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> + Modifications By: Tom Couch <linuxraid@lsi.com> - Copyright (C) 2004-2005 Applied Micro Circuits Corporation. + Copyright (C) 2004-2009 Applied Micro Circuits Corporation. + Copyright (C) 2010 LSI Corporation. 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 @@ -39,10 +41,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com Note: This version of the driver does not contain a bundled firmware image. @@ -61,6 +63,22 @@ 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. */ #include <linux/module.h> @@ -74,6 +92,7 @@ #include <linux/pci.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -84,24 +103,29 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.004" +#define TW_DRIVER_VERSION "2.26.02.014" +static DEFINE_MUTEX(twa_chrdev_mutex); static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; extern struct timezone sys_tz; /* Module parameters */ -MODULE_AUTHOR ("AMCC"); +MODULE_AUTHOR ("LSI"); MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(TW_DRIVER_VERSION); +static int use_msi = 0; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0"); + /* Function prototypes */ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header); static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id); static char *twa_aen_severity_lookup(unsigned char severity_code); static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id); -static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static int twa_chrdev_open(struct inode *inode, struct file *file); static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host); static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id); @@ -116,11 +140,11 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, unsigned short *fw_on_ctlr_branch, unsigned short *fw_on_ctlr_build, u32 *init_connect_result); -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); +static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds); static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds); static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal); -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); +static int twa_reset_device_extension(TW_Device_Extension *tw_dev); static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset); static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg); static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id); @@ -130,9 +154,10 @@ static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id); /* Functions */ /* Show some statistics about the card */ -static ssize_t twa_show_stats(struct class_device *class_dev, char *buf) +static ssize_t twa_show_stats(struct device *dev, + struct device_attribute *attr, char *buf) { - struct Scsi_Host *host = class_to_shost(class_dev); + struct Scsi_Host *host = class_to_shost(dev); TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; unsigned long flags = 0; ssize_t len; @@ -165,8 +190,12 @@ static ssize_t twa_show_stats(struct class_device *class_dev, char *buf) } /* End twa_show_stats() */ /* This function will set a devices queue depth */ -static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth) +static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > TW_Q_LENGTH-2) queue_depth = TW_Q_LENGTH-2; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); @@ -174,7 +203,7 @@ static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth) } /* End twa_change_queue_depth() */ /* Create sysfs 'stats' entry */ -static struct class_device_attribute twa_host_stats_attr = { +static struct device_attribute twa_host_stats_attr = { .attr = { .name = "stats", .mode = S_IRUGO, @@ -183,17 +212,18 @@ static struct class_device_attribute twa_host_stats_attr = { }; /* Host attributes initializer */ -static struct class_device_attribute *twa_host_attrs[] = { +static struct device_attribute *twa_host_attrs[] = { &twa_host_stats_attr, NULL, }; /* File operations struct for character device */ -static struct file_operations twa_fops = { +static const struct file_operations twa_fops = { .owner = THIS_MODULE, - .ioctl = twa_chrdev_ioctl, + .unlocked_ioctl = twa_chrdev_ioctl, .open = twa_chrdev_open, - .release = NULL + .release = NULL, + .llseek = noop_llseek, }; /* This function will complete an aen request from the isr */ @@ -207,7 +237,7 @@ static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id) header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; tw_dev->posted_request_count--; - aen = header->status_block.error; + aen = le16_to_cpu(header->status_block.error); full_command_packet = tw_dev->command_packet_virt[request_id]; command_packet = &full_command_packet->command.oldcommand; @@ -304,7 +334,7 @@ static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset) tw_dev->posted_request_count--; header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; - aen = header->status_block.error; + aen = le16_to_cpu(header->status_block.error); queue = 0; count++; @@ -364,7 +394,7 @@ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H tw_dev->aen_clobber = 1; } - aen = header->status_block.error; + aen = le16_to_cpu(header->status_block.error); memset(event, 0, sizeof(TW_Event)); event->severity = TW_SEV_OUT(header->status_block.severity__reserved); @@ -381,7 +411,7 @@ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0'; event->parameter_len = strlen(header->err_specific_desc); - memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len); + memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + (error_str[0] == '\0' ? 0 : (1 + strlen(error_str)))); if (event->severity != TW_AEN_SEVERITY_DEBUG) printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", host, @@ -461,24 +491,24 @@ static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id) command_packet = &full_command_packet->command.oldcommand; command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM); command_packet->request_id = request_id; - command_packet->byte8_offset.param.sgl[0].address = tw_dev->generic_buffer_phys[request_id]; - command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE; + command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); + command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE); command_packet->size = TW_COMMAND_SIZE; - command_packet->byte6_offset.parameter_count = 1; + command_packet->byte6_offset.parameter_count = cpu_to_le16(1); /* Setup the param */ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; memset(param, 0, TW_SECTOR_SIZE); - param->table_id = TW_TIMEKEEP_TABLE | 0x8000; /* Controller time keep table */ - param->parameter_id = 0x3; /* SchedulerTime */ - param->parameter_size_bytes = 4; + param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */ + param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */ + param->parameter_size_bytes = cpu_to_le16(4); /* Convert system time in UTC to local time seconds since last Sunday 12:00AM */ do_gettimeofday(&utc); local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60)); schedulertime = local_time - (3 * 86400); - schedulertime = schedulertime % 604800; + schedulertime = cpu_to_le32(schedulertime % 604800); memcpy(param->data, &schedulertime, sizeof(u32)); @@ -561,9 +591,9 @@ static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed) goto out; } - tw_dev->working_srl = fw_on_ctlr_srl; - tw_dev->working_branch = fw_on_ctlr_branch; - tw_dev->working_build = fw_on_ctlr_build; + tw_dev->tw_compat_info.working_srl = fw_on_ctlr_srl; + tw_dev->tw_compat_info.working_branch = fw_on_ctlr_branch; + tw_dev->tw_compat_info.working_build = fw_on_ctlr_build; /* Try base mode compatibility */ if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) { @@ -585,18 +615,32 @@ static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed) } goto out; } - tw_dev->working_srl = TW_BASE_FW_SRL; - tw_dev->working_branch = TW_BASE_FW_BRANCH; - tw_dev->working_build = TW_BASE_FW_BUILD; - } + tw_dev->tw_compat_info.working_srl = TW_BASE_FW_SRL; + tw_dev->tw_compat_info.working_branch = TW_BASE_FW_BRANCH; + tw_dev->tw_compat_info.working_build = TW_BASE_FW_BUILD; + } + + /* Load rest of compatibility struct */ + strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); + tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL; + tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH; + tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD; + tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL; + tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH; + tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD; + tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl; + tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch; + tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build; + retval = 0; out: return retval; } /* End twa_check_srl() */ /* This function handles ioctl for the character device */ -static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct inode *inode = file_inode(file); long timeout; unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0; dma_addr_t dma_handle; @@ -615,6 +659,8 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int int retval = TW_IOCTL_ERROR_OS_EFAULT; void __user *argp = (void __user *)arg; + mutex_lock(&twa_chrdev_mutex); + /* Only let one of these through at a time */ if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) { retval = TW_IOCTL_ERROR_OS_EINTR; @@ -626,7 +672,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int goto out2; /* Check data buffer size */ - if (driver_command.buffer_length > TW_MAX_SECTORS * 512) { + if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) { retval = TW_IOCTL_ERROR_OS_EINVAL; goto out2; } @@ -662,7 +708,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int full_command_packet = &tw_ioctl->firmware_command; /* Load request id and sglist for both command types */ - twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); + twa_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); @@ -675,26 +721,14 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int /* Now wait for command to complete */ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); - /* See if we reset while waiting for the ioctl to complete */ - if (test_bit(TW_IN_RESET, &tw_dev->flags)) { - clear_bit(TW_IN_RESET, &tw_dev->flags); - retval = TW_IOCTL_ERROR_OS_ERESTARTSYS; - goto out3; - } - /* 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, 0xc, + tw_dev->host->host_no, TW_DRIVER, 0x37, cmd); retval = TW_IOCTL_ERROR_OS_EIO; - spin_lock_irqsave(tw_dev->host->host_lock, flags); - tw_dev->state[request_id] = TW_S_COMPLETED; - twa_free_request_id(tw_dev, request_id); - tw_dev->posted_request_count--; - spin_unlock_irqrestore(tw_dev->host->host_lock, flags); - twa_reset_device_extension(tw_dev, 1); + twa_reset_device_extension(tw_dev); goto out3; } @@ -710,18 +744,9 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int break; case TW_IOCTL_GET_COMPATIBILITY_INFO: tw_ioctl->driver_command.status = 0; - /* Copy compatiblity struct into ioctl data buffer */ + /* Copy compatibility struct into ioctl data buffer */ tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; - strncpy(tw_compat_info->driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); - tw_compat_info->working_srl = tw_dev->working_srl; - tw_compat_info->working_branch = tw_dev->working_branch; - tw_compat_info->working_build = tw_dev->working_build; - tw_compat_info->driver_srl_high = TW_CURRENT_DRIVER_SRL; - tw_compat_info->driver_branch_high = TW_CURRENT_DRIVER_BRANCH; - tw_compat_info->driver_build_high = TW_CURRENT_DRIVER_BUILD; - tw_compat_info->driver_srl_low = TW_BASE_FW_SRL; - tw_compat_info->driver_branch_low = TW_BASE_FW_BRANCH; - tw_compat_info->driver_build_low = TW_BASE_FW_BUILD; + memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info)); break; case TW_IOCTL_GET_LAST_EVENT: if (tw_dev->event_queue_wrapped) { @@ -855,10 +880,12 @@ out3: 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. */ static int twa_chrdev_open(struct inode *inode, struct file *file) { unsigned int minor_number; @@ -890,7 +917,10 @@ static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value) } if (status_reg_value & TW_STATUS_QUEUE_ERROR) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); + if (((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) && + (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9690SA)) || + (!test_bit(TW_IN_RESET, &tw_dev->flags))) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); } @@ -930,26 +960,20 @@ out: /* This function will clear the pchip/response queue on 9550SX */ static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev) { - u32 status_reg_value, response_que_value; - int count = 0, retval = 1; - - if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) { - status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); + u32 response_que_value = 0; + unsigned long before; + int retval = 1; - while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) { + 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)); - if ((response_que_value & TW_9550SX_DRAIN_COMPLETED) == TW_9550SX_DRAIN_COMPLETED) { - /* P-chip settle time */ - msleep(500); - retval = 0; + msleep(1); + if (time_after(jiffies, before + HZ * 30)) goto out; - } - status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); - count++; } - if (count == TW_MAX_RESPONSE_DRAIN) - goto out; - + /* P-chip settle time */ + msleep(500); retval = 0; } else retval = 0; @@ -971,7 +995,7 @@ static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_ error_str = &(full_command_packet->header.err_specific_desc[strlen(full_command_packet->header.err_specific_desc) + 1]); /* Don't print error for Logical unit not supported during rollcall */ - error = full_command_packet->header.status_block.error; + error = le16_to_cpu(full_command_packet->header.status_block.error); if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE)) { if (print_host) printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n", @@ -1029,13 +1053,12 @@ static void twa_free_request_id(TW_Device_Extension *tw_dev, int request_id) tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH; } /* End twa_free_request_id() */ -/* This function will get parameter table entires from the firmware */ +/* This function will get parameter table entries from the firmware */ static void *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; - unsigned long param_value; void *retval = NULL; /* Setup the command packet */ @@ -1046,18 +1069,17 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM); command_packet->size = TW_COMMAND_SIZE; command_packet->request_id = request_id; - command_packet->byte6_offset.block_count = 1; + command_packet->byte6_offset.block_count = cpu_to_le16(1); /* Now setup the param */ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; memset(param, 0, TW_SECTOR_SIZE); - param->table_id = table_id | 0x8000; - param->parameter_id = parameter_id; - param->parameter_size_bytes = parameter_size_bytes; - param_value = tw_dev->generic_buffer_phys[request_id]; + param->table_id = cpu_to_le16(table_id | 0x8000); + param->parameter_id = cpu_to_le16(parameter_id); + param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes); - command_packet->byte8_offset.param.sgl[0].address = param_value; - command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE; + command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); + command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE); /* Post the command packet to the board */ twa_post_command_packet(tw_dev, request_id, 1); @@ -1106,18 +1128,20 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION); tw_initconnect->request_id = request_id; - tw_initconnect->message_credits = message_credits; + tw_initconnect->message_credits = cpu_to_le16(message_credits); tw_initconnect->features = set_features; /* Turn on 64-bit sgl support if we need to */ tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0; + tw_initconnect->features = cpu_to_le32(tw_initconnect->features); + if (set_features & TW_EXTENDED_INIT_CONNECT) { tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED; - tw_initconnect->fw_srl = current_fw_srl; - tw_initconnect->fw_arch_id = current_fw_arch_id; - tw_initconnect->fw_branch = current_fw_branch; - tw_initconnect->fw_build = current_fw_build; + tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl); + tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id); + tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch); + tw_initconnect->fw_build = cpu_to_le16(current_fw_build); } else tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE; @@ -1129,11 +1153,11 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "No valid response during init connection"); } else { if (set_features & TW_EXTENDED_INIT_CONNECT) { - *fw_on_ctlr_srl = tw_initconnect->fw_srl; - *fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id; - *fw_on_ctlr_branch = tw_initconnect->fw_branch; - *fw_on_ctlr_build = tw_initconnect->fw_build; - *init_connect_result = tw_initconnect->result; + *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl); + *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id); + *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch); + *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build); + *init_connect_result = le32_to_cpu(tw_initconnect->result); } retval = 0; } @@ -1162,13 +1186,12 @@ static int twa_initialize_device_extension(TW_Device_Extension *tw_dev) } /* Allocate event info space */ - tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_KERNEL); + tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL); if (!tw_dev->event_queue[0]) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x18, "Event info memory allocation failed"); goto out; } - memset(tw_dev->event_queue[0], 0, sizeof(TW_Event) * TW_Q_LENGTH); for (i = 0; i < TW_Q_LENGTH; i++) { tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event))); @@ -1192,13 +1215,12 @@ out: } /* End twa_initialize_device_extension() */ /* This function is the interrupt service routine */ -static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +static irqreturn_t twa_interrupt(int irq, void *dev_instance) { int request_id, error = 0; u32 status_reg_value; TW_Response_Queue response_que; TW_Command_Full *full_command_packet; - TW_Command *command_packet; TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; int handled = 0; @@ -1214,6 +1236,10 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *re handled = 1; + /* If we are resetting, bail */ + if (test_bit(TW_IN_RESET, &tw_dev->flags)) + goto twa_interrupt_bail; + /* Check controller for errors */ if (twa_check_bits(status_reg_value)) { if (twa_decode_bits(tw_dev, status_reg_value)) { @@ -1272,10 +1298,9 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *re request_id = TW_RESID_OUT(response_que.response_id); full_command_packet = tw_dev->command_packet_virt[request_id]; error = 0; - command_packet = &full_command_packet->command.oldcommand; /* Check for command packet errors */ if (full_command_packet->command.newcommand.status != 0) { - if (tw_dev->srb[request_id] != 0) { + if (tw_dev->srb[request_id] != NULL) { error = twa_fill_sense(tw_dev, request_id, 1, 1); } else { /* Skip ioctl error prints */ @@ -1287,7 +1312,7 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *re /* Check for correct state */ if (tw_dev->state[request_id] != TW_S_POSTED) { - if (tw_dev->srb[request_id] != 0) { + 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; @@ -1295,7 +1320,7 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *re } /* Check for internal command completion */ - if (tw_dev->srb[request_id] == 0) { + 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"); @@ -1304,22 +1329,26 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *re 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) { - tw_dev->srb[request_id]->result = (DID_OK << 16); + cmd->result = (DID_OK << 16); } /* If error, command failed */ if (error == 1) { /* Ask for a host reset */ - tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + cmd->result = (DID_OK << 16) | (CHECK_CONDITION << 1); } /* Report residual bytes for single sgl */ - if ((tw_dev->srb[request_id]->use_sg <= 1) && (full_command_packet->command.newcommand.status == 0)) { - if (full_command_packet->command.newcommand.sg_list[0].length < tw_dev->srb[request_id]->request_bufflen) - tw_dev->srb[request_id]->resid = tw_dev->srb[request_id]->request_bufflen - full_command_packet->command.newcommand.sg_list[0].length; + if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) { + if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id])) + scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length); } /* Now complete the io */ @@ -1347,32 +1376,40 @@ twa_interrupt_bail: } /* End twa_interrupt() */ /* This function will load the request id and various sgls for ioctls */ -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) +static void 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; + unsigned int pae = 0; + + if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) + pae = 1; if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { newcommand = &full_command_packet->command.newcommand; - newcommand->request_id__lunl = - TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id); - newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1; - newcommand->sg_list[0].length = length; + newcommand->request_id__lunl = + cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id)); + if (length) { + newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); + newcommand->sg_list[0].length = cpu_to_le32(length); + } newcommand->sgl_entries__lunh = - TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), 1); + cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0)); } else { oldcommand = &full_command_packet->command.oldcommand; oldcommand->request_id = request_id; if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { /* Load the sg list */ - sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset)); - sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1; - sgl->length = length; + 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) - 1); + sgl->length = cpu_to_le32(length); - if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) - oldcommand->size += 1; + oldcommand->size += pae; } } } /* End twa_load_sgl() */ @@ -1382,52 +1419,20 @@ static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id) { int use_sg; struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - int retval = 0; - - if (cmd->use_sg == 0) - goto out; - use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - - if (use_sg == 0) { + use_sg = scsi_dma_map(cmd); + if (!use_sg) + return 0; + else if (use_sg < 0) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list"); - goto out; + return 0; } cmd->SCp.phase = TW_PHASE_SGLIST; cmd->SCp.have_data_in = use_sg; - retval = use_sg; -out: - return retval; -} /* End twa_map_scsi_sg_data() */ - -/* This function will perform a pci-dma map for a single buffer */ -static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id) -{ - dma_addr_t mapping; - struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - int retval = 0; - - if (cmd->request_bufflen == 0) { - retval = 0; - goto out; - } - mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, DMA_BIDIRECTIONAL); - - if (mapping == 0) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page"); - goto out; - } - - cmd->SCp.phase = TW_PHASE_SINGLE; - cmd->SCp.have_data_in = mapping; - retval = mapping; -out: - return retval; -} /* End twa_map_scsi_single_data() */ + return use_sg; +} /* End twa_map_scsi_sg_data() */ /* This function will poll for a response interrupt of a request */ static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds) @@ -1531,6 +1536,14 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, int retval = 1; command_que_value = tw_dev->command_packet_phys[request_id]; + + /* For 9650SE write low 4 bytes first */ + if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { + command_que_value += TW_COMMAND_OFFSET; + writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev)); + } + status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); if (twa_check_bits(status_reg_value)) @@ -1557,13 +1570,18 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, TW_UNMASK_COMMAND_INTERRUPT(tw_dev); goto out; } else { - /* We successfully posted the command packet */ - 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); + 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 { - writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); + 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++; @@ -1577,7 +1595,7 @@ out: } /* End twa_post_command_packet() */ /* This function will reset a device extension */ -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) +static int twa_reset_device_extension(TW_Device_Extension *tw_dev) { int i = 0; int retval = 1; @@ -1620,14 +1638,9 @@ static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res goto out; TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); + clear_bit(TW_IN_RESET, &tw_dev->flags); + tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; - /* Wake up any ioctl that was pending before the reset */ - if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) { - clear_bit(TW_IN_RESET, &tw_dev->flags); - } else { - tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; - wake_up(&tw_dev->ioctl_wqueue); - } retval = 0; out: return retval; @@ -1736,25 +1749,35 @@ static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt) "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n", TW_DRIVER, 0x2c, SCpnt->cmnd[0]); + /* 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, 0)) { + if (twa_reset_device_extension(tw_dev)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); goto out; } retval = SUCCESS; out: + mutex_unlock(&tw_dev->ioctl_lock); return retval; } /* End twa_scsi_eh_reset() */ /* This is the main scsi queue function to handle scsi opcodes */ -static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) +static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { 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->working_srl < TW_FW_SRL_LUNS_SUPPORTED)) { + 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; @@ -1777,10 +1800,12 @@ static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd switch (retval) { case SCSI_MLQUEUE_HOST_BUSY: twa_free_request_id(tw_dev, request_id); + twa_unmap_scsi_data(tw_dev, request_id); break; case 1: tw_dev->state[request_id] = TW_S_COMPLETED; twa_free_request_id(tw_dev, request_id); + twa_unmap_scsi_data(tw_dev, request_id); SCpnt->result = (DID_ERROR << 16); done(SCpnt); retval = 0; @@ -1789,6 +1814,8 @@ out: return retval; } /* End twa_scsi_queue() */ +static DEF_SCSI_QCMD(twa_scsi_queue) + /* This function hands scsi cdb's to the firmware */ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg) { @@ -1797,15 +1824,13 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, u32 num_sectors = 0x0; int i, sg_count; struct scsi_cmnd *srb = NULL; - struct scatterlist *sglist = NULL; - u32 buffaddr = 0x0; + struct scatterlist *sglist = NULL, *sg; int retval = 1; if (tw_dev->srb[request_id]) { - if (tw_dev->srb[request_id]->request_buffer) { - sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; - } srb = tw_dev->srb[request_id]; + if (scsi_sglist(srb)) + sglist = scsi_sglist(srb); } /* Initialize command packet */ @@ -1827,10 +1852,10 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (srb) { command_packet->unit = srb->device->id; command_packet->request_id__lunl = - TW_REQ_LUN_IN(srb->device->lun, request_id); + cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id)); } else { command_packet->request_id__lunl = - TW_REQ_LUN_IN(0, request_id); + cpu_to_le16(TW_REQ_LUN_IN(0, request_id)); command_packet->unit = 0; } @@ -1838,65 +1863,44 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (!sglistarg) { /* Map sglist from scsi layer to cmd packet */ - if (tw_dev->srb[request_id]->use_sg == 0) { - if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) { - command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; - command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; - if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) - memcpy(tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen); - } else { - buffaddr = twa_map_scsi_single_data(tw_dev, request_id); - if (buffaddr == 0) - goto out; - - command_packet->sg_list[0].address = buffaddr; - command_packet->sg_list[0].length = tw_dev->srb[request_id]->request_bufflen; - } - command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), 1); - - if (command_packet->sg_list[0].address & TW_ALIGNMENT_9000_SGL) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi"); - goto out; - } - } - if (tw_dev->srb[request_id]->use_sg > 0) { - if ((tw_dev->srb[request_id]->use_sg == 1) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { - if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) { - struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; - char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; - memcpy(tw_dev->generic_buffer_virt[request_id], buf, sg->length); - kunmap_atomic(buf - sg->offset, KM_IRQ0); - } - command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; - command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; + if (scsi_sg_count(srb)) { + if ((scsi_sg_count(srb) == 1) && + (scsi_bufflen(srb) < TW_MIN_SGL_LENGTH)) { + if (srb->sc_data_direction == DMA_TO_DEVICE || + srb->sc_data_direction == DMA_BIDIRECTIONAL) + scsi_sg_copy_to_buffer(srb, + tw_dev->generic_buffer_virt[request_id], + TW_SECTOR_SIZE); + command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); + command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH); } else { sg_count = twa_map_scsi_sg_data(tw_dev, request_id); if (sg_count == 0) goto out; - for (i = 0; i < sg_count; i++) { - command_packet->sg_list[i].address = sg_dma_address(&sglist[i]); - command_packet->sg_list[i].length = sg_dma_len(&sglist[i]); - if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) { + scsi_for_each_sg(srb, sg, sg_count, i) { + command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg)); + command_packet->sg_list[i].length = cpu_to_le32(sg_dma_len(sg)); + if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi"); goto out; } } } - command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), tw_dev->srb[request_id]->use_sg); + command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id]))); } } else { /* Internal cdb post */ for (i = 0; i < use_sg; i++) { - command_packet->sg_list[i].address = sglistarg[i].address; - command_packet->sg_list[i].length = sglistarg[i].length; - if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) { + command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address); + command_packet->sg_list[i].length = cpu_to_le32(sglistarg[i].length); + if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post"); goto out; } } - command_packet->sgl_entries__lunh = TW_REQ_LUN_IN(0, use_sg); + command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg)); } if (srb) { @@ -1914,7 +1918,7 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, /* Update SG statistics */ if (srb) { - tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; + 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; } @@ -1933,19 +1937,15 @@ out: /* This function completes an execute scsi operation */ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id) { - if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH && - (tw_dev->srb[request_id]->sc_data_direction == DMA_FROM_DEVICE || - tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL)) { - if (tw_dev->srb[request_id]->use_sg == 0) { - memcpy(tw_dev->srb[request_id]->request_buffer, - tw_dev->generic_buffer_virt[request_id], - tw_dev->srb[request_id]->request_bufflen); - } - if (tw_dev->srb[request_id]->use_sg == 1) { - struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; - char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; - memcpy(buf, tw_dev->generic_buffer_virt[request_id], sg->length); - kunmap_atomic(buf - sg->offset, KM_IRQ0); + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + + if (scsi_bufflen(cmd) < TW_MIN_SGL_LENGTH && + (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() */ @@ -1956,6 +1956,9 @@ static void __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 */ @@ -1992,18 +1995,20 @@ static char *twa_string_lookup(twa_message_type *table, unsigned int code) static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id) { struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - switch(cmd->SCp.phase) { - case TW_PHASE_SINGLE: - pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL); - break; - case TW_PHASE_SGLIST: - pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - break; - } + if (cmd->SCp.phase == TW_PHASE_SGLIST) + scsi_dma_unmap(cmd); } /* End twa_unmap_scsi_data() */ +/* This function gets called when a disk is coming on-line */ +static int twa_slave_configure(struct scsi_device *sdev) +{ + /* Force 60 second timeout */ + blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + + return 0; +} /* End twa_slave_configure() */ + /* scsi_host_template initializer */ static struct scsi_host_template driver_template = { .module = THIS_MODULE, @@ -2013,21 +2018,23 @@ static struct scsi_host_template driver_template = { .bios_param = twa_scsi_biosparam, .change_queue_depth = twa_change_queue_depth, .can_queue = TW_Q_LENGTH-2, + .slave_configure = twa_slave_configure, .this_id = -1, .sg_tablesize = TW_APACHE_MAX_SGL_LENGTH, .max_sectors = TW_MAX_SECTORS, .cmd_per_lun = TW_MAX_CMDS_PER_LUN, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = twa_host_attrs, - .emulated = 1 + .emulated = 1, + .no_write_same = 1, }; /* This function will probe and initialize a card */ -static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) +static int twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) { struct Scsi_Host *host = NULL; TW_Device_Extension *tw_dev; - u32 mem_addr; + unsigned long mem_addr, mem_len; int retval = -ENODEV; retval = pci_enable_device(pdev); @@ -2037,12 +2044,16 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id } pci_set_master(pdev); - - retval = pci_set_dma_mask(pdev, sizeof(dma_addr_t) > 4 ? DMA_64BIT_MASK : DMA_32BIT_MASK); - if (retval) { - TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); - goto out_disable_device; - } + pci_try_set_mwi(pdev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); + retval = -ENODEV; + goto out_disable_device; + } host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) { @@ -2052,8 +2063,6 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id } tw_dev = (TW_Device_Extension *)host->hostdata; - memset(tw_dev, 0, sizeof(TW_Device_Extension)); - /* Save values to device extension */ tw_dev->host = host; tw_dev->tw_pci_dev = pdev; @@ -2070,13 +2079,16 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id goto out_free_device_extension; } - if (pdev->device == PCI_DEVICE_ID_3WARE_9000) + if (pdev->device == PCI_DEVICE_ID_3WARE_9000) { mem_addr = pci_resource_start(pdev, 1); - else + mem_len = pci_resource_len(pdev, 1); + } else { mem_addr = pci_resource_start(pdev, 2); + mem_len = pci_resource_len(pdev, 2); + } /* Save base address */ - tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE); + tw_dev->base_addr = ioremap(mem_addr, mem_len); if (!tw_dev->base_addr) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x35, "Failed to ioremap"); goto out_release_mem_region; @@ -2087,26 +2099,31 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id /* Initialize the card */ if (twa_reset_sequence(tw_dev, 0)) - goto out_release_mem_region; + goto out_iounmap; /* Set host specific parameters */ - host->max_id = TW_MAX_UNITS; + if ((pdev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (pdev->device == PCI_DEVICE_ID_3WARE_9690SA)) + host->max_id = TW_MAX_UNITS_9650SE; + else + host->max_id = TW_MAX_UNITS; + host->max_cmd_len = TW_MAX_CDB_LEN; /* Channels aren't supported by adapter */ - host->max_lun = TW_MAX_LUNS(tw_dev->working_srl); + host->max_lun = TW_MAX_LUNS(tw_dev->tw_compat_info.working_srl); host->max_channel = 0; /* Register the card with the kernel SCSI layer */ retval = scsi_add_host(host, &pdev->dev); if (retval) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed"); - goto out_release_mem_region; + goto out_iounmap; } pci_set_drvdata(pdev, host); - printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", + printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%lx, IRQ: %d.\n", host->host_no, mem_addr, pdev->irq); printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n", host->host_no, @@ -2114,11 +2131,16 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH), (char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE, TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH), - *(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE, - TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH)); + le32_to_cpu(*(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE, + TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH))); + + /* Try to enable MSI */ + if (use_msi && (pdev->device != PCI_DEVICE_ID_3WARE_9000) && + !pci_enable_msi(pdev)) + set_bit(TW_USING_MSI, &tw_dev->flags); /* Now setup the interrupt handler */ - retval = request_irq(pdev->irq, twa_interrupt, SA_SHIRQ, "3w-9xxx", tw_dev); + retval = request_irq(pdev->irq, twa_interrupt, IRQF_SHARED, "3w-9xxx", tw_dev); if (retval) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x30, "Error requesting IRQ"); goto out_remove_host; @@ -2140,7 +2162,11 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id return 0; out_remove_host: + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); scsi_remove_host(host); +out_iounmap: + iounmap(tw_dev->base_addr); out_release_mem_region: pci_release_regions(pdev); out_free_device_extension: @@ -2166,12 +2192,16 @@ static void twa_remove(struct pci_dev *pdev) twa_major = -1; } - /* Free up the IRQ */ - free_irq(tw_dev->tw_pci_dev->irq, tw_dev); - /* Shutdown the card */ __twa_shutdown(tw_dev); + /* Disable MSI if enabled */ + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); + + /* Free IO remapping */ + iounmap(tw_dev->base_addr); + /* Free up the mem region */ pci_release_regions(pdev); @@ -2183,12 +2213,108 @@ static void twa_remove(struct pci_dev *pdev) twa_device_extension_count--; } /* End twa_remove() */ +#ifdef CONFIG_PM +/* This function is called on PCI suspend */ +static int twa_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + + printk(KERN_WARNING "3w-9xxx: Suspending host %d.\n", tw_dev->host->host_no); + + TW_DISABLE_INTERRUPTS(tw_dev); + free_irq(tw_dev->tw_pci_dev->irq, tw_dev); + + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); + + /* 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, 0x38, "Connection shutdown failed during suspend"); + } else { + printk(KERN_WARNING "3w-9xxx: Suspend complete.\n"); + } + TW_CLEAR_ALL_INTERRUPTS(tw_dev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} /* End twa_suspend() */ + +/* This function is called on PCI resume */ +static int twa_resume(struct pci_dev *pdev) +{ + int retval = 0; + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + + printk(KERN_WARNING "3w-9xxx: Resuming host %d.\n", tw_dev->host->host_no); + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + + retval = pci_enable_device(pdev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x39, "Enable device failed during resume"); + return retval; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + TW_PRINTK(host, TW_DRIVER, 0x40, "Failed to set dma mask during resume"); + retval = -ENODEV; + goto out_disable_device; + } + + /* Initialize the card */ + if (twa_reset_sequence(tw_dev, 0)) { + retval = -ENODEV; + goto out_disable_device; + } + + /* Now setup the interrupt handler */ + retval = request_irq(pdev->irq, twa_interrupt, IRQF_SHARED, "3w-9xxx", tw_dev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x42, "Error requesting IRQ during resume"); + retval = -ENODEV; + goto out_disable_device; + } + + /* Now enable MSI if enabled */ + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_enable_msi(pdev); + + /* Re-enable interrupts on the card */ + TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); + + printk(KERN_WARNING "3w-9xxx: Resume complete.\n"); + return 0; + +out_disable_device: + scsi_remove_host(host); + pci_disable_device(pdev); + + return retval; +} /* End twa_resume() */ +#endif + /* PCI Devices supported by this driver */ -static struct pci_device_id twa_pci_tbl[] __devinitdata = { +static struct pci_device_id twa_pci_tbl[] = { { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9650SE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9690SA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { } }; MODULE_DEVICE_TABLE(pci, twa_pci_tbl); @@ -2199,6 +2325,10 @@ static struct pci_driver twa_driver = { .id_table = twa_pci_tbl, .probe = twa_probe, .remove = twa_remove, +#ifdef CONFIG_PM + .suspend = twa_suspend, + .resume = twa_resume, +#endif .shutdown = twa_shutdown }; @@ -2207,7 +2337,7 @@ static int __init twa_init(void) { printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); - return pci_module_init(&twa_driver); + return pci_register_driver(&twa_driver); } /* End twa_init() */ /* This function is called on driver exit */ |
