Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 40047 Details for
Bug 64814
please include the 3w-9xxx driver with 2.6.7
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
3w-9xxx patch against vanilla linux 2.6.6
3w-9xxx-linux-2.6.6.patch (text/plain), 103.44 KB, created by
Joshua Hoblitt
on 2004-09-20 19:04:18 UTC
(
hide
)
Description:
3w-9xxx patch against vanilla linux 2.6.6
Filename:
MIME Type:
Creator:
Joshua Hoblitt
Created:
2004-09-20 19:04:18 UTC
Size:
103.44 KB
patch
obsolete
>diff -Naupr linux-2.6.6/drivers/scsi/3w-9xxx.c linux-2.6.6-ss1/drivers/scsi/3w-9xxx.c >--- linux-2.6.6/drivers/scsi/3w-9xxx.c 1969-12-31 18:00:00.000000000 -0600 >+++ linux-2.6.6-ss1/drivers/scsi/3w-9xxx.c 2004-05-04 13:38:34.000000000 -0500 >@@ -0,0 +1,2131 @@ >+/* >+ 3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux. >+ >+ Written By: Adam Radford <linuxraid@amcc.com> >+ >+ Copyright (C) 2004 Applied Micro Circuits 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 >+ 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: >+ linuxraid@amcc.com >+ >+ For more information, goto: >+ http://www.amcc.com >+ >+ Note: This version of the driver does not contain a bundled firmware >+ image. >+ >+ History >+ ------- >+ 2.26.00.005 - Initial release. >+ 2.26.00.006 - Remove TW_WRITEL macro, replace with writeq(). >+ 2.26.00.007 - Skip lun and channel probes. >+*/ >+ >+#include <linux/module.h> >+ >+MODULE_AUTHOR ("AMCC"); >+#ifdef CONFIG_SMP >+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver (SMP)"); >+#else >+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver"); >+#endif >+MODULE_LICENSE("GPL"); >+ >+#include <linux/kernel.h> >+#include <linux/pci.h> >+#include <linux/time.h> >+#include <linux/proc_fs.h> >+#include <linux/sched.h> >+#include <linux/ioport.h> >+#include <linux/blkdev.h> >+#include <linux/hdreg.h> >+#include <linux/string.h> >+#include <linux/delay.h> >+#include <linux/smp.h> >+#include <linux/reboot.h> >+#include <linux/spinlock.h> >+#include <linux/interrupt.h> >+#include <linux/moduleparam.h> >+ >+#include <asm/errno.h> >+#include <asm/io.h> >+#include <asm/irq.h> >+#include <asm/uaccess.h> >+ >+#include "scsi.h" >+#include "hosts.h" >+ >+#include "3w-9xxx.h" >+ >+/* Notifier block to get a notify on system shutdown/halt/reboot */ >+static struct notifier_block twa_notifier = { >+ .notifier_call = twa_halt, >+ .next = NULL, >+ .priority = 0 >+}; >+ >+/* File operations struct for character device */ >+static struct file_operations twa_fops = { >+ .owner = THIS_MODULE, >+ .ioctl = twa_chrdev_ioctl, >+ .open = twa_chrdev_open, >+ .release = twa_chrdev_release >+}; >+ >+/* Globals */ >+char *twa_driver_version="2.26.00.007"; >+TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; >+unsigned int twa_device_extension_count = 0; >+static int twa_major = -1; >+extern struct timezone sys_tz; >+static int cmds_per_lun; >+ >+/* Module parameters */ >+module_param(cmds_per_lun, int, 0); >+MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN"); >+ >+/* Functions */ >+ >+/* This function will complete an aen request from the isr */ >+static int 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; >+ unsigned short aen; >+ int retval = 1; >+ >+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; >+ tw_dev->posted_request_count--; >+ aen = header->status_block.error; >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ command_packet = &full_command_packet->command.oldcommand; >+ >+ /* First check for internal completion of set param for time sync */ >+ if (command_packet->byte0_offset.opcode == 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); >+ TW_CLEAR_ATTENTION_INTERRUPT(tw_dev); >+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); >+out: >+ return retval; >+} /* End twa_aen_complete() */ >+ >+/* This function will drain aen queue */ >+static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset) >+{ >+ int request_id = 0; >+ char cdb[TW_MAX_CDB_LEN]; >+ TW_SG_Apache sglist[1]; >+ int finished = 0, count = 0; >+ TW_Command_Full *full_command_packet; >+ TW_Command_Apache_Header *header; >+ unsigned short aen; >+ int first_reset = 0, queue = 0, retval = 1; >+ >+ if (no_check_reset) >+ first_reset = 0; >+ else >+ first_reset = 1; >+ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ memset(full_command_packet, 0, sizeof(TW_Command_Full)); >+ >+ /* Initialize cdb */ >+ memset(&cdb, 0, TW_MAX_CDB_LEN); >+ cdb[0] = REQUEST_SENSE; /* opcode */ >+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ >+ >+ /* Initialize sglist */ >+ memset(&sglist, 0, sizeof(TW_SG_Apache)); >+ sglist[0].length = TW_SECTOR_SIZE; >+ sglist[0].address = tw_dev->generic_buffer_phys[request_id]; >+ >+ if (sglist[0].address & 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; >+ } >+ >+ tw_dev->posted_request_count--; >+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; >+ aen = header->status_block.error; >+ queue = 0; >+ count++; >+ >+ switch (aen) { >+ case TW_AEN_QUEUE_EMPTY: >+ if (first_reset != 1) >+ goto out; >+ else >+ finished = 1; >+ break; >+ case TW_AEN_SOFT_RESET: >+ if (first_reset == 0) >+ first_reset = 1; >+ else >+ queue = 1; >+ break; >+ case TW_AEN_SYNC_TIME_WITH_HOST: >+ break; >+ default: >+ queue = 1; >+ } >+ >+ /* Now queue an event info */ >+ if (queue) >+ twa_aen_queue_event(tw_dev, header); >+ } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN)); >+ >+ if (count == TW_MAX_AEN_DRAIN) >+ goto out; >+ >+ retval = 0; >+out: >+ tw_dev->state[request_id] = TW_S_INITIAL; >+ return retval; >+} /* End twa_aen_drain_queue() */ >+ >+/* This function will queue an event */ >+static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header) >+{ >+ u32 local_time; >+ struct timeval time; >+ TW_Event *event; >+ unsigned short aen; >+ char host[16]; >+ >+ tw_dev->aen_count++; >+ >+ /* Fill out event info */ >+ event = tw_dev->event_queue[tw_dev->error_index]; >+ >+ /* Check for clobber */ >+ host[0] = '\0'; >+ if (tw_dev->host) { >+ sprintf(host, " scsi%d:", tw_dev->host->host_no); >+ if (event->retrieved == TW_AEN_NOT_RETRIEVED) >+ tw_dev->aen_clobber = 1; >+ } >+ >+ aen = header->status_block.error; >+ memset(event, 0, sizeof(TW_Event)); >+ >+ event->severity = header->status_block.substatus_block.severity; >+ do_gettimeofday(&time); >+ local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); >+ event->time_stamp_sec = local_time; >+ event->aen_code = aen; >+ event->retrieved = TW_AEN_NOT_RETRIEVED; >+ event->sequence_id = tw_dev->error_sequence_id; >+ tw_dev->error_sequence_id++; >+ >+ 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); >+ if (event->severity != TW_AEN_SEVERITY_DEBUG) >+ printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", host, twa_aen_severity_lookup(header->status_block.substatus_block.severity), TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, twa_string_lookup(twa_aen_table, aen), header->err_specific_desc); >+ else >+ tw_dev->aen_count--; >+ >+ if ((tw_dev->error_index + 1) == TW_Q_LENGTH) >+ tw_dev->event_queue_wrapped = 1; >+ tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH; >+} /* End twa_aen_queue_event() */ >+ >+/* This function will read the aen queue from the isr */ >+static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) >+{ >+ char cdb[TW_MAX_CDB_LEN]; >+ TW_SG_Apache sglist[1]; >+ TW_Command_Full *full_command_packet; >+ int retval = 1; >+ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ memset(full_command_packet, 0, sizeof(TW_Command_Full)); >+ >+ /* Initialize cdb */ >+ memset(&cdb, 0, TW_MAX_CDB_LEN); >+ cdb[0] = REQUEST_SENSE; /* opcode */ >+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ >+ >+ /* Initialize sglist */ >+ memset(&sglist, 0, sizeof(TW_SG_Apache)); >+ sglist[0].length = TW_SECTOR_SIZE; >+ sglist[0].address = tw_dev->generic_buffer_phys[request_id]; >+ >+ /* Mark internal command */ >+ tw_dev->srb[request_id] = NULL; >+ >+ /* Now post the command packet */ >+ if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "Post failed while reading AEN queue"); >+ goto out; >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_aen_read_queue() */ >+ >+/* This function will look up an AEN severity string */ >+static char *twa_aen_severity_lookup(unsigned char severity_code) >+{ >+ char *retval = NULL; >+ >+ if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) || >+ (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG)) >+ goto out; >+ >+ retval = twa_aen_severity_table[severity_code]; >+out: >+ return retval; >+} /* End twa_aen_severity_lookup() */ >+ >+/* This function will sync firmware time with the host time */ >+static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id) >+{ >+ u32 schedulertime; >+ struct timeval utc; >+ TW_Command_Full *full_command_packet; >+ TW_Command *command_packet; >+ TW_Param_Apache *param; >+ u32 local_time; >+ >+ /* Fill out the command packet */ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ memset(full_command_packet, 0, sizeof(TW_Command_Full)); >+ command_packet = &full_command_packet->command.oldcommand; >+ command_packet->byte0_offset.opcode = TW_OP_SET_PARAM; >+ command_packet->byte0_offset.sgl_offset = 2; >+ 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->size = TW_COMMAND_SIZE; >+ command_packet->byte6_offset.parameter_count = 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; >+ >+ /* 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; >+ >+ memcpy(param->data, &schedulertime, sizeof(u32)); >+ >+ /* Mark internal command */ >+ tw_dev->srb[request_id] = NULL; >+ >+ /* Now post the command */ >+ twa_post_command_packet(tw_dev, request_id); >+} /* End twa_aen_sync_time() */ >+ >+/* This function will allocate memory and check if it is correctly aligned */ >+static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) >+{ >+ int i; >+ dma_addr_t dma_handle; >+ unsigned long *cpu_addr; >+ int retval = 1; >+ >+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle); >+ if (!cpu_addr) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed"); >+ goto out; >+ } >+ >+ if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory"); >+ pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle); >+ goto out; >+ } >+ >+ memset(cpu_addr, 0, size*TW_Q_LENGTH); >+ >+ for (i = 0; i < TW_Q_LENGTH; i++) { >+ switch(which) { >+ case 0: >+ tw_dev->command_packet_phys[i] = dma_handle+(i*size); >+ tw_dev->command_packet_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); >+ break; >+ case 1: >+ tw_dev->generic_buffer_phys[i] = dma_handle+(i*size); >+ tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); >+ break; >+ } >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_allocate_memory() */ >+ >+/* This function will check the status register for unexpected bits */ >+static int twa_check_bits(u32 status_reg_value) >+{ >+ int retval = 1; >+ >+ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) >+ goto out; >+ if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) >+ goto out; >+ >+ retval = 0; >+out: >+ return retval; >+} /* End twa_check_bits() */ >+ >+/* This function will check the srl and decide if we are compatible */ >+static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed) >+{ >+ int retval = 1; >+ unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; >+ unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0; >+ u32 init_connect_result = 0; >+ >+ if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_CURRENT_FW_SRL, TW_9000_ARCH_ID, TW_CURRENT_FW_BRANCH, TW_CURRENT_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "Initconnection failed while checking SRL"); >+ goto out; >+ } >+ >+ tw_dev->working_srl = TW_CURRENT_FW_SRL; >+ tw_dev->working_branch = TW_CURRENT_FW_BRANCH; >+ tw_dev->working_build = TW_CURRENT_FW_BUILD; >+ >+ /* Try base mode compatibility */ >+ if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) { >+ if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_BASE_FW_SRL, TW_9000_ARCH_ID, TW_BASE_FW_BRANCH, TW_BASE_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Initconnection (base mode) failed while checking SRL"); >+ goto out; >+ } >+ if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) { >+ if (TW_CURRENT_FW_SRL > fw_on_ctlr_srl) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x32, "Firmware and driver incompatibility: please upgrade firmware"); >+ } else { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x33, "Firmware and driver incompatibility: please upgrade driver"); >+ } >+ 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; >+ } >+ 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) >+{ >+ long timeout; >+ unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0; >+ dma_addr_t dma_handle; >+ int request_id = 0; >+ unsigned int sequence_id = 0; >+ unsigned char event_index, start_index; >+ TW_Ioctl_Driver_Command driver_command; >+ TW_Ioctl_Buf_Apache *tw_ioctl; >+ TW_Lock *tw_lock; >+ TW_Command_Full *full_command_packet; >+ TW_Compatibility_Info *tw_compat_info; >+ TW_Event *event; >+ struct timeval current_time; >+ u32 current_time_ms; >+ TW_Device_Extension *tw_dev = twa_device_extension_list[MINOR(inode->i_rdev)]; >+ int retval = TW_IOCTL_ERROR_OS_EFAULT; >+ >+ /* Only let one of these through at a time */ >+ if (down_interruptible(&tw_dev->ioctl_sem)) { >+ retval = TW_IOCTL_ERROR_OS_EINTR; >+ goto out; >+ } >+ >+ /* First copy down the driver command */ >+ if (copy_from_user(&driver_command, (void *)arg, sizeof(TW_Ioctl_Driver_Command))) >+ goto out2; >+ >+ /* Check data buffer size */ >+ if (driver_command.buffer_length > TW_MAX_SECTORS * 512) { >+ retval = TW_IOCTL_ERROR_OS_EINVAL; >+ goto out2; >+ } >+ >+ /* Hardware can only do multiple of 512 byte transfers */ >+ data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511; >+ >+ /* Now allocate ioctl buf memory */ >+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle); >+ if (!cpu_addr) { >+ retval = TW_IOCTL_ERROR_OS_ENOMEM; >+ goto out2; >+ } >+ >+ tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr; >+ >+ /* Now copy down the entire ioctl */ >+ if (copy_from_user(tw_ioctl, (void *)arg, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1)) >+ 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] = 0; >+ >+ /* Flag chrdev ioctl */ >+ tw_dev->chrdev_request_id = request_id; >+ >+ 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); >+ >+ memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); >+ >+ /* Now post the command packet to the controller */ >+ twa_post_command_packet(tw_dev, request_id); >+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags); >+ >+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ; >+ >+ /* Now wait for command to complete */ >+ timeout = wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); >+ >+ /* Check if we timed out, got a signal, or didn't get >+ an interrupt */ >+ if ((timeout <= 0) && (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)) { >+ /* Now we need to reset the board */ >+ if (timeout == TW_IOCTL_ERROR_OS_ERESTARTSYS) { >+ retval = timeout; >+ } else { >+ 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, 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--; >+ twa_reset_device_extension(tw_dev); >+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags); >+ 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 complete the io */ >+ spin_lock_irqsave(tw_dev->host->host_lock, flags); >+ tw_dev->posted_request_count--; >+ tw_dev->state[request_id] = TW_S_COMPLETED; >+ twa_free_request_id(tw_dev, request_id); >+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags); >+ break; >+ case TW_IOCTL_GET_COMPATIBILITY_INFO: >+ tw_ioctl->driver_command.status = 0; >+ /* Copy compatiblity struct into ioctl data buffer */ >+ tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; >+ strncpy(tw_compat_info->driver_version, twa_driver_version, strlen(twa_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; >+ break; >+ case TW_IOCTL_GET_LAST_EVENT: >+ if (tw_dev->event_queue_wrapped) { >+ if (tw_dev->aen_clobber) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER; >+ tw_dev->aen_clobber = 0; >+ } else >+ tw_ioctl->driver_command.status = 0; >+ } else { >+ if (!tw_dev->error_index) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ tw_ioctl->driver_command.status = 0; >+ } >+ event_index = (tw_dev->error_index - 1 + TW_Q_LENGTH) % TW_Q_LENGTH; >+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event)); >+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED; >+ break; >+ case TW_IOCTL_GET_FIRST_EVENT: >+ if (tw_dev->event_queue_wrapped) { >+ if (tw_dev->aen_clobber) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER; >+ tw_dev->aen_clobber = 0; >+ } else >+ tw_ioctl->driver_command.status = 0; >+ event_index = tw_dev->error_index; >+ } else { >+ if (!tw_dev->error_index) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ tw_ioctl->driver_command.status = 0; >+ event_index = 0; >+ } >+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event)); >+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED; >+ break; >+ case TW_IOCTL_GET_NEXT_EVENT: >+ event = (TW_Event *)tw_ioctl->data_buffer; >+ sequence_id = event->sequence_id; >+ tw_ioctl->driver_command.status = 0; >+ >+ if (tw_dev->event_queue_wrapped) { >+ if (tw_dev->aen_clobber) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER; >+ tw_dev->aen_clobber = 0; >+ } >+ start_index = tw_dev->error_index; >+ } else { >+ if (!tw_dev->error_index) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ start_index = 0; >+ } >+ event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id + 1) % TW_Q_LENGTH; >+ >+ if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) { >+ if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER) >+ tw_dev->aen_clobber = 1; >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event)); >+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED; >+ break; >+ case TW_IOCTL_GET_PREVIOUS_EVENT: >+ event = (TW_Event *)tw_ioctl->data_buffer; >+ sequence_id = event->sequence_id; >+ tw_ioctl->driver_command.status = 0; >+ >+ if (tw_dev->event_queue_wrapped) { >+ if (tw_dev->aen_clobber) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER; >+ tw_dev->aen_clobber = 0; >+ } >+ start_index = tw_dev->error_index; >+ } else { >+ if (!tw_dev->error_index) { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ start_index = 0; >+ } >+ event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id - 1) % TW_Q_LENGTH; >+ >+ if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) { >+ if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER) >+ tw_dev->aen_clobber = 1; >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS; >+ break; >+ } >+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event)); >+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED; >+ break; >+ case TW_IOCTL_GET_LOCK: >+ tw_lock = (TW_Lock *)tw_ioctl->data_buffer; >+ do_gettimeofday(¤t_time); >+ current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000); >+ >+ if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) || (current_time_ms >= tw_dev->ioctl_msec)) { >+ tw_dev->ioctl_sem_lock = 1; >+ tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec; >+ tw_ioctl->driver_command.status = 0; >+ tw_lock->time_remaining_msec = tw_lock->timeout_msec; >+ } else { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_LOCKED; >+ tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms; >+ } >+ break; >+ case TW_IOCTL_RELEASE_LOCK: >+ if (tw_dev->ioctl_sem_lock == 1) { >+ tw_dev->ioctl_sem_lock = 0; >+ tw_ioctl->driver_command.status = 0; >+ } else { >+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NOT_LOCKED; >+ } >+ break; >+ default: >+ retval = TW_IOCTL_ERROR_OS_ENOTTY; >+ goto out3; >+ } >+ >+ /* Now copy the entire response to userspace */ >+ if (copy_to_user((void *)arg, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0) >+ retval = 0; >+out3: >+ /* Now free ioctl buf memory */ >+ pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle); >+out2: >+ up(&tw_dev->ioctl_sem); >+out: >+ return retval; >+} /* End twa_chrdev_ioctl() */ >+ >+/* This function handles open for the character device */ >+static int twa_chrdev_open(struct inode *inode, struct file *file) >+{ >+ unsigned int minor_number; >+ int retval = TW_IOCTL_ERROR_OS_ENODEV; >+ >+ minor_number = MINOR(inode->i_rdev); >+ if (minor_number >= twa_device_extension_count) >+ goto out; >+ retval = 0; >+out: >+ return retval; >+} /* End twa_chrdev_open() */ >+ >+/* This function handles close for the character device */ >+static int twa_chrdev_release(struct inode *inode, struct file *file) >+{ >+ return 0; >+} /* End twa_chrdev_release() */ >+ >+/* This function is called by twa_scsi_proc_info */ >+static int twa_copy_info(TW_Info *info, char *fmt, ...) >+{ >+ va_list args; >+ char buf[81]; >+ int len; >+ >+ va_start(args, fmt); >+ len = vsprintf(buf, fmt, args); >+ va_end(args); >+ twa_copy_mem_info(info, buf, len); >+ >+ return len; >+} /* End twa_copy_info() */ >+ >+/* This function is called by twa_scsi_proc_info */ >+static void twa_copy_mem_info(TW_Info *info, char *data, int len) >+{ >+ if (info->position + len > info->length) >+ len = info->length - info->position; >+ >+ if (info->position + len < info->offset) { >+ info->position += len; >+ return; >+ } >+ if (info->position < info->offset) { >+ data += (info->offset - info->position); >+ len -= (info->offset - info->position); >+ } >+ if (len > 0) { >+ memcpy(info->buffer + info->position, data, len); >+ info->position += len; >+ } >+} /* End twa_copy_mem_info() */ >+ >+/* This function will print readable messages from status register errors */ >+static int 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)); >+ } >+ >+ if (status_reg_value & TW_STATUS_PCI_ABORT) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "PCI Abort: clearing"); >+ writel(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev)); >+ pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT); >+ } >+ >+ if (status_reg_value & TW_STATUS_QUEUE_ERROR) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); >+ writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); >+ } >+ >+ if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "SBUF Write Error: clearing"); >+ writel(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); >+ } >+ >+ if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) { >+ if (tw_dev->reset_print == 0) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Microcontroller Error: clearing"); >+ tw_dev->reset_print = 1; >+ } >+ goto out; >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_decode_bits() */ >+ >+/* This function will empty the response queue */ >+static int twa_empty_response_queue(TW_Device_Extension *tw_dev) >+{ >+ u32 status_reg_value, response_que_value; >+ int count = 0, retval = 1; >+ >+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); >+ >+ while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) { >+ response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev)); >+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); >+ count++; >+ } >+ if (count == TW_MAX_RESPONSE_DRAIN) >+ goto out; >+ >+ retval = 0; >+out: >+ return retval; >+} /* End twa_empty_response_queue() */ >+ >+/* This function passes sense keys from firmware to scsi layer */ >+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host) >+{ >+ TW_Command_Full *full_command_packet; >+ unsigned short error; >+ int retval = 1; >+ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ /* Don't print error for Logical unit not supported during rollcall */ >+ error = 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", tw_dev->host->host_no, TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); >+ else >+ printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n", TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); >+ } >+ >+ if (copy_sense) { >+ memcpy(tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data, TW_SENSE_DATA_LENGTH); >+ tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1); >+ retval = TW_ISR_DONT_RESULT; >+ goto out; >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_fill_sense() */ >+ >+/* This function will free up device extension resources */ >+static void twa_free_device_extension(TW_Device_Extension *tw_dev) >+{ >+ if (tw_dev->command_packet_virt[0]) >+ pci_free_consistent(tw_dev->tw_pci_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]) >+ pci_free_consistent(tw_dev->tw_pci_dev, TW_SECTOR_SIZE*TW_Q_LENGTH, tw_dev->generic_buffer_virt[0], tw_dev->generic_buffer_phys[0]); >+ >+ if (tw_dev->event_queue[0]) >+ kfree(tw_dev->event_queue[0]); >+} /* End twa_free_device_extension() */ >+ >+/* This function will free a request id */ >+static void 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 entires 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 */ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ memset(full_command_packet, 0, sizeof(TW_Command_Full)); >+ command_packet = &full_command_packet->command.oldcommand; >+ >+ command_packet->byte0_offset.opcode = TW_OP_GET_PARAM; >+ command_packet->byte0_offset.sgl_offset = 2; >+ command_packet->size = TW_COMMAND_SIZE; >+ command_packet->request_id = request_id; >+ command_packet->byte6_offset.block_count = 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]; >+ >+ command_packet->byte8_offset.param.sgl[0].address = param_value; >+ command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE; >+ >+ /* Post the command packet to the board */ >+ twa_post_command_packet(tw_dev, request_id); >+ >+ /* 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]); >+ >+ tw_dev->posted_request_count--; >+ tw_dev->state[request_id] = TW_S_INITIAL; >+ >+ return retval; >+} /* End twa_get_param() */ >+ >+/* This function will assign an available request id */ >+static void 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() */ >+ >+/* Clean shutdown routine */ >+static int twa_halt(struct notifier_block *nb, ulong event, void *buf) >+{ >+ unsigned int i; >+ >+ for (i = 0; i < twa_device_extension_count; i++) { >+ if (twa_device_extension_list[i]->online == 1) { >+ printk(KERN_WARNING "3w-9xxx: Shutting down card %d.\n", i); >+ twa_shutdown_device(twa_device_extension_list[i]); >+ twa_device_extension_list[i]->online = 0; >+ } >+ } >+ unregister_reboot_notifier(&twa_notifier); >+ >+ return NOTIFY_OK; >+} /* End twa_halt() */ >+ >+/* This function will send an initconnection command to controller */ >+static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, >+ u32 set_features, unsigned short current_fw_srl, >+ unsigned short current_fw_arch_id, >+ unsigned short current_fw_branch, >+ unsigned short current_fw_build, >+ unsigned short *fw_on_ctlr_srl, >+ unsigned short *fw_on_ctlr_arch_id, >+ unsigned short *fw_on_ctlr_branch, >+ unsigned short *fw_on_ctlr_build, >+ u32 *init_connect_result) >+{ >+ TW_Command_Full *full_command_packet; >+ TW_Initconnect *tw_initconnect; >+ int request_id = 0, retval = 1; >+ >+ /* Initialize InitConnection command packet */ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ memset(full_command_packet, 0, sizeof(TW_Command_Full)); >+ full_command_packet->header.header_desc.size_header = 128; >+ >+ tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; >+ tw_initconnect->opcode = TW_OP_INIT_CONNECTION; >+ >+ tw_initconnect->request_id = request_id; >+ tw_initconnect->message_credits = message_credits; >+ tw_initconnect->features = set_features; >+#if BITS_PER_LONG > 32 >+ /* Turn on 64-bit sgl support */ >+ tw_initconnect->features |= 1; >+#endif >+ >+ 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; >+ } else >+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE; >+ >+ /* Send command packet to the board */ >+ twa_post_command_packet(tw_dev, request_id); >+ >+ /* Poll for completion */ >+ if (twa_poll_response(tw_dev, request_id, 30)) { >+ 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; >+ } >+ retval = 0; >+ } >+ >+ tw_dev->posted_request_count--; >+ tw_dev->state[request_id] = TW_S_INITIAL; >+ >+ return retval; >+} /* End twa_initconnection() */ >+ >+/* This function will initialize the fields of a device extension */ >+static int twa_initialize_device_extension(TW_Device_Extension *tw_dev) >+{ >+ int i, retval = 1; >+ >+ /* Initialize command packet buffers */ >+ if (twa_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Command packet memory allocation failed"); >+ goto out; >+ } >+ >+ /* Initialize generic buffer */ >+ if (twa_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x17, "Generic memory allocation failed"); >+ goto out; >+ } >+ >+ /* Allocate event info space */ >+ tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_ATOMIC); >+ 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))); >+ tw_dev->free_queue[i] = i; >+ tw_dev->state[i] = TW_S_INITIAL; >+ } >+ >+ tw_dev->pending_head = TW_Q_START; >+ tw_dev->pending_tail = TW_Q_START; >+ tw_dev->free_head = TW_Q_START; >+ tw_dev->free_tail = TW_Q_START; >+ tw_dev->error_sequence_id = 1; >+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; >+ retval = 0; >+out: >+ return retval; >+} /* 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) >+{ >+ int request_id, error = 0; >+ u32 status_reg_value; >+ TW_Response_Queue response_que; >+ unsigned long flags = 0; >+ TW_Command_Full *full_command_packet; >+ TW_Command *command_packet; >+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; >+ int handled = 0; >+ >+ /* See if we are already running on another processor */ >+ if (test_and_set_bit(TW_IN_INTR, &tw_dev->flags)) >+ return IRQ_NONE; >+ >+ /* Get the per adapter lock */ >+ spin_lock_irqsave(tw_dev->host->host_lock, flags); >+ >+ /* See if the interrupt matches this instance */ >+ if (tw_dev->tw_pci_dev->irq == (unsigned int)irq) { >+ >+ handled = 1; >+ >+ /* Read the registers */ >+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); >+ >+ /* Check if this is our interrupt, otherwise bail */ >+ if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT)) >+ goto twa_interrupt_bail; >+ >+ /* Check controller for errors */ >+ 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; >+ } >+ } >+ >+ /* Handle host interrupt */ >+ if (status_reg_value & TW_STATUS_HOST_INTERRUPT) >+ TW_CLEAR_HOST_INTERRUPT(tw_dev); >+ >+ /* Handle attention interrupt */ >+ if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) { >+ if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) { >+ twa_get_request_id(tw_dev, &request_id); >+ >+ error = twa_aen_read_queue(tw_dev, request_id); >+ if (error) { >+ tw_dev->state[request_id] = TW_S_COMPLETED; >+ twa_free_request_id(tw_dev, request_id); >+ TW_CLEAR_ATTENTION_INTERRUPT(tw_dev); >+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); >+ } >+ } >+ } >+ >+ /* Handle command interrupt */ >+ if ((status_reg_value & TW_STATUS_COMMAND_INTERRUPT) || !(status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) { >+ /* 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)==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; >+ } >+ } >+ /* If there are no more pending requests, we mask command interrupt */ >+ if (tw_dev->pending_request_count == 0) >+ TW_MASK_COMMAND_INTERRUPT(tw_dev); >+ } >+ >+ /* 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 = response_que.u.response_id; >+ full_command_packet = (TW_Command_Full *)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) { >+ 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] != 0) { >+ 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] == 0) { >+ 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 { >+ 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); >+ } >+ >+ /* If error, command failed */ >+ if (error == 1) { >+ /* Ask for a host reset */ >+ tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1); >+ } >+ >+ /* Now complete the io */ >+ tw_dev->state[request_id] = TW_S_COMPLETED; >+ twa_free_request_id(tw_dev, request_id); >+ tw_dev->posted_request_count--; >+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); >+ twa_unmap_scsi_data(tw_dev, request_id); >+ } >+ >+ /* 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_irqrestore(tw_dev->host->host_lock, flags); >+ clear_bit(TW_IN_INTR, &tw_dev->flags); >+ return IRQ_RETVAL(handled); >+} /* 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) >+{ >+ TW_Command *oldcommand; >+ TW_Command_Apache *newcommand; >+ TW_SG_Entry *sgl; >+ >+ if (full_command_packet->command.newcommand.command.opcode == TW_OP_EXECUTE_SCSI) { >+ newcommand = &full_command_packet->command.newcommand; >+ newcommand->request_id = request_id; >+ newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1; >+ newcommand->sg_list[0].length = length; >+ } else { >+ oldcommand = &full_command_packet->command.oldcommand; >+ oldcommand->request_id = request_id; >+ >+ if (oldcommand->byte0_offset.sgl_offset) { >+ /* Load the sg list */ >+ sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->byte0_offset.sgl_offset); >+ sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1; >+ sgl->length = length; >+ } >+ } >+} /* End twa_load_sgl() */ >+ >+/* This function will perform a pci-dma mapping for a scatter gather list */ >+static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id) >+{ >+ int use_sg; >+ Scsi_Cmnd *cmd = tw_dev->srb[request_id]; >+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); >+ 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_dir); >+ >+ if (use_sg == 0) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list"); >+ goto out; >+ } >+ >+ cmd->SCp.phase = 2; >+ 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; >+ Scsi_Cmnd *cmd = tw_dev->srb[request_id]; >+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); >+ 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_dir); >+ >+ if (mapping == 0) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page"); >+ goto out; >+ } >+ >+ cmd->SCp.phase = 1; >+ cmd->SCp.have_data_in = mapping; >+ retval = mapping; >+out: >+ return retval; >+} /* End twa_map_scsi_single_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) >+{ >+ int retval = 1, found = 0, response_request_id; >+ TW_Response_Queue response_queue; >+ TW_Command_Full *full_command_packet = (TW_Command_Full *)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 = (unsigned char)response_queue.u.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 (full_command_packet->command.newcommand.command.opcode == 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 */ >+static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds) >+{ >+ u32 status_reg_value; >+ unsigned long 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; >+ >+ mdelay(5); >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_poll_status() */ >+ >+/* This function will poll the status register for disappearance of a flag */ >+static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds) >+{ >+ u32 status_reg_value; >+ unsigned long 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; >+ >+ mdelay(5); >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_poll_status_gone() */ >+ >+/* This function will attempt to post a command packet to the board */ >+static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id) >+{ >+ u32 status_reg_value; >+ unsigned long command_que_value; >+ int retval = 1; >+ >+ command_que_value = tw_dev->command_packet_phys[request_id]; >+ 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 (((tw_dev->pending_request_count > 0) && (tw_dev->state[request_id] != TW_S_PENDING)) || (status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) { >+ /* 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 { >+ /* We successfully posted the command packet */ >+#if BITS_PER_LONG > 32 >+ writeq(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); >+#else >+ writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); >+#endif >+ 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 */ >+static int twa_reset_device_extension(TW_Device_Extension *tw_dev) >+{ >+ int i = 0; >+ int retval = 1; >+ >+ /* 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]) { >+ tw_dev->srb[i]->result = (DID_RESET << 16); >+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]); >+ twa_unmap_scsi_data(tw_dev, i); >+ } >+ } >+ } >+ >+ /* Reset queues and counts */ >+ for (i = 0; i < TW_Q_LENGTH; i++) { >+ tw_dev->free_queue[i] = i; >+ tw_dev->state[i] = TW_S_INITIAL; >+ } >+ tw_dev->free_head = TW_Q_START; >+ tw_dev->free_tail = TW_Q_START; >+ tw_dev->posted_request_count = 0; >+ tw_dev->pending_request_count = 0; >+ tw_dev->pending_head = TW_Q_START; >+ tw_dev->pending_tail = TW_Q_START; >+ tw_dev->reset_print = 0; >+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; >+ tw_dev->flags = 0; >+ >+ TW_DISABLE_INTERRUPTS(tw_dev); >+ >+ if (twa_reset_sequence(tw_dev, 1)) >+ goto out; >+ >+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); >+ >+ retval = 0; >+out: >+ return retval; >+} /* End twa_reset_device_extension() */ >+ >+/* This function will reset a controller */ >+static int 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); >+ >+ /* 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), 30)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence"); >+ do_soft_reset = 1; >+ tries++; >+ continue; >+ } >+ >+ /* Empty response queue */ >+ if (twa_empty_response_queue(tw_dev)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Response queue empty failed during reset sequence"); >+ do_soft_reset = 1; >+ tries++; >+ continue; >+ } >+ >+ flashed = 0; >+ >+ /* 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 */ >+static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) >+{ >+ int heads, sectors, cylinders; >+ TW_Device_Extension *tw_dev; >+ >+ tw_dev = (TW_Device_Extension *)sdev->host->hostdata; >+ >+ heads = 64; >+ sectors = 32; >+ cylinders = (unsigned long)capacity / (heads * sectors); >+ >+ if (capacity >= 0x200000) { >+ heads = 255; >+ sectors = 63; >+ cylinders = (unsigned long)capacity / (heads * sectors); >+ } >+ >+ geom[0] = heads; >+ geom[1] = sectors; >+ geom[2] = cylinders; >+ >+ return 0; >+} /* End twa_scsi_biosparam() */ >+ >+/* This function will find and initialize all cards */ >+static int twa_scsi_detect(Scsi_Host_Template *tw_host) >+{ >+ int numcards = 0, i; >+ struct Scsi_Host *host = NULL; >+ TW_Device_Extension *tw_dev, *tw_dev2; >+ struct pci_dev *tw_pci_dev=NULL; >+ u32 mem_addr, mem_len; >+ u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID_9000 }; >+ >+ printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version); >+ >+ for (i = 0; i < TW_NUMDEVICES; i++) { >+ while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) { >+ if (pci_enable_device(tw_pci_dev)) >+ continue; >+ >+ if (pci_set_dma_mask(tw_pci_dev, TW_DMA_MASK)) { >+ TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); >+ continue; >+ } >+ >+ /* Prepare temporary device extension */ >+ tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC); >+ if (!tw_dev) { >+ TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension"); >+ continue; >+ } >+ memset(tw_dev, 0, sizeof(TW_Device_Extension)); >+ >+ /* Save pci_dev struct to device extension */ >+ tw_dev->tw_pci_dev = tw_pci_dev; >+ >+ if (twa_initialize_device_extension(tw_dev)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension"); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ continue; >+ } >+ >+ mem_addr = pci_resource_start(tw_pci_dev, 1); >+ mem_len = pci_resource_len(tw_pci_dev, 1); >+ >+ /* Make sure that mem region isn't already taken */ >+ if (check_mem_region(mem_addr, mem_len)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Failed to get mem region"); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ continue; >+ } >+ >+ /* Reserve the mem address space */ >+ request_mem_region(mem_addr, mem_len, TW_DEVICE_NAME); >+ >+ /* Save base address */ >+ tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE); >+ >+ /* Disable interrupts on the card */ >+ TW_DISABLE_INTERRUPTS(tw_dev); >+ >+ /* Initialize the card */ >+ if (twa_reset_sequence(tw_dev, 0)) { >+ release_mem_region(mem_addr, mem_len); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ continue; >+ } >+ >+ /* Set card status as online */ >+ tw_dev->online = 1; >+ >+ /* Register the card with the kernel SCSI layer */ >+ host = scsi_register(tw_host, sizeof(TW_Device_Extension)); >+ if (!host) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi register failed"); >+ release_mem_region(mem_addr, mem_len); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ continue; >+ } >+ >+ /* Set max target id's */ >+ host->max_id = TW_MAX_UNITS; >+ >+ /* Set max cdb size in bytes */ >+ host->max_cmd_len = TW_MAX_CDB_LEN; >+ >+ scsi_set_device(host, &tw_pci_dev->dev); >+ >+ printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, tw_pci_dev->irq); >+ printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n", host->host_no, (char *)twa_get_param(tw_dev, 0, TW_VERSION_TABLE, 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)); >+ if (host->hostdata) { >+ tw_dev2 = (TW_Device_Extension *)host->hostdata; >+ memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension)); >+ /* Need to init the sem/wqueue after the copy */ >+ init_MUTEX(&tw_dev2->ioctl_sem); >+ init_waitqueue_head(&tw_dev2->ioctl_wqueue); >+ >+ twa_device_extension_list[twa_device_extension_count] = tw_dev2; >+ numcards++; >+ twa_device_extension_count = numcards; >+ tw_dev2->host = host; >+ } else { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x28, "Bad scsi host data"); >+ scsi_unregister(host); >+ release_mem_region(mem_addr, mem_len); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ continue; >+ } >+ >+ /* Now setup the interrupt handler */ >+ if (twa_setup_irq(tw_dev2)) { >+ scsi_unregister(host); >+ release_mem_region(mem_addr, mem_len); >+ twa_free_device_extension(tw_dev); >+ kfree(tw_dev); >+ numcards--; >+ continue; >+ } >+ >+ /* Re-enable interrupts on the card */ >+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev2); >+ >+ /* Free the temporary device extension */ >+ if (tw_dev) >+ kfree(tw_dev); >+ } >+ } >+ >+ if (numcards == 0) { >+ printk(KERN_WARNING "3w-9xxx: No cards successfully initialized.\n"); >+ } else { >+ register_reboot_notifier(&twa_notifier); >+ if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0) >+ TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device"); >+ } >+ return numcards; >+} /* End twa_scsi_detect() */ >+ >+/* This is the new scsi eh abort function */ >+static int twa_scsi_eh_abort(Scsi_Cmnd *SCpnt) >+{ >+ int i; >+ TW_Device_Extension *tw_dev = NULL; >+ int retval = FAILED; >+ >+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; >+ >+ tw_dev->num_aborts++; >+ >+ /* If we find any IO's in process, we have to reset the card */ >+ for (i = 0; i < TW_Q_LENGTH; i++) { >+ if ((tw_dev->state[i] != TW_S_FINISHED) && (tw_dev->state[i] != TW_S_INITIAL)) { >+ printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0x2c, SCpnt->device->id, SCpnt->cmnd[0]); >+ if (twa_reset_device_extension(tw_dev)) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2a, "Controller reset failed during scsi abort"); >+ goto out; >+ } >+ break; >+ } >+ } >+ retval = SUCCESS; >+out: >+ return retval; >+} /* End twa_scsi_eh_abort() */ >+ >+/* This is the new scsi eh reset function */ >+static int twa_scsi_eh_reset(Scsi_Cmnd *SCpnt) >+{ >+ TW_Device_Extension *tw_dev = NULL; >+ int retval = FAILED; >+ >+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; >+ >+ tw_dev->num_resets++; >+ >+ printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset started.\n", tw_dev->host->host_no); >+ >+ /* 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; >+ } >+ printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset succeeded.\n", tw_dev->host->host_no); >+ retval = SUCCESS; >+out: >+ return retval; >+} /* End twa_scsi_eh_reset() */ >+ >+/* This function handles input and output from /proc/scsi/3w-9xxx/x */ >+static int twa_scsi_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout) >+{ >+ TW_Device_Extension *tw_dev = NULL; >+ TW_Info info; >+ unsigned int i; >+ int retval = -EINVAL; >+ >+ /* Find the correct device extension */ >+ for (i = 0; i < twa_device_extension_count; i++) >+ if (twa_device_extension_list[i]->host->host_no == shost->host_no) >+ tw_dev = twa_device_extension_list[i]; >+ if (!tw_dev) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2c, "No device extension for proc operation"); >+ goto out; >+ } >+ >+ info.buffer = buffer; >+ info.length = length; >+ info.offset = offset; >+ info.position = 0; >+ >+ if (!inout) { >+ /* Read */ >+ if (start) { >+ *start = buffer; >+ } >+ twa_copy_info(&info, "scsi%d: 3ware 9000 Storage Controller\n", shost->host_no); >+ twa_copy_info(&info, "Driver version: %s\n", twa_driver_version); >+ twa_copy_info(&info, "Current commands posted: %4d\n", tw_dev->posted_request_count); >+ twa_copy_info(&info, "Max commands posted: %4d\n", tw_dev->max_posted_request_count); >+ twa_copy_info(&info, "Current pending commands: %4d\n", tw_dev->pending_request_count); >+ twa_copy_info(&info, "Max pending commands: %4d\n", tw_dev->max_pending_request_count); >+ twa_copy_info(&info, "Last sgl length: %4d\n", tw_dev->sgl_entries); >+ twa_copy_info(&info, "Max sgl length: %4d\n", tw_dev->max_sgl_entries); >+ twa_copy_info(&info, "Last sector count: %4d\n", tw_dev->sector_count); >+ twa_copy_info(&info, "Max sector count: %4d\n", tw_dev->max_sector_count); >+ twa_copy_info(&info, "SCSI Host Resets: %4d\n", tw_dev->num_resets); >+ twa_copy_info(&info, "SCSI Aborts/Timeouts: %4d\n", tw_dev->num_aborts); >+ twa_copy_info(&info, "AEN's: %4d\n", tw_dev->aen_count); >+ } >+ if (info.position > info.offset) { >+ retval = info.position - info.offset; >+ } else { >+ retval = 0; >+ } >+out: >+ return retval; >+} /* End twa_scsi_proc_info() */ >+ >+/* This is the main scsi queue function to handle scsi opcodes */ >+static int twa_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) >+{ >+ int request_id; >+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; >+ >+ /* Skip lun and channel probes */ >+ if ((SCpnt->device->lun != 0) || (SCpnt->device->channel != 0)) { >+ SCpnt->result = (DID_BAD_TARGET << 16); >+ done(SCpnt); >+ goto out; >+ } >+ >+ /* Save done function into Scsi_Cmnd struct */ >+ SCpnt->scsi_done = done; >+ >+ /* 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; >+ >+ /* Initialize phase to zero */ >+ SCpnt->SCp.phase = 0; >+ >+ if (twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL)) { >+ tw_dev->state[request_id] = TW_S_COMPLETED; >+ twa_free_request_id(tw_dev, request_id); >+ SCpnt->result = (DID_ERROR << 16); >+ done(SCpnt); >+ } >+out: >+ return 0; >+} /* End twa_scsi_queue() */ >+ >+/* This function will release the resources on an rmmod call */ >+static int twa_scsi_release(struct Scsi_Host *tw_host) >+{ >+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)tw_host->hostdata; >+ >+ twa_halt(0, 0, 0); >+ >+ /* Free up the mem region */ >+ release_mem_region(pci_resource_start(tw_dev->tw_pci_dev, 1), pci_resource_len(tw_dev->tw_pci_dev, 1)); >+ >+ /* Free up the IRQ */ >+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev); >+ >+ /* Unregister character device */ >+ if (twa_major >= 0) { >+ unregister_chrdev(twa_major, "twa"); >+ twa_major = -1; >+ } >+ >+ /* Free up device extension resources */ >+ twa_free_device_extension(tw_dev); >+ >+ /* Tell kernel scsi-layer we are gone */ >+ scsi_unregister(tw_host); >+ >+ return 0; >+} /* End twa_scsi_release() */ >+ >+/* 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_Apache *sglistarg) >+{ >+ TW_Command_Full *full_command_packet; >+ TW_Command_Apache *command_packet; >+ u32 num_sectors = 0x0; >+ int i, sg_count; >+ Scsi_Cmnd *srb = NULL; >+ struct scatterlist *sglist = NULL; >+ u32 buffaddr = 0x0; >+ 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]; >+ } >+ >+ /* Initialize command packet */ >+ full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; >+ full_command_packet->header.header_desc.size_header = 128; >+ full_command_packet->header.status_block.error = 0; >+ full_command_packet->header.status_block.substatus_block.severity = 0; >+ >+ command_packet = &full_command_packet->command.newcommand; >+ command_packet->status = 0; >+ command_packet->command.opcode = TW_OP_EXECUTE_SCSI; >+ >+ /* We forced 16 byte cdb use earlier */ >+ if (!cdb) >+ memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN); >+ else >+ memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN); >+ >+ if (srb) >+ command_packet->unit = srb->device->id; >+ else >+ command_packet->unit = 0; >+ >+ command_packet->request_id = request_id; >+ command_packet->sgl_offset = 16; >+ >+ 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; >+ } 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 = 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) { >+ 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) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi"); >+ goto out; >+ } >+ } >+ command_packet->sgl_entries = tw_dev->srb[request_id]->use_sg; >+ } >+ } 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) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post"); >+ goto out; >+ } >+ } >+ command_packet->sgl_entries = use_sg; >+ } >+ >+ if (srb) { >+ if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) >+ num_sectors = (u32)srb->cmnd[4]; >+ >+ if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) >+ num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8); >+ } >+ >+ /* Update sector statistic */ >+ tw_dev->sector_count = num_sectors; >+ if (tw_dev->sector_count > tw_dev->max_sector_count) >+ tw_dev->max_sector_count = tw_dev->sector_count; >+ >+ /* Update SG statistics */ >+ if (srb) { >+ tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; >+ 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 */ >+ twa_post_command_packet(tw_dev, request_id); >+ retval = 0; >+out: >+ return retval; >+} /* End twa_scsiop_execute_scsi() */ >+ >+/* This function completes an execute scsi operation */ >+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id) >+{ >+ /* Copy the response if too small */ >+ if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { >+ memcpy(tw_dev->srb[request_id]->request_buffer, tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_bufflen); >+ } >+} /* End twa_scsiop_execute_scsi_complete() */ >+ >+/* This function will setup the interrupt handler */ >+static int twa_setup_irq(TW_Device_Extension *tw_dev) >+{ >+ char *device = TW_DEVICE_NAME; >+ int retval = 1; >+ >+ if (request_irq(tw_dev->tw_pci_dev->irq, twa_interrupt, SA_SHIRQ, device, tw_dev) < 0) { >+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x30, "Error requesting IRQ"); >+ goto out; >+ } >+ retval = 0; >+out: >+ return retval; >+} /* End twa_setup_irq() */ >+ >+/* This function tells the controller to shut down */ >+static void twa_shutdown_device(TW_Device_Extension *tw_dev) >+{ >+ /* Disable interrupts */ >+ TW_DISABLE_INTERRUPTS(tw_dev); >+ >+ /* 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"); >+ } >+ >+ /* Clear all interrupts just before exit */ >+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); >+} /* End twa_shutdown_device() */ >+ >+/* This function will configure individual target parameters */ >+static int twa_slave_configure(Scsi_Device *SDptr) >+{ >+ int max_cmds; >+ >+ if (cmds_per_lun) { >+ max_cmds = cmds_per_lun; >+ if (max_cmds > TW_MAX_CMDS_PER_LUN) >+ max_cmds = TW_MAX_CMDS_PER_LUN; >+ } else { >+ max_cmds = TW_MAX_CMDS_PER_LUN; >+ } >+ scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, max_cmds); >+ >+ return 0; >+} /* End twa_slave_configure */ >+ >+/* This function will look up a string */ >+static char *twa_string_lookup(twa_message_type *table, unsigned int code) >+{ >+ int index; >+ >+ for (index = 0; ((code != table[index].code) && >+ (table[index].text != (char *)0)); index++); >+ return(table[index].text); >+} /* End twa_string_lookup() */ >+ >+/* This function will perform a pci-dma unmap */ >+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id) >+{ >+ Scsi_Cmnd *cmd = tw_dev->srb[request_id]; >+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); >+ struct pci_dev *pdev = tw_dev->tw_pci_dev; >+ >+ switch(cmd->SCp.phase) { >+ case 1: >+ pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir); >+ break; >+ case 2: >+ pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir); >+ break; >+ } >+} /* End twa_unmap_scsi_data() */ >+ >+/* Now get things going */ >+static Scsi_Host_Template driver_template = { >+ .proc_name = "3w-9xxx", >+ .proc_info = twa_scsi_proc_info, >+ .name = "3ware 9000 Storage Controller", >+ .detect = twa_scsi_detect, >+ .release = twa_scsi_release, >+ .queuecommand = twa_scsi_queue, >+ .eh_abort_handler = twa_scsi_eh_abort, >+ .eh_host_reset_handler = twa_scsi_eh_reset, >+ .bios_param = twa_scsi_biosparam, >+ .slave_configure = twa_slave_configure, >+ .can_queue = TW_Q_LENGTH-2, >+ .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, >+ .emulated = 1 >+}; >+#include "scsi_module.c" >+ >diff -Naupr linux-2.6.6/drivers/scsi/3w-9xxx.h linux-2.6.6-ss1/drivers/scsi/3w-9xxx.h >--- linux-2.6.6/drivers/scsi/3w-9xxx.h 1969-12-31 18:00:00.000000000 -0600 >+++ linux-2.6.6-ss1/drivers/scsi/3w-9xxx.h 2004-05-04 13:38:34.000000000 -0500 >@@ -0,0 +1,791 @@ >+/* >+ 3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux. >+ >+ Written By: Adam Radford <linuxraid@amcc.com> >+ >+ Copyright (C) 2004 Applied Micro Circuits 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 >+ 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: >+ linuxraid@amcc.com >+ >+ For more information, goto: >+ http://www.amcc.com >+*/ >+ >+#ifndef _3W_9XXX_H >+#define _3W_9XXX_H >+ >+#include <linux/version.h> >+#include <linux/types.h> >+ >+/* AEN string type */ >+typedef struct TAG_twa_message_type { >+ unsigned int code; >+ char* text; >+} twa_message_type; >+ >+/* AEN strings */ >+static twa_message_type twa_aen_table[] = { >+ {0x0000, "AEN queue empty"}, >+ {0x0001, "Controller reset occurred"}, >+ {0x0002, "Degraded unit detected"}, >+ {0x0003, "Controller error occured"}, >+ {0x0004, "Background rebuild failed"}, >+ {0x0005, "Background rebuild done"}, >+ {0x0006, "Incomplete unit detected"}, >+ {0x0007, "Background initialize done"}, >+ {0x0008, "Unclean shutdown detected"}, >+ {0x0009, "Drive timeout detected"}, >+ {0x000A, "Drive error detected"}, >+ {0x000B, "Rebuild started"}, >+ {0x000C, "Background initialize started"}, >+ {0x000D, "Entire logical unit was deleted"}, >+ {0x000E, "Background initialize failed"}, >+ {0x000F, "SMART attribute exceeded threshold"}, >+ {0x0010, "Power supply reported AC under range"}, >+ {0x0011, "Power supply reported DC out of range"}, >+ {0x0012, "Power supply reported a malfunction"}, >+ {0x0013, "Power supply predicted malfunction"}, >+ {0x0014, "Battery charge is below threshold"}, >+ {0x0015, "Fan speed is below threshold"}, >+ {0x0016, "Temperature sensor is above threshold"}, >+ {0x0017, "Power supply was removed"}, >+ {0x0018, "Power supply was inserted"}, >+ {0x0019, "Drive was removed from a bay"}, >+ {0x001A, "Drive was inserted into a bay"}, >+ {0x001B, "Drive bay cover door was opened"}, >+ {0x001C, "Drive bay cover door was closed"}, >+ {0x001D, "Product case was opened"}, >+ {0x0020, "Prepare for shutdown (power-off)"}, >+ {0x0021, "Downgrade UDMA mode to lower speed"}, >+ {0x0022, "Upgrade UDMA mode to higher speed"}, >+ {0x0023, "Sector repair completed"}, >+ {0x0024, "Sbuf memory test failed"}, >+ {0x0025, "Error flushing cached write data to array"}, >+ {0x0026, "Drive reported data ECC error"}, >+ {0x0027, "DCB has checksum error"}, >+ {0x0028, "DCB version is unsupported"}, >+ {0x0029, "Background verify started"}, >+ {0x002A, "Background verify failed"}, >+ {0x002B, "Background verify done"}, >+ {0x002C, "Bad sector overwritten during rebuild"}, >+ {0x002D, "Background rebuild error on source drive"}, >+ {0x002E, "Replace failed because replacement drive too small"}, >+ {0x002F, "Verify failed because array was never initialized"}, >+ {0x0030, "Unsupported ATA drive"}, >+ {0x0031, "Synchronize host/controller time"}, >+ {0x0032, "Spare capacity is inadequate for some units"}, >+ {0x0033, "Background migration started"}, >+ {0x0034, "Background migration failed"}, >+ {0x0035, "Background migration done"}, >+ {0x0036, "Verify detected and fixed data/parity mismatch"}, >+ {0x0037, "SO-DIMM incompatible"}, >+ {0x0038, "SO-DIMM not detected"}, >+ {0x0039, "Corrected Sbuf ECC error"}, >+ {0x003A, "Drive power on reset detected"}, >+ {0x003B, "Background rebuild paused"}, >+ {0x003C, "Background initialize paused"}, >+ {0x003D, "Background verify paused"}, >+ {0x003E, "Background migration paused"}, >+ {0x003F, "Corrupt flash file system detected"}, >+ {0x0040, "Flash file system repaired"}, >+ {0x0041, "Unit number assignments were lost"}, >+ {0x0042, "Error during read of primary DCB"}, >+ {0x0043, "Latent error found in backup DCB"}, >+ {0x00FC, "Recovered/finished array membership update"}, >+ {0x00FD, "Handler lockup"}, >+ {0x00FE, "Retrying PCI transfer"}, >+ {0x00FF, "AEN queue is full"}, >+ {0xFFFFFFFF, (char*) 0} >+}; >+ >+/* AEN severity table */ >+static char *twa_aen_severity_table[] = >+{ >+ "None", "ERROR", "WARNING", "INFO", "DEBUG", (char*) 0 >+}; >+ >+/* Error strings */ >+static twa_message_type twa_error_table[] = { >+ {0x0100, "SGL entry contains zero data"}, >+ {0x0101, "Invalid command opcode"}, >+ {0x0102, "SGL entry has unaligned address"}, >+ {0x0103, "SGL size does not match command"}, >+ {0x0104, "SGL entry has illegal length"}, >+ {0x0105, "Command packet is not aligned"}, >+ {0x0106, "Invalid request ID"}, >+ {0x0107, "Duplicate request ID"}, >+ {0x0108, "ID not locked"}, >+ {0x0109, "LBA out of range"}, >+ {0x010A, "Logical unit not supported"}, >+ {0x010B, "Parameter table does not exist"}, >+ {0x010C, "Parameter index does not exist"}, >+ {0x010D, "Invalid field in CDB"}, >+ {0x010E, "Specified port has invalid drive"}, >+ {0x010F, "Parameter item size mismatch"}, >+ {0x0110, "Failed memory allocation"}, >+ {0x0111, "Memory request too large"}, >+ {0x0112, "Out of memory segments"}, >+ {0x0113, "Invalid address to deallocate"}, >+ {0x0114, "Out of memory"}, >+ {0x0115, "Out of heap"}, >+ {0x0120, "Double degrade"}, >+ {0x0121, "Drive not degraded"}, >+ {0x0122, "Reconstruct error"}, >+ {0x0123, "Replace not accepted"}, >+ {0x0124, "Replace drive capacity too small"}, >+ {0x0125, "Sector count not allowed"}, >+ {0x0126, "No spares left"}, >+ {0x0127, "Reconstruct error"}, >+ {0x0128, "Unit is offline"}, >+ {0x0129, "Cannot update status to DCB"}, >+ {0x0130, "Invalid stripe handle"}, >+ {0x0131, "Handle that was not locked"}, >+ {0x0132, "Handle that was not empty"}, >+ {0x0133, "Handle has different owner"}, >+ {0x0140, "IPR has parent"}, >+ {0x0150, "Illegal Pbuf address alignment"}, >+ {0x0151, "Illegal Pbuf transfer length"}, >+ {0x0152, "Illegal Sbuf address alignment"}, >+ {0x0153, "Illegal Sbuf transfer length"}, >+ {0x0160, "Command packet too large"}, >+ {0x0161, "SGL exceeds maximum length"}, >+ {0x0162, "SGL has too many entries"}, >+ {0x0170, "Insufficient resources for rebuilder"}, >+ {0x0171, "Verify error (data != parity)"}, >+ {0x0180, "Requested segment not in directory of this DCB"}, >+ {0x0181, "DCB segment has unsupported version"}, >+ {0x0182, "DCB segment has checksum error"}, >+ {0x0183, "DCB support (settings) segment invalid"}, >+ {0x0184, "DCB UDB (unit descriptor block) segment invalid"}, >+ {0x0185, "DCB GUID (globally unique identifier) segment invalid"}, >+ {0x01A0, "Could not clear Sbuf"}, >+ {0x01C0, "Flash identify failed"}, >+ {0x01C1, "Flash out of bounds"}, >+ {0x01C2, "Flash verify error"}, >+ {0x01C3, "Flash file object not found"}, >+ {0x01C4, "Flash file already present"}, >+ {0x01C5, "Flash file system full"}, >+ {0x01C6, "Flash file not present"}, >+ {0x01C7, "Flash file size error"}, >+ {0x01C8, "Bad flash file checksum"}, >+ {0x01CA, "Corrupt flash file system detected"}, >+ {0x01D0, "Invalid field in parameter list"}, >+ {0x01D1, "Parameter list length error"}, >+ {0x01D2, "Parameter item is not changeable"}, >+ {0x01D3, "Parameter item is not saveable"}, >+ {0x0200, "UDMA CRC error"}, >+ {0x0201, "Internal CRC error"}, >+ {0x0202, "Data ECC error"}, >+ {0x0203, "ADP level 1 error"}, >+ {0x0204, "Port timeout"}, >+ {0x0205, "Drive power on reset"}, >+ {0x0206, "ADP level 2 error"}, >+ {0x0207, "Soft reset failed"}, >+ {0x0208, "Drive not ready"}, >+ {0x0209, "Unclassified port error"}, >+ {0x020A, "Drive aborted command"}, >+ {0x0210, "Internal CRC error"}, >+ {0x0211, "PCI abort error"}, >+ {0x0212, "PCI parity error"}, >+ {0x0213, "Port handler error"}, >+ {0x0214, "Token interrupt count error"}, >+ {0x0215, "Timeout waiting for PCI transfer"}, >+ {0x0216, "Corrected buffer ECC"}, >+ {0x0217, "Uncorrected buffer ECC"}, >+ {0x0230, "Unsupported command during flash recovery"}, >+ {0x0231, "Next image buffer expected"}, >+ {0x0232, "Binary image architecture incompatible"}, >+ {0x0233, "Binary image has no signature"}, >+ {0x0234, "Binary image has bad checksum"}, >+ {0x0235, "Image downloaded overflowed buffer"}, >+ {0x0240, "I2C device not found"}, >+ {0x0241, "I2C transaction aborted"}, >+ {0x0242, "SO-DIMM parameter(s) incompatible using defaults"}, >+ {0x0243, "SO-DIMM unsupported"}, >+ {0x0248, "SPI transfer status error"}, >+ {0x0249, "SPI transfer timeout error"}, >+ {0x0250, "Invalid unit descriptor size in CreateUnit"}, >+ {0x0251, "Unit descriptor size exceeds data buffer in CreateUnit"}, >+ {0x0252, "Invalid value in CreateUnit descriptor"}, >+ {0x0253, "Inadequate disk space to support descriptor in CreateUnit"}, >+ {0x0254, "Unable to create data channel for this unit descriptor"}, >+ {0x0255, "CreateUnit descriptor specifies a drive already in use"}, >+ {0x0256, "Unable to write configuration to all disks during CreateUnit"}, >+ {0x0257, "CreateUnit does not support this descriptor version"}, >+ {0x0258, "Invalid subunit for RAID 0 or 5 in CreateUnit"}, >+ {0x0259, "Too many descriptors in CreateUnit"}, >+ {0x025A, "Invalid configuration specified in CreateUnit descriptor"}, >+ {0x025B, "Invalid LBA offset specified in CreateUnit descriptor"}, >+ {0x025C, "Invalid stripelet size specified in CreateUnit descriptor"}, >+ {0x0260, "SMART attribute exceeded threshold"}, >+ {0xFFFFFFFF, (char*) 0} >+}; >+ >+/* Control register bit definitions */ >+#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000 >+#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000 >+#define TW_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000 >+#define TW_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000 >+#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000 >+#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000 >+#define TW_CONTROL_CLEAR_ERROR_STATUS 0x00000200 >+#define TW_CONTROL_ISSUE_SOFT_RESET 0x00000100 >+#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080 >+#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040 >+#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020 >+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000 >+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000 >+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000 >+#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008 >+ >+/* Status register bit definitions */ >+#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000 >+#define TW_STATUS_MINOR_VERSION_MASK 0x0F000000 >+#define TW_STATUS_PCI_PARITY_ERROR 0x00800000 >+#define TW_STATUS_QUEUE_ERROR 0x00400000 >+#define TW_STATUS_MICROCONTROLLER_ERROR 0x00200000 >+#define TW_STATUS_PCI_ABORT 0x00100000 >+#define TW_STATUS_HOST_INTERRUPT 0x00080000 >+#define TW_STATUS_ATTENTION_INTERRUPT 0x00040000 >+#define TW_STATUS_COMMAND_INTERRUPT 0x00020000 >+#define TW_STATUS_RESPONSE_INTERRUPT 0x00010000 >+#define TW_STATUS_COMMAND_QUEUE_FULL 0x00008000 >+#define TW_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000 >+#define TW_STATUS_MICROCONTROLLER_READY 0x00002000 >+#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000 >+#define TW_STATUS_EXPECTED_BITS 0x00002000 >+#define TW_STATUS_UNEXPECTED_BITS 0x00F00008 >+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008 >+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008 >+ >+/* RESPONSE QUEUE BIT DEFINITIONS */ >+#define TW_RESPONSE_ID_MASK 0x00000FF0 >+ >+/* PCI related defines */ >+#define TW_DEVICE_NAME "3ware 9000 Storage Controller" >+#define TW_VENDOR_ID (0x13C1) /* 3ware */ >+#define TW_DEVICE_ID_9000 (0x1002) /* 9000 series controller */ >+#define TW_NUMDEVICES 1 >+#define TW_PCI_CLEAR_PARITY_ERRORS 0xc100 >+#define TW_PCI_CLEAR_PCI_ABORT 0x2000 >+ >+/* Command packet opcodes used by the driver */ >+#define TW_OP_INIT_CONNECTION 0x1 >+#define TW_OP_GET_PARAM 0x12 >+#define TW_OP_SET_PARAM 0x13 >+#define TW_OP_EXECUTE_SCSI 0x10 >+#define TW_OP_DOWNLOAD_FIRMWARE 0x16 >+#define TW_OP_RESET 0x1C >+ >+/* Asynchronous Event Notification (AEN) codes used by the driver */ >+#define TW_AEN_QUEUE_EMPTY 0x0000 >+#define TW_AEN_SOFT_RESET 0x0001 >+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031 >+#define TW_AEN_SEVERITY_ERROR 0x1 >+#define TW_AEN_SEVERITY_DEBUG 0x4 >+#define TW_AEN_NOT_RETRIEVED 0x1 >+#define TW_AEN_RETRIEVED 0x2 >+ >+/* Command state defines */ >+#define TW_S_INITIAL 0x1 /* Initial state */ >+#define TW_S_STARTED 0x2 /* Id in use */ >+#define TW_S_POSTED 0x4 /* Posted to the controller */ >+#define TW_S_PENDING 0x8 /* Waiting to be posted in isr */ >+#define TW_S_COMPLETED 0x10 /* Completed by isr */ >+#define TW_S_FINISHED 0x20 /* I/O completely done */ >+ >+/* Compatibility defines */ >+#define TW_9000_ARCH_ID 0x5 >+#define TW_CURRENT_FW_SRL 24 >+#define TW_CURRENT_FW_BUILD 5 >+#define TW_CURRENT_FW_BRANCH 1 >+ >+/* Misc defines */ >+#define TW_SECTOR_SIZE 512 >+#define TW_ALIGNMENT_9000 4 /* 4 bytes */ >+#define TW_ALIGNMENT_9000_SGL 0x3 >+#define TW_MAX_UNITS 16 >+#define TW_INIT_MESSAGE_CREDITS 0x100 >+#define TW_INIT_COMMAND_PACKET_SIZE 0x3 >+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6 >+#define TW_EXTENDED_INIT_CONNECT 0x2 >+#define TW_BUNDLED_FW_SAFE_TO_FLASH 0x4 >+#define TW_CTLR_FW_RECOMMENDS_FLASH 0x8 >+#define TW_CTLR_FW_COMPATIBLE 0x2 >+#define TW_BASE_FW_SRL 0x17 >+#define TW_BASE_FW_BRANCH 0 >+#define TW_BASE_FW_BUILD 1 >+#if BITS_PER_LONG > 32 >+#define TW_APACHE_MAX_SGL_LENGTH 72 >+#define TW_ESCALADE_MAX_SGL_LENGTH 41 >+#define TW_APACHE_CMD_PKT_SIZE 5 >+#else >+#define TW_APACHE_MAX_SGL_LENGTH 109 >+#define TW_ESCALADE_MAX_SGL_LENGTH 62 >+#define TW_APACHE_CMD_PKT_SIZE 4 >+#endif >+#define TW_ATA_PASS_SGL_MAX 60 >+#define TW_Q_LENGTH 256 >+#define TW_Q_START 0 >+#define TW_MAX_SLOT 32 >+#define TW_MAX_RESET_TRIES 2 >+#define TW_MAX_CMDS_PER_LUN 254 >+#define TW_MAX_RESPONSE_DRAIN 256 >+#define TW_MAX_AEN_DRAIN 40 >+#define TW_IN_INTR 1 >+#define TW_IN_IOCTL 2 >+#define TW_IN_CHRDEV_IOCTL 3 >+#define TW_IN_ATTENTION_LOOP 4 >+#define TW_MAX_SECTORS 256 >+#define TW_AEN_WAIT_TIME 1000 >+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */ >+#define TW_MAX_CDB_LEN 16 >+#define TW_ISR_DONT_COMPLETE 2 >+#define TW_ISR_DONT_RESULT 3 >+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */ >+#define TW_IOCTL_CHRDEV_FREE -1 >+#define TW_COMMAND_OFFSET 128 /* 128 bytes */ >+#define TW_VERSION_TABLE 0x0402 >+#define TW_TIMEKEEP_TABLE 0x040A >+#define TW_INFORMATION_TABLE 0x0403 >+#define TW_PARAM_FWVER 3 >+#define TW_PARAM_FWVER_LENGTH 16 >+#define TW_PARAM_BIOSVER 4 >+#define TW_PARAM_BIOSVER_LENGTH 16 >+#define TW_PARAM_PORTCOUNT 3 >+#define TW_PARAM_PORTCOUNT_LENGTH 1 >+#define TW_MIN_SGL_LENGTH 0x200 /* 512 bytes */ >+#define TW_MAX_SENSE_LENGTH 256 >+#define TW_EVENT_SOURCE_AEN 0x1000 >+#define TW_EVENT_SOURCE_COMMAND 0x1001 >+#define TW_EVENT_SOURCE_PCHIP 0x1002 >+#define TW_EVENT_SOURCE_DRIVER 0x1003 >+#define TW_IOCTL_GET_COMPATIBILITY_INFO 0x101 >+#define TW_IOCTL_GET_LAST_EVENT 0x102 >+#define TW_IOCTL_GET_FIRST_EVENT 0x103 >+#define TW_IOCTL_GET_NEXT_EVENT 0x104 >+#define TW_IOCTL_GET_PREVIOUS_EVENT 0x105 >+#define TW_IOCTL_GET_LOCK 0x106 >+#define TW_IOCTL_RELEASE_LOCK 0x107 >+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 >+#define TW_IOCTL_ERROR_STATUS_NOT_LOCKED 0x1001 // Not locked >+#define TW_IOCTL_ERROR_STATUS_LOCKED 0x1002 // Already locked >+#define TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS 0x1003 // No more events >+#define TW_IOCTL_ERROR_STATUS_AEN_CLOBBER 0x1004 // AEN clobber occurred >+#define TW_IOCTL_ERROR_OS_EFAULT -EFAULT // Bad address >+#define TW_IOCTL_ERROR_OS_EINTR -EINTR // Interrupted system call >+#define TW_IOCTL_ERROR_OS_EINVAL -EINVAL // Invalid argument >+#define TW_IOCTL_ERROR_OS_ENOMEM -ENOMEM // Out of memory >+#define TW_IOCTL_ERROR_OS_ERESTARTSYS -ERESTARTSYS // Restart system call >+#define TW_IOCTL_ERROR_OS_EIO -EIO // I/O error >+#define TW_IOCTL_ERROR_OS_ENOTTY -ENOTTY // Not a typewriter >+#define TW_IOCTL_ERROR_OS_ENODEV -ENODEV // No such device >+#define TW_ALLOCATION_LENGTH 128 >+#define TW_SENSE_DATA_LENGTH 18 >+#define TW_STATUS_CHECK_CONDITION 2 >+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a >+#define TW_ERROR_UNIT_OFFLINE 0x128 >+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3 >+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4 >+#define TW_MESSAGE_SOURCE_LINUX_DRIVER 6 >+#define TW_DRIVER TW_MESSAGE_SOURCE_LINUX_DRIVER >+#define TW_MESSAGE_SOURCE_LINUX_OS 9 >+#define TW_OS TW_MESSAGE_SOURCE_LINUX_OS >+#if BITS_PER_LONG > 32 >+#define TW_COMMAND_SIZE 5 >+#define TW_DMA_MASK 0xffffffffffffffff >+#else >+#define TW_COMMAND_SIZE 4 >+#define TW_DMA_MASK 0xffffffff >+#endif >+ >+/* Macros */ >+#define TW_CONTROL_REG_ADDR(x) (x->base_addr) >+#define TW_STATUS_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x4) >+#if BITS_PER_LONG > 32 >+#define TW_COMMAND_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x20) >+#else >+#define TW_COMMAND_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x8) >+#endif >+#define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0xC) >+#define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x))) >+#define TW_CLEAR_ATTENTION_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x))) >+#define TW_CLEAR_HOST_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x))) >+#define TW_DISABLE_INTERRUPTS(x) (writel(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x))) >+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x))) >+#define TW_MASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x))) >+#define TW_UNMASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x))) >+#define TW_SOFT_RESET(x) (writel(TW_CONTROL_ISSUE_SOFT_RESET | \ >+ TW_CONTROL_CLEAR_HOST_INTERRUPT | \ >+ TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \ >+ TW_CONTROL_MASK_COMMAND_INTERRUPT | \ >+ TW_CONTROL_MASK_RESPONSE_INTERRUPT | \ >+ TW_CONTROL_CLEAR_ERROR_STATUS | \ >+ TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x))) >+#define TW_PRINTK(h,a,b,c) { \ >+if (h) \ >+printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \ >+else \ >+printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \ >+} >+ >+#pragma pack(1) >+ >+/* Scatter Gather List Entry */ >+typedef struct TAG_TW_SG_Entry { >+ unsigned long address; >+ u32 length; >+} TW_SG_Entry; >+ >+/* Command Packet */ >+typedef struct TW_Command { >+ /* First DWORD */ >+ struct { >+ unsigned char opcode:5; >+ unsigned char sgl_offset:3; >+ } byte0_offset; >+ unsigned char size; >+ unsigned char request_id; >+ struct { >+ unsigned char unit:4; >+ unsigned char host_id:4; >+ } byte3_offset; >+ /* Second DWORD */ >+ unsigned char status; >+ unsigned char flags; >+ union { >+ unsigned short block_count; >+ unsigned short parameter_count; >+ } byte6_offset; >+ union { >+ struct { >+ u32 lba; >+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH]; >+#if BITS_PER_LONG > 32 >+ u32 padding[2]; /* pad to 512 bytes */ >+#else >+ u32 padding; >+#endif >+ } io; >+ struct { >+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH]; >+#if BITS_PER_LONG > 32 >+ u32 padding[3]; >+#else >+ u32 padding[2]; >+#endif >+ } param; >+ } byte8_offset; >+} TW_Command; >+ >+/* Scatter gather element for 9000+ controllers */ >+typedef struct TAG_TW_SG_Apache { >+ unsigned long address; >+ u32 length; >+} TW_SG_Apache; >+ >+/* Command Packet for 9000+ controllers */ >+typedef struct TAG_TW_Command_Apache { >+ struct { >+ unsigned char opcode:5; >+ unsigned char reserved:3; >+ } command; >+ unsigned char unit; >+ unsigned short request_id; >+ unsigned char status; >+ unsigned char sgl_offset; >+ unsigned short sgl_entries; >+ unsigned char cdb[16]; >+ TW_SG_Apache sg_list[TW_APACHE_MAX_SGL_LENGTH]; >+#if BITS_PER_LONG > 32 >+ unsigned char padding[8]; >+#endif >+} TW_Command_Apache; >+ >+/* New command packet header */ >+typedef struct TAG_TW_Command_Apache_Header { >+ unsigned char sense_data[TW_SENSE_DATA_LENGTH]; >+ struct { >+ char reserved[4]; >+ unsigned short error; >+ unsigned char padding; >+ struct { >+ unsigned char severity:3; >+ unsigned char reserved:5; >+ } substatus_block; >+ } status_block; >+ unsigned char err_specific_desc[98]; >+ struct { >+ unsigned char size_header; >+ unsigned short reserved; >+ unsigned char size_sense; >+ } header_desc; >+} TW_Command_Apache_Header; >+ >+/* This struct is a union of the 2 command packets */ >+typedef struct TAG_TW_Command_Full { >+ TW_Command_Apache_Header header; >+ union { >+ TW_Command oldcommand; >+ TW_Command_Apache newcommand; >+ } command; >+} TW_Command_Full; >+ >+/* Initconnection structure */ >+typedef struct TAG_TW_Initconnect { >+ unsigned char opcode:5; >+ unsigned char res1:3; >+ unsigned char size; >+ unsigned char request_id; >+ unsigned char res2; >+ unsigned char status; >+ unsigned char flags; >+ unsigned short message_credits; >+ u32 features; >+ unsigned short fw_srl; >+ unsigned short fw_arch_id; >+ unsigned short fw_branch; >+ unsigned short fw_build; >+ u32 result; >+} TW_Initconnect; >+ >+/* Event info structure */ >+typedef struct TAG_TW_Event >+{ >+ unsigned int sequence_id; >+ unsigned int time_stamp_sec; >+ unsigned short aen_code; >+ unsigned char severity; >+ unsigned char retrieved; >+ unsigned char repeat_count; >+ unsigned char parameter_len; >+ unsigned char parameter_data[98]; >+} TW_Event; >+ >+typedef struct TAG_TW_Ioctl_Driver_Command { >+ unsigned int control_code; >+ unsigned int status; >+ unsigned int unique_id; >+ unsigned int sequence_id; >+ unsigned int os_specific; >+ unsigned int buffer_length; >+} TW_Ioctl_Driver_Command; >+ >+typedef struct TAG_TW_Ioctl_Apache { >+ TW_Ioctl_Driver_Command driver_command; >+ char padding[488]; >+ TW_Command_Full firmware_command; >+ char data_buffer[1]; >+} TW_Ioctl_Buf_Apache; >+ >+/* Lock structure for ioctl get/release lock */ >+typedef struct TAG_TW_Lock { >+ unsigned long timeout_msec; >+ unsigned long time_remaining_msec; >+ unsigned long force_flag; >+} TW_Lock; >+ >+/* GetParam descriptor */ >+typedef struct { >+ unsigned short table_id; >+ unsigned short parameter_id; >+ unsigned short parameter_size_bytes; >+ unsigned short actual_parameter_size_bytes; >+ unsigned char data[1]; >+} TW_Param_Apache, *PTW_Param_Apache; >+ >+/* Response queue */ >+typedef union TAG_TW_Response_Queue { >+ struct { >+ u32 undefined_1: 4; >+ u32 response_id: 8; >+ u32 undefined_2: 20; >+ } u; >+ u32 value; >+} TW_Response_Queue; >+ >+typedef struct TAG_TW_Info { >+ char *buffer; >+ int length; >+ int offset; >+ int position; >+} TW_Info; >+ >+/* Compatibility information structure */ >+typedef struct TAG_TW_Compatibility_Info >+{ >+ char driver_version[32]; >+ unsigned short working_srl; >+ unsigned short working_branch; >+ unsigned short working_build; >+} TW_Compatibility_Info; >+ >+/* Command header for ATA pass-thru */ >+typedef struct TAG_TW_Passthru >+{ >+ struct { >+ unsigned char opcode:5; >+ unsigned char sgloff:3; >+ } byte0; >+ unsigned char size; >+ unsigned char request_id; >+ struct { >+ unsigned char aport:4; >+ unsigned char host_id:4; >+ } byte3; >+ unsigned char status; >+ unsigned char flags; >+ unsigned short param; >+ unsigned short features; >+ unsigned short sector_count; >+ unsigned short sector_num; >+ unsigned short cylinder_lo; >+ unsigned short cylinder_hi; >+ unsigned char drive_head; >+ unsigned char command; >+ TW_SG_Entry sg_list[TW_ATA_PASS_SGL_MAX]; >+ unsigned char padding[12]; >+} TW_Passthru; >+ >+typedef struct TAG_TW_Device_Extension { >+ u32 *base_addr; >+ unsigned long *generic_buffer_virt[TW_Q_LENGTH]; >+ unsigned long generic_buffer_phys[TW_Q_LENGTH]; >+ unsigned long *command_packet_virt[TW_Q_LENGTH]; >+ unsigned long command_packet_phys[TW_Q_LENGTH]; >+ struct pci_dev *tw_pci_dev; >+ Scsi_Cmnd *srb[TW_Q_LENGTH]; >+ unsigned char free_queue[TW_Q_LENGTH]; >+ unsigned char free_head; >+ unsigned char free_tail; >+ unsigned char pending_queue[TW_Q_LENGTH]; >+ unsigned char pending_head; >+ unsigned char pending_tail; >+ int state[TW_Q_LENGTH]; >+ unsigned int posted_request_count; >+ unsigned int max_posted_request_count; >+ unsigned int pending_request_count; >+ unsigned int max_pending_request_count; >+ unsigned int max_sgl_entries; >+ unsigned int sgl_entries; >+ unsigned int num_aborts; >+ unsigned int num_resets; >+ unsigned int sector_count; >+ unsigned int max_sector_count; >+ unsigned int aen_count; >+ struct Scsi_Host *host; >+ long flags; >+ int reset_print; >+ char online; >+ TW_Event *event_queue[TW_Q_LENGTH]; >+ unsigned char error_index; >+ unsigned char event_queue_wrapped; >+ unsigned int error_sequence_id; >+ int ioctl_sem_lock; >+ u32 ioctl_msec; >+ int chrdev_request_id; >+ wait_queue_head_t ioctl_wqueue; >+ struct semaphore ioctl_sem; >+ char aen_clobber; >+ unsigned short working_srl; >+ unsigned short working_branch; >+ unsigned short working_build; >+} TW_Device_Extension; >+ >+#pragma pack() >+ >+/* Function prototypes */ >+static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id); >+static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int check_reset); >+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_allocate_memory(TW_Device_Extension *tw_dev, int size, int which); >+static int twa_check_bits(u32 status_reg_value); >+static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed); >+static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); >+static int twa_chrdev_open(struct inode *inode, struct file *file); >+static int twa_chrdev_release(struct inode *inode, struct file *file); >+static int twa_copy_info(TW_Info *info, char *fmt, ...); >+static void twa_copy_mem_info(TW_Info *info, char *data, int len); >+static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value); >+static int twa_empty_response_queue(TW_Device_Extension *tw_dev); >+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host); >+static void twa_free_device_extension(TW_Device_Extension *tw_dev); >+static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id); >+static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes); >+static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id); >+static int twa_halt(struct notifier_block *nb, ulong event, void *buf); >+static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, >+ u32 set_features, unsigned short current_fw_srl, >+ unsigned short current_fw_arch_id, >+ unsigned short current_fw_branch, >+ unsigned short current_fw_build, >+ unsigned short *fw_on_ctlr_srl, >+ unsigned short *fw_on_ctlr_arch_id, >+ unsigned short *fw_on_ctlr_branch, >+ unsigned short *fw_on_ctlr_build, >+ u32 *init_connect_result); >+static int twa_initialize_device_extension(TW_Device_Extension *tw_dev); >+static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs); >+static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); >+static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id); >+static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id); >+static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds); >+static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, 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); >+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_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]); >+static int twa_scsi_detect(Scsi_Host_Template *tw_host); >+static int twa_scsi_eh_abort(Scsi_Cmnd *SCpnt); >+static int twa_scsi_eh_reset(Scsi_Cmnd *SCpnt); >+static int twa_scsi_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout); >+static int twa_scsi_queue(Scsi_Cmnd *cmd, void (*done) (Scsi_Cmnd *)); >+static int twa_scsi_release(struct Scsi_Host *tw_host); >+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg); >+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id); >+static int twa_setup_irq(TW_Device_Extension *tw_dev); >+static void twa_shutdown_device(TW_Device_Extension *tw_dev); >+static int twa_slave_configure(Scsi_Device *SDptr); >+static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code); >+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id); >+ >+#endif /* _3W_9XXX_H */ >+ >diff -Naupr linux-2.6.6/drivers/scsi/Kconfig linux-2.6.6-ss1/drivers/scsi/Kconfig >--- linux-2.6.6/drivers/scsi/Kconfig 2004-05-09 21:33:19.000000000 -0500 >+++ linux-2.6.6-ss1/drivers/scsi/Kconfig 2004-05-18 21:29:29.187681542 -0500 >@@ -250,6 +250,22 @@ config BLK_DEV_3W_XXXX_RAID > Please read the comments at the top of > <file:drivers/scsi/3w-xxxx.c>. > >+config BLK_DEV_3W_9XXX_RAID >+ tristate "3ware 9xxx Hardware ATA-RAID support" >+ depends on PCI && SCSI >+ help >+ 3ware is the only hardware ATA-Raid product in Linux to date. >+ This card is 4, 8, or 12 channel master mode support only. >+ SCSI support required!!! >+ This driver is *only* for the 9000 series of 3ware SATA RAID >+ cards. It will not work with other series or cards. Use the >+ 3w-xxxx driver for other 3ware cards. >+ >+ <http://www.3ware.com/> >+ >+ Please read the comments at the top of >+ <file:drivers/scsi/3w-9xxx.c>. >+ > config SCSI_7000FASST > tristate "7000FASST SCSI support" > depends on ISA && SCSI >diff -Naupr linux-2.6.6/drivers/scsi/Makefile linux-2.6.6-ss1/drivers/scsi/Makefile >--- linux-2.6.6/drivers/scsi/Makefile 2004-05-09 21:33:13.000000000 -0500 >+++ linux-2.6.6-ss1/drivers/scsi/Makefile 2004-05-18 21:26:34.428160988 -0500 >@@ -108,6 +108,7 @@ obj-$(CONFIG_SCSI_MAC53C94) += mac53c94. > obj-$(CONFIG_SCSI_PLUTO) += pluto.o > obj-$(CONFIG_SCSI_DECNCR) += NCR53C9x.o dec_esp.o > obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o >+obj-$(CONFIG_BLK_DEV_3W_9XXX_RAID) += 3w-9xxx.o > obj-$(CONFIG_SCSI_PPA) += ppa.o > obj-$(CONFIG_SCSI_IMM) += imm.o > obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 64814
: 40047