summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ips.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/ips.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/ips.c')
-rw-r--r--drivers/scsi/ips.c7491
1 files changed, 7491 insertions, 0 deletions
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
new file mode 100644
index 000000000000..e46096da8db2
--- /dev/null
+++ b/drivers/scsi/ips.c
@@ -0,0 +1,7491 @@
+/*****************************************************************************/
+/* ips.c -- driver for the Adaptec / IBM ServeRAID controller */
+/* */
+/* Written By: Keith Mitchell, IBM Corporation */
+/* Jack Hammer, Adaptec, Inc. */
+/* David Jeffery, Adaptec, Inc. */
+/* */
+/* Copyright (C) 2000 IBM Corporation */
+/* Copyright (C) 2002,2003 Adaptec, Inc. */
+/* */
+/* 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; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* 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 about this driver should be mailed to: */
+/* ipslinux@adaptec.com */
+/* */
+/* For system support issues, contact your local IBM Customer support. */
+/* Directions to find IBM Customer Support for each country can be found at: */
+/* http://www.ibm.com/planetwide/ */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* Change Log */
+/* */
+/* 0.99.02 - Breakup commands that are bigger than 8 * the stripe size */
+/* 0.99.03 - Make interrupt routine handle all completed request on the */
+/* adapter not just the first one */
+/* - Make sure passthru commands get woken up if we run out of */
+/* SCBs */
+/* - Send all of the commands on the queue at once rather than */
+/* one at a time since the card will support it. */
+/* 0.99.04 - Fix race condition in the passthru mechanism -- this required */
+/* the interface to the utilities to change */
+/* - Fix error recovery code */
+/* 0.99.05 - Fix an oops when we get certain passthru commands */
+/* 1.00.00 - Initial Public Release */
+/* Functionally equivalent to 0.99.05 */
+/* 3.60.00 - Bump max commands to 128 for use with firmware 3.60 */
+/* - Change version to 3.60 to coincide with release numbering. */
+/* 3.60.01 - Remove bogus error check in passthru routine */
+/* 3.60.02 - Make DCDB direction based on lookup table */
+/* - Only allow one DCDB command to a SCSI ID at a time */
+/* 4.00.00 - Add support for ServeRAID 4 */
+/* 4.00.01 - Add support for First Failure Data Capture */
+/* 4.00.02 - Fix problem with PT DCDB with no buffer */
+/* 4.00.03 - Add alternative passthru interface */
+/* - Add ability to flash BIOS */
+/* 4.00.04 - Rename structures/constants to be prefixed with IPS_ */
+/* 4.00.05 - Remove wish_block from init routine */
+/* - Use linux/spinlock.h instead of asm/spinlock.h for kernels */
+/* 2.3.18 and later */
+/* - Sync with other changes from the 2.3 kernels */
+/* 4.00.06 - Fix timeout with initial FFDC command */
+/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@infradead.org> */
+/* 4.10.00 - Add support for ServeRAID 4M/4L */
+/* 4.10.13 - Fix for dynamic unload and proc file system */
+/* 4.20.03 - Rename version to coincide with new release schedules */
+/* Performance fixes */
+/* Fix truncation of /proc files with cat */
+/* Merge in changes through kernel 2.4.0test1ac21 */
+/* 4.20.13 - Fix some failure cases / reset code */
+/* - Hook into the reboot_notifier to flush the controller cache */
+/* 4.50.01 - Fix problem when there is a hole in logical drive numbering */
+/* 4.70.09 - Use a Common ( Large Buffer ) for Flashing from the JCRM CD */
+/* - Add IPSSEND Flash Support */
+/* - Set Sense Data for Unknown SCSI Command */
+/* - Use Slot Number from NVRAM Page 5 */
+/* - Restore caller's DCDB Structure */
+/* 4.70.12 - Corrective actions for bad controller ( during initialization )*/
+/* 4.70.13 - Don't Send CDB's if we already know the device is not present */
+/* - Don't release HA Lock in ips_next() until SC taken off queue */
+/* - Unregister SCSI device in ips_release() */
+/* 4.70.15 - Fix Breakup for very large ( non-SG ) requests in ips_done() */
+/* 4.71.00 - Change all memory allocations to not use GFP_DMA flag */
+/* Code Clean-Up for 2.4.x kernel */
+/* 4.72.00 - Allow for a Scatter-Gather Element to exceed MAX_XFER Size */
+/* 4.72.01 - I/O Mapped Memory release ( so "insmod ips" does not Fail ) */
+/* - Don't Issue Internal FFDC Command if there are Active Commands */
+/* - Close Window for getting too many IOCTL's active */
+/* 4.80.00 - Make ia64 Safe */
+/* 4.80.04 - Eliminate calls to strtok() if 2.4.x or greater */
+/* - Adjustments to Device Queue Depth */
+/* 4.80.14 - Take all semaphores off stack */
+/* - Clean Up New_IOCTL path */
+/* 4.80.20 - Set max_sectors in Scsi_Host structure ( if >= 2.4.7 kernel ) */
+/* - 5 second delay needed after resetting an i960 adapter */
+/* 4.80.26 - Clean up potential code problems ( Arjan's recommendations ) */
+/* 4.90.01 - Version Matching for FirmWare, BIOS, and Driver */
+/* 4.90.05 - Use New PCI Architecture to facilitate Hot Plug Development */
+/* 4.90.08 - Increase Delays in Flashing ( Trombone Only - 4H ) */
+/* 4.90.08 - Data Corruption if First Scatter Gather Element is > 64K */
+/* 4.90.11 - Don't actually RESET unless it's physically required */
+/* - Remove unused compile options */
+/* 5.00.01 - Sarasota ( 5i ) adapters must always be scanned first */
+/* - Get rid on IOCTL_NEW_COMMAND code */
+/* - Add Extended DCDB Commands for Tape Support in 5I */
+/* 5.10.12 - use pci_dma interfaces, update for 2.5 kernel changes */
+/* 5.10.15 - remove unused code (sem, macros, etc.) */
+/* 5.30.00 - use __devexit_p() */
+/* 6.00.00 - Add 6x Adapters and Battery Flash */
+/* 6.10.00 - Remove 1G Addressing Limitations */
+/* 6.11.xx - Get VersionInfo buffer off the stack ! DDTS 60401 */
+/* 6.11.xx - Make Logical Drive Info structure safe for DMA DDTS 60639 */
+/* 7.10.xx - Add highmem_io flag in SCSI Templete for 2.4 kernels */
+/* - Fix path/name for scsi_hosts.h include for 2.6 kernels */
+/* - Fix sort order of 7k */
+/* - Remove 3 unused "inline" functions */
+/*****************************************************************************/
+
+/*
+ * Conditional Compilation directives for this driver:
+ *
+ * IPS_DEBUG - Turn on debugging info
+ *
+ * Parameters:
+ *
+ * debug:<number> - Set debug level to <number>
+ * NOTE: only works when IPS_DEBUG compile directive is used.
+ * 1 - Normal debug messages
+ * 2 - Verbose debug messages
+ * 11 - Method trace (non interrupt)
+ * 12 - Method trace (includes interrupt)
+ *
+ * noi2o - Don't use I2O Queues (ServeRAID 4 only)
+ * nommap - Don't use memory mapped I/O
+ * ioctlsize - Initial size of the IOCTL buffer
+ */
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/page.h>
+#include <linux/stddef.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+
+#include <linux/blkdev.h>
+#include <linux/types.h>
+
+#include <scsi/sg.h>
+
+#include "scsi.h"
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
+#include "hosts.h"
+#else
+#include <scsi/scsi_host.h>
+#endif
+
+#include "ips.h"
+
+#include <linux/module.h>
+
+#include <linux/stat.h>
+#include <linux/config.h>
+
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <linux/smp.h>
+
+#ifdef MODULE
+static char *ips = NULL;
+module_param(ips, charp, 0);
+#endif
+
+/*
+ * DRIVER_VER
+ */
+#define IPS_VERSION_HIGH "7.10"
+#define IPS_VERSION_LOW ".18 "
+
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
+#include <linux/blk.h>
+#include "sd.h"
+#define IPS_SG_ADDRESS(sg) ((sg)->address)
+#define IPS_LOCK_SAVE(lock,flags) spin_lock_irqsave(&io_request_lock,flags)
+#define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock_irqrestore(&io_request_lock,flags)
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+#else
+#define IPS_SG_ADDRESS(sg) (page_address((sg)->page) ? \
+ page_address((sg)->page)+(sg)->offset : NULL)
+#define IPS_LOCK_SAVE(lock,flags) do{spin_lock(lock);(void)flags;}while(0)
+#define IPS_UNLOCK_RESTORE(lock,flags) do{spin_unlock(lock);(void)flags;}while(0)
+#endif
+
+#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
+ SCSI_DATA_NONE == scb->scsi_cmd->sc_data_direction) ? \
+ PCI_DMA_BIDIRECTIONAL : \
+ scsi_to_pci_dma_dir(scb->scsi_cmd->sc_data_direction))
+
+#ifdef IPS_DEBUG
+#define METHOD_TRACE(s, i) if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
+#define DEBUG(i, s) if (ips_debug >= i) printk(KERN_NOTICE s "\n");
+#define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
+#else
+#define METHOD_TRACE(s, i)
+#define DEBUG(i, s)
+#define DEBUG_VAR(i, s, v...)
+#endif
+
+/*
+ * Function prototypes
+ */
+static int ips_detect(Scsi_Host_Template *);
+static int ips_release(struct Scsi_Host *);
+static int ips_eh_abort(Scsi_Cmnd *);
+static int ips_eh_reset(Scsi_Cmnd *);
+static int ips_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+static const char *ips_info(struct Scsi_Host *);
+static irqreturn_t do_ipsintr(int, void *, struct pt_regs *);
+static int ips_hainit(ips_ha_t *);
+static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
+static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
+static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
+static int ips_online(ips_ha_t *, ips_scb_t *);
+static int ips_inquiry(ips_ha_t *, ips_scb_t *);
+static int ips_rdcap(ips_ha_t *, ips_scb_t *);
+static int ips_msense(ips_ha_t *, ips_scb_t *);
+static int ips_reqsen(ips_ha_t *, ips_scb_t *);
+static int ips_deallocatescbs(ips_ha_t *, int);
+static int ips_allocatescbs(ips_ha_t *);
+static int ips_reset_copperhead(ips_ha_t *);
+static int ips_reset_copperhead_memio(ips_ha_t *);
+static int ips_reset_morpheus(ips_ha_t *);
+static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
+static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
+static int ips_isintr_copperhead(ips_ha_t *);
+static int ips_isintr_copperhead_memio(ips_ha_t *);
+static int ips_isintr_morpheus(ips_ha_t *);
+static int ips_wait(ips_ha_t *, int, int);
+static int ips_write_driver_status(ips_ha_t *, int);
+static int ips_read_adapter_status(ips_ha_t *, int);
+static int ips_read_subsystem_parameters(ips_ha_t *, int);
+static int ips_read_config(ips_ha_t *, int);
+static int ips_clear_adapter(ips_ha_t *, int);
+static int ips_readwrite_page5(ips_ha_t *, int, int);
+static int ips_init_copperhead(ips_ha_t *);
+static int ips_init_copperhead_memio(ips_ha_t *);
+static int ips_init_morpheus(ips_ha_t *);
+static int ips_isinit_copperhead(ips_ha_t *);
+static int ips_isinit_copperhead_memio(ips_ha_t *);
+static int ips_isinit_morpheus(ips_ha_t *);
+static int ips_erase_bios(ips_ha_t *);
+static int ips_program_bios(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_verify_bios(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_erase_bios_memio(ips_ha_t *);
+static int ips_program_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_verify_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_flash_copperhead(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static int ips_flash_bios(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static int ips_flash_firmware(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static void ips_free_flash_copperhead(ips_ha_t * ha);
+static void ips_get_bios_version(ips_ha_t *, int);
+static void ips_identify_controller(ips_ha_t *);
+static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
+static void ips_enable_int_copperhead(ips_ha_t *);
+static void ips_enable_int_copperhead_memio(ips_ha_t *);
+static void ips_enable_int_morpheus(ips_ha_t *);
+static int ips_intr_copperhead(ips_ha_t *);
+static int ips_intr_morpheus(ips_ha_t *);
+static void ips_next(ips_ha_t *, int);
+static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
+static void ipsintr_done(ips_ha_t *, struct ips_scb *);
+static void ips_done(ips_ha_t *, ips_scb_t *);
+static void ips_free(ips_ha_t *);
+static void ips_init_scb(ips_ha_t *, ips_scb_t *);
+static void ips_freescb(ips_ha_t *, ips_scb_t *);
+static void ips_setup_funclist(ips_ha_t *);
+static void ips_statinit(ips_ha_t *);
+static void ips_statinit_memio(ips_ha_t *);
+static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
+static void ips_ffdc_reset(ips_ha_t *, int);
+static void ips_ffdc_time(ips_ha_t *);
+static uint32_t ips_statupd_copperhead(ips_ha_t *);
+static uint32_t ips_statupd_copperhead_memio(ips_ha_t *);
+static uint32_t ips_statupd_morpheus(ips_ha_t *);
+static ips_scb_t *ips_getscb(ips_ha_t *);
+static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
+static void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *);
+static void ips_putq_copp_tail(ips_copp_queue_t *,
+ ips_copp_wait_item_t *);
+static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *);
+static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
+static Scsi_Cmnd *ips_removeq_wait_head(ips_wait_queue_t *);
+static Scsi_Cmnd *ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *);
+static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *,
+ ips_copp_wait_item_t *);
+static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *);
+
+static int ips_is_passthru(Scsi_Cmnd *);
+static int ips_make_passthru(ips_ha_t *, Scsi_Cmnd *, ips_scb_t *, int);
+static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *);
+static void ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data,
+ unsigned int count);
+static void ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned int count);
+
+static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+static int ips_host_info(ips_ha_t *, char *, off_t, int);
+static void copy_mem_info(IPS_INFOSTR *, char *, int);
+static int copy_info(IPS_INFOSTR *, char *, ...);
+static int ips_get_version_info(ips_ha_t * ha, dma_addr_t, int intr);
+static void ips_version_check(ips_ha_t * ha, int intr);
+static int ips_abort_init(ips_ha_t * ha, int index);
+static int ips_init_phase2(int index);
+
+static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
+static int ips_register_scsi(int index);
+
+/*
+ * global variables
+ */
+static const char ips_name[] = "ips";
+static struct Scsi_Host *ips_sh[IPS_MAX_ADAPTERS]; /* Array of host controller structures */
+static ips_ha_t *ips_ha[IPS_MAX_ADAPTERS]; /* Array of HA structures */
+static unsigned int ips_next_controller;
+static unsigned int ips_num_controllers;
+static unsigned int ips_released_controllers;
+static int ips_hotplug;
+static int ips_cmd_timeout = 60;
+static int ips_reset_timeout = 60 * 5;
+static int ips_force_memio = 1; /* Always use Memory Mapped I/O */
+static int ips_force_i2o = 1; /* Always use I2O command delivery */
+static int ips_ioctlsize = IPS_IOCTL_SIZE; /* Size of the ioctl buffer */
+static int ips_cd_boot; /* Booting from Manager CD */
+static char *ips_FlashData = NULL; /* CD Boot - Flash Data Buffer */
+static dma_addr_t ips_flashbusaddr;
+static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */
+static uint32_t MaxLiteCmds = 32; /* Max Active Cmds for a Lite Adapter */
+static Scsi_Host_Template ips_driver_template = {
+ .detect = ips_detect,
+ .release = ips_release,
+ .info = ips_info,
+ .queuecommand = ips_queue,
+ .eh_abort_handler = ips_eh_abort,
+ .eh_host_reset_handler = ips_eh_reset,
+ .proc_name = "ips",
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ .proc_info = ips_proc_info,
+ .slave_configure = ips_slave_configure,
+#else
+ .proc_info = ips_proc24_info,
+ .select_queue_depths = ips_select_queue_depth,
+#endif
+ .bios_param = ips_biosparam,
+ .this_id = -1,
+ .sg_tablesize = IPS_MAX_SG,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .use_new_eh_code = 1,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .highmem_io = 1,
+#endif
+};
+
+static IPS_DEFINE_COMPAT_TABLE( Compatable ); /* Version Compatability Table */
+
+
+/* This table describes all ServeRAID Adapters */
+static struct pci_device_id ips_pci_table[] = {
+ { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE( pci, ips_pci_table );
+
+static char ips_hot_plug_name[] = "ips";
+
+static int __devinit ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
+static void __devexit ips_remove_device(struct pci_dev *pci_dev);
+
+static struct pci_driver ips_pci_driver = {
+ .name = ips_hot_plug_name,
+ .id_table = ips_pci_table,
+ .probe = ips_insert_device,
+ .remove = __devexit_p(ips_remove_device),
+};
+
+
+/*
+ * Necessary forward function protoypes
+ */
+static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
+
+#define MAX_ADAPTER_NAME 15
+
+static char ips_adapter_name[][30] = {
+ "ServeRAID",
+ "ServeRAID II",
+ "ServeRAID on motherboard",
+ "ServeRAID on motherboard",
+ "ServeRAID 3H",
+ "ServeRAID 3L",
+ "ServeRAID 4H",
+ "ServeRAID 4M",
+ "ServeRAID 4L",
+ "ServeRAID 4Mx",
+ "ServeRAID 4Lx",
+ "ServeRAID 5i",
+ "ServeRAID 5i",
+ "ServeRAID 6M",
+ "ServeRAID 6i",
+ "ServeRAID 7t",
+ "ServeRAID 7k",
+ "ServeRAID 7M"
+};
+
+static struct notifier_block ips_notifier = {
+ ips_halt, NULL, 0
+};
+
+/*
+ * Direction table
+ */
+static char ips_command_direction[] = {
+ IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK,
+ IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT,
+ IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN,
+ IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE,
+ IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK,
+ IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE,
+ IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE,
+ IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK
+};
+
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_setup */
+/* */
+/* Routine Description: */
+/* */
+/* setup parameters to the driver */
+/* */
+/****************************************************************************/
+static int
+ips_setup(char *ips_str)
+{
+
+ int i;
+ char *key;
+ char *value;
+ IPS_OPTION options[] = {
+ {"noi2o", &ips_force_i2o, 0},
+ {"nommap", &ips_force_memio, 0},
+ {"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
+ {"cdboot", &ips_cd_boot, 0},
+ {"maxcmds", &MaxLiteCmds, 32},
+ };
+
+ /* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */
+ /* Search for value */
+ while ((key = strsep(&ips_str, ",."))) {
+ if (!*key)
+ continue;
+ value = strchr(key, ':');
+ if (value)
+ *value++ = '\0';
+ /*
+ * We now have key/value pairs.
+ * Update the variables
+ */
+ for (i = 0; i < (sizeof (options) / sizeof (options[0])); i++) {
+ if (strnicmp
+ (key, options[i].option_name,
+ strlen(options[i].option_name)) == 0) {
+ if (value)
+ *options[i].option_flag =
+ simple_strtoul(value, NULL, 0);
+ else
+ *options[i].option_flag =
+ options[i].option_value;
+ break;
+ }
+ }
+ }
+
+ return (1);
+}
+
+__setup("ips=", ips_setup);
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_detect */
+/* */
+/* Routine Description: */
+/* */
+/* Detect and initialize the driver */
+/* */
+/* NOTE: this routine is called under the io_request_lock spinlock */
+/* */
+/****************************************************************************/
+static int
+ips_detect(Scsi_Host_Template * SHT)
+{
+ int i;
+
+ METHOD_TRACE("ips_detect", 1);
+
+#ifdef MODULE
+ if (ips)
+ ips_setup(ips);
+#endif
+
+ for (i = 0; i < ips_num_controllers; i++) {
+ if (ips_register_scsi(i))
+ ips_free(ips_ha[i]);
+ ips_released_controllers++;
+ }
+ ips_hotplug = 1;
+ return (ips_num_controllers);
+}
+
+/****************************************************************************/
+/* configure the function pointers to use the functions that will work */
+/* with the found version of the adapter */
+/****************************************************************************/
+static void
+ips_setup_funclist(ips_ha_t * ha)
+{
+
+ /*
+ * Setup Functions
+ */
+ if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) {
+ /* morpheus / marco / sebring */
+ ha->func.isintr = ips_isintr_morpheus;
+ ha->func.isinit = ips_isinit_morpheus;
+ ha->func.issue = ips_issue_i2o_memio;
+ ha->func.init = ips_init_morpheus;
+ ha->func.statupd = ips_statupd_morpheus;
+ ha->func.reset = ips_reset_morpheus;
+ ha->func.intr = ips_intr_morpheus;
+ ha->func.enableint = ips_enable_int_morpheus;
+ } else if (IPS_USE_MEMIO(ha)) {
+ /* copperhead w/MEMIO */
+ ha->func.isintr = ips_isintr_copperhead_memio;
+ ha->func.isinit = ips_isinit_copperhead_memio;
+ ha->func.init = ips_init_copperhead_memio;
+ ha->func.statupd = ips_statupd_copperhead_memio;
+ ha->func.statinit = ips_statinit_memio;
+ ha->func.reset = ips_reset_copperhead_memio;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios_memio;
+ ha->func.programbios = ips_program_bios_memio;
+ ha->func.verifybios = ips_verify_bios_memio;
+ ha->func.enableint = ips_enable_int_copperhead_memio;
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o_memio;
+ else
+ ha->func.issue = ips_issue_copperhead_memio;
+ } else {
+ /* copperhead */
+ ha->func.isintr = ips_isintr_copperhead;
+ ha->func.isinit = ips_isinit_copperhead;
+ ha->func.init = ips_init_copperhead;
+ ha->func.statupd = ips_statupd_copperhead;
+ ha->func.statinit = ips_statinit;
+ ha->func.reset = ips_reset_copperhead;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios;
+ ha->func.programbios = ips_program_bios;
+ ha->func.verifybios = ips_verify_bios;
+ ha->func.enableint = ips_enable_int_copperhead;
+
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o;
+ else
+ ha->func.issue = ips_issue_copperhead;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_release */
+/* */
+/* Routine Description: */
+/* */
+/* Remove a driver */
+/* */
+/****************************************************************************/
+static int
+ips_release(struct Scsi_Host *sh)
+{
+ ips_scb_t *scb;
+ ips_ha_t *ha;
+ int i;
+
+ METHOD_TRACE("ips_release", 1);
+
+ for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++) ;
+
+ if (i == IPS_MAX_ADAPTERS) {
+ printk(KERN_WARNING
+ "(%s) release, invalid Scsi_Host pointer.\n", ips_name);
+ BUG();
+ return (FALSE);
+ }
+
+ ha = IPS_HA(sh);
+
+ if (!ha)
+ return (FALSE);
+
+ /* flush the cache on the controller */
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
+
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
+
+ /* send command */
+ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n");
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n");
+
+ ips_sh[i] = NULL;
+ ips_ha[i] = NULL;
+
+ /* free extra memory */
+ ips_free(ha);
+
+ /* Free I/O Region */
+ if (ha->io_addr)
+ release_region(ha->io_addr, ha->io_len);
+
+ /* free IRQ */
+ free_irq(ha->irq, ha);
+
+ IPS_REMOVE_HOST(sh);
+ scsi_host_put(sh);
+
+ ips_released_controllers++;
+
+ return (FALSE);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_halt */
+/* */
+/* Routine Description: */
+/* */
+/* Perform cleanup when the system reboots */
+/* */
+/****************************************************************************/
+static int
+ips_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+ ips_scb_t *scb;
+ ips_ha_t *ha;
+ int i;
+
+ if ((event != SYS_RESTART) && (event != SYS_HALT) &&
+ (event != SYS_POWER_OFF))
+ return (NOTIFY_DONE);
+
+ for (i = 0; i < ips_next_controller; i++) {
+ ha = (ips_ha_t *) ips_ha[i];
+
+ if (!ha)
+ continue;
+
+ if (!ha->active)
+ continue;
+
+ /* flush the cache on the controller */
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
+
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
+
+ /* send command */
+ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) ==
+ IPS_FAILURE)
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Incomplete Flush.\n");
+ else
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Flushing Complete.\n");
+ }
+
+ return (NOTIFY_OK);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_eh_abort */
+/* */
+/* Routine Description: */
+/* */
+/* Abort a command (using the new error code stuff) */
+/* Note: this routine is called under the io_request_lock */
+/****************************************************************************/
+int
+ips_eh_abort(Scsi_Cmnd * SC)
+{
+ ips_ha_t *ha;
+ ips_copp_wait_item_t *item;
+ int ret;
+
+ METHOD_TRACE("ips_eh_abort", 1);
+
+ if (!SC)
+ return (FAILED);
+
+ ha = (ips_ha_t *) SC->device->host->hostdata;
+
+ if (!ha)
+ return (FAILED);
+
+ if (!ha->active)
+ return (FAILED);
+
+ if (SC->serial_number != SC->serial_number_at_timeout) {
+ /* HMM, looks like a bogus command */
+ DEBUG(1, "Abort called with bogus scsi command");
+
+ return (FAILED);
+ }
+
+ /* See if the command is on the copp queue */
+ item = ha->copp_waitlist.head;
+ while ((item) && (item->scsi_cmd != SC))
+ item = item->next;
+
+ if (item) {
+ /* Found it */
+ ips_removeq_copp(&ha->copp_waitlist, item);
+ ret = (SUCCESS);
+
+ /* See if the command is on the wait queue */
+ } else if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
+ /* command not sent yet */
+ ret = (SUCCESS);
+ } else {
+ /* command must have already been sent */
+ ret = (FAILED);
+ }
+ return ret;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_eh_reset */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller (with new eh error code) */
+/* */
+/* NOTE: this routine is called under the io_request_lock spinlock */
+/*