| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*****************************************************************************/ | 
|  | 2 | /* ips.c -- driver for the Adaptec / IBM ServeRAID controller                */ | 
|  | 3 | /*                                                                           */ | 
|  | 4 | /* Written By: Keith Mitchell, IBM Corporation                               */ | 
|  | 5 | /*             Jack Hammer, Adaptec, Inc.                                    */ | 
|  | 6 | /*             David Jeffery, Adaptec, Inc.                                  */ | 
|  | 7 | /*                                                                           */ | 
|  | 8 | /* Copyright (C) 2000 IBM Corporation                                        */ | 
|  | 9 | /* Copyright (C) 2002,2003 Adaptec, Inc.                                     */ | 
|  | 10 | /*                                                                           */ | 
|  | 11 | /* This program is free software; you can redistribute it and/or modify      */ | 
|  | 12 | /* it under the terms of the GNU General Public License as published by      */ | 
|  | 13 | /* the Free Software Foundation; either version 2 of the License, or         */ | 
|  | 14 | /* (at your option) any later version.                                       */ | 
|  | 15 | /*                                                                           */ | 
|  | 16 | /* This program is distributed in the hope that it will be useful,           */ | 
|  | 17 | /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */ | 
|  | 18 | /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */ | 
|  | 19 | /* GNU General Public License for more details.                              */ | 
|  | 20 | /*                                                                           */ | 
|  | 21 | /* NO WARRANTY                                                               */ | 
|  | 22 | /* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        */ | 
|  | 23 | /* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      */ | 
|  | 24 | /* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      */ | 
|  | 25 | /* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    */ | 
|  | 26 | /* solely responsible for determining the appropriateness of using and       */ | 
|  | 27 | /* distributing the Program and assumes all risks associated with its        */ | 
|  | 28 | /* exercise of rights under this Agreement, including but not limited to     */ | 
|  | 29 | /* the risks and costs of program errors, damage to or loss of data,         */ | 
|  | 30 | /* programs or equipment, and unavailability or interruption of operations.  */ | 
|  | 31 | /*                                                                           */ | 
|  | 32 | /* DISCLAIMER OF LIABILITY                                                   */ | 
|  | 33 | /* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   */ | 
|  | 34 | /* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */ | 
|  | 35 | /* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   */ | 
|  | 36 | /* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     */ | 
|  | 37 | /* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    */ | 
|  | 38 | /* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  */ | 
|  | 39 | /* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             */ | 
|  | 40 | /*                                                                           */ | 
|  | 41 | /* You should have received a copy of the GNU General Public License         */ | 
|  | 42 | /* along with this program; if not, write to the Free Software               */ | 
|  | 43 | /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */ | 
|  | 44 | /*                                                                           */ | 
|  | 45 | /* Bugs/Comments/Suggestions about this driver should be mailed to:          */ | 
|  | 46 | /*      ipslinux@adaptec.com        	                                     */ | 
|  | 47 | /*                                                                           */ | 
|  | 48 | /* For system support issues, contact your local IBM Customer support.       */ | 
|  | 49 | /* Directions to find IBM Customer Support for each country can be found at: */ | 
|  | 50 | /*      http://www.ibm.com/planetwide/                                       */ | 
|  | 51 | /*                                                                           */ | 
|  | 52 | /*****************************************************************************/ | 
|  | 53 |  | 
|  | 54 | /*****************************************************************************/ | 
|  | 55 | /* Change Log                                                                */ | 
|  | 56 | /*                                                                           */ | 
|  | 57 | /* 0.99.02  - Breakup commands that are bigger than 8 * the stripe size      */ | 
|  | 58 | /* 0.99.03  - Make interrupt routine handle all completed request on the     */ | 
|  | 59 | /*            adapter not just the first one                                 */ | 
|  | 60 | /*          - Make sure passthru commands get woken up if we run out of      */ | 
|  | 61 | /*            SCBs                                                           */ | 
|  | 62 | /*          - Send all of the commands on the queue at once rather than      */ | 
|  | 63 | /*            one at a time since the card will support it.                  */ | 
|  | 64 | /* 0.99.04  - Fix race condition in the passthru mechanism -- this required  */ | 
|  | 65 | /*            the interface to the utilities to change                       */ | 
|  | 66 | /*          - Fix error recovery code                                        */ | 
|  | 67 | /* 0.99.05  - Fix an oops when we get certain passthru commands              */ | 
|  | 68 | /* 1.00.00  - Initial Public Release                                         */ | 
|  | 69 | /*            Functionally equivalent to 0.99.05                             */ | 
|  | 70 | /* 3.60.00  - Bump max commands to 128 for use with firmware 3.60            */ | 
|  | 71 | /*          - Change version to 3.60 to coincide with release numbering.     */ | 
|  | 72 | /* 3.60.01  - Remove bogus error check in passthru routine                   */ | 
|  | 73 | /* 3.60.02  - Make DCDB direction based on lookup table                      */ | 
|  | 74 | /*          - Only allow one DCDB command to a SCSI ID at a time             */ | 
|  | 75 | /* 4.00.00  - Add support for ServeRAID 4                                    */ | 
|  | 76 | /* 4.00.01  - Add support for First Failure Data Capture                     */ | 
|  | 77 | /* 4.00.02  - Fix problem with PT DCDB with no buffer                        */ | 
|  | 78 | /* 4.00.03  - Add alternative passthru interface                             */ | 
|  | 79 | /*          - Add ability to flash BIOS                                      */ | 
|  | 80 | /* 4.00.04  - Rename structures/constants to be prefixed with IPS_           */ | 
|  | 81 | /* 4.00.05  - Remove wish_block from init routine                            */ | 
|  | 82 | /*          - Use linux/spinlock.h instead of asm/spinlock.h for kernels     */ | 
|  | 83 | /*            2.3.18 and later                                               */ | 
|  | 84 | /*          - Sync with other changes from the 2.3 kernels                   */ | 
|  | 85 | /* 4.00.06  - Fix timeout with initial FFDC command                          */ | 
|  | 86 | /* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@infradead.org> */ | 
|  | 87 | /* 4.10.00  - Add support for ServeRAID 4M/4L                                */ | 
|  | 88 | /* 4.10.13  - Fix for dynamic unload and proc file system                    */ | 
|  | 89 | /* 4.20.03  - Rename version to coincide with new release schedules          */ | 
|  | 90 | /*            Performance fixes                                              */ | 
|  | 91 | /*            Fix truncation of /proc files with cat                         */ | 
|  | 92 | /*            Merge in changes through kernel 2.4.0test1ac21                 */ | 
|  | 93 | /* 4.20.13  - Fix some failure cases / reset code                            */ | 
|  | 94 | /*          - Hook into the reboot_notifier to flush the controller cache    */ | 
|  | 95 | /* 4.50.01  - Fix problem when there is a hole in logical drive numbering    */ | 
|  | 96 | /* 4.70.09  - Use a Common ( Large Buffer ) for Flashing from the JCRM CD    */ | 
|  | 97 | /*          - Add IPSSEND Flash Support                                      */ | 
|  | 98 | /*          - Set Sense Data for Unknown SCSI Command                        */ | 
|  | 99 | /*          - Use Slot Number from NVRAM Page 5                              */ | 
|  | 100 | /*          - Restore caller's DCDB Structure                                */ | 
|  | 101 | /* 4.70.12  - Corrective actions for bad controller ( during initialization )*/ | 
|  | 102 | /* 4.70.13  - Don't Send CDB's if we already know the device is not present  */ | 
|  | 103 | /*          - Don't release HA Lock in ips_next() until SC taken off queue   */ | 
|  | 104 | /*          - Unregister SCSI device in ips_release()                        */ | 
|  | 105 | /* 4.70.15  - Fix Breakup for very large ( non-SG ) requests in ips_done()   */ | 
|  | 106 | /* 4.71.00  - Change all memory allocations to not use GFP_DMA flag          */ | 
|  | 107 | /*            Code Clean-Up for 2.4.x kernel                                 */ | 
|  | 108 | /* 4.72.00  - Allow for a Scatter-Gather Element to exceed MAX_XFER Size     */ | 
|  | 109 | /* 4.72.01  - I/O Mapped Memory release ( so "insmod ips" does not Fail )    */ | 
|  | 110 | /*          - Don't Issue Internal FFDC Command if there are Active Commands */ | 
|  | 111 | /*          - Close Window for getting too many IOCTL's active               */ | 
|  | 112 | /* 4.80.00  - Make ia64 Safe                                                 */ | 
|  | 113 | /* 4.80.04  - Eliminate calls to strtok() if 2.4.x or greater                */ | 
|  | 114 | /*          - Adjustments to Device Queue Depth                              */ | 
|  | 115 | /* 4.80.14  - Take all semaphores off stack                                  */ | 
|  | 116 | /*          - Clean Up New_IOCTL path                                        */ | 
|  | 117 | /* 4.80.20  - Set max_sectors in Scsi_Host structure ( if >= 2.4.7 kernel )  */ | 
|  | 118 | /*          - 5 second delay needed after resetting an i960 adapter          */ | 
|  | 119 | /* 4.80.26  - Clean up potential code problems ( Arjan's recommendations )   */ | 
|  | 120 | /* 4.90.01  - Version Matching for FirmWare, BIOS, and Driver                */ | 
|  | 121 | /* 4.90.05  - Use New PCI Architecture to facilitate Hot Plug Development    */ | 
|  | 122 | /* 4.90.08  - Increase Delays in Flashing ( Trombone Only - 4H )             */ | 
|  | 123 | /* 4.90.08  - Data Corruption if First Scatter Gather Element is > 64K       */ | 
|  | 124 | /* 4.90.11  - Don't actually RESET unless it's physically required           */ | 
|  | 125 | /*          - Remove unused compile options                                  */ | 
|  | 126 | /* 5.00.01  - Sarasota ( 5i ) adapters must always be scanned first          */ | 
|  | 127 | /*          - Get rid on IOCTL_NEW_COMMAND code                              */ | 
|  | 128 | /*          - Add Extended DCDB Commands for Tape Support in 5I              */ | 
|  | 129 | /* 5.10.12  - use pci_dma interfaces, update for 2.5 kernel changes          */ | 
|  | 130 | /* 5.10.15  - remove unused code (sem, macros, etc.)                         */ | 
|  | 131 | /* 5.30.00  - use __devexit_p()                                              */ | 
|  | 132 | /* 6.00.00  - Add 6x Adapters and Battery Flash                              */ | 
|  | 133 | /* 6.10.00  - Remove 1G Addressing Limitations                               */ | 
|  | 134 | /* 6.11.xx  - Get VersionInfo buffer off the stack !              DDTS 60401 */ | 
|  | 135 | /* 6.11.xx  - Make Logical Drive Info structure safe for DMA      DDTS 60639 */ | 
| Jack Hammer | c1a1546 | 2005-07-26 10:20:33 -0400 | [diff] [blame] | 136 | /* 7.10.18  - Add highmem_io flag in SCSI Templete for 2.4 kernels           */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | /*          - Fix path/name for scsi_hosts.h include for 2.6 kernels         */ | 
|  | 138 | /*          - Fix sort order of 7k                                           */ | 
|  | 139 | /*          - Remove 3 unused "inline" functions                             */ | 
| Jack Hammer | c1a1546 | 2005-07-26 10:20:33 -0400 | [diff] [blame] | 140 | /* 7.12.xx  - Use STATIC functions whereever possible                        */ | 
|  | 141 | /*          - Clean up deprecated MODULE_PARM calls                          */ | 
| Jack Hammer | a60768e | 2005-11-03 09:46:00 -0500 | [diff] [blame] | 142 | /* 7.12.05  - Remove Version Matching per IBM request                        */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | /*****************************************************************************/ | 
|  | 144 |  | 
|  | 145 | /* | 
|  | 146 | * Conditional Compilation directives for this driver: | 
|  | 147 | * | 
|  | 148 | * IPS_DEBUG            - Turn on debugging info | 
|  | 149 | * | 
|  | 150 | * Parameters: | 
|  | 151 | * | 
|  | 152 | * debug:<number>       - Set debug level to <number> | 
|  | 153 | *                        NOTE: only works when IPS_DEBUG compile directive is used. | 
|  | 154 | *       1              - Normal debug messages | 
|  | 155 | *       2              - Verbose debug messages | 
|  | 156 | *       11             - Method trace (non interrupt) | 
|  | 157 | *       12             - Method trace (includes interrupt) | 
|  | 158 | * | 
|  | 159 | * noi2o                - Don't use I2O Queues (ServeRAID 4 only) | 
|  | 160 | * nommap               - Don't use memory mapped I/O | 
|  | 161 | * ioctlsize            - Initial size of the IOCTL buffer | 
|  | 162 | */ | 
|  | 163 |  | 
|  | 164 | #include <asm/io.h> | 
|  | 165 | #include <asm/byteorder.h> | 
|  | 166 | #include <asm/page.h> | 
|  | 167 | #include <linux/stddef.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | #include <linux/string.h> | 
|  | 169 | #include <linux/errno.h> | 
|  | 170 | #include <linux/kernel.h> | 
|  | 171 | #include <linux/ioport.h> | 
|  | 172 | #include <linux/slab.h> | 
|  | 173 | #include <linux/delay.h> | 
|  | 174 | #include <linux/pci.h> | 
|  | 175 | #include <linux/proc_fs.h> | 
|  | 176 | #include <linux/reboot.h> | 
|  | 177 | #include <linux/interrupt.h> | 
|  | 178 |  | 
|  | 179 | #include <linux/blkdev.h> | 
|  | 180 | #include <linux/types.h> | 
| Matthias Gehre | 910638a | 2006-03-28 01:56:48 -0800 | [diff] [blame] | 181 | #include <linux/dma-mapping.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 |  | 
|  | 183 | #include <scsi/sg.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | #include "scsi.h" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 185 | #include <scsi/scsi_host.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 |  | 
|  | 187 | #include "ips.h" | 
|  | 188 |  | 
|  | 189 | #include <linux/module.h> | 
|  | 190 |  | 
|  | 191 | #include <linux/stat.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 |  | 
|  | 193 | #include <linux/spinlock.h> | 
|  | 194 | #include <linux/init.h> | 
|  | 195 |  | 
|  | 196 | #include <linux/smp.h> | 
|  | 197 |  | 
|  | 198 | #ifdef MODULE | 
|  | 199 | static char *ips = NULL; | 
|  | 200 | module_param(ips, charp, 0); | 
|  | 201 | #endif | 
|  | 202 |  | 
|  | 203 | /* | 
|  | 204 | * DRIVER_VER | 
|  | 205 | */ | 
| Bernhard Walle | 8c8fdc5 | 2007-09-22 21:55:19 +0200 | [diff] [blame] | 206 | #define IPS_VERSION_HIGH        IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING | 
|  | 207 | #define IPS_VERSION_LOW         "." IPS_VER_BUILD_STRING " " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 208 |  | 
|  | 209 | #if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__) | 
|  | 210 | #warning "This driver has only been tested on the x86/ia64/x86_64 platforms" | 
|  | 211 | #endif | 
|  | 212 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 | #define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \ | 
|  | be7db05 | 2005-04-17 15:26:13 -0500 | [diff] [blame] | 214 | DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | PCI_DMA_BIDIRECTIONAL : \ | 
|  | be7db05 | 2005-04-17 15:26:13 -0500 | [diff] [blame] | 216 | scb->scsi_cmd->sc_data_direction) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 |  | 
|  | 218 | #ifdef IPS_DEBUG | 
|  | 219 | #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n"); | 
|  | 220 | #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n"); | 
|  | 221 | #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v); | 
|  | 222 | #else | 
|  | 223 | #define METHOD_TRACE(s, i) | 
|  | 224 | #define DEBUG(i, s) | 
|  | 225 | #define DEBUG_VAR(i, s, v...) | 
|  | 226 | #endif | 
|  | 227 |  | 
|  | 228 | /* | 
|  | 229 | * Function prototypes | 
|  | 230 | */ | 
| Christoph Hellwig | d0be4a7d | 2005-10-31 18:31:40 +0100 | [diff] [blame] | 231 | static int ips_detect(struct scsi_host_template *); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | static int ips_release(struct Scsi_Host *); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 233 | static int ips_eh_abort(struct scsi_cmnd *); | 
|  | 234 | static int ips_eh_reset(struct scsi_cmnd *); | 
|  | 235 | static int ips_queue(struct scsi_cmnd *, void (*)(struct scsi_cmnd *)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | static const char *ips_info(struct Scsi_Host *); | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 237 | static irqreturn_t do_ipsintr(int, void *); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 | static int ips_hainit(ips_ha_t *); | 
|  | 239 | static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *); | 
|  | 240 | static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int); | 
|  | 241 | static int ips_send_cmd(ips_ha_t *, ips_scb_t *); | 
|  | 242 | static int ips_online(ips_ha_t *, ips_scb_t *); | 
|  | 243 | static int ips_inquiry(ips_ha_t *, ips_scb_t *); | 
|  | 244 | static int ips_rdcap(ips_ha_t *, ips_scb_t *); | 
|  | 245 | static int ips_msense(ips_ha_t *, ips_scb_t *); | 
|  | 246 | static int ips_reqsen(ips_ha_t *, ips_scb_t *); | 
|  | 247 | static int ips_deallocatescbs(ips_ha_t *, int); | 
|  | 248 | static int ips_allocatescbs(ips_ha_t *); | 
|  | 249 | static int ips_reset_copperhead(ips_ha_t *); | 
|  | 250 | static int ips_reset_copperhead_memio(ips_ha_t *); | 
|  | 251 | static int ips_reset_morpheus(ips_ha_t *); | 
|  | 252 | static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *); | 
|  | 253 | static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *); | 
|  | 254 | static int ips_issue_i2o(ips_ha_t *, ips_scb_t *); | 
|  | 255 | static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *); | 
|  | 256 | static int ips_isintr_copperhead(ips_ha_t *); | 
|  | 257 | static int ips_isintr_copperhead_memio(ips_ha_t *); | 
|  | 258 | static int ips_isintr_morpheus(ips_ha_t *); | 
|  | 259 | static int ips_wait(ips_ha_t *, int, int); | 
|  | 260 | static int ips_write_driver_status(ips_ha_t *, int); | 
|  | 261 | static int ips_read_adapter_status(ips_ha_t *, int); | 
|  | 262 | static int ips_read_subsystem_parameters(ips_ha_t *, int); | 
|  | 263 | static int ips_read_config(ips_ha_t *, int); | 
|  | 264 | static int ips_clear_adapter(ips_ha_t *, int); | 
|  | 265 | static int ips_readwrite_page5(ips_ha_t *, int, int); | 
|  | 266 | static int ips_init_copperhead(ips_ha_t *); | 
|  | 267 | static int ips_init_copperhead_memio(ips_ha_t *); | 
|  | 268 | static int ips_init_morpheus(ips_ha_t *); | 
|  | 269 | static int ips_isinit_copperhead(ips_ha_t *); | 
|  | 270 | static int ips_isinit_copperhead_memio(ips_ha_t *); | 
|  | 271 | static int ips_isinit_morpheus(ips_ha_t *); | 
|  | 272 | static int ips_erase_bios(ips_ha_t *); | 
|  | 273 | static int ips_program_bios(ips_ha_t *, char *, uint32_t, uint32_t); | 
|  | 274 | static int ips_verify_bios(ips_ha_t *, char *, uint32_t, uint32_t); | 
|  | 275 | static int ips_erase_bios_memio(ips_ha_t *); | 
|  | 276 | static int ips_program_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t); | 
|  | 277 | static int ips_verify_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t); | 
|  | 278 | static int ips_flash_copperhead(ips_ha_t *, ips_passthru_t *, ips_scb_t *); | 
|  | 279 | static int ips_flash_bios(ips_ha_t *, ips_passthru_t *, ips_scb_t *); | 
|  | 280 | static int ips_flash_firmware(ips_ha_t *, ips_passthru_t *, ips_scb_t *); | 
|  | 281 | static void ips_free_flash_copperhead(ips_ha_t * ha); | 
|  | 282 | static void ips_get_bios_version(ips_ha_t *, int); | 
|  | 283 | static void ips_identify_controller(ips_ha_t *); | 
|  | 284 | static void ips_chkstatus(ips_ha_t *, IPS_STATUS *); | 
|  | 285 | static void ips_enable_int_copperhead(ips_ha_t *); | 
|  | 286 | static void ips_enable_int_copperhead_memio(ips_ha_t *); | 
|  | 287 | static void ips_enable_int_morpheus(ips_ha_t *); | 
|  | 288 | static int ips_intr_copperhead(ips_ha_t *); | 
|  | 289 | static int ips_intr_morpheus(ips_ha_t *); | 
|  | 290 | static void ips_next(ips_ha_t *, int); | 
|  | 291 | static void ipsintr_blocking(ips_ha_t *, struct ips_scb *); | 
|  | 292 | static void ipsintr_done(ips_ha_t *, struct ips_scb *); | 
|  | 293 | static void ips_done(ips_ha_t *, ips_scb_t *); | 
|  | 294 | static void ips_free(ips_ha_t *); | 
|  | 295 | static void ips_init_scb(ips_ha_t *, ips_scb_t *); | 
|  | 296 | static void ips_freescb(ips_ha_t *, ips_scb_t *); | 
|  | 297 | static void ips_setup_funclist(ips_ha_t *); | 
|  | 298 | static void ips_statinit(ips_ha_t *); | 
|  | 299 | static void ips_statinit_memio(ips_ha_t *); | 
|  | 300 | static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t); | 
|  | 301 | static void ips_ffdc_reset(ips_ha_t *, int); | 
|  | 302 | static void ips_ffdc_time(ips_ha_t *); | 
|  | 303 | static uint32_t ips_statupd_copperhead(ips_ha_t *); | 
|  | 304 | static uint32_t ips_statupd_copperhead_memio(ips_ha_t *); | 
|  | 305 | static uint32_t ips_statupd_morpheus(ips_ha_t *); | 
|  | 306 | static ips_scb_t *ips_getscb(ips_ha_t *); | 
|  | 307 | static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 308 | static void ips_putq_wait_tail(ips_wait_queue_t *, struct scsi_cmnd *); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 309 | static void ips_putq_copp_tail(ips_copp_queue_t *, | 
|  | 310 | ips_copp_wait_item_t *); | 
|  | 311 | static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *); | 
|  | 312 | static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 313 | static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *); | 
|  | 314 | static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *, | 
|  | 315 | struct scsi_cmnd *); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 | static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *, | 
|  | 317 | ips_copp_wait_item_t *); | 
|  | 318 | static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *); | 
|  | 319 |  | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 320 | static int ips_is_passthru(struct scsi_cmnd *); | 
|  | 321 | static int ips_make_passthru(ips_ha_t *, struct scsi_cmnd *, ips_scb_t *, int); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 322 | static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *); | 
|  | 323 | static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 324 | static void ips_scmd_buf_write(struct scsi_cmnd * scmd, void *data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | unsigned int count); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 326 | static void ips_scmd_buf_read(struct scsi_cmnd * scmd, void *data, | 
|  | 327 | unsigned int count); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 |  | 
|  | 329 | static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); | 
|  | 330 | static int ips_host_info(ips_ha_t *, char *, off_t, int); | 
|  | 331 | static void copy_mem_info(IPS_INFOSTR *, char *, int); | 
|  | 332 | static int copy_info(IPS_INFOSTR *, char *, ...); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 333 | static int ips_abort_init(ips_ha_t * ha, int index); | 
|  | 334 | static int ips_init_phase2(int index); | 
|  | 335 |  | 
|  | 336 | static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr); | 
|  | 337 | static int ips_register_scsi(int index); | 
|  | 338 |  | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 339 | static int  ips_poll_for_flush_complete(ips_ha_t * ha); | 
|  | 340 | static void ips_flush_and_reset(ips_ha_t *ha); | 
|  | 341 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 342 | /* | 
|  | 343 | * global variables | 
|  | 344 | */ | 
|  | 345 | static const char ips_name[] = "ips"; | 
|  | 346 | static struct Scsi_Host *ips_sh[IPS_MAX_ADAPTERS];	/* Array of host controller structures */ | 
|  | 347 | static ips_ha_t *ips_ha[IPS_MAX_ADAPTERS];	/* Array of HA structures */ | 
|  | 348 | static unsigned int ips_next_controller; | 
|  | 349 | static unsigned int ips_num_controllers; | 
|  | 350 | static unsigned int ips_released_controllers; | 
|  | 351 | static int ips_hotplug; | 
|  | 352 | static int ips_cmd_timeout = 60; | 
|  | 353 | static int ips_reset_timeout = 60 * 5; | 
|  | 354 | static int ips_force_memio = 1;		/* Always use Memory Mapped I/O    */ | 
|  | 355 | static int ips_force_i2o = 1;	/* Always use I2O command delivery */ | 
|  | 356 | static int ips_ioctlsize = IPS_IOCTL_SIZE;	/* Size of the ioctl buffer        */ | 
|  | 357 | static int ips_cd_boot;			/* Booting from Manager CD         */ | 
|  | 358 | static char *ips_FlashData = NULL;	/* CD Boot - Flash Data Buffer      */ | 
|  | 359 | static dma_addr_t ips_flashbusaddr; | 
|  | 360 | static long ips_FlashDataInUse;		/* CD Boot - Flash Data In Use Flag */ | 
|  | 361 | static uint32_t MaxLiteCmds = 32;	/* Max Active Cmds for a Lite Adapter */ | 
| Christoph Hellwig | d0be4a7d | 2005-10-31 18:31:40 +0100 | [diff] [blame] | 362 | static struct scsi_host_template ips_driver_template = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 363 | .detect			= ips_detect, | 
|  | 364 | .release		= ips_release, | 
|  | 365 | .info			= ips_info, | 
|  | 366 | .queuecommand		= ips_queue, | 
|  | 367 | .eh_abort_handler	= ips_eh_abort, | 
|  | 368 | .eh_host_reset_handler	= ips_eh_reset, | 
|  | 369 | .proc_name		= "ips", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 370 | .proc_info		= ips_proc_info, | 
|  | 371 | .slave_configure	= ips_slave_configure, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 372 | .bios_param		= ips_biosparam, | 
|  | 373 | .this_id		= -1, | 
|  | 374 | .sg_tablesize		= IPS_MAX_SG, | 
|  | 375 | .cmd_per_lun		= 3, | 
|  | 376 | .use_clustering		= ENABLE_CLUSTERING, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 377 | }; | 
|  | 378 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 379 |  | 
|  | 380 | /* This table describes all ServeRAID Adapters */ | 
|  | 381 | static struct  pci_device_id  ips_pci_table[] = { | 
|  | 382 | { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, | 
|  | 383 | { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, | 
|  | 384 | { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, | 
|  | 385 | { 0, } | 
|  | 386 | }; | 
|  | 387 |  | 
|  | 388 | MODULE_DEVICE_TABLE( pci, ips_pci_table ); | 
|  | 389 |  | 
|  | 390 | static char ips_hot_plug_name[] = "ips"; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 391 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 392 | static int __devinit  ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent); | 
|  | 393 | static void __devexit ips_remove_device(struct pci_dev *pci_dev); | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 394 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | static struct pci_driver ips_pci_driver = { | 
|  | 396 | .name		= ips_hot_plug_name, | 
|  | 397 | .id_table	= ips_pci_table, | 
|  | 398 | .probe		= ips_insert_device, | 
|  | 399 | .remove		= __devexit_p(ips_remove_device), | 
|  | 400 | }; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 401 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 402 |  | 
|  | 403 | /* | 
|  | 404 | * Necessary forward function protoypes | 
|  | 405 | */ | 
|  | 406 | static int ips_halt(struct notifier_block *nb, ulong event, void *buf); | 
|  | 407 |  | 
|  | 408 | #define MAX_ADAPTER_NAME 15 | 
|  | 409 |  | 
|  | 410 | static char ips_adapter_name[][30] = { | 
|  | 411 | "ServeRAID", | 
|  | 412 | "ServeRAID II", | 
|  | 413 | "ServeRAID on motherboard", | 
|  | 414 | "ServeRAID on motherboard", | 
|  | 415 | "ServeRAID 3H", | 
|  | 416 | "ServeRAID 3L", | 
|  | 417 | "ServeRAID 4H", | 
|  | 418 | "ServeRAID 4M", | 
|  | 419 | "ServeRAID 4L", | 
|  | 420 | "ServeRAID 4Mx", | 
|  | 421 | "ServeRAID 4Lx", | 
|  | 422 | "ServeRAID 5i", | 
|  | 423 | "ServeRAID 5i", | 
|  | 424 | "ServeRAID 6M", | 
|  | 425 | "ServeRAID 6i", | 
|  | 426 | "ServeRAID 7t", | 
|  | 427 | "ServeRAID 7k", | 
|  | 428 | "ServeRAID 7M" | 
|  | 429 | }; | 
|  | 430 |  | 
|  | 431 | static struct notifier_block ips_notifier = { | 
|  | 432 | ips_halt, NULL, 0 | 
|  | 433 | }; | 
|  | 434 |  | 
|  | 435 | /* | 
|  | 436 | * Direction table | 
|  | 437 | */ | 
|  | 438 | static char ips_command_direction[] = { | 
|  | 439 | IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, | 
|  | 440 | IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, | 
|  | 441 | IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 442 | IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, | 
|  | 443 | IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT, | 
|  | 444 | IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, | 
|  | 445 | IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN, | 
|  | 446 | IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 447 | IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, | 
|  | 448 | IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, | 
|  | 449 | IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, | 
|  | 450 | IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, | 
|  | 451 | IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, | 
|  | 452 | IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE, | 
|  | 453 | IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, | 
|  | 454 | IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 455 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 456 | IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 457 | IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 458 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 459 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 460 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 461 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 462 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 463 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 464 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 465 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 466 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 467 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 468 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 469 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 470 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 471 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 472 | IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE, | 
|  | 473 | IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT, | 
|  | 474 | IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE, | 
|  | 475 | IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, | 
|  | 476 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 477 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 478 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 479 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 480 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 481 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 482 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 483 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 484 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 485 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT, | 
|  | 486 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 487 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 488 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, | 
|  | 489 | IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK | 
|  | 490 | }; | 
|  | 491 |  | 
|  | 492 |  | 
|  | 493 | /****************************************************************************/ | 
|  | 494 | /*                                                                          */ | 
|  | 495 | /* Routine Name: ips_setup                                                  */ | 
|  | 496 | /*                                                                          */ | 
|  | 497 | /* Routine Description:                                                     */ | 
|  | 498 | /*                                                                          */ | 
|  | 499 | /*   setup parameters to the driver                                         */ | 
|  | 500 | /*                                                                          */ | 
|  | 501 | /****************************************************************************/ | 
|  | 502 | static int | 
|  | 503 | ips_setup(char *ips_str) | 
|  | 504 | { | 
|  | 505 |  | 
|  | 506 | int i; | 
|  | 507 | char *key; | 
|  | 508 | char *value; | 
|  | 509 | IPS_OPTION options[] = { | 
|  | 510 | {"noi2o", &ips_force_i2o, 0}, | 
|  | 511 | {"nommap", &ips_force_memio, 0}, | 
|  | 512 | {"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE}, | 
|  | 513 | {"cdboot", &ips_cd_boot, 0}, | 
|  | 514 | {"maxcmds", &MaxLiteCmds, 32}, | 
|  | 515 | }; | 
|  | 516 |  | 
|  | 517 | /* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */ | 
|  | 518 | /* Search for value */ | 
|  | 519 | while ((key = strsep(&ips_str, ",."))) { | 
|  | 520 | if (!*key) | 
|  | 521 | continue; | 
|  | 522 | value = strchr(key, ':'); | 
|  | 523 | if (value) | 
|  | 524 | *value++ = '\0'; | 
|  | 525 | /* | 
|  | 526 | * We now have key/value pairs. | 
|  | 527 | * Update the variables | 
|  | 528 | */ | 
| Tobias Klauser | 6391a11 | 2006-06-08 22:23:48 -0700 | [diff] [blame] | 529 | for (i = 0; i < ARRAY_SIZE(options); i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 530 | if (strnicmp | 
|  | 531 | (key, options[i].option_name, | 
|  | 532 | strlen(options[i].option_name)) == 0) { | 
|  | 533 | if (value) | 
|  | 534 | *options[i].option_flag = | 
|  | 535 | simple_strtoul(value, NULL, 0); | 
|  | 536 | else | 
|  | 537 | *options[i].option_flag = | 
|  | 538 | options[i].option_value; | 
|  | 539 | break; | 
|  | 540 | } | 
|  | 541 | } | 
|  | 542 | } | 
|  | 543 |  | 
|  | 544 | return (1); | 
|  | 545 | } | 
|  | 546 |  | 
|  | 547 | __setup("ips=", ips_setup); | 
|  | 548 |  | 
|  | 549 | /****************************************************************************/ | 
|  | 550 | /*                                                                          */ | 
|  | 551 | /* Routine Name: ips_detect                                                 */ | 
|  | 552 | /*                                                                          */ | 
|  | 553 | /* Routine Description:                                                     */ | 
|  | 554 | /*                                                                          */ | 
|  | 555 | /*   Detect and initialize the driver                                       */ | 
|  | 556 | /*                                                                          */ | 
|  | 557 | /* NOTE: this routine is called under the io_request_lock spinlock          */ | 
|  | 558 | /*                                                                          */ | 
|  | 559 | /****************************************************************************/ | 
|  | 560 | static int | 
| Christoph Hellwig | d0be4a7d | 2005-10-31 18:31:40 +0100 | [diff] [blame] | 561 | ips_detect(struct scsi_host_template * SHT) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 562 | { | 
|  | 563 | int i; | 
|  | 564 |  | 
|  | 565 | METHOD_TRACE("ips_detect", 1); | 
|  | 566 |  | 
|  | 567 | #ifdef MODULE | 
|  | 568 | if (ips) | 
|  | 569 | ips_setup(ips); | 
|  | 570 | #endif | 
|  | 571 |  | 
|  | 572 | for (i = 0; i < ips_num_controllers; i++) { | 
|  | 573 | if (ips_register_scsi(i)) | 
|  | 574 | ips_free(ips_ha[i]); | 
|  | 575 | ips_released_controllers++; | 
|  | 576 | } | 
|  | 577 | ips_hotplug = 1; | 
|  | 578 | return (ips_num_controllers); | 
|  | 579 | } | 
|  | 580 |  | 
|  | 581 | /****************************************************************************/ | 
|  | 582 | /*   configure the function pointers to use the functions that will work    */ | 
|  | 583 | /*   with the found version of the adapter                                  */ | 
|  | 584 | /****************************************************************************/ | 
|  | 585 | static void | 
|  | 586 | ips_setup_funclist(ips_ha_t * ha) | 
|  | 587 | { | 
|  | 588 |  | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 589 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 590 | * Setup Functions | 
|  | 591 | */ | 
|  | 592 | if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) { | 
|  | 593 | /* morpheus / marco / sebring */ | 
|  | 594 | ha->func.isintr = ips_isintr_morpheus; | 
|  | 595 | ha->func.isinit = ips_isinit_morpheus; | 
|  | 596 | ha->func.issue = ips_issue_i2o_memio; | 
|  | 597 | ha->func.init = ips_init_morpheus; | 
|  | 598 | ha->func.statupd = ips_statupd_morpheus; | 
|  | 599 | ha->func.reset = ips_reset_morpheus; | 
|  | 600 | ha->func.intr = ips_intr_morpheus; | 
|  | 601 | ha->func.enableint = ips_enable_int_morpheus; | 
|  | 602 | } else if (IPS_USE_MEMIO(ha)) { | 
|  | 603 | /* copperhead w/MEMIO */ | 
|  | 604 | ha->func.isintr = ips_isintr_copperhead_memio; | 
|  | 605 | ha->func.isinit = ips_isinit_copperhead_memio; | 
|  | 606 | ha->func.init = ips_init_copperhead_memio; | 
|  | 607 | ha->func.statupd = ips_statupd_copperhead_memio; | 
|  | 608 | ha->func.statinit = ips_statinit_memio; | 
|  | 609 | ha->func.reset = ips_reset_copperhead_memio; | 
|  | 610 | ha->func.intr = ips_intr_copperhead; | 
|  | 611 | ha->func.erasebios = ips_erase_bios_memio; | 
|  | 612 | ha->func.programbios = ips_program_bios_memio; | 
|  | 613 | ha->func.verifybios = ips_verify_bios_memio; | 
|  | 614 | ha->func.enableint = ips_enable_int_copperhead_memio; | 
|  | 615 | if (IPS_USE_I2O_DELIVER(ha)) | 
|  | 616 | ha->func.issue = ips_issue_i2o_memio; | 
|  | 617 | else | 
|  | 618 | ha->func.issue = ips_issue_copperhead_memio; | 
|  | 619 | } else { | 
|  | 620 | /* copperhead */ | 
|  | 621 | ha->func.isintr = ips_isintr_copperhead; | 
|  | 622 | ha->func.isinit = ips_isinit_copperhead; | 
|  | 623 | ha->func.init = ips_init_copperhead; | 
|  | 624 | ha->func.statupd = ips_statupd_copperhead; | 
|  | 625 | ha->func.statinit = ips_statinit; | 
|  | 626 | ha->func.reset = ips_reset_copperhead; | 
|  | 627 | ha->func.intr = ips_intr_copperhead; | 
|  | 628 | ha->func.erasebios = ips_erase_bios; | 
|  | 629 | ha->func.programbios = ips_program_bios; | 
|  | 630 | ha->func.verifybios = ips_verify_bios; | 
|  | 631 | ha->func.enableint = ips_enable_int_copperhead; | 
|  | 632 |  | 
|  | 633 | if (IPS_USE_I2O_DELIVER(ha)) | 
|  | 634 | ha->func.issue = ips_issue_i2o; | 
|  | 635 | else | 
|  | 636 | ha->func.issue = ips_issue_copperhead; | 
|  | 637 | } | 
|  | 638 | } | 
|  | 639 |  | 
|  | 640 | /****************************************************************************/ | 
|  | 641 | /*                                                                          */ | 
|  | 642 | /* Routine Name: ips_release                                                */ | 
|  | 643 | /*                                                                          */ | 
|  | 644 | /* Routine Description:                                                     */ | 
|  | 645 | /*                                                                          */ | 
|  | 646 | /*   Remove a driver                                                        */ | 
|  | 647 | /*                                                                          */ | 
|  | 648 | /****************************************************************************/ | 
|  | 649 | static int | 
|  | 650 | ips_release(struct Scsi_Host *sh) | 
|  | 651 | { | 
|  | 652 | ips_scb_t *scb; | 
|  | 653 | ips_ha_t *ha; | 
|  | 654 | int i; | 
|  | 655 |  | 
|  | 656 | METHOD_TRACE("ips_release", 1); | 
|  | 657 |  | 
| Matthew Wilcox | a50ee7a | 2007-08-15 12:57:00 -0600 | [diff] [blame] | 658 | scsi_remove_host(sh); | 
|  | 659 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 660 | for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++) ; | 
|  | 661 |  | 
|  | 662 | if (i == IPS_MAX_ADAPTERS) { | 
|  | 663 | printk(KERN_WARNING | 
|  | 664 | "(%s) release, invalid Scsi_Host pointer.\n", ips_name); | 
|  | 665 | BUG(); | 
|  | 666 | return (FALSE); | 
|  | 667 | } | 
|  | 668 |  | 
|  | 669 | ha = IPS_HA(sh); | 
|  | 670 |  | 
|  | 671 | if (!ha) | 
|  | 672 | return (FALSE); | 
|  | 673 |  | 
|  | 674 | /* flush the cache on the controller */ | 
|  | 675 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 676 |  | 
|  | 677 | ips_init_scb(ha, scb); | 
|  | 678 |  | 
|  | 679 | scb->timeout = ips_cmd_timeout; | 
|  | 680 | scb->cdb[0] = IPS_CMD_FLUSH; | 
|  | 681 |  | 
|  | 682 | scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; | 
|  | 683 | scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 684 | scb->cmd.flush_cache.state = IPS_NORM_STATE; | 
|  | 685 | scb->cmd.flush_cache.reserved = 0; | 
|  | 686 | scb->cmd.flush_cache.reserved2 = 0; | 
|  | 687 | scb->cmd.flush_cache.reserved3 = 0; | 
|  | 688 | scb->cmd.flush_cache.reserved4 = 0; | 
|  | 689 |  | 
|  | 690 | IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n"); | 
|  | 691 |  | 
|  | 692 | /* send command */ | 
|  | 693 | if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE) | 
|  | 694 | IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n"); | 
|  | 695 |  | 
|  | 696 | IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n"); | 
|  | 697 |  | 
|  | 698 | ips_sh[i] = NULL; | 
|  | 699 | ips_ha[i] = NULL; | 
|  | 700 |  | 
|  | 701 | /* free extra memory */ | 
|  | 702 | ips_free(ha); | 
|  | 703 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 | /* free IRQ */ | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 705 | free_irq(ha->pcidev->irq, ha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 706 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 707 | scsi_host_put(sh); | 
|  | 708 |  | 
|  | 709 | ips_released_controllers++; | 
|  | 710 |  | 
|  | 711 | return (FALSE); | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | /****************************************************************************/ | 
|  | 715 | /*                                                                          */ | 
|  | 716 | /* Routine Name: ips_halt                                                   */ | 
|  | 717 | /*                                                                          */ | 
|  | 718 | /* Routine Description:                                                     */ | 
|  | 719 | /*                                                                          */ | 
|  | 720 | /*   Perform cleanup when the system reboots                                */ | 
|  | 721 | /*                                                                          */ | 
|  | 722 | /****************************************************************************/ | 
|  | 723 | static int | 
|  | 724 | ips_halt(struct notifier_block *nb, ulong event, void *buf) | 
|  | 725 | { | 
|  | 726 | ips_scb_t *scb; | 
|  | 727 | ips_ha_t *ha; | 
|  | 728 | int i; | 
|  | 729 |  | 
|  | 730 | if ((event != SYS_RESTART) && (event != SYS_HALT) && | 
|  | 731 | (event != SYS_POWER_OFF)) | 
|  | 732 | return (NOTIFY_DONE); | 
|  | 733 |  | 
|  | 734 | for (i = 0; i < ips_next_controller; i++) { | 
|  | 735 | ha = (ips_ha_t *) ips_ha[i]; | 
|  | 736 |  | 
|  | 737 | if (!ha) | 
|  | 738 | continue; | 
|  | 739 |  | 
|  | 740 | if (!ha->active) | 
|  | 741 | continue; | 
|  | 742 |  | 
|  | 743 | /* flush the cache on the controller */ | 
|  | 744 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 745 |  | 
|  | 746 | ips_init_scb(ha, scb); | 
|  | 747 |  | 
|  | 748 | scb->timeout = ips_cmd_timeout; | 
|  | 749 | scb->cdb[0] = IPS_CMD_FLUSH; | 
|  | 750 |  | 
|  | 751 | scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; | 
|  | 752 | scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 753 | scb->cmd.flush_cache.state = IPS_NORM_STATE; | 
|  | 754 | scb->cmd.flush_cache.reserved = 0; | 
|  | 755 | scb->cmd.flush_cache.reserved2 = 0; | 
|  | 756 | scb->cmd.flush_cache.reserved3 = 0; | 
|  | 757 | scb->cmd.flush_cache.reserved4 = 0; | 
|  | 758 |  | 
|  | 759 | IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n"); | 
|  | 760 |  | 
|  | 761 | /* send command */ | 
|  | 762 | if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == | 
|  | 763 | IPS_FAILURE) | 
|  | 764 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 765 | "Incomplete Flush.\n"); | 
|  | 766 | else | 
|  | 767 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 768 | "Flushing Complete.\n"); | 
|  | 769 | } | 
|  | 770 |  | 
|  | 771 | return (NOTIFY_OK); | 
|  | 772 | } | 
|  | 773 |  | 
|  | 774 | /****************************************************************************/ | 
|  | 775 | /*                                                                          */ | 
|  | 776 | /* Routine Name: ips_eh_abort                                               */ | 
|  | 777 | /*                                                                          */ | 
|  | 778 | /* Routine Description:                                                     */ | 
|  | 779 | /*                                                                          */ | 
|  | 780 | /*   Abort a command (using the new error code stuff)                       */ | 
|  | 781 | /* Note: this routine is called under the io_request_lock                   */ | 
|  | 782 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 783 | int ips_eh_abort(struct scsi_cmnd *SC) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 784 | { | 
|  | 785 | ips_ha_t *ha; | 
|  | 786 | ips_copp_wait_item_t *item; | 
|  | 787 | int ret; | 
| Jeff Garzik | 8fa728a | 2005-05-28 07:54:40 -0400 | [diff] [blame] | 788 | struct Scsi_Host *host; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 789 |  | 
|  | 790 | METHOD_TRACE("ips_eh_abort", 1); | 
|  | 791 |  | 
|  | 792 | if (!SC) | 
|  | 793 | return (FAILED); | 
|  | 794 |  | 
| Jeff Garzik | 8fa728a | 2005-05-28 07:54:40 -0400 | [diff] [blame] | 795 | host = SC->device->host; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 796 | ha = (ips_ha_t *) SC->device->host->hostdata; | 
|  | 797 |  | 
|  | 798 | if (!ha) | 
|  | 799 | return (FAILED); | 
|  | 800 |  | 
|  | 801 | if (!ha->active) | 
|  | 802 | return (FAILED); | 
|  | 803 |  | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 804 | spin_lock(host->host_lock); | 
| Jeff Garzik | 8fa728a | 2005-05-28 07:54:40 -0400 | [diff] [blame] | 805 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 806 | /* See if the command is on the copp queue */ | 
|  | 807 | item = ha->copp_waitlist.head; | 
|  | 808 | while ((item) && (item->scsi_cmd != SC)) | 
|  | 809 | item = item->next; | 
|  | 810 |  | 
|  | 811 | if (item) { | 
|  | 812 | /* Found it */ | 
|  | 813 | ips_removeq_copp(&ha->copp_waitlist, item); | 
|  | 814 | ret = (SUCCESS); | 
|  | 815 |  | 
|  | 816 | /* See if the command is on the wait queue */ | 
|  | 817 | } else if (ips_removeq_wait(&ha->scb_waitlist, SC)) { | 
|  | 818 | /* command not sent yet */ | 
|  | 819 | ret = (SUCCESS); | 
|  | 820 | } else { | 
|  | 821 | /* command must have already been sent */ | 
|  | 822 | ret = (FAILED); | 
|  | 823 | } | 
| Jeff Garzik | 8fa728a | 2005-05-28 07:54:40 -0400 | [diff] [blame] | 824 |  | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 825 | spin_unlock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 826 | return ret; | 
|  | 827 | } | 
|  | 828 |  | 
|  | 829 | /****************************************************************************/ | 
|  | 830 | /*                                                                          */ | 
|  | 831 | /* Routine Name: ips_eh_reset                                               */ | 
|  | 832 | /*                                                                          */ | 
|  | 833 | /* Routine Description:                                                     */ | 
|  | 834 | /*                                                                          */ | 
|  | 835 | /*   Reset the controller (with new eh error code)                          */ | 
|  | 836 | /*                                                                          */ | 
|  | 837 | /* NOTE: this routine is called under the io_request_lock spinlock          */ | 
|  | 838 | /*                                                                          */ | 
|  | 839 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 840 | static int __ips_eh_reset(struct scsi_cmnd *SC) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 841 | { | 
|  | 842 | int ret; | 
|  | 843 | int i; | 
|  | 844 | ips_ha_t *ha; | 
|  | 845 | ips_scb_t *scb; | 
|  | 846 | ips_copp_wait_item_t *item; | 
|  | 847 |  | 
|  | 848 | METHOD_TRACE("ips_eh_reset", 1); | 
|  | 849 |  | 
|  | 850 | #ifdef NO_IPS_RESET | 
|  | 851 | return (FAILED); | 
|  | 852 | #else | 
|  | 853 |  | 
|  | 854 | if (!SC) { | 
|  | 855 | DEBUG(1, "Reset called with NULL scsi command"); | 
|  | 856 |  | 
|  | 857 | return (FAILED); | 
|  | 858 | } | 
|  | 859 |  | 
|  | 860 | ha = (ips_ha_t *) SC->device->host->hostdata; | 
|  | 861 |  | 
|  | 862 | if (!ha) { | 
|  | 863 | DEBUG(1, "Reset called with NULL ha struct"); | 
|  | 864 |  | 
|  | 865 | return (FAILED); | 
|  | 866 | } | 
|  | 867 |  | 
|  | 868 | if (!ha->active) | 
|  | 869 | return (FAILED); | 
|  | 870 |  | 
|  | 871 | /* See if the command is on the copp queue */ | 
|  | 872 | item = ha->copp_waitlist.head; | 
|  | 873 | while ((item) && (item->scsi_cmd != SC)) | 
|  | 874 | item = item->next; | 
|  | 875 |  | 
|  | 876 | if (item) { | 
|  | 877 | /* Found it */ | 
|  | 878 | ips_removeq_copp(&ha->copp_waitlist, item); | 
|  | 879 | return (SUCCESS); | 
|  | 880 | } | 
|  | 881 |  | 
|  | 882 | /* See if the command is on the wait queue */ | 
|  | 883 | if (ips_removeq_wait(&ha->scb_waitlist, SC)) { | 
|  | 884 | /* command not sent yet */ | 
|  | 885 | return (SUCCESS); | 
|  | 886 | } | 
|  | 887 |  | 
|  | 888 | /* An explanation for the casual observer:                              */ | 
|  | 889 | /* Part of the function of a RAID controller is automatic error         */ | 
|  | 890 | /* detection and recovery.  As such, the only problem that physically   */ | 
|  | 891 | /* resetting an adapter will ever fix is when, for some reason,         */ | 
|  | 892 | /* the driver is not successfully communicating with the adapter.       */ | 
|  | 893 | /* Therefore, we will attempt to flush this adapter.  If that succeeds, */ | 
|  | 894 | /* then there's no real purpose in a physical reset. This will complete */ | 
|  | 895 | /* much faster and avoids any problems that might be caused by a        */ | 
|  | 896 | /* physical reset ( such as having to fail all the outstanding I/O's ). */ | 
|  | 897 |  | 
|  | 898 | if (ha->ioctl_reset == 0) {	/* IF Not an IOCTL Requested Reset */ | 
|  | 899 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 900 |  | 
|  | 901 | ips_init_scb(ha, scb); | 
|  | 902 |  | 
|  | 903 | scb->timeout = ips_cmd_timeout; | 
|  | 904 | scb->cdb[0] = IPS_CMD_FLUSH; | 
|  | 905 |  | 
|  | 906 | scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; | 
|  | 907 | scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 908 | scb->cmd.flush_cache.state = IPS_NORM_STATE; | 
|  | 909 | scb->cmd.flush_cache.reserved = 0; | 
|  | 910 | scb->cmd.flush_cache.reserved2 = 0; | 
|  | 911 | scb->cmd.flush_cache.reserved3 = 0; | 
|  | 912 | scb->cmd.flush_cache.reserved4 = 0; | 
|  | 913 |  | 
|  | 914 | /* Attempt the flush command */ | 
|  | 915 | ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL); | 
|  | 916 | if (ret == IPS_SUCCESS) { | 
|  | 917 | IPS_PRINTK(KERN_NOTICE, ha->pcidev, | 
|  | 918 | "Reset Request - Flushed Cache\n"); | 
|  | 919 | return (SUCCESS); | 
|  | 920 | } | 
|  | 921 | } | 
|  | 922 |  | 
|  | 923 | /* Either we can't communicate with the adapter or it's an IOCTL request */ | 
|  | 924 | /* from a utility.  A physical reset is needed at this point.            */ | 
|  | 925 |  | 
|  | 926 | ha->ioctl_reset = 0;	/* Reset the IOCTL Requested Reset Flag */ | 
|  | 927 |  | 
|  | 928 | /* | 
|  | 929 | * command must have already been sent | 
|  | 930 | * reset the controller | 
|  | 931 | */ | 
|  | 932 | IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Resetting controller.\n"); | 
|  | 933 | ret = (*ha->func.reset) (ha); | 
|  | 934 |  | 
|  | 935 | if (!ret) { | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 936 | struct scsi_cmnd *scsi_cmd; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 937 |  | 
|  | 938 | IPS_PRINTK(KERN_NOTICE, ha->pcidev, | 
|  | 939 | "Controller reset failed - controller now offline.\n"); | 
|  | 940 |  | 
|  | 941 | /* Now fail all of the active commands */ | 
|  | 942 | DEBUG_VAR(1, "(%s%d) Failing active commands", | 
|  | 943 | ips_name, ha->host_num); | 
|  | 944 |  | 
|  | 945 | while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { | 
|  | 946 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 947 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 948 | ips_freescb(ha, scb); | 
|  | 949 | } | 
|  | 950 |  | 
|  | 951 | /* Now fail all of the pending commands */ | 
|  | 952 | DEBUG_VAR(1, "(%s%d) Failing pending commands", | 
|  | 953 | ips_name, ha->host_num); | 
|  | 954 |  | 
|  | 955 | while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { | 
|  | 956 | scsi_cmd->result = DID_ERROR; | 
|  | 957 | scsi_cmd->scsi_done(scsi_cmd); | 
|  | 958 | } | 
|  | 959 |  | 
|  | 960 | ha->active = FALSE; | 
|  | 961 | return (FAILED); | 
|  | 962 | } | 
|  | 963 |  | 
|  | 964 | if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 965 | struct scsi_cmnd *scsi_cmd; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 966 |  | 
|  | 967 | IPS_PRINTK(KERN_NOTICE, ha->pcidev, | 
|  | 968 | "Controller reset failed - controller now offline.\n"); | 
|  | 969 |  | 
|  | 970 | /* Now fail all of the active commands */ | 
|  | 971 | DEBUG_VAR(1, "(%s%d) Failing active commands", | 
|  | 972 | ips_name, ha->host_num); | 
|  | 973 |  | 
|  | 974 | while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { | 
|  | 975 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 976 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 977 | ips_freescb(ha, scb); | 
|  | 978 | } | 
|  | 979 |  | 
|  | 980 | /* Now fail all of the pending commands */ | 
|  | 981 | DEBUG_VAR(1, "(%s%d) Failing pending commands", | 
|  | 982 | ips_name, ha->host_num); | 
|  | 983 |  | 
|  | 984 | while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { | 
|  | 985 | scsi_cmd->result = DID_ERROR << 16; | 
|  | 986 | scsi_cmd->scsi_done(scsi_cmd); | 
|  | 987 | } | 
|  | 988 |  | 
|  | 989 | ha->active = FALSE; | 
|  | 990 | return (FAILED); | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | /* FFDC */ | 
|  | 994 | if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) { | 
|  | 995 | struct timeval tv; | 
|  | 996 |  | 
|  | 997 | do_gettimeofday(&tv); | 
|  | 998 | ha->last_ffdc = tv.tv_sec; | 
|  | 999 | ha->reset_count++; | 
|  | 1000 | ips_ffdc_reset(ha, IPS_INTR_IORL); | 
|  | 1001 | } | 
|  | 1002 |  | 
|  | 1003 | /* Now fail all of the active commands */ | 
|  | 1004 | DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num); | 
|  | 1005 |  | 
|  | 1006 | while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { | 
| Martin K. Petersen | 1c9fbaf | 2009-01-04 03:14:11 -0500 | [diff] [blame] | 1007 | scb->scsi_cmd->result = DID_RESET << 16; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1008 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 1009 | ips_freescb(ha, scb); | 
|  | 1010 | } | 
|  | 1011 |  | 
|  | 1012 | /* Reset DCDB active command bits */ | 
|  | 1013 | for (i = 1; i < ha->nbus; i++) | 
|  | 1014 | ha->dcdb_active[i - 1] = 0; | 
|  | 1015 |  | 
|  | 1016 | /* Reset the number of active IOCTLs */ | 
|  | 1017 | ha->num_ioctl = 0; | 
|  | 1018 |  | 
|  | 1019 | ips_next(ha, IPS_INTR_IORL); | 
|  | 1020 |  | 
|  | 1021 | return (SUCCESS); | 
|  | 1022 | #endif				/* NO_IPS_RESET */ | 
|  | 1023 |  | 
|  | 1024 | } | 
|  | 1025 |  | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 1026 | static int ips_eh_reset(struct scsi_cmnd *SC) | 
| Jeff Garzik | df0ae24 | 2005-05-28 07:57:14 -0400 | [diff] [blame] | 1027 | { | 
|  | 1028 | int rc; | 
|  | 1029 |  | 
|  | 1030 | spin_lock_irq(SC->device->host->host_lock); | 
|  | 1031 | rc = __ips_eh_reset(SC); | 
|  | 1032 | spin_unlock_irq(SC->device->host->host_lock); | 
|  | 1033 |  | 
|  | 1034 | return rc; | 
|  | 1035 | } | 
|  | 1036 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1037 | /****************************************************************************/ | 
|  | 1038 | /*                                                                          */ | 
|  | 1039 | /* Routine Name: ips_queue                                                  */ | 
|  | 1040 | /*                                                                          */ | 
|  | 1041 | /* Routine Description:                                                     */ | 
|  | 1042 | /*                                                                          */ | 
|  | 1043 | /*   Send a command to the controller                                       */ | 
|  | 1044 | /*                                                                          */ | 
|  | 1045 | /* NOTE:                                                                    */ | 
|  | 1046 | /*    Linux obtains io_request_lock before calling this function            */ | 
|  | 1047 | /*                                                                          */ | 
|  | 1048 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 1049 | static int ips_queue(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1050 | { | 
|  | 1051 | ips_ha_t *ha; | 
|  | 1052 | ips_passthru_t *pt; | 
|  | 1053 |  | 
|  | 1054 | METHOD_TRACE("ips_queue", 1); | 
|  | 1055 |  | 
|  | 1056 | ha = (ips_ha_t *) SC->device->host->hostdata; | 
|  | 1057 |  | 
|  | 1058 | if (!ha) | 
|  | 1059 | return (1); | 
|  | 1060 |  | 
|  | 1061 | if (!ha->active) | 
|  | 1062 | return (DID_ERROR); | 
|  | 1063 |  | 
|  | 1064 | if (ips_is_passthru(SC)) { | 
|  | 1065 | if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) { | 
|  | 1066 | SC->result = DID_BUS_BUSY << 16; | 
|  | 1067 | done(SC); | 
|  | 1068 |  | 
|  | 1069 | return (0); | 
|  | 1070 | } | 
|  | 1071 | } else if (ha->scb_waitlist.count == IPS_MAX_QUEUE) { | 
|  | 1072 | SC->result = DID_BUS_BUSY << 16; | 
|  | 1073 | done(SC); | 
|  | 1074 |  | 
|  | 1075 | return (0); | 
|  | 1076 | } | 
|  | 1077 |  | 
|  | 1078 | SC->scsi_done = done; | 
|  | 1079 |  | 
|  | 1080 | DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)", | 
|  | 1081 | ips_name, | 
|  | 1082 | ha->host_num, | 
|  | 1083 | SC->cmnd[0], | 
|  | 1084 | SC->device->channel, SC->device->id, SC->device->lun); | 
|  | 1085 |  | 
|  | 1086 | /* Check for command to initiator IDs */ | 
| Jeff Garzik | 422c0d6 | 2005-10-24 18:05:09 -0400 | [diff] [blame] | 1087 | if ((scmd_channel(SC) > 0) | 
|  | 1088 | && (scmd_id(SC) == ha->ha_id[scmd_channel(SC)])) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1089 | SC->result = DID_NO_CONNECT << 16; | 
|  | 1090 | done(SC); | 
|  | 1091 |  | 
|  | 1092 | return (0); | 
|  | 1093 | } | 
|  | 1094 |  | 
|  | 1095 | if (ips_is_passthru(SC)) { | 
|  | 1096 |  | 
|  | 1097 | ips_copp_wait_item_t *scratch; | 
|  | 1098 |  | 
|  | 1099 | /* A Reset IOCTL is only sent by the boot CD in extreme cases.           */ | 
|  | 1100 | /* There can never be any system activity ( network or disk ), but check */ | 
|  | 1101 | /* anyway just as a good practice.                                       */ | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1102 | pt = (ips_passthru_t *) scsi_sglist(SC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1103 | if ((pt->CoppCP.cmd.reset.op_code == IPS_CMD_RESET_CHANNEL) && | 
|  | 1104 | (pt->CoppCP.cmd.reset.adapter_flag == 1)) { | 
|  | 1105 | if (ha->scb_activelist.count != 0) { | 
|  | 1106 | SC->result = DID_BUS_BUSY << 16; | 
|  | 1107 | done(SC); | 
|  | 1108 | return (0); | 
|  | 1109 | } | 
|  | 1110 | ha->ioctl_reset = 1;	/* This reset request is from an IOCTL */ | 
| Mike Christie | ba3af0a | 2006-02-22 02:11:59 -0600 | [diff] [blame] | 1111 | __ips_eh_reset(SC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1112 | SC->result = DID_OK << 16; | 
|  | 1113 | SC->scsi_done(SC); | 
|  | 1114 | return (0); | 
|  | 1115 | } | 
|  | 1116 |  | 
|  | 1117 | /* allocate space for the scribble */ | 
|  | 1118 | scratch = kmalloc(sizeof (ips_copp_wait_item_t), GFP_ATOMIC); | 
|  | 1119 |  | 
|  | 1120 | if (!scratch) { | 
|  | 1121 | SC->result = DID_ERROR << 16; | 
|  | 1122 | done(SC); | 
|  | 1123 |  | 
|  | 1124 | return (0); | 
|  | 1125 | } | 
|  | 1126 |  | 
|  | 1127 | scratch->scsi_cmd = SC; | 
|  | 1128 | scratch->next = NULL; | 
|  | 1129 |  | 
|  | 1130 | ips_putq_copp_tail(&ha->copp_waitlist, scratch); | 
|  | 1131 | } else { | 
|  | 1132 | ips_putq_wait_tail(&ha->scb_waitlist, SC); | 
|  | 1133 | } | 
|  | 1134 |  | 
|  | 1135 | ips_next(ha, IPS_INTR_IORL); | 
|  | 1136 |  | 
|  | 1137 | return (0); | 
|  | 1138 | } | 
|  | 1139 |  | 
|  | 1140 | /****************************************************************************/ | 
|  | 1141 | /*                                                                          */ | 
|  | 1142 | /* Routine Name: ips_biosparam                                              */ | 
|  | 1143 | /*                                                                          */ | 
|  | 1144 | /* Routine Description:                                                     */ | 
|  | 1145 | /*                                                                          */ | 
|  | 1146 | /*   Set bios geometry for the controller                                   */ | 
|  | 1147 | /*                                                                          */ | 
|  | 1148 | /****************************************************************************/ | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 1149 | static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev, | 
|  | 1150 | sector_t capacity, int geom[]) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1151 | { | 
|  | 1152 | ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1153 | int heads; | 
|  | 1154 | int sectors; | 
|  | 1155 | int cylinders; | 
|  | 1156 |  | 
|  | 1157 | METHOD_TRACE("ips_biosparam", 1); | 
|  | 1158 |  | 
|  | 1159 | if (!ha) | 
|  | 1160 | /* ?!?! host adater info invalid */ | 
|  | 1161 | return (0); | 
|  | 1162 |  | 
|  | 1163 | if (!ha->active) | 
|  | 1164 | return (0); | 
|  | 1165 |  | 
|  | 1166 | if (!ips_read_adapter_status(ha, IPS_INTR_ON)) | 
|  | 1167 | /* ?!?! Enquiry command failed */ | 
|  | 1168 | return (0); | 
|  | 1169 |  | 
|  | 1170 | if ((capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) { | 
|  | 1171 | heads = IPS_NORM_HEADS; | 
|  | 1172 | sectors = IPS_NORM_SECTORS; | 
|  | 1173 | } else { | 
|  | 1174 | heads = IPS_COMP_HEADS; | 
|  | 1175 | sectors = IPS_COMP_SECTORS; | 
|  | 1176 | } | 
|  | 1177 |  | 
|  | 1178 | cylinders = (unsigned long) capacity / (heads * sectors); | 
|  | 1179 |  | 
|  | 1180 | DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d", | 
|  | 1181 | heads, sectors, cylinders); | 
|  | 1182 |  | 
|  | 1183 | geom[0] = heads; | 
|  | 1184 | geom[1] = sectors; | 
|  | 1185 | geom[2] = cylinders; | 
|  | 1186 |  | 
|  | 1187 | return (0); | 
|  | 1188 | } | 
|  | 1189 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1190 | /****************************************************************************/ | 
|  | 1191 | /*                                                                          */ | 
|  | 1192 | /* Routine Name: ips_slave_configure                                        */ | 
|  | 1193 | /*                                                                          */ | 
|  | 1194 | /* Routine Description:                                                     */ | 
|  | 1195 | /*                                                                          */ | 
|  | 1196 | /*   Set queue depths on devices once scan is complete                      */ | 
|  | 1197 | /*                                                                          */ | 
|  | 1198 | /****************************************************************************/ | 
|  | 1199 | static int | 
| Christoph Hellwig | f64a181 | 2005-10-31 18:32:08 +0100 | [diff] [blame] | 1200 | ips_slave_configure(struct scsi_device * SDptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1201 | { | 
|  | 1202 | ips_ha_t *ha; | 
|  | 1203 | int min; | 
|  | 1204 |  | 
|  | 1205 | ha = IPS_HA(SDptr->host); | 
|  | 1206 | if (SDptr->tagged_supported && SDptr->type == TYPE_DISK) { | 
|  | 1207 | min = ha->max_cmds / 2; | 
|  | 1208 | if (ha->enq->ucLogDriveCount <= 2) | 
|  | 1209 | min = ha->max_cmds - 1; | 
|  | 1210 | scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, min); | 
|  | 1211 | } | 
| Jack Hammer | 560c26c | 2006-01-13 10:06:50 -0500 | [diff] [blame] | 1212 |  | 
|  | 1213 | SDptr->skip_ms_page_8 = 1; | 
|  | 1214 | SDptr->skip_ms_page_3f = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1215 | return 0; | 
|  | 1216 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1217 |  | 
|  | 1218 | /****************************************************************************/ | 
|  | 1219 | /*                                                                          */ | 
|  | 1220 | /* Routine Name: do_ipsintr                                                 */ | 
|  | 1221 | /*                                                                          */ | 
|  | 1222 | /* Routine Description:                                                     */ | 
|  | 1223 | /*                                                                          */ | 
|  | 1224 | /*   Wrapper for the interrupt handler                                      */ | 
|  | 1225 | /*                                                                          */ | 
|  | 1226 | /****************************************************************************/ | 
|  | 1227 | static irqreturn_t | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 1228 | do_ipsintr(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1229 | { | 
|  | 1230 | ips_ha_t *ha; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1231 | struct Scsi_Host *host; | 
|  | 1232 | int irqstatus; | 
|  | 1233 |  | 
|  | 1234 | METHOD_TRACE("do_ipsintr", 2); | 
|  | 1235 |  | 
|  | 1236 | ha = (ips_ha_t *) dev_id; | 
|  | 1237 | if (!ha) | 
|  | 1238 | return IRQ_NONE; | 
|  | 1239 | host = ips_sh[ha->host_num]; | 
|  | 1240 | /* interrupt during initialization */ | 
|  | 1241 | if (!host) { | 
|  | 1242 | (*ha->func.intr) (ha); | 
|  | 1243 | return IRQ_HANDLED; | 
|  | 1244 | } | 
|  | 1245 |  | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 1246 | spin_lock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1247 |  | 
|  | 1248 | if (!ha->active) { | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 1249 | spin_unlock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1250 | return IRQ_HANDLED; | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | irqstatus = (*ha->func.intr) (ha); | 
|  | 1254 |  | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 1255 | spin_unlock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1256 |  | 
|  | 1257 | /* start the next command */ | 
|  | 1258 | ips_next(ha, IPS_INTR_ON); | 
|  | 1259 | return IRQ_RETVAL(irqstatus); | 
|  | 1260 | } | 
|  | 1261 |  | 
|  | 1262 | /****************************************************************************/ | 
|  | 1263 | /*                                                                          */ | 
|  | 1264 | /* Routine Name: ips_intr_copperhead                                        */ | 
|  | 1265 | /*                                                                          */ | 
|  | 1266 | /* Routine Description:                                                     */ | 
|  | 1267 | /*                                                                          */ | 
|  | 1268 | /*   Polling interrupt handler                                              */ | 
|  | 1269 | /*                                                                          */ | 
|  | 1270 | /*   ASSUMES interrupts are disabled                                        */ | 
|  | 1271 | /*                                                                          */ | 
|  | 1272 | /****************************************************************************/ | 
|  | 1273 | int | 
|  | 1274 | ips_intr_copperhead(ips_ha_t * ha) | 
|  | 1275 | { | 
|  | 1276 | ips_stat_t *sp; | 
|  | 1277 | ips_scb_t *scb; | 
|  | 1278 | IPS_STATUS cstatus; | 
|  | 1279 | int intrstatus; | 
|  | 1280 |  | 
|  | 1281 | METHOD_TRACE("ips_intr", 2); | 
|  | 1282 |  | 
|  | 1283 | if (!ha) | 
|  | 1284 | return 0; | 
|  | 1285 |  | 
|  | 1286 | if (!ha->active) | 
|  | 1287 | return 0; | 
|  | 1288 |  | 
|  | 1289 | intrstatus = (*ha->func.isintr) (ha); | 
|  | 1290 |  | 
|  | 1291 | if (!intrstatus) { | 
|  | 1292 | /* | 
|  | 1293 | * Unexpected/Shared interrupt | 
|  | 1294 | */ | 
|  | 1295 |  | 
|  | 1296 | return 0; | 
|  | 1297 | } | 
|  | 1298 |  | 
|  | 1299 | while (TRUE) { | 
|  | 1300 | sp = &ha->sp; | 
|  | 1301 |  | 
|  | 1302 | intrstatus = (*ha->func.isintr) (ha); | 
|  | 1303 |  | 
|  | 1304 | if (!intrstatus) | 
|  | 1305 | break; | 
|  | 1306 | else | 
|  | 1307 | cstatus.value = (*ha->func.statupd) (ha); | 
|  | 1308 |  | 
|  | 1309 | if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { | 
| Joe Perches | b1c1181 | 2008-02-03 17:28:22 +0200 | [diff] [blame] | 1310 | /* Spurious Interrupt ? */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1311 | continue; | 
|  | 1312 | } | 
|  | 1313 |  | 
|  | 1314 | ips_chkstatus(ha, &cstatus); | 
|  | 1315 | scb = (ips_scb_t *) sp->scb_addr; | 
|  | 1316 |  | 
|  | 1317 | /* | 
|  | 1318 | * use the callback function to finish things up | 
|  | 1319 | * NOTE: interrupts are OFF for this | 
|  | 1320 | */ | 
|  | 1321 | (*scb->callback) (ha, scb); | 
|  | 1322 | }			/* end while */ | 
|  | 1323 | return 1; | 
|  | 1324 | } | 
|  | 1325 |  | 
|  | 1326 | /****************************************************************************/ | 
|  | 1327 | /*                                                                          */ | 
|  | 1328 | /* Routine Name: ips_intr_morpheus                                          */ | 
|  | 1329 | /*                                                                          */ | 
|  | 1330 | /* Routine Description:                                                     */ | 
|  | 1331 | /*                                                                          */ | 
|  | 1332 | /*   Polling interrupt handler                                              */ | 
|  | 1333 | /*                                                                          */ | 
|  | 1334 | /*   ASSUMES interrupts are disabled                                        */ | 
|  | 1335 | /*                                                                          */ | 
|  | 1336 | /****************************************************************************/ | 
|  | 1337 | int | 
|  | 1338 | ips_intr_morpheus(ips_ha_t * ha) | 
|  | 1339 | { | 
|  | 1340 | ips_stat_t *sp; | 
|  | 1341 | ips_scb_t *scb; | 
|  | 1342 | IPS_STATUS cstatus; | 
|  | 1343 | int intrstatus; | 
|  | 1344 |  | 
|  | 1345 | METHOD_TRACE("ips_intr_morpheus", 2); | 
|  | 1346 |  | 
|  | 1347 | if (!ha) | 
|  | 1348 | return 0; | 
|  | 1349 |  | 
|  | 1350 | if (!ha->active) | 
|  | 1351 | return 0; | 
|  | 1352 |  | 
|  | 1353 | intrstatus = (*ha->func.isintr) (ha); | 
|  | 1354 |  | 
|  | 1355 | if (!intrstatus) { | 
|  | 1356 | /* | 
|  | 1357 | * Unexpected/Shared interrupt | 
|  | 1358 | */ | 
|  | 1359 |  | 
|  | 1360 | return 0; | 
|  | 1361 | } | 
|  | 1362 |  | 
|  | 1363 | while (TRUE) { | 
|  | 1364 | sp = &ha->sp; | 
|  | 1365 |  | 
|  | 1366 | intrstatus = (*ha->func.isintr) (ha); | 
|  | 1367 |  | 
|  | 1368 | if (!intrstatus) | 
|  | 1369 | break; | 
|  | 1370 | else | 
|  | 1371 | cstatus.value = (*ha->func.statupd) (ha); | 
|  | 1372 |  | 
|  | 1373 | if (cstatus.value == 0xffffffff) | 
|  | 1374 | /* No more to process */ | 
|  | 1375 | break; | 
|  | 1376 |  | 
|  | 1377 | if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { | 
|  | 1378 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 1379 | "Spurious interrupt; no ccb.\n"); | 
|  | 1380 |  | 
|  | 1381 | continue; | 
|  | 1382 | } | 
|  | 1383 |  | 
|  | 1384 | ips_chkstatus(ha, &cstatus); | 
|  | 1385 | scb = (ips_scb_t *) sp->scb_addr; | 
|  | 1386 |  | 
|  | 1387 | /* | 
|  | 1388 | * use the callback function to finish things up | 
|  | 1389 | * NOTE: interrupts are OFF for this | 
|  | 1390 | */ | 
|  | 1391 | (*scb->callback) (ha, scb); | 
|  | 1392 | }			/* end while */ | 
|  | 1393 | return 1; | 
|  | 1394 | } | 
|  | 1395 |  | 
|  | 1396 | /****************************************************************************/ | 
|  | 1397 | /*                                                                          */ | 
|  | 1398 | /* Routine Name: ips_info                                                   */ | 
|  | 1399 | /*                                                                          */ | 
|  | 1400 | /* Routine Description:                                                     */ | 
|  | 1401 | /*                                                                          */ | 
|  | 1402 | /*   Return info about the driver                                           */ | 
|  | 1403 | /*                                                                          */ | 
|  | 1404 | /****************************************************************************/ | 
|  | 1405 | static const char * | 
|  | 1406 | ips_info(struct Scsi_Host *SH) | 
|  | 1407 | { | 
|  | 1408 | static char buffer[256]; | 
|  | 1409 | char *bp; | 
|  | 1410 | ips_ha_t *ha; | 
|  | 1411 |  | 
|  | 1412 | METHOD_TRACE("ips_info", 1); | 
|  | 1413 |  | 
|  | 1414 | ha = IPS_HA(SH); | 
|  | 1415 |  | 
|  | 1416 | if (!ha) | 
|  | 1417 | return (NULL); | 
|  | 1418 |  | 
|  | 1419 | bp = &buffer[0]; | 
|  | 1420 | memset(bp, 0, sizeof (buffer)); | 
|  | 1421 |  | 
|  | 1422 | sprintf(bp, "%s%s%s Build %d", "IBM PCI ServeRAID ", | 
|  | 1423 | IPS_VERSION_HIGH, IPS_VERSION_LOW, IPS_BUILD_IDENT); | 
|  | 1424 |  | 
|  | 1425 | if (ha->ad_type > 0 && ha->ad_type <= MAX_ADAPTER_NAME) { | 
|  | 1426 | strcat(bp, " <"); | 
|  | 1427 | strcat(bp, ips_adapter_name[ha->ad_type - 1]); | 
|  | 1428 | strcat(bp, ">"); | 
|  | 1429 | } | 
|  | 1430 |  | 
|  | 1431 | return (bp); | 
|  | 1432 | } | 
|  | 1433 |  | 
|  | 1434 | /****************************************************************************/ | 
|  | 1435 | /*                                                                          */ | 
|  | 1436 | /* Routine Name: ips_proc_info                                              */ | 
|  | 1437 | /*                                                                          */ | 
|  | 1438 | /* Routine Description:                                                     */ | 
|  | 1439 | /*                                                                          */ | 
|  | 1440 | /*   The passthru interface for the driver                                  */ | 
|  | 1441 | /*                                                                          */ | 
|  | 1442 | /****************************************************************************/ | 
|  | 1443 | static int | 
|  | 1444 | ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, | 
|  | 1445 | int length, int func) | 
|  | 1446 | { | 
|  | 1447 | int i; | 
|  | 1448 | int ret; | 
|  | 1449 | ips_ha_t *ha = NULL; | 
|  | 1450 |  | 
|  | 1451 | METHOD_TRACE("ips_proc_info", 1); | 
|  | 1452 |  | 
|  | 1453 | /* Find our host structure */ | 
|  | 1454 | for (i = 0; i < ips_next_controller; i++) { | 
|  | 1455 | if (ips_sh[i]) { | 
|  | 1456 | if (ips_sh[i] == host) { | 
|  | 1457 | ha = (ips_ha_t *) ips_sh[i]->hostdata; | 
|  | 1458 | break; | 
|  | 1459 | } | 
|  | 1460 | } | 
|  | 1461 | } | 
|  | 1462 |  | 
|  | 1463 | if (!ha) | 
|  | 1464 | return (-EINVAL); | 
|  | 1465 |  | 
|  | 1466 | if (func) { | 
|  | 1467 | /* write */ | 
|  | 1468 | return (0); | 
|  | 1469 | } else { | 
|  | 1470 | /* read */ | 
|  | 1471 | if (start) | 
|  | 1472 | *start = buffer; | 
|  | 1473 |  | 
|  | 1474 | ret = ips_host_info(ha, buffer, offset, length); | 
|  | 1475 |  | 
|  | 1476 | return (ret); | 
|  | 1477 | } | 
|  | 1478 | } | 
|  | 1479 |  | 
|  | 1480 | /*--------------------------------------------------------------------------*/ | 
|  | 1481 | /* Helper Functions                                                         */ | 
|  | 1482 | /*--------------------------------------------------------------------------*/ | 
|  | 1483 |  | 
|  | 1484 | /****************************************************************************/ | 
|  | 1485 | /*                                                                          */ | 
|  | 1486 | /* Routine Name: ips_is_passthru                                            */ | 
|  | 1487 | /*                                                                          */ | 
|  | 1488 | /* Routine Description:                                                     */ | 
|  | 1489 | /*                                                                          */ | 
|  | 1490 | /*   Determine if the specified SCSI command is really a passthru command   */ | 
|  | 1491 | /*                                                                          */ | 
|  | 1492 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 1493 | static int ips_is_passthru(struct scsi_cmnd *SC) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1494 | { | 
| Jack Hammer | a3632fa | 2005-10-25 14:13:03 -0400 | [diff] [blame] | 1495 | unsigned long flags; | 
|  | 1496 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1497 | METHOD_TRACE("ips_is_passthru", 1); | 
|  | 1498 |  | 
|  | 1499 | if (!SC) | 
|  | 1500 | return (0); | 
|  | 1501 |  | 
|  | 1502 | if ((SC->cmnd[0] == IPS_IOCTL_COMMAND) && | 
|  | 1503 | (SC->device->channel == 0) && | 
|  | 1504 | (SC->device->id == IPS_ADAPTER_ID) && | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1505 | (SC->device->lun == 0) && scsi_sglist(SC)) { | 
|  | 1506 | struct scatterlist *sg = scsi_sglist(SC); | 
|  | 1507 | char  *buffer; | 
| Jack Hammer | a3632fa | 2005-10-25 14:13:03 -0400 | [diff] [blame] | 1508 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1509 | /* kmap_atomic() ensures addressability of the user buffer.*/ | 
|  | 1510 | /* local_irq_save() protects the KM_IRQ0 address slot.     */ | 
|  | 1511 | local_irq_save(flags); | 
| Jens Axboe | 45711f1 | 2007-10-22 21:19:53 +0200 | [diff] [blame] | 1512 | buffer = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1513 | if (buffer && buffer[0] == 'C' && buffer[1] == 'O' && | 
|  | 1514 | buffer[2] == 'P' && buffer[3] == 'P') { | 
|  | 1515 | kunmap_atomic(buffer - sg->offset, KM_IRQ0); | 
|  | 1516 | local_irq_restore(flags); | 
|  | 1517 | return 1; | 
|  | 1518 | } | 
|  | 1519 | kunmap_atomic(buffer - sg->offset, KM_IRQ0); | 
|  | 1520 | local_irq_restore(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1521 | } | 
|  | 1522 | return 0; | 
|  | 1523 | } | 
|  | 1524 |  | 
|  | 1525 | /****************************************************************************/ | 
|  | 1526 | /*                                                                          */ | 
|  | 1527 | /* Routine Name: ips_alloc_passthru_buffer                                  */ | 
|  | 1528 | /*                                                                          */ | 
|  | 1529 | /* Routine Description:                                                     */ | 
|  | 1530 | /*   allocate a buffer large enough for the ioctl data if the ioctl buffer  */ | 
|  | 1531 | /*   is too small or doesn't exist                                          */ | 
|  | 1532 | /****************************************************************************/ | 
|  | 1533 | static int | 
|  | 1534 | ips_alloc_passthru_buffer(ips_ha_t * ha, int length) | 
|  | 1535 | { | 
|  | 1536 | void *bigger_buf; | 
|  | 1537 | dma_addr_t dma_busaddr; | 
|  | 1538 |  | 
|  | 1539 | if (ha->ioctl_data && length <= ha->ioctl_len) | 
|  | 1540 | return 0; | 
|  | 1541 | /* there is no buffer or it's not big enough, allocate a new one */ | 
|  | 1542 | bigger_buf = pci_alloc_consistent(ha->pcidev, length, &dma_busaddr); | 
|  | 1543 | if (bigger_buf) { | 
|  | 1544 | /* free the old memory */ | 
|  | 1545 | pci_free_consistent(ha->pcidev, ha->ioctl_len, ha->ioctl_data, | 
|  | 1546 | ha->ioctl_busaddr); | 
|  | 1547 | /* use the new memory */ | 
|  | 1548 | ha->ioctl_data = (char *) bigger_buf; | 
|  | 1549 | ha->ioctl_len = length; | 
|  | 1550 | ha->ioctl_busaddr = dma_busaddr; | 
|  | 1551 | } else { | 
|  | 1552 | return -1; | 
|  | 1553 | } | 
|  | 1554 | return 0; | 
|  | 1555 | } | 
|  | 1556 |  | 
|  | 1557 | /****************************************************************************/ | 
|  | 1558 | /*                                                                          */ | 
|  | 1559 | /* Routine Name: ips_make_passthru                                          */ | 
|  | 1560 | /*                                                                          */ | 
|  | 1561 | /* Routine Description:                                                     */ | 
|  | 1562 | /*                                                                          */ | 
|  | 1563 | /*   Make a passthru command out of the info in the Scsi block              */ | 
|  | 1564 | /*                                                                          */ | 
|  | 1565 | /****************************************************************************/ | 
|  | 1566 | static int | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 1567 | ips_make_passthru(ips_ha_t *ha, struct scsi_cmnd *SC, ips_scb_t *scb, int intr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1568 | { | 
|  | 1569 | ips_passthru_t *pt; | 
|  | 1570 | int length = 0; | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1571 | int i, ret; | 
|  | 1572 | struct scatterlist *sg = scsi_sglist(SC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1573 |  | 
|  | 1574 | METHOD_TRACE("ips_make_passthru", 1); | 
|  | 1575 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1576 | scsi_for_each_sg(SC, sg, scsi_sg_count(SC), i) | 
| FUJITA Tomonori | 2b28a47 | 2008-02-19 17:02:27 +0900 | [diff] [blame] | 1577 | length += sg->length; | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 1578 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1579 | if (length < sizeof (ips_passthru_t)) { | 
|  | 1580 | /* wrong size */ | 
|  | 1581 | DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", | 
|  | 1582 | ips_name, ha->host_num); | 
|  | 1583 | return (IPS_FAILURE); | 
|  | 1584 | } | 
|  | 1585 | if (ips_alloc_passthru_buffer(ha, length)) { | 
|  | 1586 | /* allocation failure!  If ha->ioctl_data exists, use it to return | 
|  | 1587 | some error codes.  Return a failed command to the scsi layer. */ | 
|  | 1588 | if (ha->ioctl_data) { | 
|  | 1589 | pt = (ips_passthru_t *) ha->ioctl_data; | 
|  | 1590 | ips_scmd_buf_read(SC, pt, sizeof (ips_passthru_t)); | 
|  | 1591 | pt->BasicStatus = 0x0B; | 
|  | 1592 | pt->ExtendedStatus = 0x00; | 
|  | 1593 | ips_scmd_buf_write(SC, pt, sizeof (ips_passthru_t)); | 
|  | 1594 | } | 
|  | 1595 | return IPS_FAILURE; | 
|  | 1596 | } | 
|  | 1597 | ha->ioctl_datasize = length; | 
|  | 1598 |  | 
|  | 1599 | ips_scmd_buf_read(SC, ha->ioctl_data, ha->ioctl_datasize); | 
|  | 1600 | pt = (ips_passthru_t *) ha->ioctl_data; | 
|  | 1601 |  | 
|  | 1602 | /* | 
|  | 1603 | * Some notes about the passthru interface used | 
|  | 1604 | * | 
|  | 1605 | * IF the scsi op_code == 0x0d then we assume | 
|  | 1606 | * that the data came along with/goes with the | 
|  | 1607 | * packet we received from the sg driver. In this | 
|  | 1608 | * case the CmdBSize field of the pt structure is | 
|  | 1609 | * used for the size of the buffer. | 
|  | 1610 | */ | 
|  | 1611 |  | 
|  | 1612 | switch (pt->CoppCmd) { | 
|  | 1613 | case IPS_NUMCTRLS: | 
|  | 1614 | memcpy(ha->ioctl_data + sizeof (ips_passthru_t), | 
|  | 1615 | &ips_num_controllers, sizeof (int)); | 
|  | 1616 | ips_scmd_buf_write(SC, ha->ioctl_data, | 
|  | 1617 | sizeof (ips_passthru_t) + sizeof (int)); | 
|  | 1618 | SC->result = DID_OK << 16; | 
|  | 1619 |  | 
|  | 1620 | return (IPS_SUCCESS_IMM); | 
|  | 1621 |  | 
|  | 1622 | case IPS_COPPUSRCMD: | 
|  | 1623 | case IPS_COPPIOCCMD: | 
|  | 1624 | if (SC->cmnd[0] == IPS_IOCTL_COMMAND) { | 
|  | 1625 | if (length < (sizeof (ips_passthru_t) + pt->CmdBSize)) { | 
|  | 1626 | /* wrong size */ | 
|  | 1627 | DEBUG_VAR(1, | 
|  | 1628 | "(%s%d) Passthru structure wrong size", | 
|  | 1629 | ips_name, ha->host_num); | 
|  | 1630 |  | 
|  | 1631 | return (IPS_FAILURE); | 
|  | 1632 | } | 
|  | 1633 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 1634 | if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD && | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1635 | pt->CoppCP.cmd.flashfw.op_code == | 
|  | 1636 | IPS_CMD_RW_BIOSFW) { | 
|  | 1637 | ret = ips_flash_copperhead(ha, pt, scb); | 
|  | 1638 | ips_scmd_buf_write(SC, ha->ioctl_data, | 
|  | 1639 | sizeof (ips_passthru_t)); | 
|  | 1640 | return ret; | 
|  | 1641 | } | 
|  | 1642 | if (ips_usrcmd(ha, pt, scb)) | 
|  | 1643 | return (IPS_SUCCESS); | 
|  | 1644 | else | 
|  | 1645 | return (IPS_FAILURE); | 
|  | 1646 | } | 
|  | 1647 |  | 
|  | 1648 | break; | 
|  | 1649 |  | 
|  | 1650 | }			/* end switch */ | 
|  | 1651 |  | 
|  | 1652 | return (IPS_FAILURE); | 
|  | 1653 | } | 
|  | 1654 |  | 
|  | 1655 | /****************************************************************************/ | 
|  | 1656 | /* Routine Name: ips_flash_copperhead                                       */ | 
|  | 1657 | /* Routine Description:                                                     */ | 
|  | 1658 | /*   Flash the BIOS/FW on a Copperhead style controller                     */ | 
|  | 1659 | /****************************************************************************/ | 
|  | 1660 | static int | 
|  | 1661 | ips_flash_copperhead(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb) | 
|  | 1662 | { | 
|  | 1663 | int datasize; | 
|  | 1664 |  | 
|  | 1665 | /* Trombone is the only copperhead that can do packet flash, but only | 
|  | 1666 | * for firmware. No one said it had to make sence. */ | 
|  | 1667 | if (IPS_IS_TROMBONE(ha) && pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE) { | 
|  | 1668 | if (ips_usrcmd(ha, pt, scb)) | 
|  | 1669 | return IPS_SUCCESS; | 
|  | 1670 | else | 
|  | 1671 | return IPS_FAILURE; | 
|  | 1672 | } | 
|  | 1673 | pt->BasicStatus = 0x0B; | 
|  | 1674 | pt->ExtendedStatus = 0; | 
|  | 1675 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 1676 | /* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can     */ | 
|  | 1677 | /* avoid allocating a huge buffer per adapter ( which can fail ). */ | 
|  | 1678 | if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE && | 
|  | 1679 | pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) { | 
|  | 1680 | pt->BasicStatus = 0; | 
|  | 1681 | return ips_flash_bios(ha, pt, scb); | 
|  | 1682 | } else if (pt->CoppCP.cmd.flashfw.packet_num == 0) { | 
|  | 1683 | if (ips_FlashData && !test_and_set_bit(0, &ips_FlashDataInUse)){ | 
|  | 1684 | ha->flash_data = ips_FlashData; | 
|  | 1685 | ha->flash_busaddr = ips_flashbusaddr; | 
|  | 1686 | ha->flash_len = PAGE_SIZE << 7; | 
|  | 1687 | ha->flash_datasize = 0; | 
|  | 1688 | } else if (!ha->flash_data) { | 
|  | 1689 | datasize = pt->CoppCP.cmd.flashfw.total_packets * | 
|  | 1690 | pt->CoppCP.cmd.flashfw.count; | 
|  | 1691 | ha->flash_data = pci_alloc_consistent(ha->pcidev, | 
|  | 1692 | datasize, | 
|  | 1693 | &ha->flash_busaddr); | 
|  | 1694 | if (!ha->flash_data){ | 
|  | 1695 | printk(KERN_WARNING "Unable to allocate a flash buffer\n"); | 
|  | 1696 | return IPS_FAILURE; | 
|  | 1697 | } | 
|  | 1698 | ha->flash_datasize = 0; | 
|  | 1699 | ha->flash_len = datasize; | 
|  | 1700 | } else | 
|  | 1701 | return IPS_FAILURE; | 
|  | 1702 | } else { | 
|  | 1703 | if (pt->CoppCP.cmd.flashfw.count + ha->flash_datasize > | 
|  | 1704 | ha->flash_len) { | 
|  | 1705 | ips_free_flash_copperhead(ha); | 
|  | 1706 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 1707 | "failed size sanity check\n"); | 
|  | 1708 | return IPS_FAILURE; | 
|  | 1709 | } | 
|  | 1710 | } | 
|  | 1711 | if (!ha->flash_data) | 
|  | 1712 | return IPS_FAILURE; | 
|  | 1713 | pt->BasicStatus = 0; | 
|  | 1714 | memcpy(&ha->flash_data[ha->flash_datasize], pt + 1, | 
|  | 1715 | pt->CoppCP.cmd.flashfw.count); | 
|  | 1716 | ha->flash_datasize += pt->CoppCP.cmd.flashfw.count; | 
|  | 1717 | if (pt->CoppCP.cmd.flashfw.packet_num == | 
|  | 1718 | pt->CoppCP.cmd.flashfw.total_packets - 1) { | 
|  | 1719 | if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE) | 
|  | 1720 | return ips_flash_bios(ha, pt, scb); | 
|  | 1721 | else if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE) | 
|  | 1722 | return ips_flash_firmware(ha, pt, scb); | 
|  | 1723 | } | 
|  | 1724 | return IPS_SUCCESS_IMM; | 
|  | 1725 | } | 
|  | 1726 |  | 
|  | 1727 | /****************************************************************************/ | 
|  | 1728 | /* Routine Name: ips_flash_bios                                             */ | 
|  | 1729 | /* Routine Description:                                                     */ | 
|  | 1730 | /*   flashes the bios of a copperhead adapter                               */ | 
|  | 1731 | /****************************************************************************/ | 
|  | 1732 | static int | 
|  | 1733 | ips_flash_bios(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb) | 
|  | 1734 | { | 
|  | 1735 |  | 
|  | 1736 | if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE && | 
|  | 1737 | pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_BIOS) { | 
|  | 1738 | if ((!ha->func.programbios) || (!ha->func.erasebios) || | 
|  | 1739 | (!ha->func.verifybios)) | 
|  | 1740 | goto error; | 
|  | 1741 | if ((*ha->func.erasebios) (ha)) { | 
|  | 1742 | DEBUG_VAR(1, | 
|  | 1743 | "(%s%d) flash bios failed - unable to erase flash", | 
|  | 1744 | ips_name, ha->host_num); | 
|  | 1745 | goto error; | 
|  | 1746 | } else | 
|  | 1747 | if ((*ha->func.programbios) (ha, | 
|  | 1748 | ha->flash_data + | 
|  | 1749 | IPS_BIOS_HEADER, | 
|  | 1750 | ha->flash_datasize - | 
|  | 1751 | IPS_BIOS_HEADER, 0)) { | 
|  | 1752 | DEBUG_VAR(1, | 
|  | 1753 | "(%s%d) flash bios failed - unable to flash", | 
|  | 1754 | ips_name, ha->host_num); | 
|  | 1755 | goto error; | 
|  | 1756 | } else | 
|  | 1757 | if ((*ha->func.verifybios) (ha, | 
|  | 1758 | ha->flash_data + | 
|  | 1759 | IPS_BIOS_HEADER, | 
|  | 1760 | ha->flash_datasize - | 
|  | 1761 | IPS_BIOS_HEADER, 0)) { | 
|  | 1762 | DEBUG_VAR(1, | 
|  | 1763 | "(%s%d) flash bios failed - unable to verify flash", | 
|  | 1764 | ips_name, ha->host_num); | 
|  | 1765 | goto error; | 
|  | 1766 | } | 
|  | 1767 | ips_free_flash_copperhead(ha); | 
|  | 1768 | return IPS_SUCCESS_IMM; | 
|  | 1769 | } else if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE && | 
|  | 1770 | pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) { | 
|  | 1771 | if (!ha->func.erasebios) | 
|  | 1772 | goto error; | 
|  | 1773 | if ((*ha->func.erasebios) (ha)) { | 
|  | 1774 | DEBUG_VAR(1, | 
|  | 1775 | "(%s%d) flash bios failed - unable to erase flash", | 
|  | 1776 | ips_name, ha->host_num); | 
|  | 1777 | goto error; | 
|  | 1778 | } | 
|  | 1779 | return IPS_SUCCESS_IMM; | 
|  | 1780 | } | 
|  | 1781 | error: | 
|  | 1782 | pt->BasicStatus = 0x0B; | 
|  | 1783 | pt->ExtendedStatus = 0x00; | 
|  | 1784 | ips_free_flash_copperhead(ha); | 
|  | 1785 | return IPS_FAILURE; | 
|  | 1786 | } | 
|  | 1787 |  | 
|  | 1788 | /****************************************************************************/ | 
|  | 1789 | /*                                                                          */ | 
|  | 1790 | /* Routine Name: ips_fill_scb_sg_single                                     */ | 
|  | 1791 | /*                                                                          */ | 
|  | 1792 | /* Routine Description:                                                     */ | 
|  | 1793 | /*   Fill in a single scb sg_list element from an address                   */ | 
|  | 1794 | /*   return a -1 if a breakup occurred                                      */ | 
|  | 1795 | /****************************************************************************/ | 
|  | 1796 | static int | 
|  | 1797 | ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr, | 
|  | 1798 | ips_scb_t * scb, int indx, unsigned int e_len) | 
|  | 1799 | { | 
|  | 1800 |  | 
|  | 1801 | int ret_val = 0; | 
|  | 1802 |  | 
|  | 1803 | if ((scb->data_len + e_len) > ha->max_xfer) { | 
|  | 1804 | e_len = ha->max_xfer - scb->data_len; | 
|  | 1805 | scb->breakup = indx; | 
|  | 1806 | ++scb->sg_break; | 
|  | 1807 | ret_val = -1; | 
|  | 1808 | } else { | 
|  | 1809 | scb->breakup = 0; | 
|  | 1810 | scb->sg_break = 0; | 
|  | 1811 | } | 
|  | 1812 | if (IPS_USE_ENH_SGLIST(ha)) { | 
|  | 1813 | scb->sg_list.enh_list[indx].address_lo = | 
|  | 1814 | cpu_to_le32(pci_dma_lo32(busaddr)); | 
|  | 1815 | scb->sg_list.enh_list[indx].address_hi = | 
|  | 1816 | cpu_to_le32(pci_dma_hi32(busaddr)); | 
|  | 1817 | scb->sg_list.enh_list[indx].length = cpu_to_le32(e_len); | 
|  | 1818 | } else { | 
|  | 1819 | scb->sg_list.std_list[indx].address = | 
|  | 1820 | cpu_to_le32(pci_dma_lo32(busaddr)); | 
|  | 1821 | scb->sg_list.std_list[indx].length = cpu_to_le32(e_len); | 
|  | 1822 | } | 
|  | 1823 |  | 
|  | 1824 | ++scb->sg_len; | 
|  | 1825 | scb->data_len += e_len; | 
|  | 1826 | return ret_val; | 
|  | 1827 | } | 
|  | 1828 |  | 
|  | 1829 | /****************************************************************************/ | 
|  | 1830 | /* Routine Name: ips_flash_firmware                                         */ | 
|  | 1831 | /* Routine Description:                                                     */ | 
|  | 1832 | /*   flashes the firmware of a copperhead adapter                           */ | 
|  | 1833 | /****************************************************************************/ | 
|  | 1834 | static int | 
|  | 1835 | ips_flash_firmware(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb) | 
|  | 1836 | { | 
|  | 1837 | IPS_SG_LIST sg_list; | 
|  | 1838 | uint32_t cmd_busaddr; | 
|  | 1839 |  | 
|  | 1840 | if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE && | 
|  | 1841 | pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_FW) { | 
|  | 1842 | memset(&pt->CoppCP.cmd, 0, sizeof (IPS_HOST_COMMAND)); | 
|  | 1843 | pt->CoppCP.cmd.flashfw.op_code = IPS_CMD_DOWNLOAD; | 
|  | 1844 | pt->CoppCP.cmd.flashfw.count = cpu_to_le32(ha->flash_datasize); | 
|  | 1845 | } else { | 
|  | 1846 | pt->BasicStatus = 0x0B; | 
|  | 1847 | pt->ExtendedStatus = 0x00; | 
|  | 1848 | ips_free_flash_copperhead(ha); | 
|  | 1849 | return IPS_FAILURE; | 
|  | 1850 | } | 
|  | 1851 | /* Save the S/G list pointer so it doesn't get clobbered */ | 
|  | 1852 | sg_list.list = scb->sg_list.list; | 
|  | 1853 | cmd_busaddr = scb->scb_busaddr; | 
|  | 1854 | /* copy in the CP */ | 
|  | 1855 | memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD)); | 
|  | 1856 | /* FIX stuff that might be wrong */ | 
|  | 1857 | scb->sg_list.list = sg_list.list; | 
|  | 1858 | scb->scb_busaddr = cmd_busaddr; | 
|  | 1859 | scb->bus = scb->scsi_cmd->device->channel; | 
|  | 1860 | scb->target_id = scb->scsi_cmd->device->id; | 
|  | 1861 | scb->lun = scb->scsi_cmd->device->lun; | 
|  | 1862 | scb->sg_len = 0; | 
|  | 1863 | scb->data_len = 0; | 
|  | 1864 | scb->flags = 0; | 
|  | 1865 | scb->op_code = 0; | 
|  | 1866 | scb->callback = ipsintr_done; | 
|  | 1867 | scb->timeout = ips_cmd_timeout; | 
|  | 1868 |  | 
|  | 1869 | scb->data_len = ha->flash_datasize; | 
|  | 1870 | scb->data_busaddr = | 
|  | 1871 | pci_map_single(ha->pcidev, ha->flash_data, scb->data_len, | 
|  | 1872 | IPS_DMA_DIR(scb)); | 
|  | 1873 | scb->flags |= IPS_SCB_MAP_SINGLE; | 
|  | 1874 | scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 1875 | scb->cmd.flashfw.buffer_addr = cpu_to_le32(scb->data_busaddr); | 
|  | 1876 | if (pt->TimeOut) | 
|  | 1877 | scb->timeout = pt->TimeOut; | 
|  | 1878 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 1879 | return IPS_SUCCESS; | 
|  | 1880 | } | 
|  | 1881 |  | 
|  | 1882 | /****************************************************************************/ | 
|  | 1883 | /* Routine Name: ips_free_flash_copperhead                                  */ | 
|  | 1884 | /* Routine Description:                                                     */ | 
|  | 1885 | /*   release the memory resources used to hold the flash image              */ | 
|  | 1886 | /****************************************************************************/ | 
|  | 1887 | static void | 
|  | 1888 | ips_free_flash_copperhead(ips_ha_t * ha) | 
|  | 1889 | { | 
|  | 1890 | if (ha->flash_data == ips_FlashData) | 
|  | 1891 | test_and_clear_bit(0, &ips_FlashDataInUse); | 
|  | 1892 | else if (ha->flash_data) | 
|  | 1893 | pci_free_consistent(ha->pcidev, ha->flash_len, ha->flash_data, | 
|  | 1894 | ha->flash_busaddr); | 
|  | 1895 | ha->flash_data = NULL; | 
|  | 1896 | } | 
|  | 1897 |  | 
|  | 1898 | /****************************************************************************/ | 
|  | 1899 | /*                                                                          */ | 
|  | 1900 | /* Routine Name: ips_usrcmd                                                 */ | 
|  | 1901 | /*                                                                          */ | 
|  | 1902 | /* Routine Description:                                                     */ | 
|  | 1903 | /*                                                                          */ | 
|  | 1904 | /*   Process a user command and make it ready to send                       */ | 
|  | 1905 | /*                                                                          */ | 
|  | 1906 | /****************************************************************************/ | 
|  | 1907 | static int | 
|  | 1908 | ips_usrcmd(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb) | 
|  | 1909 | { | 
|  | 1910 | IPS_SG_LIST sg_list; | 
|  | 1911 | uint32_t cmd_busaddr; | 
|  | 1912 |  | 
|  | 1913 | METHOD_TRACE("ips_usrcmd", 1); | 
|  | 1914 |  | 
|  | 1915 | if ((!scb) || (!pt) || (!ha)) | 
|  | 1916 | return (0); | 
|  | 1917 |  | 
|  | 1918 | /* Save the S/G list pointer so it doesn't get clobbered */ | 
|  | 1919 | sg_list.list = scb->sg_list.list; | 
|  | 1920 | cmd_busaddr = scb->scb_busaddr; | 
|  | 1921 | /* copy in the CP */ | 
|  | 1922 | memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD)); | 
|  | 1923 | memcpy(&scb->dcdb, &pt->CoppCP.dcdb, sizeof (IPS_DCDB_TABLE)); | 
|  | 1924 |  | 
|  | 1925 | /* FIX stuff that might be wrong */ | 
|  | 1926 | scb->sg_list.list = sg_list.list; | 
|  | 1927 | scb->scb_busaddr = cmd_busaddr; | 
|  | 1928 | scb->bus = scb->scsi_cmd->device->channel; | 
|  | 1929 | scb->target_id = scb->scsi_cmd->device->id; | 
|  | 1930 | scb->lun = scb->scsi_cmd->device->lun; | 
|  | 1931 | scb->sg_len = 0; | 
|  | 1932 | scb->data_len = 0; | 
|  | 1933 | scb->flags = 0; | 
|  | 1934 | scb->op_code = 0; | 
|  | 1935 | scb->callback = ipsintr_done; | 
|  | 1936 | scb->timeout = ips_cmd_timeout; | 
|  | 1937 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 1938 |  | 
|  | 1939 | /* we don't support DCDB/READ/WRITE Scatter Gather */ | 
|  | 1940 | if ((scb->cmd.basic_io.op_code == IPS_CMD_READ_SG) || | 
|  | 1941 | (scb->cmd.basic_io.op_code == IPS_CMD_WRITE_SG) || | 
|  | 1942 | (scb->cmd.basic_io.op_code == IPS_CMD_DCDB_SG)) | 
|  | 1943 | return (0); | 
|  | 1944 |  | 
|  | 1945 | if (pt->CmdBSize) { | 
|  | 1946 | scb->data_len = pt->CmdBSize; | 
|  | 1947 | scb->data_busaddr = ha->ioctl_busaddr + sizeof (ips_passthru_t); | 
|  | 1948 | } else { | 
|  | 1949 | scb->data_busaddr = 0L; | 
|  | 1950 | } | 
|  | 1951 |  | 
|  | 1952 | if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) | 
|  | 1953 | scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr + | 
|  | 1954 | (unsigned long) &scb-> | 
|  | 1955 | dcdb - | 
|  | 1956 | (unsigned long) scb); | 
|  | 1957 |  | 
|  | 1958 | if (pt->CmdBSize) { | 
|  | 1959 | if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) | 
|  | 1960 | scb->dcdb.buffer_pointer = | 
|  | 1961 | cpu_to_le32(scb->data_busaddr); | 
|  | 1962 | else | 
|  | 1963 | scb->cmd.basic_io.sg_addr = | 
|  | 1964 | cpu_to_le32(scb->data_busaddr); | 
|  | 1965 | } | 
|  | 1966 |  | 
|  | 1967 | /* set timeouts */ | 
|  | 1968 | if (pt->TimeOut) { | 
|  | 1969 | scb->timeout = pt->TimeOut; | 
|  | 1970 |  | 
|  | 1971 | if (pt->TimeOut <= 10) | 
|  | 1972 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT10; | 
|  | 1973 | else if (pt->TimeOut <= 60) | 
|  | 1974 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT60; | 
|  | 1975 | else | 
|  | 1976 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M; | 
|  | 1977 | } | 
|  | 1978 |  | 
|  | 1979 | /* assume success */ | 
|  | 1980 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 1981 |  | 
|  | 1982 | /* success */ | 
|  | 1983 | return (1); | 
|  | 1984 | } | 
|  | 1985 |  | 
|  | 1986 | /****************************************************************************/ | 
|  | 1987 | /*                                                                          */ | 
|  | 1988 | /* Routine Name: ips_cleanup_passthru                                       */ | 
|  | 1989 | /*                                                                          */ | 
|  | 1990 | /* Routine Description:                                                     */ | 
|  | 1991 | /*                                                                          */ | 
|  | 1992 | /*   Cleanup after a passthru command                                       */ | 
|  | 1993 | /*                                                                          */ | 
|  | 1994 | /****************************************************************************/ | 
|  | 1995 | static void | 
|  | 1996 | ips_cleanup_passthru(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 1997 | { | 
|  | 1998 | ips_passthru_t *pt; | 
|  | 1999 |  | 
|  | 2000 | METHOD_TRACE("ips_cleanup_passthru", 1); | 
|  | 2001 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 2002 | if ((!scb) || (!scb->scsi_cmd) || (!scsi_sglist(scb->scsi_cmd))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2003 | DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru", | 
|  | 2004 | ips_name, ha->host_num); | 
|  | 2005 |  | 
|  | 2006 | return; | 
|  | 2007 | } | 
|  | 2008 | pt = (ips_passthru_t *) ha->ioctl_data; | 
|  | 2009 |  | 
|  | 2010 | /* Copy data back to the user */ | 
|  | 2011 | if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)	/* Copy DCDB Back to Caller's Area */ | 
|  | 2012 | memcpy(&pt->CoppCP.dcdb, &scb->dcdb, sizeof (IPS_DCDB_TABLE)); | 
|  | 2013 |  | 
|  | 2014 | pt->BasicStatus = scb->basic_status; | 
|  | 2015 | pt->ExtendedStatus = scb->extended_status; | 
|  | 2016 | pt->AdapterType = ha->ad_type; | 
|  | 2017 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2018 | if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD && | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2019 | (scb->cmd.flashfw.op_code == IPS_CMD_DOWNLOAD || | 
|  | 2020 | scb->cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW)) | 
|  | 2021 | ips_free_flash_copperhead(ha); | 
|  | 2022 |  | 
|  | 2023 | ips_scmd_buf_write(scb->scsi_cmd, ha->ioctl_data, ha->ioctl_datasize); | 
|  | 2024 | } | 
|  | 2025 |  | 
|  | 2026 | /****************************************************************************/ | 
|  | 2027 | /*                                                                          */ | 
|  | 2028 | /* Routine Name: ips_host_info                                              */ | 
|  | 2029 | /*                                                                          */ | 
|  | 2030 | /* Routine Description:                                                     */ | 
|  | 2031 | /*                                                                          */ | 
|  | 2032 | /*   The passthru interface for the driver                                  */ | 
|  | 2033 | /*                                                                          */ | 
|  | 2034 | /****************************************************************************/ | 
|  | 2035 | static int | 
|  | 2036 | ips_host_info(ips_ha_t * ha, char *ptr, off_t offset, int len) | 
|  | 2037 | { | 
|  | 2038 | IPS_INFOSTR info; | 
|  | 2039 |  | 
|  | 2040 | METHOD_TRACE("ips_host_info", 1); | 
|  | 2041 |  | 
|  | 2042 | info.buffer = ptr; | 
|  | 2043 | info.length = len; | 
|  | 2044 | info.offset = offset; | 
|  | 2045 | info.pos = 0; | 
|  | 2046 | info.localpos = 0; | 
|  | 2047 |  | 
|  | 2048 | copy_info(&info, "\nIBM ServeRAID General Information:\n\n"); | 
|  | 2049 |  | 
|  | 2050 | if ((le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) && | 
|  | 2051 | (le16_to_cpu(ha->nvram->adapter_type) != 0)) | 
|  | 2052 | copy_info(&info, "\tController Type                   : %s\n", | 
|  | 2053 | ips_adapter_name[ha->ad_type - 1]); | 
|  | 2054 | else | 
|  | 2055 | copy_info(&info, | 
|  | 2056 | "\tController Type                   : Unknown\n"); | 
|  | 2057 |  | 
|  | 2058 | if (ha->io_addr) | 
|  | 2059 | copy_info(&info, | 
|  | 2060 | "\tIO region                         : 0x%lx (%d bytes)\n", | 
|  | 2061 | ha->io_addr, ha->io_len); | 
|  | 2062 |  | 
|  | 2063 | if (ha->mem_addr) { | 
|  | 2064 | copy_info(&info, | 
|  | 2065 | "\tMemory region                     : 0x%lx (%d bytes)\n", | 
|  | 2066 | ha->mem_addr, ha->mem_len); | 
|  | 2067 | copy_info(&info, | 
|  | 2068 | "\tShared memory address             : 0x%lx\n", | 
|  | 2069 | ha->mem_ptr); | 
|  | 2070 | } | 
|  | 2071 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2072 | copy_info(&info, "\tIRQ number                        : %d\n", ha->pcidev->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2073 |  | 
|  | 2074 | /* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */ | 
|  | 2075 | /* That keeps everything happy for "text" operations on the proc file.                    */ | 
|  | 2076 |  | 
|  | 2077 | if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) { | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 2078 | if (ha->nvram->bios_low[3] == 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2079 | copy_info(&info, | 
|  | 2080 | "\tBIOS Version                      : %c%c%c%c%c%c%c\n", | 
|  | 2081 | ha->nvram->bios_high[0], ha->nvram->bios_high[1], | 
|  | 2082 | ha->nvram->bios_high[2], ha->nvram->bios_high[3], | 
|  | 2083 | ha->nvram->bios_low[0], ha->nvram->bios_low[1], | 
|  | 2084 | ha->nvram->bios_low[2]); | 
|  | 2085 |  | 
|  | 2086 | } else { | 
|  | 2087 | copy_info(&info, | 
|  | 2088 | "\tBIOS Version                      : %c%c%c%c%c%c%c%c\n", | 
|  | 2089 | ha->nvram->bios_high[0], ha->nvram->bios_high[1], | 
|  | 2090 | ha->nvram->bios_high[2], ha->nvram->bios_high[3], | 
|  | 2091 | ha->nvram->bios_low[0], ha->nvram->bios_low[1], | 
|  | 2092 | ha->nvram->bios_low[2], ha->nvram->bios_low[3]); | 
|  | 2093 | } | 
|  | 2094 |  | 
|  | 2095 | } | 
|  | 2096 |  | 
|  | 2097 | if (ha->enq->CodeBlkVersion[7] == 0) { | 
|  | 2098 | copy_info(&info, | 
|  | 2099 | "\tFirmware Version                  : %c%c%c%c%c%c%c\n", | 
|  | 2100 | ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1], | 
|  | 2101 | ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3], | 
|  | 2102 | ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5], | 
|  | 2103 | ha->enq->CodeBlkVersion[6]); | 
|  | 2104 | } else { | 
|  | 2105 | copy_info(&info, | 
|  | 2106 | "\tFirmware Version                  : %c%c%c%c%c%c%c%c\n", | 
|  | 2107 | ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1], | 
|  | 2108 | ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3], | 
|  | 2109 | ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5], | 
|  | 2110 | ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]); | 
|  | 2111 | } | 
|  | 2112 |  | 
|  | 2113 | if (ha->enq->BootBlkVersion[7] == 0) { | 
|  | 2114 | copy_info(&info, | 
|  | 2115 | "\tBoot Block Version                : %c%c%c%c%c%c%c\n", | 
|  | 2116 | ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1], | 
|  | 2117 | ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3], | 
|  | 2118 | ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5], | 
|  | 2119 | ha->enq->BootBlkVersion[6]); | 
|  | 2120 | } else { | 
|  | 2121 | copy_info(&info, | 
|  | 2122 | "\tBoot Block Version                : %c%c%c%c%c%c%c%c\n", | 
|  | 2123 | ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1], | 
|  | 2124 | ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3], | 
|  | 2125 | ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5], | 
|  | 2126 | ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]); | 
|  | 2127 | } | 
|  | 2128 |  | 
|  | 2129 | copy_info(&info, "\tDriver Version                    : %s%s\n", | 
|  | 2130 | IPS_VERSION_HIGH, IPS_VERSION_LOW); | 
|  | 2131 |  | 
|  | 2132 | copy_info(&info, "\tDriver Build                      : %d\n", | 
|  | 2133 | IPS_BUILD_IDENT); | 
|  | 2134 |  | 
|  | 2135 | copy_info(&info, "\tMax Physical Devices              : %d\n", | 
|  | 2136 | ha->enq->ucMaxPhysicalDevices); | 
|  | 2137 | copy_info(&info, "\tMax Active Commands               : %d\n", | 
|  | 2138 | ha->max_cmds); | 
|  | 2139 | copy_info(&info, "\tCurrent Queued Commands           : %d\n", | 
|  | 2140 | ha->scb_waitlist.count); | 
|  | 2141 | copy_info(&info, "\tCurrent Active Commands           : %d\n", | 
|  | 2142 | ha->scb_activelist.count - ha->num_ioctl); | 
|  | 2143 | copy_info(&info, "\tCurrent Queued PT Commands        : %d\n", | 
|  | 2144 | ha->copp_waitlist.count); | 
|  | 2145 | copy_info(&info, "\tCurrent Active PT Commands        : %d\n", | 
|  | 2146 | ha->num_ioctl); | 
|  | 2147 |  | 
|  | 2148 | copy_info(&info, "\n"); | 
|  | 2149 |  | 
|  | 2150 | return (info.localpos); | 
|  | 2151 | } | 
|  | 2152 |  | 
|  | 2153 | /****************************************************************************/ | 
|  | 2154 | /*                                                                          */ | 
|  | 2155 | /* Routine Name: copy_mem_info                                              */ | 
|  | 2156 | /*                                                                          */ | 
|  | 2157 | /* Routine Description:                                                     */ | 
|  | 2158 | /*                                                                          */ | 
|  | 2159 | /*   Copy data into an IPS_INFOSTR structure                                */ | 
|  | 2160 | /*                                                                          */ | 
|  | 2161 | /****************************************************************************/ | 
|  | 2162 | static void | 
|  | 2163 | copy_mem_info(IPS_INFOSTR * info, char *data, int len) | 
|  | 2164 | { | 
|  | 2165 | METHOD_TRACE("copy_mem_info", 1); | 
|  | 2166 |  | 
|  | 2167 | if (info->pos + len < info->offset) { | 
|  | 2168 | info->pos += len; | 
|  | 2169 | return; | 
|  | 2170 | } | 
|  | 2171 |  | 
|  | 2172 | if (info->pos < info->offset) { | 
|  | 2173 | data += (info->offset - info->pos); | 
|  | 2174 | len -= (info->offset - info->pos); | 
|  | 2175 | info->pos += (info->offset - info->pos); | 
|  | 2176 | } | 
|  | 2177 |  | 
|  | 2178 | if (info->localpos + len > info->length) | 
|  | 2179 | len = info->length - info->localpos; | 
|  | 2180 |  | 
|  | 2181 | if (len > 0) { | 
|  | 2182 | memcpy(info->buffer + info->localpos, data, len); | 
|  | 2183 | info->pos += len; | 
|  | 2184 | info->localpos += len; | 
|  | 2185 | } | 
|  | 2186 | } | 
|  | 2187 |  | 
|  | 2188 | /****************************************************************************/ | 
|  | 2189 | /*                                                                          */ | 
|  | 2190 | /* Routine Name: copy_info                                                  */ | 
|  | 2191 | /*                                                                          */ | 
|  | 2192 | /* Routine Description:                                                     */ | 
|  | 2193 | /*                                                                          */ | 
|  | 2194 | /*   printf style wrapper for an info structure                             */ | 
|  | 2195 | /*                                                                          */ | 
|  | 2196 | /****************************************************************************/ | 
|  | 2197 | static int | 
|  | 2198 | copy_info(IPS_INFOSTR * info, char *fmt, ...) | 
|  | 2199 | { | 
|  | 2200 | va_list args; | 
|  | 2201 | char buf[128]; | 
|  | 2202 | int len; | 
|  | 2203 |  | 
|  | 2204 | METHOD_TRACE("copy_info", 1); | 
|  | 2205 |  | 
|  | 2206 | va_start(args, fmt); | 
|  | 2207 | len = vsprintf(buf, fmt, args); | 
|  | 2208 | va_end(args); | 
|  | 2209 |  | 
|  | 2210 | copy_mem_info(info, buf, len); | 
|  | 2211 |  | 
|  | 2212 | return (len); | 
|  | 2213 | } | 
|  | 2214 |  | 
|  | 2215 | /****************************************************************************/ | 
|  | 2216 | /*                                                                          */ | 
|  | 2217 | /* Routine Name: ips_identify_controller                                    */ | 
|  | 2218 | /*                                                                          */ | 
|  | 2219 | /* Routine Description:                                                     */ | 
|  | 2220 | /*                                                                          */ | 
|  | 2221 | /*   Identify this controller                                               */ | 
|  | 2222 | /*                                                                          */ | 
|  | 2223 | /****************************************************************************/ | 
|  | 2224 | static void | 
|  | 2225 | ips_identify_controller(ips_ha_t * ha) | 
|  | 2226 | { | 
|  | 2227 | METHOD_TRACE("ips_identify_controller", 1); | 
|  | 2228 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2229 | switch (ha->pcidev->device) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2230 | case IPS_DEVICEID_COPPERHEAD: | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2231 | if (ha->pcidev->revision <= IPS_REVID_SERVERAID) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2232 | ha->ad_type = IPS_ADTYPE_SERVERAID; | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2233 | } else if (ha->pcidev->revision == IPS_REVID_SERVERAID2) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2234 | ha->ad_type = IPS_ADTYPE_SERVERAID2; | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2235 | } else if (ha->pcidev->revision == IPS_REVID_NAVAJO) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2236 | ha->ad_type = IPS_ADTYPE_NAVAJO; | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2237 | } else if ((ha->pcidev->revision == IPS_REVID_SERVERAID2) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2238 | && (ha->slot_num == 0)) { | 
|  | 2239 | ha->ad_type = IPS_ADTYPE_KIOWA; | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2240 | } else if ((ha->pcidev->revision >= IPS_REVID_CLARINETP1) && | 
|  | 2241 | (ha->pcidev->revision <= IPS_REVID_CLARINETP3)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2242 | if (ha->enq->ucMaxPhysicalDevices == 15) | 
|  | 2243 | ha->ad_type = IPS_ADTYPE_SERVERAID3L; | 
|  | 2244 | else | 
|  | 2245 | ha->ad_type = IPS_ADTYPE_SERVERAID3; | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2246 | } else if ((ha->pcidev->revision >= IPS_REVID_TROMBONE32) && | 
|  | 2247 | (ha->pcidev->revision <= IPS_REVID_TROMBONE64)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2248 | ha->ad_type = IPS_ADTYPE_SERVERAID4H; | 
|  | 2249 | } | 
|  | 2250 | break; | 
|  | 2251 |  | 
|  | 2252 | case IPS_DEVICEID_MORPHEUS: | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2253 | switch (ha->pcidev->subsystem_device) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2254 | case IPS_SUBDEVICEID_4L: | 
|  | 2255 | ha->ad_type = IPS_ADTYPE_SERVERAID4L; | 
|  | 2256 | break; | 
|  | 2257 |  | 
|  | 2258 | case IPS_SUBDEVICEID_4M: | 
|  | 2259 | ha->ad_type = IPS_ADTYPE_SERVERAID4M; | 
|  | 2260 | break; | 
|  | 2261 |  | 
|  | 2262 | case IPS_SUBDEVICEID_4MX: | 
|  | 2263 | ha->ad_type = IPS_ADTYPE_SERVERAID4MX; | 
|  | 2264 | break; | 
|  | 2265 |  | 
|  | 2266 | case IPS_SUBDEVICEID_4LX: | 
|  | 2267 | ha->ad_type = IPS_ADTYPE_SERVERAID4LX; | 
|  | 2268 | break; | 
|  | 2269 |  | 
|  | 2270 | case IPS_SUBDEVICEID_5I2: | 
|  | 2271 | ha->ad_type = IPS_ADTYPE_SERVERAID5I2; | 
|  | 2272 | break; | 
|  | 2273 |  | 
|  | 2274 | case IPS_SUBDEVICEID_5I1: | 
|  | 2275 | ha->ad_type = IPS_ADTYPE_SERVERAID5I1; | 
|  | 2276 | break; | 
|  | 2277 | } | 
|  | 2278 |  | 
|  | 2279 | break; | 
|  | 2280 |  | 
|  | 2281 | case IPS_DEVICEID_MARCO: | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2282 | switch (ha->pcidev->subsystem_device) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2283 | case IPS_SUBDEVICEID_6M: | 
|  | 2284 | ha->ad_type = IPS_ADTYPE_SERVERAID6M; | 
|  | 2285 | break; | 
|  | 2286 | case IPS_SUBDEVICEID_6I: | 
|  | 2287 | ha->ad_type = IPS_ADTYPE_SERVERAID6I; | 
|  | 2288 | break; | 
|  | 2289 | case IPS_SUBDEVICEID_7k: | 
|  | 2290 | ha->ad_type = IPS_ADTYPE_SERVERAID7k; | 
|  | 2291 | break; | 
|  | 2292 | case IPS_SUBDEVICEID_7M: | 
|  | 2293 | ha->ad_type = IPS_ADTYPE_SERVERAID7M; | 
|  | 2294 | break; | 
|  | 2295 | } | 
|  | 2296 | break; | 
|  | 2297 | } | 
|  | 2298 | } | 
|  | 2299 |  | 
|  | 2300 | /****************************************************************************/ | 
|  | 2301 | /*                                                                          */ | 
|  | 2302 | /* Routine Name: ips_get_bios_version                                       */ | 
|  | 2303 | /*                                                                          */ | 
|  | 2304 | /* Routine Description:                                                     */ | 
|  | 2305 | /*                                                                          */ | 
|  | 2306 | /*   Get the BIOS revision number                                           */ | 
|  | 2307 | /*                                                                          */ | 
|  | 2308 | /****************************************************************************/ | 
|  | 2309 | static void | 
|  | 2310 | ips_get_bios_version(ips_ha_t * ha, int intr) | 
|  | 2311 | { | 
|  | 2312 | ips_scb_t *scb; | 
|  | 2313 | int ret; | 
|  | 2314 | uint8_t major; | 
|  | 2315 | uint8_t minor; | 
|  | 2316 | uint8_t subminor; | 
|  | 2317 | uint8_t *buffer; | 
|  | 2318 | char hexDigits[] = | 
|  | 2319 | { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', | 
|  | 2320 | 'D', 'E', 'F' }; | 
|  | 2321 |  | 
|  | 2322 | METHOD_TRACE("ips_get_bios_version", 1); | 
|  | 2323 |  | 
|  | 2324 | major = 0; | 
|  | 2325 | minor = 0; | 
|  | 2326 |  | 
|  | 2327 | strncpy(ha->bios_version, "       ?", 8); | 
|  | 2328 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2329 | if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2330 | if (IPS_USE_MEMIO(ha)) { | 
|  | 2331 | /* Memory Mapped I/O */ | 
|  | 2332 |  | 
|  | 2333 | /* test 1st byte */ | 
|  | 2334 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2335 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2336 | udelay(25);	/* 25 us */ | 
|  | 2337 |  | 
|  | 2338 | if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55) | 
|  | 2339 | return; | 
|  | 2340 |  | 
|  | 2341 | writel(1, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2342 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2343 | udelay(25);	/* 25 us */ | 
|  | 2344 |  | 
|  | 2345 | if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA) | 
|  | 2346 | return; | 
|  | 2347 |  | 
|  | 2348 | /* Get Major version */ | 
|  | 2349 | writel(0x1FF, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2350 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2351 | udelay(25);	/* 25 us */ | 
|  | 2352 |  | 
|  | 2353 | major = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 2354 |  | 
|  | 2355 | /* Get Minor version */ | 
|  | 2356 | writel(0x1FE, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2357 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2358 | udelay(25);	/* 25 us */ | 
|  | 2359 | minor = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 2360 |  | 
|  | 2361 | /* Get SubMinor version */ | 
|  | 2362 | writel(0x1FD, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2363 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2364 | udelay(25);	/* 25 us */ | 
|  | 2365 | subminor = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 2366 |  | 
|  | 2367 | } else { | 
|  | 2368 | /* Programmed I/O */ | 
|  | 2369 |  | 
|  | 2370 | /* test 1st byte */ | 
|  | 2371 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2372 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2373 | udelay(25);	/* 25 us */ | 
|  | 2374 |  | 
|  | 2375 | if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55) | 
|  | 2376 | return; | 
|  | 2377 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 2378 | outl(1, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2379 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2380 | udelay(25);	/* 25 us */ | 
|  | 2381 |  | 
|  | 2382 | if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA) | 
|  | 2383 | return; | 
|  | 2384 |  | 
|  | 2385 | /* Get Major version */ | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 2386 | outl(0x1FF, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2387 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2388 | udelay(25);	/* 25 us */ | 
|  | 2389 |  | 
|  | 2390 | major = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 2391 |  | 
|  | 2392 | /* Get Minor version */ | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 2393 | outl(0x1FE, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2394 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2395 | udelay(25);	/* 25 us */ | 
|  | 2396 |  | 
|  | 2397 | minor = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 2398 |  | 
|  | 2399 | /* Get SubMinor version */ | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 2400 | outl(0x1FD, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 2401 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2402 | udelay(25);	/* 25 us */ | 
|  | 2403 |  | 
|  | 2404 | subminor = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 2405 |  | 
|  | 2406 | } | 
|  | 2407 | } else { | 
|  | 2408 | /* Morpheus Family - Send Command to the card */ | 
|  | 2409 |  | 
|  | 2410 | buffer = ha->ioctl_data; | 
|  | 2411 |  | 
|  | 2412 | memset(buffer, 0, 0x1000); | 
|  | 2413 |  | 
|  | 2414 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 2415 |  | 
|  | 2416 | ips_init_scb(ha, scb); | 
|  | 2417 |  | 
|  | 2418 | scb->timeout = ips_cmd_timeout; | 
|  | 2419 | scb->cdb[0] = IPS_CMD_RW_BIOSFW; | 
|  | 2420 |  | 
|  | 2421 | scb->cmd.flashfw.op_code = IPS_CMD_RW_BIOSFW; | 
|  | 2422 | scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 2423 | scb->cmd.flashfw.type = 1; | 
|  | 2424 | scb->cmd.flashfw.direction = 0; | 
|  | 2425 | scb->cmd.flashfw.count = cpu_to_le32(0x800); | 
|  | 2426 | scb->cmd.flashfw.total_packets = 1; | 
|  | 2427 | scb->cmd.flashfw.packet_num = 0; | 
|  | 2428 | scb->data_len = 0x1000; | 
|  | 2429 | scb->cmd.flashfw.buffer_addr = ha->ioctl_busaddr; | 
|  | 2430 |  | 
|  | 2431 | /* issue the command */ | 
|  | 2432 | if (((ret = | 
|  | 2433 | ips_send_wait(ha, scb, ips_cmd_timeout, | 
|  | 2434 | intr)) == IPS_FAILURE) | 
|  | 2435 | || (ret == IPS_SUCCESS_IMM) | 
|  | 2436 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { | 
|  | 2437 | /* Error occurred */ | 
|  | 2438 |  | 
|  | 2439 | return; | 
|  | 2440 | } | 
|  | 2441 |  | 
|  | 2442 | if ((buffer[0xC0] == 0x55) && (buffer[0xC1] == 0xAA)) { | 
|  | 2443 | major = buffer[0x1ff + 0xC0];	/* Offset 0x1ff after the header (0xc0) */ | 
|  | 2444 | minor = buffer[0x1fe + 0xC0];	/* Offset 0x1fe after the header (0xc0) */ | 
|  | 2445 | subminor = buffer[0x1fd + 0xC0];	/* Offset 0x1fd after the header (0xc0) */ | 
|  | 2446 | } else { | 
|  | 2447 | return; | 
|  | 2448 | } | 
|  | 2449 | } | 
|  | 2450 |  | 
|  | 2451 | ha->bios_version[0] = hexDigits[(major & 0xF0) >> 4]; | 
|  | 2452 | ha->bios_version[1] = '.'; | 
|  | 2453 | ha->bios_version[2] = hexDigits[major & 0x0F]; | 
|  | 2454 | ha->bios_version[3] = hexDigits[subminor]; | 
|  | 2455 | ha->bios_version[4] = '.'; | 
|  | 2456 | ha->bios_version[5] = hexDigits[(minor & 0xF0) >> 4]; | 
|  | 2457 | ha->bios_version[6] = hexDigits[minor & 0x0F]; | 
|  | 2458 | ha->bios_version[7] = 0; | 
|  | 2459 | } | 
|  | 2460 |  | 
|  | 2461 | /****************************************************************************/ | 
|  | 2462 | /*                                                                          */ | 
|  | 2463 | /* Routine Name: ips_hainit                                                 */ | 
|  | 2464 | /*                                                                          */ | 
|  | 2465 | /* Routine Description:                                                     */ | 
|  | 2466 | /*                                                                          */ | 
|  | 2467 | /*   Initialize the controller                                              */ | 
|  | 2468 | /*                                                                          */ | 
|  | 2469 | /* NOTE: Assumes to be called from with a lock                              */ | 
|  | 2470 | /*                                                                          */ | 
|  | 2471 | /****************************************************************************/ | 
|  | 2472 | static int | 
|  | 2473 | ips_hainit(ips_ha_t * ha) | 
|  | 2474 | { | 
|  | 2475 | int i; | 
|  | 2476 | struct timeval tv; | 
|  | 2477 |  | 
|  | 2478 | METHOD_TRACE("ips_hainit", 1); | 
|  | 2479 |  | 
|  | 2480 | if (!ha) | 
|  | 2481 | return (0); | 
|  | 2482 |  | 
|  | 2483 | if (ha->func.statinit) | 
|  | 2484 | (*ha->func.statinit) (ha); | 
|  | 2485 |  | 
|  | 2486 | if (ha->func.enableint) | 
|  | 2487 | (*ha->func.enableint) (ha); | 
|  | 2488 |  | 
|  | 2489 | /* Send FFDC */ | 
|  | 2490 | ha->reset_count = 1; | 
|  | 2491 | do_gettimeofday(&tv); | 
|  | 2492 | ha->last_ffdc = tv.tv_sec; | 
|  | 2493 | ips_ffdc_reset(ha, IPS_INTR_IORL); | 
|  | 2494 |  | 
|  | 2495 | if (!ips_read_config(ha, IPS_INTR_IORL)) { | 
|  | 2496 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 2497 | "unable to read config from controller.\n"); | 
|  | 2498 |  | 
|  | 2499 | return (0); | 
|  | 2500 | } | 
|  | 2501 | /* end if */ | 
|  | 2502 | if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) { | 
|  | 2503 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 2504 | "unable to read controller status.\n"); | 
|  | 2505 |  | 
|  | 2506 | return (0); | 
|  | 2507 | } | 
|  | 2508 |  | 
|  | 2509 | /* Identify this controller */ | 
|  | 2510 | ips_identify_controller(ha); | 
|  | 2511 |  | 
|  | 2512 | if (!ips_read_subsystem_parameters(ha, IPS_INTR_IORL)) { | 
|  | 2513 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 2514 | "unable to read subsystem parameters.\n"); | 
|  | 2515 |  | 
|  | 2516 | return (0); | 
|  | 2517 | } | 
|  | 2518 |  | 
|  | 2519 | /* write nvram user page 5 */ | 
|  | 2520 | if (!ips_write_driver_status(ha, IPS_INTR_IORL)) { | 
|  | 2521 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 2522 | "unable to write driver info to controller.\n"); | 
|  | 2523 |  | 
|  | 2524 | return (0); | 
|  | 2525 | } | 
|  | 2526 |  | 
|  | 2527 | /* If there are Logical Drives and a Reset Occurred, then an EraseStripeLock is Needed */ | 
|  | 2528 | if ((ha->conf->ucLogDriveCount > 0) && (ha->requires_esl == 1)) | 
|  | 2529 | ips_clear_adapter(ha, IPS_INTR_IORL); | 
|  | 2530 |  | 
|  | 2531 | /* set limits on SID, LUN, BUS */ | 
|  | 2532 | ha->ntargets = IPS_MAX_TARGETS + 1; | 
|  | 2533 | ha->nlun = 1; | 
|  | 2534 | ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1; | 
|  | 2535 |  | 
|  | 2536 | switch (ha->conf->logical_drive[0].ucStripeSize) { | 
|  | 2537 | case 4: | 
|  | 2538 | ha->max_xfer = 0x10000; | 
|  | 2539 | break; | 
|  | 2540 |  | 
|  | 2541 | case 5: | 
|  | 2542 | ha->max_xfer = 0x20000; | 
|  | 2543 | break; | 
|  | 2544 |  | 
|  | 2545 | case 6: | 
|  | 2546 | ha->max_xfer = 0x40000; | 
|  | 2547 | break; | 
|  | 2548 |  | 
|  | 2549 | case 7: | 
|  | 2550 | default: | 
|  | 2551 | ha->max_xfer = 0x80000; | 
|  | 2552 | break; | 
|  | 2553 | } | 
|  | 2554 |  | 
|  | 2555 | /* setup max concurrent commands */ | 
|  | 2556 | if (le32_to_cpu(ha->subsys->param[4]) & 0x1) { | 
|  | 2557 | /* Use the new method */ | 
|  | 2558 | ha->max_cmds = ha->enq->ucConcurrentCmdCount; | 
|  | 2559 | } else { | 
|  | 2560 | /* use the old method */ | 
|  | 2561 | switch (ha->conf->logical_drive[0].ucStripeSize) { | 
|  | 2562 | case 4: | 
|  | 2563 | ha->max_cmds = 32; | 
|  | 2564 | break; | 
|  | 2565 |  | 
|  | 2566 | case 5: | 
|  | 2567 | ha->max_cmds = 16; | 
|  | 2568 | break; | 
|  | 2569 |  | 
|  | 2570 | case 6: | 
|  | 2571 | ha->max_cmds = 8; | 
|  | 2572 | break; | 
|  | 2573 |  | 
|  | 2574 | case 7: | 
|  | 2575 | default: | 
|  | 2576 | ha->max_cmds = 4; | 
|  | 2577 | break; | 
|  | 2578 | } | 
|  | 2579 | } | 
|  | 2580 |  | 
|  | 2581 | /* Limit the Active Commands on a Lite Adapter */ | 
|  | 2582 | if ((ha->ad_type == IPS_ADTYPE_SERVERAID3L) || | 
|  | 2583 | (ha->ad_type == IPS_ADTYPE_SERVERAID4L) || | 
|  | 2584 | (ha->ad_type == IPS_ADTYPE_SERVERAID4LX)) { | 
|  | 2585 | if ((ha->max_cmds > MaxLiteCmds) && (MaxLiteCmds)) | 
|  | 2586 | ha->max_cmds = MaxLiteCmds; | 
|  | 2587 | } | 
|  | 2588 |  | 
|  | 2589 | /* set controller IDs */ | 
|  | 2590 | ha->ha_id[0] = IPS_ADAPTER_ID; | 
|  | 2591 | for (i = 1; i < ha->nbus; i++) { | 
|  | 2592 | ha->ha_id[i] = ha->conf->init_id[i - 1] & 0x1f; | 
|  | 2593 | ha->dcdb_active[i - 1] = 0; | 
|  | 2594 | } | 
|  | 2595 |  | 
|  | 2596 | return (1); | 
|  | 2597 | } | 
|  | 2598 |  | 
|  | 2599 | /****************************************************************************/ | 
|  | 2600 | /*                                                                          */ | 
|  | 2601 | /* Routine Name: ips_next                                                   */ | 
|  | 2602 | /*                                                                          */ | 
|  | 2603 | /* Routine Description:                                                     */ | 
|  | 2604 | /*                                                                          */ | 
|  | 2605 | /*   Take the next command off the queue and send it to the controller      */ | 
|  | 2606 | /*                                                                          */ | 
|  | 2607 | /****************************************************************************/ | 
|  | 2608 | static void | 
|  | 2609 | ips_next(ips_ha_t * ha, int intr) | 
|  | 2610 | { | 
|  | 2611 | ips_scb_t *scb; | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2612 | struct scsi_cmnd *SC; | 
|  | 2613 | struct scsi_cmnd *p; | 
|  | 2614 | struct scsi_cmnd *q; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2615 | ips_copp_wait_item_t *item; | 
|  | 2616 | int ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2617 | struct Scsi_Host *host; | 
|  | 2618 | METHOD_TRACE("ips_next", 1); | 
|  | 2619 |  | 
|  | 2620 | if (!ha) | 
|  | 2621 | return; | 
|  | 2622 | host = ips_sh[ha->host_num]; | 
|  | 2623 | /* | 
|  | 2624 | * Block access to the queue function so | 
|  | 2625 | * this command won't time out | 
|  | 2626 | */ | 
|  | 2627 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2628 | spin_lock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2629 |  | 
|  | 2630 | if ((ha->subsys->param[3] & 0x300000) | 
|  | 2631 | && (ha->scb_activelist.count == 0)) { | 
|  | 2632 | struct timeval tv; | 
|  | 2633 |  | 
|  | 2634 | do_gettimeofday(&tv); | 
|  | 2635 |  | 
|  | 2636 | if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) { | 
|  | 2637 | ha->last_ffdc = tv.tv_sec; | 
|  | 2638 | ips_ffdc_time(ha); | 
|  | 2639 | } | 
|  | 2640 | } | 
|  | 2641 |  | 
|  | 2642 | /* | 
|  | 2643 | * Send passthru commands | 
|  | 2644 | * These have priority over normal I/O | 
|  | 2645 | * but shouldn't affect performance too much | 
|  | 2646 | * since we limit the number that can be active | 
|  | 2647 | * on the card at any one time | 
|  | 2648 | */ | 
|  | 2649 | while ((ha->num_ioctl < IPS_MAX_IOCTL) && | 
|  | 2650 | (ha->copp_waitlist.head) && (scb = ips_getscb(ha))) { | 
|  | 2651 |  | 
|  | 2652 | item = ips_removeq_copp_head(&ha->copp_waitlist); | 
|  | 2653 | ha->num_ioctl++; | 
|  | 2654 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2655 | spin_unlock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2656 | scb->scsi_cmd = item->scsi_cmd; | 
|  | 2657 | kfree(item); | 
|  | 2658 |  | 
|  | 2659 | ret = ips_make_passthru(ha, scb->scsi_cmd, scb, intr); | 
|  | 2660 |  | 
|  | 2661 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2662 | spin_lock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2663 | switch (ret) { | 
|  | 2664 | case IPS_FAILURE: | 
|  | 2665 | if (scb->scsi_cmd) { | 
|  | 2666 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 2667 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 2668 | } | 
|  | 2669 |  | 
|  | 2670 | ips_freescb(ha, scb); | 
|  | 2671 | break; | 
|  | 2672 | case IPS_SUCCESS_IMM: | 
|  | 2673 | if (scb->scsi_cmd) { | 
|  | 2674 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 2675 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 2676 | } | 
|  | 2677 |  | 
|  | 2678 | ips_freescb(ha, scb); | 
|  | 2679 | break; | 
|  | 2680 | default: | 
|  | 2681 | break; | 
|  | 2682 | }		/* end case */ | 
|  | 2683 |  | 
|  | 2684 | if (ret != IPS_SUCCESS) { | 
|  | 2685 | ha->num_ioctl--; | 
|  | 2686 | continue; | 
|  | 2687 | } | 
|  | 2688 |  | 
|  | 2689 | ret = ips_send_cmd(ha, scb); | 
|  | 2690 |  | 
|  | 2691 | if (ret == IPS_SUCCESS) | 
|  | 2692 | ips_putq_scb_head(&ha->scb_activelist, scb); | 
|  | 2693 | else | 
|  | 2694 | ha->num_ioctl--; | 
|  | 2695 |  | 
|  | 2696 | switch (ret) { | 
|  | 2697 | case IPS_FAILURE: | 
|  | 2698 | if (scb->scsi_cmd) { | 
|  | 2699 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 2700 | } | 
|  | 2701 |  | 
|  | 2702 | ips_freescb(ha, scb); | 
|  | 2703 | break; | 
|  | 2704 | case IPS_SUCCESS_IMM: | 
|  | 2705 | ips_freescb(ha, scb); | 
|  | 2706 | break; | 
|  | 2707 | default: | 
|  | 2708 | break; | 
|  | 2709 | }		/* end case */ | 
|  | 2710 |  | 
|  | 2711 | } | 
|  | 2712 |  | 
|  | 2713 | /* | 
|  | 2714 | * Send "Normal" I/O commands | 
|  | 2715 | */ | 
|  | 2716 |  | 
|  | 2717 | p = ha->scb_waitlist.head; | 
|  | 2718 | while ((p) && (scb = ips_getscb(ha))) { | 
| Jeff Garzik | 422c0d6 | 2005-10-24 18:05:09 -0400 | [diff] [blame] | 2719 | if ((scmd_channel(p) > 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2720 | && (ha-> | 
| Jeff Garzik | 422c0d6 | 2005-10-24 18:05:09 -0400 | [diff] [blame] | 2721 | dcdb_active[scmd_channel(p) - | 
|  | 2722 | 1] & (1 << scmd_id(p)))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2723 | ips_freescb(ha, scb); | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2724 | p = (struct scsi_cmnd *) p->host_scribble; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2725 | continue; | 
|  | 2726 | } | 
|  | 2727 |  | 
|  | 2728 | q = p; | 
|  | 2729 | SC = ips_removeq_wait(&ha->scb_waitlist, q); | 
|  | 2730 |  | 
|  | 2731 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2732 | spin_unlock(host->host_lock);	/* Unlock HA after command is taken off queue */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2733 |  | 
|  | 2734 | SC->result = DID_OK; | 
|  | 2735 | SC->host_scribble = NULL; | 
|  | 2736 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2737 | scb->target_id = SC->device->id; | 
|  | 2738 | scb->lun = SC->device->lun; | 
|  | 2739 | scb->bus = SC->device->channel; | 
|  | 2740 | scb->scsi_cmd = SC; | 
|  | 2741 | scb->breakup = 0; | 
|  | 2742 | scb->data_len = 0; | 
|  | 2743 | scb->callback = ipsintr_done; | 
|  | 2744 | scb->timeout = ips_cmd_timeout; | 
|  | 2745 | memset(&scb->cmd, 0, 16); | 
|  | 2746 |  | 
|  | 2747 | /* copy in the CDB */ | 
|  | 2748 | memcpy(scb->cdb, SC->cmnd, SC->cmd_len); | 
|  | 2749 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 2750 | scb->sg_count = scsi_dma_map(SC); | 
|  | 2751 | BUG_ON(scb->sg_count < 0); | 
|  | 2752 | if (scb->sg_count) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2753 | struct scatterlist *sg; | 
|  | 2754 | int i; | 
|  | 2755 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2756 | scb->flags |= IPS_SCB_MAP_SG; | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 2757 |  | 
|  | 2758 | scsi_for_each_sg(SC, sg, scb->sg_count, i) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2759 | if (ips_fill_scb_sg_single | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 2760 | (ha, sg_dma_address(sg), scb, i, | 
|  | 2761 | sg_dma_len(sg)) < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2762 | break; | 
|  | 2763 | } | 
|  | 2764 | scb->dcdb.transfer_length = scb->data_len; | 
|  | 2765 | } else { | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 2766 | scb->data_busaddr = 0L; | 
|  | 2767 | scb->sg_len = 0; | 
|  | 2768 | scb->data_len = 0; | 
|  | 2769 | scb->dcdb.transfer_length = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2770 | } | 
|  | 2771 |  | 
|  | 2772 | scb->dcdb.cmd_attribute = | 
|  | 2773 | ips_command_direction[scb->scsi_cmd->cmnd[0]]; | 
|  | 2774 |  | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 2775 | /* Allow a WRITE BUFFER Command to Have no Data */ | 
|  | 2776 | /* This is Used by Tape Flash Utilites          */ | 
|  | 2777 | if ((scb->scsi_cmd->cmnd[0] == WRITE_BUFFER) && | 
|  | 2778 | (scb->data_len == 0)) | 
|  | 2779 | scb->dcdb.cmd_attribute = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2780 |  | 
|  | 2781 | if (!(scb->dcdb.cmd_attribute & 0x3)) | 
|  | 2782 | scb->dcdb.transfer_length = 0; | 
|  | 2783 |  | 
|  | 2784 | if (scb->data_len >= IPS_MAX_XFER) { | 
|  | 2785 | scb->dcdb.cmd_attribute |= IPS_TRANSFER64K; | 
|  | 2786 | scb->dcdb.transfer_length = 0; | 
|  | 2787 | } | 
|  | 2788 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2789 | spin_lock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2790 |  | 
|  | 2791 | ret = ips_send_cmd(ha, scb); | 
|  | 2792 |  | 
|  | 2793 | switch (ret) { | 
|  | 2794 | case IPS_SUCCESS: | 
|  | 2795 | ips_putq_scb_head(&ha->scb_activelist, scb); | 
|  | 2796 | break; | 
|  | 2797 | case IPS_FAILURE: | 
|  | 2798 | if (scb->scsi_cmd) { | 
|  | 2799 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 2800 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 2801 | } | 
|  | 2802 |  | 
|  | 2803 | if (scb->bus) | 
|  | 2804 | ha->dcdb_active[scb->bus - 1] &= | 
|  | 2805 | ~(1 << scb->target_id); | 
|  | 2806 |  | 
|  | 2807 | ips_freescb(ha, scb); | 
|  | 2808 | break; | 
|  | 2809 | case IPS_SUCCESS_IMM: | 
|  | 2810 | if (scb->scsi_cmd) | 
|  | 2811 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 2812 |  | 
|  | 2813 | if (scb->bus) | 
|  | 2814 | ha->dcdb_active[scb->bus - 1] &= | 
|  | 2815 | ~(1 << scb->target_id); | 
|  | 2816 |  | 
|  | 2817 | ips_freescb(ha, scb); | 
|  | 2818 | break; | 
|  | 2819 | default: | 
|  | 2820 | break; | 
|  | 2821 | }		/* end case */ | 
|  | 2822 |  | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2823 | p = (struct scsi_cmnd *) p->host_scribble; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2824 |  | 
|  | 2825 | }			/* end while */ | 
|  | 2826 |  | 
|  | 2827 | if (intr == IPS_INTR_ON) | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 2828 | spin_unlock(host->host_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2829 | } | 
|  | 2830 |  | 
|  | 2831 | /****************************************************************************/ | 
|  | 2832 | /*                                                                          */ | 
|  | 2833 | /* Routine Name: ips_putq_scb_head                                          */ | 
|  | 2834 | /*                                                                          */ | 
|  | 2835 | /* Routine Description:                                                     */ | 
|  | 2836 | /*                                                                          */ | 
|  | 2837 | /*   Add an item to the head of the queue                                   */ | 
|  | 2838 | /*                                                                          */ | 
|  | 2839 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 2840 | /*                                                                          */ | 
|  | 2841 | /****************************************************************************/ | 
|  | 2842 | static void | 
|  | 2843 | ips_putq_scb_head(ips_scb_queue_t * queue, ips_scb_t * item) | 
|  | 2844 | { | 
|  | 2845 | METHOD_TRACE("ips_putq_scb_head", 1); | 
|  | 2846 |  | 
|  | 2847 | if (!item) | 
|  | 2848 | return; | 
|  | 2849 |  | 
|  | 2850 | item->q_next = queue->head; | 
|  | 2851 | queue->head = item; | 
|  | 2852 |  | 
|  | 2853 | if (!queue->tail) | 
|  | 2854 | queue->tail = item; | 
|  | 2855 |  | 
|  | 2856 | queue->count++; | 
|  | 2857 | } | 
|  | 2858 |  | 
|  | 2859 | /****************************************************************************/ | 
|  | 2860 | /*                                                                          */ | 
|  | 2861 | /* Routine Name: ips_removeq_scb_head                                       */ | 
|  | 2862 | /*                                                                          */ | 
|  | 2863 | /* Routine Description:                                                     */ | 
|  | 2864 | /*                                                                          */ | 
|  | 2865 | /*   Remove the head of the queue                                           */ | 
|  | 2866 | /*                                                                          */ | 
|  | 2867 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 2868 | /*                                                                          */ | 
|  | 2869 | /****************************************************************************/ | 
|  | 2870 | static ips_scb_t * | 
|  | 2871 | ips_removeq_scb_head(ips_scb_queue_t * queue) | 
|  | 2872 | { | 
|  | 2873 | ips_scb_t *item; | 
|  | 2874 |  | 
|  | 2875 | METHOD_TRACE("ips_removeq_scb_head", 1); | 
|  | 2876 |  | 
|  | 2877 | item = queue->head; | 
|  | 2878 |  | 
|  | 2879 | if (!item) { | 
|  | 2880 | return (NULL); | 
|  | 2881 | } | 
|  | 2882 |  | 
|  | 2883 | queue->head = item->q_next; | 
|  | 2884 | item->q_next = NULL; | 
|  | 2885 |  | 
|  | 2886 | if (queue->tail == item) | 
|  | 2887 | queue->tail = NULL; | 
|  | 2888 |  | 
|  | 2889 | queue->count--; | 
|  | 2890 |  | 
|  | 2891 | return (item); | 
|  | 2892 | } | 
|  | 2893 |  | 
|  | 2894 | /****************************************************************************/ | 
|  | 2895 | /*                                                                          */ | 
|  | 2896 | /* Routine Name: ips_removeq_scb                                            */ | 
|  | 2897 | /*                                                                          */ | 
|  | 2898 | /* Routine Description:                                                     */ | 
|  | 2899 | /*                                                                          */ | 
|  | 2900 | /*   Remove an item from a queue                                            */ | 
|  | 2901 | /*                                                                          */ | 
|  | 2902 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 2903 | /*                                                                          */ | 
|  | 2904 | /****************************************************************************/ | 
|  | 2905 | static ips_scb_t * | 
|  | 2906 | ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item) | 
|  | 2907 | { | 
|  | 2908 | ips_scb_t *p; | 
|  | 2909 |  | 
|  | 2910 | METHOD_TRACE("ips_removeq_scb", 1); | 
|  | 2911 |  | 
|  | 2912 | if (!item) | 
|  | 2913 | return (NULL); | 
|  | 2914 |  | 
|  | 2915 | if (item == queue->head) { | 
|  | 2916 | return (ips_removeq_scb_head(queue)); | 
|  | 2917 | } | 
|  | 2918 |  | 
|  | 2919 | p = queue->head; | 
|  | 2920 |  | 
|  | 2921 | while ((p) && (item != p->q_next)) | 
|  | 2922 | p = p->q_next; | 
|  | 2923 |  | 
|  | 2924 | if (p) { | 
|  | 2925 | /* found a match */ | 
|  | 2926 | p->q_next = item->q_next; | 
|  | 2927 |  | 
|  | 2928 | if (!item->q_next) | 
|  | 2929 | queue->tail = p; | 
|  | 2930 |  | 
|  | 2931 | item->q_next = NULL; | 
|  | 2932 | queue->count--; | 
|  | 2933 |  | 
|  | 2934 | return (item); | 
|  | 2935 | } | 
|  | 2936 |  | 
|  | 2937 | return (NULL); | 
|  | 2938 | } | 
|  | 2939 |  | 
|  | 2940 | /****************************************************************************/ | 
|  | 2941 | /*                                                                          */ | 
|  | 2942 | /* Routine Name: ips_putq_wait_tail                                         */ | 
|  | 2943 | /*                                                                          */ | 
|  | 2944 | /* Routine Description:                                                     */ | 
|  | 2945 | /*                                                                          */ | 
|  | 2946 | /*   Add an item to the tail of the queue                                   */ | 
|  | 2947 | /*                                                                          */ | 
|  | 2948 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 2949 | /*                                                                          */ | 
|  | 2950 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2951 | static void ips_putq_wait_tail(ips_wait_queue_t *queue, struct scsi_cmnd *item) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2952 | { | 
|  | 2953 | METHOD_TRACE("ips_putq_wait_tail", 1); | 
|  | 2954 |  | 
|  | 2955 | if (!item) | 
|  | 2956 | return; | 
|  | 2957 |  | 
|  | 2958 | item->host_scribble = NULL; | 
|  | 2959 |  | 
|  | 2960 | if (queue->tail) | 
|  | 2961 | queue->tail->host_scribble = (char *) item; | 
|  | 2962 |  | 
|  | 2963 | queue->tail = item; | 
|  | 2964 |  | 
|  | 2965 | if (!queue->head) | 
|  | 2966 | queue->head = item; | 
|  | 2967 |  | 
|  | 2968 | queue->count++; | 
|  | 2969 | } | 
|  | 2970 |  | 
|  | 2971 | /****************************************************************************/ | 
|  | 2972 | /*                                                                          */ | 
|  | 2973 | /* Routine Name: ips_removeq_wait_head                                      */ | 
|  | 2974 | /*                                                                          */ | 
|  | 2975 | /* Routine Description:                                                     */ | 
|  | 2976 | /*                                                                          */ | 
|  | 2977 | /*   Remove the head of the queue                                           */ | 
|  | 2978 | /*                                                                          */ | 
|  | 2979 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 2980 | /*                                                                          */ | 
|  | 2981 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2982 | static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *queue) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2983 | { | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2984 | struct scsi_cmnd *item; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2985 |  | 
|  | 2986 | METHOD_TRACE("ips_removeq_wait_head", 1); | 
|  | 2987 |  | 
|  | 2988 | item = queue->head; | 
|  | 2989 |  | 
|  | 2990 | if (!item) { | 
|  | 2991 | return (NULL); | 
|  | 2992 | } | 
|  | 2993 |  | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 2994 | queue->head = (struct scsi_cmnd *) item->host_scribble; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2995 | item->host_scribble = NULL; | 
|  | 2996 |  | 
|  | 2997 | if (queue->tail == item) | 
|  | 2998 | queue->tail = NULL; | 
|  | 2999 |  | 
|  | 3000 | queue->count--; | 
|  | 3001 |  | 
|  | 3002 | return (item); | 
|  | 3003 | } | 
|  | 3004 |  | 
|  | 3005 | /****************************************************************************/ | 
|  | 3006 | /*                                                                          */ | 
|  | 3007 | /* Routine Name: ips_removeq_wait                                           */ | 
|  | 3008 | /*                                                                          */ | 
|  | 3009 | /* Routine Description:                                                     */ | 
|  | 3010 | /*                                                                          */ | 
|  | 3011 | /*   Remove an item from a queue                                            */ | 
|  | 3012 | /*                                                                          */ | 
|  | 3013 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 3014 | /*                                                                          */ | 
|  | 3015 | /****************************************************************************/ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3016 | static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *queue, | 
|  | 3017 | struct scsi_cmnd *item) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3018 | { | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3019 | struct scsi_cmnd *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3020 |  | 
|  | 3021 | METHOD_TRACE("ips_removeq_wait", 1); | 
|  | 3022 |  | 
|  | 3023 | if (!item) | 
|  | 3024 | return (NULL); | 
|  | 3025 |  | 
|  | 3026 | if (item == queue->head) { | 
|  | 3027 | return (ips_removeq_wait_head(queue)); | 
|  | 3028 | } | 
|  | 3029 |  | 
|  | 3030 | p = queue->head; | 
|  | 3031 |  | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3032 | while ((p) && (item != (struct scsi_cmnd *) p->host_scribble)) | 
|  | 3033 | p = (struct scsi_cmnd *) p->host_scribble; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3034 |  | 
|  | 3035 | if (p) { | 
|  | 3036 | /* found a match */ | 
|  | 3037 | p->host_scribble = item->host_scribble; | 
|  | 3038 |  | 
|  | 3039 | if (!item->host_scribble) | 
|  | 3040 | queue->tail = p; | 
|  | 3041 |  | 
|  | 3042 | item->host_scribble = NULL; | 
|  | 3043 | queue->count--; | 
|  | 3044 |  | 
|  | 3045 | return (item); | 
|  | 3046 | } | 
|  | 3047 |  | 
|  | 3048 | return (NULL); | 
|  | 3049 | } | 
|  | 3050 |  | 
|  | 3051 | /****************************************************************************/ | 
|  | 3052 | /*                                                                          */ | 
|  | 3053 | /* Routine Name: ips_putq_copp_tail                                         */ | 
|  | 3054 | /*                                                                          */ | 
|  | 3055 | /* Routine Description:                                                     */ | 
|  | 3056 | /*                                                                          */ | 
|  | 3057 | /*   Add an item to the tail of the queue                                   */ | 
|  | 3058 | /*                                                                          */ | 
|  | 3059 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 3060 | /*                                                                          */ | 
|  | 3061 | /****************************************************************************/ | 
|  | 3062 | static void | 
|  | 3063 | ips_putq_copp_tail(ips_copp_queue_t * queue, ips_copp_wait_item_t * item) | 
|  | 3064 | { | 
|  | 3065 | METHOD_TRACE("ips_putq_copp_tail", 1); | 
|  | 3066 |  | 
|  | 3067 | if (!item) | 
|  | 3068 | return; | 
|  | 3069 |  | 
|  | 3070 | item->next = NULL; | 
|  | 3071 |  | 
|  | 3072 | if (queue->tail) | 
|  | 3073 | queue->tail->next = item; | 
|  | 3074 |  | 
|  | 3075 | queue->tail = item; | 
|  | 3076 |  | 
|  | 3077 | if (!queue->head) | 
|  | 3078 | queue->head = item; | 
|  | 3079 |  | 
|  | 3080 | queue->count++; | 
|  | 3081 | } | 
|  | 3082 |  | 
|  | 3083 | /****************************************************************************/ | 
|  | 3084 | /*                                                                          */ | 
|  | 3085 | /* Routine Name: ips_removeq_copp_head                                      */ | 
|  | 3086 | /*                                                                          */ | 
|  | 3087 | /* Routine Description:                                                     */ | 
|  | 3088 | /*                                                                          */ | 
|  | 3089 | /*   Remove the head of the queue                                           */ | 
|  | 3090 | /*                                                                          */ | 
|  | 3091 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 3092 | /*                                                                          */ | 
|  | 3093 | /****************************************************************************/ | 
|  | 3094 | static ips_copp_wait_item_t * | 
|  | 3095 | ips_removeq_copp_head(ips_copp_queue_t * queue) | 
|  | 3096 | { | 
|  | 3097 | ips_copp_wait_item_t *item; | 
|  | 3098 |  | 
|  | 3099 | METHOD_TRACE("ips_removeq_copp_head", 1); | 
|  | 3100 |  | 
|  | 3101 | item = queue->head; | 
|  | 3102 |  | 
|  | 3103 | if (!item) { | 
|  | 3104 | return (NULL); | 
|  | 3105 | } | 
|  | 3106 |  | 
|  | 3107 | queue->head = item->next; | 
|  | 3108 | item->next = NULL; | 
|  | 3109 |  | 
|  | 3110 | if (queue->tail == item) | 
|  | 3111 | queue->tail = NULL; | 
|  | 3112 |  | 
|  | 3113 | queue->count--; | 
|  | 3114 |  | 
|  | 3115 | return (item); | 
|  | 3116 | } | 
|  | 3117 |  | 
|  | 3118 | /****************************************************************************/ | 
|  | 3119 | /*                                                                          */ | 
|  | 3120 | /* Routine Name: ips_removeq_copp                                           */ | 
|  | 3121 | /*                                                                          */ | 
|  | 3122 | /* Routine Description:                                                     */ | 
|  | 3123 | /*                                                                          */ | 
|  | 3124 | /*   Remove an item from a queue                                            */ | 
|  | 3125 | /*                                                                          */ | 
|  | 3126 | /* ASSUMED to be called from within the HA lock                             */ | 
|  | 3127 | /*                                                                          */ | 
|  | 3128 | /****************************************************************************/ | 
|  | 3129 | static ips_copp_wait_item_t * | 
|  | 3130 | ips_removeq_copp(ips_copp_queue_t * queue, ips_copp_wait_item_t * item) | 
|  | 3131 | { | 
|  | 3132 | ips_copp_wait_item_t *p; | 
|  | 3133 |  | 
|  | 3134 | METHOD_TRACE("ips_removeq_copp", 1); | 
|  | 3135 |  | 
|  | 3136 | if (!item) | 
|  | 3137 | return (NULL); | 
|  | 3138 |  | 
|  | 3139 | if (item == queue->head) { | 
|  | 3140 | return (ips_removeq_copp_head(queue)); | 
|  | 3141 | } | 
|  | 3142 |  | 
|  | 3143 | p = queue->head; | 
|  | 3144 |  | 
|  | 3145 | while ((p) && (item != p->next)) | 
|  | 3146 | p = p->next; | 
|  | 3147 |  | 
|  | 3148 | if (p) { | 
|  | 3149 | /* found a match */ | 
|  | 3150 | p->next = item->next; | 
|  | 3151 |  | 
|  | 3152 | if (!item->next) | 
|  | 3153 | queue->tail = p; | 
|  | 3154 |  | 
|  | 3155 | item->next = NULL; | 
|  | 3156 | queue->count--; | 
|  | 3157 |  | 
|  | 3158 | return (item); | 
|  | 3159 | } | 
|  | 3160 |  | 
|  | 3161 | return (NULL); | 
|  | 3162 | } | 
|  | 3163 |  | 
|  | 3164 | /****************************************************************************/ | 
|  | 3165 | /*                                                                          */ | 
|  | 3166 | /* Routine Name: ipsintr_blocking                                           */ | 
|  | 3167 | /*                                                                          */ | 
|  | 3168 | /* Routine Description:                                                     */ | 
|  | 3169 | /*                                                                          */ | 
|  | 3170 | /*   Finalize an interrupt for internal commands                            */ | 
|  | 3171 | /*                                                                          */ | 
|  | 3172 | /****************************************************************************/ | 
|  | 3173 | static void | 
|  | 3174 | ipsintr_blocking(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 3175 | { | 
|  | 3176 | METHOD_TRACE("ipsintr_blocking", 2); | 
|  | 3177 |  | 
|  | 3178 | ips_freescb(ha, scb); | 
|  | 3179 | if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) { | 
|  | 3180 | ha->waitflag = FALSE; | 
|  | 3181 |  | 
|  | 3182 | return; | 
|  | 3183 | } | 
|  | 3184 | } | 
|  | 3185 |  | 
|  | 3186 | /****************************************************************************/ | 
|  | 3187 | /*                                                                          */ | 
|  | 3188 | /* Routine Name: ipsintr_done                                               */ | 
|  | 3189 | /*                                                                          */ | 
|  | 3190 | /* Routine Description:                                                     */ | 
|  | 3191 | /*                                                                          */ | 
|  | 3192 | /*   Finalize an interrupt for non-internal commands                        */ | 
|  | 3193 | /*                                                                          */ | 
|  | 3194 | /****************************************************************************/ | 
|  | 3195 | static void | 
|  | 3196 | ipsintr_done(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 3197 | { | 
|  | 3198 | METHOD_TRACE("ipsintr_done", 2); | 
|  | 3199 |  | 
|  | 3200 | if (!scb) { | 
|  | 3201 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 3202 | "Spurious interrupt; scb NULL.\n"); | 
|  | 3203 |  | 
|  | 3204 | return; | 
|  | 3205 | } | 
|  | 3206 |  | 
|  | 3207 | if (scb->scsi_cmd == NULL) { | 
|  | 3208 | /* unexpected interrupt */ | 
|  | 3209 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 3210 | "Spurious interrupt; scsi_cmd not set.\n"); | 
|  | 3211 |  | 
|  | 3212 | return; | 
|  | 3213 | } | 
|  | 3214 |  | 
|  | 3215 | ips_done(ha, scb); | 
|  | 3216 | } | 
|  | 3217 |  | 
|  | 3218 | /****************************************************************************/ | 
|  | 3219 | /*                                                                          */ | 
|  | 3220 | /* Routine Name: ips_done                                                   */ | 
|  | 3221 | /*                                                                          */ | 
|  | 3222 | /* Routine Description:                                                     */ | 
|  | 3223 | /*                                                                          */ | 
|  | 3224 | /*   Do housekeeping on completed commands                                  */ | 
|  | 3225 | /*  ASSUMED to be called form within the request lock                       */ | 
|  | 3226 | /****************************************************************************/ | 
|  | 3227 | static void | 
|  | 3228 | ips_done(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 3229 | { | 
|  | 3230 | int ret; | 
|  | 3231 |  | 
|  | 3232 | METHOD_TRACE("ips_done", 1); | 
|  | 3233 |  | 
|  | 3234 | if (!scb) | 
|  | 3235 | return; | 
|  | 3236 |  | 
|  | 3237 | if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) { | 
|  | 3238 | ips_cleanup_passthru(ha, scb); | 
|  | 3239 | ha->num_ioctl--; | 
|  | 3240 | } else { | 
|  | 3241 | /* | 
|  | 3242 | * Check to see if this command had too much | 
|  | 3243 | * data and had to be broke up.  If so, queue | 
|  | 3244 | * the rest of the data and continue. | 
|  | 3245 | */ | 
|  | 3246 | if ((scb->breakup) || (scb->sg_break)) { | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3247 | struct scatterlist *sg; | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3248 | int i, sg_dma_index, ips_sg_index = 0; | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3249 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3250 | /* we had a data breakup */ | 
|  | 3251 | scb->data_len = 0; | 
|  | 3252 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3253 | sg = scsi_sglist(scb->scsi_cmd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3254 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3255 | /* Spin forward to last dma chunk */ | 
|  | 3256 | sg_dma_index = scb->breakup; | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3257 | for (i = 0; i < scb->breakup; i++) | 
|  | 3258 | sg = sg_next(sg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3259 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3260 | /* Take care of possible partial on last chunk */ | 
|  | 3261 | ips_fill_scb_sg_single(ha, | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3262 | sg_dma_address(sg), | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3263 | scb, ips_sg_index++, | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3264 | sg_dma_len(sg)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3265 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3266 | for (; sg_dma_index < scsi_sg_count(scb->scsi_cmd); | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3267 | sg_dma_index++, sg = sg_next(sg)) { | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3268 | if (ips_fill_scb_sg_single | 
|  | 3269 | (ha, | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3270 | sg_dma_address(sg), | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3271 | scb, ips_sg_index++, | 
| FUJITA Tomonori | e0eaf88 | 2007-07-16 15:24:14 +0200 | [diff] [blame] | 3272 | sg_dma_len(sg)) < 0) | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 3273 | break; | 
|  | 3274 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3275 |  | 
|  | 3276 | scb->dcdb.transfer_length = scb->data_len; | 
|  | 3277 | scb->dcdb.cmd_attribute |= | 
|  | 3278 | ips_command_direction[scb->scsi_cmd->cmnd[0]]; | 
|  | 3279 |  | 
|  | 3280 | if (!(scb->dcdb.cmd_attribute & 0x3)) | 
|  | 3281 | scb->dcdb.transfer_length = 0; | 
|  | 3282 |  | 
|  | 3283 | if (scb->data_len >= IPS_MAX_XFER) { | 
|  | 3284 | scb->dcdb.cmd_attribute |= IPS_TRANSFER64K; | 
|  | 3285 | scb->dcdb.transfer_length = 0; | 
|  | 3286 | } | 
|  | 3287 |  | 
|  | 3288 | ret = ips_send_cmd(ha, scb); | 
|  | 3289 |  | 
|  | 3290 | switch (ret) { | 
|  | 3291 | case IPS_FAILURE: | 
|  | 3292 | if (scb->scsi_cmd) { | 
|  | 3293 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 3294 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 3295 | } | 
|  | 3296 |  | 
|  | 3297 | ips_freescb(ha, scb); | 
|  | 3298 | break; | 
|  | 3299 | case IPS_SUCCESS_IMM: | 
|  | 3300 | if (scb->scsi_cmd) { | 
|  | 3301 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 3302 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 3303 | } | 
|  | 3304 |  | 
|  | 3305 | ips_freescb(ha, scb); | 
|  | 3306 | break; | 
|  | 3307 | default: | 
|  | 3308 | break; | 
|  | 3309 | }	/* end case */ | 
|  | 3310 |  | 
|  | 3311 | return; | 
|  | 3312 | } | 
|  | 3313 | }			/* end if passthru */ | 
|  | 3314 |  | 
|  | 3315 | if (scb->bus) { | 
|  | 3316 | ha->dcdb_active[scb->bus - 1] &= ~(1 << scb->target_id); | 
|  | 3317 | } | 
|  | 3318 |  | 
|  | 3319 | scb->scsi_cmd->scsi_done(scb->scsi_cmd); | 
|  | 3320 |  | 
|  | 3321 | ips_freescb(ha, scb); | 
|  | 3322 | } | 
|  | 3323 |  | 
|  | 3324 | /****************************************************************************/ | 
|  | 3325 | /*                                                                          */ | 
|  | 3326 | /* Routine Name: ips_map_status                                             */ | 
|  | 3327 | /*                                                                          */ | 
|  | 3328 | /* Routine Description:                                                     */ | 
|  | 3329 | /*                                                                          */ | 
|  | 3330 | /*   Map Controller Error codes to Linux Error Codes                        */ | 
|  | 3331 | /*                                                                          */ | 
|  | 3332 | /****************************************************************************/ | 
|  | 3333 | static int | 
|  | 3334 | ips_map_status(ips_ha_t * ha, ips_scb_t * scb, ips_stat_t * sp) | 
|  | 3335 | { | 
|  | 3336 | int errcode; | 
|  | 3337 | int device_error; | 
|  | 3338 | uint32_t transfer_len; | 
|  | 3339 | IPS_DCDB_TABLE_TAPE *tapeDCDB; | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 3340 | IPS_SCSI_INQ_DATA inquiryData; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3341 |  | 
|  | 3342 | METHOD_TRACE("ips_map_status", 1); | 
|  | 3343 |  | 
|  | 3344 | if (scb->bus) { | 
|  | 3345 | DEBUG_VAR(2, | 
|  | 3346 | "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x", | 
|  | 3347 | ips_name, ha->host_num, | 
|  | 3348 | scb->scsi_cmd->device->channel, | 
|  | 3349 | scb->scsi_cmd->device->id, scb->scsi_cmd->device->lun, | 
|  | 3350 | scb->basic_status, scb->extended_status, | 
|  | 3351 | scb->extended_status == | 
|  | 3352 | IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0, | 
|  | 3353 | scb->extended_status == | 
|  | 3354 | IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0, | 
|  | 3355 | scb->extended_status == | 
|  | 3356 | IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0); | 
|  | 3357 | } | 
|  | 3358 |  | 
|  | 3359 | /* default driver error */ | 
|  | 3360 | errcode = DID_ERROR; | 
|  | 3361 | device_error = 0; | 
|  | 3362 |  | 
|  | 3363 | switch (scb->basic_status & IPS_GSC_STATUS_MASK) { | 
|  | 3364 | case IPS_CMD_TIMEOUT: | 
|  | 3365 | errcode = DID_TIME_OUT; | 
|  | 3366 | break; | 
|  | 3367 |  | 
|  | 3368 | case IPS_INVAL_OPCO: | 
|  | 3369 | case IPS_INVAL_CMD_BLK: | 
|  | 3370 | case IPS_INVAL_PARM_BLK: | 
|  | 3371 | case IPS_LD_ERROR: | 
|  | 3372 | case IPS_CMD_CMPLT_WERROR: | 
|  | 3373 | break; | 
|  | 3374 |  | 
|  | 3375 | case IPS_PHYS_DRV_ERROR: | 
|  | 3376 | switch (scb->extended_status) { | 
|  | 3377 | case IPS_ERR_SEL_TO: | 
|  | 3378 | if (scb->bus) | 
|  | 3379 | errcode = DID_NO_CONNECT; | 
|  | 3380 |  | 
|  | 3381 | break; | 
|  | 3382 |  | 
|  | 3383 | case IPS_ERR_OU_RUN: | 
|  | 3384 | if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) || | 
|  | 3385 | (scb->cmd.dcdb.op_code == | 
|  | 3386 | IPS_CMD_EXTENDED_DCDB_SG)) { | 
|  | 3387 | tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb; | 
|  | 3388 | transfer_len = tapeDCDB->transfer_length; | 
|  | 3389 | } else { | 
|  | 3390 | transfer_len = | 
|  | 3391 | (uint32_t) scb->dcdb.transfer_length; | 
|  | 3392 | } | 
|  | 3393 |  | 
|  | 3394 | if ((scb->bus) && (transfer_len < scb->data_len)) { | 
|  | 3395 | /* Underrun - set default to no error */ | 
|  | 3396 | errcode = DID_OK; | 
|  | 3397 |  | 
|  | 3398 | /* Restrict access to physical DASD */ | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 3399 | if (scb->scsi_cmd->cmnd[0] == INQUIRY) { | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 3400 | ips_scmd_buf_read(scb->scsi_cmd, | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 3401 | &inquiryData, sizeof (inquiryData)); | 
|  | 3402 | if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK) { | 
|  | 3403 | errcode = DID_TIME_OUT; | 
|  | 3404 | break; | 
|  | 3405 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3406 | } | 
|  | 3407 | } else | 
|  | 3408 | errcode = DID_ERROR; | 
|  | 3409 |  | 
|  | 3410 | break; | 
|  | 3411 |  | 
|  | 3412 | case IPS_ERR_RECOVERY: | 
|  | 3413 | /* don't fail recovered errors */ | 
|  | 3414 | if (scb->bus) | 
|  | 3415 | errcode = DID_OK; | 
|  | 3416 |  | 
|  | 3417 | break; | 
|  | 3418 |  | 
|  | 3419 | case IPS_ERR_HOST_RESET: | 
|  | 3420 | case IPS_ERR_DEV_RESET: | 
|  | 3421 | errcode = DID_RESET; | 
|  | 3422 | break; | 
|  | 3423 |  | 
|  | 3424 | case IPS_ERR_CKCOND: | 
|  | 3425 | if (scb->bus) { | 
|  | 3426 | if ((scb->cmd.dcdb.op_code == | 
|  | 3427 | IPS_CMD_EXTENDED_DCDB) | 
|  | 3428 | || (scb->cmd.dcdb.op_code == | 
|  | 3429 | IPS_CMD_EXTENDED_DCDB_SG)) { | 
|  | 3430 | tapeDCDB = | 
|  | 3431 | (IPS_DCDB_TABLE_TAPE *) & scb->dcdb; | 
|  | 3432 | memcpy(scb->scsi_cmd->sense_buffer, | 
|  | 3433 | tapeDCDB->sense_info, | 
| FUJITA Tomonori | b80ca4f | 2008-01-13 15:46:13 +0900 | [diff] [blame] | 3434 | SCSI_SENSE_BUFFERSIZE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3435 | } else { | 
|  | 3436 | memcpy(scb->scsi_cmd->sense_buffer, | 
|  | 3437 | scb->dcdb.sense_info, | 
| FUJITA Tomonori | b80ca4f | 2008-01-13 15:46:13 +0900 | [diff] [blame] | 3438 | SCSI_SENSE_BUFFERSIZE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3439 | } | 
|  | 3440 | device_error = 2;	/* check condition */ | 
|  | 3441 | } | 
|  | 3442 |  | 
|  | 3443 | errcode = DID_OK; | 
|  | 3444 |  | 
|  | 3445 | break; | 
|  | 3446 |  | 
|  | 3447 | default: | 
|  | 3448 | errcode = DID_ERROR; | 
|  | 3449 | break; | 
|  | 3450 |  | 
|  | 3451 | }		/* end switch */ | 
|  | 3452 | }			/* end switch */ | 
|  | 3453 |  | 
|  | 3454 | scb->scsi_cmd->result = device_error | (errcode << 16); | 
|  | 3455 |  | 
|  | 3456 | return (1); | 
|  | 3457 | } | 
|  | 3458 |  | 
|  | 3459 | /****************************************************************************/ | 
|  | 3460 | /*                                                                          */ | 
|  | 3461 | /* Routine Name: ips_send_wait                                              */ | 
|  | 3462 | /*                                                                          */ | 
|  | 3463 | /* Routine Description:                                                     */ | 
|  | 3464 | /*                                                                          */ | 
|  | 3465 | /*   Send a command to the controller and wait for it to return             */ | 
|  | 3466 | /*                                                                          */ | 
|  | 3467 | /*   The FFDC Time Stamp use this function for the callback, but doesn't    */ | 
|  | 3468 | /*   actually need to wait.                                                 */ | 
|  | 3469 | /****************************************************************************/ | 
|  | 3470 | static int | 
|  | 3471 | ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr) | 
|  | 3472 | { | 
|  | 3473 | int ret; | 
|  | 3474 |  | 
|  | 3475 | METHOD_TRACE("ips_send_wait", 1); | 
|  | 3476 |  | 
|  | 3477 | if (intr != IPS_FFDC) {	/* Won't be Waiting if this is a Time Stamp */ | 
|  | 3478 | ha->waitflag = TRUE; | 
|  | 3479 | ha->cmd_in_progress = scb->cdb[0]; | 
|  | 3480 | } | 
|  | 3481 | scb->callback = ipsintr_blocking; | 
|  | 3482 | ret = ips_send_cmd(ha, scb); | 
|  | 3483 |  | 
|  | 3484 | if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM)) | 
|  | 3485 | return (ret); | 
|  | 3486 |  | 
|  | 3487 | if (intr != IPS_FFDC)	/* Don't Wait around if this is a Time Stamp */ | 
|  | 3488 | ret = ips_wait(ha, timeout, intr); | 
|  | 3489 |  | 
|  | 3490 | return (ret); | 
|  | 3491 | } | 
|  | 3492 |  | 
|  | 3493 | /****************************************************************************/ | 
|  | 3494 | /*                                                                          */ | 
|  | 3495 | /* Routine Name: ips_scmd_buf_write                                         */ | 
|  | 3496 | /*                                                                          */ | 
|  | 3497 | /* Routine Description:                                                     */ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3498 | /*  Write data to struct scsi_cmnd request_buffer at proper offsets	    */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3499 | /****************************************************************************/ | 
|  | 3500 | static void | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3501 | ips_scmd_buf_write(struct scsi_cmnd *scmd, void *data, unsigned int count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3502 | { | 
| FUJITA Tomonori | 6690bae | 2008-03-09 13:44:33 +0900 | [diff] [blame] | 3503 | unsigned long flags; | 
| Jack Hammer | a3632fa | 2005-10-25 14:13:03 -0400 | [diff] [blame] | 3504 |  | 
| FUJITA Tomonori | 6690bae | 2008-03-09 13:44:33 +0900 | [diff] [blame] | 3505 | local_irq_save(flags); | 
|  | 3506 | scsi_sg_copy_from_buffer(scmd, data, count); | 
|  | 3507 | local_irq_restore(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3508 | } | 
|  | 3509 |  | 
|  | 3510 | /****************************************************************************/ | 
|  | 3511 | /*                                                                          */ | 
|  | 3512 | /* Routine Name: ips_scmd_buf_read                                          */ | 
|  | 3513 | /*                                                                          */ | 
|  | 3514 | /* Routine Description:                                                     */ | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3515 | /*  Copy data from a struct scsi_cmnd to a new, linear buffer		    */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3516 | /****************************************************************************/ | 
|  | 3517 | static void | 
| Henne | 1516b55 | 2006-10-02 14:56:23 +0200 | [diff] [blame] | 3518 | ips_scmd_buf_read(struct scsi_cmnd *scmd, void *data, unsigned int count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3519 | { | 
| FUJITA Tomonori | 6690bae | 2008-03-09 13:44:33 +0900 | [diff] [blame] | 3520 | unsigned long flags; | 
| Jack Hammer | a3632fa | 2005-10-25 14:13:03 -0400 | [diff] [blame] | 3521 |  | 
| FUJITA Tomonori | 6690bae | 2008-03-09 13:44:33 +0900 | [diff] [blame] | 3522 | local_irq_save(flags); | 
|  | 3523 | scsi_sg_copy_to_buffer(scmd, data, count); | 
|  | 3524 | local_irq_restore(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3525 | } | 
|  | 3526 |  | 
|  | 3527 | /****************************************************************************/ | 
|  | 3528 | /*                                                                          */ | 
|  | 3529 | /* Routine Name: ips_send_cmd                                               */ | 
|  | 3530 | /*                                                                          */ | 
|  | 3531 | /* Routine Description:                                                     */ | 
|  | 3532 | /*                                                                          */ | 
|  | 3533 | /*   Map SCSI commands to ServeRAID commands for logical drives             */ | 
|  | 3534 | /*                                                                          */ | 
|  | 3535 | /****************************************************************************/ | 
|  | 3536 | static int | 
|  | 3537 | ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 3538 | { | 
|  | 3539 | int ret; | 
|  | 3540 | char *sp; | 
|  | 3541 | int device_error; | 
|  | 3542 | IPS_DCDB_TABLE_TAPE *tapeDCDB; | 
|  | 3543 | int TimeOut; | 
|  | 3544 |  | 
|  | 3545 | METHOD_TRACE("ips_send_cmd", 1); | 
|  | 3546 |  | 
|  | 3547 | ret = IPS_SUCCESS; | 
|  | 3548 |  | 
|  | 3549 | if (!scb->scsi_cmd) { | 
|  | 3550 | /* internal command */ | 
|  | 3551 |  | 
|  | 3552 | if (scb->bus > 0) { | 
|  | 3553 | /* Controller commands can't be issued */ | 
|  | 3554 | /* to real devices -- fail them        */ | 
|  | 3555 | if ((ha->waitflag == TRUE) && | 
|  | 3556 | (ha->cmd_in_progress == scb->cdb[0])) { | 
|  | 3557 | ha->waitflag = FALSE; | 
|  | 3558 | } | 
|  | 3559 |  | 
|  | 3560 | return (1); | 
|  | 3561 | } | 
|  | 3562 | } else if ((scb->bus == 0) && (!ips_is_passthru(scb->scsi_cmd))) { | 
|  | 3563 | /* command to logical bus -- interpret */ | 
|  | 3564 | ret = IPS_SUCCESS_IMM; | 
|  | 3565 |  | 
|  | 3566 | switch (scb->scsi_cmd->cmnd[0]) { | 
|  | 3567 | case ALLOW_MEDIUM_REMOVAL: | 
|  | 3568 | case REZERO_UNIT: | 
|  | 3569 | case ERASE: | 
|  | 3570 | case WRITE_FILEMARKS: | 
|  | 3571 | case SPACE: | 
|  | 3572 | scb->scsi_cmd->result = DID_ERROR << 16; | 
|  | 3573 | break; | 
|  | 3574 |  | 
|  | 3575 | case START_STOP: | 
|  | 3576 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3577 |  | 
|  | 3578 | case TEST_UNIT_READY: | 
|  | 3579 | case INQUIRY: | 
|  | 3580 | if (scb->target_id == IPS_ADAPTER_ID) { | 
|  | 3581 | /* | 
|  | 3582 | * Either we have a TUR | 
|  | 3583 | * or we have a SCSI inquiry | 
|  | 3584 | */ | 
|  | 3585 | if (scb->scsi_cmd->cmnd[0] == TEST_UNIT_READY) | 
|  | 3586 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3587 |  | 
|  | 3588 | if (scb->scsi_cmd->cmnd[0] == INQUIRY) { | 
|  | 3589 | IPS_SCSI_INQ_DATA inquiry; | 
|  | 3590 |  | 
|  | 3591 | memset(&inquiry, 0, | 
|  | 3592 | sizeof (IPS_SCSI_INQ_DATA)); | 
|  | 3593 |  | 
|  | 3594 | inquiry.DeviceType = | 
|  | 3595 | IPS_SCSI_INQ_TYPE_PROCESSOR; | 
|  | 3596 | inquiry.DeviceTypeQualifier = | 
|  | 3597 | IPS_SCSI_INQ_LU_CONNECTED; | 
|  | 3598 | inquiry.Version = IPS_SCSI_INQ_REV2; | 
|  | 3599 | inquiry.ResponseDataFormat = | 
|  | 3600 | IPS_SCSI_INQ_RD_REV2; | 
|  | 3601 | inquiry.AdditionalLength = 31; | 
|  | 3602 | inquiry.Flags[0] = | 
|  | 3603 | IPS_SCSI_INQ_Address16; | 
|  | 3604 | inquiry.Flags[1] = | 
|  | 3605 | IPS_SCSI_INQ_WBus16 | | 
|  | 3606 | IPS_SCSI_INQ_Sync; | 
|  | 3607 | strncpy(inquiry.VendorId, "IBM     ", | 
|  | 3608 | 8); | 
|  | 3609 | strncpy(inquiry.ProductId, | 
|  | 3610 | "SERVERAID       ", 16); | 
|  | 3611 | strncpy(inquiry.ProductRevisionLevel, | 
|  | 3612 | "1.00", 4); | 
|  | 3613 |  | 
|  | 3614 | ips_scmd_buf_write(scb->scsi_cmd, | 
|  | 3615 | &inquiry, | 
|  | 3616 | sizeof (inquiry)); | 
|  | 3617 |  | 
|  | 3618 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3619 | } | 
|  | 3620 | } else { | 
|  | 3621 | scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO; | 
|  | 3622 | scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3623 | scb->cmd.logical_info.reserved = 0; | 
|  | 3624 | scb->cmd.logical_info.reserved2 = 0; | 
|  | 3625 | scb->data_len = sizeof (IPS_LD_INFO); | 
|  | 3626 | scb->data_busaddr = ha->logical_drive_info_dma_addr; | 
|  | 3627 | scb->flags = 0; | 
|  | 3628 | scb->cmd.logical_info.buffer_addr = scb->data_busaddr; | 
|  | 3629 | ret = IPS_SUCCESS; | 
|  | 3630 | } | 
|  | 3631 |  | 
|  | 3632 | break; | 
|  | 3633 |  | 
|  | 3634 | case REQUEST_SENSE: | 
|  | 3635 | ips_reqsen(ha, scb); | 
|  | 3636 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3637 | break; | 
|  | 3638 |  | 
|  | 3639 | case READ_6: | 
|  | 3640 | case WRITE_6: | 
|  | 3641 | if (!scb->sg_len) { | 
|  | 3642 | scb->cmd.basic_io.op_code = | 
|  | 3643 | (scb->scsi_cmd->cmnd[0] == | 
|  | 3644 | READ_6) ? IPS_CMD_READ : IPS_CMD_WRITE; | 
|  | 3645 | scb->cmd.basic_io.enhanced_sg = 0; | 
|  | 3646 | scb->cmd.basic_io.sg_addr = | 
|  | 3647 | cpu_to_le32(scb->data_busaddr); | 
|  | 3648 | } else { | 
|  | 3649 | scb->cmd.basic_io.op_code = | 
|  | 3650 | (scb->scsi_cmd->cmnd[0] == | 
|  | 3651 | READ_6) ? IPS_CMD_READ_SG : | 
|  | 3652 | IPS_CMD_WRITE_SG; | 
|  | 3653 | scb->cmd.basic_io.enhanced_sg = | 
|  | 3654 | IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0; | 
|  | 3655 | scb->cmd.basic_io.sg_addr = | 
|  | 3656 | cpu_to_le32(scb->sg_busaddr); | 
|  | 3657 | } | 
|  | 3658 |  | 
|  | 3659 | scb->cmd.basic_io.segment_4G = 0; | 
|  | 3660 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3661 | scb->cmd.basic_io.log_drv = scb->target_id; | 
|  | 3662 | scb->cmd.basic_io.sg_count = scb->sg_len; | 
|  | 3663 |  | 
|  | 3664 | if (scb->cmd.basic_io.lba) | 
| Marcin Slusarz | 36b8dd1 | 2008-03-28 14:48:35 -0700 | [diff] [blame] | 3665 | le32_add_cpu(&scb->cmd.basic_io.lba, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3666 | le16_to_cpu(scb->cmd.basic_io. | 
|  | 3667 | sector_count)); | 
|  | 3668 | else | 
|  | 3669 | scb->cmd.basic_io.lba = | 
|  | 3670 | (((scb->scsi_cmd-> | 
|  | 3671 | cmnd[1] & 0x1f) << 16) | (scb->scsi_cmd-> | 
|  | 3672 | cmnd[2] << 8) | | 
|  | 3673 | (scb->scsi_cmd->cmnd[3])); | 
|  | 3674 |  | 
|  | 3675 | scb->cmd.basic_io.sector_count = | 
|  | 3676 | cpu_to_le16(scb->data_len / IPS_BLKSIZE); | 
|  | 3677 |  | 
|  | 3678 | if (le16_to_cpu(scb->cmd.basic_io.sector_count) == 0) | 
|  | 3679 | scb->cmd.basic_io.sector_count = | 
|  | 3680 | cpu_to_le16(256); | 
|  | 3681 |  | 
|  | 3682 | ret = IPS_SUCCESS; | 
|  | 3683 | break; | 
|  | 3684 |  | 
|  | 3685 | case READ_10: | 
|  | 3686 | case WRITE_10: | 
|  | 3687 | if (!scb->sg_len) { | 
|  | 3688 | scb->cmd.basic_io.op_code = | 
|  | 3689 | (scb->scsi_cmd->cmnd[0] == | 
|  | 3690 | READ_10) ? IPS_CMD_READ : IPS_CMD_WRITE; | 
|  | 3691 | scb->cmd.basic_io.enhanced_sg = 0; | 
|  | 3692 | scb->cmd.basic_io.sg_addr = | 
|  | 3693 | cpu_to_le32(scb->data_busaddr); | 
|  | 3694 | } else { | 
|  | 3695 | scb->cmd.basic_io.op_code = | 
|  | 3696 | (scb->scsi_cmd->cmnd[0] == | 
|  | 3697 | READ_10) ? IPS_CMD_READ_SG : | 
|  | 3698 | IPS_CMD_WRITE_SG; | 
|  | 3699 | scb->cmd.basic_io.enhanced_sg = | 
|  | 3700 | IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0; | 
|  | 3701 | scb->cmd.basic_io.sg_addr = | 
|  | 3702 | cpu_to_le32(scb->sg_busaddr); | 
|  | 3703 | } | 
|  | 3704 |  | 
|  | 3705 | scb->cmd.basic_io.segment_4G = 0; | 
|  | 3706 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3707 | scb->cmd.basic_io.log_drv = scb->target_id; | 
|  | 3708 | scb->cmd.basic_io.sg_count = scb->sg_len; | 
|  | 3709 |  | 
|  | 3710 | if (scb->cmd.basic_io.lba) | 
| Marcin Slusarz | 36b8dd1 | 2008-03-28 14:48:35 -0700 | [diff] [blame] | 3711 | le32_add_cpu(&scb->cmd.basic_io.lba, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3712 | le16_to_cpu(scb->cmd.basic_io. | 
|  | 3713 | sector_count)); | 
|  | 3714 | else | 
|  | 3715 | scb->cmd.basic_io.lba = | 
|  | 3716 | ((scb->scsi_cmd->cmnd[2] << 24) | (scb-> | 
|  | 3717 | scsi_cmd-> | 
|  | 3718 | cmnd[3] | 
|  | 3719 | << 16) | | 
|  | 3720 | (scb->scsi_cmd->cmnd[4] << 8) | scb-> | 
|  | 3721 | scsi_cmd->cmnd[5]); | 
|  | 3722 |  | 
|  | 3723 | scb->cmd.basic_io.sector_count = | 
|  | 3724 | cpu_to_le16(scb->data_len / IPS_BLKSIZE); | 
|  | 3725 |  | 
|  | 3726 | if (cpu_to_le16(scb->cmd.basic_io.sector_count) == 0) { | 
|  | 3727 | /* | 
|  | 3728 | * This is a null condition | 
|  | 3729 | * we don't have to do anything | 
|  | 3730 | * so just return | 
|  | 3731 | */ | 
|  | 3732 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3733 | } else | 
|  | 3734 | ret = IPS_SUCCESS; | 
|  | 3735 |  | 
|  | 3736 | break; | 
|  | 3737 |  | 
|  | 3738 | case RESERVE: | 
|  | 3739 | case RELEASE: | 
|  | 3740 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3741 | break; | 
|  | 3742 |  | 
|  | 3743 | case MODE_SENSE: | 
|  | 3744 | scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY; | 
|  | 3745 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3746 | scb->cmd.basic_io.segment_4G = 0; | 
|  | 3747 | scb->cmd.basic_io.enhanced_sg = 0; | 
|  | 3748 | scb->data_len = sizeof (*ha->enq); | 
|  | 3749 | scb->cmd.basic_io.sg_addr = ha->enq_busaddr; | 
|  | 3750 | ret = IPS_SUCCESS; | 
|  | 3751 | break; | 
|  | 3752 |  | 
|  | 3753 | case READ_CAPACITY: | 
|  | 3754 | scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO; | 
|  | 3755 | scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3756 | scb->cmd.logical_info.reserved = 0; | 
|  | 3757 | scb->cmd.logical_info.reserved2 = 0; | 
|  | 3758 | scb->cmd.logical_info.reserved3 = 0; | 
|  | 3759 | scb->data_len = sizeof (IPS_LD_INFO); | 
|  | 3760 | scb->data_busaddr = ha->logical_drive_info_dma_addr; | 
|  | 3761 | scb->flags = 0; | 
|  | 3762 | scb->cmd.logical_info.buffer_addr = scb->data_busaddr; | 
|  | 3763 | ret = IPS_SUCCESS; | 
|  | 3764 | break; | 
|  | 3765 |  | 
|  | 3766 | case SEND_DIAGNOSTIC: | 
|  | 3767 | case REASSIGN_BLOCKS: | 
|  | 3768 | case FORMAT_UNIT: | 
|  | 3769 | case SEEK_10: | 
|  | 3770 | case VERIFY: | 
|  | 3771 | case READ_DEFECT_DATA: | 
|  | 3772 | case READ_BUFFER: | 
|  | 3773 | case WRITE_BUFFER: | 
|  | 3774 | scb->scsi_cmd->result = DID_OK << 16; | 
|  | 3775 | break; | 
|  | 3776 |  | 
|  | 3777 | default: | 
|  | 3778 | /* Set the Return Info to appear like the Command was */ | 
|  | 3779 | /* attempted, a Check Condition occurred, and Sense   */ | 
|  | 3780 | /* Data indicating an Invalid CDB OpCode is returned. */ | 
|  | 3781 | sp = (char *) scb->scsi_cmd->sense_buffer; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3782 |  | 
|  | 3783 | sp[0] = 0x70;	/* Error Code               */ | 
|  | 3784 | sp[2] = ILLEGAL_REQUEST;	/* Sense Key 5 Illegal Req. */ | 
|  | 3785 | sp[7] = 0x0A;	/* Additional Sense Length  */ | 
|  | 3786 | sp[12] = 0x20;	/* ASC = Invalid OpCode     */ | 
|  | 3787 | sp[13] = 0x00;	/* ASCQ                     */ | 
|  | 3788 |  | 
|  | 3789 | device_error = 2;	/* Indicate Check Condition */ | 
|  | 3790 | scb->scsi_cmd->result = device_error | (DID_OK << 16); | 
|  | 3791 | break; | 
|  | 3792 | }		/* end switch */ | 
|  | 3793 | } | 
|  | 3794 | /* end if */ | 
|  | 3795 | if (ret == IPS_SUCCESS_IMM) | 
|  | 3796 | return (ret); | 
|  | 3797 |  | 
|  | 3798 | /* setup DCDB */ | 
|  | 3799 | if (scb->bus > 0) { | 
|  | 3800 |  | 
|  | 3801 | /* If we already know the Device is Not there, no need to attempt a Command   */ | 
|  | 3802 | /* This also protects an NT FailOver Controller from getting CDB's sent to it */ | 
|  | 3803 | if (ha->conf->dev[scb->bus - 1][scb->target_id].ucState == 0) { | 
|  | 3804 | scb->scsi_cmd->result = DID_NO_CONNECT << 16; | 
|  | 3805 | return (IPS_SUCCESS_IMM); | 
|  | 3806 | } | 
|  | 3807 |  | 
|  | 3808 | ha->dcdb_active[scb->bus - 1] |= (1 << scb->target_id); | 
|  | 3809 | scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 3810 | scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr + | 
|  | 3811 | (unsigned long) &scb-> | 
|  | 3812 | dcdb - | 
|  | 3813 | (unsigned long) scb); | 
|  | 3814 | scb->cmd.dcdb.reserved = 0; | 
|  | 3815 | scb->cmd.dcdb.reserved2 = 0; | 
|  | 3816 | scb->cmd.dcdb.reserved3 = 0; | 
|  | 3817 | scb->cmd.dcdb.segment_4G = 0; | 
|  | 3818 | scb->cmd.dcdb.enhanced_sg = 0; | 
|  | 3819 |  | 
| Jens Axboe | 242f9dc | 2008-09-14 05:55:09 -0700 | [diff] [blame] | 3820 | TimeOut = scb->scsi_cmd->request->timeout; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3821 |  | 
|  | 3822 | if (ha->subsys->param[4] & 0x00100000) {	/* If NEW Tape DCDB is Supported */ | 
|  | 3823 | if (!scb->sg_len) { | 
|  | 3824 | scb->cmd.dcdb.op_code = IPS_CMD_EXTENDED_DCDB; | 
|  | 3825 | } else { | 
|  | 3826 | scb->cmd.dcdb.op_code = | 
|  | 3827 | IPS_CMD_EXTENDED_DCDB_SG; | 
|  | 3828 | scb->cmd.dcdb.enhanced_sg = | 
|  | 3829 | IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0; | 
|  | 3830 | } | 
|  | 3831 |  | 
|  | 3832 | tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;	/* Use Same Data Area as Old DCDB Struct */ | 
|  | 3833 | tapeDCDB->device_address = | 
|  | 3834 | ((scb->bus - 1) << 4) | scb->target_id; | 
|  | 3835 | tapeDCDB->cmd_attribute |= IPS_DISCONNECT_ALLOWED; | 
|  | 3836 | tapeDCDB->cmd_attribute &= ~IPS_TRANSFER64K;	/* Always Turn OFF 64K Size Flag */ | 
|  | 3837 |  | 
|  | 3838 | if (TimeOut) { | 
|  | 3839 | if (TimeOut < (10 * HZ)) | 
|  | 3840 | tapeDCDB->cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */ | 
|  | 3841 | else if (TimeOut < (60 * HZ)) | 
|  | 3842 | tapeDCDB->cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */ | 
|  | 3843 | else if (TimeOut < (1200 * HZ)) | 
|  | 3844 | tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */ | 
|  | 3845 | } | 
|  | 3846 |  | 
|  | 3847 | tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len; | 
|  | 3848 | tapeDCDB->reserved_for_LUN = 0; | 
|  | 3849 | tapeDCDB->transfer_length = scb->data_len; | 
|  | 3850 | if (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG) | 
|  | 3851 | tapeDCDB->buffer_pointer = | 
|  | 3852 | cpu_to_le32(scb->sg_busaddr); | 
|  | 3853 | else | 
|  | 3854 | tapeDCDB->buffer_pointer = | 
|  | 3855 | cpu_to_le32(scb->data_busaddr); | 
|  | 3856 | tapeDCDB->sg_count = scb->sg_len; | 
|  | 3857 | tapeDCDB->sense_length = sizeof (tapeDCDB->sense_info); | 
|  | 3858 | tapeDCDB->scsi_status = 0; | 
|  | 3859 | tapeDCDB->reserved = 0; | 
|  | 3860 | memcpy(tapeDCDB->scsi_cdb, scb->scsi_cmd->cmnd, | 
|  | 3861 | scb->scsi_cmd->cmd_len); | 
|  | 3862 | } else { | 
|  | 3863 | if (!scb->sg_len) { | 
|  | 3864 | scb->cmd.dcdb.op_code = IPS_CMD_DCDB; | 
|  | 3865 | } else { | 
|  | 3866 | scb->cmd.dcdb.op_code = IPS_CMD_DCDB_SG; | 
|  | 3867 | scb->cmd.dcdb.enhanced_sg = | 
|  | 3868 | IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0; | 
|  | 3869 | } | 
|  | 3870 |  | 
|  | 3871 | scb->dcdb.device_address = | 
|  | 3872 | ((scb->bus - 1) << 4) | scb->target_id; | 
|  | 3873 | scb->dcdb.cmd_attribute |= IPS_DISCONNECT_ALLOWED; | 
|  | 3874 |  | 
|  | 3875 | if (TimeOut) { | 
|  | 3876 | if (TimeOut < (10 * HZ)) | 
|  | 3877 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */ | 
|  | 3878 | else if (TimeOut < (60 * HZ)) | 
|  | 3879 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */ | 
|  | 3880 | else if (TimeOut < (1200 * HZ)) | 
|  | 3881 | scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */ | 
|  | 3882 | } | 
|  | 3883 |  | 
|  | 3884 | scb->dcdb.transfer_length = scb->data_len; | 
|  | 3885 | if (scb->dcdb.cmd_attribute & IPS_TRANSFER64K) | 
|  | 3886 | scb->dcdb.transfer_length = 0; | 
|  | 3887 | if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB_SG) | 
|  | 3888 | scb->dcdb.buffer_pointer = | 
|  | 3889 | cpu_to_le32(scb->sg_busaddr); | 
|  | 3890 | else | 
|  | 3891 | scb->dcdb.buffer_pointer = | 
|  | 3892 | cpu_to_le32(scb->data_busaddr); | 
|  | 3893 | scb->dcdb.cdb_length = scb->scsi_cmd->cmd_len; | 
|  | 3894 | scb->dcdb.sense_length = sizeof (scb->dcdb.sense_info); | 
|  | 3895 | scb->dcdb.sg_count = scb->sg_len; | 
|  | 3896 | scb->dcdb.reserved = 0; | 
|  | 3897 | memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, | 
|  | 3898 | scb->scsi_cmd->cmd_len); | 
|  | 3899 | scb->dcdb.scsi_status = 0; | 
|  | 3900 | scb->dcdb.reserved2[0] = 0; | 
|  | 3901 | scb->dcdb.reserved2[1] = 0; | 
|  | 3902 | scb->dcdb.reserved2[2] = 0; | 
|  | 3903 | } | 
|  | 3904 | } | 
|  | 3905 |  | 
|  | 3906 | return ((*ha->func.issue) (ha, scb)); | 
|  | 3907 | } | 
|  | 3908 |  | 
|  | 3909 | /****************************************************************************/ | 
|  | 3910 | /*                                                                          */ | 
|  | 3911 | /* Routine Name: ips_chk_status                                             */ | 
|  | 3912 | /*                                                                          */ | 
|  | 3913 | /* Routine Description:                                                     */ | 
|  | 3914 | /*                                                                          */ | 
|  | 3915 | /*   Check the status of commands to logical drives                         */ | 
|  | 3916 | /*   Assumed to be called with the HA lock                                  */ | 
|  | 3917 | /****************************************************************************/ | 
|  | 3918 | static void | 
|  | 3919 | ips_chkstatus(ips_ha_t * ha, IPS_STATUS * pstatus) | 
|  | 3920 | { | 
|  | 3921 | ips_scb_t *scb; | 
|  | 3922 | ips_stat_t *sp; | 
|  | 3923 | uint8_t basic_status; | 
|  | 3924 | uint8_t ext_status; | 
|  | 3925 | int errcode; | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 3926 | IPS_SCSI_INQ_DATA inquiryData; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3927 |  | 
|  | 3928 | METHOD_TRACE("ips_chkstatus", 1); | 
|  | 3929 |  | 
|  | 3930 | scb = &ha->scbs[pstatus->fields.command_id]; | 
|  | 3931 | scb->basic_status = basic_status = | 
|  | 3932 | pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK; | 
|  | 3933 | scb->extended_status = ext_status = pstatus->fields.extended_status; | 
|  | 3934 |  | 
|  | 3935 | sp = &ha->sp; | 
|  | 3936 | sp->residue_len = 0; | 
|  | 3937 | sp->scb_addr = (void *) scb; | 
|  | 3938 |  | 
|  | 3939 | /* Remove the item from the active queue */ | 
|  | 3940 | ips_removeq_scb(&ha->scb_activelist, scb); | 
|  | 3941 |  | 
|  | 3942 | if (!scb->scsi_cmd) | 
|  | 3943 | /* internal commands are handled in do_ipsintr */ | 
|  | 3944 | return; | 
|  | 3945 |  | 
|  | 3946 | DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)", | 
|  | 3947 | ips_name, | 
|  | 3948 | ha->host_num, | 
|  | 3949 | scb->cdb[0], | 
|  | 3950 | scb->cmd.basic_io.command_id, | 
|  | 3951 | scb->bus, scb->target_id, scb->lun); | 
|  | 3952 |  | 
|  | 3953 | if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) | 
|  | 3954 | /* passthru - just returns the raw result */ | 
|  | 3955 | return; | 
|  | 3956 |  | 
|  | 3957 | errcode = DID_OK; | 
|  | 3958 |  | 
|  | 3959 | if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) || | 
|  | 3960 | ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) { | 
|  | 3961 |  | 
|  | 3962 | if (scb->bus == 0) { | 
|  | 3963 | if ((basic_status & IPS_GSC_STATUS_MASK) == | 
|  | 3964 | IPS_CMD_RECOVERED_ERROR) { | 
|  | 3965 | DEBUG_VAR(1, | 
|  | 3966 | "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x", | 
|  | 3967 | ips_name, ha->host_num, | 
|  | 3968 | scb->cmd.basic_io.op_code, | 
|  | 3969 | basic_status, ext_status); | 
|  | 3970 | } | 
|  | 3971 |  | 
|  | 3972 | switch (scb->scsi_cmd->cmnd[0]) { | 
|  | 3973 | case ALLOW_MEDIUM_REMOVAL: | 
|  | 3974 | case REZERO_UNIT: | 
|  | 3975 | case ERASE: | 
|  | 3976 | case WRITE_FILEMARKS: | 
|  | 3977 | case SPACE: | 
|  | 3978 | errcode = DID_ERROR; | 
|  | 3979 | break; | 
|  | 3980 |  | 
|  | 3981 | case START_STOP: | 
|  | 3982 | break; | 
|  | 3983 |  | 
|  | 3984 | case TEST_UNIT_READY: | 
|  | 3985 | if (!ips_online(ha, scb)) { | 
|  | 3986 | errcode = DID_TIME_OUT; | 
|  | 3987 | } | 
|  | 3988 | break; | 
|  | 3989 |  | 
|  | 3990 | case INQUIRY: | 
|  | 3991 | if (ips_online(ha, scb)) { | 
|  | 3992 | ips_inquiry(ha, scb); | 
|  | 3993 | } else { | 
|  | 3994 | errcode = DID_TIME_OUT; | 
|  | 3995 | } | 
|  | 3996 | break; | 
|  | 3997 |  | 
|  | 3998 | case REQUEST_SENSE: | 
|  | 3999 | ips_reqsen(ha, scb); | 
|  | 4000 | break; | 
|  | 4001 |  | 
|  | 4002 | case READ_6: | 
|  | 4003 | case WRITE_6: | 
|  | 4004 | case READ_10: | 
|  | 4005 | case WRITE_10: | 
|  | 4006 | case RESERVE: | 
|  | 4007 | case RELEASE: | 
|  | 4008 | break; | 
|  | 4009 |  | 
|  | 4010 | case MODE_SENSE: | 
|  | 4011 | if (!ips_online(ha, scb) | 
|  | 4012 | || !ips_msense(ha, scb)) { | 
|  | 4013 | errcode = DID_ERROR; | 
|  | 4014 | } | 
|  | 4015 | break; | 
|  | 4016 |  | 
|  | 4017 | case READ_CAPACITY: | 
|  | 4018 | if (ips_online(ha, scb)) | 
|  | 4019 | ips_rdcap(ha, scb); | 
|  | 4020 | else { | 
|  | 4021 | errcode = DID_TIME_OUT; | 
|  | 4022 | } | 
|  | 4023 | break; | 
|  | 4024 |  | 
|  | 4025 | case SEND_DIAGNOSTIC: | 
|  | 4026 | case REASSIGN_BLOCKS: | 
|  | 4027 | break; | 
|  | 4028 |  | 
|  | 4029 | case FORMAT_UNIT: | 
|  | 4030 | errcode = DID_ERROR; | 
|  | 4031 | break; | 
|  | 4032 |  | 
|  | 4033 | case SEEK_10: | 
|  | 4034 | case VERIFY: | 
|  | 4035 | case READ_DEFECT_DATA: | 
|  | 4036 | case READ_BUFFER: | 
|  | 4037 | case WRITE_BUFFER: | 
|  | 4038 | break; | 
|  | 4039 |  | 
|  | 4040 | default: | 
|  | 4041 | errcode = DID_ERROR; | 
|  | 4042 | }	/* end switch */ | 
|  | 4043 |  | 
|  | 4044 | scb->scsi_cmd->result = errcode << 16; | 
|  | 4045 | } else {	/* bus == 0 */ | 
|  | 4046 | /* restrict access to physical drives */ | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4047 | if (scb->scsi_cmd->cmnd[0] == INQUIRY) { | 
|  | 4048 | ips_scmd_buf_read(scb->scsi_cmd, | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 4049 | &inquiryData, sizeof (inquiryData)); | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4050 | if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK) | 
| Jack Hammer | a5b3c86 | 2006-01-31 13:17:55 -0500 | [diff] [blame] | 4051 | scb->scsi_cmd->result = DID_TIME_OUT << 16; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4052 | } | 
|  | 4053 | }		/* else */ | 
|  | 4054 | } else {		/* recovered error / success */ | 
|  | 4055 | if (scb->bus == 0) { | 
|  | 4056 | DEBUG_VAR(1, | 
|  | 4057 | "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x", | 
|  | 4058 | ips_name, ha->host_num, | 
|  | 4059 | scb->cmd.basic_io.op_code, basic_status, | 
|  | 4060 | ext_status); | 
|  | 4061 | } | 
|  | 4062 |  | 
|  | 4063 | ips_map_status(ha, scb, sp); | 
|  | 4064 | }			/* else */ | 
|  | 4065 | } | 
|  | 4066 |  | 
|  | 4067 | /****************************************************************************/ | 
|  | 4068 | /*                                                                          */ | 
|  | 4069 | /* Routine Name: ips_online                                                 */ | 
|  | 4070 | /*                                                                          */ | 
|  | 4071 | /* Routine Description:                                                     */ | 
|  | 4072 | /*                                                                          */ | 
|  | 4073 | /*   Determine if a logical drive is online                                 */ | 
|  | 4074 | /*                                                                          */ | 
|  | 4075 | /****************************************************************************/ | 
|  | 4076 | static int | 
|  | 4077 | ips_online(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4078 | { | 
|  | 4079 | METHOD_TRACE("ips_online", 1); | 
|  | 4080 |  | 
|  | 4081 | if (scb->target_id >= IPS_MAX_LD) | 
|  | 4082 | return (0); | 
|  | 4083 |  | 
|  | 4084 | if ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1) { | 
|  | 4085 | memset(ha->logical_drive_info, 0, sizeof (IPS_LD_INFO)); | 
|  | 4086 | return (0); | 
|  | 4087 | } | 
|  | 4088 |  | 
|  | 4089 | if (ha->logical_drive_info->drive_info[scb->target_id].state != | 
|  | 4090 | IPS_LD_OFFLINE | 
|  | 4091 | && ha->logical_drive_info->drive_info[scb->target_id].state != | 
|  | 4092 | IPS_LD_FREE | 
|  | 4093 | && ha->logical_drive_info->drive_info[scb->target_id].state != | 
|  | 4094 | IPS_LD_CRS | 
|  | 4095 | && ha->logical_drive_info->drive_info[scb->target_id].state != | 
|  | 4096 | IPS_LD_SYS) | 
|  | 4097 | return (1); | 
|  | 4098 | else | 
|  | 4099 | return (0); | 
|  | 4100 | } | 
|  | 4101 |  | 
|  | 4102 | /****************************************************************************/ | 
|  | 4103 | /*                                                                          */ | 
|  | 4104 | /* Routine Name: ips_inquiry                                                */ | 
|  | 4105 | /*                                                                          */ | 
|  | 4106 | /* Routine Description:                                                     */ | 
|  | 4107 | /*                                                                          */ | 
|  | 4108 | /*   Simulate an inquiry command to a logical drive                         */ | 
|  | 4109 | /*                                                                          */ | 
|  | 4110 | /****************************************************************************/ | 
|  | 4111 | static int | 
|  | 4112 | ips_inquiry(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4113 | { | 
|  | 4114 | IPS_SCSI_INQ_DATA inquiry; | 
|  | 4115 |  | 
|  | 4116 | METHOD_TRACE("ips_inquiry", 1); | 
|  | 4117 |  | 
|  | 4118 | memset(&inquiry, 0, sizeof (IPS_SCSI_INQ_DATA)); | 
|  | 4119 |  | 
|  | 4120 | inquiry.DeviceType = IPS_SCSI_INQ_TYPE_DASD; | 
|  | 4121 | inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED; | 
|  | 4122 | inquiry.Version = IPS_SCSI_INQ_REV2; | 
|  | 4123 | inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2; | 
|  | 4124 | inquiry.AdditionalLength = 31; | 
|  | 4125 | inquiry.Flags[0] = IPS_SCSI_INQ_Address16; | 
|  | 4126 | inquiry.Flags[1] = | 
|  | 4127 | IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync | IPS_SCSI_INQ_CmdQue; | 
|  | 4128 | strncpy(inquiry.VendorId, "IBM     ", 8); | 
|  | 4129 | strncpy(inquiry.ProductId, "SERVERAID       ", 16); | 
|  | 4130 | strncpy(inquiry.ProductRevisionLevel, "1.00", 4); | 
|  | 4131 |  | 
|  | 4132 | ips_scmd_buf_write(scb->scsi_cmd, &inquiry, sizeof (inquiry)); | 
|  | 4133 |  | 
|  | 4134 | return (1); | 
|  | 4135 | } | 
|  | 4136 |  | 
|  | 4137 | /****************************************************************************/ | 
|  | 4138 | /*                                                                          */ | 
|  | 4139 | /* Routine Name: ips_rdcap                                                  */ | 
|  | 4140 | /*                                                                          */ | 
|  | 4141 | /* Routine Description:                                                     */ | 
|  | 4142 | /*                                                                          */ | 
|  | 4143 | /*   Simulate a read capacity command to a logical drive                    */ | 
|  | 4144 | /*                                                                          */ | 
|  | 4145 | /****************************************************************************/ | 
|  | 4146 | static int | 
|  | 4147 | ips_rdcap(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4148 | { | 
|  | 4149 | IPS_SCSI_CAPACITY cap; | 
|  | 4150 |  | 
|  | 4151 | METHOD_TRACE("ips_rdcap", 1); | 
|  | 4152 |  | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 4153 | if (scsi_bufflen(scb->scsi_cmd) < 8) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4154 | return (0); | 
|  | 4155 |  | 
|  | 4156 | cap.lba = | 
|  | 4157 | cpu_to_be32(le32_to_cpu | 
|  | 4158 | (ha->logical_drive_info-> | 
|  | 4159 | drive_info[scb->target_id].sector_count) - 1); | 
|  | 4160 | cap.len = cpu_to_be32((uint32_t) IPS_BLKSIZE); | 
|  | 4161 |  | 
|  | 4162 | ips_scmd_buf_write(scb->scsi_cmd, &cap, sizeof (cap)); | 
|  | 4163 |  | 
|  | 4164 | return (1); | 
|  | 4165 | } | 
|  | 4166 |  | 
|  | 4167 | /****************************************************************************/ | 
|  | 4168 | /*                                                                          */ | 
|  | 4169 | /* Routine Name: ips_msense                                                 */ | 
|  | 4170 | /*                                                                          */ | 
|  | 4171 | /* Routine Description:                                                     */ | 
|  | 4172 | /*                                                                          */ | 
|  | 4173 | /*   Simulate a mode sense command to a logical drive                       */ | 
|  | 4174 | /*                                                                          */ | 
|  | 4175 | /****************************************************************************/ | 
|  | 4176 | static int | 
|  | 4177 | ips_msense(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4178 | { | 
|  | 4179 | uint16_t heads; | 
|  | 4180 | uint16_t sectors; | 
|  | 4181 | uint32_t cylinders; | 
|  | 4182 | IPS_SCSI_MODE_PAGE_DATA mdata; | 
|  | 4183 |  | 
|  | 4184 | METHOD_TRACE("ips_msense", 1); | 
|  | 4185 |  | 
|  | 4186 | if (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) > 0x400000 && | 
|  | 4187 | (ha->enq->ucMiscFlag & 0x8) == 0) { | 
|  | 4188 | heads = IPS_NORM_HEADS; | 
|  | 4189 | sectors = IPS_NORM_SECTORS; | 
|  | 4190 | } else { | 
|  | 4191 | heads = IPS_COMP_HEADS; | 
|  | 4192 | sectors = IPS_COMP_SECTORS; | 
|  | 4193 | } | 
|  | 4194 |  | 
|  | 4195 | cylinders = | 
|  | 4196 | (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) - | 
|  | 4197 | 1) / (heads * sectors); | 
|  | 4198 |  | 
|  | 4199 | memset(&mdata, 0, sizeof (IPS_SCSI_MODE_PAGE_DATA)); | 
|  | 4200 |  | 
|  | 4201 | mdata.hdr.BlockDescLength = 8; | 
|  | 4202 |  | 
|  | 4203 | switch (scb->scsi_cmd->cmnd[2] & 0x3f) { | 
|  | 4204 | case 0x03:		/* page 3 */ | 
|  | 4205 | mdata.pdata.pg3.PageCode = 3; | 
|  | 4206 | mdata.pdata.pg3.PageLength = sizeof (IPS_SCSI_MODE_PAGE3); | 
|  | 4207 | mdata.hdr.DataLength = | 
|  | 4208 | 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg3.PageLength; | 
|  | 4209 | mdata.pdata.pg3.TracksPerZone = 0; | 
|  | 4210 | mdata.pdata.pg3.AltSectorsPerZone = 0; | 
|  | 4211 | mdata.pdata.pg3.AltTracksPerZone = 0; | 
|  | 4212 | mdata.pdata.pg3.AltTracksPerVolume = 0; | 
|  | 4213 | mdata.pdata.pg3.SectorsPerTrack = cpu_to_be16(sectors); | 
|  | 4214 | mdata.pdata.pg3.BytesPerSector = cpu_to_be16(IPS_BLKSIZE); | 
|  | 4215 | mdata.pdata.pg3.Interleave = cpu_to_be16(1); | 
|  | 4216 | mdata.pdata.pg3.TrackSkew = 0; | 
|  | 4217 | mdata.pdata.pg3.CylinderSkew = 0; | 
|  | 4218 | mdata.pdata.pg3.flags = IPS_SCSI_MP3_SoftSector; | 
|  | 4219 | break; | 
|  | 4220 |  | 
|  | 4221 | case 0x4: | 
|  | 4222 | mdata.pdata.pg4.PageCode = 4; | 
|  | 4223 | mdata.pdata.pg4.PageLength = sizeof (IPS_SCSI_MODE_PAGE4); | 
|  | 4224 | mdata.hdr.DataLength = | 
|  | 4225 | 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg4.PageLength; | 
|  | 4226 | mdata.pdata.pg4.CylindersHigh = | 
|  | 4227 | cpu_to_be16((cylinders >> 8) & 0xFFFF); | 
|  | 4228 | mdata.pdata.pg4.CylindersLow = (cylinders & 0xFF); | 
|  | 4229 | mdata.pdata.pg4.Heads = heads; | 
|  | 4230 | mdata.pdata.pg4.WritePrecompHigh = 0; | 
|  | 4231 | mdata.pdata.pg4.WritePrecompLow = 0; | 
|  | 4232 | mdata.pdata.pg4.ReducedWriteCurrentHigh = 0; | 
|  | 4233 | mdata.pdata.pg4.ReducedWriteCurrentLow = 0; | 
|  | 4234 | mdata.pdata.pg4.StepRate = cpu_to_be16(1); | 
|  | 4235 | mdata.pdata.pg4.LandingZoneHigh = 0; | 
|  | 4236 | mdata.pdata.pg4.LandingZoneLow = 0; | 
|  | 4237 | mdata.pdata.pg4.flags = 0; | 
|  | 4238 | mdata.pdata.pg4.RotationalOffset = 0; | 
|  | 4239 | mdata.pdata.pg4.MediumRotationRate = 0; | 
|  | 4240 | break; | 
|  | 4241 | case 0x8: | 
|  | 4242 | mdata.pdata.pg8.PageCode = 8; | 
|  | 4243 | mdata.pdata.pg8.PageLength = sizeof (IPS_SCSI_MODE_PAGE8); | 
|  | 4244 | mdata.hdr.DataLength = | 
|  | 4245 | 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg8.PageLength; | 
|  | 4246 | /* everything else is left set to 0 */ | 
|  | 4247 | break; | 
|  | 4248 |  | 
|  | 4249 | default: | 
|  | 4250 | return (0); | 
|  | 4251 | }			/* end switch */ | 
|  | 4252 |  | 
|  | 4253 | ips_scmd_buf_write(scb->scsi_cmd, &mdata, sizeof (mdata)); | 
|  | 4254 |  | 
|  | 4255 | return (1); | 
|  | 4256 | } | 
|  | 4257 |  | 
|  | 4258 | /****************************************************************************/ | 
|  | 4259 | /*                                                                          */ | 
|  | 4260 | /* Routine Name: ips_reqsen                                                 */ | 
|  | 4261 | /*                                                                          */ | 
|  | 4262 | /* Routine Description:                                                     */ | 
|  | 4263 | /*                                                                          */ | 
|  | 4264 | /*   Simulate a request sense command to a logical drive                    */ | 
|  | 4265 | /*                                                                          */ | 
|  | 4266 | /****************************************************************************/ | 
|  | 4267 | static int | 
|  | 4268 | ips_reqsen(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4269 | { | 
|  | 4270 | IPS_SCSI_REQSEN reqsen; | 
|  | 4271 |  | 
|  | 4272 | METHOD_TRACE("ips_reqsen", 1); | 
|  | 4273 |  | 
|  | 4274 | memset(&reqsen, 0, sizeof (IPS_SCSI_REQSEN)); | 
|  | 4275 |  | 
|  | 4276 | reqsen.ResponseCode = | 
|  | 4277 | IPS_SCSI_REQSEN_VALID | IPS_SCSI_REQSEN_CURRENT_ERR; | 
|  | 4278 | reqsen.AdditionalLength = 10; | 
|  | 4279 | reqsen.AdditionalSenseCode = IPS_SCSI_REQSEN_NO_SENSE; | 
|  | 4280 | reqsen.AdditionalSenseCodeQual = IPS_SCSI_REQSEN_NO_SENSE; | 
|  | 4281 |  | 
|  | 4282 | ips_scmd_buf_write(scb->scsi_cmd, &reqsen, sizeof (reqsen)); | 
|  | 4283 |  | 
|  | 4284 | return (1); | 
|  | 4285 | } | 
|  | 4286 |  | 
|  | 4287 | /****************************************************************************/ | 
|  | 4288 | /*                                                                          */ | 
|  | 4289 | /* Routine Name: ips_free                                                   */ | 
|  | 4290 | /*                                                                          */ | 
|  | 4291 | /* Routine Description:                                                     */ | 
|  | 4292 | /*                                                                          */ | 
|  | 4293 | /*   Free any allocated space for this controller                           */ | 
|  | 4294 | /*                                                                          */ | 
|  | 4295 | /****************************************************************************/ | 
|  | 4296 | static void | 
|  | 4297 | ips_free(ips_ha_t * ha) | 
|  | 4298 | { | 
|  | 4299 |  | 
|  | 4300 | METHOD_TRACE("ips_free", 1); | 
|  | 4301 |  | 
|  | 4302 | if (ha) { | 
|  | 4303 | if (ha->enq) { | 
|  | 4304 | pci_free_consistent(ha->pcidev, sizeof(IPS_ENQ), | 
|  | 4305 | ha->enq, ha->enq_busaddr); | 
|  | 4306 | ha->enq = NULL; | 
|  | 4307 | } | 
|  | 4308 |  | 
| Jesper Juhl | c9475cb | 2005-11-07 01:01:26 -0800 | [diff] [blame] | 4309 | kfree(ha->conf); | 
|  | 4310 | ha->conf = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4311 |  | 
|  | 4312 | if (ha->adapt) { | 
|  | 4313 | pci_free_consistent(ha->pcidev, | 
|  | 4314 | sizeof (IPS_ADAPTER) + | 
|  | 4315 | sizeof (IPS_IO_CMD), ha->adapt, | 
|  | 4316 | ha->adapt->hw_status_start); | 
|  | 4317 | ha->adapt = NULL; | 
|  | 4318 | } | 
|  | 4319 |  | 
|  | 4320 | if (ha->logical_drive_info) { | 
|  | 4321 | pci_free_consistent(ha->pcidev, | 
|  | 4322 | sizeof (IPS_LD_INFO), | 
|  | 4323 | ha->logical_drive_info, | 
|  | 4324 | ha->logical_drive_info_dma_addr); | 
|  | 4325 | ha->logical_drive_info = NULL; | 
|  | 4326 | } | 
|  | 4327 |  | 
| Jesper Juhl | c9475cb | 2005-11-07 01:01:26 -0800 | [diff] [blame] | 4328 | kfree(ha->nvram); | 
|  | 4329 | ha->nvram = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4330 |  | 
| Jesper Juhl | c9475cb | 2005-11-07 01:01:26 -0800 | [diff] [blame] | 4331 | kfree(ha->subsys); | 
|  | 4332 | ha->subsys = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4333 |  | 
|  | 4334 | if (ha->ioctl_data) { | 
|  | 4335 | pci_free_consistent(ha->pcidev, ha->ioctl_len, | 
|  | 4336 | ha->ioctl_data, ha->ioctl_busaddr); | 
|  | 4337 | ha->ioctl_data = NULL; | 
|  | 4338 | ha->ioctl_datasize = 0; | 
|  | 4339 | ha->ioctl_len = 0; | 
|  | 4340 | } | 
|  | 4341 | ips_deallocatescbs(ha, ha->max_cmds); | 
|  | 4342 |  | 
|  | 4343 | /* free memory mapped (if applicable) */ | 
|  | 4344 | if (ha->mem_ptr) { | 
|  | 4345 | iounmap(ha->ioremap_ptr); | 
|  | 4346 | ha->ioremap_ptr = NULL; | 
|  | 4347 | ha->mem_ptr = NULL; | 
|  | 4348 | } | 
|  | 4349 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4350 | ha->mem_addr = 0; | 
|  | 4351 |  | 
|  | 4352 | } | 
|  | 4353 | } | 
|  | 4354 |  | 
|  | 4355 | /****************************************************************************/ | 
|  | 4356 | /*                                                                          */ | 
|  | 4357 | /* Routine Name: ips_deallocatescbs                                         */ | 
|  | 4358 | /*                                                                          */ | 
|  | 4359 | /* Routine Description:                                                     */ | 
|  | 4360 | /*                                                                          */ | 
|  | 4361 | /*   Free the command blocks                                                */ | 
|  | 4362 | /*                                                                          */ | 
|  | 4363 | /****************************************************************************/ | 
|  | 4364 | static int | 
|  | 4365 | ips_deallocatescbs(ips_ha_t * ha, int cmds) | 
|  | 4366 | { | 
|  | 4367 | if (ha->scbs) { | 
|  | 4368 | pci_free_consistent(ha->pcidev, | 
|  | 4369 | IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * cmds, | 
|  | 4370 | ha->scbs->sg_list.list, | 
|  | 4371 | ha->scbs->sg_busaddr); | 
|  | 4372 | pci_free_consistent(ha->pcidev, sizeof (ips_scb_t) * cmds, | 
|  | 4373 | ha->scbs, ha->scbs->scb_busaddr); | 
|  | 4374 | ha->scbs = NULL; | 
|  | 4375 | }			/* end if */ | 
|  | 4376 | return 1; | 
|  | 4377 | } | 
|  | 4378 |  | 
|  | 4379 | /****************************************************************************/ | 
|  | 4380 | /*                                                                          */ | 
|  | 4381 | /* Routine Name: ips_allocatescbs                                           */ | 
|  | 4382 | /*                                                                          */ | 
|  | 4383 | /* Routine Description:                                                     */ | 
|  | 4384 | /*                                                                          */ | 
|  | 4385 | /*   Allocate the command blocks                                            */ | 
|  | 4386 | /*                                                                          */ | 
|  | 4387 | /****************************************************************************/ | 
|  | 4388 | static int | 
|  | 4389 | ips_allocatescbs(ips_ha_t * ha) | 
|  | 4390 | { | 
|  | 4391 | ips_scb_t *scb_p; | 
|  | 4392 | IPS_SG_LIST ips_sg; | 
|  | 4393 | int i; | 
|  | 4394 | dma_addr_t command_dma, sg_dma; | 
|  | 4395 |  | 
|  | 4396 | METHOD_TRACE("ips_allocatescbs", 1); | 
|  | 4397 |  | 
|  | 4398 | /* Allocate memory for the SCBs */ | 
|  | 4399 | ha->scbs = | 
|  | 4400 | pci_alloc_consistent(ha->pcidev, ha->max_cmds * sizeof (ips_scb_t), | 
|  | 4401 | &command_dma); | 
|  | 4402 | if (ha->scbs == NULL) | 
|  | 4403 | return 0; | 
|  | 4404 | ips_sg.list = | 
|  | 4405 | pci_alloc_consistent(ha->pcidev, | 
|  | 4406 | IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * | 
|  | 4407 | ha->max_cmds, &sg_dma); | 
|  | 4408 | if (ips_sg.list == NULL) { | 
|  | 4409 | pci_free_consistent(ha->pcidev, | 
|  | 4410 | ha->max_cmds * sizeof (ips_scb_t), ha->scbs, | 
|  | 4411 | command_dma); | 
|  | 4412 | return 0; | 
|  | 4413 | } | 
|  | 4414 |  | 
|  | 4415 | memset(ha->scbs, 0, ha->max_cmds * sizeof (ips_scb_t)); | 
|  | 4416 |  | 
|  | 4417 | for (i = 0; i < ha->max_cmds; i++) { | 
|  | 4418 | scb_p = &ha->scbs[i]; | 
|  | 4419 | scb_p->scb_busaddr = command_dma + sizeof (ips_scb_t) * i; | 
|  | 4420 | /* set up S/G list */ | 
|  | 4421 | if (IPS_USE_ENH_SGLIST(ha)) { | 
|  | 4422 | scb_p->sg_list.enh_list = | 
|  | 4423 | ips_sg.enh_list + i * IPS_MAX_SG; | 
|  | 4424 | scb_p->sg_busaddr = | 
|  | 4425 | sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i; | 
|  | 4426 | } else { | 
|  | 4427 | scb_p->sg_list.std_list = | 
|  | 4428 | ips_sg.std_list + i * IPS_MAX_SG; | 
|  | 4429 | scb_p->sg_busaddr = | 
|  | 4430 | sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i; | 
|  | 4431 | } | 
|  | 4432 |  | 
|  | 4433 | /* add to the free list */ | 
|  | 4434 | if (i < ha->max_cmds - 1) { | 
|  | 4435 | scb_p->q_next = ha->scb_freelist; | 
|  | 4436 | ha->scb_freelist = scb_p; | 
|  | 4437 | } | 
|  | 4438 | } | 
|  | 4439 |  | 
|  | 4440 | /* success */ | 
|  | 4441 | return (1); | 
|  | 4442 | } | 
|  | 4443 |  | 
|  | 4444 | /****************************************************************************/ | 
|  | 4445 | /*                                                                          */ | 
|  | 4446 | /* Routine Name: ips_init_scb                                               */ | 
|  | 4447 | /*                                                                          */ | 
|  | 4448 | /* Routine Description:                                                     */ | 
|  | 4449 | /*                                                                          */ | 
|  | 4450 | /*   Initialize a CCB to default values                                     */ | 
|  | 4451 | /*                                                                          */ | 
|  | 4452 | /****************************************************************************/ | 
|  | 4453 | static void | 
|  | 4454 | ips_init_scb(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4455 | { | 
|  | 4456 | IPS_SG_LIST sg_list; | 
|  | 4457 | uint32_t cmd_busaddr, sg_busaddr; | 
|  | 4458 | METHOD_TRACE("ips_init_scb", 1); | 
|  | 4459 |  | 
|  | 4460 | if (scb == NULL) | 
|  | 4461 | return; | 
|  | 4462 |  | 
|  | 4463 | sg_list.list = scb->sg_list.list; | 
|  | 4464 | cmd_busaddr = scb->scb_busaddr; | 
|  | 4465 | sg_busaddr = scb->sg_busaddr; | 
|  | 4466 | /* zero fill */ | 
|  | 4467 | memset(scb, 0, sizeof (ips_scb_t)); | 
|  | 4468 | memset(ha->dummy, 0, sizeof (IPS_IO_CMD)); | 
|  | 4469 |  | 
|  | 4470 | /* Initialize dummy command bucket */ | 
|  | 4471 | ha->dummy->op_code = 0xFF; | 
|  | 4472 | ha->dummy->ccsar = cpu_to_le32(ha->adapt->hw_status_start | 
|  | 4473 | + sizeof (IPS_ADAPTER)); | 
|  | 4474 | ha->dummy->command_id = IPS_MAX_CMDS; | 
|  | 4475 |  | 
|  | 4476 | /* set bus address of scb */ | 
|  | 4477 | scb->scb_busaddr = cmd_busaddr; | 
|  | 4478 | scb->sg_busaddr = sg_busaddr; | 
|  | 4479 | scb->sg_list.list = sg_list.list; | 
|  | 4480 |  | 
|  | 4481 | /* Neptune Fix */ | 
|  | 4482 | scb->cmd.basic_io.cccr = cpu_to_le32((uint32_t) IPS_BIT_ILE); | 
|  | 4483 | scb->cmd.basic_io.ccsar = cpu_to_le32(ha->adapt->hw_status_start | 
|  | 4484 | + sizeof (IPS_ADAPTER)); | 
|  | 4485 | } | 
|  | 4486 |  | 
|  | 4487 | /****************************************************************************/ | 
|  | 4488 | /*                                                                          */ | 
|  | 4489 | /* Routine Name: ips_get_scb                                                */ | 
|  | 4490 | /*                                                                          */ | 
|  | 4491 | /* Routine Description:                                                     */ | 
|  | 4492 | /*                                                                          */ | 
|  | 4493 | /*   Initialize a CCB to default values                                     */ | 
|  | 4494 | /*                                                                          */ | 
|  | 4495 | /* ASSUMED to be callled from within a lock                                 */ | 
|  | 4496 | /*                                                                          */ | 
|  | 4497 | /****************************************************************************/ | 
|  | 4498 | static ips_scb_t * | 
|  | 4499 | ips_getscb(ips_ha_t * ha) | 
|  | 4500 | { | 
|  | 4501 | ips_scb_t *scb; | 
|  | 4502 |  | 
|  | 4503 | METHOD_TRACE("ips_getscb", 1); | 
|  | 4504 |  | 
|  | 4505 | if ((scb = ha->scb_freelist) == NULL) { | 
|  | 4506 |  | 
|  | 4507 | return (NULL); | 
|  | 4508 | } | 
|  | 4509 |  | 
|  | 4510 | ha->scb_freelist = scb->q_next; | 
|  | 4511 | scb->flags = 0; | 
|  | 4512 | scb->q_next = NULL; | 
|  | 4513 |  | 
|  | 4514 | ips_init_scb(ha, scb); | 
|  | 4515 |  | 
|  | 4516 | return (scb); | 
|  | 4517 | } | 
|  | 4518 |  | 
|  | 4519 | /****************************************************************************/ | 
|  | 4520 | /*                                                                          */ | 
|  | 4521 | /* Routine Name: ips_free_scb                                               */ | 
|  | 4522 | /*                                                                          */ | 
|  | 4523 | /* Routine Description:                                                     */ | 
|  | 4524 | /*                                                                          */ | 
|  | 4525 | /*   Return an unused CCB back to the free list                             */ | 
|  | 4526 | /*                                                                          */ | 
|  | 4527 | /* ASSUMED to be called from within a lock                                  */ | 
|  | 4528 | /*                                                                          */ | 
|  | 4529 | /****************************************************************************/ | 
|  | 4530 | static void | 
|  | 4531 | ips_freescb(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 4532 | { | 
|  | 4533 |  | 
|  | 4534 | METHOD_TRACE("ips_freescb", 1); | 
|  | 4535 | if (scb->flags & IPS_SCB_MAP_SG) | 
| FUJITA Tomonori | 2f4cf91 | 2007-06-13 23:27:09 +0900 | [diff] [blame] | 4536 | scsi_dma_unmap(scb->scsi_cmd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4537 | else if (scb->flags & IPS_SCB_MAP_SINGLE) | 
|  | 4538 | pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len, | 
|  | 4539 | IPS_DMA_DIR(scb)); | 
|  | 4540 |  | 
|  | 4541 | /* check to make sure this is not our "special" scb */ | 
|  | 4542 | if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) { | 
|  | 4543 | scb->q_next = ha->scb_freelist; | 
|  | 4544 | ha->scb_freelist = scb; | 
|  | 4545 | } | 
|  | 4546 | } | 
|  | 4547 |  | 
|  | 4548 | /****************************************************************************/ | 
|  | 4549 | /*                                                                          */ | 
|  | 4550 | /* Routine Name: ips_isinit_copperhead                                      */ | 
|  | 4551 | /*                                                                          */ | 
|  | 4552 | /* Routine Description:                                                     */ | 
|  | 4553 | /*                                                                          */ | 
|  | 4554 | /*   Is controller initialized ?                                            */ | 
|  | 4555 | /*                                                                          */ | 
|  | 4556 | /****************************************************************************/ | 
|  | 4557 | static int | 
|  | 4558 | ips_isinit_copperhead(ips_ha_t * ha) | 
|  | 4559 | { | 
|  | 4560 | uint8_t scpr; | 
|  | 4561 | uint8_t isr; | 
|  | 4562 |  | 
|  | 4563 | METHOD_TRACE("ips_isinit_copperhead", 1); | 
|  | 4564 |  | 
|  | 4565 | isr = inb(ha->io_addr + IPS_REG_HISR); | 
|  | 4566 | scpr = inb(ha->io_addr + IPS_REG_SCPR); | 
|  | 4567 |  | 
|  | 4568 | if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0)) | 
|  | 4569 | return (0); | 
|  | 4570 | else | 
|  | 4571 | return (1); | 
|  | 4572 | } | 
|  | 4573 |  | 
|  | 4574 | /****************************************************************************/ | 
|  | 4575 | /*                                                                          */ | 
|  | 4576 | /* Routine Name: ips_isinit_copperhead_memio                                */ | 
|  | 4577 | /*                                                                          */ | 
|  | 4578 | /* Routine Description:                                                     */ | 
|  | 4579 | /*                                                                          */ | 
|  | 4580 | /*   Is controller initialized ?                                            */ | 
|  | 4581 | /*                                                                          */ | 
|  | 4582 | /****************************************************************************/ | 
|  | 4583 | static int | 
|  | 4584 | ips_isinit_copperhead_memio(ips_ha_t * ha) | 
|  | 4585 | { | 
|  | 4586 | uint8_t isr = 0; | 
|  | 4587 | uint8_t scpr; | 
|  | 4588 |  | 
|  | 4589 | METHOD_TRACE("ips_is_init_copperhead_memio", 1); | 
|  | 4590 |  | 
|  | 4591 | isr = readb(ha->mem_ptr + IPS_REG_HISR); | 
|  | 4592 | scpr = readb(ha->mem_ptr + IPS_REG_SCPR); | 
|  | 4593 |  | 
|  | 4594 | if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0)) | 
|  | 4595 | return (0); | 
|  | 4596 | else | 
|  | 4597 | return (1); | 
|  | 4598 | } | 
|  | 4599 |  | 
|  | 4600 | /****************************************************************************/ | 
|  | 4601 | /*                                                                          */ | 
|  | 4602 | /* Routine Name: ips_isinit_morpheus                                        */ | 
|  | 4603 | /*                                                                          */ | 
|  | 4604 | /* Routine Description:                                                     */ | 
|  | 4605 | /*                                                                          */ | 
|  | 4606 | /*   Is controller initialized ?                                            */ | 
|  | 4607 | /*                                                                          */ | 
|  | 4608 | /****************************************************************************/ | 
|  | 4609 | static int | 
|  | 4610 | ips_isinit_morpheus(ips_ha_t * ha) | 
|  | 4611 | { | 
|  | 4612 | uint32_t post; | 
|  | 4613 | uint32_t bits; | 
|  | 4614 |  | 
|  | 4615 | METHOD_TRACE("ips_is_init_morpheus", 1); | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4616 |  | 
|  | 4617 | if (ips_isintr_morpheus(ha)) | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4618 | ips_flush_and_reset(ha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4619 |  | 
|  | 4620 | post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); | 
|  | 4621 | bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 4622 |  | 
|  | 4623 | if (post == 0) | 
|  | 4624 | return (0); | 
|  | 4625 | else if (bits & 0x3) | 
|  | 4626 | return (0); | 
|  | 4627 | else | 
|  | 4628 | return (1); | 
|  | 4629 | } | 
|  | 4630 |  | 
|  | 4631 | /****************************************************************************/ | 
|  | 4632 | /*                                                                          */ | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4633 | /* Routine Name: ips_flush_and_reset                                        */ | 
|  | 4634 | /*                                                                          */ | 
|  | 4635 | /* Routine Description:                                                     */ | 
|  | 4636 | /*                                                                          */ | 
|  | 4637 | /*   Perform cleanup ( FLUSH and RESET ) when the adapter is in an unknown  */ | 
|  | 4638 | /*   state ( was trying to INIT and an interrupt was already pending ) ...  */ | 
|  | 4639 | /*                                                                          */ | 
|  | 4640 | /****************************************************************************/ | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4641 | static void | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4642 | ips_flush_and_reset(ips_ha_t *ha) | 
|  | 4643 | { | 
|  | 4644 | ips_scb_t *scb; | 
|  | 4645 | int  ret; | 
|  | 4646 | int  time; | 
|  | 4647 | int  done; | 
|  | 4648 | dma_addr_t command_dma; | 
|  | 4649 |  | 
|  | 4650 | /* Create a usuable SCB */ | 
|  | 4651 | scb = pci_alloc_consistent(ha->pcidev, sizeof(ips_scb_t), &command_dma); | 
|  | 4652 | if (scb) { | 
|  | 4653 | memset(scb, 0, sizeof(ips_scb_t)); | 
|  | 4654 | ips_init_scb(ha, scb); | 
|  | 4655 | scb->scb_busaddr = command_dma; | 
|  | 4656 |  | 
|  | 4657 | scb->timeout = ips_cmd_timeout; | 
|  | 4658 | scb->cdb[0] = IPS_CMD_FLUSH; | 
|  | 4659 |  | 
|  | 4660 | scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; | 
|  | 4661 | scb->cmd.flush_cache.command_id = IPS_MAX_CMDS;   /* Use an ID that would otherwise not exist */ | 
|  | 4662 | scb->cmd.flush_cache.state = IPS_NORM_STATE; | 
|  | 4663 | scb->cmd.flush_cache.reserved = 0; | 
|  | 4664 | scb->cmd.flush_cache.reserved2 = 0; | 
|  | 4665 | scb->cmd.flush_cache.reserved3 = 0; | 
|  | 4666 | scb->cmd.flush_cache.reserved4 = 0; | 
|  | 4667 |  | 
|  | 4668 | ret = ips_send_cmd(ha, scb);                      /* Send the Flush Command */ | 
|  | 4669 |  | 
|  | 4670 | if (ret == IPS_SUCCESS) { | 
|  | 4671 | time = 60 * IPS_ONE_SEC;	              /* Max Wait time is 60 seconds */ | 
|  | 4672 | done = 0; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4673 |  | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4674 | while ((time > 0) && (!done)) { | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4675 | done = ips_poll_for_flush_complete(ha); | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4676 | /* This may look evil, but it's only done during extremely rare start-up conditions ! */ | 
|  | 4677 | udelay(1000); | 
|  | 4678 | time--; | 
|  | 4679 | } | 
|  | 4680 | } | 
|  | 4681 | } | 
|  | 4682 |  | 
|  | 4683 | /* Now RESET and INIT the adapter */ | 
|  | 4684 | (*ha->func.reset) (ha); | 
|  | 4685 |  | 
|  | 4686 | pci_free_consistent(ha->pcidev, sizeof(ips_scb_t), scb, command_dma); | 
|  | 4687 | return; | 
|  | 4688 | } | 
|  | 4689 |  | 
|  | 4690 | /****************************************************************************/ | 
|  | 4691 | /*                                                                          */ | 
|  | 4692 | /* Routine Name: ips_poll_for_flush_complete                                */ | 
|  | 4693 | /*                                                                          */ | 
|  | 4694 | /* Routine Description:                                                     */ | 
|  | 4695 | /*                                                                          */ | 
|  | 4696 | /*   Poll for the Flush Command issued by ips_flush_and_reset() to complete */ | 
|  | 4697 | /*   All other responses are just taken off the queue and ignored           */ | 
|  | 4698 | /*                                                                          */ | 
|  | 4699 | /****************************************************************************/ | 
|  | 4700 | static int | 
|  | 4701 | ips_poll_for_flush_complete(ips_ha_t * ha) | 
|  | 4702 | { | 
|  | 4703 | IPS_STATUS cstatus; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4704 |  | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4705 | while (TRUE) { | 
|  | 4706 | cstatus.value = (*ha->func.statupd) (ha); | 
|  | 4707 |  | 
|  | 4708 | if (cstatus.value == 0xffffffff)      /* If No Interrupt to process */ | 
|  | 4709 | break; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4710 |  | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4711 | /* Success is when we see the Flush Command ID */ | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4712 | if (cstatus.fields.command_id == IPS_MAX_CMDS) | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4713 | return 1; | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 4714 | } | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4715 |  | 
|  | 4716 | return 0; | 
| James Bottomley | 0ee957c | 2005-11-04 23:22:55 -0600 | [diff] [blame] | 4717 | } | 
| Jack Hammer | ee807c2 | 2005-08-29 10:44:34 -0400 | [diff] [blame] | 4718 |  | 
|  | 4719 | /****************************************************************************/ | 
|  | 4720 | /*                                                                          */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4721 | /* Routine Name: ips_enable_int_copperhead                                  */ | 
|  | 4722 | /*                                                                          */ | 
|  | 4723 | /* Routine Description:                                                     */ | 
|  | 4724 | /*   Turn on interrupts                                                     */ | 
|  | 4725 | /*                                                                          */ | 
|  | 4726 | /****************************************************************************/ | 
|  | 4727 | static void | 
|  | 4728 | ips_enable_int_copperhead(ips_ha_t * ha) | 
|  | 4729 | { | 
|  | 4730 | METHOD_TRACE("ips_enable_int_copperhead", 1); | 
|  | 4731 |  | 
|  | 4732 | outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI); | 
|  | 4733 | inb(ha->io_addr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/ | 
|  | 4734 | } | 
|  | 4735 |  | 
|  | 4736 | /****************************************************************************/ | 
|  | 4737 | /*                                                                          */ | 
|  | 4738 | /* Routine Name: ips_enable_int_copperhead_memio                            */ | 
|  | 4739 | /*                                                                          */ | 
|  | 4740 | /* Routine Description:                                                     */ | 
|  | 4741 | /*   Turn on interrupts                                                     */ | 
|  | 4742 | /*                                                                          */ | 
|  | 4743 | /****************************************************************************/ | 
|  | 4744 | static void | 
|  | 4745 | ips_enable_int_copperhead_memio(ips_ha_t * ha) | 
|  | 4746 | { | 
|  | 4747 | METHOD_TRACE("ips_enable_int_copperhead_memio", 1); | 
|  | 4748 |  | 
|  | 4749 | writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR); | 
|  | 4750 | readb(ha->mem_ptr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/ | 
|  | 4751 | } | 
|  | 4752 |  | 
|  | 4753 | /****************************************************************************/ | 
|  | 4754 | /*                                                                          */ | 
|  | 4755 | /* Routine Name: ips_enable_int_morpheus                                    */ | 
|  | 4756 | /*                                                                          */ | 
|  | 4757 | /* Routine Description:                                                     */ | 
|  | 4758 | /*   Turn on interrupts                                                     */ | 
|  | 4759 | /*                                                                          */ | 
|  | 4760 | /****************************************************************************/ | 
|  | 4761 | static void | 
|  | 4762 | ips_enable_int_morpheus(ips_ha_t * ha) | 
|  | 4763 | { | 
|  | 4764 | uint32_t Oimr; | 
|  | 4765 |  | 
|  | 4766 | METHOD_TRACE("ips_enable_int_morpheus", 1); | 
|  | 4767 |  | 
|  | 4768 | Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR); | 
|  | 4769 | Oimr &= ~0x08; | 
|  | 4770 | writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR); | 
|  | 4771 | readl(ha->mem_ptr + IPS_REG_I960_OIMR);	/*Ensure PCI Posting Completes*/ | 
|  | 4772 | } | 
|  | 4773 |  | 
|  | 4774 | /****************************************************************************/ | 
|  | 4775 | /*                                                                          */ | 
|  | 4776 | /* Routine Name: ips_init_copperhead                                        */ | 
|  | 4777 | /*                                                                          */ | 
|  | 4778 | /* Routine Description:                                                     */ | 
|  | 4779 | /*                                                                          */ | 
|  | 4780 | /*   Initialize a copperhead controller                                     */ | 
|  | 4781 | /*                                                                          */ | 
|  | 4782 | /****************************************************************************/ | 
|  | 4783 | static int | 
|  | 4784 | ips_init_copperhead(ips_ha_t * ha) | 
|  | 4785 | { | 
|  | 4786 | uint8_t Isr; | 
|  | 4787 | uint8_t Cbsp; | 
|  | 4788 | uint8_t PostByte[IPS_MAX_POST_BYTES]; | 
|  | 4789 | uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES]; | 
|  | 4790 | int i, j; | 
|  | 4791 |  | 
|  | 4792 | METHOD_TRACE("ips_init_copperhead", 1); | 
|  | 4793 |  | 
|  | 4794 | for (i = 0; i < IPS_MAX_POST_BYTES; i++) { | 
|  | 4795 | for (j = 0; j < 45; j++) { | 
|  | 4796 | Isr = inb(ha->io_addr + IPS_REG_HISR); | 
|  | 4797 | if (Isr & IPS_BIT_GHI) | 
|  | 4798 | break; | 
|  | 4799 |  | 
|  | 4800 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4801 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4802 | } | 
|  | 4803 |  | 
|  | 4804 | if (j >= 45) | 
|  | 4805 | /* error occurred */ | 
|  | 4806 | return (0); | 
|  | 4807 |  | 
|  | 4808 | PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR); | 
|  | 4809 | outb(Isr, ha->io_addr + IPS_REG_HISR); | 
|  | 4810 | } | 
|  | 4811 |  | 
|  | 4812 | if (PostByte[0] < IPS_GOOD_POST_STATUS) { | 
|  | 4813 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 4814 | "reset controller fails (post status %x %x).\n", | 
|  | 4815 | PostByte[0], PostByte[1]); | 
|  | 4816 |  | 
|  | 4817 | return (0); | 
|  | 4818 | } | 
|  | 4819 |  | 
|  | 4820 | for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) { | 
|  | 4821 | for (j = 0; j < 240; j++) { | 
|  | 4822 | Isr = inb(ha->io_addr + IPS_REG_HISR); | 
|  | 4823 | if (Isr & IPS_BIT_GHI) | 
|  | 4824 | break; | 
|  | 4825 |  | 
|  | 4826 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4827 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4828 | } | 
|  | 4829 |  | 
|  | 4830 | if (j >= 240) | 
|  | 4831 | /* error occurred */ | 
|  | 4832 | return (0); | 
|  | 4833 |  | 
|  | 4834 | ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR); | 
|  | 4835 | outb(Isr, ha->io_addr + IPS_REG_HISR); | 
|  | 4836 | } | 
|  | 4837 |  | 
|  | 4838 | for (i = 0; i < 240; i++) { | 
|  | 4839 | Cbsp = inb(ha->io_addr + IPS_REG_CBSP); | 
|  | 4840 |  | 
|  | 4841 | if ((Cbsp & IPS_BIT_OP) == 0) | 
|  | 4842 | break; | 
|  | 4843 |  | 
|  | 4844 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4845 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4846 | } | 
|  | 4847 |  | 
|  | 4848 | if (i >= 240) | 
|  | 4849 | /* reset failed */ | 
|  | 4850 | return (0); | 
|  | 4851 |  | 
|  | 4852 | /* setup CCCR */ | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 4853 | outl(0x1010, ha->io_addr + IPS_REG_CCCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4854 |  | 
|  | 4855 | /* Enable busmastering */ | 
|  | 4856 | outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR); | 
|  | 4857 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 4858 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4859 | /* fix for anaconda64 */ | 
|  | 4860 | outl(0, ha->io_addr + IPS_REG_NDAE); | 
|  | 4861 |  | 
|  | 4862 | /* Enable interrupts */ | 
|  | 4863 | outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR); | 
|  | 4864 |  | 
|  | 4865 | return (1); | 
|  | 4866 | } | 
|  | 4867 |  | 
|  | 4868 | /****************************************************************************/ | 
|  | 4869 | /*                                                                          */ | 
|  | 4870 | /* Routine Name: ips_init_copperhead_memio                                  */ | 
|  | 4871 | /*                                                                          */ | 
|  | 4872 | /* Routine Description:                                                     */ | 
|  | 4873 | /*                                                                          */ | 
|  | 4874 | /*   Initialize a copperhead controller with memory mapped I/O              */ | 
|  | 4875 | /*                                                                          */ | 
|  | 4876 | /****************************************************************************/ | 
|  | 4877 | static int | 
|  | 4878 | ips_init_copperhead_memio(ips_ha_t * ha) | 
|  | 4879 | { | 
|  | 4880 | uint8_t Isr = 0; | 
|  | 4881 | uint8_t Cbsp; | 
|  | 4882 | uint8_t PostByte[IPS_MAX_POST_BYTES]; | 
|  | 4883 | uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES]; | 
|  | 4884 | int i, j; | 
|  | 4885 |  | 
|  | 4886 | METHOD_TRACE("ips_init_copperhead_memio", 1); | 
|  | 4887 |  | 
|  | 4888 | for (i = 0; i < IPS_MAX_POST_BYTES; i++) { | 
|  | 4889 | for (j = 0; j < 45; j++) { | 
|  | 4890 | Isr = readb(ha->mem_ptr + IPS_REG_HISR); | 
|  | 4891 | if (Isr & IPS_BIT_GHI) | 
|  | 4892 | break; | 
|  | 4893 |  | 
|  | 4894 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4895 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4896 | } | 
|  | 4897 |  | 
|  | 4898 | if (j >= 45) | 
|  | 4899 | /* error occurred */ | 
|  | 4900 | return (0); | 
|  | 4901 |  | 
|  | 4902 | PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR); | 
|  | 4903 | writeb(Isr, ha->mem_ptr + IPS_REG_HISR); | 
|  | 4904 | } | 
|  | 4905 |  | 
|  | 4906 | if (PostByte[0] < IPS_GOOD_POST_STATUS) { | 
|  | 4907 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 4908 | "reset controller fails (post status %x %x).\n", | 
|  | 4909 | PostByte[0], PostByte[1]); | 
|  | 4910 |  | 
|  | 4911 | return (0); | 
|  | 4912 | } | 
|  | 4913 |  | 
|  | 4914 | for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) { | 
|  | 4915 | for (j = 0; j < 240; j++) { | 
|  | 4916 | Isr = readb(ha->mem_ptr + IPS_REG_HISR); | 
|  | 4917 | if (Isr & IPS_BIT_GHI) | 
|  | 4918 | break; | 
|  | 4919 |  | 
|  | 4920 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4921 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4922 | } | 
|  | 4923 |  | 
|  | 4924 | if (j >= 240) | 
|  | 4925 | /* error occurred */ | 
|  | 4926 | return (0); | 
|  | 4927 |  | 
|  | 4928 | ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR); | 
|  | 4929 | writeb(Isr, ha->mem_ptr + IPS_REG_HISR); | 
|  | 4930 | } | 
|  | 4931 |  | 
|  | 4932 | for (i = 0; i < 240; i++) { | 
|  | 4933 | Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP); | 
|  | 4934 |  | 
|  | 4935 | if ((Cbsp & IPS_BIT_OP) == 0) | 
|  | 4936 | break; | 
|  | 4937 |  | 
|  | 4938 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4939 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4940 | } | 
|  | 4941 |  | 
|  | 4942 | if (i >= 240) | 
|  | 4943 | /* error occurred */ | 
|  | 4944 | return (0); | 
|  | 4945 |  | 
|  | 4946 | /* setup CCCR */ | 
|  | 4947 | writel(0x1010, ha->mem_ptr + IPS_REG_CCCR); | 
|  | 4948 |  | 
|  | 4949 | /* Enable busmastering */ | 
|  | 4950 | writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR); | 
|  | 4951 |  | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 4952 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4953 | /* fix for anaconda64 */ | 
|  | 4954 | writel(0, ha->mem_ptr + IPS_REG_NDAE); | 
|  | 4955 |  | 
|  | 4956 | /* Enable interrupts */ | 
|  | 4957 | writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR); | 
|  | 4958 |  | 
|  | 4959 | /* if we get here then everything went OK */ | 
|  | 4960 | return (1); | 
|  | 4961 | } | 
|  | 4962 |  | 
|  | 4963 | /****************************************************************************/ | 
|  | 4964 | /*                                                                          */ | 
|  | 4965 | /* Routine Name: ips_init_morpheus                                          */ | 
|  | 4966 | /*                                                                          */ | 
|  | 4967 | /* Routine Description:                                                     */ | 
|  | 4968 | /*                                                                          */ | 
|  | 4969 | /*   Initialize a morpheus controller                                       */ | 
|  | 4970 | /*                                                                          */ | 
|  | 4971 | /****************************************************************************/ | 
|  | 4972 | static int | 
|  | 4973 | ips_init_morpheus(ips_ha_t * ha) | 
|  | 4974 | { | 
|  | 4975 | uint32_t Post; | 
|  | 4976 | uint32_t Config; | 
|  | 4977 | uint32_t Isr; | 
|  | 4978 | uint32_t Oimr; | 
|  | 4979 | int i; | 
|  | 4980 |  | 
|  | 4981 | METHOD_TRACE("ips_init_morpheus", 1); | 
|  | 4982 |  | 
|  | 4983 | /* Wait up to 45 secs for Post */ | 
|  | 4984 | for (i = 0; i < 45; i++) { | 
|  | 4985 | Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 4986 |  | 
|  | 4987 | if (Isr & IPS_BIT_I960_MSG0I) | 
|  | 4988 | break; | 
|  | 4989 |  | 
|  | 4990 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 4991 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4992 | } | 
|  | 4993 |  | 
|  | 4994 | if (i >= 45) { | 
|  | 4995 | /* error occurred */ | 
|  | 4996 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 4997 | "timeout waiting for post.\n"); | 
|  | 4998 |  | 
|  | 4999 | return (0); | 
|  | 5000 | } | 
|  | 5001 |  | 
|  | 5002 | Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); | 
|  | 5003 |  | 
|  | 5004 | if (Post == 0x4F00) {	/* If Flashing the Battery PIC         */ | 
|  | 5005 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5006 | "Flashing Battery PIC, Please wait ...\n"); | 
|  | 5007 |  | 
|  | 5008 | /* Clear the interrupt bit */ | 
|  | 5009 | Isr = (uint32_t) IPS_BIT_I960_MSG0I; | 
|  | 5010 | writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 5011 |  | 
|  | 5012 | for (i = 0; i < 120; i++) {	/*    Wait Up to 2 Min. for Completion */ | 
|  | 5013 | Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); | 
|  | 5014 | if (Post != 0x4F00) | 
|  | 5015 | break; | 
|  | 5016 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5017 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5018 | } | 
|  | 5019 |  | 
|  | 5020 | if (i >= 120) { | 
|  | 5021 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5022 | "timeout waiting for Battery PIC Flash\n"); | 
|  | 5023 | return (0); | 
|  | 5024 | } | 
|  | 5025 |  | 
|  | 5026 | } | 
|  | 5027 |  | 
|  | 5028 | /* Clear the interrupt bit */ | 
|  | 5029 | Isr = (uint32_t) IPS_BIT_I960_MSG0I; | 
|  | 5030 | writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 5031 |  | 
|  | 5032 | if (Post < (IPS_GOOD_POST_STATUS << 8)) { | 
|  | 5033 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5034 | "reset controller fails (post status %x).\n", Post); | 
|  | 5035 |  | 
|  | 5036 | return (0); | 
|  | 5037 | } | 
|  | 5038 |  | 
|  | 5039 | /* Wait up to 240 secs for config bytes */ | 
|  | 5040 | for (i = 0; i < 240; i++) { | 
|  | 5041 | Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 5042 |  | 
|  | 5043 | if (Isr & IPS_BIT_I960_MSG1I) | 
|  | 5044 | break; | 
|  | 5045 |  | 
|  | 5046 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5047 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5048 | } | 
|  | 5049 |  | 
|  | 5050 | if (i >= 240) { | 
|  | 5051 | /* error occurred */ | 
|  | 5052 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5053 | "timeout waiting for config.\n"); | 
|  | 5054 |  | 
|  | 5055 | return (0); | 
|  | 5056 | } | 
|  | 5057 |  | 
|  | 5058 | Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1); | 
|  | 5059 |  | 
|  | 5060 | /* Clear interrupt bit */ | 
|  | 5061 | Isr = (uint32_t) IPS_BIT_I960_MSG1I; | 
|  | 5062 | writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 5063 |  | 
|  | 5064 | /* Turn on the interrupts */ | 
|  | 5065 | Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR); | 
|  | 5066 | Oimr &= ~0x8; | 
|  | 5067 | writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR); | 
|  | 5068 |  | 
|  | 5069 | /* if we get here then everything went OK */ | 
|  | 5070 |  | 
|  | 5071 | /* Since we did a RESET, an EraseStripeLock may be needed */ | 
|  | 5072 | if (Post == 0xEF10) { | 
|  | 5073 | if ((Config == 0x000F) || (Config == 0x0009)) | 
|  | 5074 | ha->requires_esl = 1; | 
|  | 5075 | } | 
|  | 5076 |  | 
|  | 5077 | return (1); | 
|  | 5078 | } | 
|  | 5079 |  | 
|  | 5080 | /****************************************************************************/ | 
|  | 5081 | /*                                                                          */ | 
|  | 5082 | /* Routine Name: ips_reset_copperhead                                       */ | 
|  | 5083 | /*                                                                          */ | 
|  | 5084 | /* Routine Description:                                                     */ | 
|  | 5085 | /*                                                                          */ | 
|  | 5086 | /*   Reset the controller                                                   */ | 
|  | 5087 | /*                                                                          */ | 
|  | 5088 | /****************************************************************************/ | 
|  | 5089 | static int | 
|  | 5090 | ips_reset_copperhead(ips_ha_t * ha) | 
|  | 5091 | { | 
|  | 5092 | int reset_counter; | 
|  | 5093 |  | 
|  | 5094 | METHOD_TRACE("ips_reset_copperhead", 1); | 
|  | 5095 |  | 
|  | 5096 | DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d", | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 5097 | ips_name, ha->host_num, ha->io_addr, ha->pcidev->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5098 |  | 
|  | 5099 | reset_counter = 0; | 
|  | 5100 |  | 
|  | 5101 | while (reset_counter < 2) { | 
|  | 5102 | reset_counter++; | 
|  | 5103 |  | 
|  | 5104 | outb(IPS_BIT_RST, ha->io_addr + IPS_REG_SCPR); | 
|  | 5105 |  | 
|  | 5106 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5107 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5108 |  | 
|  | 5109 | outb(0, ha->io_addr + IPS_REG_SCPR); | 
|  | 5110 |  | 
|  | 5111 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5112 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5113 |  | 
|  | 5114 | if ((*ha->func.init) (ha)) | 
|  | 5115 | break; | 
|  | 5116 | else if (reset_counter >= 2) { | 
|  | 5117 |  | 
|  | 5118 | return (0); | 
|  | 5119 | } | 
|  | 5120 | } | 
|  | 5121 |  | 
|  | 5122 | return (1); | 
|  | 5123 | } | 
|  | 5124 |  | 
|  | 5125 | /****************************************************************************/ | 
|  | 5126 | /*                                                                          */ | 
|  | 5127 | /* Routine Name: ips_reset_copperhead_memio                                 */ | 
|  | 5128 | /*                                                                          */ | 
|  | 5129 | /* Routine Description:                                                     */ | 
|  | 5130 | /*                                                                          */ | 
|  | 5131 | /*   Reset the controller                                                   */ | 
|  | 5132 | /*                                                                          */ | 
|  | 5133 | /****************************************************************************/ | 
|  | 5134 | static int | 
|  | 5135 | ips_reset_copperhead_memio(ips_ha_t * ha) | 
|  | 5136 | { | 
|  | 5137 | int reset_counter; | 
|  | 5138 |  | 
|  | 5139 | METHOD_TRACE("ips_reset_copperhead_memio", 1); | 
|  | 5140 |  | 
|  | 5141 | DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d", | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 5142 | ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5143 |  | 
|  | 5144 | reset_counter = 0; | 
|  | 5145 |  | 
|  | 5146 | while (reset_counter < 2) { | 
|  | 5147 | reset_counter++; | 
|  | 5148 |  | 
|  | 5149 | writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR); | 
|  | 5150 |  | 
|  | 5151 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5152 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5153 |  | 
|  | 5154 | writeb(0, ha->mem_ptr + IPS_REG_SCPR); | 
|  | 5155 |  | 
|  | 5156 | /* Delay for 1 Second */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5157 | MDELAY(IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5158 |  | 
|  | 5159 | if ((*ha->func.init) (ha)) | 
|  | 5160 | break; | 
|  | 5161 | else if (reset_counter >= 2) { | 
|  | 5162 |  | 
|  | 5163 | return (0); | 
|  | 5164 | } | 
|  | 5165 | } | 
|  | 5166 |  | 
|  | 5167 | return (1); | 
|  | 5168 | } | 
|  | 5169 |  | 
|  | 5170 | /****************************************************************************/ | 
|  | 5171 | /*                                                                          */ | 
|  | 5172 | /* Routine Name: ips_reset_morpheus                                         */ | 
|  | 5173 | /*                                                                          */ | 
|  | 5174 | /* Routine Description:                                                     */ | 
|  | 5175 | /*                                                                          */ | 
|  | 5176 | /*   Reset the controller                                                   */ | 
|  | 5177 | /*                                                                          */ | 
|  | 5178 | /****************************************************************************/ | 
|  | 5179 | static int | 
|  | 5180 | ips_reset_morpheus(ips_ha_t * ha) | 
|  | 5181 | { | 
|  | 5182 | int reset_counter; | 
|  | 5183 | uint8_t junk; | 
|  | 5184 |  | 
|  | 5185 | METHOD_TRACE("ips_reset_morpheus", 1); | 
|  | 5186 |  | 
|  | 5187 | DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d", | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 5188 | ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5189 |  | 
|  | 5190 | reset_counter = 0; | 
|  | 5191 |  | 
|  | 5192 | while (reset_counter < 2) { | 
|  | 5193 | reset_counter++; | 
|  | 5194 |  | 
|  | 5195 | writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR); | 
|  | 5196 |  | 
|  | 5197 | /* Delay for 5 Seconds */ | 
| Andrew Morton | bf47134 | 2006-11-08 19:56:24 -0800 | [diff] [blame] | 5198 | MDELAY(5 * IPS_ONE_SEC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5199 |  | 
|  | 5200 | /* Do a PCI config read to wait for adapter */ | 
|  | 5201 | pci_read_config_byte(ha->pcidev, 4, &junk); | 
|  | 5202 |  | 
|  | 5203 | if ((*ha->func.init) (ha)) | 
|  | 5204 | break; | 
|  | 5205 | else if (reset_counter >= 2) { | 
|  | 5206 |  | 
|  | 5207 | return (0); | 
|  | 5208 | } | 
|  | 5209 | } | 
|  | 5210 |  | 
|  | 5211 | return (1); | 
|  | 5212 | } | 
|  | 5213 |  | 
|  | 5214 | /****************************************************************************/ | 
|  | 5215 | /*                                                                          */ | 
|  | 5216 | /* Routine Name: ips_statinit                                               */ | 
|  | 5217 | /*                                                                          */ | 
|  | 5218 | /* Routine Description:                                                     */ | 
|  | 5219 | /*                                                                          */ | 
|  | 5220 | /*   Initialize the status queues on the controller                         */ | 
|  | 5221 | /*                                                                          */ | 
|  | 5222 | /****************************************************************************/ | 
|  | 5223 | static void | 
|  | 5224 | ips_statinit(ips_ha_t * ha) | 
|  | 5225 | { | 
|  | 5226 | uint32_t phys_status_start; | 
|  | 5227 |  | 
|  | 5228 | METHOD_TRACE("ips_statinit", 1); | 
|  | 5229 |  | 
|  | 5230 | ha->adapt->p_status_start = ha->adapt->status; | 
|  | 5231 | ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; | 
|  | 5232 | ha->adapt->p_status_tail = ha->adapt->status; | 
|  | 5233 |  | 
|  | 5234 | phys_status_start = ha->adapt->hw_status_start; | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5235 | outl(phys_status_start, ha->io_addr + IPS_REG_SQSR); | 
|  | 5236 | outl(phys_status_start + IPS_STATUS_Q_SIZE, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5237 | ha->io_addr + IPS_REG_SQER); | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5238 | outl(phys_status_start + IPS_STATUS_SIZE, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5239 | ha->io_addr + IPS_REG_SQHR); | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5240 | outl(phys_status_start, ha->io_addr + IPS_REG_SQTR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5241 |  | 
|  | 5242 | ha->adapt->hw_status_tail = phys_status_start; | 
|  | 5243 | } | 
|  | 5244 |  | 
|  | 5245 | /****************************************************************************/ | 
|  | 5246 | /*                                                                          */ | 
|  | 5247 | /* Routine Name: ips_statinit_memio                                         */ | 
|  | 5248 | /*                                                                          */ | 
|  | 5249 | /* Routine Description:                                                     */ | 
|  | 5250 | /*                                                                          */ | 
|  | 5251 | /*   Initialize the status queues on the controller                         */ | 
|  | 5252 | /*                                                                          */ | 
|  | 5253 | /****************************************************************************/ | 
|  | 5254 | static void | 
|  | 5255 | ips_statinit_memio(ips_ha_t * ha) | 
|  | 5256 | { | 
|  | 5257 | uint32_t phys_status_start; | 
|  | 5258 |  | 
|  | 5259 | METHOD_TRACE("ips_statinit_memio", 1); | 
|  | 5260 |  | 
|  | 5261 | ha->adapt->p_status_start = ha->adapt->status; | 
|  | 5262 | ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; | 
|  | 5263 | ha->adapt->p_status_tail = ha->adapt->status; | 
|  | 5264 |  | 
|  | 5265 | phys_status_start = ha->adapt->hw_status_start; | 
|  | 5266 | writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR); | 
|  | 5267 | writel(phys_status_start + IPS_STATUS_Q_SIZE, | 
|  | 5268 | ha->mem_ptr + IPS_REG_SQER); | 
|  | 5269 | writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR); | 
|  | 5270 | writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR); | 
|  | 5271 |  | 
|  | 5272 | ha->adapt->hw_status_tail = phys_status_start; | 
|  | 5273 | } | 
|  | 5274 |  | 
|  | 5275 | /****************************************************************************/ | 
|  | 5276 | /*                                                                          */ | 
|  | 5277 | /* Routine Name: ips_statupd_copperhead                                     */ | 
|  | 5278 | /*                                                                          */ | 
|  | 5279 | /* Routine Description:                                                     */ | 
|  | 5280 | /*                                                                          */ | 
|  | 5281 | /*   Remove an element from the status queue                                */ | 
|  | 5282 | /*                                                                          */ | 
|  | 5283 | /****************************************************************************/ | 
|  | 5284 | static uint32_t | 
|  | 5285 | ips_statupd_copperhead(ips_ha_t * ha) | 
|  | 5286 | { | 
|  | 5287 | METHOD_TRACE("ips_statupd_copperhead", 1); | 
|  | 5288 |  | 
|  | 5289 | if (ha->adapt->p_status_tail != ha->adapt->p_status_end) { | 
|  | 5290 | ha->adapt->p_status_tail++; | 
|  | 5291 | ha->adapt->hw_status_tail += sizeof (IPS_STATUS); | 
|  | 5292 | } else { | 
|  | 5293 | ha->adapt->p_status_tail = ha->adapt->p_status_start; | 
|  | 5294 | ha->adapt->hw_status_tail = ha->adapt->hw_status_start; | 
|  | 5295 | } | 
|  | 5296 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5297 | outl(ha->adapt->hw_status_tail, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5298 | ha->io_addr + IPS_REG_SQTR); | 
|  | 5299 |  | 
|  | 5300 | return (ha->adapt->p_status_tail->value); | 
|  | 5301 | } | 
|  | 5302 |  | 
|  | 5303 | /****************************************************************************/ | 
|  | 5304 | /*                                                                          */ | 
|  | 5305 | /* Routine Name: ips_statupd_copperhead_memio                               */ | 
|  | 5306 | /*                                                                          */ | 
|  | 5307 | /* Routine Description:                                                     */ | 
|  | 5308 | /*                                                                          */ | 
|  | 5309 | /*   Remove an element from the status queue                                */ | 
|  | 5310 | /*                                                                          */ | 
|  | 5311 | /****************************************************************************/ | 
|  | 5312 | static uint32_t | 
|  | 5313 | ips_statupd_copperhead_memio(ips_ha_t * ha) | 
|  | 5314 | { | 
|  | 5315 | METHOD_TRACE("ips_statupd_copperhead_memio", 1); | 
|  | 5316 |  | 
|  | 5317 | if (ha->adapt->p_status_tail != ha->adapt->p_status_end) { | 
|  | 5318 | ha->adapt->p_status_tail++; | 
|  | 5319 | ha->adapt->hw_status_tail += sizeof (IPS_STATUS); | 
|  | 5320 | } else { | 
|  | 5321 | ha->adapt->p_status_tail = ha->adapt->p_status_start; | 
|  | 5322 | ha->adapt->hw_status_tail = ha->adapt->hw_status_start; | 
|  | 5323 | } | 
|  | 5324 |  | 
|  | 5325 | writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR); | 
|  | 5326 |  | 
|  | 5327 | return (ha->adapt->p_status_tail->value); | 
|  | 5328 | } | 
|  | 5329 |  | 
|  | 5330 | /****************************************************************************/ | 
|  | 5331 | /*                                                                          */ | 
|  | 5332 | /* Routine Name: ips_statupd_morpheus                                       */ | 
|  | 5333 | /*                                                                          */ | 
|  | 5334 | /* Routine Description:                                                     */ | 
|  | 5335 | /*                                                                          */ | 
|  | 5336 | /*   Remove an element from the status queue                                */ | 
|  | 5337 | /*                                                                          */ | 
|  | 5338 | /****************************************************************************/ | 
|  | 5339 | static uint32_t | 
|  | 5340 | ips_statupd_morpheus(ips_ha_t * ha) | 
|  | 5341 | { | 
|  | 5342 | uint32_t val; | 
|  | 5343 |  | 
|  | 5344 | METHOD_TRACE("ips_statupd_morpheus", 1); | 
|  | 5345 |  | 
|  | 5346 | val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ); | 
|  | 5347 |  | 
|  | 5348 | return (val); | 
|  | 5349 | } | 
|  | 5350 |  | 
|  | 5351 | /****************************************************************************/ | 
|  | 5352 | /*                                                                          */ | 
|  | 5353 | /* Routine Name: ips_issue_copperhead                                       */ | 
|  | 5354 | /*                                                                          */ | 
|  | 5355 | /* Routine Description:                                                     */ | 
|  | 5356 | /*                                                                          */ | 
|  | 5357 | /*   Send a command down to the controller                                  */ | 
|  | 5358 | /*                                                                          */ | 
|  | 5359 | /****************************************************************************/ | 
|  | 5360 | static int | 
|  | 5361 | ips_issue_copperhead(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 5362 | { | 
|  | 5363 | uint32_t TimeOut; | 
|  | 5364 | uint32_t val; | 
|  | 5365 |  | 
|  | 5366 | METHOD_TRACE("ips_issue_copperhead", 1); | 
|  | 5367 |  | 
|  | 5368 | if (scb->scsi_cmd) { | 
|  | 5369 | DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", | 
|  | 5370 | ips_name, | 
|  | 5371 | ha->host_num, | 
|  | 5372 | scb->cdb[0], | 
|  | 5373 | scb->cmd.basic_io.command_id, | 
|  | 5374 | scb->bus, scb->target_id, scb->lun); | 
|  | 5375 | } else { | 
|  | 5376 | DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d", | 
|  | 5377 | ips_name, ha->host_num, scb->cmd.basic_io.command_id); | 
|  | 5378 | } | 
|  | 5379 |  | 
|  | 5380 | TimeOut = 0; | 
|  | 5381 |  | 
|  | 5382 | while ((val = | 
|  | 5383 | le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) { | 
|  | 5384 | udelay(1000); | 
|  | 5385 |  | 
|  | 5386 | if (++TimeOut >= IPS_SEM_TIMEOUT) { | 
|  | 5387 | if (!(val & IPS_BIT_START_STOP)) | 
|  | 5388 | break; | 
|  | 5389 |  | 
|  | 5390 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5391 | "ips_issue val [0x%x].\n", val); | 
|  | 5392 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5393 | "ips_issue semaphore chk timeout.\n"); | 
|  | 5394 |  | 
|  | 5395 | return (IPS_FAILURE); | 
|  | 5396 | }		/* end if */ | 
|  | 5397 | }			/* end while */ | 
|  | 5398 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5399 | outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR); | 
|  | 5400 | outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5401 |  | 
|  | 5402 | return (IPS_SUCCESS); | 
|  | 5403 | } | 
|  | 5404 |  | 
|  | 5405 | /****************************************************************************/ | 
|  | 5406 | /*                                                                          */ | 
|  | 5407 | /* Routine Name: ips_issue_copperhead_memio                                 */ | 
|  | 5408 | /*                                                                          */ | 
|  | 5409 | /* Routine Description:                                                     */ | 
|  | 5410 | /*                                                                          */ | 
|  | 5411 | /*   Send a command down to the controller                                  */ | 
|  | 5412 | /*                                                                          */ | 
|  | 5413 | /****************************************************************************/ | 
|  | 5414 | static int | 
|  | 5415 | ips_issue_copperhead_memio(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 5416 | { | 
|  | 5417 | uint32_t TimeOut; | 
|  | 5418 | uint32_t val; | 
|  | 5419 |  | 
|  | 5420 | METHOD_TRACE("ips_issue_copperhead_memio", 1); | 
|  | 5421 |  | 
|  | 5422 | if (scb->scsi_cmd) { | 
|  | 5423 | DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", | 
|  | 5424 | ips_name, | 
|  | 5425 | ha->host_num, | 
|  | 5426 | scb->cdb[0], | 
|  | 5427 | scb->cmd.basic_io.command_id, | 
|  | 5428 | scb->bus, scb->target_id, scb->lun); | 
|  | 5429 | } else { | 
|  | 5430 | DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", | 
|  | 5431 | ips_name, ha->host_num, scb->cmd.basic_io.command_id); | 
|  | 5432 | } | 
|  | 5433 |  | 
|  | 5434 | TimeOut = 0; | 
|  | 5435 |  | 
|  | 5436 | while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) { | 
|  | 5437 | udelay(1000); | 
|  | 5438 |  | 
|  | 5439 | if (++TimeOut >= IPS_SEM_TIMEOUT) { | 
|  | 5440 | if (!(val & IPS_BIT_START_STOP)) | 
|  | 5441 | break; | 
|  | 5442 |  | 
|  | 5443 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5444 | "ips_issue val [0x%x].\n", val); | 
|  | 5445 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5446 | "ips_issue semaphore chk timeout.\n"); | 
|  | 5447 |  | 
|  | 5448 | return (IPS_FAILURE); | 
|  | 5449 | }		/* end if */ | 
|  | 5450 | }			/* end while */ | 
|  | 5451 |  | 
|  | 5452 | writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR); | 
|  | 5453 | writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR); | 
|  | 5454 |  | 
|  | 5455 | return (IPS_SUCCESS); | 
|  | 5456 | } | 
|  | 5457 |  | 
|  | 5458 | /****************************************************************************/ | 
|  | 5459 | /*                                                                          */ | 
|  | 5460 | /* Routine Name: ips_issue_i2o                                              */ | 
|  | 5461 | /*                                                                          */ | 
|  | 5462 | /* Routine Description:                                                     */ | 
|  | 5463 | /*                                                                          */ | 
|  | 5464 | /*   Send a command down to the controller                                  */ | 
|  | 5465 | /*                                                                          */ | 
|  | 5466 | /****************************************************************************/ | 
|  | 5467 | static int | 
|  | 5468 | ips_issue_i2o(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 5469 | { | 
|  | 5470 |  | 
|  | 5471 | METHOD_TRACE("ips_issue_i2o", 1); | 
|  | 5472 |  | 
|  | 5473 | if (scb->scsi_cmd) { | 
|  | 5474 | DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", | 
|  | 5475 | ips_name, | 
|  | 5476 | ha->host_num, | 
|  | 5477 | scb->cdb[0], | 
|  | 5478 | scb->cmd.basic_io.command_id, | 
|  | 5479 | scb->bus, scb->target_id, scb->lun); | 
|  | 5480 | } else { | 
|  | 5481 | DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", | 
|  | 5482 | ips_name, ha->host_num, scb->cmd.basic_io.command_id); | 
|  | 5483 | } | 
|  | 5484 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 5485 | outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5486 |  | 
|  | 5487 | return (IPS_SUCCESS); | 
|  | 5488 | } | 
|  | 5489 |  | 
|  | 5490 | /****************************************************************************/ | 
|  | 5491 | /*                                                                          */ | 
|  | 5492 | /* Routine Name: ips_issue_i2o_memio                                        */ | 
|  | 5493 | /*                                                                          */ | 
|  | 5494 | /* Routine Description:                                                     */ | 
|  | 5495 | /*                                                                          */ | 
|  | 5496 | /*   Send a command down to the controller                                  */ | 
|  | 5497 | /*                                                                          */ | 
|  | 5498 | /****************************************************************************/ | 
|  | 5499 | static int | 
|  | 5500 | ips_issue_i2o_memio(ips_ha_t * ha, ips_scb_t * scb) | 
|  | 5501 | { | 
|  | 5502 |  | 
|  | 5503 | METHOD_TRACE("ips_issue_i2o_memio", 1); | 
|  | 5504 |  | 
|  | 5505 | if (scb->scsi_cmd) { | 
|  | 5506 | DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", | 
|  | 5507 | ips_name, | 
|  | 5508 | ha->host_num, | 
|  | 5509 | scb->cdb[0], | 
|  | 5510 | scb->cmd.basic_io.command_id, | 
|  | 5511 | scb->bus, scb->target_id, scb->lun); | 
|  | 5512 | } else { | 
|  | 5513 | DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", | 
|  | 5514 | ips_name, ha->host_num, scb->cmd.basic_io.command_id); | 
|  | 5515 | } | 
|  | 5516 |  | 
|  | 5517 | writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ); | 
|  | 5518 |  | 
|  | 5519 | return (IPS_SUCCESS); | 
|  | 5520 | } | 
|  | 5521 |  | 
|  | 5522 | /****************************************************************************/ | 
|  | 5523 | /*                                                                          */ | 
|  | 5524 | /* Routine Name: ips_isintr_copperhead                                      */ | 
|  | 5525 | /*                                                                          */ | 
|  | 5526 | /* Routine Description:                                                     */ | 
|  | 5527 | /*                                                                          */ | 
|  | 5528 | /*   Test to see if an interrupt is for us                                  */ | 
|  | 5529 | /*                                                                          */ | 
|  | 5530 | /****************************************************************************/ | 
|  | 5531 | static int | 
|  | 5532 | ips_isintr_copperhead(ips_ha_t * ha) | 
|  | 5533 | { | 
|  | 5534 | uint8_t Isr; | 
|  | 5535 |  | 
|  | 5536 | METHOD_TRACE("ips_isintr_copperhead", 2); | 
|  | 5537 |  | 
|  | 5538 | Isr = inb(ha->io_addr + IPS_REG_HISR); | 
|  | 5539 |  | 
|  | 5540 | if (Isr == 0xFF) | 
|  | 5541 | /* ?!?! Nothing really there */ | 
|  | 5542 | return (0); | 
|  | 5543 |  | 
|  | 5544 | if (Isr & IPS_BIT_SCE) | 
|  | 5545 | return (1); | 
|  | 5546 | else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) { | 
|  | 5547 | /* status queue overflow or GHI */ | 
|  | 5548 | /* just clear the interrupt */ | 
|  | 5549 | outb(Isr, ha->io_addr + IPS_REG_HISR); | 
|  | 5550 | } | 
|  | 5551 |  | 
|  | 5552 | return (0); | 
|  | 5553 | } | 
|  | 5554 |  | 
|  | 5555 | /****************************************************************************/ | 
|  | 5556 | /*                                                                          */ | 
|  | 5557 | /* Routine Name: ips_isintr_copperhead_memio                                */ | 
|  | 5558 | /*                                                                          */ | 
|  | 5559 | /* Routine Description:                                                     */ | 
|  | 5560 | /*                                                                          */ | 
|  | 5561 | /*   Test to see if an interrupt is for us                                  */ | 
|  | 5562 | /*                                                                          */ | 
|  | 5563 | /****************************************************************************/ | 
|  | 5564 | static int | 
|  | 5565 | ips_isintr_copperhead_memio(ips_ha_t * ha) | 
|  | 5566 | { | 
|  | 5567 | uint8_t Isr; | 
|  | 5568 |  | 
|  | 5569 | METHOD_TRACE("ips_isintr_memio", 2); | 
|  | 5570 |  | 
|  | 5571 | Isr = readb(ha->mem_ptr + IPS_REG_HISR); | 
|  | 5572 |  | 
|  | 5573 | if (Isr == 0xFF) | 
|  | 5574 | /* ?!?! Nothing really there */ | 
|  | 5575 | return (0); | 
|  | 5576 |  | 
|  | 5577 | if (Isr & IPS_BIT_SCE) | 
|  | 5578 | return (1); | 
|  | 5579 | else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) { | 
|  | 5580 | /* status queue overflow or GHI */ | 
|  | 5581 | /* just clear the interrupt */ | 
|  | 5582 | writeb(Isr, ha->mem_ptr + IPS_REG_HISR); | 
|  | 5583 | } | 
|  | 5584 |  | 
|  | 5585 | return (0); | 
|  | 5586 | } | 
|  | 5587 |  | 
|  | 5588 | /****************************************************************************/ | 
|  | 5589 | /*                                                                          */ | 
|  | 5590 | /* Routine Name: ips_isintr_morpheus                                        */ | 
|  | 5591 | /*                                                                          */ | 
|  | 5592 | /* Routine Description:                                                     */ | 
|  | 5593 | /*                                                                          */ | 
|  | 5594 | /*   Test to see if an interrupt is for us                                  */ | 
|  | 5595 | /*                                                                          */ | 
|  | 5596 | /****************************************************************************/ | 
|  | 5597 | static int | 
|  | 5598 | ips_isintr_morpheus(ips_ha_t * ha) | 
|  | 5599 | { | 
|  | 5600 | uint32_t Isr; | 
|  | 5601 |  | 
|  | 5602 | METHOD_TRACE("ips_isintr_morpheus", 2); | 
|  | 5603 |  | 
|  | 5604 | Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); | 
|  | 5605 |  | 
|  | 5606 | if (Isr & IPS_BIT_I2O_OPQI) | 
|  | 5607 | return (1); | 
|  | 5608 | else | 
|  | 5609 | return (0); | 
|  | 5610 | } | 
|  | 5611 |  | 
|  | 5612 | /****************************************************************************/ | 
|  | 5613 | /*                                                                          */ | 
|  | 5614 | /* Routine Name: ips_wait                                                   */ | 
|  | 5615 | /*                                                                          */ | 
|  | 5616 | /* Routine Description:                                                     */ | 
|  | 5617 | /*                                                                          */ | 
|  | 5618 | /*   Wait for a command to complete                                         */ | 
|  | 5619 | /*                                                                          */ | 
|  | 5620 | /****************************************************************************/ | 
|  | 5621 | static int | 
|  | 5622 | ips_wait(ips_ha_t * ha, int time, int intr) | 
|  | 5623 | { | 
|  | 5624 | int ret; | 
|  | 5625 | int done; | 
|  | 5626 |  | 
|  | 5627 | METHOD_TRACE("ips_wait", 1); | 
|  | 5628 |  | 
|  | 5629 | ret = IPS_FAILURE; | 
|  | 5630 | done = FALSE; | 
|  | 5631 |  | 
|  | 5632 | time *= IPS_ONE_SEC;	/* convert seconds */ | 
|  | 5633 |  | 
|  | 5634 | while ((time > 0) && (!done)) { | 
|  | 5635 | if (intr == IPS_INTR_ON) { | 
|  | 5636 | if (ha->waitflag == FALSE) { | 
|  | 5637 | ret = IPS_SUCCESS; | 
|  | 5638 | done = TRUE; | 
|  | 5639 | break; | 
|  | 5640 | } | 
|  | 5641 | } else if (intr == IPS_INTR_IORL) { | 
|  | 5642 | if (ha->waitflag == FALSE) { | 
|  | 5643 | /* | 
|  | 5644 | * controller generated an interrupt to | 
|  | 5645 | * acknowledge completion of the command | 
|  | 5646 | * and ips_intr() has serviced the interrupt. | 
|  | 5647 | */ | 
|  | 5648 | ret = IPS_SUCCESS; | 
|  | 5649 | done = TRUE; | 
|  | 5650 | break; | 
|  | 5651 | } | 
|  | 5652 |  | 
|  | 5653 | /* | 
|  | 5654 | * NOTE: we already have the io_request_lock so | 
|  | 5655 | * even if we get an interrupt it won't get serviced | 
|  | 5656 | * until after we finish. | 
|  | 5657 | */ | 
|  | 5658 |  | 
|  | 5659 | (*ha->func.intr) (ha); | 
|  | 5660 | } | 
|  | 5661 |  | 
|  | 5662 | /* This looks like a very evil loop, but it only does this during start-up */ | 
|  | 5663 | udelay(1000); | 
|  | 5664 | time--; | 
|  | 5665 | } | 
|  | 5666 |  | 
|  | 5667 | return (ret); | 
|  | 5668 | } | 
|  | 5669 |  | 
|  | 5670 | /****************************************************************************/ | 
|  | 5671 | /*                                                                          */ | 
|  | 5672 | /* Routine Name: ips_write_driver_status                                    */ | 
|  | 5673 | /*                                                                          */ | 
|  | 5674 | /* Routine Description:                                                     */ | 
|  | 5675 | /*                                                                          */ | 
|  | 5676 | /*   Write OS/Driver version to Page 5 of the nvram on the controller       */ | 
|  | 5677 | /*                                                                          */ | 
|  | 5678 | /****************************************************************************/ | 
|  | 5679 | static int | 
|  | 5680 | ips_write_driver_status(ips_ha_t * ha, int intr) | 
|  | 5681 | { | 
|  | 5682 | METHOD_TRACE("ips_write_driver_status", 1); | 
|  | 5683 |  | 
|  | 5684 | if (!ips_readwrite_page5(ha, FALSE, intr)) { | 
|  | 5685 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5686 | "unable to read NVRAM page 5.\n"); | 
|  | 5687 |  | 
|  | 5688 | return (0); | 
|  | 5689 | } | 
|  | 5690 |  | 
|  | 5691 | /* check to make sure the page has a valid */ | 
|  | 5692 | /* signature */ | 
|  | 5693 | if (le32_to_cpu(ha->nvram->signature) != IPS_NVRAM_P5_SIG) { | 
|  | 5694 | DEBUG_VAR(1, | 
|  | 5695 | "(%s%d) NVRAM page 5 has an invalid signature: %X.", | 
|  | 5696 | ips_name, ha->host_num, ha->nvram->signature); | 
|  | 5697 | ha->nvram->signature = IPS_NVRAM_P5_SIG; | 
|  | 5698 | } | 
|  | 5699 |  | 
|  | 5700 | DEBUG_VAR(2, | 
|  | 5701 | "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.", | 
|  | 5702 | ips_name, ha->host_num, le16_to_cpu(ha->nvram->adapter_type), | 
|  | 5703 | ha->nvram->adapter_slot, ha->nvram->bios_high[0], | 
|  | 5704 | ha->nvram->bios_high[1], ha->nvram->bios_high[2], | 
|  | 5705 | ha->nvram->bios_high[3], ha->nvram->bios_low[0], | 
|  | 5706 | ha->nvram->bios_low[1], ha->nvram->bios_low[2], | 
|  | 5707 | ha->nvram->bios_low[3]); | 
|  | 5708 |  | 
|  | 5709 | ips_get_bios_version(ha, intr); | 
|  | 5710 |  | 
|  | 5711 | /* change values (as needed) */ | 
|  | 5712 | ha->nvram->operating_system = IPS_OS_LINUX; | 
|  | 5713 | ha->nvram->adapter_type = ha->ad_type; | 
|  | 5714 | strncpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4); | 
|  | 5715 | strncpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4); | 
|  | 5716 | strncpy((char *) ha->nvram->bios_high, ha->bios_version, 4); | 
|  | 5717 | strncpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4); | 
|  | 5718 |  | 
| Jack Hammer | a60768e | 2005-11-03 09:46:00 -0500 | [diff] [blame] | 5719 | ha->nvram->versioning = 0;	/* Indicate the Driver Does Not Support Versioning */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5720 |  | 
|  | 5721 | /* now update the page */ | 
|  | 5722 | if (!ips_readwrite_page5(ha, TRUE, intr)) { | 
|  | 5723 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 5724 | "unable to write NVRAM page 5.\n"); | 
|  | 5725 |  | 
|  | 5726 | return (0); | 
|  | 5727 | } | 
|  | 5728 |  | 
|  | 5729 | /* IF NVRAM Page 5 is OK, Use it for Slot Number Info Because Linux Doesn't Do Slots */ | 
|  | 5730 | ha->slot_num = ha->nvram->adapter_slot; | 
|  | 5731 |  | 
|  | 5732 | return (1); | 
|  | 5733 | } | 
|  | 5734 |  | 
|  | 5735 | /****************************************************************************/ | 
|  | 5736 | /*                                                                          */ | 
|  | 5737 | /* Routine Name: ips_read_adapter_status                                    */ | 
|  | 5738 | /*                                                                          */ | 
|  | 5739 | /* Routine Description:                                                     */ | 
|  | 5740 | /*                                                                          */ | 
|  | 5741 | /*   Do an Inquiry command to the adapter                                   */ | 
|  | 5742 | /*                                                                          */ | 
|  | 5743 | /****************************************************************************/ | 
|  | 5744 | static int | 
|  | 5745 | ips_read_adapter_status(ips_ha_t * ha, int intr) | 
|  | 5746 | { | 
|  | 5747 | ips_scb_t *scb; | 
|  | 5748 | int ret; | 
|  | 5749 |  | 
|  | 5750 | METHOD_TRACE("ips_read_adapter_status", 1); | 
|  | 5751 |  | 
|  | 5752 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 5753 |  | 
|  | 5754 | ips_init_scb(ha, scb); | 
|  | 5755 |  | 
|  | 5756 | scb->timeout = ips_cmd_timeout; | 
|  | 5757 | scb->cdb[0] = IPS_CMD_ENQUIRY; | 
|  | 5758 |  | 
|  | 5759 | scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY; | 
|  | 5760 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5761 | scb->cmd.basic_io.sg_count = 0; | 
|  | 5762 | scb->cmd.basic_io.lba = 0; | 
|  | 5763 | scb->cmd.basic_io.sector_count = 0; | 
|  | 5764 | scb->cmd.basic_io.log_drv = 0; | 
|  | 5765 | scb->data_len = sizeof (*ha->enq); | 
|  | 5766 | scb->cmd.basic_io.sg_addr = ha->enq_busaddr; | 
|  | 5767 |  | 
|  | 5768 | /* send command */ | 
|  | 5769 | if (((ret = | 
|  | 5770 | ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) | 
|  | 5771 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5772 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) | 
|  | 5773 | return (0); | 
|  | 5774 |  | 
|  | 5775 | return (1); | 
|  | 5776 | } | 
|  | 5777 |  | 
|  | 5778 | /****************************************************************************/ | 
|  | 5779 | /*                                                                          */ | 
|  | 5780 | /* Routine Name: ips_read_subsystem_parameters                              */ | 
|  | 5781 | /*                                                                          */ | 
|  | 5782 | /* Routine Description:                                                     */ | 
|  | 5783 | /*                                                                          */ | 
|  | 5784 | /*   Read subsystem parameters from the adapter                             */ | 
|  | 5785 | /*                                                                          */ | 
|  | 5786 | /****************************************************************************/ | 
|  | 5787 | static int | 
|  | 5788 | ips_read_subsystem_parameters(ips_ha_t * ha, int intr) | 
|  | 5789 | { | 
|  | 5790 | ips_scb_t *scb; | 
|  | 5791 | int ret; | 
|  | 5792 |  | 
|  | 5793 | METHOD_TRACE("ips_read_subsystem_parameters", 1); | 
|  | 5794 |  | 
|  | 5795 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 5796 |  | 
|  | 5797 | ips_init_scb(ha, scb); | 
|  | 5798 |  | 
|  | 5799 | scb->timeout = ips_cmd_timeout; | 
|  | 5800 | scb->cdb[0] = IPS_CMD_GET_SUBSYS; | 
|  | 5801 |  | 
|  | 5802 | scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS; | 
|  | 5803 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5804 | scb->cmd.basic_io.sg_count = 0; | 
|  | 5805 | scb->cmd.basic_io.lba = 0; | 
|  | 5806 | scb->cmd.basic_io.sector_count = 0; | 
|  | 5807 | scb->cmd.basic_io.log_drv = 0; | 
|  | 5808 | scb->data_len = sizeof (*ha->subsys); | 
|  | 5809 | scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr; | 
|  | 5810 |  | 
|  | 5811 | /* send command */ | 
|  | 5812 | if (((ret = | 
|  | 5813 | ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) | 
|  | 5814 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5815 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) | 
|  | 5816 | return (0); | 
|  | 5817 |  | 
|  | 5818 | memcpy(ha->subsys, ha->ioctl_data, sizeof(*ha->subsys)); | 
|  | 5819 | return (1); | 
|  | 5820 | } | 
|  | 5821 |  | 
|  | 5822 | /****************************************************************************/ | 
|  | 5823 | /*                                                                          */ | 
|  | 5824 | /* Routine Name: ips_read_config                                            */ | 
|  | 5825 | /*                                                                          */ | 
|  | 5826 | /* Routine Description:                                                     */ | 
|  | 5827 | /*                                                                          */ | 
|  | 5828 | /*   Read the configuration on the adapter                                  */ | 
|  | 5829 | /*                                                                          */ | 
|  | 5830 | /****************************************************************************/ | 
|  | 5831 | static int | 
|  | 5832 | ips_read_config(ips_ha_t * ha, int intr) | 
|  | 5833 | { | 
|  | 5834 | ips_scb_t *scb; | 
|  | 5835 | int i; | 
|  | 5836 | int ret; | 
|  | 5837 |  | 
|  | 5838 | METHOD_TRACE("ips_read_config", 1); | 
|  | 5839 |  | 
|  | 5840 | /* set defaults for initiator IDs */ | 
|  | 5841 | for (i = 0; i < 4; i++) | 
|  | 5842 | ha->conf->init_id[i] = 7; | 
|  | 5843 |  | 
|  | 5844 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 5845 |  | 
|  | 5846 | ips_init_scb(ha, scb); | 
|  | 5847 |  | 
|  | 5848 | scb->timeout = ips_cmd_timeout; | 
|  | 5849 | scb->cdb[0] = IPS_CMD_READ_CONF; | 
|  | 5850 |  | 
|  | 5851 | scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF; | 
|  | 5852 | scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5853 | scb->data_len = sizeof (*ha->conf); | 
|  | 5854 | scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr; | 
|  | 5855 |  | 
|  | 5856 | /* send command */ | 
|  | 5857 | if (((ret = | 
|  | 5858 | ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) | 
|  | 5859 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5860 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { | 
|  | 5861 |  | 
|  | 5862 | memset(ha->conf, 0, sizeof (IPS_CONF)); | 
|  | 5863 |  | 
|  | 5864 | /* reset initiator IDs */ | 
|  | 5865 | for (i = 0; i < 4; i++) | 
|  | 5866 | ha->conf->init_id[i] = 7; | 
|  | 5867 |  | 
|  | 5868 | /* Allow Completed with Errors, so JCRM can access the Adapter to fix the problems */ | 
|  | 5869 | if ((scb->basic_status & IPS_GSC_STATUS_MASK) == | 
|  | 5870 | IPS_CMD_CMPLT_WERROR) | 
|  | 5871 | return (1); | 
|  | 5872 |  | 
|  | 5873 | return (0); | 
|  | 5874 | } | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 5875 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5876 | memcpy(ha->conf, ha->ioctl_data, sizeof(*ha->conf)); | 
|  | 5877 | return (1); | 
|  | 5878 | } | 
|  | 5879 |  | 
|  | 5880 | /****************************************************************************/ | 
|  | 5881 | /*                                                                          */ | 
|  | 5882 | /* Routine Name: ips_readwrite_page5                                        */ | 
|  | 5883 | /*                                                                          */ | 
|  | 5884 | /* Routine Description:                                                     */ | 
|  | 5885 | /*                                                                          */ | 
|  | 5886 | /*   Read nvram page 5 from the adapter                                     */ | 
|  | 5887 | /*                                                                          */ | 
|  | 5888 | /****************************************************************************/ | 
|  | 5889 | static int | 
|  | 5890 | ips_readwrite_page5(ips_ha_t * ha, int write, int intr) | 
|  | 5891 | { | 
|  | 5892 | ips_scb_t *scb; | 
|  | 5893 | int ret; | 
|  | 5894 |  | 
|  | 5895 | METHOD_TRACE("ips_readwrite_page5", 1); | 
|  | 5896 |  | 
|  | 5897 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 5898 |  | 
|  | 5899 | ips_init_scb(ha, scb); | 
|  | 5900 |  | 
|  | 5901 | scb->timeout = ips_cmd_timeout; | 
|  | 5902 | scb->cdb[0] = IPS_CMD_RW_NVRAM_PAGE; | 
|  | 5903 |  | 
|  | 5904 | scb->cmd.nvram.op_code = IPS_CMD_RW_NVRAM_PAGE; | 
|  | 5905 | scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5906 | scb->cmd.nvram.page = 5; | 
|  | 5907 | scb->cmd.nvram.write = write; | 
|  | 5908 | scb->cmd.nvram.reserved = 0; | 
|  | 5909 | scb->cmd.nvram.reserved2 = 0; | 
|  | 5910 | scb->data_len = sizeof (*ha->nvram); | 
|  | 5911 | scb->cmd.nvram.buffer_addr = ha->ioctl_busaddr; | 
|  | 5912 | if (write) | 
|  | 5913 | memcpy(ha->ioctl_data, ha->nvram, sizeof(*ha->nvram)); | 
| Jeff Garzik | 2f277d6 | 2007-12-13 16:14:08 -0800 | [diff] [blame] | 5914 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5915 | /* issue the command */ | 
|  | 5916 | if (((ret = | 
|  | 5917 | ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) | 
|  | 5918 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5919 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { | 
|  | 5920 |  | 
|  | 5921 | memset(ha->nvram, 0, sizeof (IPS_NVRAM_P5)); | 
|  | 5922 |  | 
|  | 5923 | return (0); | 
|  | 5924 | } | 
|  | 5925 | if (!write) | 
|  | 5926 | memcpy(ha->nvram, ha->ioctl_data, sizeof(*ha->nvram)); | 
|  | 5927 | return (1); | 
|  | 5928 | } | 
|  | 5929 |  | 
|  | 5930 | /****************************************************************************/ | 
|  | 5931 | /*                                                                          */ | 
|  | 5932 | /* Routine Name: ips_clear_adapter                                          */ | 
|  | 5933 | /*                                                                          */ | 
|  | 5934 | /* Routine Description:                                                     */ | 
|  | 5935 | /*                                                                          */ | 
|  | 5936 | /*   Clear the stripe lock tables                                           */ | 
|  | 5937 | /*                                                                          */ | 
|  | 5938 | /****************************************************************************/ | 
|  | 5939 | static int | 
|  | 5940 | ips_clear_adapter(ips_ha_t * ha, int intr) | 
|  | 5941 | { | 
|  | 5942 | ips_scb_t *scb; | 
|  | 5943 | int ret; | 
|  | 5944 |  | 
|  | 5945 | METHOD_TRACE("ips_clear_adapter", 1); | 
|  | 5946 |  | 
|  | 5947 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 5948 |  | 
|  | 5949 | ips_init_scb(ha, scb); | 
|  | 5950 |  | 
|  | 5951 | scb->timeout = ips_reset_timeout; | 
|  | 5952 | scb->cdb[0] = IPS_CMD_CONFIG_SYNC; | 
|  | 5953 |  | 
|  | 5954 | scb->cmd.config_sync.op_code = IPS_CMD_CONFIG_SYNC; | 
|  | 5955 | scb->cmd.config_sync.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5956 | scb->cmd.config_sync.channel = 0; | 
|  | 5957 | scb->cmd.config_sync.source_target = IPS_POCL; | 
|  | 5958 | scb->cmd.config_sync.reserved = 0; | 
|  | 5959 | scb->cmd.config_sync.reserved2 = 0; | 
|  | 5960 | scb->cmd.config_sync.reserved3 = 0; | 
|  | 5961 |  | 
|  | 5962 | /* issue command */ | 
|  | 5963 | if (((ret = | 
|  | 5964 | ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) | 
|  | 5965 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5966 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) | 
|  | 5967 | return (0); | 
|  | 5968 |  | 
|  | 5969 | /* send unlock stripe command */ | 
|  | 5970 | ips_init_scb(ha, scb); | 
|  | 5971 |  | 
|  | 5972 | scb->cdb[0] = IPS_CMD_ERROR_TABLE; | 
|  | 5973 | scb->timeout = ips_reset_timeout; | 
|  | 5974 |  | 
|  | 5975 | scb->cmd.unlock_stripe.op_code = IPS_CMD_ERROR_TABLE; | 
|  | 5976 | scb->cmd.unlock_stripe.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 5977 | scb->cmd.unlock_stripe.log_drv = 0; | 
|  | 5978 | scb->cmd.unlock_stripe.control = IPS_CSL; | 
|  | 5979 | scb->cmd.unlock_stripe.reserved = 0; | 
|  | 5980 | scb->cmd.unlock_stripe.reserved2 = 0; | 
|  | 5981 | scb->cmd.unlock_stripe.reserved3 = 0; | 
|  | 5982 |  | 
|  | 5983 | /* issue command */ | 
|  | 5984 | if (((ret = | 
|  | 5985 | ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) | 
|  | 5986 | || (ret == IPS_SUCCESS_IMM) | 
|  | 5987 | || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) | 
|  | 5988 | return (0); | 
|  | 5989 |  | 
|  | 5990 | return (1); | 
|  | 5991 | } | 
|  | 5992 |  | 
|  | 5993 | /****************************************************************************/ | 
|  | 5994 | /*                                                                          */ | 
|  | 5995 | /* Routine Name: ips_ffdc_reset                                             */ | 
|  | 5996 | /*                                                                          */ | 
|  | 5997 | /* Routine Description:                                                     */ | 
|  | 5998 | /*                                                                          */ | 
|  | 5999 | /*   FFDC: write reset info                                                 */ | 
|  | 6000 | /*                                                                          */ | 
|  | 6001 | /****************************************************************************/ | 
|  | 6002 | static void | 
|  | 6003 | ips_ffdc_reset(ips_ha_t * ha, int intr) | 
|  | 6004 | { | 
|  | 6005 | ips_scb_t *scb; | 
|  | 6006 |  | 
|  | 6007 | METHOD_TRACE("ips_ffdc_reset", 1); | 
|  | 6008 |  | 
|  | 6009 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 6010 |  | 
|  | 6011 | ips_init_scb(ha, scb); | 
|  | 6012 |  | 
|  | 6013 | scb->timeout = ips_cmd_timeout; | 
|  | 6014 | scb->cdb[0] = IPS_CMD_FFDC; | 
|  | 6015 | scb->cmd.ffdc.op_code = IPS_CMD_FFDC; | 
|  | 6016 | scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 6017 | scb->cmd.ffdc.reset_count = ha->reset_count; | 
|  | 6018 | scb->cmd.ffdc.reset_type = 0x80; | 
|  | 6019 |  | 
|  | 6020 | /* convert time to what the card wants */ | 
|  | 6021 | ips_fix_ffdc_time(ha, scb, ha->last_ffdc); | 
|  | 6022 |  | 
|  | 6023 | /* issue command */ | 
|  | 6024 | ips_send_wait(ha, scb, ips_cmd_timeout, intr); | 
|  | 6025 | } | 
|  | 6026 |  | 
|  | 6027 | /****************************************************************************/ | 
|  | 6028 | /*                                                                          */ | 
|  | 6029 | /* Routine Name: ips_ffdc_time                                              */ | 
|  | 6030 | /*                                                                          */ | 
|  | 6031 | /* Routine Description:                                                     */ | 
|  | 6032 | /*                                                                          */ | 
|  | 6033 | /*   FFDC: write time info                                                  */ | 
|  | 6034 | /*                                                                          */ | 
|  | 6035 | /****************************************************************************/ | 
|  | 6036 | static void | 
|  | 6037 | ips_ffdc_time(ips_ha_t * ha) | 
|  | 6038 | { | 
|  | 6039 | ips_scb_t *scb; | 
|  | 6040 |  | 
|  | 6041 | METHOD_TRACE("ips_ffdc_time", 1); | 
|  | 6042 |  | 
|  | 6043 | DEBUG_VAR(1, "(%s%d) Sending time update.", ips_name, ha->host_num); | 
|  | 6044 |  | 
|  | 6045 | scb = &ha->scbs[ha->max_cmds - 1]; | 
|  | 6046 |  | 
|  | 6047 | ips_init_scb(ha, scb); | 
|  | 6048 |  | 
|  | 6049 | scb->timeout = ips_cmd_timeout; | 
|  | 6050 | scb->cdb[0] = IPS_CMD_FFDC; | 
|  | 6051 | scb->cmd.ffdc.op_code = IPS_CMD_FFDC; | 
|  | 6052 | scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb); | 
|  | 6053 | scb->cmd.ffdc.reset_count = 0; | 
|  | 6054 | scb->cmd.ffdc.reset_type = 0; | 
|  | 6055 |  | 
|  | 6056 | /* convert time to what the card wants */ | 
|  | 6057 | ips_fix_ffdc_time(ha, scb, ha->last_ffdc); | 
|  | 6058 |  | 
|  | 6059 | /* issue command */ | 
|  | 6060 | ips_send_wait(ha, scb, ips_cmd_timeout, IPS_FFDC); | 
|  | 6061 | } | 
|  | 6062 |  | 
|  | 6063 | /****************************************************************************/ | 
|  | 6064 | /*                                                                          */ | 
|  | 6065 | /* Routine Name: ips_fix_ffdc_time                                          */ | 
|  | 6066 | /*                                                                          */ | 
|  | 6067 | /* Routine Description:                                                     */ | 
|  | 6068 | /*   Adjust time_t to what the card wants                                   */ | 
|  | 6069 | /*                                                                          */ | 
|  | 6070 | /****************************************************************************/ | 
|  | 6071 | static void | 
|  | 6072 | ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time_t current_time) | 
|  | 6073 | { | 
|  | 6074 | long days; | 
|  | 6075 | long rem; | 
|  | 6076 | int i; | 
|  | 6077 | int year; | 
|  | 6078 | int yleap; | 
|  | 6079 | int year_lengths[2] = { IPS_DAYS_NORMAL_YEAR, IPS_DAYS_LEAP_YEAR }; | 
|  | 6080 | int month_lengths[12][2] = { {31, 31}, | 
|  | 6081 | {28, 29}, | 
|  | 6082 | {31, 31}, | 
|  | 6083 | {30, 30}, | 
|  | 6084 | {31, 31}, | 
|  | 6085 | {30, 30}, | 
|  | 6086 | {31, 31}, | 
|  | 6087 | {31, 31}, | 
|  | 6088 | {30, 30}, | 
|  | 6089 | {31, 31}, | 
|  | 6090 | {30, 30}, | 
|  | 6091 | {31, 31} | 
|  | 6092 | }; | 
|  | 6093 |  | 
|  | 6094 | METHOD_TRACE("ips_fix_ffdc_time", 1); | 
|  | 6095 |  | 
|  | 6096 | days = current_time / IPS_SECS_DAY; | 
|  | 6097 | rem = current_time % IPS_SECS_DAY; | 
|  | 6098 |  | 
|  | 6099 | scb->cmd.ffdc.hour = (rem / IPS_SECS_HOUR); | 
|  | 6100 | rem = rem % IPS_SECS_HOUR; | 
|  | 6101 | scb->cmd.ffdc.minute = (rem / IPS_SECS_MIN); | 
|  | 6102 | scb->cmd.ffdc.second = (rem % IPS_SECS_MIN); | 
|  | 6103 |  | 
|  | 6104 | year = IPS_EPOCH_YEAR; | 
|  | 6105 | while (days < 0 || days >= year_lengths[yleap = IPS_IS_LEAP_YEAR(year)]) { | 
|  | 6106 | int newy; | 
|  | 6107 |  | 
|  | 6108 | newy = year + (days / IPS_DAYS_NORMAL_YEAR); | 
|  | 6109 | if (days < 0) | 
|  | 6110 | --newy; | 
|  | 6111 | days -= (newy - year) * IPS_DAYS_NORMAL_YEAR + | 
|  | 6112 | IPS_NUM_LEAP_YEARS_THROUGH(newy - 1) - | 
|  | 6113 | IPS_NUM_LEAP_YEARS_THROUGH(year - 1); | 
|  | 6114 | year = newy; | 
|  | 6115 | } | 
|  | 6116 |  | 
|  | 6117 | scb->cmd.ffdc.yearH = year / 100; | 
|  | 6118 | scb->cmd.ffdc.yearL = year % 100; | 
|  | 6119 |  | 
|  | 6120 | for (i = 0; days >= month_lengths[i][yleap]; ++i) | 
|  | 6121 | days -= month_lengths[i][yleap]; | 
|  | 6122 |  | 
|  | 6123 | scb->cmd.ffdc.month = i + 1; | 
|  | 6124 | scb->cmd.ffdc.day = days + 1; | 
|  | 6125 | } | 
|  | 6126 |  | 
|  | 6127 | /**************************************************************************** | 
|  | 6128 | * BIOS Flash Routines                                                      * | 
|  | 6129 | ****************************************************************************/ | 
|  | 6130 |  | 
|  | 6131 | /****************************************************************************/ | 
|  | 6132 | /*                                                                          */ | 
|  | 6133 | /* Routine Name: ips_erase_bios                                             */ | 
|  | 6134 | /*                                                                          */ | 
|  | 6135 | /* Routine Description:                                                     */ | 
|  | 6136 | /*   Erase the BIOS on the adapter                                          */ | 
|  | 6137 | /*                                                                          */ | 
|  | 6138 | /****************************************************************************/ | 
|  | 6139 | static int | 
|  | 6140 | ips_erase_bios(ips_ha_t * ha) | 
|  | 6141 | { | 
|  | 6142 | int timeout; | 
|  | 6143 | uint8_t status = 0; | 
|  | 6144 |  | 
|  | 6145 | METHOD_TRACE("ips_erase_bios", 1); | 
|  | 6146 |  | 
|  | 6147 | status = 0; | 
|  | 6148 |  | 
|  | 6149 | /* Clear the status register */ | 
|  | 6150 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6151 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6152 | udelay(25);	/* 25 us */ | 
|  | 6153 |  | 
|  | 6154 | outb(0x50, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6155 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6156 | udelay(25);	/* 25 us */ | 
|  | 6157 |  | 
|  | 6158 | /* Erase Setup */ | 
|  | 6159 | outb(0x20, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6160 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6161 | udelay(25);	/* 25 us */ | 
|  | 6162 |  | 
|  | 6163 | /* Erase Confirm */ | 
|  | 6164 | outb(0xD0, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6165 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6166 | udelay(25);	/* 25 us */ | 
|  | 6167 |  | 
|  | 6168 | /* Erase Status */ | 
|  | 6169 | outb(0x70, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6170 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6171 | udelay(25);	/* 25 us */ | 
|  | 6172 |  | 
|  | 6173 | timeout = 80000;	/* 80 seconds */ | 
|  | 6174 |  | 
|  | 6175 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6176 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6177 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
|  | 6178 | udelay(25);	/* 25 us */ | 
|  | 6179 | } | 
|  | 6180 |  | 
|  | 6181 | status = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 6182 |  | 
|  | 6183 | if (status & 0x80) | 
|  | 6184 | break; | 
|  | 6185 |  | 
|  | 6186 | MDELAY(1); | 
|  | 6187 | timeout--; | 
|  | 6188 | } | 
|  | 6189 |  | 
|  | 6190 | /* check for timeout */ | 
|  | 6191 | if (timeout <= 0) { | 
|  | 6192 | /* timeout */ | 
|  | 6193 |  | 
|  | 6194 | /* try to suspend the erase */ | 
|  | 6195 | outb(0xB0, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6196 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6197 | udelay(25);	/* 25 us */ | 
|  | 6198 |  | 
|  | 6199 | /* wait for 10 seconds */ | 
|  | 6200 | timeout = 10000; | 
|  | 6201 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6202 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6203 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
|  | 6204 | udelay(25);	/* 25 us */ | 
|  | 6205 | } | 
|  | 6206 |  | 
|  | 6207 | status = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 6208 |  | 
|  | 6209 | if (status & 0xC0) | 
|  | 6210 | break; | 
|  | 6211 |  | 
|  | 6212 | MDELAY(1); | 
|  | 6213 | timeout--; | 
|  | 6214 | } | 
|  | 6215 |  | 
|  | 6216 | return (1); | 
|  | 6217 | } | 
|  | 6218 |  | 
|  | 6219 | /* check for valid VPP */ | 
|  | 6220 | if (status & 0x08) | 
|  | 6221 | /* VPP failure */ | 
|  | 6222 | return (1); | 
|  | 6223 |  | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 6224 | /* check for successful flash */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6225 | if (status & 0x30) | 
|  | 6226 | /* sequence error */ | 
|  | 6227 | return (1); | 
|  | 6228 |  | 
|  | 6229 | /* Otherwise, we were successful */ | 
|  | 6230 | /* clear status */ | 
|  | 6231 | outb(0x50, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6232 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6233 | udelay(25);	/* 25 us */ | 
|  | 6234 |  | 
|  | 6235 | /* enable reads */ | 
|  | 6236 | outb(0xFF, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6237 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6238 | udelay(25);	/* 25 us */ | 
|  | 6239 |  | 
|  | 6240 | return (0); | 
|  | 6241 | } | 
|  | 6242 |  | 
|  | 6243 | /****************************************************************************/ | 
|  | 6244 | /*                                                                          */ | 
|  | 6245 | /* Routine Name: ips_erase_bios_memio                                       */ | 
|  | 6246 | /*                                                                          */ | 
|  | 6247 | /* Routine Description:                                                     */ | 
|  | 6248 | /*   Erase the BIOS on the adapter                                          */ | 
|  | 6249 | /*                                                                          */ | 
|  | 6250 | /****************************************************************************/ | 
|  | 6251 | static int | 
|  | 6252 | ips_erase_bios_memio(ips_ha_t * ha) | 
|  | 6253 | { | 
|  | 6254 | int timeout; | 
|  | 6255 | uint8_t status; | 
|  | 6256 |  | 
|  | 6257 | METHOD_TRACE("ips_erase_bios_memio", 1); | 
|  | 6258 |  | 
|  | 6259 | status = 0; | 
|  | 6260 |  | 
|  | 6261 | /* Clear the status register */ | 
|  | 6262 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6263 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6264 | udelay(25);	/* 25 us */ | 
|  | 6265 |  | 
|  | 6266 | writeb(0x50, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6267 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6268 | udelay(25);	/* 25 us */ | 
|  | 6269 |  | 
|  | 6270 | /* Erase Setup */ | 
|  | 6271 | writeb(0x20, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6272 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6273 | udelay(25);	/* 25 us */ | 
|  | 6274 |  | 
|  | 6275 | /* Erase Confirm */ | 
|  | 6276 | writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6277 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6278 | udelay(25);	/* 25 us */ | 
|  | 6279 |  | 
|  | 6280 | /* Erase Status */ | 
|  | 6281 | writeb(0x70, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6282 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6283 | udelay(25);	/* 25 us */ | 
|  | 6284 |  | 
|  | 6285 | timeout = 80000;	/* 80 seconds */ | 
|  | 6286 |  | 
|  | 6287 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6288 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6289 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
|  | 6290 | udelay(25);	/* 25 us */ | 
|  | 6291 | } | 
|  | 6292 |  | 
|  | 6293 | status = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 6294 |  | 
|  | 6295 | if (status & 0x80) | 
|  | 6296 | break; | 
|  | 6297 |  | 
|  | 6298 | MDELAY(1); | 
|  | 6299 | timeout--; | 
|  | 6300 | } | 
|  | 6301 |  | 
|  | 6302 | /* check for timeout */ | 
|  | 6303 | if (timeout <= 0) { | 
|  | 6304 | /* timeout */ | 
|  | 6305 |  | 
|  | 6306 | /* try to suspend the erase */ | 
|  | 6307 | writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6308 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6309 | udelay(25);	/* 25 us */ | 
|  | 6310 |  | 
|  | 6311 | /* wait for 10 seconds */ | 
|  | 6312 | timeout = 10000; | 
|  | 6313 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6314 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6315 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
|  | 6316 | udelay(25);	/* 25 us */ | 
|  | 6317 | } | 
|  | 6318 |  | 
|  | 6319 | status = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 6320 |  | 
|  | 6321 | if (status & 0xC0) | 
|  | 6322 | break; | 
|  | 6323 |  | 
|  | 6324 | MDELAY(1); | 
|  | 6325 | timeout--; | 
|  | 6326 | } | 
|  | 6327 |  | 
|  | 6328 | return (1); | 
|  | 6329 | } | 
|  | 6330 |  | 
|  | 6331 | /* check for valid VPP */ | 
|  | 6332 | if (status & 0x08) | 
|  | 6333 | /* VPP failure */ | 
|  | 6334 | return (1); | 
|  | 6335 |  | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 6336 | /* check for successful flash */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6337 | if (status & 0x30) | 
|  | 6338 | /* sequence error */ | 
|  | 6339 | return (1); | 
|  | 6340 |  | 
|  | 6341 | /* Otherwise, we were successful */ | 
|  | 6342 | /* clear status */ | 
|  | 6343 | writeb(0x50, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6344 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6345 | udelay(25);	/* 25 us */ | 
|  | 6346 |  | 
|  | 6347 | /* enable reads */ | 
|  | 6348 | writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6349 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6350 | udelay(25);	/* 25 us */ | 
|  | 6351 |  | 
|  | 6352 | return (0); | 
|  | 6353 | } | 
|  | 6354 |  | 
|  | 6355 | /****************************************************************************/ | 
|  | 6356 | /*                                                                          */ | 
|  | 6357 | /* Routine Name: ips_program_bios                                           */ | 
|  | 6358 | /*                                                                          */ | 
|  | 6359 | /* Routine Description:                                                     */ | 
|  | 6360 | /*   Program the BIOS on the adapter                                        */ | 
|  | 6361 | /*                                                                          */ | 
|  | 6362 | /****************************************************************************/ | 
|  | 6363 | static int | 
|  | 6364 | ips_program_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize, | 
|  | 6365 | uint32_t offset) | 
|  | 6366 | { | 
|  | 6367 | int i; | 
|  | 6368 | int timeout; | 
|  | 6369 | uint8_t status = 0; | 
|  | 6370 |  | 
|  | 6371 | METHOD_TRACE("ips_program_bios", 1); | 
|  | 6372 |  | 
|  | 6373 | status = 0; | 
|  | 6374 |  | 
|  | 6375 | for (i = 0; i < buffersize; i++) { | 
|  | 6376 | /* write a byte */ | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 6377 | outl(i + offset, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6378 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6379 | udelay(25);	/* 25 us */ | 
|  | 6380 |  | 
|  | 6381 | outb(0x40, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6382 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6383 | udelay(25);	/* 25 us */ | 
|  | 6384 |  | 
|  | 6385 | outb(buffer[i], ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6386 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6387 | udelay(25);	/* 25 us */ | 
|  | 6388 |  | 
|  | 6389 | /* wait up to one second */ | 
|  | 6390 | timeout = 1000; | 
|  | 6391 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6392 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6393 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
|  | 6394 | udelay(25);	/* 25 us */ | 
|  | 6395 | } | 
|  | 6396 |  | 
|  | 6397 | status = inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 6398 |  | 
|  | 6399 | if (status & 0x80) | 
|  | 6400 | break; | 
|  | 6401 |  | 
|  | 6402 | MDELAY(1); | 
|  | 6403 | timeout--; | 
|  | 6404 | } | 
|  | 6405 |  | 
|  | 6406 | if (timeout == 0) { | 
|  | 6407 | /* timeout error */ | 
|  | 6408 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6409 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6410 | udelay(25);	/* 25 us */ | 
|  | 6411 |  | 
|  | 6412 | outb(0xFF, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6413 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6414 | udelay(25);	/* 25 us */ | 
|  | 6415 |  | 
|  | 6416 | return (1); | 
|  | 6417 | } | 
|  | 6418 |  | 
|  | 6419 | /* check the status */ | 
|  | 6420 | if (status & 0x18) { | 
|  | 6421 | /* programming error */ | 
|  | 6422 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6423 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6424 | udelay(25);	/* 25 us */ | 
|  | 6425 |  | 
|  | 6426 | outb(0xFF, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6427 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6428 | udelay(25);	/* 25 us */ | 
|  | 6429 |  | 
|  | 6430 | return (1); | 
|  | 6431 | } | 
|  | 6432 | }			/* end for */ | 
|  | 6433 |  | 
|  | 6434 | /* Enable reading */ | 
|  | 6435 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6436 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6437 | udelay(25);	/* 25 us */ | 
|  | 6438 |  | 
|  | 6439 | outb(0xFF, ha->io_addr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6440 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6441 | udelay(25);	/* 25 us */ | 
|  | 6442 |  | 
|  | 6443 | return (0); | 
|  | 6444 | } | 
|  | 6445 |  | 
|  | 6446 | /****************************************************************************/ | 
|  | 6447 | /*                                                                          */ | 
|  | 6448 | /* Routine Name: ips_program_bios_memio                                     */ | 
|  | 6449 | /*                                                                          */ | 
|  | 6450 | /* Routine Description:                                                     */ | 
|  | 6451 | /*   Program the BIOS on the adapter                                        */ | 
|  | 6452 | /*                                                                          */ | 
|  | 6453 | /****************************************************************************/ | 
|  | 6454 | static int | 
|  | 6455 | ips_program_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize, | 
|  | 6456 | uint32_t offset) | 
|  | 6457 | { | 
|  | 6458 | int i; | 
|  | 6459 | int timeout; | 
|  | 6460 | uint8_t status = 0; | 
|  | 6461 |  | 
|  | 6462 | METHOD_TRACE("ips_program_bios_memio", 1); | 
|  | 6463 |  | 
|  | 6464 | status = 0; | 
|  | 6465 |  | 
|  | 6466 | for (i = 0; i < buffersize; i++) { | 
|  | 6467 | /* write a byte */ | 
|  | 6468 | writel(i + offset, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6469 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6470 | udelay(25);	/* 25 us */ | 
|  | 6471 |  | 
|  | 6472 | writeb(0x40, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6473 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6474 | udelay(25);	/* 25 us */ | 
|  | 6475 |  | 
|  | 6476 | writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6477 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6478 | udelay(25);	/* 25 us */ | 
|  | 6479 |  | 
|  | 6480 | /* wait up to one second */ | 
|  | 6481 | timeout = 1000; | 
|  | 6482 | while (timeout > 0) { | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6483 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6484 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
|  | 6485 | udelay(25);	/* 25 us */ | 
|  | 6486 | } | 
|  | 6487 |  | 
|  | 6488 | status = readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 6489 |  | 
|  | 6490 | if (status & 0x80) | 
|  | 6491 | break; | 
|  | 6492 |  | 
|  | 6493 | MDELAY(1); | 
|  | 6494 | timeout--; | 
|  | 6495 | } | 
|  | 6496 |  | 
|  | 6497 | if (timeout == 0) { | 
|  | 6498 | /* timeout error */ | 
|  | 6499 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6500 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6501 | udelay(25);	/* 25 us */ | 
|  | 6502 |  | 
|  | 6503 | writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6504 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6505 | udelay(25);	/* 25 us */ | 
|  | 6506 |  | 
|  | 6507 | return (1); | 
|  | 6508 | } | 
|  | 6509 |  | 
|  | 6510 | /* check the status */ | 
|  | 6511 | if (status & 0x18) { | 
|  | 6512 | /* programming error */ | 
|  | 6513 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6514 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6515 | udelay(25);	/* 25 us */ | 
|  | 6516 |  | 
|  | 6517 | writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6518 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6519 | udelay(25);	/* 25 us */ | 
|  | 6520 |  | 
|  | 6521 | return (1); | 
|  | 6522 | } | 
|  | 6523 | }			/* end for */ | 
|  | 6524 |  | 
|  | 6525 | /* Enable reading */ | 
|  | 6526 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6527 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6528 | udelay(25);	/* 25 us */ | 
|  | 6529 |  | 
|  | 6530 | writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6531 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6532 | udelay(25);	/* 25 us */ | 
|  | 6533 |  | 
|  | 6534 | return (0); | 
|  | 6535 | } | 
|  | 6536 |  | 
|  | 6537 | /****************************************************************************/ | 
|  | 6538 | /*                                                                          */ | 
|  | 6539 | /* Routine Name: ips_verify_bios                                            */ | 
|  | 6540 | /*                                                                          */ | 
|  | 6541 | /* Routine Description:                                                     */ | 
|  | 6542 | /*   Verify the BIOS on the adapter                                         */ | 
|  | 6543 | /*                                                                          */ | 
|  | 6544 | /****************************************************************************/ | 
|  | 6545 | static int | 
|  | 6546 | ips_verify_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize, | 
|  | 6547 | uint32_t offset) | 
|  | 6548 | { | 
|  | 6549 | uint8_t checksum; | 
|  | 6550 | int i; | 
|  | 6551 |  | 
|  | 6552 | METHOD_TRACE("ips_verify_bios", 1); | 
|  | 6553 |  | 
|  | 6554 | /* test 1st byte */ | 
|  | 6555 | outl(0, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6556 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6557 | udelay(25);	/* 25 us */ | 
|  | 6558 |  | 
|  | 6559 | if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55) | 
|  | 6560 | return (1); | 
|  | 6561 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 6562 | outl(1, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6563 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6564 | udelay(25);	/* 25 us */ | 
|  | 6565 | if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA) | 
|  | 6566 | return (1); | 
|  | 6567 |  | 
|  | 6568 | checksum = 0xff; | 
|  | 6569 | for (i = 2; i < buffersize; i++) { | 
|  | 6570 |  | 
| James Bottomley | db3cc20 | 2008-04-03 12:28:20 -0500 | [diff] [blame] | 6571 | outl(i + offset, ha->io_addr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6572 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6573 | udelay(25);	/* 25 us */ | 
|  | 6574 |  | 
|  | 6575 | checksum = (uint8_t) checksum + inb(ha->io_addr + IPS_REG_FLDP); | 
|  | 6576 | } | 
|  | 6577 |  | 
|  | 6578 | if (checksum != 0) | 
|  | 6579 | /* failure */ | 
|  | 6580 | return (1); | 
|  | 6581 | else | 
|  | 6582 | /* success */ | 
|  | 6583 | return (0); | 
|  | 6584 | } | 
|  | 6585 |  | 
|  | 6586 | /****************************************************************************/ | 
|  | 6587 | /*                                                                          */ | 
|  | 6588 | /* Routine Name: ips_verify_bios_memio                                      */ | 
|  | 6589 | /*                                                                          */ | 
|  | 6590 | /* Routine Description:                                                     */ | 
|  | 6591 | /*   Verify the BIOS on the adapter                                         */ | 
|  | 6592 | /*                                                                          */ | 
|  | 6593 | /****************************************************************************/ | 
|  | 6594 | static int | 
|  | 6595 | ips_verify_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize, | 
|  | 6596 | uint32_t offset) | 
|  | 6597 | { | 
|  | 6598 | uint8_t checksum; | 
|  | 6599 | int i; | 
|  | 6600 |  | 
|  | 6601 | METHOD_TRACE("ips_verify_bios_memio", 1); | 
|  | 6602 |  | 
|  | 6603 | /* test 1st byte */ | 
|  | 6604 | writel(0, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6605 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6606 | udelay(25);	/* 25 us */ | 
|  | 6607 |  | 
|  | 6608 | if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55) | 
|  | 6609 | return (1); | 
|  | 6610 |  | 
|  | 6611 | writel(1, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6612 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6613 | udelay(25);	/* 25 us */ | 
|  | 6614 | if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA) | 
|  | 6615 | return (1); | 
|  | 6616 |  | 
|  | 6617 | checksum = 0xff; | 
|  | 6618 | for (i = 2; i < buffersize; i++) { | 
|  | 6619 |  | 
|  | 6620 | writel(i + offset, ha->mem_ptr + IPS_REG_FLAP); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6621 | if (ha->pcidev->revision == IPS_REVID_TROMBONE64) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6622 | udelay(25);	/* 25 us */ | 
|  | 6623 |  | 
|  | 6624 | checksum = | 
|  | 6625 | (uint8_t) checksum + readb(ha->mem_ptr + IPS_REG_FLDP); | 
|  | 6626 | } | 
|  | 6627 |  | 
|  | 6628 | if (checksum != 0) | 
|  | 6629 | /* failure */ | 
|  | 6630 | return (1); | 
|  | 6631 | else | 
|  | 6632 | /* success */ | 
|  | 6633 | return (0); | 
|  | 6634 | } | 
|  | 6635 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6636 | /****************************************************************************/ | 
|  | 6637 | /*                                                                          */ | 
|  | 6638 | /* Routine Name: ips_abort_init                                             */ | 
|  | 6639 | /*                                                                          */ | 
|  | 6640 | /* Routine Description:                                                     */ | 
|  | 6641 | /*   cleanup routine for a failed adapter initialization                    */ | 
|  | 6642 | /****************************************************************************/ | 
|  | 6643 | static int | 
|  | 6644 | ips_abort_init(ips_ha_t * ha, int index) | 
|  | 6645 | { | 
|  | 6646 | ha->active = 0; | 
|  | 6647 | ips_free(ha); | 
|  | 6648 | ips_ha[index] = NULL; | 
|  | 6649 | ips_sh[index] = NULL; | 
|  | 6650 | return -1; | 
|  | 6651 | } | 
|  | 6652 |  | 
|  | 6653 | /****************************************************************************/ | 
|  | 6654 | /*                                                                          */ | 
|  | 6655 | /* Routine Name: ips_shift_controllers                                      */ | 
|  | 6656 | /*                                                                          */ | 
|  | 6657 | /* Routine Description:                                                     */ | 
|  | 6658 | /*   helper function for ordering adapters                                  */ | 
|  | 6659 | /****************************************************************************/ | 
|  | 6660 | static void | 
|  | 6661 | ips_shift_controllers(int lowindex, int highindex) | 
|  | 6662 | { | 
|  | 6663 | ips_ha_t *ha_sav = ips_ha[highindex]; | 
|  | 6664 | struct Scsi_Host *sh_sav = ips_sh[highindex]; | 
|  | 6665 | int i; | 
|  | 6666 |  | 
|  | 6667 | for (i = highindex; i > lowindex; i--) { | 
|  | 6668 | ips_ha[i] = ips_ha[i - 1]; | 
|  | 6669 | ips_sh[i] = ips_sh[i - 1]; | 
|  | 6670 | ips_ha[i]->host_num = i; | 
|  | 6671 | } | 
|  | 6672 | ha_sav->host_num = lowindex; | 
|  | 6673 | ips_ha[lowindex] = ha_sav; | 
|  | 6674 | ips_sh[lowindex] = sh_sav; | 
|  | 6675 | } | 
|  | 6676 |  | 
|  | 6677 | /****************************************************************************/ | 
|  | 6678 | /*                                                                          */ | 
|  | 6679 | /* Routine Name: ips_order_controllers                                      */ | 
|  | 6680 | /*                                                                          */ | 
|  | 6681 | /* Routine Description:                                                     */ | 
|  | 6682 | /*   place controllers is the "proper" boot order                           */ | 
|  | 6683 | /****************************************************************************/ | 
|  | 6684 | static void | 
|  | 6685 | ips_order_controllers(void) | 
|  | 6686 | { | 
|  | 6687 | int i, j, tmp, position = 0; | 
|  | 6688 | IPS_NVRAM_P5 *nvram; | 
|  | 6689 | if (!ips_ha[0]) | 
|  | 6690 | return; | 
|  | 6691 | nvram = ips_ha[0]->nvram; | 
|  | 6692 |  | 
|  | 6693 | if (nvram->adapter_order[0]) { | 
|  | 6694 | for (i = 1; i <= nvram->adapter_order[0]; i++) { | 
|  | 6695 | for (j = position; j < ips_num_controllers; j++) { | 
|  | 6696 | switch (ips_ha[j]->ad_type) { | 
|  | 6697 | case IPS_ADTYPE_SERVERAID6M: | 
|  | 6698 | case IPS_ADTYPE_SERVERAID7M: | 
|  | 6699 | if (nvram->adapter_order[i] == 'M') { | 
|  | 6700 | ips_shift_controllers(position, | 
|  | 6701 | j); | 
|  | 6702 | position++; | 
|  | 6703 | } | 
|  | 6704 | break; | 
|  | 6705 | case IPS_ADTYPE_SERVERAID4L: | 
|  | 6706 | case IPS_ADTYPE_SERVERAID4M: | 
|  | 6707 | case IPS_ADTYPE_SERVERAID4MX: | 
|  | 6708 | case IPS_ADTYPE_SERVERAID4LX: | 
|  | 6709 | if (nvram->adapter_order[i] == 'N') { | 
|  | 6710 | ips_shift_controllers(position, | 
|  | 6711 | j); | 
|  | 6712 | position++; | 
|  | 6713 | } | 
|  | 6714 | break; | 
|  | 6715 | case IPS_ADTYPE_SERVERAID6I: | 
|  | 6716 | case IPS_ADTYPE_SERVERAID5I2: | 
|  | 6717 | case IPS_ADTYPE_SERVERAID5I1: | 
|  | 6718 | case IPS_ADTYPE_SERVERAID7k: | 
|  | 6719 | if (nvram->adapter_order[i] == 'S') { | 
|  | 6720 | ips_shift_controllers(position, | 
|  | 6721 | j); | 
|  | 6722 | position++; | 
|  | 6723 | } | 
|  | 6724 | break; | 
|  | 6725 | case IPS_ADTYPE_SERVERAID: | 
|  | 6726 | case IPS_ADTYPE_SERVERAID2: | 
|  | 6727 | case IPS_ADTYPE_NAVAJO: | 
|  | 6728 | case IPS_ADTYPE_KIOWA: | 
|  | 6729 | case IPS_ADTYPE_SERVERAID3L: | 
|  | 6730 | case IPS_ADTYPE_SERVERAID3: | 
|  | 6731 | case IPS_ADTYPE_SERVERAID4H: | 
|  | 6732 | if (nvram->adapter_order[i] == 'A') { | 
|  | 6733 | ips_shift_controllers(position, | 
|  | 6734 | j); | 
|  | 6735 | position++; | 
|  | 6736 | } | 
|  | 6737 | break; | 
|  | 6738 | default: | 
|  | 6739 | break; | 
|  | 6740 | } | 
|  | 6741 | } | 
|  | 6742 | } | 
|  | 6743 | /* if adapter_order[0], then ordering is complete */ | 
|  | 6744 | return; | 
|  | 6745 | } | 
|  | 6746 | /* old bios, use older ordering */ | 
|  | 6747 | tmp = 0; | 
|  | 6748 | for (i = position; i < ips_num_controllers; i++) { | 
|  | 6749 | if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I2 || | 
|  | 6750 | ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I1) { | 
|  | 6751 | ips_shift_controllers(position, i); | 
|  | 6752 | position++; | 
|  | 6753 | tmp = 1; | 
|  | 6754 | } | 
|  | 6755 | } | 
|  | 6756 | /* if there were no 5I cards, then don't do any extra ordering */ | 
|  | 6757 | if (!tmp) | 
|  | 6758 | return; | 
|  | 6759 | for (i = position; i < ips_num_controllers; i++) { | 
|  | 6760 | if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4L || | 
|  | 6761 | ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4M || | 
|  | 6762 | ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4LX || | 
|  | 6763 | ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4MX) { | 
|  | 6764 | ips_shift_controllers(position, i); | 
|  | 6765 | position++; | 
|  | 6766 | } | 
|  | 6767 | } | 
|  | 6768 |  | 
|  | 6769 | return; | 
|  | 6770 | } | 
|  | 6771 |  | 
|  | 6772 | /****************************************************************************/ | 
|  | 6773 | /*                                                                          */ | 
|  | 6774 | /* Routine Name: ips_register_scsi                                          */ | 
|  | 6775 | /*                                                                          */ | 
|  | 6776 | /* Routine Description:                                                     */ | 
|  | 6777 | /*   perform any registration and setup with the scsi layer                 */ | 
|  | 6778 | /****************************************************************************/ | 
|  | 6779 | static int | 
|  | 6780 | ips_register_scsi(int index) | 
|  | 6781 | { | 
|  | 6782 | struct Scsi_Host *sh; | 
|  | 6783 | ips_ha_t *ha, *oldha = ips_ha[index]; | 
|  | 6784 | sh = scsi_host_alloc(&ips_driver_template, sizeof (ips_ha_t)); | 
|  | 6785 | if (!sh) { | 
|  | 6786 | IPS_PRINTK(KERN_WARNING, oldha->pcidev, | 
|  | 6787 | "Unable to register controller with SCSI subsystem\n"); | 
|  | 6788 | return -1; | 
|  | 6789 | } | 
|  | 6790 | ha = IPS_HA(sh); | 
|  | 6791 | memcpy(ha, oldha, sizeof (ips_ha_t)); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6792 | free_irq(oldha->pcidev->irq, oldha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6793 | /* Install the interrupt handler with the new ha */ | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 6794 | if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6795 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 6796 | "Unable to install interrupt handler\n"); | 
| Jeff Garzik | 2551a13 | 2007-12-13 16:14:10 -0800 | [diff] [blame] | 6797 | goto err_out_sh; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6798 | } | 
|  | 6799 |  | 
|  | 6800 | kfree(oldha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6801 |  | 
|  | 6802 | /* Store away needed values for later use */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6803 | sh->unique_id = (ha->io_addr) ? ha->io_addr : ha->mem_addr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6804 | sh->sg_tablesize = sh->hostt->sg_tablesize; | 
|  | 6805 | sh->can_queue = sh->hostt->can_queue; | 
|  | 6806 | sh->cmd_per_lun = sh->hostt->cmd_per_lun; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6807 | sh->use_clustering = sh->hostt->use_clustering; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6808 | sh->max_sectors = 128; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6809 |  | 
|  | 6810 | sh->max_id = ha->ntargets; | 
|  | 6811 | sh->max_lun = ha->nlun; | 
|  | 6812 | sh->max_channel = ha->nbus - 1; | 
|  | 6813 | sh->can_queue = ha->max_cmds - 1; | 
|  | 6814 |  | 
| Jeff Garzik | 2551a13 | 2007-12-13 16:14:10 -0800 | [diff] [blame] | 6815 | if (scsi_add_host(sh, &ha->pcidev->dev)) | 
|  | 6816 | goto err_out; | 
|  | 6817 |  | 
|  | 6818 | ips_sh[index] = sh; | 
|  | 6819 | ips_ha[index] = ha; | 
|  | 6820 |  | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 6821 | scsi_scan_host(sh); | 
|  | 6822 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6823 | return 0; | 
| Jeff Garzik | 2551a13 | 2007-12-13 16:14:10 -0800 | [diff] [blame] | 6824 |  | 
|  | 6825 | err_out: | 
|  | 6826 | free_irq(ha->pcidev->irq, ha); | 
|  | 6827 | err_out_sh: | 
|  | 6828 | scsi_host_put(sh); | 
|  | 6829 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6830 | } | 
|  | 6831 |  | 
|  | 6832 | /*---------------------------------------------------------------------------*/ | 
|  | 6833 | /*   Routine Name: ips_remove_device                                         */ | 
|  | 6834 | /*                                                                           */ | 
|  | 6835 | /*   Routine Description:                                                    */ | 
|  | 6836 | /*     Remove one Adapter ( Hot Plugging )                                   */ | 
|  | 6837 | /*---------------------------------------------------------------------------*/ | 
|  | 6838 | static void __devexit | 
|  | 6839 | ips_remove_device(struct pci_dev *pci_dev) | 
|  | 6840 | { | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6841 | struct Scsi_Host *sh = pci_get_drvdata(pci_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6842 |  | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6843 | pci_set_drvdata(pci_dev, NULL); | 
|  | 6844 |  | 
|  | 6845 | ips_release(sh); | 
|  | 6846 |  | 
|  | 6847 | pci_release_regions(pci_dev); | 
|  | 6848 | pci_disable_device(pci_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6849 | } | 
|  | 6850 |  | 
|  | 6851 | /****************************************************************************/ | 
|  | 6852 | /*                                                                          */ | 
|  | 6853 | /* Routine Name: ips_module_init                                            */ | 
|  | 6854 | /*                                                                          */ | 
|  | 6855 | /* Routine Description:                                                     */ | 
|  | 6856 | /*   function called on module load                                         */ | 
|  | 6857 | /****************************************************************************/ | 
|  | 6858 | static int __init | 
|  | 6859 | ips_module_init(void) | 
|  | 6860 | { | 
| Alan Cox | 02a0fa6 | 2006-09-25 23:45:51 +0100 | [diff] [blame] | 6861 | if (pci_register_driver(&ips_pci_driver) < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6862 | return -ENODEV; | 
|  | 6863 | ips_driver_template.module = THIS_MODULE; | 
|  | 6864 | ips_order_controllers(); | 
| Adrian Bunk | c6a6c81 | 2007-05-23 14:41:46 -0700 | [diff] [blame] | 6865 | if (!ips_detect(&ips_driver_template)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6866 | pci_unregister_driver(&ips_pci_driver); | 
|  | 6867 | return -ENODEV; | 
|  | 6868 | } | 
|  | 6869 | register_reboot_notifier(&ips_notifier); | 
|  | 6870 | return 0; | 
|  | 6871 | } | 
|  | 6872 |  | 
|  | 6873 | /****************************************************************************/ | 
|  | 6874 | /*                                                                          */ | 
|  | 6875 | /* Routine Name: ips_module_exit                                            */ | 
|  | 6876 | /*                                                                          */ | 
|  | 6877 | /* Routine Description:                                                     */ | 
|  | 6878 | /*   function called on module unload                                       */ | 
|  | 6879 | /****************************************************************************/ | 
|  | 6880 | static void __exit | 
|  | 6881 | ips_module_exit(void) | 
|  | 6882 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6883 | pci_unregister_driver(&ips_pci_driver); | 
|  | 6884 | unregister_reboot_notifier(&ips_notifier); | 
|  | 6885 | } | 
|  | 6886 |  | 
|  | 6887 | module_init(ips_module_init); | 
|  | 6888 | module_exit(ips_module_exit); | 
|  | 6889 |  | 
|  | 6890 | /*---------------------------------------------------------------------------*/ | 
|  | 6891 | /*   Routine Name: ips_insert_device                                         */ | 
|  | 6892 | /*                                                                           */ | 
|  | 6893 | /*   Routine Description:                                                    */ | 
|  | 6894 | /*     Add One Adapter ( Hot Plug )                                          */ | 
|  | 6895 | /*                                                                           */ | 
|  | 6896 | /*   Return Value:                                                           */ | 
|  | 6897 | /*     0 if Successful, else non-zero                                        */ | 
|  | 6898 | /*---------------------------------------------------------------------------*/ | 
|  | 6899 | static int __devinit | 
|  | 6900 | ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent) | 
|  | 6901 | { | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6902 | int index = -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6903 | int rc; | 
|  | 6904 |  | 
|  | 6905 | METHOD_TRACE("ips_insert_device", 1); | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6906 | rc = pci_enable_device(pci_dev); | 
|  | 6907 | if (rc) | 
|  | 6908 | return rc; | 
|  | 6909 |  | 
|  | 6910 | rc = pci_request_regions(pci_dev, "ips"); | 
|  | 6911 | if (rc) | 
|  | 6912 | goto err_out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6913 |  | 
|  | 6914 | rc = ips_init_phase1(pci_dev, &index); | 
|  | 6915 | if (rc == SUCCESS) | 
|  | 6916 | rc = ips_init_phase2(index); | 
|  | 6917 |  | 
|  | 6918 | if (ips_hotplug) | 
|  | 6919 | if (ips_register_scsi(index)) { | 
|  | 6920 | ips_free(ips_ha[index]); | 
|  | 6921 | rc = -1; | 
|  | 6922 | } | 
|  | 6923 |  | 
|  | 6924 | if (rc == SUCCESS) | 
|  | 6925 | ips_num_controllers++; | 
|  | 6926 |  | 
|  | 6927 | ips_next_controller = ips_num_controllers; | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6928 |  | 
|  | 6929 | if (rc < 0) { | 
|  | 6930 | rc = -ENODEV; | 
|  | 6931 | goto err_out_regions; | 
|  | 6932 | } | 
|  | 6933 |  | 
|  | 6934 | pci_set_drvdata(pci_dev, ips_sh[index]); | 
|  | 6935 | return 0; | 
|  | 6936 |  | 
|  | 6937 | err_out_regions: | 
|  | 6938 | pci_release_regions(pci_dev); | 
|  | 6939 | err_out: | 
|  | 6940 | pci_disable_device(pci_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6941 | return rc; | 
|  | 6942 | } | 
|  | 6943 |  | 
|  | 6944 | /*---------------------------------------------------------------------------*/ | 
|  | 6945 | /*   Routine Name: ips_init_phase1                                           */ | 
|  | 6946 | /*                                                                           */ | 
|  | 6947 | /*   Routine Description:                                                    */ | 
|  | 6948 | /*     Adapter Initialization                                                */ | 
|  | 6949 | /*                                                                           */ | 
|  | 6950 | /*   Return Value:                                                           */ | 
|  | 6951 | /*     0 if Successful, else non-zero                                        */ | 
|  | 6952 | /*---------------------------------------------------------------------------*/ | 
|  | 6953 | static int | 
|  | 6954 | ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr) | 
|  | 6955 | { | 
|  | 6956 | ips_ha_t *ha; | 
|  | 6957 | uint32_t io_addr; | 
|  | 6958 | uint32_t mem_addr; | 
|  | 6959 | uint32_t io_len; | 
|  | 6960 | uint32_t mem_len; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6961 | uint8_t bus; | 
|  | 6962 | uint8_t func; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6963 | int j; | 
|  | 6964 | int index; | 
|  | 6965 | dma_addr_t dma_address; | 
|  | 6966 | char __iomem *ioremap_ptr; | 
|  | 6967 | char __iomem *mem_ptr; | 
|  | 6968 | uint32_t IsDead; | 
|  | 6969 |  | 
|  | 6970 | METHOD_TRACE("ips_init_phase1", 1); | 
|  | 6971 | index = IPS_MAX_ADAPTERS; | 
|  | 6972 | for (j = 0; j < IPS_MAX_ADAPTERS; j++) { | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 6973 | if (ips_ha[j] == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6974 | index = j; | 
|  | 6975 | break; | 
|  | 6976 | } | 
|  | 6977 | } | 
|  | 6978 |  | 
|  | 6979 | if (index >= IPS_MAX_ADAPTERS) | 
|  | 6980 | return -1; | 
|  | 6981 |  | 
|  | 6982 | /* stuff that we get in dev */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6983 | bus = pci_dev->bus->number; | 
|  | 6984 | func = pci_dev->devfn; | 
|  | 6985 |  | 
|  | 6986 | /* Init MEM/IO addresses to 0 */ | 
|  | 6987 | mem_addr = 0; | 
|  | 6988 | io_addr = 0; | 
|  | 6989 | mem_len = 0; | 
|  | 6990 | io_len = 0; | 
|  | 6991 |  | 
|  | 6992 | for (j = 0; j < 2; j++) { | 
|  | 6993 | if (!pci_resource_start(pci_dev, j)) | 
|  | 6994 | break; | 
|  | 6995 |  | 
|  | 6996 | if (pci_resource_flags(pci_dev, j) & IORESOURCE_IO) { | 
|  | 6997 | io_addr = pci_resource_start(pci_dev, j); | 
|  | 6998 | io_len = pci_resource_len(pci_dev, j); | 
|  | 6999 | } else { | 
|  | 7000 | mem_addr = pci_resource_start(pci_dev, j); | 
|  | 7001 | mem_len = pci_resource_len(pci_dev, j); | 
|  | 7002 | } | 
|  | 7003 | } | 
|  | 7004 |  | 
|  | 7005 | /* setup memory mapped area (if applicable) */ | 
|  | 7006 | if (mem_addr) { | 
|  | 7007 | uint32_t base; | 
|  | 7008 | uint32_t offs; | 
|  | 7009 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7010 | base = mem_addr & PAGE_MASK; | 
|  | 7011 | offs = mem_addr - base; | 
|  | 7012 | ioremap_ptr = ioremap(base, PAGE_SIZE); | 
| Jeff Garzik | 21e1a5f | 2007-12-13 16:14:09 -0800 | [diff] [blame] | 7013 | if (!ioremap_ptr) | 
|  | 7014 | return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7015 | mem_ptr = ioremap_ptr + offs; | 
|  | 7016 | } else { | 
|  | 7017 | ioremap_ptr = NULL; | 
|  | 7018 | mem_ptr = NULL; | 
|  | 7019 | } | 
|  | 7020 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7021 | /* found a controller */ | 
| Yoann Padioleau | dd00cc4 | 2007-07-19 01:49:03 -0700 | [diff] [blame] | 7022 | ha = kzalloc(sizeof (ips_ha_t), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7023 | if (ha == NULL) { | 
|  | 7024 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7025 | "Unable to allocate temporary ha struct\n"); | 
|  | 7026 | return -1; | 
|  | 7027 | } | 
|  | 7028 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7029 | ips_sh[index] = NULL; | 
|  | 7030 | ips_ha[index] = ha; | 
|  | 7031 | ha->active = 1; | 
|  | 7032 |  | 
|  | 7033 | /* Store info in HA structure */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7034 | ha->io_addr = io_addr; | 
|  | 7035 | ha->io_len = io_len; | 
|  | 7036 | ha->mem_addr = mem_addr; | 
|  | 7037 | ha->mem_len = mem_len; | 
|  | 7038 | ha->mem_ptr = mem_ptr; | 
|  | 7039 | ha->ioremap_ptr = ioremap_ptr; | 
|  | 7040 | ha->host_num = (uint32_t) index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7041 | ha->slot_num = PCI_SLOT(pci_dev->devfn); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7042 | ha->pcidev = pci_dev; | 
|  | 7043 |  | 
|  | 7044 | /* | 
|  | 7045 | * Set the pci_dev's dma_mask.  Not all adapters support 64bit | 
|  | 7046 | * addressing so don't enable it if the adapter can't support | 
|  | 7047 | * it!  Also, don't use 64bit addressing if dma addresses | 
|  | 7048 | * are guaranteed to be < 4G. | 
|  | 7049 | */ | 
|  | 7050 | if (IPS_ENABLE_DMA64 && IPS_HAS_ENH_SGLIST(ha) && | 
| Yang Hongyang | 6a35528 | 2009-04-06 19:01:13 -0700 | [diff] [blame] | 7051 | !pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(64))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7052 | (ha)->flags |= IPS_HA_ENH_SG; | 
|  | 7053 | } else { | 
| Yang Hongyang | 284901a | 2009-04-06 19:01:15 -0700 | [diff] [blame] | 7054 | if (pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(32)) != 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7055 | printk(KERN_WARNING "Unable to set DMA Mask\n"); | 
|  | 7056 | return ips_abort_init(ha, index); | 
|  | 7057 | } | 
|  | 7058 | } | 
|  | 7059 | if(ips_cd_boot && !ips_FlashData){ | 
|  | 7060 | ips_FlashData = pci_alloc_consistent(pci_dev, PAGE_SIZE << 7, | 
|  | 7061 | &ips_flashbusaddr); | 
|  | 7062 | } | 
|  | 7063 |  | 
|  | 7064 | ha->enq = pci_alloc_consistent(pci_dev, sizeof (IPS_ENQ), | 
|  | 7065 | &ha->enq_busaddr); | 
|  | 7066 | if (!ha->enq) { | 
|  | 7067 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7068 | "Unable to allocate host inquiry structure\n"); | 
|  | 7069 | return ips_abort_init(ha, index); | 
|  | 7070 | } | 
|  | 7071 |  | 
|  | 7072 | ha->adapt = pci_alloc_consistent(pci_dev, sizeof (IPS_ADAPTER) + | 
|  | 7073 | sizeof (IPS_IO_CMD), &dma_address); | 
|  | 7074 | if (!ha->adapt) { | 
|  | 7075 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7076 | "Unable to allocate host adapt & dummy structures\n"); | 
|  | 7077 | return ips_abort_init(ha, index); | 
|  | 7078 | } | 
|  | 7079 | ha->adapt->hw_status_start = dma_address; | 
|  | 7080 | ha->dummy = (void *) (ha->adapt + 1); | 
|  | 7081 |  | 
|  | 7082 |  | 
|  | 7083 |  | 
|  | 7084 | ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), &dma_address); | 
|  | 7085 | if (!ha->logical_drive_info) { | 
|  | 7086 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7087 | "Unable to allocate logical drive info structure\n"); | 
|  | 7088 | return ips_abort_init(ha, index); | 
|  | 7089 | } | 
|  | 7090 | ha->logical_drive_info_dma_addr = dma_address; | 
|  | 7091 |  | 
|  | 7092 |  | 
|  | 7093 | ha->conf = kmalloc(sizeof (IPS_CONF), GFP_KERNEL); | 
|  | 7094 |  | 
|  | 7095 | if (!ha->conf) { | 
|  | 7096 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7097 | "Unable to allocate host conf structure\n"); | 
|  | 7098 | return ips_abort_init(ha, index); | 
|  | 7099 | } | 
|  | 7100 |  | 
|  | 7101 | ha->nvram = kmalloc(sizeof (IPS_NVRAM_P5), GFP_KERNEL); | 
|  | 7102 |  | 
|  | 7103 | if (!ha->nvram) { | 
|  | 7104 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7105 | "Unable to allocate host NVRAM structure\n"); | 
|  | 7106 | return ips_abort_init(ha, index); | 
|  | 7107 | } | 
|  | 7108 |  | 
|  | 7109 | ha->subsys = kmalloc(sizeof (IPS_SUBSYS), GFP_KERNEL); | 
|  | 7110 |  | 
|  | 7111 | if (!ha->subsys) { | 
|  | 7112 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7113 | "Unable to allocate host subsystem structure\n"); | 
|  | 7114 | return ips_abort_init(ha, index); | 
|  | 7115 | } | 
|  | 7116 |  | 
|  | 7117 | /* the ioctl buffer is now used during adapter initialization, so its | 
|  | 7118 | * successful allocation is now required */ | 
|  | 7119 | if (ips_ioctlsize < PAGE_SIZE) | 
|  | 7120 | ips_ioctlsize = PAGE_SIZE; | 
|  | 7121 |  | 
|  | 7122 | ha->ioctl_data = pci_alloc_consistent(pci_dev, ips_ioctlsize, | 
|  | 7123 | &ha->ioctl_busaddr); | 
|  | 7124 | ha->ioctl_len = ips_ioctlsize; | 
|  | 7125 | if (!ha->ioctl_data) { | 
|  | 7126 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7127 | "Unable to allocate IOCTL data\n"); | 
|  | 7128 | return ips_abort_init(ha, index); | 
|  | 7129 | } | 
|  | 7130 |  | 
|  | 7131 | /* | 
|  | 7132 | * Setup Functions | 
|  | 7133 | */ | 
|  | 7134 | ips_setup_funclist(ha); | 
|  | 7135 |  | 
|  | 7136 | if ((IPS_IS_MORPHEUS(ha)) || (IPS_IS_MARCO(ha))) { | 
|  | 7137 | /* If Morpheus appears dead, reset it */ | 
|  | 7138 | IsDead = readl(ha->mem_ptr + IPS_REG_I960_MSG1); | 
|  | 7139 | if (IsDead == 0xDEADBEEF) { | 
|  | 7140 | ips_reset_morpheus(ha); | 
|  | 7141 | } | 
|  | 7142 | } | 
|  | 7143 |  | 
|  | 7144 | /* | 
|  | 7145 | * Initialize the card if it isn't already | 
|  | 7146 | */ | 
|  | 7147 |  | 
|  | 7148 | if (!(*ha->func.isinit) (ha)) { | 
|  | 7149 | if (!(*ha->func.init) (ha)) { | 
|  | 7150 | /* | 
|  | 7151 | * Initialization failed | 
|  | 7152 | */ | 
|  | 7153 | IPS_PRINTK(KERN_WARNING, pci_dev, | 
|  | 7154 | "Unable to initialize controller\n"); | 
|  | 7155 | return ips_abort_init(ha, index); | 
|  | 7156 | } | 
|  | 7157 | } | 
|  | 7158 |  | 
|  | 7159 | *indexPtr = index; | 
|  | 7160 | return SUCCESS; | 
|  | 7161 | } | 
|  | 7162 |  | 
|  | 7163 | /*---------------------------------------------------------------------------*/ | 
|  | 7164 | /*   Routine Name: ips_init_phase2                                           */ | 
|  | 7165 | /*                                                                           */ | 
|  | 7166 | /*   Routine Description:                                                    */ | 
|  | 7167 | /*     Adapter Initialization Phase 2                                        */ | 
|  | 7168 | /*                                                                           */ | 
|  | 7169 | /*   Return Value:                                                           */ | 
|  | 7170 | /*     0 if Successful, else non-zero                                        */ | 
|  | 7171 | /*---------------------------------------------------------------------------*/ | 
|  | 7172 | static int | 
|  | 7173 | ips_init_phase2(int index) | 
|  | 7174 | { | 
|  | 7175 | ips_ha_t *ha; | 
|  | 7176 |  | 
|  | 7177 | ha = ips_ha[index]; | 
|  | 7178 |  | 
|  | 7179 | METHOD_TRACE("ips_init_phase2", 1); | 
|  | 7180 | if (!ha->active) { | 
|  | 7181 | ips_ha[index] = NULL; | 
|  | 7182 | return -1; | 
|  | 7183 | } | 
|  | 7184 |  | 
|  | 7185 | /* Install the interrupt handler */ | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 7186 | if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7187 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 7188 | "Unable to install interrupt handler\n"); | 
|  | 7189 | return ips_abort_init(ha, index); | 
|  | 7190 | } | 
|  | 7191 |  | 
|  | 7192 | /* | 
|  | 7193 | * Allocate a temporary SCB for initialization | 
|  | 7194 | */ | 
|  | 7195 | ha->max_cmds = 1; | 
|  | 7196 | if (!ips_allocatescbs(ha)) { | 
|  | 7197 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 7198 | "Unable to allocate a CCB\n"); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 7199 | free_irq(ha->pcidev->irq, ha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7200 | return ips_abort_init(ha, index); | 
|  | 7201 | } | 
|  | 7202 |  | 
|  | 7203 | if (!ips_hainit(ha)) { | 
|  | 7204 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 7205 | "Unable to initialize controller\n"); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 7206 | free_irq(ha->pcidev->irq, ha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7207 | return ips_abort_init(ha, index); | 
|  | 7208 | } | 
|  | 7209 | /* Free the temporary SCB */ | 
|  | 7210 | ips_deallocatescbs(ha, 1); | 
|  | 7211 |  | 
|  | 7212 | /* allocate CCBs */ | 
|  | 7213 | if (!ips_allocatescbs(ha)) { | 
|  | 7214 | IPS_PRINTK(KERN_WARNING, ha->pcidev, | 
|  | 7215 | "Unable to allocate CCBs\n"); | 
| Jeff Garzik | 8a694cc | 2007-12-13 16:14:07 -0800 | [diff] [blame] | 7216 | free_irq(ha->pcidev->irq, ha); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7217 | return ips_abort_init(ha, index); | 
|  | 7218 | } | 
|  | 7219 |  | 
|  | 7220 | return SUCCESS; | 
|  | 7221 | } | 
|  | 7222 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7223 | MODULE_LICENSE("GPL"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7224 | MODULE_DESCRIPTION("IBM ServeRAID Adapter Driver " IPS_VER_STRING); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7225 | MODULE_VERSION(IPS_VER_STRING); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7226 |  | 
|  | 7227 |  | 
|  | 7228 | /* | 
|  | 7229 | * Overrides for Emacs so that we almost follow Linus's tabbing style. | 
|  | 7230 | * Emacs will notice this stuff at the end of the file and automatically | 
|  | 7231 | * adjust the settings for this buffer only.  This must remain at the end | 
|  | 7232 | * of the file. | 
|  | 7233 | * --------------------------------------------------------------------------- | 
|  | 7234 | * Local variables: | 
|  | 7235 | * c-indent-level: 2 | 
|  | 7236 | * c-brace-imaginary-offset: 0 | 
|  | 7237 | * c-brace-offset: -2 | 
|  | 7238 | * c-argdecl-indent: 2 | 
|  | 7239 | * c-label-offset: -2 | 
|  | 7240 | * c-continued-statement-offset: 2 | 
|  | 7241 | * c-continued-brace-offset: 0 | 
|  | 7242 | * indent-tabs-mode: nil | 
|  | 7243 | * tab-width: 8 | 
|  | 7244 | * End: | 
|  | 7245 | */ |