ips.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:261k
- /*****************************************************************************/
- /* ips.c -- driver for the IBM ServeRAID controller */
- /* */
- /* Written By: Keith Mitchell, IBM Corporation */
- /* */
- /* Copyright (C) 2000 IBM 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; 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@us.ibm.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 ServeRAID firmware 3.60 */
- /* - Change version to 3.60 to coincide with ServeRAID 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 ServeRAID 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 */
- /*****************************************************************************/
- /*
- * 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)
- *
- * noreset - Don't reset the controller
- * nocmdline - Turn off passthru support
- * 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/vmalloc.h>
- #include <linux/delay.h>
- #include <linux/sched.h>
- #include <linux/pci.h>
- #include <linux/proc_fs.h>
- #include <linux/reboot.h>
- #include <linux/tqueue.h>
- #include <linux/interrupt.h>
- #include <linux/blk.h>
- #include <linux/types.h>
- #ifndef NO_IPS_CMDLINE
- #include <scsi/sg.h>
- #endif
- #include "sd.h"
- #include "scsi.h"
- #include "hosts.h"
- #include "ips.h"
- #include <linux/module.h>
- #include <linux/stat.h>
- #include <linux/config.h>
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,18)
- #include <linux/spinlock.h>
- #else
- #include <asm/spinlock.h>
- #endif
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
- #include <linux/init.h>
- #endif
- #include <linux/smp.h>
- #ifdef MODULE
- static char *ips = NULL;
- MODULE_PARM(ips, "s");
- #endif
- /*
- * DRIVER_VER
- */
- #define IPS_VERSION_HIGH "4.80"
- #define IPS_VERSION_LOW ".26 "
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
- struct proc_dir_entry proc_scsi_ips = {
- 0,
- 3, "ips",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
- };
- #endif
- #if !defined(__i386__) && !defined(__ia64__)
- #error "This driver has only been tested on the x86/ia64 platforms"
- #endif
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
- #error "This driver only works with kernel 2.2.0 and later"
- #endif
- #if !defined(NO_IPS_CMDLINE) && ((SG_BIG_BUFF < 8192) || !defined(SG_BIG_BUFF))
- #error "To use the command-line interface you need to define SG_BIG_BUFF"
- #endif
- #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
- /*
- * 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 = 0;
- static unsigned int ips_num_controllers = 0;
- static unsigned int ips_released_controllers = 0;
- 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_resetcontroller = 1; /* Reset the controller */
- static int ips_cmdline = 1; /* Support for passthru */
- static int ips_ioctlsize = IPS_IOCTL_SIZE; /* Size of the ioctl buffer */
- static int ips_cd_boot = 0; /* Booting from ServeRAID Manager CD */
- static char *ips_FlashData = NULL; /* CD Boot - Flash Data Buffer */
- static int ips_FlashDataInUse = 0; /* CD Boot - Flash Data In Use Flag */
- #ifdef IPS_DEBUG
- static int ips_debug = 0; /* Debug mode */
- #endif
- /*
- * Necessary forward function protoypes
- */
- static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
- #define MAX_ADAPTER_NAME 11
- 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"
- };
- 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_IN, 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
- };
- /*
- * Function prototypes
- */
- int ips_detect(Scsi_Host_Template *);
- int ips_release(struct Scsi_Host *);
- int ips_eh_abort(Scsi_Cmnd *);
- int ips_eh_reset(Scsi_Cmnd *);
- int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
- int ips_biosparam(Disk *, kdev_t, int *);
- const char * ips_info(struct Scsi_Host *);
- void 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(ips_ha_t *, ips_scb_t *, ips_scb_callback);
- 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_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 *, u_int32_t, u_int32_t);
- static int ips_verify_bios(ips_ha_t *, char *, u_int32_t, u_int32_t);
- static int ips_erase_bios_memio(ips_ha_t *);
- static int ips_program_bios_memio(ips_ha_t *, char *, u_int32_t, u_int32_t);
- static int ips_verify_bios_memio(ips_ha_t *, char *, u_int32_t, u_int32_t);
- static void ips_flash_bios_section(void *);
- static void ips_flash_bios_segment(void *);
- static void ips_scheduled_flash_bios(void *);
- static void ips_create_nvrampage5(ips_ha_t *, IPS_NVRAM_P5 *);
- static void ips_get_bios_version(ips_ha_t *, int);
- static void ips_identify_controller(ips_ha_t *);
- static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
- 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 void ips_intr_copperhead(ips_ha_t *);
- static void 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_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 *, int);
- static u_int32_t ips_statupd_copperhead(ips_ha_t *);
- static u_int32_t ips_statupd_copperhead_memio(ips_ha_t *);
- static u_int32_t ips_statupd_morpheus(ips_ha_t *);
- static ips_scb_t * ips_getscb(ips_ha_t *);
- static inline void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
- static inline void ips_putq_scb_tail(ips_scb_queue_t *, ips_scb_t *);
- static inline void ips_putq_wait_head(ips_wait_queue_t *, Scsi_Cmnd *);
- static inline void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *);
- static inline void ips_putq_copp_head(ips_copp_queue_t *, ips_copp_wait_item_t *);
- static inline void ips_putq_copp_tail(ips_copp_queue_t *, ips_copp_wait_item_t *);
- static inline ips_scb_t * ips_removeq_scb_head(ips_scb_queue_t *);
- static inline ips_scb_t * ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
- static inline Scsi_Cmnd * ips_removeq_wait_head(ips_wait_queue_t *);
- static inline Scsi_Cmnd * ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *);
- static inline ips_copp_wait_item_t * ips_removeq_copp(ips_copp_queue_t *, ips_copp_wait_item_t *);
- static inline ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t *);
- #ifndef NO_IPS_CMDLINE
- 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 int ips_newusrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
- static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *);
- #endif
- int ips_proc_info(char *, char **, off_t, int, 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 *, ...);
- /*--------------------------------------------------------------------------*/
- /* Exported Functions */
- /*--------------------------------------------------------------------------*/
- /****************************************************************************/
- /* */
- /* Routine Name: ips_setup */
- /* */
- /* Routine Description: */
- /* */
- /* setup parameters to the driver */
- /* */
- /****************************************************************************/
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
- static int
- ips_setup(char *ips_str) {
- #else
- void
- ips_setup(char *ips_str, int *dummy) {
- #endif
- int i;
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
- char *p;
- char tokens[3] = {',', '.', 0};
- #endif
- char *key;
- char *value;
- IPS_OPTION options[] = {
- {"noreset", &ips_resetcontroller, 0},
- #ifdef IPS_DEBUG
- {"debug", &ips_debug, 1},
- #endif
- {"noi2o", &ips_force_i2o, 0},
- {"nommap", &ips_force_memio, 0},
- {"nocmdline", &ips_cmdline, 0},
- {"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
- {"cdboot", &ips_cd_boot, 0},
- };
- METHOD_TRACE("ips_setup", 1);
- /* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
- /* Search for value */
- while ((key = strsep(&ips_str, ",."))) {
- if (!*key)
- continue;
- value = strchr(key, ':');
- if (value)
- *value++ = ' ';
- /*
- * 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;
- }
- }
- }
- #else
- for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) {
- p = key;
- /* Search for value */
- while ((p) && (*p != ':'))
- p++;
- if (p) {
- *p = ' ';
- value = p+1;
- } else
- value = NULL;
- /*
- * 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(ips_str)) == 0) {
- if (value)
- *options[i].option_flag = simple_strtoul(value, NULL, 0);
- else
- *options[i].option_flag = options[i].option_value;
- break;
- }
- }
- }
- #endif
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
- return (1);
- #endif
- }
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
- __setup("ips=", ips_setup);
- #endif
- /****************************************************************************/
- /* */
- /* Routine Name: ips_detect */
- /* */
- /* Routine Description: */
- /* */
- /* Detect and initialize the driver */
- /* */
- /* NOTE: this routine is called under the io_request_lock spinlock */
- /* */
- /****************************************************************************/
- int
- ips_detect(Scsi_Host_Template *SHT) {
- struct Scsi_Host *sh;
- ips_ha_t *ha;
- u_int32_t io_addr;
- u_int32_t mem_addr;
- u_int32_t io_len;
- u_int32_t mem_len;
- u_int16_t planer;
- u_int8_t revision_id;
- u_int8_t bus;
- u_int8_t func;
- u_int8_t irq;
- u_int16_t deviceID[2];
- u_int16_t subdevice_id;
- int i;
- int j;
- u_int32_t count;
- char *ioremap_ptr;
- char *mem_ptr;
- struct pci_dev *dev[2];
- struct pci_dev *morpheus = NULL;
- struct pci_dev *trombone = NULL;
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14)
- u_int32_t currbar;
- u_int32_t maskbar;
- u_int8_t barnum;
- #endif
- METHOD_TRACE("ips_detect", 1);
- #ifdef MODULE
- if (ips)
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
- ips_setup(ips);
- #else
- ips_setup(ips, NULL);
- #endif
- #endif
- /* If Booting from the ServeRAID Manager CD, Allocate a large Flash */
- /* Buffer ( so we won't need to allocate one for each adapter ). */
- if ( ips_cd_boot ) {
- ips_FlashData = ( char * ) __get_free_pages( GFP_KERNEL, 7 );
- if (ips_FlashData == NULL) {
- /* The validity of this pointer is checked in ips_make_passthru() before it is used */
- printk( KERN_WARNING "ERROR: Can't Allocate Large Buffer for Flashingn" );
- }
- }
- SHT->proc_info = ips_proc_info;
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
- SHT->proc_dir = &proc_scsi_ips;
- #else
- SHT->proc_name = "ips";
- #endif
- #if defined(CONFIG_PCI)
- /* initalize number of controllers */
- ips_num_controllers = 0;
- ips_next_controller = 0;
- ips_released_controllers = 0;
- if (!pci_present())
- return (0);
- morpheus = pci_find_device(IPS_VENDORID, IPS_DEVICEID_MORPHEUS, morpheus);
- trombone = pci_find_device(IPS_VENDORID, IPS_DEVICEID_COPPERHEAD, trombone);
- /* determine which controller to probe first */
- if (!morpheus) {
- /* we only have trombone */
- dev[0] = trombone;
- dev[1] = NULL;
- deviceID[0] = IPS_DEVICEID_COPPERHEAD;
- } else if (!trombone) {
- /* we only have morpheus */
- dev[0] = morpheus;
- dev[1] = NULL;
- deviceID[0] = IPS_DEVICEID_MORPHEUS;
- } else {
- /* we have both in the system */
- if (trombone->bus->number < morpheus->bus->number) {
- dev[0] = trombone;
- dev[1] = morpheus;
- deviceID[0] = IPS_DEVICEID_COPPERHEAD;
- deviceID[1] = IPS_DEVICEID_MORPHEUS;
- } else if (trombone->bus->number > morpheus->bus->number) {
- dev[0] = morpheus;
- dev[1] = trombone;
- deviceID[0] = IPS_DEVICEID_MORPHEUS;
- deviceID[1] = IPS_DEVICEID_COPPERHEAD;
- } else {
- /* further detection required */
- if (trombone->devfn < morpheus->devfn) {
- dev[0] = trombone;
- dev[1] = morpheus;
- deviceID[0] = IPS_DEVICEID_COPPERHEAD;
- deviceID[1] = IPS_DEVICEID_MORPHEUS;
- } else {
- dev[0] = morpheus;
- dev[1] = trombone;
- deviceID[0] = IPS_DEVICEID_MORPHEUS;
- deviceID[1] = IPS_DEVICEID_COPPERHEAD;
- }
- }
- }
- /* Now scan the controllers */
- for (i = 0; i < 2; i++) {
- if (!dev[i])
- break;
- do {
- if (ips_next_controller >= IPS_MAX_ADAPTERS)
- break;
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
- if (pci_enable_device(dev[i]))
- break;
- #endif
- /* stuff that we get in dev */
- irq = dev[i]->irq;
- bus = dev[i]->bus->number;
- func = dev[i]->devfn;
- /* Init MEM/IO addresses to 0 */
- mem_addr = 0;
- io_addr = 0;
- mem_len = 0;
- io_len = 0;
- for (j = 0; j < 2; j++) {
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
- if (!pci_resource_start(dev[i], j))
- break;
- if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) {
- io_addr = pci_resource_start(dev[i], j);
- io_len = pci_resource_len(dev[i], j);
- } else {
- mem_addr = pci_resource_start(dev[i], j);
- mem_len = pci_resource_len(dev[i], j);
- }
- #elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14)
- if (!dev[i]->resource[j].start)
- break;
- if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
- io_addr = dev[i]->resource[j].start;
- io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
- } else {
- mem_addr = dev[i]->resource[j].start;
- mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
- }
- #else
- if (!dev[i]->base_address[j])
- break;
- if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
- barnum = PCI_BASE_ADDRESS_0 + (j * 4);
- io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
- /* Get Size */
- pci_read_config_dword(dev[i], barnum, &currbar);
- pci_write_config_dword(dev[i], barnum, ~0);
- pci_read_config_dword(dev[i], barnum, &maskbar);
- pci_write_config_dword(dev[i], barnum, currbar);
- io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
- } else {
- barnum = PCI_BASE_ADDRESS_0 + (j * 4);
- mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
- /* Get Size */
- pci_read_config_dword(dev[i], barnum, &currbar);
- pci_write_config_dword(dev[i], barnum, ~0);
- pci_read_config_dword(dev[i], barnum, &maskbar);
- pci_write_config_dword(dev[i], barnum, currbar);
- mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
- }
- #endif
- }
- /* setup memory mapped area (if applicable) */
- if (mem_addr) {
- u_int32_t base;
- u_int32_t offs;
- DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
- ips_name, ips_next_controller, mem_addr, mem_len);
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
- if (check_mem_region(mem_addr, mem_len)) {
- /* Couldn't allocate io space */
- printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.n",
- ips_name, ips_next_controller, io_addr, io_len);
- ips_next_controller++;
- continue;
- }
- request_mem_region(mem_addr, mem_len, "ips");
- #endif
- base = mem_addr & PAGE_MASK;
- offs = mem_addr - base;
- ioremap_ptr = ioremap(base, PAGE_SIZE);
- mem_ptr = ioremap_ptr + offs;
- } else {
- ioremap_ptr = NULL;
- mem_ptr = NULL;
- }
- /* setup I/O mapped area (if applicable) */
- if (io_addr) {
- DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
- ips_name, ips_next_controller, io_addr, io_len);
- if (check_region(io_addr, io_len)) {
- /* Couldn't allocate io space */
- printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.n",
- ips_name, ips_next_controller, io_addr, io_len);
- ips_next_controller++;
- continue;
- }
- request_region(io_addr, io_len, "ips");
- }
- /* get planer status */
- if (pci_read_config_word(dev[i], 0x04, &planer)) {
- printk(KERN_WARNING "(%s%d) can't get planer status.n",
- ips_name, ips_next_controller);
- ips_next_controller++;
- continue;
- }
- /* check to see if an onboard planer controller is disabled */
- if (!(planer & 0x000C)) {
- DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS",
- ips_name, ips_next_controller);
- ips_next_controller++;
- continue;
- }
- DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %p",
- ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, mem_ptr);
- /* get the revision ID */
- if (pci_read_config_byte(dev[i], PCI_REVISION_ID, &revision_id)) {
- printk(KERN_WARNING "(%s%d) can't get revision id.n",
- ips_name, ips_next_controller);
- ips_next_controller++;
- continue;
- }
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,15)
- /* get the subdevice id */
- if (pci_read_config_word(dev[i], PCI_SUBSYSTEM_ID, &subdevice_id)) {
- printk(KERN_WARNING "(%s%d) can't get subdevice id.n",
- ips_name, ips_next_controller);
- ips_next_controller++;
- continue;
- }
- #else
- subdevice_id = dev[i]->subsystem_device;
- #endif
- /* found a controller */
- sh = scsi_register(SHT, sizeof(ips_ha_t));
- if (sh == NULL) {
- printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controllern",
- ips_name, ips_next_controller);
- ips_next_controller++;
- continue;
- }
- ha = IPS_HA(sh);
- memset(ha, 0, sizeof(ips_ha_t));
- /* Initialize spin lock */
- spin_lock_init(&ha->scb_lock);
- spin_lock_init(&ha->copp_lock);
- spin_lock_init(&ha->ips_lock);
- spin_lock_init(&ha->copp_waitlist.lock);
- spin_lock_init(&ha->scb_waitlist.lock);
- spin_lock_init(&ha->scb_activelist.lock);
- ips_sh[ips_next_controller] = sh;
- ips_ha[ips_next_controller] = ha;
- ips_num_controllers++;
- ha->active = 1;
- ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL);
- if (!ha->enq) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contollern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL);
- if (!ha->adapt) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL);
- if (!ha->conf) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL);
- if (!ha->nvram) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL);
- if (!ha->subsys) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL);
- if (!ha->dummy) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- for (count = PAGE_SIZE, ha->ioctl_order = 0;
- count < ips_ioctlsize;
- ha->ioctl_order++, count <<= 1);
- ha->ioctl_data = (char *) __get_free_pages(GFP_KERNEL, ha->ioctl_order);
- ha->ioctl_datasize = count;
- if (!ha->ioctl_data) {
- printk(KERN_WARNING "(%s%d) Unable to allocate ioctl datan",
- ips_name, ips_next_controller);
- ha->ioctl_data = NULL;
- ha->ioctl_order = 0;
- ha->ioctl_datasize = 0;
- }
- /* Store away needed values for later use */
- sh->io_port = io_addr;
- sh->n_io_port = io_addr ? 255 : 0;
- sh->unique_id = (io_addr) ? io_addr : mem_addr;
- sh->irq = irq;
- sh->select_queue_depths = ips_select_queue_depth;
- sh->sg_tablesize = sh->hostt->sg_tablesize;
- sh->can_queue = sh->hostt->can_queue;
- sh->cmd_per_lun = sh->hostt->cmd_per_lun;
- sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
- sh->use_clustering = sh->hostt->use_clustering;
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,7)
- sh->max_sectors = 128;
- #endif
- #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32)
- sh->wish_block = FALSE;
- #endif
- /* Store info in HA structure */
- ha->irq = irq;
- ha->io_addr = io_addr;
- ha->io_len = io_len;
- ha->mem_addr = mem_addr;
- ha->mem_len = mem_len;
- ha->mem_ptr = mem_ptr;
- ha->ioremap_ptr = ioremap_ptr;
- ha->host_num = ips_next_controller;
- ha->revision_id = revision_id;
- ha->slot_num = PCI_SLOT(dev[i]->devfn);
- ha->device_id = deviceID[i];
- ha->subdevice_id = subdevice_id;
- ha->pcidev = dev[i];
- /*
- * Setup Functions
- */
- if (IPS_IS_MORPHEUS(ha)) {
- /* morpheus */
- 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;
- }
- /*
- * Initialize the card if it isn't already
- */
- if (!(*ha->func.isinit)(ha)) {
- if (!(*ha->func.init)(ha)) {
- /*
- * Initialization failed
- */
- printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- }
- /* install the interrupt handler */
- if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
- printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controllern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- /*
- * Allocate a temporary SCB for initialization
- */
- ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_KERNEL);
- if (!ha->scbs) {
- /* couldn't allocate a temp SCB */
- printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contollern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- free_irq(ha->irq, ha);
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- memset(ha->scbs, 0, sizeof(ips_scb_t));
- ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL);
- if (!ha->scbs->sg_list) {
- /* couldn't allocate a temp SCB S/G list */
- printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contollern",
- ips_name, ips_next_controller);
- ha->active = 0;
- ips_free(ha);
- scsi_unregister(sh);
- ips_ha[ips_next_controller] = 0;
- ips_sh[ips_next_controller] = 0;
- free_irq(ha->irq, ha);
- ips_next_controller++;
- ips_num_controllers--;
- continue;
- }
- ha->max_cmds = 1;
- ips_next_controller++;
- } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i])));
- }
- /*
- * Do Phase 2 Initialization
- * Controller init
- */
- for (i = 0; i < ips_next_controller; i++) {
- if (ips_ha[i] == 0) {
- printk(KERN_WARNING "(%s%d) ignoring bad controllern",
- ips_name, i);
- continue;
- }
- ha = ips_ha[i];
- sh = ips_sh[i];
- if (!ha->active) {
- scsi_unregister(sh);
- ips_ha[i] = NULL;
- ips_sh[i] = NULL;
- continue;
- }
- if (!ips_hainit(ha)) {
- printk(KERN_WARNING "(%s%d) unable to initialize controller - skippingn",
- ips_name, i);
- ha->active = 0;
- ips_free(ha);
- free_irq(ha->irq, ha);
- scsi_unregister(sh);
- ips_ha[i] = NULL;
- ips_sh[i] = NULL;
- ips_num_controllers--;
- continue;
- }
- /*
- * Free the temporary SCB
- */
- kfree(ha->scbs->sg_list);
- kfree(ha->scbs);
- ha->scbs = NULL;
- /* allocate CCBs */
- if (!ips_allocatescbs(ha)) {
- printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contollern",
- ips_name, i);
- ha->active = 0;
- ips_free(ha);
- free_irq(ha->irq, ha);
- scsi_unregister(sh);
- ips_ha[i] = NULL;
- ips_sh[i] = NULL;
- ips_num_controllers--;
- continue;
- }
- /* finish setting values */
- sh->max_id = ha->ntargets;
- sh->max_lun = ha->nlun;
- sh->max_channel = ha->nbus - 1;
- sh->can_queue = ha->max_cmds-1;
- }
- if (ips_num_controllers > 0)
- register_reboot_notifier(&ips_notifier);
- return (ips_num_controllers);
- #else
- /* No PCI -- No ServeRAID */
- return (0);
- #endif /* CONFIG_PCI */
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_release */
- /* */
- /* Routine Description: */
- /* */
- /* Remove a driver */
- /* */
- /****************************************************************************/
- 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);
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
- BUG();
- #endif
- 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;
- printk(KERN_NOTICE "(%s%d) Flushing Cache.n", ips_name, ha->host_num);
- /* send command */
- if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
- printk(KERN_NOTICE "(%s%d) Incomplete Flush.n", ips_name, ha->host_num);
- printk(KERN_NOTICE "(%s%d) Flushing Complete.n", ips_name, ha->host_num);
- 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);
- scsi_unregister(sh);
- ips_released_controllers++;
- if (ips_num_controllers == ips_released_controllers)
- unregister_reboot_notifier(&ips_notifier);
- 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;
- printk(KERN_NOTICE "(%s%d) Flushing Cache.n", ips_name, ha->host_num);
- /* send command */
- if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
- printk(KERN_NOTICE "(%s%d) Incomplete Flush.n", ips_name, ha->host_num);
- else
- printk(KERN_NOTICE "(%s%d) Flushing Complete.n", ips_name, ha->host_num);
- }
- return (NOTIFY_OK);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_eh_abort */
- /* */
- /* Routine Description: */
- /* */
- /* Abort a command (using the new error code stuff) */
- /* */
- /****************************************************************************/
- int
- ips_eh_abort(Scsi_Cmnd *SC) {
- ips_ha_t *ha;
- ips_copp_wait_item_t *item;
- METHOD_TRACE("ips_eh_abort", 1);
- if (!SC)
- return (FAILED);
- ha = (ips_ha_t *) SC->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);
- }
- if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
- return (FAILED);
- /* See if the command is on the copp queue */
- IPS_QUEUE_LOCK(&ha->copp_waitlist);
- item = ha->copp_waitlist.head;
- while ((item) && (item->scsi_cmd != SC))
- item = item->next;
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
- if (item) {
- /* Found it */
- ips_removeq_copp(&ha->copp_waitlist, item);
- clear_bit(IPS_IN_ABORT, &ha->flags);
- return (SUCCESS);
- }
- /* See if the command is on the wait queue */
- if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
- /* command not sent yet */
- clear_bit(IPS_IN_ABORT, &ha->flags);
- return (SUCCESS);
- } else {
- /* command must have already been sent */
- clear_bit(IPS_IN_ABORT, &ha->flags);
- return (FAILED);
- }
- }
- /****************************************************************************/
- /* */
- /* 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 */
- /* */
- /****************************************************************************/
- 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;
- unsigned long cpu_flags;
- 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->host->hostdata;
- if (!ha) {
- DEBUG(1, "Reset called with NULL ha struct");
- return (FAILED);
- }
- if (!ha->active)
- return (FAILED);
- if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
- return (FAILED);
- /* See if the command is on the copp queue */
- IPS_QUEUE_LOCK(&ha->copp_waitlist);
- item = ha->copp_waitlist.head;
- while ((item) && (item->scsi_cmd != SC))
- item = item->next;
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
- if (item) {
- /* Found it */
- ips_removeq_copp(&ha->copp_waitlist, item);
- clear_bit(IPS_IN_RESET, &ha->flags);
- return (SUCCESS);
- }
- /* See if the command is on the wait queue */
- if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
- /* command not sent yet */
- clear_bit(IPS_IN_RESET, &ha->flags);
- return (SUCCESS);
- }
- /*
- * command must have already been sent
- * reset the controller
- */
- printk(KERN_NOTICE "(%s%d) Resetting controller.n",
- ips_name, ha->host_num);
- ret = (*ha->func.reset)(ha);
- if (!ret) {
- Scsi_Cmnd *scsi_cmd;
- printk(KERN_NOTICE
- "(%s%d) Controller reset failed - controller now offline.n",
- ips_name, ha->host_num);
- /* Now fail all of the active commands */
- DEBUG_VAR(1, "(%s%d) Failing active commands",
- ips_name, ha->host_num);
- while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
- scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
- ips_freescb(ha, scb);
- }
- /* Now fail all of the pending commands */
- DEBUG_VAR(1, "(%s%d) Failing pending commands",
- ips_name, ha->host_num);
- while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
- scsi_cmd->result = DID_ERROR;
- scsi_cmd->scsi_done(scsi_cmd);
- }
- ha->active = FALSE;
- clear_bit(IPS_IN_RESET, &ha->flags);
- return (FAILED);
- }
- if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
- Scsi_Cmnd *scsi_cmd;
- printk(KERN_NOTICE
- "(%s%d) Controller reset failed - controller now offline.n",
- ips_name, ha->host_num);
- /* Now fail all of the active commands */
- DEBUG_VAR(1, "(%s%d) Failing active commands",
- ips_name, ha->host_num);
- while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
- scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
- ips_freescb(ha, scb);
- }
- /* Now fail all of the pending commands */
- DEBUG_VAR(1, "(%s%d) Failing pending commands",
- ips_name, ha->host_num);
- while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
- scsi_cmd->result = DID_ERROR << 16;
- scsi_cmd->scsi_done(scsi_cmd);
- }
- ha->active = FALSE;
- clear_bit(IPS_IN_RESET, &ha->flags);
- return (FAILED);
- }
- /* FFDC */
- if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) {
- struct timeval tv;
- do_gettimeofday(&tv);
- IPS_HA_LOCK(cpu_flags);
- ha->last_ffdc = tv.tv_sec;
- ha->reset_count++;
- IPS_HA_UNLOCK(cpu_flags);
- ips_ffdc_reset(ha, IPS_INTR_IORL);
- }
- /* Now fail all of the active commands */
- DEBUG_VAR(1, "(%s%d) Failing active commands",
- ips_name, ha->host_num);
- while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
- scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
- ips_freescb(ha, scb);
- }
- /* Reset DCDB active command bits */
- for (i = 1; i < ha->nbus; i++)
- ha->dcdb_active[i-1] = 0;
- /* Reset the number of active IOCTLs */
- IPS_HA_LOCK(cpu_flags);
- ha->num_ioctl = 0;
- IPS_HA_UNLOCK(cpu_flags);
- clear_bit(IPS_IN_RESET, &ha->flags);
- if (!test_bit(IPS_IN_INTR, &ha->flags)) {
- /*
- * Only execute the next command when
- * we are not being called from the
- * interrupt handler. The interrupt
- * handler wants to do this and since
- * interrupts are turned off here....
- */
- ips_next(ha, IPS_INTR_IORL);
- }
- return (SUCCESS);
- #endif /* NO_IPS_RESET */
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_queue */
- /* */
- /* Routine Description: */
- /* */
- /* Send a command to the controller */
- /* */
- /* NOTE: */
- /* Linux obtains io_request_lock before calling this function */
- /* */
- /****************************************************************************/
- int
- ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
- ips_ha_t *ha;
- unsigned long cpu_flags;
- METHOD_TRACE("ips_queue", 1);
- ha = (ips_ha_t *) SC->host->hostdata;
- if (!ha)
- return (1);
- if (!ha->active)
- return (DID_ERROR);
- #ifndef NO_IPS_CMDLINE
- if (ips_is_passthru(SC)) {
- IPS_QUEUE_LOCK(&ha->copp_waitlist);
- if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
- SC->result = DID_BUS_BUSY << 16;
- done(SC);
- return (0);
- } else {
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
- }
- } else {
- #endif
- IPS_QUEUE_LOCK(&ha->scb_waitlist);
- if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
- IPS_QUEUE_UNLOCK(&ha->scb_waitlist);
- SC->result = DID_BUS_BUSY << 16;
- done(SC);
- return (0);
- } else {
- IPS_QUEUE_UNLOCK(&ha->scb_waitlist);
- }
- #ifndef NO_IPS_CMDLINE
- }
- #endif
- SC->scsi_done = done;
- DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
- ips_name,
- ha->host_num,
- SC->cmnd[0],
- SC->channel,
- SC->target,
- SC->lun);
- /* Check for command to initiator IDs */
- if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) {
- SC->result = DID_NO_CONNECT << 16;
- done(SC);
- return (0);
- }
- #ifndef NO_IPS_CMDLINE
- if (ips_is_passthru(SC)) {
- ips_copp_wait_item_t *scratch;
- /* The IPS_IOCTL_NEW_COMMAND is only used to flash an adapter. This should */
- /* never happen when the adapter is active. Just in case, check here, and */
- /* reject the command if anything else is going on. */
- if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
- if (ha->scb_activelist.count != 0) {
- /* printk( KERN_WARNING "New IOCTL Cmd Return BUSY: %d Cmds Activen", */
- /* ha->scb_activelist.count ); */
- SC->result = DID_BUS_BUSY << 16;
- done(SC);
- return (0);
- }
- }
- /* allocate space for the scribble */
- scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC);
- if (!scratch) {
- SC->result = DID_ERROR << 16;
- done(SC);
- return (0);
- }
- scratch->scsi_cmd = SC;
- sema_init(&ha->ioctl_sem, 0);
- scratch->sem = &ha->ioctl_sem;
- scratch->next = NULL;
- ips_putq_copp_tail(&ha->copp_waitlist, scratch);
- }
- else
- #endif
- ips_putq_wait_tail(&ha->scb_waitlist, SC);
- IPS_HA_LOCK(cpu_flags);
- if ((!test_bit(IPS_IN_INTR, &ha->flags)) &&
- (!test_bit(IPS_IN_ABORT, &ha->flags)) &&
- (!test_bit(IPS_IN_RESET, &ha->flags))) {
- IPS_HA_UNLOCK(cpu_flags);
- ips_next(ha, IPS_INTR_IORL);
- } else {
- IPS_HA_UNLOCK(cpu_flags);
- }
- /*
- * If this request was a new style IOCTL wait
- * for it to finish.
- *
- * NOTE: we relinquished the lock above so this should
- * not cause contention problems
- */
- if (ips_is_passthru(SC) && SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
- char *user_area;
- char *kern_area;
- u_int32_t datasize;
- /* free io_request_lock */
- spin_unlock_irq(&io_request_lock);
- /* wait for the command to finish */
- down(&ha->ioctl_sem);
- /* reobtain the lock */
- spin_lock_irq(&io_request_lock);
- /* command finished -- copy back */
- user_area = *((char **) &SC->cmnd[4]);
- kern_area = ha->ioctl_data;
- datasize = *((u_int32_t *) &SC->cmnd[8]);
- if (datasize) {
- if (copy_to_user(user_area, kern_area, datasize) > 0) {
- DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data",
- ips_name, ha->host_num);
- SC->result = DID_ERROR << 16;
- }
- }
- SC->scsi_done(SC);
- }
- /* If We were using the CD Boot Flash Buffer, Restore the Old Values */
- if ( ips_FlashData == ha->ioctl_data ) {
- ha->ioctl_data = ha->save_ioctl_data;
- ha->ioctl_order = ha->save_ioctl_order;
- ha->ioctl_datasize = ha->save_ioctl_datasize;
- ips_FlashDataInUse = 0;
- }
- return (0);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_biosparam */
- /* */
- /* Routine Description: */
- /* */
- /* Set bios geometry for the controller */
- /* */
- /****************************************************************************/
- int
- ips_biosparam(Disk *disk, kdev_t dev, int geom[]) {
- ips_ha_t *ha;
- int heads;
- int sectors;
- int cylinders;
- METHOD_TRACE("ips_biosparam", 1);
- ha = (ips_ha_t *) disk->device->host->hostdata;
- if (!ha)
- /* ?!?! host adater info invalid */
- return (0);
- if (!ha->active)
- return (0);
- if (!ips_read_adapter_status(ha, IPS_INTR_ON))
- /* ?!?! Enquiry command failed */
- return (0);
- if ((disk->capacity > 0x400000) &&
- ((ha->enq->ucMiscFlag & 0x8) == 0)) {
- heads = IPS_NORM_HEADS;
- sectors = IPS_NORM_SECTORS;
- } else {
- heads = IPS_COMP_HEADS;
- sectors = IPS_COMP_SECTORS;
- }
- cylinders = disk->capacity / (heads * sectors);
- DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
- heads, sectors, cylinders);
- geom[0] = heads;
- geom[1] = sectors;
- geom[2] = cylinders;
- return (0);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_select_queue_depth */
- /* */
- /* Routine Description: */
- /* */
- /* Select queue depths for the devices on the contoller */
- /* */
- /****************************************************************************/
- static void
- ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
- Scsi_Device *device;
- ips_ha_t *ha;
- int count = 0;
- int min;
- ha = IPS_HA(host);
- min = ha->max_cmds / 4;
- for (device = scsi_devs; device; device = device->next) {
- if (device->host == host) {
- if ((device->channel == 0) && (device->type == 0))
- count++;
- }
- }
- for (device = scsi_devs; device; device = device->next) {
- if (device->host == host) {
- if ((device->channel == 0) && (device->type == 0)) {
- device->queue_depth = ( ha->max_cmds - 1 ) / count;
- if (device->queue_depth < min)
- device->queue_depth = min;
- }
- else {
- device->queue_depth = 2;
- }
- if (device->queue_depth < 2)
- device->queue_depth = 2;
- }
- }
- }
- /****************************************************************************/
- /* */
- /* Routine Name: do_ipsintr */
- /* */
- /* Routine Description: */
- /* */
- /* Wrapper for the interrupt handler */
- /* */
- /****************************************************************************/
- void
- do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
- ips_ha_t *ha;
- unsigned long cpu_flags;
- METHOD_TRACE("do_ipsintr", 2);
- ha = (ips_ha_t *) dev_id;
- spin_lock_irqsave(&io_request_lock, cpu_flags);
- if (test_and_set_bit(IPS_IN_INTR, &ha->flags)) {
- spin_unlock_irqrestore(&io_request_lock, cpu_flags);
- return ;
- }
- if (!ha) {
- clear_bit(IPS_IN_INTR, &ha->flags);
- spin_unlock_irqrestore(&io_request_lock, cpu_flags);
- return;
- }
- if (!ha->active) {
- clear_bit(IPS_IN_INTR, &ha->flags);
- spin_unlock_irqrestore(&io_request_lock, cpu_flags);
- return;
- }
- (*ha->func.intr)(ha);
- clear_bit(IPS_IN_INTR, &ha->flags);
- spin_unlock_irqrestore(&io_request_lock, cpu_flags);
- /* start the next command */
- ips_next(ha, IPS_INTR_ON);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_intr_copperhead */
- /* */
- /* Routine Description: */
- /* */
- /* Polling interrupt handler */
- /* */
- /* ASSUMES interrupts are disabled */
- /* */
- /****************************************************************************/
- void
- ips_intr_copperhead(ips_ha_t *ha) {
- ips_stat_t *sp;
- ips_scb_t *scb;
- IPS_STATUS cstatus;
- int intrstatus;
- unsigned long cpu_flags;
- METHOD_TRACE("ips_intr", 2);
- if (!ha)
- return;
- if (!ha->active)
- return;
- IPS_HA_LOCK(cpu_flags);
- intrstatus = (*ha->func.isintr)(ha);
- if (!intrstatus) {
- /*
- * Unexpected/Shared interrupt
- */
- IPS_HA_UNLOCK(cpu_flags);
- return;
- }
- while (TRUE) {
- sp = &ha->sp;
- intrstatus = (*ha->func.isintr)(ha);
- if (!intrstatus)
- break;
- else
- cstatus.value = (*ha->func.statupd)(ha);
- if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
- /* Spurious Interupt ? */
- continue;
- }
- ips_chkstatus(ha, &cstatus);
- scb = (ips_scb_t *) sp->scb_addr;
- /*
- * use the callback function to finish things up
- * NOTE: interrupts are OFF for this
- */
- IPS_HA_UNLOCK(cpu_flags);
- (*scb->callback) (ha, scb);
- IPS_HA_LOCK(cpu_flags);
- } /* end while */
- IPS_HA_UNLOCK(cpu_flags);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_intr_morpheus */
- /* */
- /* Routine Description: */
- /* */
- /* Polling interrupt handler */
- /* */
- /* ASSUMES interrupts are disabled */
- /* */
- /****************************************************************************/
- void
- ips_intr_morpheus(ips_ha_t *ha) {
- ips_stat_t *sp;
- ips_scb_t *scb;
- IPS_STATUS cstatus;
- int intrstatus;
- unsigned long cpu_flags;
- METHOD_TRACE("ips_intr_morpheus", 2);
- if (!ha)
- return;
- if (!ha->active)
- return;
- IPS_HA_LOCK(cpu_flags);
- intrstatus = (*ha->func.isintr)(ha);
- if (!intrstatus) {
- /*
- * Unexpected/Shared interrupt
- */
- IPS_HA_UNLOCK(cpu_flags);
- return;
- }
- while (TRUE) {
- sp = &ha->sp;
- intrstatus = (*ha->func.isintr)(ha);
- if (!intrstatus)
- break;
- else
- cstatus.value = (*ha->func.statupd)(ha);
- if (cstatus.value == 0xffffffff)
- /* No more to process */
- break;
- if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
- printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.n",
- ips_name, ha->host_num);
- continue;
- }
- ips_chkstatus(ha, &cstatus);
- scb = (ips_scb_t *) sp->scb_addr;
- /*
- * use the callback function to finish things up
- * NOTE: interrupts are OFF for this
- */
- IPS_HA_UNLOCK(cpu_flags);
- (*scb->callback) (ha, scb);
- IPS_HA_LOCK(cpu_flags);
- } /* end while */
- IPS_HA_UNLOCK(cpu_flags);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_info */
- /* */
- /* Routine Description: */
- /* */
- /* Return info about the driver */
- /* */
- /****************************************************************************/
- const char *
- ips_info(struct Scsi_Host *SH) {
- static char buffer[256];
- char *bp;
- ips_ha_t *ha;
- METHOD_TRACE("ips_info", 1);
- ha = IPS_HA(SH);
- if (!ha)
- return (NULL);
- bp = &buffer[0];
- memset(bp, 0, sizeof(buffer));
- sprintf(bp, "%s%s%s", "IBM PCI ServeRAID ", IPS_VERSION_HIGH, IPS_VERSION_LOW );
- if (ha->ad_type > 0 &&
- ha->ad_type <= MAX_ADAPTER_NAME) {
- strcat(bp, " <");
- strcat(bp, ips_adapter_name[ha->ad_type-1]);
- strcat(bp, ">");
- }
- return (bp);
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_proc_info */
- /* */
- /* Routine Description: */
- /* */
- /* The passthru interface for the driver */
- /* */
- /****************************************************************************/
- int
- ips_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int func) {
- int i;
- int ret;
- ips_ha_t *ha = NULL;
- METHOD_TRACE("ips_proc_info", 1);
- /* Find our host structure */
- for (i = 0; i < ips_next_controller; i++) {
- if (ips_sh[i]) {
- if (ips_sh[i]->host_no == hostno) {
- ha = (ips_ha_t *) ips_sh[i]->hostdata;
- break;
- }
- }
- }
- if (!ha)
- return (-EINVAL);
- if (func) {
- /* write */
- return (0);
- } else {
- /* read */
- if (start)
- *start = buffer;
- ret = ips_host_info(ha, buffer, offset, length);
- return (ret);
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Helper Functions */
- /*--------------------------------------------------------------------------*/
- #ifndef NO_IPS_CMDLINE
- /****************************************************************************/
- /* */
- /* Routine Name: ips_is_passthru */
- /* */
- /* Routine Description: */
- /* */
- /* Determine if the specified SCSI command is really a passthru command */
- /* */
- /****************************************************************************/
- static int
- ips_is_passthru(Scsi_Cmnd *SC) {
- METHOD_TRACE("ips_is_passthru", 1);
- if (!SC)
- return (0);
- if (((SC->cmnd[0] == IPS_IOCTL_COMMAND) || (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND)) &&
- (SC->channel == 0) &&
- (SC->target == IPS_ADAPTER_ID) &&
- (SC->lun == 0) &&
- (SC->request_bufflen) &&
- (!SC->use_sg) &&
- (((char *) SC->request_buffer)[0] == 'C') &&
- (((char *) SC->request_buffer)[1] == 'O') &&
- (((char *) SC->request_buffer)[2] == 'P') &&
- (((char *) SC->request_buffer)[3] == 'P')) {
- return (1);
- } else {
- return (0);
- }
- }
- /****************************************************************************/
- /* */
- /* Routine Name: ips_make_passthru */
- /* */
- /* Routine Description: */
- /* */
- /* Make a passthru command out of the info in the Scsi block */
- /* */
- /****************************************************************************/
- static int
- ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb, int intr) {
- IPS_NVRAM_P5 nvram;
- ips_passthru_t *pt;
- METHOD_TRACE("ips_make_passthru", 1);
- if (!SC->request_bufflen || !SC->request_buffer) {
- /* no data */
- DEBUG_VAR(1, "(%s%d) No passthru structure",
- ips_name, ha->host_num);
- return (IPS_FAILURE);
- }
- if (SC->request_bufflen < sizeof(ips_passthru_t)) {
- /* wrong size */
- DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
- ips_name, ha->host_num);
- return (IPS_FAILURE);
- }
- if ((((char *) SC->request_buffer)[0] != 'C') ||
- (((char *) SC->request_buffer)[1] != 'O') ||
- (((char *) SC->request_buffer)[2] != 'P') ||
- (((char *) SC->request_buffer)[3] != 'P')) {
- /* signature doesn't match */
- DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.",
- ips_name, ha->host_num);
- return (IPS_FAILURE);
- }
- pt = (ips_passthru_t *) SC->request_buffer;
- /*
- * Some notes about the passthru interface used
- *
- * IF the scsi op_code == 0x0d then we assume
- * that the data came along with/goes with the
- * packet we received from the sg driver. In this
- * case the CmdBSize field of the pt structure is
- * used for the size of the buffer.
- *
- * IF the scsi op_code == 0x81 then we assume that
- * we will need our own buffer and we will copy the
- * data to/from the user buffer passed in the scsi
- * command. The data address resides at offset 4
- * in the scsi command. The length of the data resides
- * at offset 8 in the scsi command.
- */
- switch (pt->CoppCmd) {
- case IPS_NUMCTRLS:
- memcpy(SC->request_buffer + sizeof(ips_passthru_t),
- &ips_num_controllers, sizeof(int));
- SC->result = DID_OK << 16;
- return (IPS_SUCCESS_IMM);
- case IPS_CTRLINFO:
- memcpy(SC->request_buffer + sizeof(ips_passthru_t),
- ha, sizeof(ips_ha_t));
- SC->result = DID_OK << 16;
- return (IPS_SUCCESS_IMM);
- case IPS_COPPUSRCMD:
- case IPS_COPPIOCCMD:
- if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
- if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) {
- /* wrong size */
- DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
- ips_name, ha->host_num);
- return (IPS_FAILURE);
- }
- if (ips_usrcmd(ha, pt, scb))
- return (IPS_SUCCESS);
- else
- return (IPS_FAILURE);
- } else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
- char *user_area;
- char *kern_area;
- u_int32_t datasize;
- if (SC->request_bufflen < (sizeof(ips_passthru_t))) {
- /* wrong size */
- DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
- ips_name, ha->host_num);
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- /* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can */
- /* avoid allocating a huge buffer per adapter ( which can fail ). */
- if ( (ips_FlashData) &&
- (pt->CmdBSize == IPS_IMAGE_SIZE) &&
- (ips_FlashDataInUse == 0) ) {
- ips_FlashDataInUse = 1;
- ha->save_ioctl_data = ha->ioctl_data;
- ha->save_ioctl_order = ha->ioctl_order;
- ha->save_ioctl_datasize = ha->ioctl_datasize;
- ha->ioctl_data = ips_FlashData;
- ha->ioctl_order = 7;
- ha->ioctl_datasize = IPS_IMAGE_SIZE;
- }
- if ((pt->CoppCP.cmd.nvram.op_code == IPS_CMD_RW_NVRAM_PAGE) &&
- (pt->CoppCP.cmd.nvram.page == 5) &&
- (pt->CoppCP.cmd.nvram.write == 0)) {
- datasize = *((u_int32_t *) &scb->scsi_cmd->cmnd[8]);
- if (datasize < sizeof(IPS_NVRAM_P5)) {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- ips_get_bios_version(ha, IPS_INTR_IORL);
- ips_create_nvrampage5(ha, &nvram);
- user_area = *((char **) &scb->scsi_cmd->cmnd[4]);
- kern_area = (char *) &nvram;
- datasize = *((u_int32_t *) &scb->scsi_cmd->cmnd[8]);
- if (datasize > sizeof(IPS_NVRAM_P5))
- datasize = sizeof(IPS_NVRAM_P5);
- /* Copy out the buffer */
- if (copy_to_user((void *) user_area, (void *) kern_area, datasize) > 0) {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (EFAULT);
- }
- pt->BasicStatus = 0x00;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_OK << 16;
- return (IPS_SUCCESS_IMM);
- }
- /*
- * IPSSEND flashing BIOS
- */
- if ((pt->CoppCP.cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW) &&
- (pt->CoppCP.cmd.flashfw.type == 1) &&
- (pt->CoppCP.cmd.flashfw.direction == 2) &&
- (ha->device_id == IPS_DEVICEID_COPPERHEAD)) {
- struct tq_struct task;
- IPS_FLASH_DATA flash_data;
- /* We only support one packet */
- if (pt->CoppCP.cmd.flashfw.total_packets != 1) {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- /* copy in the size/buffer ptr from the scsi command */
- memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4);
- memcpy(&pt->CmdBSize, &SC->cmnd[8], 4);
- if (pt->CmdBSize > le32_to_cpu(pt->CoppCP.cmd.flashfw.count)) {
- pt->CmdBSize = le32_to_cpu(pt->CoppCP.cmd.flashfw.count);
- } else {
- /* ERROR: Command/Buffer mismatch */
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- if ((!ha->func.programbios) ||
- (!ha->func.erasebios) ||
- (!ha->func.verifybios)) {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- /* must have a buffer */
- if ((!pt->CmdBSize) || (!pt->CmdBuffer)) {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- /* make sure buffer is big enough */
- if (pt->CmdBSize > ha->ioctl_datasize) {
- void *bigger_struct;
- u_int32_t count;
- u_int32_t order;
- /* try to allocate a bigger struct */
- for (count = PAGE_SIZE, order = 0;
- count < pt->CmdBSize;
- order++, count <<= 1);
- bigger_struct = (void *) __get_free_pages(GFP_ATOMIC, order);
- if (bigger_struct) {
- /* free the old memory */
- free_pages((unsigned long) ha->ioctl_data, ha->ioctl_order);
- /* use the new memory */
- ha->ioctl_data = (char *) bigger_struct;
- ha->ioctl_order = order;
- ha->ioctl_datasize = count;
- } else {
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- spin_unlock(&ha->ips_lock);
- return (IPS_FAILURE);
- }
- }
- /* copy in the buffer */
- if (copy_from_user(ha->ioctl_data, pt->CmdBuffer, pt->CmdBSize) > 0) {
- DEBUG_VAR(1, "(%s%d) flash bios failed - unable to copy user buffer",
- ips_name, ha->host_num);
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- flash_data.userbuffer = pt->CmdBuffer;
- flash_data.usersize = pt->CmdBSize;
- flash_data.kernbuffer = ha->ioctl_data;
- flash_data.kernsize = ha->ioctl_datasize;
- flash_data.offset = 0;
- flash_data.SC = (void *) SC;
- flash_data.pt = (void *) pt;
- flash_data.ha = (void *) ha;
- sema_init( &ha->flash_ioctl_sem, 0 );
- flash_data.sem = &ha->flash_ioctl_sem;
- task.sync = 0;
- task.routine = ips_scheduled_flash_bios;
- task.data = (void *) &flash_data;
- /* Unlock the master lock */
- spin_unlock_irq(&io_request_lock);
- queue_task(&task, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
-
- /* Wait for the flash to complete */
- down(&ha->flash_ioctl_sem);
- /* Obtain the master lock */
- spin_lock_irq(&io_request_lock);
- return (flash_data.retcode);
- }
- /*
- * IPSSEND flashing BIOS in sectioned mode
- */
- if ((pt->CoppCP.cmd.flashbios.op_code == IPS_CMD_RW_BIOSFW) &&
- (pt->CoppCP.cmd.flashbios.type == 1) &&
- (pt->CoppCP.cmd.flashbios.direction == 4) &&
- (ha->device_id == IPS_DEVICEID_COPPERHEAD)) {
- struct tq_struct task;
- IPS_FLASH_DATA flash_data;
- /* copy in the size/buffer ptr from the scsi command */
- memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4);
- memcpy(&pt->CmdBSize, &SC->cmnd[8], 4);
- if (pt->CmdBSize > le32_to_cpu(pt->CoppCP.cmd.flashbios.count)) {
- pt->CmdBSize = le32_to_cpu(pt->CoppCP.cmd.flashbios.count);
- } else {
- /* ERROR: Command/Buffer mismatch */
- pt->BasicStatus = 0x0B;
- pt->ExtendedStatus = 0x00;
- SC->result = DID_ERROR << 16;
- return (IPS_FAILURE);
- }
- /* Update the Card BIOS */
- if ((!ha->func.programbios) ||