diff options
Diffstat (limited to 'drivers/scsi/ips.c')
-rw-r--r-- | drivers/scsi/ips.c | 7491 |
1 files changed, 7491 insertions, 0 deletions
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c new file mode 100644 index 00000000000..e46096da8db --- /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 */ +/* */ +/****************************************************************************/ +static int +ips_eh_reset(Scsi_Cmnd * SC) +{ + int ret; + int i; + ips_ha_t *ha; + ips_scb_t *scb; + ips_copp_wait_item_t *item; + + METHOD_TRACE("ips_eh_reset", 1); + +#ifdef NO_IPS_RESET + return (FAILED); +#else + + if (!SC) { + DEBUG(1, "Reset called with NULL scsi command"); + + return (FAILED); + } + + ha = (ips_ha_t *) SC->device->host->hostdata; + + if (!ha) { + DEBUG(1, "Reset called with NULL ha struct"); + + return (FAILED); + } + + if (!ha->active) + return (FAILED); + |