xref: /openbmc/linux/drivers/scsi/ips.c (revision db5ed4dfd5dd0142ec36ff7b335e0ec3b836b3e6)
11da177e4SLinus Torvalds /*****************************************************************************/
21da177e4SLinus Torvalds /* ips.c -- driver for the Adaptec / IBM ServeRAID controller                */
31da177e4SLinus Torvalds /*                                                                           */
41da177e4SLinus Torvalds /* Written By: Keith Mitchell, IBM Corporation                               */
51da177e4SLinus Torvalds /*             Jack Hammer, Adaptec, Inc.                                    */
61da177e4SLinus Torvalds /*             David Jeffery, Adaptec, Inc.                                  */
71da177e4SLinus Torvalds /*                                                                           */
81da177e4SLinus Torvalds /* Copyright (C) 2000 IBM Corporation                                        */
91da177e4SLinus Torvalds /* Copyright (C) 2002,2003 Adaptec, Inc.                                     */
101da177e4SLinus Torvalds /*                                                                           */
111da177e4SLinus Torvalds /* This program is free software; you can redistribute it and/or modify      */
121da177e4SLinus Torvalds /* it under the terms of the GNU General Public License as published by      */
131da177e4SLinus Torvalds /* the Free Software Foundation; either version 2 of the License, or         */
141da177e4SLinus Torvalds /* (at your option) any later version.                                       */
151da177e4SLinus Torvalds /*                                                                           */
161da177e4SLinus Torvalds /* This program is distributed in the hope that it will be useful,           */
171da177e4SLinus Torvalds /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
181da177e4SLinus Torvalds /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
191da177e4SLinus Torvalds /* GNU General Public License for more details.                              */
201da177e4SLinus Torvalds /*                                                                           */
211da177e4SLinus Torvalds /* NO WARRANTY                                                               */
221da177e4SLinus Torvalds /* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        */
231da177e4SLinus Torvalds /* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      */
241da177e4SLinus Torvalds /* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      */
251da177e4SLinus Torvalds /* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    */
261da177e4SLinus Torvalds /* solely responsible for determining the appropriateness of using and       */
271da177e4SLinus Torvalds /* distributing the Program and assumes all risks associated with its        */
281da177e4SLinus Torvalds /* exercise of rights under this Agreement, including but not limited to     */
291da177e4SLinus Torvalds /* the risks and costs of program errors, damage to or loss of data,         */
301da177e4SLinus Torvalds /* programs or equipment, and unavailability or interruption of operations.  */
311da177e4SLinus Torvalds /*                                                                           */
321da177e4SLinus Torvalds /* DISCLAIMER OF LIABILITY                                                   */
331da177e4SLinus Torvalds /* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   */
341da177e4SLinus Torvalds /* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */
351da177e4SLinus Torvalds /* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   */
361da177e4SLinus Torvalds /* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     */
371da177e4SLinus Torvalds /* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    */
381da177e4SLinus Torvalds /* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  */
391da177e4SLinus Torvalds /* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             */
401da177e4SLinus Torvalds /*                                                                           */
411da177e4SLinus Torvalds /* You should have received a copy of the GNU General Public License         */
421da177e4SLinus Torvalds /* along with this program; if not, write to the Free Software               */
431da177e4SLinus Torvalds /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
441da177e4SLinus Torvalds /*                                                                           */
451da177e4SLinus Torvalds /* Bugs/Comments/Suggestions about this driver should be mailed to:          */
461da177e4SLinus Torvalds /*      ipslinux@adaptec.com        	                                     */
471da177e4SLinus Torvalds /*                                                                           */
481da177e4SLinus Torvalds /* For system support issues, contact your local IBM Customer support.       */
491da177e4SLinus Torvalds /* Directions to find IBM Customer Support for each country can be found at: */
501da177e4SLinus Torvalds /*      http://www.ibm.com/planetwide/                                       */
511da177e4SLinus Torvalds /*                                                                           */
521da177e4SLinus Torvalds /*****************************************************************************/
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds /*****************************************************************************/
551da177e4SLinus Torvalds /* Change Log                                                                */
561da177e4SLinus Torvalds /*                                                                           */
571da177e4SLinus Torvalds /* 0.99.02  - Breakup commands that are bigger than 8 * the stripe size      */
581da177e4SLinus Torvalds /* 0.99.03  - Make interrupt routine handle all completed request on the     */
591da177e4SLinus Torvalds /*            adapter not just the first one                                 */
601da177e4SLinus Torvalds /*          - Make sure passthru commands get woken up if we run out of      */
611da177e4SLinus Torvalds /*            SCBs                                                           */
621da177e4SLinus Torvalds /*          - Send all of the commands on the queue at once rather than      */
631da177e4SLinus Torvalds /*            one at a time since the card will support it.                  */
641da177e4SLinus Torvalds /* 0.99.04  - Fix race condition in the passthru mechanism -- this required  */
651da177e4SLinus Torvalds /*            the interface to the utilities to change                       */
661da177e4SLinus Torvalds /*          - Fix error recovery code                                        */
671da177e4SLinus Torvalds /* 0.99.05  - Fix an oops when we get certain passthru commands              */
681da177e4SLinus Torvalds /* 1.00.00  - Initial Public Release                                         */
691da177e4SLinus Torvalds /*            Functionally equivalent to 0.99.05                             */
701da177e4SLinus Torvalds /* 3.60.00  - Bump max commands to 128 for use with firmware 3.60            */
711da177e4SLinus Torvalds /*          - Change version to 3.60 to coincide with release numbering.     */
721da177e4SLinus Torvalds /* 3.60.01  - Remove bogus error check in passthru routine                   */
731da177e4SLinus Torvalds /* 3.60.02  - Make DCDB direction based on lookup table                      */
741da177e4SLinus Torvalds /*          - Only allow one DCDB command to a SCSI ID at a time             */
751da177e4SLinus Torvalds /* 4.00.00  - Add support for ServeRAID 4                                    */
761da177e4SLinus Torvalds /* 4.00.01  - Add support for First Failure Data Capture                     */
771da177e4SLinus Torvalds /* 4.00.02  - Fix problem with PT DCDB with no buffer                        */
781da177e4SLinus Torvalds /* 4.00.03  - Add alternative passthru interface                             */
791da177e4SLinus Torvalds /*          - Add ability to flash BIOS                                      */
801da177e4SLinus Torvalds /* 4.00.04  - Rename structures/constants to be prefixed with IPS_           */
811da177e4SLinus Torvalds /* 4.00.05  - Remove wish_block from init routine                            */
821da177e4SLinus Torvalds /*          - Use linux/spinlock.h instead of asm/spinlock.h for kernels     */
831da177e4SLinus Torvalds /*            2.3.18 and later                                               */
841da177e4SLinus Torvalds /*          - Sync with other changes from the 2.3 kernels                   */
851da177e4SLinus Torvalds /* 4.00.06  - Fix timeout with initial FFDC command                          */
861da177e4SLinus Torvalds /* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@infradead.org> */
871da177e4SLinus Torvalds /* 4.10.00  - Add support for ServeRAID 4M/4L                                */
881da177e4SLinus Torvalds /* 4.10.13  - Fix for dynamic unload and proc file system                    */
891da177e4SLinus Torvalds /* 4.20.03  - Rename version to coincide with new release schedules          */
901da177e4SLinus Torvalds /*            Performance fixes                                              */
911da177e4SLinus Torvalds /*            Fix truncation of /proc files with cat                         */
921da177e4SLinus Torvalds /*            Merge in changes through kernel 2.4.0test1ac21                 */
931da177e4SLinus Torvalds /* 4.20.13  - Fix some failure cases / reset code                            */
941da177e4SLinus Torvalds /*          - Hook into the reboot_notifier to flush the controller cache    */
951da177e4SLinus Torvalds /* 4.50.01  - Fix problem when there is a hole in logical drive numbering    */
961da177e4SLinus Torvalds /* 4.70.09  - Use a Common ( Large Buffer ) for Flashing from the JCRM CD    */
971da177e4SLinus Torvalds /*          - Add IPSSEND Flash Support                                      */
981da177e4SLinus Torvalds /*          - Set Sense Data for Unknown SCSI Command                        */
991da177e4SLinus Torvalds /*          - Use Slot Number from NVRAM Page 5                              */
1001da177e4SLinus Torvalds /*          - Restore caller's DCDB Structure                                */
1011da177e4SLinus Torvalds /* 4.70.12  - Corrective actions for bad controller ( during initialization )*/
1021da177e4SLinus Torvalds /* 4.70.13  - Don't Send CDB's if we already know the device is not present  */
1031da177e4SLinus Torvalds /*          - Don't release HA Lock in ips_next() until SC taken off queue   */
1041da177e4SLinus Torvalds /*          - Unregister SCSI device in ips_release()                        */
1051da177e4SLinus Torvalds /* 4.70.15  - Fix Breakup for very large ( non-SG ) requests in ips_done()   */
1061da177e4SLinus Torvalds /* 4.71.00  - Change all memory allocations to not use GFP_DMA flag          */
1071da177e4SLinus Torvalds /*            Code Clean-Up for 2.4.x kernel                                 */
1081da177e4SLinus Torvalds /* 4.72.00  - Allow for a Scatter-Gather Element to exceed MAX_XFER Size     */
1091da177e4SLinus Torvalds /* 4.72.01  - I/O Mapped Memory release ( so "insmod ips" does not Fail )    */
1101da177e4SLinus Torvalds /*          - Don't Issue Internal FFDC Command if there are Active Commands */
1111da177e4SLinus Torvalds /*          - Close Window for getting too many IOCTL's active               */
1121da177e4SLinus Torvalds /* 4.80.00  - Make ia64 Safe                                                 */
1131da177e4SLinus Torvalds /* 4.80.04  - Eliminate calls to strtok() if 2.4.x or greater                */
1141da177e4SLinus Torvalds /*          - Adjustments to Device Queue Depth                              */
1151da177e4SLinus Torvalds /* 4.80.14  - Take all semaphores off stack                                  */
1161da177e4SLinus Torvalds /*          - Clean Up New_IOCTL path                                        */
1171da177e4SLinus Torvalds /* 4.80.20  - Set max_sectors in Scsi_Host structure ( if >= 2.4.7 kernel )  */
1181da177e4SLinus Torvalds /*          - 5 second delay needed after resetting an i960 adapter          */
1191da177e4SLinus Torvalds /* 4.80.26  - Clean up potential code problems ( Arjan's recommendations )   */
1201da177e4SLinus Torvalds /* 4.90.01  - Version Matching for FirmWare, BIOS, and Driver                */
1211da177e4SLinus Torvalds /* 4.90.05  - Use New PCI Architecture to facilitate Hot Plug Development    */
1221da177e4SLinus Torvalds /* 4.90.08  - Increase Delays in Flashing ( Trombone Only - 4H )             */
1231da177e4SLinus Torvalds /* 4.90.08  - Data Corruption if First Scatter Gather Element is > 64K       */
1241da177e4SLinus Torvalds /* 4.90.11  - Don't actually RESET unless it's physically required           */
1251da177e4SLinus Torvalds /*          - Remove unused compile options                                  */
1261da177e4SLinus Torvalds /* 5.00.01  - Sarasota ( 5i ) adapters must always be scanned first          */
1271da177e4SLinus Torvalds /*          - Get rid on IOCTL_NEW_COMMAND code                              */
1281da177e4SLinus Torvalds /*          - Add Extended DCDB Commands for Tape Support in 5I              */
1291da177e4SLinus Torvalds /* 5.10.12  - use pci_dma interfaces, update for 2.5 kernel changes          */
1301da177e4SLinus Torvalds /* 5.10.15  - remove unused code (sem, macros, etc.)                         */
1311da177e4SLinus Torvalds /* 5.30.00  - use __devexit_p()                                              */
1321da177e4SLinus Torvalds /* 6.00.00  - Add 6x Adapters and Battery Flash                              */
1331da177e4SLinus Torvalds /* 6.10.00  - Remove 1G Addressing Limitations                               */
1341da177e4SLinus Torvalds /* 6.11.xx  - Get VersionInfo buffer off the stack !              DDTS 60401 */
1351da177e4SLinus Torvalds /* 6.11.xx  - Make Logical Drive Info structure safe for DMA      DDTS 60639 */
136c1a15468SJack Hammer /* 7.10.18  - Add highmem_io flag in SCSI Templete for 2.4 kernels           */
1371da177e4SLinus Torvalds /*          - Fix path/name for scsi_hosts.h include for 2.6 kernels         */
1381da177e4SLinus Torvalds /*          - Fix sort order of 7k                                           */
1391da177e4SLinus Torvalds /*          - Remove 3 unused "inline" functions                             */
14025985edcSLucas De Marchi /* 7.12.xx  - Use STATIC functions wherever possible                        */
141c1a15468SJack Hammer /*          - Clean up deprecated MODULE_PARM calls                          */
142a60768e2SJack Hammer /* 7.12.05  - Remove Version Matching per IBM request                        */
1431da177e4SLinus Torvalds /*****************************************************************************/
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds /*
1461da177e4SLinus Torvalds  * Conditional Compilation directives for this driver:
1471da177e4SLinus Torvalds  *
1481da177e4SLinus Torvalds  * IPS_DEBUG            - Turn on debugging info
1491da177e4SLinus Torvalds  *
1501da177e4SLinus Torvalds  * Parameters:
1511da177e4SLinus Torvalds  *
1521da177e4SLinus Torvalds  * debug:<number>       - Set debug level to <number>
1531da177e4SLinus Torvalds  *                        NOTE: only works when IPS_DEBUG compile directive is used.
1541da177e4SLinus Torvalds  *       1              - Normal debug messages
1551da177e4SLinus Torvalds  *       2              - Verbose debug messages
1561da177e4SLinus Torvalds  *       11             - Method trace (non interrupt)
1571da177e4SLinus Torvalds  *       12             - Method trace (includes interrupt)
1581da177e4SLinus Torvalds  *
1591da177e4SLinus Torvalds  * noi2o                - Don't use I2O Queues (ServeRAID 4 only)
1601da177e4SLinus Torvalds  * nommap               - Don't use memory mapped I/O
1611da177e4SLinus Torvalds  * ioctlsize            - Initial size of the IOCTL buffer
1621da177e4SLinus Torvalds  */
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds #include <asm/io.h>
1651da177e4SLinus Torvalds #include <asm/byteorder.h>
1661da177e4SLinus Torvalds #include <asm/page.h>
1671da177e4SLinus Torvalds #include <linux/stddef.h>
1681da177e4SLinus Torvalds #include <linux/string.h>
1691da177e4SLinus Torvalds #include <linux/errno.h>
1701da177e4SLinus Torvalds #include <linux/kernel.h>
1711da177e4SLinus Torvalds #include <linux/ioport.h>
1721da177e4SLinus Torvalds #include <linux/slab.h>
1731da177e4SLinus Torvalds #include <linux/delay.h>
1741da177e4SLinus Torvalds #include <linux/pci.h>
1751da177e4SLinus Torvalds #include <linux/proc_fs.h>
1761da177e4SLinus Torvalds #include <linux/reboot.h>
1771da177e4SLinus Torvalds #include <linux/interrupt.h>
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds #include <linux/blkdev.h>
1801da177e4SLinus Torvalds #include <linux/types.h>
181910638aeSMatthias Gehre #include <linux/dma-mapping.h>
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds #include <scsi/sg.h>
1841da177e4SLinus Torvalds #include "scsi.h"
1851da177e4SLinus Torvalds #include <scsi/scsi_host.h>
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds #include "ips.h"
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds #include <linux/module.h>
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds #include <linux/stat.h>
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds #include <linux/spinlock.h>
1941da177e4SLinus Torvalds #include <linux/init.h>
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds #include <linux/smp.h>
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds #ifdef MODULE
1991da177e4SLinus Torvalds static char *ips = NULL;
2001da177e4SLinus Torvalds module_param(ips, charp, 0);
2011da177e4SLinus Torvalds #endif
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds /*
2041da177e4SLinus Torvalds  * DRIVER_VER
2051da177e4SLinus Torvalds  */
2068c8fdc59SBernhard Walle #define IPS_VERSION_HIGH        IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING
2078c8fdc59SBernhard Walle #define IPS_VERSION_LOW         "." IPS_VER_BUILD_STRING " "
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds #if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
2101da177e4SLinus Torvalds #warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
2111da177e4SLinus Torvalds #endif
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds #define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
214be7db055S                          DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
2151da177e4SLinus Torvalds                          PCI_DMA_BIDIRECTIONAL : \
216be7db055S                          scb->scsi_cmd->sc_data_direction)
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds #ifdef IPS_DEBUG
2191da177e4SLinus Torvalds #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
2201da177e4SLinus Torvalds #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n");
2211da177e4SLinus Torvalds #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
2221da177e4SLinus Torvalds #else
2231da177e4SLinus Torvalds #define METHOD_TRACE(s, i)
2241da177e4SLinus Torvalds #define DEBUG(i, s)
2251da177e4SLinus Torvalds #define DEBUG_VAR(i, s, v...)
2261da177e4SLinus Torvalds #endif
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds /*
2291da177e4SLinus Torvalds  * Function prototypes
2301da177e4SLinus Torvalds  */
231d0be4a7dSChristoph Hellwig static int ips_detect(struct scsi_host_template *);
2321da177e4SLinus Torvalds static int ips_release(struct Scsi_Host *);
2331516b55dSHenne static int ips_eh_abort(struct scsi_cmnd *);
2341516b55dSHenne static int ips_eh_reset(struct scsi_cmnd *);
235f281233dSJeff Garzik static int ips_queue(struct Scsi_Host *, struct scsi_cmnd *);
2361da177e4SLinus Torvalds static const char *ips_info(struct Scsi_Host *);
2377d12e780SDavid Howells static irqreturn_t do_ipsintr(int, void *);
2381da177e4SLinus Torvalds static int ips_hainit(ips_ha_t *);
2391da177e4SLinus Torvalds static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
2401da177e4SLinus Torvalds static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
2411da177e4SLinus Torvalds static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
2421da177e4SLinus Torvalds static int ips_online(ips_ha_t *, ips_scb_t *);
2431da177e4SLinus Torvalds static int ips_inquiry(ips_ha_t *, ips_scb_t *);
2441da177e4SLinus Torvalds static int ips_rdcap(ips_ha_t *, ips_scb_t *);
2451da177e4SLinus Torvalds static int ips_msense(ips_ha_t *, ips_scb_t *);
2461da177e4SLinus Torvalds static int ips_reqsen(ips_ha_t *, ips_scb_t *);
2471da177e4SLinus Torvalds static int ips_deallocatescbs(ips_ha_t *, int);
2481da177e4SLinus Torvalds static int ips_allocatescbs(ips_ha_t *);
2491da177e4SLinus Torvalds static int ips_reset_copperhead(ips_ha_t *);
2501da177e4SLinus Torvalds static int ips_reset_copperhead_memio(ips_ha_t *);
2511da177e4SLinus Torvalds static int ips_reset_morpheus(ips_ha_t *);
2521da177e4SLinus Torvalds static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
2531da177e4SLinus Torvalds static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
2541da177e4SLinus Torvalds static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
2551da177e4SLinus Torvalds static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
2561da177e4SLinus Torvalds static int ips_isintr_copperhead(ips_ha_t *);
2571da177e4SLinus Torvalds static int ips_isintr_copperhead_memio(ips_ha_t *);
2581da177e4SLinus Torvalds static int ips_isintr_morpheus(ips_ha_t *);
2591da177e4SLinus Torvalds static int ips_wait(ips_ha_t *, int, int);
2601da177e4SLinus Torvalds static int ips_write_driver_status(ips_ha_t *, int);
2611da177e4SLinus Torvalds static int ips_read_adapter_status(ips_ha_t *, int);
2621da177e4SLinus Torvalds static int ips_read_subsystem_parameters(ips_ha_t *, int);
2631da177e4SLinus Torvalds static int ips_read_config(ips_ha_t *, int);
2641da177e4SLinus Torvalds static int ips_clear_adapter(ips_ha_t *, int);
2651da177e4SLinus Torvalds static int ips_readwrite_page5(ips_ha_t *, int, int);
2661da177e4SLinus Torvalds static int ips_init_copperhead(ips_ha_t *);
2671da177e4SLinus Torvalds static int ips_init_copperhead_memio(ips_ha_t *);
2681da177e4SLinus Torvalds static int ips_init_morpheus(ips_ha_t *);
2691da177e4SLinus Torvalds static int ips_isinit_copperhead(ips_ha_t *);
2701da177e4SLinus Torvalds static int ips_isinit_copperhead_memio(ips_ha_t *);
2711da177e4SLinus Torvalds static int ips_isinit_morpheus(ips_ha_t *);
2721da177e4SLinus Torvalds static int ips_erase_bios(ips_ha_t *);
2731da177e4SLinus Torvalds static int ips_program_bios(ips_ha_t *, char *, uint32_t, uint32_t);
2741da177e4SLinus Torvalds static int ips_verify_bios(ips_ha_t *, char *, uint32_t, uint32_t);
2751da177e4SLinus Torvalds static int ips_erase_bios_memio(ips_ha_t *);
2761da177e4SLinus Torvalds static int ips_program_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
2771da177e4SLinus Torvalds static int ips_verify_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
2781da177e4SLinus Torvalds static int ips_flash_copperhead(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2791da177e4SLinus Torvalds static int ips_flash_bios(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2801da177e4SLinus Torvalds static int ips_flash_firmware(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2811da177e4SLinus Torvalds static void ips_free_flash_copperhead(ips_ha_t * ha);
2821da177e4SLinus Torvalds static void ips_get_bios_version(ips_ha_t *, int);
2831da177e4SLinus Torvalds static void ips_identify_controller(ips_ha_t *);
2841da177e4SLinus Torvalds static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
2851da177e4SLinus Torvalds static void ips_enable_int_copperhead(ips_ha_t *);
2861da177e4SLinus Torvalds static void ips_enable_int_copperhead_memio(ips_ha_t *);
2871da177e4SLinus Torvalds static void ips_enable_int_morpheus(ips_ha_t *);
2881da177e4SLinus Torvalds static int ips_intr_copperhead(ips_ha_t *);
2891da177e4SLinus Torvalds static int ips_intr_morpheus(ips_ha_t *);
2901da177e4SLinus Torvalds static void ips_next(ips_ha_t *, int);
2911da177e4SLinus Torvalds static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
2921da177e4SLinus Torvalds static void ipsintr_done(ips_ha_t *, struct ips_scb *);
2931da177e4SLinus Torvalds static void ips_done(ips_ha_t *, ips_scb_t *);
2941da177e4SLinus Torvalds static void ips_free(ips_ha_t *);
2951da177e4SLinus Torvalds static void ips_init_scb(ips_ha_t *, ips_scb_t *);
2961da177e4SLinus Torvalds static void ips_freescb(ips_ha_t *, ips_scb_t *);
2971da177e4SLinus Torvalds static void ips_setup_funclist(ips_ha_t *);
2981da177e4SLinus Torvalds static void ips_statinit(ips_ha_t *);
2991da177e4SLinus Torvalds static void ips_statinit_memio(ips_ha_t *);
3001da177e4SLinus Torvalds static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
3011da177e4SLinus Torvalds static void ips_ffdc_reset(ips_ha_t *, int);
3021da177e4SLinus Torvalds static void ips_ffdc_time(ips_ha_t *);
3031da177e4SLinus Torvalds static uint32_t ips_statupd_copperhead(ips_ha_t *);
3041da177e4SLinus Torvalds static uint32_t ips_statupd_copperhead_memio(ips_ha_t *);
3051da177e4SLinus Torvalds static uint32_t ips_statupd_morpheus(ips_ha_t *);
3061da177e4SLinus Torvalds static ips_scb_t *ips_getscb(ips_ha_t *);
3071da177e4SLinus Torvalds static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
3081516b55dSHenne static void ips_putq_wait_tail(ips_wait_queue_t *, struct scsi_cmnd *);
3091da177e4SLinus Torvalds static void ips_putq_copp_tail(ips_copp_queue_t *,
3101da177e4SLinus Torvalds 				      ips_copp_wait_item_t *);
3111da177e4SLinus Torvalds static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *);
3121da177e4SLinus Torvalds static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
3131516b55dSHenne static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *);
3141516b55dSHenne static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *,
3151516b55dSHenne 					  struct scsi_cmnd *);
3161da177e4SLinus Torvalds static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *,
3171da177e4SLinus Torvalds 						     ips_copp_wait_item_t *);
3181da177e4SLinus Torvalds static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *);
3191da177e4SLinus Torvalds 
3201516b55dSHenne static int ips_is_passthru(struct scsi_cmnd *);
3211516b55dSHenne static int ips_make_passthru(ips_ha_t *, struct scsi_cmnd *, ips_scb_t *, int);
3221da177e4SLinus Torvalds static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
3231da177e4SLinus Torvalds static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *);
3241516b55dSHenne static void ips_scmd_buf_write(struct scsi_cmnd * scmd, void *data,
3251da177e4SLinus Torvalds 			       unsigned int count);
3261516b55dSHenne static void ips_scmd_buf_read(struct scsi_cmnd * scmd, void *data,
3271516b55dSHenne 			      unsigned int count);
3281da177e4SLinus Torvalds 
329aacce706SAl Viro static int ips_write_info(struct Scsi_Host *, char *, int);
330aacce706SAl Viro static int ips_show_info(struct seq_file *, struct Scsi_Host *);
331aacce706SAl Viro static int ips_host_info(ips_ha_t *, struct seq_file *);
3321da177e4SLinus Torvalds static int ips_abort_init(ips_ha_t * ha, int index);
3331da177e4SLinus Torvalds static int ips_init_phase2(int index);
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
3361da177e4SLinus Torvalds static int ips_register_scsi(int index);
3371da177e4SLinus Torvalds 
338ee807c2dSJack Hammer static int  ips_poll_for_flush_complete(ips_ha_t * ha);
339ee807c2dSJack Hammer static void ips_flush_and_reset(ips_ha_t *ha);
340ee807c2dSJack Hammer 
3411da177e4SLinus Torvalds /*
3421da177e4SLinus Torvalds  * global variables
3431da177e4SLinus Torvalds  */
3441da177e4SLinus Torvalds static const char ips_name[] = "ips";
3451da177e4SLinus Torvalds static struct Scsi_Host *ips_sh[IPS_MAX_ADAPTERS];	/* Array of host controller structures */
3461da177e4SLinus Torvalds static ips_ha_t *ips_ha[IPS_MAX_ADAPTERS];	/* Array of HA structures */
3471da177e4SLinus Torvalds static unsigned int ips_next_controller;
3481da177e4SLinus Torvalds static unsigned int ips_num_controllers;
3491da177e4SLinus Torvalds static unsigned int ips_released_controllers;
3501da177e4SLinus Torvalds static int ips_hotplug;
3511da177e4SLinus Torvalds static int ips_cmd_timeout = 60;
3521da177e4SLinus Torvalds static int ips_reset_timeout = 60 * 5;
3531da177e4SLinus Torvalds static int ips_force_memio = 1;		/* Always use Memory Mapped I/O    */
3541da177e4SLinus Torvalds static int ips_force_i2o = 1;	/* Always use I2O command delivery */
3551da177e4SLinus Torvalds static int ips_ioctlsize = IPS_IOCTL_SIZE;	/* Size of the ioctl buffer        */
3561da177e4SLinus Torvalds static int ips_cd_boot;			/* Booting from Manager CD         */
3571da177e4SLinus Torvalds static char *ips_FlashData = NULL;	/* CD Boot - Flash Data Buffer      */
3581da177e4SLinus Torvalds static dma_addr_t ips_flashbusaddr;
3591da177e4SLinus Torvalds static long ips_FlashDataInUse;		/* CD Boot - Flash Data In Use Flag */
3601da177e4SLinus Torvalds static uint32_t MaxLiteCmds = 32;	/* Max Active Cmds for a Lite Adapter */
361d0be4a7dSChristoph Hellwig static struct scsi_host_template ips_driver_template = {
3621da177e4SLinus Torvalds 	.detect			= ips_detect,
3631da177e4SLinus Torvalds 	.release		= ips_release,
3641da177e4SLinus Torvalds 	.info			= ips_info,
3651da177e4SLinus Torvalds 	.queuecommand		= ips_queue,
3661da177e4SLinus Torvalds 	.eh_abort_handler	= ips_eh_abort,
3671da177e4SLinus Torvalds 	.eh_host_reset_handler	= ips_eh_reset,
3681da177e4SLinus Torvalds 	.proc_name		= "ips",
369aacce706SAl Viro 	.show_info		= ips_show_info,
370aacce706SAl Viro 	.write_info		= ips_write_info,
3711da177e4SLinus Torvalds 	.slave_configure	= ips_slave_configure,
3721da177e4SLinus Torvalds 	.bios_param		= ips_biosparam,
3731da177e4SLinus Torvalds 	.this_id		= -1,
3741da177e4SLinus Torvalds 	.sg_tablesize		= IPS_MAX_SG,
3751da177e4SLinus Torvalds 	.cmd_per_lun		= 3,
3761da177e4SLinus Torvalds 	.use_clustering		= ENABLE_CLUSTERING,
37754b2b50cSMartin K. Petersen 	.no_write_same		= 1,
3781da177e4SLinus Torvalds };
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds /* This table describes all ServeRAID Adapters */
3821da177e4SLinus Torvalds static struct  pci_device_id  ips_pci_table[] = {
3831da177e4SLinus Torvalds 	{ 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3841da177e4SLinus Torvalds 	{ 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3851da177e4SLinus Torvalds 	{ 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3861da177e4SLinus Torvalds 	{ 0, }
3871da177e4SLinus Torvalds };
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds MODULE_DEVICE_TABLE( pci, ips_pci_table );
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds static char ips_hot_plug_name[] = "ips";
3921da177e4SLinus Torvalds 
3936f039790SGreg Kroah-Hartman static int  ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
3946f039790SGreg Kroah-Hartman static void ips_remove_device(struct pci_dev *pci_dev);
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds static struct pci_driver ips_pci_driver = {
3971da177e4SLinus Torvalds 	.name		= ips_hot_plug_name,
3981da177e4SLinus Torvalds 	.id_table	= ips_pci_table,
3991da177e4SLinus Torvalds 	.probe		= ips_insert_device,
4006f039790SGreg Kroah-Hartman 	.remove		= ips_remove_device,
4011da177e4SLinus Torvalds };
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds /*
4051da177e4SLinus Torvalds  * Necessary forward function protoypes
4061da177e4SLinus Torvalds  */
4071da177e4SLinus Torvalds static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds #define MAX_ADAPTER_NAME 15
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds static char ips_adapter_name[][30] = {
4121da177e4SLinus Torvalds 	"ServeRAID",
4131da177e4SLinus Torvalds 	"ServeRAID II",
4141da177e4SLinus Torvalds 	"ServeRAID on motherboard",
4151da177e4SLinus Torvalds 	"ServeRAID on motherboard",
4161da177e4SLinus Torvalds 	"ServeRAID 3H",
4171da177e4SLinus Torvalds 	"ServeRAID 3L",
4181da177e4SLinus Torvalds 	"ServeRAID 4H",
4191da177e4SLinus Torvalds 	"ServeRAID 4M",
4201da177e4SLinus Torvalds 	"ServeRAID 4L",
4211da177e4SLinus Torvalds 	"ServeRAID 4Mx",
4221da177e4SLinus Torvalds 	"ServeRAID 4Lx",
4231da177e4SLinus Torvalds 	"ServeRAID 5i",
4241da177e4SLinus Torvalds 	"ServeRAID 5i",
4251da177e4SLinus Torvalds 	"ServeRAID 6M",
4261da177e4SLinus Torvalds 	"ServeRAID 6i",
4271da177e4SLinus Torvalds 	"ServeRAID 7t",
4281da177e4SLinus Torvalds 	"ServeRAID 7k",
4291da177e4SLinus Torvalds 	"ServeRAID 7M"
4301da177e4SLinus Torvalds };
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds static struct notifier_block ips_notifier = {
4331da177e4SLinus Torvalds 	ips_halt, NULL, 0
4341da177e4SLinus Torvalds };
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds /*
4371da177e4SLinus Torvalds  * Direction table
4381da177e4SLinus Torvalds  */
4391da177e4SLinus Torvalds static char ips_command_direction[] = {
4401da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT,
4411da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK,
4421da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4431da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
4441da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT,
4451da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
4461da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN,
4471da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
4481da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK,
4491da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
4501da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE,
4511da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
4521da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT,
4531da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE,
4541da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK,
4551da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
4561da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4571da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4581da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4591da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4601da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4611da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4621da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4631da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4641da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4651da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4661da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4671da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4681da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4691da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4701da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4711da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4721da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4731da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE,
4741da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT,
4751da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE,
4761da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN,
4771da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4781da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4791da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4801da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4811da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4821da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4831da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4841da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4851da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4861da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT,
4871da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4881da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4891da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4901da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK
4911da177e4SLinus Torvalds };
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds /****************************************************************************/
4951da177e4SLinus Torvalds /*                                                                          */
4961da177e4SLinus Torvalds /* Routine Name: ips_setup                                                  */
4971da177e4SLinus Torvalds /*                                                                          */
4981da177e4SLinus Torvalds /* Routine Description:                                                     */
4991da177e4SLinus Torvalds /*                                                                          */
5001da177e4SLinus Torvalds /*   setup parameters to the driver                                         */
5011da177e4SLinus Torvalds /*                                                                          */
5021da177e4SLinus Torvalds /****************************************************************************/
5031da177e4SLinus Torvalds static int
5041da177e4SLinus Torvalds ips_setup(char *ips_str)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	int i;
5081da177e4SLinus Torvalds 	char *key;
5091da177e4SLinus Torvalds 	char *value;
5101da177e4SLinus Torvalds 	IPS_OPTION options[] = {
5111da177e4SLinus Torvalds 		{"noi2o", &ips_force_i2o, 0},
5121da177e4SLinus Torvalds 		{"nommap", &ips_force_memio, 0},
5131da177e4SLinus Torvalds 		{"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
5141da177e4SLinus Torvalds 		{"cdboot", &ips_cd_boot, 0},
5151da177e4SLinus Torvalds 		{"maxcmds", &MaxLiteCmds, 32},
5161da177e4SLinus Torvalds 	};
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 	/* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */
5191da177e4SLinus Torvalds 	/* Search for value */
5201da177e4SLinus Torvalds 	while ((key = strsep(&ips_str, ",."))) {
5211da177e4SLinus Torvalds 		if (!*key)
5221da177e4SLinus Torvalds 			continue;
5231da177e4SLinus Torvalds 		value = strchr(key, ':');
5241da177e4SLinus Torvalds 		if (value)
5251da177e4SLinus Torvalds 			*value++ = '\0';
5261da177e4SLinus Torvalds 		/*
5271da177e4SLinus Torvalds 		 * We now have key/value pairs.
5281da177e4SLinus Torvalds 		 * Update the variables
5291da177e4SLinus Torvalds 		 */
5306391a113STobias Klauser 		for (i = 0; i < ARRAY_SIZE(options); i++) {
53148a96876SRasmus Villemoes 			if (strncasecmp
5321da177e4SLinus Torvalds 			    (key, options[i].option_name,
5331da177e4SLinus Torvalds 			     strlen(options[i].option_name)) == 0) {
5341da177e4SLinus Torvalds 				if (value)
5351da177e4SLinus Torvalds 					*options[i].option_flag =
5361da177e4SLinus Torvalds 					    simple_strtoul(value, NULL, 0);
5371da177e4SLinus Torvalds 				else
5381da177e4SLinus Torvalds 					*options[i].option_flag =
5391da177e4SLinus Torvalds 					    options[i].option_value;
5401da177e4SLinus Torvalds 				break;
5411da177e4SLinus Torvalds 			}
5421da177e4SLinus Torvalds 		}
5431da177e4SLinus Torvalds 	}
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	return (1);
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds __setup("ips=", ips_setup);
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds /****************************************************************************/
5511da177e4SLinus Torvalds /*                                                                          */
5521da177e4SLinus Torvalds /* Routine Name: ips_detect                                                 */
5531da177e4SLinus Torvalds /*                                                                          */
5541da177e4SLinus Torvalds /* Routine Description:                                                     */
5551da177e4SLinus Torvalds /*                                                                          */
5561da177e4SLinus Torvalds /*   Detect and initialize the driver                                       */
5571da177e4SLinus Torvalds /*                                                                          */
5581da177e4SLinus Torvalds /* NOTE: this routine is called under the io_request_lock spinlock          */
5591da177e4SLinus Torvalds /*                                                                          */
5601da177e4SLinus Torvalds /****************************************************************************/
5611da177e4SLinus Torvalds static int
562d0be4a7dSChristoph Hellwig ips_detect(struct scsi_host_template * SHT)
5631da177e4SLinus Torvalds {
5641da177e4SLinus Torvalds 	int i;
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds 	METHOD_TRACE("ips_detect", 1);
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds #ifdef MODULE
5691da177e4SLinus Torvalds 	if (ips)
5701da177e4SLinus Torvalds 		ips_setup(ips);
5711da177e4SLinus Torvalds #endif
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	for (i = 0; i < ips_num_controllers; i++) {
5741da177e4SLinus Torvalds 		if (ips_register_scsi(i))
5751da177e4SLinus Torvalds 			ips_free(ips_ha[i]);
5761da177e4SLinus Torvalds 		ips_released_controllers++;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 	ips_hotplug = 1;
5791da177e4SLinus Torvalds 	return (ips_num_controllers);
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds /****************************************************************************/
5831da177e4SLinus Torvalds /*   configure the function pointers to use the functions that will work    */
5841da177e4SLinus Torvalds /*   with the found version of the adapter                                  */
5851da177e4SLinus Torvalds /****************************************************************************/
5861da177e4SLinus Torvalds static void
5871da177e4SLinus Torvalds ips_setup_funclist(ips_ha_t * ha)
5881da177e4SLinus Torvalds {
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	/*
5911da177e4SLinus Torvalds 	 * Setup Functions
5921da177e4SLinus Torvalds 	 */
5931da177e4SLinus Torvalds 	if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) {
5941da177e4SLinus Torvalds 		/* morpheus / marco / sebring */
5951da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_morpheus;
5961da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_morpheus;
5971da177e4SLinus Torvalds 		ha->func.issue = ips_issue_i2o_memio;
5981da177e4SLinus Torvalds 		ha->func.init = ips_init_morpheus;
5991da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_morpheus;
6001da177e4SLinus Torvalds 		ha->func.reset = ips_reset_morpheus;
6011da177e4SLinus Torvalds 		ha->func.intr = ips_intr_morpheus;
6021da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_morpheus;
6031da177e4SLinus Torvalds 	} else if (IPS_USE_MEMIO(ha)) {
6041da177e4SLinus Torvalds 		/* copperhead w/MEMIO */
6051da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_copperhead_memio;
6061da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_copperhead_memio;
6071da177e4SLinus Torvalds 		ha->func.init = ips_init_copperhead_memio;
6081da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_copperhead_memio;
6091da177e4SLinus Torvalds 		ha->func.statinit = ips_statinit_memio;
6101da177e4SLinus Torvalds 		ha->func.reset = ips_reset_copperhead_memio;
6111da177e4SLinus Torvalds 		ha->func.intr = ips_intr_copperhead;
6121da177e4SLinus Torvalds 		ha->func.erasebios = ips_erase_bios_memio;
6131da177e4SLinus Torvalds 		ha->func.programbios = ips_program_bios_memio;
6141da177e4SLinus Torvalds 		ha->func.verifybios = ips_verify_bios_memio;
6151da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_copperhead_memio;
6161da177e4SLinus Torvalds 		if (IPS_USE_I2O_DELIVER(ha))
6171da177e4SLinus Torvalds 			ha->func.issue = ips_issue_i2o_memio;
6181da177e4SLinus Torvalds 		else
6191da177e4SLinus Torvalds 			ha->func.issue = ips_issue_copperhead_memio;
6201da177e4SLinus Torvalds 	} else {
6211da177e4SLinus Torvalds 		/* copperhead */
6221da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_copperhead;
6231da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_copperhead;
6241da177e4SLinus Torvalds 		ha->func.init = ips_init_copperhead;
6251da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_copperhead;
6261da177e4SLinus Torvalds 		ha->func.statinit = ips_statinit;
6271da177e4SLinus Torvalds 		ha->func.reset = ips_reset_copperhead;
6281da177e4SLinus Torvalds 		ha->func.intr = ips_intr_copperhead;
6291da177e4SLinus Torvalds 		ha->func.erasebios = ips_erase_bios;
6301da177e4SLinus Torvalds 		ha->func.programbios = ips_program_bios;
6311da177e4SLinus Torvalds 		ha->func.verifybios = ips_verify_bios;
6321da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_copperhead;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 		if (IPS_USE_I2O_DELIVER(ha))
6351da177e4SLinus Torvalds 			ha->func.issue = ips_issue_i2o;
6361da177e4SLinus Torvalds 		else
6371da177e4SLinus Torvalds 			ha->func.issue = ips_issue_copperhead;
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds /****************************************************************************/
6421da177e4SLinus Torvalds /*                                                                          */
6431da177e4SLinus Torvalds /* Routine Name: ips_release                                                */
6441da177e4SLinus Torvalds /*                                                                          */
6451da177e4SLinus Torvalds /* Routine Description:                                                     */
6461da177e4SLinus Torvalds /*                                                                          */
6471da177e4SLinus Torvalds /*   Remove a driver                                                        */
6481da177e4SLinus Torvalds /*                                                                          */
6491da177e4SLinus Torvalds /****************************************************************************/
6501da177e4SLinus Torvalds static int
6511da177e4SLinus Torvalds ips_release(struct Scsi_Host *sh)
6521da177e4SLinus Torvalds {
6531da177e4SLinus Torvalds 	ips_scb_t *scb;
6541da177e4SLinus Torvalds 	ips_ha_t *ha;
6551da177e4SLinus Torvalds 	int i;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	METHOD_TRACE("ips_release", 1);
6581da177e4SLinus Torvalds 
659a50ee7a7SMatthew Wilcox 	scsi_remove_host(sh);
660a50ee7a7SMatthew Wilcox 
6611da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++) ;
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 	if (i == IPS_MAX_ADAPTERS) {
6641da177e4SLinus Torvalds 		printk(KERN_WARNING
6651da177e4SLinus Torvalds 		       "(%s) release, invalid Scsi_Host pointer.\n", ips_name);
6661da177e4SLinus Torvalds 		BUG();
6671da177e4SLinus Torvalds 		return (FALSE);
6681da177e4SLinus Torvalds 	}
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 	ha = IPS_HA(sh);
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	if (!ha)
6731da177e4SLinus Torvalds 		return (FALSE);
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 	/* flush the cache on the controller */
6761da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
6811da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FLUSH;
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 	scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
6841da177e4SLinus Torvalds 	scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
6851da177e4SLinus Torvalds 	scb->cmd.flush_cache.state = IPS_NORM_STATE;
6861da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved = 0;
6871da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved2 = 0;
6881da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved3 = 0;
6891da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved4 = 0;
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	/* send command */
6941da177e4SLinus Torvalds 	if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
6951da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n");
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n");
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 	ips_sh[i] = NULL;
7001da177e4SLinus Torvalds 	ips_ha[i] = NULL;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	/* free extra memory */
7031da177e4SLinus Torvalds 	ips_free(ha);
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	/* free IRQ */
7068a694cc8SJeff Garzik 	free_irq(ha->pcidev->irq, ha);
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 	scsi_host_put(sh);
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds 	ips_released_controllers++;
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 	return (FALSE);
7131da177e4SLinus Torvalds }
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds /****************************************************************************/
7161da177e4SLinus Torvalds /*                                                                          */
7171da177e4SLinus Torvalds /* Routine Name: ips_halt                                                   */
7181da177e4SLinus Torvalds /*                                                                          */
7191da177e4SLinus Torvalds /* Routine Description:                                                     */
7201da177e4SLinus Torvalds /*                                                                          */
7211da177e4SLinus Torvalds /*   Perform cleanup when the system reboots                                */
7221da177e4SLinus Torvalds /*                                                                          */
7231da177e4SLinus Torvalds /****************************************************************************/
7241da177e4SLinus Torvalds static int
7251da177e4SLinus Torvalds ips_halt(struct notifier_block *nb, ulong event, void *buf)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	ips_scb_t *scb;
7281da177e4SLinus Torvalds 	ips_ha_t *ha;
7291da177e4SLinus Torvalds 	int i;
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 	if ((event != SYS_RESTART) && (event != SYS_HALT) &&
7321da177e4SLinus Torvalds 	    (event != SYS_POWER_OFF))
7331da177e4SLinus Torvalds 		return (NOTIFY_DONE);
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	for (i = 0; i < ips_next_controller; i++) {
7361da177e4SLinus Torvalds 		ha = (ips_ha_t *) ips_ha[i];
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 		if (!ha)
7391da177e4SLinus Torvalds 			continue;
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 		if (!ha->active)
7421da177e4SLinus Torvalds 			continue;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 		/* flush the cache on the controller */
7451da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
7501da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_FLUSH;
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 		scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
7531da177e4SLinus Torvalds 		scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
7541da177e4SLinus Torvalds 		scb->cmd.flush_cache.state = IPS_NORM_STATE;
7551da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved = 0;
7561da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved2 = 0;
7571da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved3 = 0;
7581da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved4 = 0;
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 		/* send command */
7631da177e4SLinus Torvalds 		if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) ==
7641da177e4SLinus Torvalds 		    IPS_FAILURE)
7651da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
7661da177e4SLinus Torvalds 				   "Incomplete Flush.\n");
7671da177e4SLinus Torvalds 		else
7681da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
7691da177e4SLinus Torvalds 				   "Flushing Complete.\n");
7701da177e4SLinus Torvalds 	}
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 	return (NOTIFY_OK);
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds /****************************************************************************/
7761da177e4SLinus Torvalds /*                                                                          */
7771da177e4SLinus Torvalds /* Routine Name: ips_eh_abort                                               */
7781da177e4SLinus Torvalds /*                                                                          */
7791da177e4SLinus Torvalds /* Routine Description:                                                     */
7801da177e4SLinus Torvalds /*                                                                          */
7811da177e4SLinus Torvalds /*   Abort a command (using the new error code stuff)                       */
7821da177e4SLinus Torvalds /* Note: this routine is called under the io_request_lock                   */
7831da177e4SLinus Torvalds /****************************************************************************/
7841516b55dSHenne int ips_eh_abort(struct scsi_cmnd *SC)
7851da177e4SLinus Torvalds {
7861da177e4SLinus Torvalds 	ips_ha_t *ha;
7871da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
7881da177e4SLinus Torvalds 	int ret;
7898fa728a2SJeff Garzik  	struct Scsi_Host *host;
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds 	METHOD_TRACE("ips_eh_abort", 1);
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	if (!SC)
7941da177e4SLinus Torvalds 		return (FAILED);
7951da177e4SLinus Torvalds 
7968fa728a2SJeff Garzik  	host = SC->device->host;
7971da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	if (!ha)
8001da177e4SLinus Torvalds 		return (FAILED);
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	if (!ha->active)
8031da177e4SLinus Torvalds 		return (FAILED);
8041da177e4SLinus Torvalds 
805c6a6c81cSAdrian Bunk 	spin_lock(host->host_lock);
8068fa728a2SJeff Garzik  
8071da177e4SLinus Torvalds 	/* See if the command is on the copp queue */
8081da177e4SLinus Torvalds 	item = ha->copp_waitlist.head;
8091da177e4SLinus Torvalds 	while ((item) && (item->scsi_cmd != SC))
8101da177e4SLinus Torvalds 		item = item->next;
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 	if (item) {
8131da177e4SLinus Torvalds 		/* Found it */
8141da177e4SLinus Torvalds 		ips_removeq_copp(&ha->copp_waitlist, item);
8151da177e4SLinus Torvalds 		ret = (SUCCESS);
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 		/* See if the command is on the wait queue */
8181da177e4SLinus Torvalds 	} else if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
8191da177e4SLinus Torvalds 		/* command not sent yet */
8201da177e4SLinus Torvalds 		ret = (SUCCESS);
8211da177e4SLinus Torvalds 	} else {
8221da177e4SLinus Torvalds 		/* command must have already been sent */
8231da177e4SLinus Torvalds 		ret = (FAILED);
8241da177e4SLinus Torvalds 	}
8258fa728a2SJeff Garzik  
826c6a6c81cSAdrian Bunk 	spin_unlock(host->host_lock);
8271da177e4SLinus Torvalds 	return ret;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds /****************************************************************************/
8311da177e4SLinus Torvalds /*                                                                          */
8321da177e4SLinus Torvalds /* Routine Name: ips_eh_reset                                               */
8331da177e4SLinus Torvalds /*                                                                          */
8341da177e4SLinus Torvalds /* Routine Description:                                                     */
8351da177e4SLinus Torvalds /*                                                                          */
8361da177e4SLinus Torvalds /*   Reset the controller (with new eh error code)                          */
8371da177e4SLinus Torvalds /*                                                                          */
8381da177e4SLinus Torvalds /* NOTE: this routine is called under the io_request_lock spinlock          */
8391da177e4SLinus Torvalds /*                                                                          */
8401da177e4SLinus Torvalds /****************************************************************************/
8411516b55dSHenne static int __ips_eh_reset(struct scsi_cmnd *SC)
8421da177e4SLinus Torvalds {
8431da177e4SLinus Torvalds 	int ret;
8441da177e4SLinus Torvalds 	int i;
8451da177e4SLinus Torvalds 	ips_ha_t *ha;
8461da177e4SLinus Torvalds 	ips_scb_t *scb;
8471da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds 	METHOD_TRACE("ips_eh_reset", 1);
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds #ifdef NO_IPS_RESET
8521da177e4SLinus Torvalds 	return (FAILED);
8531da177e4SLinus Torvalds #else
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds 	if (!SC) {
8561da177e4SLinus Torvalds 		DEBUG(1, "Reset called with NULL scsi command");
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 		return (FAILED);
8591da177e4SLinus Torvalds 	}
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
8621da177e4SLinus Torvalds 
8631da177e4SLinus Torvalds 	if (!ha) {
8641da177e4SLinus Torvalds 		DEBUG(1, "Reset called with NULL ha struct");
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 		return (FAILED);
8671da177e4SLinus Torvalds 	}
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 	if (!ha->active)
8701da177e4SLinus Torvalds 		return (FAILED);
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	/* See if the command is on the copp queue */
8731da177e4SLinus Torvalds 	item = ha->copp_waitlist.head;
8741da177e4SLinus Torvalds 	while ((item) && (item->scsi_cmd != SC))
8751da177e4SLinus Torvalds 		item = item->next;
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds 	if (item) {
8781da177e4SLinus Torvalds 		/* Found it */
8791da177e4SLinus Torvalds 		ips_removeq_copp(&ha->copp_waitlist, item);
8801da177e4SLinus Torvalds 		return (SUCCESS);
8811da177e4SLinus Torvalds 	}
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	/* See if the command is on the wait queue */
8841da177e4SLinus Torvalds 	if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
8851da177e4SLinus Torvalds 		/* command not sent yet */
8861da177e4SLinus Torvalds 		return (SUCCESS);
8871da177e4SLinus Torvalds 	}
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds 	/* An explanation for the casual observer:                              */
8901da177e4SLinus Torvalds 	/* Part of the function of a RAID controller is automatic error         */
8911da177e4SLinus Torvalds 	/* detection and recovery.  As such, the only problem that physically   */
8921da177e4SLinus Torvalds 	/* resetting an adapter will ever fix is when, for some reason,         */
8931da177e4SLinus Torvalds 	/* the driver is not successfully communicating with the adapter.       */
8941da177e4SLinus Torvalds 	/* Therefore, we will attempt to flush this adapter.  If that succeeds, */
8951da177e4SLinus Torvalds 	/* then there's no real purpose in a physical reset. This will complete */
8961da177e4SLinus Torvalds 	/* much faster and avoids any problems that might be caused by a        */
8971da177e4SLinus Torvalds 	/* physical reset ( such as having to fail all the outstanding I/O's ). */
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	if (ha->ioctl_reset == 0) {	/* IF Not an IOCTL Requested Reset */
9001da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
9051da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_FLUSH;
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 		scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
9081da177e4SLinus Torvalds 		scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
9091da177e4SLinus Torvalds 		scb->cmd.flush_cache.state = IPS_NORM_STATE;
9101da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved = 0;
9111da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved2 = 0;
9121da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved3 = 0;
9131da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved4 = 0;
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds 		/* Attempt the flush command */
9161da177e4SLinus Torvalds 		ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL);
9171da177e4SLinus Torvalds 		if (ret == IPS_SUCCESS) {
9181da177e4SLinus Torvalds 			IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9191da177e4SLinus Torvalds 				   "Reset Request - Flushed Cache\n");
9201da177e4SLinus Torvalds 			return (SUCCESS);
9211da177e4SLinus Torvalds 		}
9221da177e4SLinus Torvalds 	}
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	/* Either we can't communicate with the adapter or it's an IOCTL request */
9251da177e4SLinus Torvalds 	/* from a utility.  A physical reset is needed at this point.            */
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	ha->ioctl_reset = 0;	/* Reset the IOCTL Requested Reset Flag */
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	/*
9301da177e4SLinus Torvalds 	 * command must have already been sent
9311da177e4SLinus Torvalds 	 * reset the controller
9321da177e4SLinus Torvalds 	 */
9331da177e4SLinus Torvalds 	IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Resetting controller.\n");
9341da177e4SLinus Torvalds 	ret = (*ha->func.reset) (ha);
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	if (!ret) {
9371516b55dSHenne 		struct scsi_cmnd *scsi_cmd;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 		IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9401da177e4SLinus Torvalds 			   "Controller reset failed - controller now offline.\n");
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 		/* Now fail all of the active commands */
9431da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing active commands",
9441da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 		while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
9471da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
9481da177e4SLinus Torvalds 			scb->scsi_cmd->scsi_done(scb->scsi_cmd);
9491da177e4SLinus Torvalds 			ips_freescb(ha, scb);
9501da177e4SLinus Torvalds 		}
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 		/* Now fail all of the pending commands */
9531da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing pending commands",
9541da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 		while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
9571da177e4SLinus Torvalds 			scsi_cmd->result = DID_ERROR;
9581da177e4SLinus Torvalds 			scsi_cmd->scsi_done(scsi_cmd);
9591da177e4SLinus Torvalds 		}
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 		ha->active = FALSE;
9621da177e4SLinus Torvalds 		return (FAILED);
9631da177e4SLinus Torvalds 	}
9641da177e4SLinus Torvalds 
9651da177e4SLinus Torvalds 	if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
9661516b55dSHenne 		struct scsi_cmnd *scsi_cmd;
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 		IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9691da177e4SLinus Torvalds 			   "Controller reset failed - controller now offline.\n");
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds 		/* Now fail all of the active commands */
9721da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing active commands",
9731da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 		while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
9761da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
9771da177e4SLinus Torvalds 			scb->scsi_cmd->scsi_done(scb->scsi_cmd);
9781da177e4SLinus Torvalds 			ips_freescb(ha, scb);
9791da177e4SLinus Torvalds 		}
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds 		/* Now fail all of the pending commands */
9821da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing pending commands",
9831da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 		while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
9861da177e4SLinus Torvalds 			scsi_cmd->result = DID_ERROR << 16;
9871da177e4SLinus Torvalds 			scsi_cmd->scsi_done(scsi_cmd);
9881da177e4SLinus Torvalds 		}
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 		ha->active = FALSE;
9911da177e4SLinus Torvalds 		return (FAILED);
9921da177e4SLinus Torvalds 	}
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	/* FFDC */
9951da177e4SLinus Torvalds 	if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) {
9961da177e4SLinus Torvalds 		struct timeval tv;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 		do_gettimeofday(&tv);
9991da177e4SLinus Torvalds 		ha->last_ffdc = tv.tv_sec;
10001da177e4SLinus Torvalds 		ha->reset_count++;
10011da177e4SLinus Torvalds 		ips_ffdc_reset(ha, IPS_INTR_IORL);
10021da177e4SLinus Torvalds 	}
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 	/* Now fail all of the active commands */
10051da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num);
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 	while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
10081c9fbafcSMartin K. Petersen 		scb->scsi_cmd->result = DID_RESET << 16;
10091da177e4SLinus Torvalds 		scb->scsi_cmd->scsi_done(scb->scsi_cmd);
10101da177e4SLinus Torvalds 		ips_freescb(ha, scb);
10111da177e4SLinus Torvalds 	}
10121da177e4SLinus Torvalds 
10131da177e4SLinus Torvalds 	/* Reset DCDB active command bits */
10141da177e4SLinus Torvalds 	for (i = 1; i < ha->nbus; i++)
10151da177e4SLinus Torvalds 		ha->dcdb_active[i - 1] = 0;
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds 	/* Reset the number of active IOCTLs */
10181da177e4SLinus Torvalds 	ha->num_ioctl = 0;
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_IORL);
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	return (SUCCESS);
10231da177e4SLinus Torvalds #endif				/* NO_IPS_RESET */
10241da177e4SLinus Torvalds 
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
10271516b55dSHenne static int ips_eh_reset(struct scsi_cmnd *SC)
1028df0ae249SJeff Garzik  {
1029df0ae249SJeff Garzik  	int rc;
1030df0ae249SJeff Garzik  
1031df0ae249SJeff Garzik  	spin_lock_irq(SC->device->host->host_lock);
1032df0ae249SJeff Garzik  	rc = __ips_eh_reset(SC);
1033df0ae249SJeff Garzik  	spin_unlock_irq(SC->device->host->host_lock);
1034df0ae249SJeff Garzik  
1035df0ae249SJeff Garzik  	return rc;
1036df0ae249SJeff Garzik  }
1037df0ae249SJeff Garzik  
10381da177e4SLinus Torvalds /****************************************************************************/
10391da177e4SLinus Torvalds /*                                                                          */
10401da177e4SLinus Torvalds /* Routine Name: ips_queue                                                  */
10411da177e4SLinus Torvalds /*                                                                          */
10421da177e4SLinus Torvalds /* Routine Description:                                                     */
10431da177e4SLinus Torvalds /*                                                                          */
10441da177e4SLinus Torvalds /*   Send a command to the controller                                       */
10451da177e4SLinus Torvalds /*                                                                          */
10461da177e4SLinus Torvalds /* NOTE:                                                                    */
10471da177e4SLinus Torvalds /*    Linux obtains io_request_lock before calling this function            */
10481da177e4SLinus Torvalds /*                                                                          */
10491da177e4SLinus Torvalds /****************************************************************************/
1050f281233dSJeff Garzik static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *))
10511da177e4SLinus Torvalds {
10521da177e4SLinus Torvalds 	ips_ha_t *ha;
10531da177e4SLinus Torvalds 	ips_passthru_t *pt;
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds 	METHOD_TRACE("ips_queue", 1);
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	if (!ha)
10601da177e4SLinus Torvalds 		return (1);
10611da177e4SLinus Torvalds 
10621da177e4SLinus Torvalds 	if (!ha->active)
10631da177e4SLinus Torvalds 		return (DID_ERROR);
10641da177e4SLinus Torvalds 
10651da177e4SLinus Torvalds 	if (ips_is_passthru(SC)) {
10661da177e4SLinus Torvalds 		if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
10671da177e4SLinus Torvalds 			SC->result = DID_BUS_BUSY << 16;
10681da177e4SLinus Torvalds 			done(SC);
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 			return (0);
10711da177e4SLinus Torvalds 		}
10721da177e4SLinus Torvalds 	} else if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
10731da177e4SLinus Torvalds 		SC->result = DID_BUS_BUSY << 16;
10741da177e4SLinus Torvalds 		done(SC);
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 		return (0);
10771da177e4SLinus Torvalds 	}
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	SC->scsi_done = done;
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds 	DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
10821da177e4SLinus Torvalds 		  ips_name,
10831da177e4SLinus Torvalds 		  ha->host_num,
10841da177e4SLinus Torvalds 		  SC->cmnd[0],
10851da177e4SLinus Torvalds 		  SC->device->channel, SC->device->id, SC->device->lun);
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 	/* Check for command to initiator IDs */
1088422c0d61SJeff Garzik 	if ((scmd_channel(SC) > 0)
1089422c0d61SJeff Garzik 	    && (scmd_id(SC) == ha->ha_id[scmd_channel(SC)])) {
10901da177e4SLinus Torvalds 		SC->result = DID_NO_CONNECT << 16;
10911da177e4SLinus Torvalds 		done(SC);
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 		return (0);
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds 
10961da177e4SLinus Torvalds 	if (ips_is_passthru(SC)) {
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 		ips_copp_wait_item_t *scratch;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 		/* A Reset IOCTL is only sent by the boot CD in extreme cases.           */
11011da177e4SLinus Torvalds 		/* There can never be any system activity ( network or disk ), but check */
11021da177e4SLinus Torvalds 		/* anyway just as a good practice.                                       */
11032f4cf91cSFUJITA Tomonori 		pt = (ips_passthru_t *) scsi_sglist(SC);
11041da177e4SLinus Torvalds 		if ((pt->CoppCP.cmd.reset.op_code == IPS_CMD_RESET_CHANNEL) &&
11051da177e4SLinus Torvalds 		    (pt->CoppCP.cmd.reset.adapter_flag == 1)) {
11061da177e4SLinus Torvalds 			if (ha->scb_activelist.count != 0) {
11071da177e4SLinus Torvalds 				SC->result = DID_BUS_BUSY << 16;
11081da177e4SLinus Torvalds 				done(SC);
11091da177e4SLinus Torvalds 				return (0);
11101da177e4SLinus Torvalds 			}
11111da177e4SLinus Torvalds 			ha->ioctl_reset = 1;	/* This reset request is from an IOCTL */
1112ba3af0afSMike Christie 			__ips_eh_reset(SC);
11131da177e4SLinus Torvalds 			SC->result = DID_OK << 16;
11141da177e4SLinus Torvalds 			SC->scsi_done(SC);
11151da177e4SLinus Torvalds 			return (0);
11161da177e4SLinus Torvalds 		}
11171da177e4SLinus Torvalds 
11181da177e4SLinus Torvalds 		/* allocate space for the scribble */
11191da177e4SLinus Torvalds 		scratch = kmalloc(sizeof (ips_copp_wait_item_t), GFP_ATOMIC);
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 		if (!scratch) {
11221da177e4SLinus Torvalds 			SC->result = DID_ERROR << 16;
11231da177e4SLinus Torvalds 			done(SC);
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds 			return (0);
11261da177e4SLinus Torvalds 		}
11271da177e4SLinus Torvalds 
11281da177e4SLinus Torvalds 		scratch->scsi_cmd = SC;
11291da177e4SLinus Torvalds 		scratch->next = NULL;
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 		ips_putq_copp_tail(&ha->copp_waitlist, scratch);
11321da177e4SLinus Torvalds 	} else {
11331da177e4SLinus Torvalds 		ips_putq_wait_tail(&ha->scb_waitlist, SC);
11341da177e4SLinus Torvalds 	}
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_IORL);
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds 	return (0);
11391da177e4SLinus Torvalds }
11401da177e4SLinus Torvalds 
1141f281233dSJeff Garzik static DEF_SCSI_QCMD(ips_queue)
1142f281233dSJeff Garzik 
11431da177e4SLinus Torvalds /****************************************************************************/
11441da177e4SLinus Torvalds /*                                                                          */
11451da177e4SLinus Torvalds /* Routine Name: ips_biosparam                                              */
11461da177e4SLinus Torvalds /*                                                                          */
11471da177e4SLinus Torvalds /* Routine Description:                                                     */
11481da177e4SLinus Torvalds /*                                                                          */
11491da177e4SLinus Torvalds /*   Set bios geometry for the controller                                   */
11501da177e4SLinus Torvalds /*                                                                          */
11511da177e4SLinus Torvalds /****************************************************************************/
1152c6a6c81cSAdrian Bunk static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
11531da177e4SLinus Torvalds 			 sector_t capacity, int geom[])
11541da177e4SLinus Torvalds {
11551da177e4SLinus Torvalds 	ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata;
11561da177e4SLinus Torvalds 	int heads;
11571da177e4SLinus Torvalds 	int sectors;
11581da177e4SLinus Torvalds 	int cylinders;
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds 	METHOD_TRACE("ips_biosparam", 1);
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	if (!ha)
11631da177e4SLinus Torvalds 		/* ?!?! host adater info invalid */
11641da177e4SLinus Torvalds 		return (0);
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 	if (!ha->active)
11671da177e4SLinus Torvalds 		return (0);
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 	if (!ips_read_adapter_status(ha, IPS_INTR_ON))
11701da177e4SLinus Torvalds 		/* ?!?! Enquiry command failed */
11711da177e4SLinus Torvalds 		return (0);
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 	if ((capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) {
11741da177e4SLinus Torvalds 		heads = IPS_NORM_HEADS;
11751da177e4SLinus Torvalds 		sectors = IPS_NORM_SECTORS;
11761da177e4SLinus Torvalds 	} else {
11771da177e4SLinus Torvalds 		heads = IPS_COMP_HEADS;
11781da177e4SLinus Torvalds 		sectors = IPS_COMP_SECTORS;
11791da177e4SLinus Torvalds 	}
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	cylinders = (unsigned long) capacity / (heads * sectors);
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 	DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
11841da177e4SLinus Torvalds 		  heads, sectors, cylinders);
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 	geom[0] = heads;
11871da177e4SLinus Torvalds 	geom[1] = sectors;
11881da177e4SLinus Torvalds 	geom[2] = cylinders;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds 	return (0);
11911da177e4SLinus Torvalds }
11921da177e4SLinus Torvalds 
11931da177e4SLinus Torvalds /****************************************************************************/
11941da177e4SLinus Torvalds /*                                                                          */
11951da177e4SLinus Torvalds /* Routine Name: ips_slave_configure                                        */
11961da177e4SLinus Torvalds /*                                                                          */
11971da177e4SLinus Torvalds /* Routine Description:                                                     */
11981da177e4SLinus Torvalds /*                                                                          */
11991da177e4SLinus Torvalds /*   Set queue depths on devices once scan is complete                      */
12001da177e4SLinus Torvalds /*                                                                          */
12011da177e4SLinus Torvalds /****************************************************************************/
12021da177e4SLinus Torvalds static int
1203f64a181dSChristoph Hellwig ips_slave_configure(struct scsi_device * SDptr)
12041da177e4SLinus Torvalds {
12051da177e4SLinus Torvalds 	ips_ha_t *ha;
12061da177e4SLinus Torvalds 	int min;
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	ha = IPS_HA(SDptr->host);
12091da177e4SLinus Torvalds 	if (SDptr->tagged_supported && SDptr->type == TYPE_DISK) {
12101da177e4SLinus Torvalds 		min = ha->max_cmds / 2;
12111da177e4SLinus Torvalds 		if (ha->enq->ucLogDriveCount <= 2)
12121da177e4SLinus Torvalds 			min = ha->max_cmds - 1;
1213*db5ed4dfSChristoph Hellwig 		scsi_change_queue_depth(SDptr, min);
12141da177e4SLinus Torvalds 	}
1215560c26c8SJack Hammer 
1216560c26c8SJack Hammer 	SDptr->skip_ms_page_8 = 1;
1217560c26c8SJack Hammer 	SDptr->skip_ms_page_3f = 1;
12181da177e4SLinus Torvalds 	return 0;
12191da177e4SLinus Torvalds }
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds /****************************************************************************/
12221da177e4SLinus Torvalds /*                                                                          */
12231da177e4SLinus Torvalds /* Routine Name: do_ipsintr                                                 */
12241da177e4SLinus Torvalds /*                                                                          */
12251da177e4SLinus Torvalds /* Routine Description:                                                     */
12261da177e4SLinus Torvalds /*                                                                          */
12271da177e4SLinus Torvalds /*   Wrapper for the interrupt handler                                      */
12281da177e4SLinus Torvalds /*                                                                          */
12291da177e4SLinus Torvalds /****************************************************************************/
12301da177e4SLinus Torvalds static irqreturn_t
12317d12e780SDavid Howells do_ipsintr(int irq, void *dev_id)
12321da177e4SLinus Torvalds {
12331da177e4SLinus Torvalds 	ips_ha_t *ha;
12341da177e4SLinus Torvalds 	struct Scsi_Host *host;
12351da177e4SLinus Torvalds 	int irqstatus;
12361da177e4SLinus Torvalds 
12371da177e4SLinus Torvalds 	METHOD_TRACE("do_ipsintr", 2);
12381da177e4SLinus Torvalds 
12391da177e4SLinus Torvalds 	ha = (ips_ha_t *) dev_id;
12401da177e4SLinus Torvalds 	if (!ha)
12411da177e4SLinus Torvalds 		return IRQ_NONE;
12421da177e4SLinus Torvalds 	host = ips_sh[ha->host_num];
12431da177e4SLinus Torvalds 	/* interrupt during initialization */
12441da177e4SLinus Torvalds 	if (!host) {
12451da177e4SLinus Torvalds 		(*ha->func.intr) (ha);
12461da177e4SLinus Torvalds 		return IRQ_HANDLED;
12471da177e4SLinus Torvalds 	}
12481da177e4SLinus Torvalds 
1249c6a6c81cSAdrian Bunk 	spin_lock(host->host_lock);
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds 	if (!ha->active) {
1252c6a6c81cSAdrian Bunk 		spin_unlock(host->host_lock);
12531da177e4SLinus Torvalds 		return IRQ_HANDLED;
12541da177e4SLinus Torvalds 	}
12551da177e4SLinus Torvalds 
12561da177e4SLinus Torvalds 	irqstatus = (*ha->func.intr) (ha);
12571da177e4SLinus Torvalds 
1258c6a6c81cSAdrian Bunk 	spin_unlock(host->host_lock);
12591da177e4SLinus Torvalds 
12601da177e4SLinus Torvalds 	/* start the next command */
12611da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_ON);
12621da177e4SLinus Torvalds 	return IRQ_RETVAL(irqstatus);
12631da177e4SLinus Torvalds }
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds /****************************************************************************/
12661da177e4SLinus Torvalds /*                                                                          */
12671da177e4SLinus Torvalds /* Routine Name: ips_intr_copperhead                                        */
12681da177e4SLinus Torvalds /*                                                                          */
12691da177e4SLinus Torvalds /* Routine Description:                                                     */
12701da177e4SLinus Torvalds /*                                                                          */
12711da177e4SLinus Torvalds /*   Polling interrupt handler                                              */
12721da177e4SLinus Torvalds /*                                                                          */
12731da177e4SLinus Torvalds /*   ASSUMES interrupts are disabled                                        */
12741da177e4SLinus Torvalds /*                                                                          */
12751da177e4SLinus Torvalds /****************************************************************************/
12761da177e4SLinus Torvalds int
12771da177e4SLinus Torvalds ips_intr_copperhead(ips_ha_t * ha)
12781da177e4SLinus Torvalds {
12791da177e4SLinus Torvalds 	ips_stat_t *sp;
12801da177e4SLinus Torvalds 	ips_scb_t *scb;
12811da177e4SLinus Torvalds 	IPS_STATUS cstatus;
12821da177e4SLinus Torvalds 	int intrstatus;
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	METHOD_TRACE("ips_intr", 2);
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds 	if (!ha)
12871da177e4SLinus Torvalds 		return 0;
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 	if (!ha->active)
12901da177e4SLinus Torvalds 		return 0;
12911da177e4SLinus Torvalds 
12921da177e4SLinus Torvalds 	intrstatus = (*ha->func.isintr) (ha);
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	if (!intrstatus) {
12951da177e4SLinus Torvalds 		/*
12961da177e4SLinus Torvalds 		 * Unexpected/Shared interrupt
12971da177e4SLinus Torvalds 		 */
12981da177e4SLinus Torvalds 
12991da177e4SLinus Torvalds 		return 0;
13001da177e4SLinus Torvalds 	}
13011da177e4SLinus Torvalds 
13021da177e4SLinus Torvalds 	while (TRUE) {
13031da177e4SLinus Torvalds 		sp = &ha->sp;
13041da177e4SLinus Torvalds 
13051da177e4SLinus Torvalds 		intrstatus = (*ha->func.isintr) (ha);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 		if (!intrstatus)
13081da177e4SLinus Torvalds 			break;
13091da177e4SLinus Torvalds 		else
13101da177e4SLinus Torvalds 			cstatus.value = (*ha->func.statupd) (ha);
13111da177e4SLinus Torvalds 
13121da177e4SLinus Torvalds 		if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
1313b1c11812SJoe Perches 			/* Spurious Interrupt ? */
13141da177e4SLinus Torvalds 			continue;
13151da177e4SLinus Torvalds 		}
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 		ips_chkstatus(ha, &cstatus);
13181da177e4SLinus Torvalds 		scb = (ips_scb_t *) sp->scb_addr;
13191da177e4SLinus Torvalds 
13201da177e4SLinus Torvalds 		/*
13211da177e4SLinus Torvalds 		 * use the callback function to finish things up
13221da177e4SLinus Torvalds 		 * NOTE: interrupts are OFF for this
13231da177e4SLinus Torvalds 		 */
13241da177e4SLinus Torvalds 		(*scb->callback) (ha, scb);
13251da177e4SLinus Torvalds 	}			/* end while */
13261da177e4SLinus Torvalds 	return 1;
13271da177e4SLinus Torvalds }
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds /****************************************************************************/
13301da177e4SLinus Torvalds /*                                                                          */
13311da177e4SLinus Torvalds /* Routine Name: ips_intr_morpheus                                          */
13321da177e4SLinus Torvalds /*                                                                          */
13331da177e4SLinus Torvalds /* Routine Description:                                                     */
13341da177e4SLinus Torvalds /*                                                                          */
13351da177e4SLinus Torvalds /*   Polling interrupt handler                                              */
13361da177e4SLinus Torvalds /*                                                                          */
13371da177e4SLinus Torvalds /*   ASSUMES interrupts are disabled                                        */
13381da177e4SLinus Torvalds /*                                                                          */
13391da177e4SLinus Torvalds /****************************************************************************/
13401da177e4SLinus Torvalds int
13411da177e4SLinus Torvalds ips_intr_morpheus(ips_ha_t * ha)
13421da177e4SLinus Torvalds {
13431da177e4SLinus Torvalds 	ips_stat_t *sp;
13441da177e4SLinus Torvalds 	ips_scb_t *scb;
13451da177e4SLinus Torvalds 	IPS_STATUS cstatus;
13461da177e4SLinus Torvalds 	int intrstatus;
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds 	METHOD_TRACE("ips_intr_morpheus", 2);
13491da177e4SLinus Torvalds 
13501da177e4SLinus Torvalds 	if (!ha)
13511da177e4SLinus Torvalds 		return 0;
13521da177e4SLinus Torvalds 
13531da177e4SLinus Torvalds 	if (!ha->active)
13541da177e4SLinus Torvalds 		return 0;
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds 	intrstatus = (*ha->func.isintr) (ha);
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	if (!intrstatus) {
13591da177e4SLinus Torvalds 		/*
13601da177e4SLinus Torvalds 		 * Unexpected/Shared interrupt
13611da177e4SLinus Torvalds 		 */
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 		return 0;
13641da177e4SLinus Torvalds 	}
13651da177e4SLinus Torvalds 
13661da177e4SLinus Torvalds 	while (TRUE) {
13671da177e4SLinus Torvalds 		sp = &ha->sp;
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds 		intrstatus = (*ha->func.isintr) (ha);
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 		if (!intrstatus)
13721da177e4SLinus Torvalds 			break;
13731da177e4SLinus Torvalds 		else
13741da177e4SLinus Torvalds 			cstatus.value = (*ha->func.statupd) (ha);
13751da177e4SLinus Torvalds 
13761da177e4SLinus Torvalds 		if (cstatus.value == 0xffffffff)
13771da177e4SLinus Torvalds 			/* No more to process */
13781da177e4SLinus Torvalds 			break;
13791da177e4SLinus Torvalds 
13801da177e4SLinus Torvalds 		if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
13811da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
13821da177e4SLinus Torvalds 				   "Spurious interrupt; no ccb.\n");
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 			continue;
13851da177e4SLinus Torvalds 		}
13861da177e4SLinus Torvalds 
13871da177e4SLinus Torvalds 		ips_chkstatus(ha, &cstatus);
13881da177e4SLinus Torvalds 		scb = (ips_scb_t *) sp->scb_addr;
13891da177e4SLinus Torvalds 
13901da177e4SLinus Torvalds 		/*
13911da177e4SLinus Torvalds 		 * use the callback function to finish things up
13921da177e4SLinus Torvalds 		 * NOTE: interrupts are OFF for this
13931da177e4SLinus Torvalds 		 */
13941da177e4SLinus Torvalds 		(*scb->callback) (ha, scb);
13951da177e4SLinus Torvalds 	}			/* end while */
13961da177e4SLinus Torvalds 	return 1;
13971da177e4SLinus Torvalds }
13981da177e4SLinus Torvalds 
13991da177e4SLinus Torvalds /****************************************************************************/
14001da177e4SLinus Torvalds /*                                                                          */
14011da177e4SLinus Torvalds /* Routine Name: ips_info                                                   */
14021da177e4SLinus Torvalds /*                                                                          */
14031da177e4SLinus Torvalds /* Routine Description:                                                     */
14041da177e4SLinus Torvalds /*                                                                          */
14051da177e4SLinus Torvalds /*   Return info about the driver                                           */
14061da177e4SLinus Torvalds /*                                                                          */
14071da177e4SLinus Torvalds /****************************************************************************/
14081da177e4SLinus Torvalds static const char *
14091da177e4SLinus Torvalds ips_info(struct Scsi_Host *SH)
14101da177e4SLinus Torvalds {
14111da177e4SLinus Torvalds 	static char buffer[256];
14121da177e4SLinus Torvalds 	char *bp;
14131da177e4SLinus Torvalds 	ips_ha_t *ha;
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 	METHOD_TRACE("ips_info", 1);
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 	ha = IPS_HA(SH);
14181da177e4SLinus Torvalds 
14191da177e4SLinus Torvalds 	if (!ha)
14201da177e4SLinus Torvalds 		return (NULL);
14211da177e4SLinus Torvalds 
14221da177e4SLinus Torvalds 	bp = &buffer[0];
14231da177e4SLinus Torvalds 	memset(bp, 0, sizeof (buffer));
14241da177e4SLinus Torvalds 
14251da177e4SLinus Torvalds 	sprintf(bp, "%s%s%s Build %d", "IBM PCI ServeRAID ",
14261da177e4SLinus Torvalds 		IPS_VERSION_HIGH, IPS_VERSION_LOW, IPS_BUILD_IDENT);
14271da177e4SLinus Torvalds 
14281da177e4SLinus Torvalds 	if (ha->ad_type > 0 && ha->ad_type <= MAX_ADAPTER_NAME) {
14291da177e4SLinus Torvalds 		strcat(bp, " <");
14301da177e4SLinus Torvalds 		strcat(bp, ips_adapter_name[ha->ad_type - 1]);
14311da177e4SLinus Torvalds 		strcat(bp, ">");
14321da177e4SLinus Torvalds 	}
14331da177e4SLinus Torvalds 
14341da177e4SLinus Torvalds 	return (bp);
14351da177e4SLinus Torvalds }
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds static int
1438aacce706SAl Viro ips_write_info(struct Scsi_Host *host, char *buffer, int length)
14391da177e4SLinus Torvalds {
14401da177e4SLinus Torvalds 	int i;
14411da177e4SLinus Torvalds 	ips_ha_t *ha = NULL;
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds 	/* Find our host structure */
14441da177e4SLinus Torvalds 	for (i = 0; i < ips_next_controller; i++) {
14451da177e4SLinus Torvalds 		if (ips_sh[i]) {
14461da177e4SLinus Torvalds 			if (ips_sh[i] == host) {
14471da177e4SLinus Torvalds 				ha = (ips_ha_t *) ips_sh[i]->hostdata;
14481da177e4SLinus Torvalds 				break;
14491da177e4SLinus Torvalds 			}
14501da177e4SLinus Torvalds 		}
14511da177e4SLinus Torvalds 	}
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds 	if (!ha)
14541da177e4SLinus Torvalds 		return (-EINVAL);
14551da177e4SLinus Torvalds 
1456aacce706SAl Viro 	return 0;
14571da177e4SLinus Torvalds }
1458aacce706SAl Viro 
1459aacce706SAl Viro static int
1460aacce706SAl Viro ips_show_info(struct seq_file *m, struct Scsi_Host *host)
1461aacce706SAl Viro {
1462aacce706SAl Viro 	int i;
1463aacce706SAl Viro 	ips_ha_t *ha = NULL;
1464aacce706SAl Viro 
1465aacce706SAl Viro 	/* Find our host structure */
1466aacce706SAl Viro 	for (i = 0; i < ips_next_controller; i++) {
1467aacce706SAl Viro 		if (ips_sh[i]) {
1468aacce706SAl Viro 			if (ips_sh[i] == host) {
1469aacce706SAl Viro 				ha = (ips_ha_t *) ips_sh[i]->hostdata;
1470aacce706SAl Viro 				break;
1471aacce706SAl Viro 			}
1472aacce706SAl Viro 		}
1473aacce706SAl Viro 	}
1474aacce706SAl Viro 
1475aacce706SAl Viro 	if (!ha)
1476aacce706SAl Viro 		return (-EINVAL);
1477aacce706SAl Viro 
1478aacce706SAl Viro 	return ips_host_info(ha, m);
14791da177e4SLinus Torvalds }
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds /*--------------------------------------------------------------------------*/
14821da177e4SLinus Torvalds /* Helper Functions                                                         */
14831da177e4SLinus Torvalds /*--------------------------------------------------------------------------*/
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds /****************************************************************************/
14861da177e4SLinus Torvalds /*                                                                          */
14871da177e4SLinus Torvalds /* Routine Name: ips_is_passthru                                            */
14881da177e4SLinus Torvalds /*                                                                          */
14891da177e4SLinus Torvalds /* Routine Description:                                                     */
14901da177e4SLinus Torvalds /*                                                                          */
14911da177e4SLinus Torvalds /*   Determine if the specified SCSI command is really a passthru command   */
14921da177e4SLinus Torvalds /*                                                                          */
14931da177e4SLinus Torvalds /****************************************************************************/
14941516b55dSHenne static int ips_is_passthru(struct scsi_cmnd *SC)
14951da177e4SLinus Torvalds {
1496a3632fa3SJack Hammer 	unsigned long flags;
1497a3632fa3SJack Hammer 
14981da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_passthru", 1);
14991da177e4SLinus Torvalds 
15001da177e4SLinus Torvalds 	if (!SC)
15011da177e4SLinus Torvalds 		return (0);
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	if ((SC->cmnd[0] == IPS_IOCTL_COMMAND) &&
15041da177e4SLinus Torvalds 	    (SC->device->channel == 0) &&
15051da177e4SLinus Torvalds 	    (SC->device->id == IPS_ADAPTER_ID) &&
15062f4cf91cSFUJITA Tomonori 	    (SC->device->lun == 0) && scsi_sglist(SC)) {
15072f4cf91cSFUJITA Tomonori                 struct scatterlist *sg = scsi_sglist(SC);
1508a3632fa3SJack Hammer                 char  *buffer;
1509a3632fa3SJack Hammer 
1510a3632fa3SJack Hammer                 /* kmap_atomic() ensures addressability of the user buffer.*/
1511a3632fa3SJack Hammer                 /* local_irq_save() protects the KM_IRQ0 address slot.     */
1512a3632fa3SJack Hammer                 local_irq_save(flags);
151377dfce07SCong Wang                 buffer = kmap_atomic(sg_page(sg)) + sg->offset;
15141da177e4SLinus Torvalds                 if (buffer && buffer[0] == 'C' && buffer[1] == 'O' &&
1515a3632fa3SJack Hammer                     buffer[2] == 'P' && buffer[3] == 'P') {
151677dfce07SCong Wang                         kunmap_atomic(buffer - sg->offset);
1517a3632fa3SJack Hammer                         local_irq_restore(flags);
15181da177e4SLinus Torvalds                         return 1;
15191da177e4SLinus Torvalds                 }
152077dfce07SCong Wang                 kunmap_atomic(buffer - sg->offset);
1521a3632fa3SJack Hammer                 local_irq_restore(flags);
1522a3632fa3SJack Hammer 	}
15231da177e4SLinus Torvalds 	return 0;
15241da177e4SLinus Torvalds }
15251da177e4SLinus Torvalds 
15261da177e4SLinus Torvalds /****************************************************************************/
15271da177e4SLinus Torvalds /*                                                                          */
15281da177e4SLinus Torvalds /* Routine Name: ips_alloc_passthru_buffer                                  */
15291da177e4SLinus Torvalds /*                                                                          */
15301da177e4SLinus Torvalds /* Routine Description:                                                     */
15311da177e4SLinus Torvalds /*   allocate a buffer large enough for the ioctl data if the ioctl buffer  */
15321da177e4SLinus Torvalds /*   is too small or doesn't exist                                          */
15331da177e4SLinus Torvalds /****************************************************************************/
15341da177e4SLinus Torvalds static int
15351da177e4SLinus Torvalds ips_alloc_passthru_buffer(ips_ha_t * ha, int length)
15361da177e4SLinus Torvalds {
15371da177e4SLinus Torvalds 	void *bigger_buf;
15381da177e4SLinus Torvalds 	dma_addr_t dma_busaddr;
15391da177e4SLinus Torvalds 
15401da177e4SLinus Torvalds 	if (ha->ioctl_data && length <= ha->ioctl_len)
15411da177e4SLinus Torvalds 		return 0;
15421da177e4SLinus Torvalds 	/* there is no buffer or it's not big enough, allocate a new one */
15431da177e4SLinus Torvalds 	bigger_buf = pci_alloc_consistent(ha->pcidev, length, &dma_busaddr);
15441da177e4SLinus Torvalds 	if (bigger_buf) {
15451da177e4SLinus Torvalds 		/* free the old memory */
15461da177e4SLinus Torvalds 		pci_free_consistent(ha->pcidev, ha->ioctl_len, ha->ioctl_data,
15471da177e4SLinus Torvalds 				    ha->ioctl_busaddr);
15481da177e4SLinus Torvalds 		/* use the new memory */
15491da177e4SLinus Torvalds 		ha->ioctl_data = (char *) bigger_buf;
15501da177e4SLinus Torvalds 		ha->ioctl_len = length;
15511da177e4SLinus Torvalds 		ha->ioctl_busaddr = dma_busaddr;
15521da177e4SLinus Torvalds 	} else {
15531da177e4SLinus Torvalds 		return -1;
15541da177e4SLinus Torvalds 	}
15551da177e4SLinus Torvalds 	return 0;
15561da177e4SLinus Torvalds }
15571da177e4SLinus Torvalds 
15581da177e4SLinus Torvalds /****************************************************************************/
15591da177e4SLinus Torvalds /*                                                                          */
15601da177e4SLinus Torvalds /* Routine Name: ips_make_passthru                                          */
15611da177e4SLinus Torvalds /*                                                                          */
15621da177e4SLinus Torvalds /* Routine Description:                                                     */
15631da177e4SLinus Torvalds /*                                                                          */
15641da177e4SLinus Torvalds /*   Make a passthru command out of the info in the Scsi block              */
15651da177e4SLinus Torvalds /*                                                                          */
15661da177e4SLinus Torvalds /****************************************************************************/
15671da177e4SLinus Torvalds static int
15681516b55dSHenne ips_make_passthru(ips_ha_t *ha, struct scsi_cmnd *SC, ips_scb_t *scb, int intr)
15691da177e4SLinus Torvalds {
15701da177e4SLinus Torvalds 	ips_passthru_t *pt;
15711da177e4SLinus Torvalds 	int length = 0;
15722f4cf91cSFUJITA Tomonori 	int i, ret;
15732f4cf91cSFUJITA Tomonori         struct scatterlist *sg = scsi_sglist(SC);
15741da177e4SLinus Torvalds 
15751da177e4SLinus Torvalds 	METHOD_TRACE("ips_make_passthru", 1);
15761da177e4SLinus Torvalds 
15772f4cf91cSFUJITA Tomonori         scsi_for_each_sg(SC, sg, scsi_sg_count(SC), i)
15782b28a472SFUJITA Tomonori 		length += sg->length;
15792f4cf91cSFUJITA Tomonori 
15801da177e4SLinus Torvalds 	if (length < sizeof (ips_passthru_t)) {
15811da177e4SLinus Torvalds 		/* wrong size */
15821da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
15831da177e4SLinus Torvalds 			  ips_name, ha->host_num);
15841da177e4SLinus Torvalds 		return (IPS_FAILURE);
15851da177e4SLinus Torvalds 	}
15861da177e4SLinus Torvalds 	if (ips_alloc_passthru_buffer(ha, length)) {
15871da177e4SLinus Torvalds 		/* allocation failure!  If ha->ioctl_data exists, use it to return
15881da177e4SLinus Torvalds 		   some error codes.  Return a failed command to the scsi layer. */
15891da177e4SLinus Torvalds 		if (ha->ioctl_data) {
15901da177e4SLinus Torvalds 			pt = (ips_passthru_t *) ha->ioctl_data;
15911da177e4SLinus Torvalds 			ips_scmd_buf_read(SC, pt, sizeof (ips_passthru_t));
15921da177e4SLinus Torvalds 			pt->BasicStatus = 0x0B;
15931da177e4SLinus Torvalds 			pt->ExtendedStatus = 0x00;
15941da177e4SLinus Torvalds 			ips_scmd_buf_write(SC, pt, sizeof (ips_passthru_t));
15951da177e4SLinus Torvalds 		}
15961da177e4SLinus Torvalds 		return IPS_FAILURE;
15971da177e4SLinus Torvalds 	}
15981da177e4SLinus Torvalds 	ha->ioctl_datasize = length;
15991da177e4SLinus Torvalds 
16001da177e4SLinus Torvalds 	ips_scmd_buf_read(SC, ha->ioctl_data, ha->ioctl_datasize);
16011da177e4SLinus Torvalds 	pt = (ips_passthru_t *) ha->ioctl_data;
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	/*
16041da177e4SLinus Torvalds 	 * Some notes about the passthru interface used
16051da177e4SLinus Torvalds 	 *
16061da177e4SLinus Torvalds 	 * IF the scsi op_code == 0x0d then we assume
16071da177e4SLinus Torvalds 	 * that the data came along with/goes with the
16081da177e4SLinus Torvalds 	 * packet we received from the sg driver. In this
16091da177e4SLinus Torvalds 	 * case the CmdBSize field of the pt structure is
16101da177e4SLinus Torvalds 	 * used for the size of the buffer.
16111da177e4SLinus Torvalds 	 */
16121da177e4SLinus Torvalds 
16131da177e4SLinus Torvalds 	switch (pt->CoppCmd) {
16141da177e4SLinus Torvalds 	case IPS_NUMCTRLS:
16151da177e4SLinus Torvalds 		memcpy(ha->ioctl_data + sizeof (ips_passthru_t),
16161da177e4SLinus Torvalds 		       &ips_num_controllers, sizeof (int));
16171da177e4SLinus Torvalds 		ips_scmd_buf_write(SC, ha->ioctl_data,
16181da177e4SLinus Torvalds 				   sizeof (ips_passthru_t) + sizeof (int));
16191da177e4SLinus Torvalds 		SC->result = DID_OK << 16;
16201da177e4SLinus Torvalds 
16211da177e4SLinus Torvalds 		return (IPS_SUCCESS_IMM);
16221da177e4SLinus Torvalds 
16231da177e4SLinus Torvalds 	case IPS_COPPUSRCMD:
16241da177e4SLinus Torvalds 	case IPS_COPPIOCCMD:
16251da177e4SLinus Torvalds 		if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
16261da177e4SLinus Torvalds 			if (length < (sizeof (ips_passthru_t) + pt->CmdBSize)) {
16271da177e4SLinus Torvalds 				/* wrong size */
16281da177e4SLinus Torvalds 				DEBUG_VAR(1,
16291da177e4SLinus Torvalds 					  "(%s%d) Passthru structure wrong size",
16301da177e4SLinus Torvalds 					  ips_name, ha->host_num);
16311da177e4SLinus Torvalds 
16321da177e4SLinus Torvalds 				return (IPS_FAILURE);
16331da177e4SLinus Torvalds 			}
16341da177e4SLinus Torvalds 
16358a694cc8SJeff Garzik 			if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD &&
16361da177e4SLinus Torvalds 			    pt->CoppCP.cmd.flashfw.op_code ==
16371da177e4SLinus Torvalds 			    IPS_CMD_RW_BIOSFW) {
16381da177e4SLinus Torvalds 				ret = ips_flash_copperhead(ha, pt, scb);
16391da177e4SLinus Torvalds 				ips_scmd_buf_write(SC, ha->ioctl_data,
16401da177e4SLinus Torvalds 						   sizeof (ips_passthru_t));
16411da177e4SLinus Torvalds 				return ret;
16421da177e4SLinus Torvalds 			}
16431da177e4SLinus Torvalds 			if (ips_usrcmd(ha, pt, scb))
16441da177e4SLinus Torvalds 				return (IPS_SUCCESS);
16451da177e4SLinus Torvalds 			else
16461da177e4SLinus Torvalds 				return (IPS_FAILURE);
16471da177e4SLinus Torvalds 		}
16481da177e4SLinus Torvalds 
16491da177e4SLinus Torvalds 		break;
16501da177e4SLinus Torvalds 
16511da177e4SLinus Torvalds 	}			/* end switch */
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds 	return (IPS_FAILURE);
16541da177e4SLinus Torvalds }
16551da177e4SLinus Torvalds 
16561da177e4SLinus Torvalds /****************************************************************************/
16571da177e4SLinus Torvalds /* Routine Name: ips_flash_copperhead                                       */
16581da177e4SLinus Torvalds /* Routine Description:                                                     */
16591da177e4SLinus Torvalds /*   Flash the BIOS/FW on a Copperhead style controller                     */
16601da177e4SLinus Torvalds /****************************************************************************/
16611da177e4SLinus Torvalds static int
16621da177e4SLinus Torvalds ips_flash_copperhead(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
16631da177e4SLinus Torvalds {
16641da177e4SLinus Torvalds 	int datasize;
16651da177e4SLinus Torvalds 
16661da177e4SLinus Torvalds 	/* Trombone is the only copperhead that can do packet flash, but only
166725985edcSLucas De Marchi 	 * for firmware. No one said it had to make sense. */
16681da177e4SLinus Torvalds 	if (IPS_IS_TROMBONE(ha) && pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE) {
16691da177e4SLinus Torvalds 		if (ips_usrcmd(ha, pt, scb))
16701da177e4SLinus Torvalds 			return IPS_SUCCESS;
16711da177e4SLinus Torvalds 		else
16721da177e4SLinus Torvalds 			return IPS_FAILURE;
16731da177e4SLinus Torvalds 	}
16741da177e4SLinus Torvalds 	pt->BasicStatus = 0x0B;
16751da177e4SLinus Torvalds 	pt->ExtendedStatus = 0;
16761da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
16771da177e4SLinus Torvalds 	/* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can     */
16781da177e4SLinus Torvalds 	/* avoid allocating a huge buffer per adapter ( which can fail ). */
16791da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
16801da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
16811da177e4SLinus Torvalds 		pt->BasicStatus = 0;
16821da177e4SLinus Torvalds 		return ips_flash_bios(ha, pt, scb);
16831da177e4SLinus Torvalds 	} else if (pt->CoppCP.cmd.flashfw.packet_num == 0) {
16841da177e4SLinus Torvalds 		if (ips_FlashData && !test_and_set_bit(0, &ips_FlashDataInUse)){
16851da177e4SLinus Torvalds 			ha->flash_data = ips_FlashData;
16861da177e4SLinus Torvalds 			ha->flash_busaddr = ips_flashbusaddr;
16871da177e4SLinus Torvalds 			ha->flash_len = PAGE_SIZE << 7;
16881da177e4SLinus Torvalds 			ha->flash_datasize = 0;
16891da177e4SLinus Torvalds 		} else if (!ha->flash_data) {
16901da177e4SLinus Torvalds 			datasize = pt->CoppCP.cmd.flashfw.total_packets *
16911da177e4SLinus Torvalds 			    pt->CoppCP.cmd.flashfw.count;
16921da177e4SLinus Torvalds 			ha->flash_data = pci_alloc_consistent(ha->pcidev,
16931da177e4SLinus Torvalds 					                      datasize,
16941da177e4SLinus Torvalds 							      &ha->flash_busaddr);
16951da177e4SLinus Torvalds 			if (!ha->flash_data){
16961da177e4SLinus Torvalds 				printk(KERN_WARNING "Unable to allocate a flash buffer\n");
16971da177e4SLinus Torvalds 				return IPS_FAILURE;
16981da177e4SLinus Torvalds 			}
16991da177e4SLinus Torvalds 			ha->flash_datasize = 0;
17001da177e4SLinus Torvalds 			ha->flash_len = datasize;
17011da177e4SLinus Torvalds 		} else
17021da177e4SLinus Torvalds 			return IPS_FAILURE;
17031da177e4SLinus Torvalds 	} else {
17041da177e4SLinus Torvalds 		if (pt->CoppCP.cmd.flashfw.count + ha->flash_datasize >
17051da177e4SLinus Torvalds 		    ha->flash_len) {
17061da177e4SLinus Torvalds 			ips_free_flash_copperhead(ha);
17071da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
17081da177e4SLinus Torvalds 				   "failed size sanity check\n");
17091da177e4SLinus Torvalds 			return IPS_FAILURE;
17101da177e4SLinus Torvalds 		}
17111da177e4SLinus Torvalds 	}
17121da177e4SLinus Torvalds 	if (!ha->flash_data)
17131da177e4SLinus Torvalds 		return IPS_FAILURE;
17141da177e4SLinus Torvalds 	pt->BasicStatus = 0;
17151da177e4SLinus Torvalds 	memcpy(&ha->flash_data[ha->flash_datasize], pt + 1,
17161da177e4SLinus Torvalds 	       pt->CoppCP.cmd.flashfw.count);
17171da177e4SLinus Torvalds 	ha->flash_datasize += pt->CoppCP.cmd.flashfw.count;
17181da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.packet_num ==
17191da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.total_packets - 1) {
17201da177e4SLinus Torvalds 		if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE)
17211da177e4SLinus Torvalds 			return ips_flash_bios(ha, pt, scb);
17221da177e4SLinus Torvalds 		else if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE)
17231da177e4SLinus Torvalds 			return ips_flash_firmware(ha, pt, scb);
17241da177e4SLinus Torvalds 	}
17251da177e4SLinus Torvalds 	return IPS_SUCCESS_IMM;
17261da177e4SLinus Torvalds }
17271da177e4SLinus Torvalds 
17281da177e4SLinus Torvalds /****************************************************************************/
17291da177e4SLinus Torvalds /* Routine Name: ips_flash_bios                                             */
17301da177e4SLinus Torvalds /* Routine Description:                                                     */
17311da177e4SLinus Torvalds /*   flashes the bios of a copperhead adapter                               */
17321da177e4SLinus Torvalds /****************************************************************************/
17331da177e4SLinus Torvalds static int
17341da177e4SLinus Torvalds ips_flash_bios(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
17351da177e4SLinus Torvalds {
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
17381da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_BIOS) {
17391da177e4SLinus Torvalds 		if ((!ha->func.programbios) || (!ha->func.erasebios) ||
17401da177e4SLinus Torvalds 		    (!ha->func.verifybios))
17411da177e4SLinus Torvalds 			goto error;
17421da177e4SLinus Torvalds 		if ((*ha->func.erasebios) (ha)) {
17431da177e4SLinus Torvalds 			DEBUG_VAR(1,
17441da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to erase flash",
17451da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17461da177e4SLinus Torvalds 			goto error;
17471da177e4SLinus Torvalds 		} else
17481da177e4SLinus Torvalds 		    if ((*ha->func.programbios) (ha,
17491da177e4SLinus Torvalds 						 ha->flash_data +
17501da177e4SLinus Torvalds 						 IPS_BIOS_HEADER,
17511da177e4SLinus Torvalds 						 ha->flash_datasize -
17521da177e4SLinus Torvalds 						 IPS_BIOS_HEADER, 0)) {
17531da177e4SLinus Torvalds 			DEBUG_VAR(1,
17541da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to flash",
17551da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17561da177e4SLinus Torvalds 			goto error;
17571da177e4SLinus Torvalds 		} else
17581da177e4SLinus Torvalds 		    if ((*ha->func.verifybios) (ha,
17591da177e4SLinus Torvalds 						ha->flash_data +
17601da177e4SLinus Torvalds 						IPS_BIOS_HEADER,
17611da177e4SLinus Torvalds 						ha->flash_datasize -
17621da177e4SLinus Torvalds 						IPS_BIOS_HEADER, 0)) {
17631da177e4SLinus Torvalds 			DEBUG_VAR(1,
17641da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to verify flash",
17651da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17661da177e4SLinus Torvalds 			goto error;
17671da177e4SLinus Torvalds 		}
17681da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
17691da177e4SLinus Torvalds 		return IPS_SUCCESS_IMM;
17701da177e4SLinus Torvalds 	} else if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
17711da177e4SLinus Torvalds 		   pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
17721da177e4SLinus Torvalds 		if (!ha->func.erasebios)
17731da177e4SLinus Torvalds 			goto error;
17741da177e4SLinus Torvalds 		if ((*ha->func.erasebios) (ha)) {
17751da177e4SLinus Torvalds 			DEBUG_VAR(1,
17761da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to erase flash",
17771da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17781da177e4SLinus Torvalds 			goto error;
17791da177e4SLinus Torvalds 		}
17801da177e4SLinus Torvalds 		return IPS_SUCCESS_IMM;
17811da177e4SLinus Torvalds 	}
17821da177e4SLinus Torvalds       error:
17831da177e4SLinus Torvalds 	pt->BasicStatus = 0x0B;
17841da177e4SLinus Torvalds 	pt->ExtendedStatus = 0x00;
17851da177e4SLinus Torvalds 	ips_free_flash_copperhead(ha);
17861da177e4SLinus Torvalds 	return IPS_FAILURE;
17871da177e4SLinus Torvalds }
17881da177e4SLinus Torvalds 
17891da177e4SLinus Torvalds /****************************************************************************/
17901da177e4SLinus Torvalds /*                                                                          */
17911da177e4SLinus Torvalds /* Routine Name: ips_fill_scb_sg_single                                     */
17921da177e4SLinus Torvalds /*                                                                          */
17931da177e4SLinus Torvalds /* Routine Description:                                                     */
17941da177e4SLinus Torvalds /*   Fill in a single scb sg_list element from an address                   */
17951da177e4SLinus Torvalds /*   return a -1 if a breakup occurred                                      */
17961da177e4SLinus Torvalds /****************************************************************************/
17971da177e4SLinus Torvalds static int
17981da177e4SLinus Torvalds ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr,
17991da177e4SLinus Torvalds 		       ips_scb_t * scb, int indx, unsigned int e_len)
18001da177e4SLinus Torvalds {
18011da177e4SLinus Torvalds 
18021da177e4SLinus Torvalds 	int ret_val = 0;
18031da177e4SLinus Torvalds 
18041da177e4SLinus Torvalds 	if ((scb->data_len + e_len) > ha->max_xfer) {
18051da177e4SLinus Torvalds 		e_len = ha->max_xfer - scb->data_len;
18061da177e4SLinus Torvalds 		scb->breakup = indx;
18071da177e4SLinus Torvalds 		++scb->sg_break;
18081da177e4SLinus Torvalds 		ret_val = -1;
18091da177e4SLinus Torvalds 	} else {
18101da177e4SLinus Torvalds 		scb->breakup = 0;
18111da177e4SLinus Torvalds 		scb->sg_break = 0;
18121da177e4SLinus Torvalds 	}
18131da177e4SLinus Torvalds 	if (IPS_USE_ENH_SGLIST(ha)) {
18141da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].address_lo =
18151da177e4SLinus Torvalds 		    cpu_to_le32(pci_dma_lo32(busaddr));
18161da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].address_hi =
18171da177e4SLinus Torvalds 		    cpu_to_le32(pci_dma_hi32(busaddr));
18181da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].length = cpu_to_le32(e_len);
18191da177e4SLinus Torvalds 	} else {
18201da177e4SLinus Torvalds 		scb->sg_list.std_list[indx].address =
18211da177e4SLinus Torvalds 		    cpu_to_le32(pci_dma_lo32(busaddr));
18221da177e4SLinus Torvalds 		scb->sg_list.std_list[indx].length = cpu_to_le32(e_len);
18231da177e4SLinus Torvalds 	}
18241da177e4SLinus Torvalds 
18251da177e4SLinus Torvalds 	++scb->sg_len;
18261da177e4SLinus Torvalds 	scb->data_len += e_len;
18271da177e4SLinus Torvalds 	return ret_val;
18281da177e4SLinus Torvalds }
18291da177e4SLinus Torvalds 
18301da177e4SLinus Torvalds /****************************************************************************/
18311da177e4SLinus Torvalds /* Routine Name: ips_flash_firmware                                         */
18321da177e4SLinus Torvalds /* Routine Description:                                                     */
18331da177e4SLinus Torvalds /*   flashes the firmware of a copperhead adapter                           */
18341da177e4SLinus Torvalds /****************************************************************************/
18351da177e4SLinus Torvalds static int
18361da177e4SLinus Torvalds ips_flash_firmware(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
18371da177e4SLinus Torvalds {
18381da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
18391da177e4SLinus Torvalds 	uint32_t cmd_busaddr;
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE &&
18421da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_FW) {
18431da177e4SLinus Torvalds 		memset(&pt->CoppCP.cmd, 0, sizeof (IPS_HOST_COMMAND));
18441da177e4SLinus Torvalds 		pt->CoppCP.cmd.flashfw.op_code = IPS_CMD_DOWNLOAD;
18451da177e4SLinus Torvalds 		pt->CoppCP.cmd.flashfw.count = cpu_to_le32(ha->flash_datasize);
18461da177e4SLinus Torvalds 	} else {
18471da177e4SLinus Torvalds 		pt->BasicStatus = 0x0B;
18481da177e4SLinus Torvalds 		pt->ExtendedStatus = 0x00;
18491da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
18501da177e4SLinus Torvalds 		return IPS_FAILURE;
18511da177e4SLinus Torvalds 	}
18521da177e4SLinus Torvalds 	/* Save the S/G list pointer so it doesn't get clobbered */
18531da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
18541da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
18551da177e4SLinus Torvalds 	/* copy in the CP */
18561da177e4SLinus Torvalds 	memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
18571da177e4SLinus Torvalds 	/* FIX stuff that might be wrong */
18581da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
18591da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
18601da177e4SLinus Torvalds 	scb->bus = scb->scsi_cmd->device->channel;
18611da177e4SLinus Torvalds 	scb->target_id = scb->scsi_cmd->device->id;
18621da177e4SLinus Torvalds 	scb->lun = scb->scsi_cmd->device->lun;
18631da177e4SLinus Torvalds 	scb->sg_len = 0;
18641da177e4SLinus Torvalds 	scb->data_len = 0;
18651da177e4SLinus Torvalds 	scb->flags = 0;
18661da177e4SLinus Torvalds 	scb->op_code = 0;
18671da177e4SLinus Torvalds 	scb->callback = ipsintr_done;
18681da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
18691da177e4SLinus Torvalds 
18701da177e4SLinus Torvalds 	scb->data_len = ha->flash_datasize;
18711da177e4SLinus Torvalds 	scb->data_busaddr =
18721da177e4SLinus Torvalds 	    pci_map_single(ha->pcidev, ha->flash_data, scb->data_len,
18731da177e4SLinus Torvalds 			   IPS_DMA_DIR(scb));
18741da177e4SLinus Torvalds 	scb->flags |= IPS_SCB_MAP_SINGLE;
18751da177e4SLinus Torvalds 	scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
18761da177e4SLinus Torvalds 	scb->cmd.flashfw.buffer_addr = cpu_to_le32(scb->data_busaddr);
18771da177e4SLinus Torvalds 	if (pt->TimeOut)
18781da177e4SLinus Torvalds 		scb->timeout = pt->TimeOut;
18791da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
18801da177e4SLinus Torvalds 	return IPS_SUCCESS;
18811da177e4SLinus Torvalds }
18821da177e4SLinus Torvalds 
18831da177e4SLinus Torvalds /****************************************************************************/
18841da177e4SLinus Torvalds /* Routine Name: ips_free_flash_copperhead                                  */
18851da177e4SLinus Torvalds /* Routine Description:                                                     */
18861da177e4SLinus Torvalds /*   release the memory resources used to hold the flash image              */
18871da177e4SLinus Torvalds /****************************************************************************/
18881da177e4SLinus Torvalds static void
18891da177e4SLinus Torvalds ips_free_flash_copperhead(ips_ha_t * ha)
18901da177e4SLinus Torvalds {
18911da177e4SLinus Torvalds 	if (ha->flash_data == ips_FlashData)
18921da177e4SLinus Torvalds 		test_and_clear_bit(0, &ips_FlashDataInUse);
18931da177e4SLinus Torvalds 	else if (ha->flash_data)
18941da177e4SLinus Torvalds 		pci_free_consistent(ha->pcidev, ha->flash_len, ha->flash_data,
18951da177e4SLinus Torvalds 				    ha->flash_busaddr);
18961da177e4SLinus Torvalds 	ha->flash_data = NULL;
18971da177e4SLinus Torvalds }
18981da177e4SLinus Torvalds 
18991da177e4SLinus Torvalds /****************************************************************************/
19001da177e4SLinus Torvalds /*                                                                          */
19011da177e4SLinus Torvalds /* Routine Name: ips_usrcmd                                                 */
19021da177e4SLinus Torvalds /*                                                                          */
19031da177e4SLinus Torvalds /* Routine Description:                                                     */
19041da177e4SLinus Torvalds /*                                                                          */
19051da177e4SLinus Torvalds /*   Process a user command and make it ready to send                       */
19061da177e4SLinus Torvalds /*                                                                          */
19071da177e4SLinus Torvalds /****************************************************************************/
19081da177e4SLinus Torvalds static int
19091da177e4SLinus Torvalds ips_usrcmd(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
19101da177e4SLinus Torvalds {
19111da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
19121da177e4SLinus Torvalds 	uint32_t cmd_busaddr;
19131da177e4SLinus Torvalds 
19141da177e4SLinus Torvalds 	METHOD_TRACE("ips_usrcmd", 1);
19151da177e4SLinus Torvalds 
19161da177e4SLinus Torvalds 	if ((!scb) || (!pt) || (!ha))
19171da177e4SLinus Torvalds 		return (0);
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds 	/* Save the S/G list pointer so it doesn't get clobbered */
19201da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
19211da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
19221da177e4SLinus Torvalds 	/* copy in the CP */
19231da177e4SLinus Torvalds 	memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
19241da177e4SLinus Torvalds 	memcpy(&scb->dcdb, &pt->CoppCP.dcdb, sizeof (IPS_DCDB_TABLE));
19251da177e4SLinus Torvalds 
19261da177e4SLinus Torvalds 	/* FIX stuff that might be wrong */
19271da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
19281da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
19291da177e4SLinus Torvalds 	scb->bus = scb->scsi_cmd->device->channel;
19301da177e4SLinus Torvalds 	scb->target_id = scb->scsi_cmd->device->id;
19311da177e4SLinus Torvalds 	scb->lun = scb->scsi_cmd->device->lun;
19321da177e4SLinus Torvalds 	scb->sg_len = 0;
19331da177e4SLinus Torvalds 	scb->data_len = 0;
19341da177e4SLinus Torvalds 	scb->flags = 0;
19351da177e4SLinus Torvalds 	scb->op_code = 0;
19361da177e4SLinus Torvalds 	scb->callback = ipsintr_done;
19371da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
19381da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
19391da177e4SLinus Torvalds 
19401da177e4SLinus Torvalds 	/* we don't support DCDB/READ/WRITE Scatter Gather */
19411da177e4SLinus Torvalds 	if ((scb->cmd.basic_io.op_code == IPS_CMD_READ_SG) ||
19421da177e4SLinus Torvalds 	    (scb->cmd.basic_io.op_code == IPS_CMD_WRITE_SG) ||
19431da177e4SLinus Torvalds 	    (scb->cmd.basic_io.op_code == IPS_CMD_DCDB_SG))
19441da177e4SLinus Torvalds 		return (0);
19451da177e4SLinus Torvalds 
19461da177e4SLinus Torvalds 	if (pt->CmdBSize) {
19471da177e4SLinus Torvalds 		scb->data_len = pt->CmdBSize;
19481da177e4SLinus Torvalds 		scb->data_busaddr = ha->ioctl_busaddr + sizeof (ips_passthru_t);
19491da177e4SLinus Torvalds 	} else {
19501da177e4SLinus Torvalds 		scb->data_busaddr = 0L;
19511da177e4SLinus Torvalds 	}
19521da177e4SLinus Torvalds 
19531da177e4SLinus Torvalds 	if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
19541da177e4SLinus Torvalds 		scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
19551da177e4SLinus Torvalds 							 (unsigned long) &scb->
19561da177e4SLinus Torvalds 							 dcdb -
19571da177e4SLinus Torvalds 							 (unsigned long) scb);
19581da177e4SLinus Torvalds 
19591da177e4SLinus Torvalds 	if (pt->CmdBSize) {
19601da177e4SLinus Torvalds 		if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
19611da177e4SLinus Torvalds 			scb->dcdb.buffer_pointer =
19621da177e4SLinus Torvalds 			    cpu_to_le32(scb->data_busaddr);
19631da177e4SLinus Torvalds 		else
19641da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_addr =
19651da177e4SLinus Torvalds 			    cpu_to_le32(scb->data_busaddr);
19661da177e4SLinus Torvalds 	}
19671da177e4SLinus Torvalds 
19681da177e4SLinus Torvalds 	/* set timeouts */
19691da177e4SLinus Torvalds 	if (pt->TimeOut) {
19701da177e4SLinus Torvalds 		scb->timeout = pt->TimeOut;
19711da177e4SLinus Torvalds 
19721da177e4SLinus Torvalds 		if (pt->TimeOut <= 10)
19731da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;
19741da177e4SLinus Torvalds 		else if (pt->TimeOut <= 60)
19751da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;
19761da177e4SLinus Torvalds 		else
19771da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;
19781da177e4SLinus Torvalds 	}
19791da177e4SLinus Torvalds 
19801da177e4SLinus Torvalds 	/* assume success */
19811da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
19821da177e4SLinus Torvalds 
19831da177e4SLinus Torvalds 	/* success */
19841da177e4SLinus Torvalds 	return (1);
19851da177e4SLinus Torvalds }
19861da177e4SLinus Torvalds 
19871da177e4SLinus Torvalds /****************************************************************************/
19881da177e4SLinus Torvalds /*                                                                          */
19891da177e4SLinus Torvalds /* Routine Name: ips_cleanup_passthru                                       */
19901da177e4SLinus Torvalds /*                                                                          */
19911da177e4SLinus Torvalds /* Routine Description:                                                     */
19921da177e4SLinus Torvalds /*                                                                          */
19931da177e4SLinus Torvalds /*   Cleanup after a passthru command                                       */
19941da177e4SLinus Torvalds /*                                                                          */
19951da177e4SLinus Torvalds /****************************************************************************/
19961da177e4SLinus Torvalds static void
19971da177e4SLinus Torvalds ips_cleanup_passthru(ips_ha_t * ha, ips_scb_t * scb)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	ips_passthru_t *pt;
20001da177e4SLinus Torvalds 
20011da177e4SLinus Torvalds 	METHOD_TRACE("ips_cleanup_passthru", 1);
20021da177e4SLinus Torvalds 
20032f4cf91cSFUJITA Tomonori 	if ((!scb) || (!scb->scsi_cmd) || (!scsi_sglist(scb->scsi_cmd))) {
20041da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
20051da177e4SLinus Torvalds 			  ips_name, ha->host_num);
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 		return;
20081da177e4SLinus Torvalds 	}
20091da177e4SLinus Torvalds 	pt = (ips_passthru_t *) ha->ioctl_data;
20101da177e4SLinus Torvalds 
20111da177e4SLinus Torvalds 	/* Copy data back to the user */
20121da177e4SLinus Torvalds 	if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)	/* Copy DCDB Back to Caller's Area */
20131da177e4SLinus Torvalds 		memcpy(&pt->CoppCP.dcdb, &scb->dcdb, sizeof (IPS_DCDB_TABLE));
20141da177e4SLinus Torvalds 
20151da177e4SLinus Torvalds 	pt->BasicStatus = scb->basic_status;
20161da177e4SLinus Torvalds 	pt->ExtendedStatus = scb->extended_status;
20171da177e4SLinus Torvalds 	pt->AdapterType = ha->ad_type;
20181da177e4SLinus Torvalds 
20198a694cc8SJeff Garzik 	if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD &&
20201da177e4SLinus Torvalds 	    (scb->cmd.flashfw.op_code == IPS_CMD_DOWNLOAD ||
20211da177e4SLinus Torvalds 	     scb->cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW))
20221da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, ha->ioctl_data, ha->ioctl_datasize);
20251da177e4SLinus Torvalds }
20261da177e4SLinus Torvalds 
20271da177e4SLinus Torvalds /****************************************************************************/
20281da177e4SLinus Torvalds /*                                                                          */
20291da177e4SLinus Torvalds /* Routine Name: ips_host_info                                              */
20301da177e4SLinus Torvalds /*                                                                          */
20311da177e4SLinus Torvalds /* Routine Description:                                                     */
20321da177e4SLinus Torvalds /*                                                                          */
20331da177e4SLinus Torvalds /*   The passthru interface for the driver                                  */
20341da177e4SLinus Torvalds /*                                                                          */
20351da177e4SLinus Torvalds /****************************************************************************/
20361da177e4SLinus Torvalds static int
2037aacce706SAl Viro ips_host_info(ips_ha_t *ha, struct seq_file *m)
20381da177e4SLinus Torvalds {
20391da177e4SLinus Torvalds 	METHOD_TRACE("ips_host_info", 1);
20401da177e4SLinus Torvalds 
2041aacce706SAl Viro 	seq_printf(m, "\nIBM ServeRAID General Information:\n\n");
20421da177e4SLinus Torvalds 
20431da177e4SLinus Torvalds 	if ((le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) &&
20441da177e4SLinus Torvalds 	    (le16_to_cpu(ha->nvram->adapter_type) != 0))
2045aacce706SAl Viro 		seq_printf(m, "\tController Type                   : %s\n",
20461da177e4SLinus Torvalds 			  ips_adapter_name[ha->ad_type - 1]);
20471da177e4SLinus Torvalds 	else
2048aacce706SAl Viro 		seq_printf(m,
20491da177e4SLinus Torvalds 			  "\tController Type                   : Unknown\n");
20501da177e4SLinus Torvalds 
20511da177e4SLinus Torvalds 	if (ha->io_addr)
2052aacce706SAl Viro 		seq_printf(m,
20531bb7109aSAl Viro 			  "\tIO region                         : 0x%x (%d bytes)\n",
20541da177e4SLinus Torvalds 			  ha->io_addr, ha->io_len);
20551da177e4SLinus Torvalds 
20561da177e4SLinus Torvalds 	if (ha->mem_addr) {
2057aacce706SAl Viro 		seq_printf(m,
20581bb7109aSAl Viro 			  "\tMemory region                     : 0x%x (%d bytes)\n",
20591da177e4SLinus Torvalds 			  ha->mem_addr, ha->mem_len);
2060aacce706SAl Viro 		seq_printf(m,
20611da177e4SLinus Torvalds 			  "\tShared memory address             : 0x%lx\n",
20621bb7109aSAl Viro 			  (unsigned long)ha->mem_ptr);
20631da177e4SLinus Torvalds 	}
20641da177e4SLinus Torvalds 
2065aacce706SAl Viro 	seq_printf(m, "\tIRQ number                        : %d\n", ha->pcidev->irq);
20661da177e4SLinus Torvalds 
20671da177e4SLinus Torvalds     /* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */
20681da177e4SLinus Torvalds     /* That keeps everything happy for "text" operations on the proc file.                    */
20691da177e4SLinus Torvalds 
20701da177e4SLinus Torvalds 	if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) {
20711da177e4SLinus Torvalds 	if (ha->nvram->bios_low[3] == 0) {
2072aacce706SAl Viro 		seq_printf(m,
20731da177e4SLinus Torvalds 			  "\tBIOS Version                      : %c%c%c%c%c%c%c\n",
20741da177e4SLinus Torvalds 			  ha->nvram->bios_high[0], ha->nvram->bios_high[1],
20751da177e4SLinus Torvalds 			  ha->nvram->bios_high[2], ha->nvram->bios_high[3],
20761da177e4SLinus Torvalds 			  ha->nvram->bios_low[0], ha->nvram->bios_low[1],
20771da177e4SLinus Torvalds 			  ha->nvram->bios_low[2]);
20781da177e4SLinus Torvalds 
20791da177e4SLinus Torvalds         } else {
2080aacce706SAl Viro 		seq_printf(m,
20811da177e4SLinus Torvalds 			  "\tBIOS Version                      : %c%c%c%c%c%c%c%c\n",
20821da177e4SLinus Torvalds 			  ha->nvram->bios_high[0], ha->nvram->bios_high[1],
20831da177e4SLinus Torvalds 			  ha->nvram->bios_high[2], ha->nvram->bios_high[3],
20841da177e4SLinus Torvalds 			  ha->nvram->bios_low[0], ha->nvram->bios_low[1],
20851da177e4SLinus Torvalds 			  ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
20861da177e4SLinus Torvalds         }
20871da177e4SLinus Torvalds 
20881da177e4SLinus Torvalds     }
20891da177e4SLinus Torvalds 
20901da177e4SLinus Torvalds     if (ha->enq->CodeBlkVersion[7] == 0) {
2091aacce706SAl Viro         seq_printf(m,
20921da177e4SLinus Torvalds 		  "\tFirmware Version                  : %c%c%c%c%c%c%c\n",
20931da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
20941da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
20951da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
20961da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[6]);
20971da177e4SLinus Torvalds     } else {
2098aacce706SAl Viro 	seq_printf(m,
20991da177e4SLinus Torvalds 		  "\tFirmware Version                  : %c%c%c%c%c%c%c%c\n",
21001da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
21011da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
21021da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
21031da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]);
21041da177e4SLinus Torvalds     }
21051da177e4SLinus Torvalds 
21061da177e4SLinus Torvalds     if (ha->enq->BootBlkVersion[7] == 0) {
2107aacce706SAl Viro         seq_printf(m,
21081da177e4SLinus Torvalds 		  "\tBoot Block Version                : %c%c%c%c%c%c%c\n",
21091da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
21101da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
21111da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
21121da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[6]);
21131da177e4SLinus Torvalds     } else {
2114aacce706SAl Viro         seq_printf(m,
21151da177e4SLinus Torvalds 		  "\tBoot Block Version                : %c%c%c%c%c%c%c%c\n",
21161da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
21171da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
21181da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
21191da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]);
21201da177e4SLinus Torvalds     }
21211da177e4SLinus Torvalds 
2122aacce706SAl Viro 	seq_printf(m, "\tDriver Version                    : %s%s\n",
21231da177e4SLinus Torvalds 		  IPS_VERSION_HIGH, IPS_VERSION_LOW);
21241da177e4SLinus Torvalds 
2125aacce706SAl Viro 	seq_printf(m, "\tDriver Build                      : %d\n",
21261da177e4SLinus Torvalds 		  IPS_BUILD_IDENT);
21271da177e4SLinus Torvalds 
2128aacce706SAl Viro 	seq_printf(m, "\tMax Physical Devices              : %d\n",
21291da177e4SLinus Torvalds 		  ha->enq->ucMaxPhysicalDevices);
2130aacce706SAl Viro 	seq_printf(m, "\tMax Active Commands               : %d\n",
21311da177e4SLinus Torvalds 		  ha->max_cmds);
2132aacce706SAl Viro 	seq_printf(m, "\tCurrent Queued Commands           : %d\n",
21331da177e4SLinus Torvalds 		  ha->scb_waitlist.count);
2134aacce706SAl Viro 	seq_printf(m, "\tCurrent Active Commands           : %d\n",
21351da177e4SLinus Torvalds 		  ha->scb_activelist.count - ha->num_ioctl);
2136aacce706SAl Viro 	seq_printf(m, "\tCurrent Queued PT Commands        : %d\n",
21371da177e4SLinus Torvalds 		  ha->copp_waitlist.count);
2138aacce706SAl Viro 	seq_printf(m, "\tCurrent Active PT Commands        : %d\n",
21391da177e4SLinus Torvalds 		  ha->num_ioctl);
21401da177e4SLinus Torvalds 
2141aacce706SAl Viro 	seq_printf(m, "\n");
21421da177e4SLinus Torvalds 
2143aacce706SAl Viro 	return 0;
21441da177e4SLinus Torvalds }
21451da177e4SLinus Torvalds 
21461da177e4SLinus Torvalds /****************************************************************************/
21471da177e4SLinus Torvalds /*                                                                          */
21481da177e4SLinus Torvalds /* Routine Name: ips_identify_controller                                    */
21491da177e4SLinus Torvalds /*                                                                          */
21501da177e4SLinus Torvalds /* Routine Description:                                                     */
21511da177e4SLinus Torvalds /*                                                                          */
21521da177e4SLinus Torvalds /*   Identify this controller                                               */
21531da177e4SLinus Torvalds /*                                                                          */
21541da177e4SLinus Torvalds /****************************************************************************/
21551da177e4SLinus Torvalds static void
21561da177e4SLinus Torvalds ips_identify_controller(ips_ha_t * ha)
21571da177e4SLinus Torvalds {
21581da177e4SLinus Torvalds 	METHOD_TRACE("ips_identify_controller", 1);
21591da177e4SLinus Torvalds 
21608a694cc8SJeff Garzik 	switch (ha->pcidev->device) {
21611da177e4SLinus Torvalds 	case IPS_DEVICEID_COPPERHEAD:
21628a694cc8SJeff Garzik 		if (ha->pcidev->revision <= IPS_REVID_SERVERAID) {
21631da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID;
21648a694cc8SJeff Garzik 		} else if (ha->pcidev->revision == IPS_REVID_SERVERAID2) {
21651da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID2;
21668a694cc8SJeff Garzik 		} else if (ha->pcidev->revision == IPS_REVID_NAVAJO) {
21671da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_NAVAJO;
21688a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision == IPS_REVID_SERVERAID2)
21691da177e4SLinus Torvalds 			   && (ha->slot_num == 0)) {
21701da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_KIOWA;
21718a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision >= IPS_REVID_CLARINETP1) &&
21728a694cc8SJeff Garzik 			   (ha->pcidev->revision <= IPS_REVID_CLARINETP3)) {
21731da177e4SLinus Torvalds 			if (ha->enq->ucMaxPhysicalDevices == 15)
21741da177e4SLinus Torvalds 				ha->ad_type = IPS_ADTYPE_SERVERAID3L;
21751da177e4SLinus Torvalds 			else
21761da177e4SLinus Torvalds 				ha->ad_type = IPS_ADTYPE_SERVERAID3;
21778a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision >= IPS_REVID_TROMBONE32) &&
21788a694cc8SJeff Garzik 			   (ha->pcidev->revision <= IPS_REVID_TROMBONE64)) {
21791da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4H;
21801da177e4SLinus Torvalds 		}
21811da177e4SLinus Torvalds 		break;
21821da177e4SLinus Torvalds 
21831da177e4SLinus Torvalds 	case IPS_DEVICEID_MORPHEUS:
21848a694cc8SJeff Garzik 		switch (ha->pcidev->subsystem_device) {
21851da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4L:
21861da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4L;
21871da177e4SLinus Torvalds 			break;
21881da177e4SLinus Torvalds 
21891da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4M:
21901da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4M;
21911da177e4SLinus Torvalds 			break;
21921da177e4SLinus Torvalds 
21931da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4MX:
21941da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4MX;
21951da177e4SLinus Torvalds 			break;
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4LX:
21981da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4LX;
21991da177e4SLinus Torvalds 			break;
22001da177e4SLinus Torvalds 
22011da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_5I2:
22021da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID5I2;
22031da177e4SLinus Torvalds 			break;
22041da177e4SLinus Torvalds 
22051da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_5I1:
22061da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID5I1;
22071da177e4SLinus Torvalds 			break;
22081da177e4SLinus Torvalds 		}
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds 		break;
22111da177e4SLinus Torvalds 
22121da177e4SLinus Torvalds 	case IPS_DEVICEID_MARCO:
22138a694cc8SJeff Garzik 		switch (ha->pcidev->subsystem_device) {
22141da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_6M:
22151da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID6M;
22161da177e4SLinus Torvalds 			break;
22171da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_6I:
22181da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID6I;
22191da177e4SLinus Torvalds 			break;
22201da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_7k:
22211da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID7k;
22221da177e4SLinus Torvalds 			break;
22231da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_7M:
22241da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID7M;
22251da177e4SLinus Torvalds 			break;
22261da177e4SLinus Torvalds 		}
22271da177e4SLinus Torvalds 		break;
22281da177e4SLinus Torvalds 	}
22291da177e4SLinus Torvalds }
22301da177e4SLinus Torvalds 
22311da177e4SLinus Torvalds /****************************************************************************/
22321da177e4SLinus Torvalds /*                                                                          */
22331da177e4SLinus Torvalds /* Routine Name: ips_get_bios_version                                       */
22341da177e4SLinus Torvalds /*                                                                          */
22351da177e4SLinus Torvalds /* Routine Description:                                                     */
22361da177e4SLinus Torvalds /*                                                                          */
22371da177e4SLinus Torvalds /*   Get the BIOS revision number                                           */
22381da177e4SLinus Torvalds /*                                                                          */
22391da177e4SLinus Torvalds /****************************************************************************/
22401da177e4SLinus Torvalds static void
22411da177e4SLinus Torvalds ips_get_bios_version(ips_ha_t * ha, int intr)
22421da177e4SLinus Torvalds {
22431da177e4SLinus Torvalds 	ips_scb_t *scb;
22441da177e4SLinus Torvalds 	int ret;
22451da177e4SLinus Torvalds 	uint8_t major;
22461da177e4SLinus Torvalds 	uint8_t minor;
22471da177e4SLinus Torvalds 	uint8_t subminor;
22481da177e4SLinus Torvalds 	uint8_t *buffer;
22491da177e4SLinus Torvalds 	char hexDigits[] =
22501da177e4SLinus Torvalds 	    { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
22511da177e4SLinus Torvalds      'D', 'E', 'F' };
22521da177e4SLinus Torvalds 
22531da177e4SLinus Torvalds 	METHOD_TRACE("ips_get_bios_version", 1);
22541da177e4SLinus Torvalds 
22551da177e4SLinus Torvalds 	major = 0;
22561da177e4SLinus Torvalds 	minor = 0;
22571da177e4SLinus Torvalds 
22581da177e4SLinus Torvalds 	strncpy(ha->bios_version, "       ?", 8);
22591da177e4SLinus Torvalds 
22608a694cc8SJeff Garzik 	if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD) {
22611da177e4SLinus Torvalds 		if (IPS_USE_MEMIO(ha)) {
22621da177e4SLinus Torvalds 			/* Memory Mapped I/O */
22631da177e4SLinus Torvalds 
22641da177e4SLinus Torvalds 			/* test 1st byte */
22651da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
22668a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22671da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22681da177e4SLinus Torvalds 
22691da177e4SLinus Torvalds 			if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
22701da177e4SLinus Torvalds 				return;
22711da177e4SLinus Torvalds 
22721da177e4SLinus Torvalds 			writel(1, ha->mem_ptr + IPS_REG_FLAP);
22738a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22741da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22751da177e4SLinus Torvalds 
22761da177e4SLinus Torvalds 			if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
22771da177e4SLinus Torvalds 				return;
22781da177e4SLinus Torvalds 
22791da177e4SLinus Torvalds 			/* Get Major version */
22801da177e4SLinus Torvalds 			writel(0x1FF, ha->mem_ptr + IPS_REG_FLAP);
22818a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22821da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22831da177e4SLinus Torvalds 
22841da177e4SLinus Torvalds 			major = readb(ha->mem_ptr + IPS_REG_FLDP);
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds 			/* Get Minor version */
22871da177e4SLinus Torvalds 			writel(0x1FE, ha->mem_ptr + IPS_REG_FLAP);
22888a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22891da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22901da177e4SLinus Torvalds 			minor = readb(ha->mem_ptr + IPS_REG_FLDP);
22911da177e4SLinus Torvalds 
22921da177e4SLinus Torvalds 			/* Get SubMinor version */
22931da177e4SLinus Torvalds 			writel(0x1FD, ha->mem_ptr + IPS_REG_FLAP);
22948a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22951da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22961da177e4SLinus Torvalds 			subminor = readb(ha->mem_ptr + IPS_REG_FLDP);
22971da177e4SLinus Torvalds 
22981da177e4SLinus Torvalds 		} else {
22991da177e4SLinus Torvalds 			/* Programmed I/O */
23001da177e4SLinus Torvalds 
23011da177e4SLinus Torvalds 			/* test 1st byte */
23021da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
23038a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23041da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23051da177e4SLinus Torvalds 
23061da177e4SLinus Torvalds 			if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
23071da177e4SLinus Torvalds 				return;
23081da177e4SLinus Torvalds 
2309db3cc200SJames Bottomley 			outl(1, ha->io_addr + IPS_REG_FLAP);
23108a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23111da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23121da177e4SLinus Torvalds 
23131da177e4SLinus Torvalds 			if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
23141da177e4SLinus Torvalds 				return;
23151da177e4SLinus Torvalds 
23161da177e4SLinus Torvalds 			/* Get Major version */
2317db3cc200SJames Bottomley 			outl(0x1FF, ha->io_addr + IPS_REG_FLAP);
23188a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23191da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23201da177e4SLinus Torvalds 
23211da177e4SLinus Torvalds 			major = inb(ha->io_addr + IPS_REG_FLDP);
23221da177e4SLinus Torvalds 
23231da177e4SLinus Torvalds 			/* Get Minor version */
2324db3cc200SJames Bottomley 			outl(0x1FE, ha->io_addr + IPS_REG_FLAP);
23258a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23261da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 			minor = inb(ha->io_addr + IPS_REG_FLDP);
23291da177e4SLinus Torvalds 
23301da177e4SLinus Torvalds 			/* Get SubMinor version */
2331db3cc200SJames Bottomley 			outl(0x1FD, ha->io_addr + IPS_REG_FLAP);
23328a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23331da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23341da177e4SLinus Torvalds 
23351da177e4SLinus Torvalds 			subminor = inb(ha->io_addr + IPS_REG_FLDP);
23361da177e4SLinus Torvalds 
23371da177e4SLinus Torvalds 		}
23381da177e4SLinus Torvalds 	} else {
23391da177e4SLinus Torvalds 		/* Morpheus Family - Send Command to the card */
23401da177e4SLinus Torvalds 
23411da177e4SLinus Torvalds 		buffer = ha->ioctl_data;
23421da177e4SLinus Torvalds 
23431da177e4SLinus Torvalds 		memset(buffer, 0, 0x1000);
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
23461da177e4SLinus Torvalds 
23471da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
23481da177e4SLinus Torvalds 
23491da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
23501da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_RW_BIOSFW;
23511da177e4SLinus Torvalds 
23521da177e4SLinus Torvalds 		scb->cmd.flashfw.op_code = IPS_CMD_RW_BIOSFW;
23531da177e4SLinus Torvalds 		scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
23541da177e4SLinus Torvalds 		scb->cmd.flashfw.type = 1;
23551da177e4SLinus Torvalds 		scb->cmd.flashfw.direction = 0;
23561da177e4SLinus Torvalds 		scb->cmd.flashfw.count = cpu_to_le32(0x800);
23571da177e4SLinus Torvalds 		scb->cmd.flashfw.total_packets = 1;
23581da177e4SLinus Torvalds 		scb->cmd.flashfw.packet_num = 0;
23591da177e4SLinus Torvalds 		scb->data_len = 0x1000;
23601da177e4SLinus Torvalds 		scb->cmd.flashfw.buffer_addr = ha->ioctl_busaddr;
23611da177e4SLinus Torvalds 
23621da177e4SLinus Torvalds 		/* issue the command */
23631da177e4SLinus Torvalds 		if (((ret =
23641da177e4SLinus Torvalds 		      ips_send_wait(ha, scb, ips_cmd_timeout,
23651da177e4SLinus Torvalds 				    intr)) == IPS_FAILURE)
23661da177e4SLinus Torvalds 		    || (ret == IPS_SUCCESS_IMM)
23671da177e4SLinus Torvalds 		    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
23681da177e4SLinus Torvalds 			/* Error occurred */
23691da177e4SLinus Torvalds 
23701da177e4SLinus Torvalds 			return;
23711da177e4SLinus Torvalds 		}
23721da177e4SLinus Torvalds 
23731da177e4SLinus Torvalds 		if ((buffer[0xC0] == 0x55) && (buffer[0xC1] == 0xAA)) {
23741da177e4SLinus Torvalds 			major = buffer[0x1ff + 0xC0];	/* Offset 0x1ff after the header (0xc0) */
23751da177e4SLinus Torvalds 			minor = buffer[0x1fe + 0xC0];	/* Offset 0x1fe after the header (0xc0) */
23761da177e4SLinus Torvalds 			subminor = buffer[0x1fd + 0xC0];	/* Offset 0x1fd after the header (0xc0) */
23771da177e4SLinus Torvalds 		} else {
23781da177e4SLinus Torvalds 			return;
23791da177e4SLinus Torvalds 		}
23801da177e4SLinus Torvalds 	}
23811da177e4SLinus Torvalds 
23821da177e4SLinus Torvalds 	ha->bios_version[0] = hexDigits[(major & 0xF0) >> 4];
23831da177e4SLinus Torvalds 	ha->bios_version[1] = '.';
23841da177e4SLinus Torvalds 	ha->bios_version[2] = hexDigits[major & 0x0F];
23851da177e4SLinus Torvalds 	ha->bios_version[3] = hexDigits[subminor];
23861da177e4SLinus Torvalds 	ha->bios_version[4] = '.';
23871da177e4SLinus Torvalds 	ha->bios_version[5] = hexDigits[(minor & 0xF0) >> 4];
23881da177e4SLinus Torvalds 	ha->bios_version[6] = hexDigits[minor & 0x0F];
23891da177e4SLinus Torvalds 	ha->bios_version[7] = 0;
23901da177e4SLinus Torvalds }
23911da177e4SLinus Torvalds 
23921da177e4SLinus Torvalds /****************************************************************************/
23931da177e4SLinus Torvalds /*                                                                          */
23941da177e4SLinus Torvalds /* Routine Name: ips_hainit                                                 */
23951da177e4SLinus Torvalds /*                                                                          */
23961da177e4SLinus Torvalds /* Routine Description:                                                     */
23971da177e4SLinus Torvalds /*                                                                          */
23981da177e4SLinus Torvalds /*   Initialize the controller                                              */
23991da177e4SLinus Torvalds /*                                                                          */
24001da177e4SLinus Torvalds /* NOTE: Assumes to be called from with a lock                              */
24011da177e4SLinus Torvalds /*                                                                          */
24021da177e4SLinus Torvalds /****************************************************************************/
24031da177e4SLinus Torvalds static int
24041da177e4SLinus Torvalds ips_hainit(ips_ha_t * ha)
24051da177e4SLinus Torvalds {
24061da177e4SLinus Torvalds 	int i;
24071da177e4SLinus Torvalds 	struct timeval tv;
24081da177e4SLinus Torvalds 
24091da177e4SLinus Torvalds 	METHOD_TRACE("ips_hainit", 1);
24101da177e4SLinus Torvalds 
24111da177e4SLinus Torvalds 	if (!ha)
24121da177e4SLinus Torvalds 		return (0);
24131da177e4SLinus Torvalds 
24141da177e4SLinus Torvalds 	if (ha->func.statinit)
24151da177e4SLinus Torvalds 		(*ha->func.statinit) (ha);
24161da177e4SLinus Torvalds 
24171da177e4SLinus Torvalds 	if (ha->func.enableint)
24181da177e4SLinus Torvalds 		(*ha->func.enableint) (ha);
24191da177e4SLinus Torvalds 
24201da177e4SLinus Torvalds 	/* Send FFDC */
24211da177e4SLinus Torvalds 	ha->reset_count = 1;
24221da177e4SLinus Torvalds 	do_gettimeofday(&tv);
24231da177e4SLinus Torvalds 	ha->last_ffdc = tv.tv_sec;
24241da177e4SLinus Torvalds 	ips_ffdc_reset(ha, IPS_INTR_IORL);
24251da177e4SLinus Torvalds 
24261da177e4SLinus Torvalds 	if (!ips_read_config(ha, IPS_INTR_IORL)) {
24271da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24281da177e4SLinus Torvalds 			   "unable to read config from controller.\n");
24291da177e4SLinus Torvalds 
24301da177e4SLinus Torvalds 		return (0);
24311da177e4SLinus Torvalds 	}
24321da177e4SLinus Torvalds 	/* end if */
24331da177e4SLinus Torvalds 	if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
24341da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24351da177e4SLinus Torvalds 			   "unable to read controller status.\n");
24361da177e4SLinus Torvalds 
24371da177e4SLinus Torvalds 		return (0);
24381da177e4SLinus Torvalds 	}
24391da177e4SLinus Torvalds 
24401da177e4SLinus Torvalds 	/* Identify this controller */
24411da177e4SLinus Torvalds 	ips_identify_controller(ha);
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds 	if (!ips_read_subsystem_parameters(ha, IPS_INTR_IORL)) {
24441da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24451da177e4SLinus Torvalds 			   "unable to read subsystem parameters.\n");
24461da177e4SLinus Torvalds 
24471da177e4SLinus Torvalds 		return (0);
24481da177e4SLinus Torvalds 	}
24491da177e4SLinus Torvalds 
24501da177e4SLinus Torvalds 	/* write nvram user page 5 */
24511da177e4SLinus Torvalds 	if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
24521da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24531da177e4SLinus Torvalds 			   "unable to write driver info to controller.\n");
24541da177e4SLinus Torvalds 
24551da177e4SLinus Torvalds 		return (0);
24561da177e4SLinus Torvalds 	}
24571da177e4SLinus Torvalds 
24581da177e4SLinus Torvalds 	/* If there are Logical Drives and a Reset Occurred, then an EraseStripeLock is Needed */
24591da177e4SLinus Torvalds 	if ((ha->conf->ucLogDriveCount > 0) && (ha->requires_esl == 1))
24601da177e4SLinus Torvalds 		ips_clear_adapter(ha, IPS_INTR_IORL);
24611da177e4SLinus Torvalds 
24621da177e4SLinus Torvalds 	/* set limits on SID, LUN, BUS */
24631da177e4SLinus Torvalds 	ha->ntargets = IPS_MAX_TARGETS + 1;
24641da177e4SLinus Torvalds 	ha->nlun = 1;
24651da177e4SLinus Torvalds 	ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
24661da177e4SLinus Torvalds 
24671da177e4SLinus Torvalds 	switch (ha->conf->logical_drive[0].ucStripeSize) {
24681da177e4SLinus Torvalds 	case 4:
24691da177e4SLinus Torvalds 		ha->max_xfer = 0x10000;
24701da177e4SLinus Torvalds 		break;
24711da177e4SLinus Torvalds 
24721da177e4SLinus Torvalds 	case 5:
24731da177e4SLinus Torvalds 		ha->max_xfer = 0x20000;
24741da177e4SLinus Torvalds 		break;
24751da177e4SLinus Torvalds 
24761da177e4SLinus Torvalds 	case 6:
24771da177e4SLinus Torvalds 		ha->max_xfer = 0x40000;
24781da177e4SLinus Torvalds 		break;
24791da177e4SLinus Torvalds 
24801da177e4SLinus Torvalds 	case 7:
24811da177e4SLinus Torvalds 	default:
24821da177e4SLinus Torvalds 		ha->max_xfer = 0x80000;
24831da177e4SLinus Torvalds 		break;
24841da177e4SLinus Torvalds 	}
24851da177e4SLinus Torvalds 
24861da177e4SLinus Torvalds 	/* setup max concurrent commands */
24871da177e4SLinus Torvalds 	if (le32_to_cpu(ha->subsys->param[4]) & 0x1) {
24881da177e4SLinus Torvalds 		/* Use the new method */
24891da177e4SLinus Torvalds 		ha->max_cmds = ha->enq->ucConcurrentCmdCount;
24901da177e4SLinus Torvalds 	} else {
24911da177e4SLinus Torvalds 		/* use the old method */
24921da177e4SLinus Torvalds 		switch (ha->conf->logical_drive[0].ucStripeSize) {
24931da177e4SLinus Torvalds 		case 4:
24941da177e4SLinus Torvalds 			ha->max_cmds = 32;
24951da177e4SLinus Torvalds 			break;
24961da177e4SLinus Torvalds 
24971da177e4SLinus Torvalds 		case 5:
24981da177e4SLinus Torvalds 			ha->max_cmds = 16;
24991da177e4SLinus Torvalds 			break;
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds 		case 6:
25021da177e4SLinus Torvalds 			ha->max_cmds = 8;
25031da177e4SLinus Torvalds 			break;
25041da177e4SLinus Torvalds 
25051da177e4SLinus Torvalds 		case 7:
25061da177e4SLinus Torvalds 		default:
25071da177e4SLinus Torvalds 			ha->max_cmds = 4;
25081da177e4SLinus Torvalds 			break;
25091da177e4SLinus Torvalds 		}
25101da177e4SLinus Torvalds 	}
25111da177e4SLinus Torvalds 
25121da177e4SLinus Torvalds 	/* Limit the Active Commands on a Lite Adapter */
25131da177e4SLinus Torvalds 	if ((ha->ad_type == IPS_ADTYPE_SERVERAID3L) ||
25141da177e4SLinus Torvalds 	    (ha->ad_type == IPS_ADTYPE_SERVERAID4L) ||
25151da177e4SLinus Torvalds 	    (ha->ad_type == IPS_ADTYPE_SERVERAID4LX)) {
25161da177e4SLinus Torvalds 		if ((ha->max_cmds > MaxLiteCmds) && (MaxLiteCmds))
25171da177e4SLinus Torvalds 			ha->max_cmds = MaxLiteCmds;
25181da177e4SLinus Torvalds 	}
25191da177e4SLinus Torvalds 
25201da177e4SLinus Torvalds 	/* set controller IDs */
25211da177e4SLinus Torvalds 	ha->ha_id[0] = IPS_ADAPTER_ID;
25221da177e4SLinus Torvalds 	for (i = 1; i < ha->nbus; i++) {
25231da177e4SLinus Torvalds 		ha->ha_id[i] = ha->conf->init_id[i - 1] & 0x1f;
25241da177e4SLinus Torvalds 		ha->dcdb_active[i - 1] = 0;
25251da177e4SLinus Torvalds 	}
25261da177e4SLinus Torvalds 
25271da177e4SLinus Torvalds 	return (1);
25281da177e4SLinus Torvalds }
25291da177e4SLinus Torvalds 
25301da177e4SLinus Torvalds /****************************************************************************/
25311da177e4SLinus Torvalds /*                                                                          */
25321da177e4SLinus Torvalds /* Routine Name: ips_next                                                   */
25331da177e4SLinus Torvalds /*                                                                          */
25341da177e4SLinus Torvalds /* Routine Description:                                                     */
25351da177e4SLinus Torvalds /*                                                                          */
25361da177e4SLinus Torvalds /*   Take the next command off the queue and send it to the controller      */
25371da177e4SLinus Torvalds /*                                                                          */
25381da177e4SLinus Torvalds /****************************************************************************/
25391da177e4SLinus Torvalds static void
25401da177e4SLinus Torvalds ips_next(ips_ha_t * ha, int intr)
25411da177e4SLinus Torvalds {
25421da177e4SLinus Torvalds 	ips_scb_t *scb;
25431516b55dSHenne 	struct scsi_cmnd *SC;
25441516b55dSHenne 	struct scsi_cmnd *p;
25451516b55dSHenne 	struct scsi_cmnd *q;
25461da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
25471da177e4SLinus Torvalds 	int ret;
25481da177e4SLinus Torvalds 	struct Scsi_Host *host;
25491da177e4SLinus Torvalds 	METHOD_TRACE("ips_next", 1);
25501da177e4SLinus Torvalds 
25511da177e4SLinus Torvalds 	if (!ha)
25521da177e4SLinus Torvalds 		return;
25531da177e4SLinus Torvalds 	host = ips_sh[ha->host_num];
25541da177e4SLinus Torvalds 	/*
25551da177e4SLinus Torvalds 	 * Block access to the queue function so
25561da177e4SLinus Torvalds 	 * this command won't time out
25571da177e4SLinus Torvalds 	 */
25581da177e4SLinus Torvalds 	if (intr == IPS_INTR_ON)
2559c6a6c81cSAdrian Bunk 		spin_lock(host->host_lock);
25601da177e4SLinus Torvalds 
25611da177e4SLinus Torvalds 	if ((ha->subsys->param[3] & 0x300000)
25621da177e4SLinus Torvalds 	    && (ha->scb_activelist.count == 0)) {
25631da177e4SLinus Torvalds 		struct timeval tv;
25641da177e4SLinus Torvalds 
25651da177e4SLinus Torvalds 		do_gettimeofday(&tv);
25661da177e4SLinus Torvalds 
25671da177e4SLinus Torvalds 		if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) {
25681da177e4SLinus Torvalds 			ha->last_ffdc = tv.tv_sec;
25691da177e4SLinus Torvalds 			ips_ffdc_time(ha);
25701da177e4SLinus Torvalds 		}
25711da177e4SLinus Torvalds 	}
25721da177e4SLinus Torvalds 
25731da177e4SLinus Torvalds 	/*
25741da177e4SLinus Torvalds 	 * Send passthru commands
25751da177e4SLinus Torvalds 	 * These have priority over normal I/O
25761da177e4SLinus Torvalds 	 * but shouldn't affect performance too much
25771da177e4SLinus Torvalds 	 * since we limit the number that can be active
25781da177e4SLinus Torvalds 	 * on the card at any one time
25791da177e4SLinus Torvalds 	 */
25801da177e4SLinus Torvalds 	while ((ha->num_ioctl < IPS_MAX_IOCTL) &&
25811da177e4SLinus Torvalds 	       (ha->copp_waitlist.head) && (scb = ips_getscb(ha))) {
25821da177e4SLinus Torvalds 
25831da177e4SLinus Torvalds 		item = ips_removeq_copp_head(&ha->copp_waitlist);
25841da177e4SLinus Torvalds 		ha->num_ioctl++;
25851da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2586c6a6c81cSAdrian Bunk 			spin_unlock(host->host_lock);
25871da177e4SLinus Torvalds 		scb->scsi_cmd = item->scsi_cmd;
25881da177e4SLinus Torvalds 		kfree(item);
25891da177e4SLinus Torvalds 
25901da177e4SLinus Torvalds 		ret = ips_make_passthru(ha, scb->scsi_cmd, scb, intr);
25911da177e4SLinus Torvalds 
25921da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2593c6a6c81cSAdrian Bunk 			spin_lock(host->host_lock);
25941da177e4SLinus Torvalds 		switch (ret) {
25951da177e4SLinus Torvalds 		case IPS_FAILURE:
25961da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
25971da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
25981da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
25991da177e4SLinus Torvalds 			}
26001da177e4SLinus Torvalds 
26011da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26021da177e4SLinus Torvalds 			break;
26031da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
26041da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
26051da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_OK << 16;
26061da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
26071da177e4SLinus Torvalds 			}
26081da177e4SLinus Torvalds 
26091da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26101da177e4SLinus Torvalds 			break;
26111da177e4SLinus Torvalds 		default:
26121da177e4SLinus Torvalds 			break;
26131da177e4SLinus Torvalds 		}		/* end case */
26141da177e4SLinus Torvalds 
26151da177e4SLinus Torvalds 		if (ret != IPS_SUCCESS) {
26161da177e4SLinus Torvalds 			ha->num_ioctl--;
26171da177e4SLinus Torvalds 			continue;
26181da177e4SLinus Torvalds 		}
26191da177e4SLinus Torvalds 
26201da177e4SLinus Torvalds 		ret = ips_send_cmd(ha, scb);
26211da177e4SLinus Torvalds 
26221da177e4SLinus Torvalds 		if (ret == IPS_SUCCESS)
26231da177e4SLinus Torvalds 			ips_putq_scb_head(&ha->scb_activelist, scb);
26241da177e4SLinus Torvalds 		else
26251da177e4SLinus Torvalds 			ha->num_ioctl--;
26261da177e4SLinus Torvalds 
26271da177e4SLinus Torvalds 		switch (ret) {
26281da177e4SLinus Torvalds 		case IPS_FAILURE:
26291da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
26301da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
26311da177e4SLinus Torvalds 			}
26321da177e4SLinus Torvalds 
26331da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26341da177e4SLinus Torvalds 			break;
26351da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
26361da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26371da177e4SLinus Torvalds 			break;
26381da177e4SLinus Torvalds 		default:
26391da177e4SLinus Torvalds 			break;
26401da177e4SLinus Torvalds 		}		/* end case */
26411da177e4SLinus Torvalds 
26421da177e4SLinus Torvalds 	}
26431da177e4SLinus Torvalds 
26441da177e4SLinus Torvalds 	/*
26451da177e4SLinus Torvalds 	 * Send "Normal" I/O commands
26461da177e4SLinus Torvalds 	 */
26471da177e4SLinus Torvalds 
26481da177e4SLinus Torvalds 	p = ha->scb_waitlist.head;
26491da177e4SLinus Torvalds 	while ((p) && (scb = ips_getscb(ha))) {
2650422c0d61SJeff Garzik 		if ((scmd_channel(p) > 0)
26511da177e4SLinus Torvalds 		    && (ha->
2652422c0d61SJeff Garzik 			dcdb_active[scmd_channel(p) -
2653422c0d61SJeff Garzik 				    1] & (1 << scmd_id(p)))) {
26541da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26551516b55dSHenne 			p = (struct scsi_cmnd *) p->host_scribble;
26561da177e4SLinus Torvalds 			continue;
26571da177e4SLinus Torvalds 		}
26581da177e4SLinus Torvalds 
26591da177e4SLinus Torvalds 		q = p;
26601da177e4SLinus Torvalds 		SC = ips_removeq_wait(&ha->scb_waitlist, q);
26611da177e4SLinus Torvalds 
26621da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2663c6a6c81cSAdrian Bunk 			spin_unlock(host->host_lock);	/* Unlock HA after command is taken off queue */
26641da177e4SLinus Torvalds 
26651da177e4SLinus Torvalds 		SC->result = DID_OK;
26661da177e4SLinus Torvalds 		SC->host_scribble = NULL;
26671da177e4SLinus Torvalds 
26681da177e4SLinus Torvalds 		scb->target_id = SC->device->id;
26691da177e4SLinus Torvalds 		scb->lun = SC->device->lun;
26701da177e4SLinus Torvalds 		scb->bus = SC->device->channel;
26711da177e4SLinus Torvalds 		scb->scsi_cmd = SC;
26721da177e4SLinus Torvalds 		scb->breakup = 0;
26731da177e4SLinus Torvalds 		scb->data_len = 0;
26741da177e4SLinus Torvalds 		scb->callback = ipsintr_done;
26751da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
26761da177e4SLinus Torvalds 		memset(&scb->cmd, 0, 16);
26771da177e4SLinus Torvalds 
26781da177e4SLinus Torvalds 		/* copy in the CDB */
26791da177e4SLinus Torvalds 		memcpy(scb->cdb, SC->cmnd, SC->cmd_len);
26801da177e4SLinus Torvalds 
26812f4cf91cSFUJITA Tomonori                 scb->sg_count = scsi_dma_map(SC);
26822f4cf91cSFUJITA Tomonori                 BUG_ON(scb->sg_count < 0);
26832f4cf91cSFUJITA Tomonori 		if (scb->sg_count) {
26841da177e4SLinus Torvalds 			struct scatterlist *sg;
26851da177e4SLinus Torvalds 			int i;
26861da177e4SLinus Torvalds 
26871da177e4SLinus Torvalds 			scb->flags |= IPS_SCB_MAP_SG;
26882f4cf91cSFUJITA Tomonori 
26892f4cf91cSFUJITA Tomonori                         scsi_for_each_sg(SC, sg, scb->sg_count, i) {
26901da177e4SLinus Torvalds 				if (ips_fill_scb_sg_single
26912f4cf91cSFUJITA Tomonori 				    (ha, sg_dma_address(sg), scb, i,
26922f4cf91cSFUJITA Tomonori 				     sg_dma_len(sg)) < 0)
26931da177e4SLinus Torvalds 					break;
26941da177e4SLinus Torvalds 			}
26951da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
26961da177e4SLinus Torvalds 		} else {
26971da177e4SLinus Torvalds                         scb->data_busaddr = 0L;
26981da177e4SLinus Torvalds                         scb->sg_len = 0;
26991da177e4SLinus Torvalds                         scb->data_len = 0;
27001da177e4SLinus Torvalds                         scb->dcdb.transfer_length = 0;
27011da177e4SLinus Torvalds 		}
27021da177e4SLinus Torvalds 
27031da177e4SLinus Torvalds 		scb->dcdb.cmd_attribute =
27041da177e4SLinus Torvalds 		    ips_command_direction[scb->scsi_cmd->cmnd[0]];
27051da177e4SLinus Torvalds 
27061da177e4SLinus Torvalds 		/* Allow a WRITE BUFFER Command to Have no Data */
27071da177e4SLinus Torvalds 		/* This is Used by Tape Flash Utilites          */
27082f277d6aSJeff Garzik 		if ((scb->scsi_cmd->cmnd[0] == WRITE_BUFFER) &&
27092f277d6aSJeff Garzik 				(scb->data_len == 0))
27101da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute = 0;
27111da177e4SLinus Torvalds 
27121da177e4SLinus Torvalds 		if (!(scb->dcdb.cmd_attribute & 0x3))
27131da177e4SLinus Torvalds 			scb->dcdb.transfer_length = 0;
27141da177e4SLinus Torvalds 
27151da177e4SLinus Torvalds 		if (scb->data_len >= IPS_MAX_XFER) {
27161da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
27171da177e4SLinus Torvalds 			scb->dcdb.transfer_length = 0;
27181da177e4SLinus Torvalds 		}
27191da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2720c6a6c81cSAdrian Bunk 			spin_lock(host->host_lock);
27211da177e4SLinus Torvalds 
27221da177e4SLinus Torvalds 		ret = ips_send_cmd(ha, scb);
27231da177e4SLinus Torvalds 
27241da177e4SLinus Torvalds 		switch (ret) {
27251da177e4SLinus Torvalds 		case IPS_SUCCESS:
27261da177e4SLinus Torvalds 			ips_putq_scb_head(&ha->scb_activelist, scb);
27271da177e4SLinus Torvalds 			break;
27281da177e4SLinus Torvalds 		case IPS_FAILURE:
27291da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
27301da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
27311da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
27321da177e4SLinus Torvalds 			}
27331da177e4SLinus Torvalds 
27341da177e4SLinus Torvalds 			if (scb->bus)
27351da177e4SLinus Torvalds 				ha->dcdb_active[scb->bus - 1] &=
27361da177e4SLinus Torvalds 				    ~(1 << scb->target_id);
27371da177e4SLinus Torvalds 
27381da177e4SLinus Torvalds 			ips_freescb(ha, scb);
27391da177e4SLinus Torvalds 			break;
27401da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
27411da177e4SLinus Torvalds 			if (scb->scsi_cmd)
27421da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
27431da177e4SLinus Torvalds 
27441da177e4SLinus Torvalds 			if (scb->bus)
27451da177e4SLinus Torvalds 				ha->dcdb_active[scb->bus - 1] &=
27461da177e4SLinus Torvalds 				    ~(1 << scb->target_id);
27471da177e4SLinus Torvalds 
27481da177e4SLinus Torvalds 			ips_freescb(ha, scb);
27491da177e4SLinus Torvalds 			break;
27501da177e4SLinus Torvalds 		default:
27511da177e4SLinus Torvalds 			break;
27521da177e4SLinus Torvalds 		}		/* end case */
27531da177e4SLinus Torvalds 
27541516b55dSHenne 		p = (struct scsi_cmnd *) p->host_scribble;
27551da177e4SLinus Torvalds 
27561da177e4SLinus Torvalds 	}			/* end while */
27571da177e4SLinus Torvalds 
27581da177e4SLinus Torvalds 	if (intr == IPS_INTR_ON)
2759c6a6c81cSAdrian Bunk 		spin_unlock(host->host_lock);
27601da177e4SLinus Torvalds }
27611da177e4SLinus Torvalds 
27621da177e4SLinus Torvalds /****************************************************************************/
27631da177e4SLinus Torvalds /*                                                                          */
27641da177e4SLinus Torvalds /* Routine Name: ips_putq_scb_head                                          */
27651da177e4SLinus Torvalds /*                                                                          */
27661da177e4SLinus Torvalds /* Routine Description:                                                     */
27671da177e4SLinus Torvalds /*                                                                          */
27681da177e4SLinus Torvalds /*   Add an item to the head of the queue                                   */
27691da177e4SLinus Torvalds /*                                                                          */
27701da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
27711da177e4SLinus Torvalds /*                                                                          */
27721da177e4SLinus Torvalds /****************************************************************************/
27731da177e4SLinus Torvalds static void
27741da177e4SLinus Torvalds ips_putq_scb_head(ips_scb_queue_t * queue, ips_scb_t * item)
27751da177e4SLinus Torvalds {
27761da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_scb_head", 1);
27771da177e4SLinus Torvalds 
27781da177e4SLinus Torvalds 	if (!item)
27791da177e4SLinus Torvalds 		return;
27801da177e4SLinus Torvalds 
27811da177e4SLinus Torvalds 	item->q_next = queue->head;
27821da177e4SLinus Torvalds 	queue->head = item;
27831da177e4SLinus Torvalds 
27841da177e4SLinus Torvalds 	if (!queue->tail)
27851da177e4SLinus Torvalds 		queue->tail = item;
27861da177e4SLinus Torvalds 
27871da177e4SLinus Torvalds 	queue->count++;
27881da177e4SLinus Torvalds }
27891da177e4SLinus Torvalds 
27901da177e4SLinus Torvalds /****************************************************************************/
27911da177e4SLinus Torvalds /*                                                                          */
27921da177e4SLinus Torvalds /* Routine Name: ips_removeq_scb_head                                       */
27931da177e4SLinus Torvalds /*                                                                          */
27941da177e4SLinus Torvalds /* Routine Description:                                                     */
27951da177e4SLinus Torvalds /*                                                                          */
27961da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
27971da177e4SLinus Torvalds /*                                                                          */
27981da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
27991da177e4SLinus Torvalds /*                                                                          */
28001da177e4SLinus Torvalds /****************************************************************************/
28011da177e4SLinus Torvalds static ips_scb_t *
28021da177e4SLinus Torvalds ips_removeq_scb_head(ips_scb_queue_t * queue)
28031da177e4SLinus Torvalds {
28041da177e4SLinus Torvalds 	ips_scb_t *item;
28051da177e4SLinus Torvalds 
28061da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_scb_head", 1);
28071da177e4SLinus Torvalds 
28081da177e4SLinus Torvalds 	item = queue->head;
28091da177e4SLinus Torvalds 
28101da177e4SLinus Torvalds 	if (!item) {
28111da177e4SLinus Torvalds 		return (NULL);
28121da177e4SLinus Torvalds 	}
28131da177e4SLinus Torvalds 
28141da177e4SLinus Torvalds 	queue->head = item->q_next;
28151da177e4SLinus Torvalds 	item->q_next = NULL;
28161da177e4SLinus Torvalds 
28171da177e4SLinus Torvalds 	if (queue->tail == item)
28181da177e4SLinus Torvalds 		queue->tail = NULL;
28191da177e4SLinus Torvalds 
28201da177e4SLinus Torvalds 	queue->count--;
28211da177e4SLinus Torvalds 
28221da177e4SLinus Torvalds 	return (item);
28231da177e4SLinus Torvalds }
28241da177e4SLinus Torvalds 
28251da177e4SLinus Torvalds /****************************************************************************/
28261da177e4SLinus Torvalds /*                                                                          */
28271da177e4SLinus Torvalds /* Routine Name: ips_removeq_scb                                            */
28281da177e4SLinus Torvalds /*                                                                          */
28291da177e4SLinus Torvalds /* Routine Description:                                                     */
28301da177e4SLinus Torvalds /*                                                                          */
28311da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
28321da177e4SLinus Torvalds /*                                                                          */
28331da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
28341da177e4SLinus Torvalds /*                                                                          */
28351da177e4SLinus Torvalds /****************************************************************************/
28361da177e4SLinus Torvalds static ips_scb_t *
28371da177e4SLinus Torvalds ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item)
28381da177e4SLinus Torvalds {
28391da177e4SLinus Torvalds 	ips_scb_t *p;
28401da177e4SLinus Torvalds 
28411da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_scb", 1);
28421da177e4SLinus Torvalds 
28431da177e4SLinus Torvalds 	if (!item)
28441da177e4SLinus Torvalds 		return (NULL);
28451da177e4SLinus Torvalds 
28461da177e4SLinus Torvalds 	if (item == queue->head) {
28471da177e4SLinus Torvalds 		return (ips_removeq_scb_head(queue));
28481da177e4SLinus Torvalds 	}
28491da177e4SLinus Torvalds 
28501da177e4SLinus Torvalds 	p = queue->head;
28511da177e4SLinus Torvalds 
28521da177e4SLinus Torvalds 	while ((p) && (item != p->q_next))
28531da177e4SLinus Torvalds 		p = p->q_next;
28541da177e4SLinus Torvalds 
28551da177e4SLinus Torvalds 	if (p) {
28561da177e4SLinus Torvalds 		/* found a match */
28571da177e4SLinus Torvalds 		p->q_next = item->q_next;
28581da177e4SLinus Torvalds 
28591da177e4SLinus Torvalds 		if (!item->q_next)
28601da177e4SLinus Torvalds 			queue->tail = p;
28611da177e4SLinus Torvalds 
28621da177e4SLinus Torvalds 		item->q_next = NULL;
28631da177e4SLinus Torvalds 		queue->count--;
28641da177e4SLinus Torvalds 
28651da177e4SLinus Torvalds 		return (item);
28661da177e4SLinus Torvalds 	}
28671da177e4SLinus Torvalds 
28681da177e4SLinus Torvalds 	return (NULL);
28691da177e4SLinus Torvalds }
28701da177e4SLinus Torvalds 
28711da177e4SLinus Torvalds /****************************************************************************/
28721da177e4SLinus Torvalds /*                                                                          */
28731da177e4SLinus Torvalds /* Routine Name: ips_putq_wait_tail                                         */
28741da177e4SLinus Torvalds /*                                                                          */
28751da177e4SLinus Torvalds /* Routine Description:                                                     */
28761da177e4SLinus Torvalds /*                                                                          */
28771da177e4SLinus Torvalds /*   Add an item to the tail of the queue                                   */
28781da177e4SLinus Torvalds /*                                                                          */
28791da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
28801da177e4SLinus Torvalds /*                                                                          */
28811da177e4SLinus Torvalds /****************************************************************************/
28821516b55dSHenne static void ips_putq_wait_tail(ips_wait_queue_t *queue, struct scsi_cmnd *item)
28831da177e4SLinus Torvalds {
28841da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_wait_tail", 1);
28851da177e4SLinus Torvalds 
28861da177e4SLinus Torvalds 	if (!item)
28871da177e4SLinus Torvalds 		return;
28881da177e4SLinus Torvalds 
28891da177e4SLinus Torvalds 	item->host_scribble = NULL;
28901da177e4SLinus Torvalds 
28911da177e4SLinus Torvalds 	if (queue->tail)
28921da177e4SLinus Torvalds 		queue->tail->host_scribble = (char *) item;
28931da177e4SLinus Torvalds 
28941da177e4SLinus Torvalds 	queue->tail = item;
28951da177e4SLinus Torvalds 
28961da177e4SLinus Torvalds 	if (!queue->head)
28971da177e4SLinus Torvalds 		queue->head = item;
28981da177e4SLinus Torvalds 
28991da177e4SLinus Torvalds 	queue->count++;
29001da177e4SLinus Torvalds }
29011da177e4SLinus Torvalds 
29021da177e4SLinus Torvalds /****************************************************************************/
29031da177e4SLinus Torvalds /*                                                                          */
29041da177e4SLinus Torvalds /* Routine Name: ips_removeq_wait_head                                      */
29051da177e4SLinus Torvalds /*                                                                          */
29061da177e4SLinus Torvalds /* Routine Description:                                                     */
29071da177e4SLinus Torvalds /*                                                                          */
29081da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
29091da177e4SLinus Torvalds /*                                                                          */
29101da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
29111da177e4SLinus Torvalds /*                                                                          */
29121da177e4SLinus Torvalds /****************************************************************************/
29131516b55dSHenne static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *queue)
29141da177e4SLinus Torvalds {
29151516b55dSHenne 	struct scsi_cmnd *item;
29161da177e4SLinus Torvalds 
29171da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_wait_head", 1);
29181da177e4SLinus Torvalds 
29191da177e4SLinus Torvalds 	item = queue->head;
29201da177e4SLinus Torvalds 
29211da177e4SLinus Torvalds 	if (!item) {
29221da177e4SLinus Torvalds 		return (NULL);
29231da177e4SLinus Torvalds 	}
29241da177e4SLinus Torvalds 
29251516b55dSHenne 	queue->head = (struct scsi_cmnd *) item->host_scribble;
29261da177e4SLinus Torvalds 	item->host_scribble = NULL;
29271da177e4SLinus Torvalds 
29281da177e4SLinus Torvalds 	if (queue->tail == item)
29291da177e4SLinus Torvalds 		queue->tail = NULL;
29301da177e4SLinus Torvalds 
29311da177e4SLinus Torvalds 	queue->count--;
29321da177e4SLinus Torvalds 
29331da177e4SLinus Torvalds 	return (item);
29341da177e4SLinus Torvalds }
29351da177e4SLinus Torvalds 
29361da177e4SLinus Torvalds /****************************************************************************/
29371da177e4SLinus Torvalds /*                                                                          */
29381da177e4SLinus Torvalds /* Routine Name: ips_removeq_wait                                           */
29391da177e4SLinus Torvalds /*                                                                          */
29401da177e4SLinus Torvalds /* Routine Description:                                                     */
29411da177e4SLinus Torvalds /*                                                                          */
29421da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
29431da177e4SLinus Torvalds /*                                                                          */
29441da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
29451da177e4SLinus Torvalds /*                                                                          */
29461da177e4SLinus Torvalds /****************************************************************************/
29471516b55dSHenne static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *queue,
29481516b55dSHenne 					  struct scsi_cmnd *item)
29491da177e4SLinus Torvalds {
29501516b55dSHenne 	struct scsi_cmnd *p;
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_wait", 1);
29531da177e4SLinus Torvalds 
29541da177e4SLinus Torvalds 	if (!item)
29551da177e4SLinus Torvalds 		return (NULL);
29561da177e4SLinus Torvalds 
29571da177e4SLinus Torvalds 	if (item == queue->head) {
29581da177e4SLinus Torvalds 		return (ips_removeq_wait_head(queue));
29591da177e4SLinus Torvalds 	}
29601da177e4SLinus Torvalds 
29611da177e4SLinus Torvalds 	p = queue->head;
29621da177e4SLinus Torvalds 
29631516b55dSHenne 	while ((p) && (item != (struct scsi_cmnd *) p->host_scribble))
29641516b55dSHenne 		p = (struct scsi_cmnd *) p->host_scribble;
29651da177e4SLinus Torvalds 
29661da177e4SLinus Torvalds 	if (p) {
29671da177e4SLinus Torvalds 		/* found a match */
29681da177e4SLinus Torvalds 		p->host_scribble = item->host_scribble;
29691da177e4SLinus Torvalds 
29701da177e4SLinus Torvalds 		if (!item->host_scribble)
29711da177e4SLinus Torvalds 			queue->tail = p;
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds 		item->host_scribble = NULL;
29741da177e4SLinus Torvalds 		queue->count--;
29751da177e4SLinus Torvalds 
29761da177e4SLinus Torvalds 		return (item);
29771da177e4SLinus Torvalds 	}
29781da177e4SLinus Torvalds 
29791da177e4SLinus Torvalds 	return (NULL);
29801da177e4SLinus Torvalds }
29811da177e4SLinus Torvalds 
29821da177e4SLinus Torvalds /****************************************************************************/
29831da177e4SLinus Torvalds /*                                                                          */
29841da177e4SLinus Torvalds /* Routine Name: ips_putq_copp_tail                                         */
29851da177e4SLinus Torvalds /*                                                                          */
29861da177e4SLinus Torvalds /* Routine Description:                                                     */
29871da177e4SLinus Torvalds /*                                                                          */
29881da177e4SLinus Torvalds /*   Add an item to the tail of the queue                                   */
29891da177e4SLinus Torvalds /*                                                                          */
29901da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
29911da177e4SLinus Torvalds /*                                                                          */
29921da177e4SLinus Torvalds /****************************************************************************/
29931da177e4SLinus Torvalds static void
29941da177e4SLinus Torvalds ips_putq_copp_tail(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
29951da177e4SLinus Torvalds {
29961da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_copp_tail", 1);
29971da177e4SLinus Torvalds 
29981da177e4SLinus Torvalds 	if (!item)
29991da177e4SLinus Torvalds 		return;
30001da177e4SLinus Torvalds 
30011da177e4SLinus Torvalds 	item->next = NULL;
30021da177e4SLinus Torvalds 
30031da177e4SLinus Torvalds 	if (queue->tail)
30041da177e4SLinus Torvalds 		queue->tail->next = item;
30051da177e4SLinus Torvalds 
30061da177e4SLinus Torvalds 	queue->tail = item;
30071da177e4SLinus Torvalds 
30081da177e4SLinus Torvalds 	if (!queue->head)
30091da177e4SLinus Torvalds 		queue->head = item;
30101da177e4SLinus Torvalds 
30111da177e4SLinus Torvalds 	queue->count++;
30121da177e4SLinus Torvalds }
30131da177e4SLinus Torvalds 
30141da177e4SLinus Torvalds /****************************************************************************/
30151da177e4SLinus Torvalds /*                                                                          */
30161da177e4SLinus Torvalds /* Routine Name: ips_removeq_copp_head                                      */
30171da177e4SLinus Torvalds /*                                                                          */
30181da177e4SLinus Torvalds /* Routine Description:                                                     */
30191da177e4SLinus Torvalds /*                                                                          */
30201da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
30211da177e4SLinus Torvalds /*                                                                          */
30221da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
30231da177e4SLinus Torvalds /*                                                                          */
30241da177e4SLinus Torvalds /****************************************************************************/
30251da177e4SLinus Torvalds static ips_copp_wait_item_t *
30261da177e4SLinus Torvalds ips_removeq_copp_head(ips_copp_queue_t * queue)
30271da177e4SLinus Torvalds {
30281da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
30291da177e4SLinus Torvalds 
30301da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_copp_head", 1);
30311da177e4SLinus Torvalds 
30321da177e4SLinus Torvalds 	item = queue->head;
30331da177e4SLinus Torvalds 
30341da177e4SLinus Torvalds 	if (!item) {
30351da177e4SLinus Torvalds 		return (NULL);
30361da177e4SLinus Torvalds 	}
30371da177e4SLinus Torvalds 
30381da177e4SLinus Torvalds 	queue->head = item->next;
30391da177e4SLinus Torvalds 	item->next = NULL;
30401da177e4SLinus Torvalds 
30411da177e4SLinus Torvalds 	if (queue->tail == item)
30421da177e4SLinus Torvalds 		queue->tail = NULL;
30431da177e4SLinus Torvalds 
30441da177e4SLinus Torvalds 	queue->count--;
30451da177e4SLinus Torvalds 
30461da177e4SLinus Torvalds 	return (item);
30471da177e4SLinus Torvalds }
30481da177e4SLinus Torvalds 
30491da177e4SLinus Torvalds /****************************************************************************/
30501da177e4SLinus Torvalds /*                                                                          */
30511da177e4SLinus Torvalds /* Routine Name: ips_removeq_copp                                           */
30521da177e4SLinus Torvalds /*                                                                          */
30531da177e4SLinus Torvalds /* Routine Description:                                                     */
30541da177e4SLinus Torvalds /*                                                                          */
30551da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
30561da177e4SLinus Torvalds /*                                                                          */
30571da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
30581da177e4SLinus Torvalds /*                                                                          */
30591da177e4SLinus Torvalds /****************************************************************************/
30601da177e4SLinus Torvalds static ips_copp_wait_item_t *
30611da177e4SLinus Torvalds ips_removeq_copp(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
30621da177e4SLinus Torvalds {
30631da177e4SLinus Torvalds 	ips_copp_wait_item_t *p;
30641da177e4SLinus Torvalds 
30651da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_copp", 1);
30661da177e4SLinus Torvalds 
30671da177e4SLinus Torvalds 	if (!item)
30681da177e4SLinus Torvalds 		return (NULL);
30691da177e4SLinus Torvalds 
30701da177e4SLinus Torvalds 	if (item == queue->head) {
30711da177e4SLinus Torvalds 		return (ips_removeq_copp_head(queue));
30721da177e4SLinus Torvalds 	}
30731da177e4SLinus Torvalds 
30741da177e4SLinus Torvalds 	p = queue->head;
30751da177e4SLinus Torvalds 
30761da177e4SLinus Torvalds 	while ((p) && (item != p->next))
30771da177e4SLinus Torvalds 		p = p->next;
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds 	if (p) {
30801da177e4SLinus Torvalds 		/* found a match */
30811da177e4SLinus Torvalds 		p->next = item->next;
30821da177e4SLinus Torvalds 
30831da177e4SLinus Torvalds 		if (!item->next)
30841da177e4SLinus Torvalds 			queue->tail = p;
30851da177e4SLinus Torvalds 
30861da177e4SLinus Torvalds 		item->next = NULL;
30871da177e4SLinus Torvalds 		queue->count--;
30881da177e4SLinus Torvalds 
30891da177e4SLinus Torvalds 		return (item);
30901da177e4SLinus Torvalds 	}
30911da177e4SLinus Torvalds 
30921da177e4SLinus Torvalds 	return (NULL);
30931da177e4SLinus Torvalds }
30941da177e4SLinus Torvalds 
30951da177e4SLinus Torvalds /****************************************************************************/
30961da177e4SLinus Torvalds /*                                                                          */
30971da177e4SLinus Torvalds /* Routine Name: ipsintr_blocking                                           */
30981da177e4SLinus Torvalds /*                                                                          */
30991da177e4SLinus Torvalds /* Routine Description:                                                     */
31001da177e4SLinus Torvalds /*                                                                          */
31011da177e4SLinus Torvalds /*   Finalize an interrupt for internal commands                            */
31021da177e4SLinus Torvalds /*                                                                          */
31031da177e4SLinus Torvalds /****************************************************************************/
31041da177e4SLinus Torvalds static void
31051da177e4SLinus Torvalds ipsintr_blocking(ips_ha_t * ha, ips_scb_t * scb)
31061da177e4SLinus Torvalds {
31071da177e4SLinus Torvalds 	METHOD_TRACE("ipsintr_blocking", 2);
31081da177e4SLinus Torvalds 
31091da177e4SLinus Torvalds 	ips_freescb(ha, scb);
31101da177e4SLinus Torvalds 	if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) {
31111da177e4SLinus Torvalds 		ha->waitflag = FALSE;
31121da177e4SLinus Torvalds 
31131da177e4SLinus Torvalds 		return;
31141da177e4SLinus Torvalds 	}
31151da177e4SLinus Torvalds }
31161da177e4SLinus Torvalds 
31171da177e4SLinus Torvalds /****************************************************************************/
31181da177e4SLinus Torvalds /*                                                                          */
31191da177e4SLinus Torvalds /* Routine Name: ipsintr_done                                               */
31201da177e4SLinus Torvalds /*                                                                          */
31211da177e4SLinus Torvalds /* Routine Description:                                                     */
31221da177e4SLinus Torvalds /*                                                                          */
31231da177e4SLinus Torvalds /*   Finalize an interrupt for non-internal commands                        */
31241da177e4SLinus Torvalds /*                                                                          */
31251da177e4SLinus Torvalds /****************************************************************************/
31261da177e4SLinus Torvalds static void
31271da177e4SLinus Torvalds ipsintr_done(ips_ha_t * ha, ips_scb_t * scb)
31281da177e4SLinus Torvalds {
31291da177e4SLinus Torvalds 	METHOD_TRACE("ipsintr_done", 2);
31301da177e4SLinus Torvalds 
31311da177e4SLinus Torvalds 	if (!scb) {
31321da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
31331da177e4SLinus Torvalds 			   "Spurious interrupt; scb NULL.\n");
31341da177e4SLinus Torvalds 
31351da177e4SLinus Torvalds 		return;
31361da177e4SLinus Torvalds 	}
31371da177e4SLinus Torvalds 
31381da177e4SLinus Torvalds 	if (scb->scsi_cmd == NULL) {
31391da177e4SLinus Torvalds 		/* unexpected interrupt */
31401da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
31411da177e4SLinus Torvalds 			   "Spurious interrupt; scsi_cmd not set.\n");
31421da177e4SLinus Torvalds 
31431da177e4SLinus Torvalds 		return;
31441da177e4SLinus Torvalds 	}
31451da177e4SLinus Torvalds 
31461da177e4SLinus Torvalds 	ips_done(ha, scb);
31471da177e4SLinus Torvalds }
31481da177e4SLinus Torvalds 
31491da177e4SLinus Torvalds /****************************************************************************/
31501da177e4SLinus Torvalds /*                                                                          */
31511da177e4SLinus Torvalds /* Routine Name: ips_done                                                   */
31521da177e4SLinus Torvalds /*                                                                          */
31531da177e4SLinus Torvalds /* Routine Description:                                                     */
31541da177e4SLinus Torvalds /*                                                                          */
31551da177e4SLinus Torvalds /*   Do housekeeping on completed commands                                  */
31561da177e4SLinus Torvalds /*  ASSUMED to be called form within the request lock                       */
31571da177e4SLinus Torvalds /****************************************************************************/
31581da177e4SLinus Torvalds static void
31591da177e4SLinus Torvalds ips_done(ips_ha_t * ha, ips_scb_t * scb)
31601da177e4SLinus Torvalds {
31611da177e4SLinus Torvalds 	int ret;
31621da177e4SLinus Torvalds 
31631da177e4SLinus Torvalds 	METHOD_TRACE("ips_done", 1);
31641da177e4SLinus Torvalds 
31651da177e4SLinus Torvalds 	if (!scb)
31661da177e4SLinus Torvalds 		return;
31671da177e4SLinus Torvalds 
31681da177e4SLinus Torvalds 	if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) {
31691da177e4SLinus Torvalds 		ips_cleanup_passthru(ha, scb);
31701da177e4SLinus Torvalds 		ha->num_ioctl--;
31711da177e4SLinus Torvalds 	} else {
31721da177e4SLinus Torvalds 		/*
31731da177e4SLinus Torvalds 		 * Check to see if this command had too much
31741da177e4SLinus Torvalds 		 * data and had to be broke up.  If so, queue
31751da177e4SLinus Torvalds 		 * the rest of the data and continue.
31761da177e4SLinus Torvalds 		 */
31771da177e4SLinus Torvalds 		if ((scb->breakup) || (scb->sg_break)) {
31782f4cf91cSFUJITA Tomonori                         struct scatterlist *sg;
3179e0eaf888SFUJITA Tomonori                         int i, sg_dma_index, ips_sg_index = 0;
31802f4cf91cSFUJITA Tomonori 
31811da177e4SLinus Torvalds 			/* we had a data breakup */
31821da177e4SLinus Torvalds 			scb->data_len = 0;
31831da177e4SLinus Torvalds 
31842f4cf91cSFUJITA Tomonori                         sg = scsi_sglist(scb->scsi_cmd);
31851da177e4SLinus Torvalds 
31861da177e4SLinus Torvalds                         /* Spin forward to last dma chunk */
31871da177e4SLinus Torvalds                         sg_dma_index = scb->breakup;
3188e0eaf888SFUJITA Tomonori                         for (i = 0; i < scb->breakup; i++)
3189e0eaf888SFUJITA Tomonori                                 sg = sg_next(sg);
31901da177e4SLinus Torvalds 
31911da177e4SLinus Torvalds 			/* Take care of possible partial on last chunk */
31921da177e4SLinus Torvalds                         ips_fill_scb_sg_single(ha,
3193e0eaf888SFUJITA Tomonori                                                sg_dma_address(sg),
31941da177e4SLinus Torvalds                                                scb, ips_sg_index++,
3195e0eaf888SFUJITA Tomonori                                                sg_dma_len(sg));
31961da177e4SLinus Torvalds 
31972f4cf91cSFUJITA Tomonori                         for (; sg_dma_index < scsi_sg_count(scb->scsi_cmd);
3198e0eaf888SFUJITA Tomonori                              sg_dma_index++, sg = sg_next(sg)) {
31991da177e4SLinus Torvalds                                 if (ips_fill_scb_sg_single
32001da177e4SLinus Torvalds                                     (ha,
3201e0eaf888SFUJITA Tomonori                                      sg_dma_address(sg),
32021da177e4SLinus Torvalds                                      scb, ips_sg_index++,
3203e0eaf888SFUJITA Tomonori                                      sg_dma_len(sg)) < 0)
32041da177e4SLinus Torvalds                                         break;
32051da177e4SLinus Torvalds                         }
32061da177e4SLinus Torvalds 
32071da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
32081da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |=
32091da177e4SLinus Torvalds 			    ips_command_direction[scb->scsi_cmd->cmnd[0]];
32101da177e4SLinus Torvalds 
32111da177e4SLinus Torvalds 			if (!(scb->dcdb.cmd_attribute & 0x3))
32121da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds 			if (scb->data_len >= IPS_MAX_XFER) {
32151da177e4SLinus Torvalds 				scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
32161da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
32171da177e4SLinus Torvalds 			}
32181da177e4SLinus Torvalds 
32191da177e4SLinus Torvalds 			ret = ips_send_cmd(ha, scb);
32201da177e4SLinus Torvalds 
32211da177e4SLinus Torvalds 			switch (ret) {
32221da177e4SLinus Torvalds 			case IPS_FAILURE:
32231da177e4SLinus Torvalds 				if (scb->scsi_cmd) {
32241da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_ERROR << 16;
32251da177e4SLinus Torvalds 					scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32261da177e4SLinus Torvalds 				}
32271da177e4SLinus Torvalds 
32281da177e4SLinus Torvalds 				ips_freescb(ha, scb);
32291da177e4SLinus Torvalds 				break;
32301da177e4SLinus Torvalds 			case IPS_SUCCESS_IMM:
32311da177e4SLinus Torvalds 				if (scb->scsi_cmd) {
32321da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_ERROR << 16;
32331da177e4SLinus Torvalds 					scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32341da177e4SLinus Torvalds 				}
32351da177e4SLinus Torvalds 
32361da177e4SLinus Torvalds 				ips_freescb(ha, scb);
32371da177e4SLinus Torvalds 				break;
32381da177e4SLinus Torvalds 			default:
32391da177e4SLinus Torvalds 				break;
32401da177e4SLinus Torvalds 			}	/* end case */
32411da177e4SLinus Torvalds 
32421da177e4SLinus Torvalds 			return;
32431da177e4SLinus Torvalds 		}
32441da177e4SLinus Torvalds 	}			/* end if passthru */
32451da177e4SLinus Torvalds 
32461da177e4SLinus Torvalds 	if (scb->bus) {
32471da177e4SLinus Torvalds 		ha->dcdb_active[scb->bus - 1] &= ~(1 << scb->target_id);
32481da177e4SLinus Torvalds 	}
32491da177e4SLinus Torvalds 
32501da177e4SLinus Torvalds 	scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32511da177e4SLinus Torvalds 
32521da177e4SLinus Torvalds 	ips_freescb(ha, scb);
32531da177e4SLinus Torvalds }
32541da177e4SLinus Torvalds 
32551da177e4SLinus Torvalds /****************************************************************************/
32561da177e4SLinus Torvalds /*                                                                          */
32571da177e4SLinus Torvalds /* Routine Name: ips_map_status                                             */
32581da177e4SLinus Torvalds /*                                                                          */
32591da177e4SLinus Torvalds /* Routine Description:                                                     */
32601da177e4SLinus Torvalds /*                                                                          */
32611da177e4SLinus Torvalds /*   Map Controller Error codes to Linux Error Codes                        */
32621da177e4SLinus Torvalds /*                                                                          */
32631da177e4SLinus Torvalds /****************************************************************************/
32641da177e4SLinus Torvalds static int
32651da177e4SLinus Torvalds ips_map_status(ips_ha_t * ha, ips_scb_t * scb, ips_stat_t * sp)
32661da177e4SLinus Torvalds {
32671da177e4SLinus Torvalds 	int errcode;
32681da177e4SLinus Torvalds 	int device_error;
32691da177e4SLinus Torvalds 	uint32_t transfer_len;
32701da177e4SLinus Torvalds 	IPS_DCDB_TABLE_TAPE *tapeDCDB;
3271a5b3c86eSJack Hammer 	IPS_SCSI_INQ_DATA inquiryData;
32721da177e4SLinus Torvalds 
32731da177e4SLinus Torvalds 	METHOD_TRACE("ips_map_status", 1);
32741da177e4SLinus Torvalds 
32751da177e4SLinus Torvalds 	if (scb->bus) {
32761da177e4SLinus Torvalds 		DEBUG_VAR(2,
32771da177e4SLinus Torvalds 			  "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
32781da177e4SLinus Torvalds 			  ips_name, ha->host_num,
32791da177e4SLinus Torvalds 			  scb->scsi_cmd->device->channel,
32801da177e4SLinus Torvalds 			  scb->scsi_cmd->device->id, scb->scsi_cmd->device->lun,
32811da177e4SLinus Torvalds 			  scb->basic_status, scb->extended_status,
32821da177e4SLinus Torvalds 			  scb->extended_status ==
32831da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
32841da177e4SLinus Torvalds 			  scb->extended_status ==
32851da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
32861da177e4SLinus Torvalds 			  scb->extended_status ==
32871da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
32881da177e4SLinus Torvalds 	}
32891da177e4SLinus Torvalds 
32901da177e4SLinus Torvalds 	/* default driver error */
32911da177e4SLinus Torvalds 	errcode = DID_ERROR;
32921da177e4SLinus Torvalds 	device_error = 0;
32931da177e4SLinus Torvalds 
32941da177e4SLinus Torvalds 	switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
32951da177e4SLinus Torvalds 	case IPS_CMD_TIMEOUT:
32961da177e4SLinus Torvalds 		errcode = DID_TIME_OUT;
32971da177e4SLinus Torvalds 		break;
32981da177e4SLinus Torvalds 
32991da177e4SLinus Torvalds 	case IPS_INVAL_OPCO:
33001da177e4SLinus Torvalds 	case IPS_INVAL_CMD_BLK:
33011da177e4SLinus Torvalds 	case IPS_INVAL_PARM_BLK:
33021da177e4SLinus Torvalds 	case IPS_LD_ERROR:
33031da177e4SLinus Torvalds 	case IPS_CMD_CMPLT_WERROR:
33041da177e4SLinus Torvalds 		break;
33051da177e4SLinus Torvalds 
33061da177e4SLinus Torvalds 	case IPS_PHYS_DRV_ERROR:
33071da177e4SLinus Torvalds 		switch (scb->extended_status) {
33081da177e4SLinus Torvalds 		case IPS_ERR_SEL_TO:
33091da177e4SLinus Torvalds 			if (scb->bus)
33101da177e4SLinus Torvalds 				errcode = DID_NO_CONNECT;
33111da177e4SLinus Torvalds 
33121da177e4SLinus Torvalds 			break;
33131da177e4SLinus Torvalds 
33141da177e4SLinus Torvalds 		case IPS_ERR_OU_RUN:
33151da177e4SLinus Torvalds 			if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) ||
33161da177e4SLinus Torvalds 			    (scb->cmd.dcdb.op_code ==
33171da177e4SLinus Torvalds 			     IPS_CMD_EXTENDED_DCDB_SG)) {
33181da177e4SLinus Torvalds 				tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
33191da177e4SLinus Torvalds 				transfer_len = tapeDCDB->transfer_length;
33201da177e4SLinus Torvalds 			} else {
33211da177e4SLinus Torvalds 				transfer_len =
33221da177e4SLinus Torvalds 				    (uint32_t) scb->dcdb.transfer_length;
33231da177e4SLinus Torvalds 			}
33241da177e4SLinus Torvalds 
33251da177e4SLinus Torvalds 			if ((scb->bus) && (transfer_len < scb->data_len)) {
33261da177e4SLinus Torvalds 				/* Underrun - set default to no error */
33271da177e4SLinus Torvalds 				errcode = DID_OK;
33281da177e4SLinus Torvalds 
33291da177e4SLinus Torvalds 				/* Restrict access to physical DASD */
3330a5b3c86eSJack Hammer 				if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
3331a5b3c86eSJack Hammer 				    ips_scmd_buf_read(scb->scsi_cmd,
3332a5b3c86eSJack Hammer                                       &inquiryData, sizeof (inquiryData));
3333a5b3c86eSJack Hammer  				    if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK) {
33341da177e4SLinus Torvalds 				        errcode = DID_TIME_OUT;
33351da177e4SLinus Torvalds 				        break;
33361da177e4SLinus Torvalds 				    }
3337a5b3c86eSJack Hammer 				}
33381da177e4SLinus Torvalds 			} else
33391da177e4SLinus Torvalds 				errcode = DID_ERROR;
33401da177e4SLinus Torvalds 
33411da177e4SLinus Torvalds 			break;
33421da177e4SLinus Torvalds 
33431da177e4SLinus Torvalds 		case IPS_ERR_RECOVERY:
33441da177e4SLinus Torvalds 			/* don't fail recovered errors */
33451da177e4SLinus Torvalds 			if (scb->bus)
33461da177e4SLinus Torvalds 				errcode = DID_OK;
33471da177e4SLinus Torvalds 
33481da177e4SLinus Torvalds 			break;
33491da177e4SLinus Torvalds 
33501da177e4SLinus Torvalds 		case IPS_ERR_HOST_RESET:
33511da177e4SLinus Torvalds 		case IPS_ERR_DEV_RESET:
33521da177e4SLinus Torvalds 			errcode = DID_RESET;
33531da177e4SLinus Torvalds 			break;
33541da177e4SLinus Torvalds 
33551da177e4SLinus Torvalds 		case IPS_ERR_CKCOND:
33561da177e4SLinus Torvalds 			if (scb->bus) {
33571da177e4SLinus Torvalds 				if ((scb->cmd.dcdb.op_code ==
33581da177e4SLinus Torvalds 				     IPS_CMD_EXTENDED_DCDB)
33591da177e4SLinus Torvalds 				    || (scb->cmd.dcdb.op_code ==
33601da177e4SLinus Torvalds 					IPS_CMD_EXTENDED_DCDB_SG)) {
33611da177e4SLinus Torvalds 					tapeDCDB =
33621da177e4SLinus Torvalds 					    (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
33631da177e4SLinus Torvalds 					memcpy(scb->scsi_cmd->sense_buffer,
33641da177e4SLinus Torvalds 					       tapeDCDB->sense_info,
3365b80ca4f7SFUJITA Tomonori 					       SCSI_SENSE_BUFFERSIZE);
33661da177e4SLinus Torvalds 				} else {
33671da177e4SLinus Torvalds 					memcpy(scb->scsi_cmd->sense_buffer,
33681da177e4SLinus Torvalds 					       scb->dcdb.sense_info,
3369b80ca4f7SFUJITA Tomonori 					       SCSI_SENSE_BUFFERSIZE);
33701da177e4SLinus Torvalds 				}
33711da177e4SLinus Torvalds 				device_error = 2;	/* check condition */
33721da177e4SLinus Torvalds 			}
33731da177e4SLinus Torvalds 
33741da177e4SLinus Torvalds 			errcode = DID_OK;
33751da177e4SLinus Torvalds 
33761da177e4SLinus Torvalds 			break;
33771da177e4SLinus Torvalds 
33781da177e4SLinus Torvalds 		default:
33791da177e4SLinus Torvalds 			errcode = DID_ERROR;
33801da177e4SLinus Torvalds 			break;
33811da177e4SLinus Torvalds 
33821da177e4SLinus Torvalds 		}		/* end switch */
33831da177e4SLinus Torvalds 	}			/* end switch */
33841da177e4SLinus Torvalds 
33851da177e4SLinus Torvalds 	scb->scsi_cmd->result = device_error | (errcode << 16);
33861da177e4SLinus Torvalds 
33871da177e4SLinus Torvalds 	return (1);
33881da177e4SLinus Torvalds }
33891da177e4SLinus Torvalds 
33901da177e4SLinus Torvalds /****************************************************************************/
33911da177e4SLinus Torvalds /*                                                                          */
33921da177e4SLinus Torvalds /* Routine Name: ips_send_wait                                              */
33931da177e4SLinus Torvalds /*                                                                          */
33941da177e4SLinus Torvalds /* Routine Description:                                                     */
33951da177e4SLinus Torvalds /*                                                                          */
33961da177e4SLinus Torvalds /*   Send a command to the controller and wait for it to return             */
33971da177e4SLinus Torvalds /*                                                                          */
33981da177e4SLinus Torvalds /*   The FFDC Time Stamp use this function for the callback, but doesn't    */
33991da177e4SLinus Torvalds /*   actually need to wait.                                                 */
34001da177e4SLinus Torvalds /****************************************************************************/
34011da177e4SLinus Torvalds static int
34021da177e4SLinus Torvalds ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr)
34031da177e4SLinus Torvalds {
34041da177e4SLinus Torvalds 	int ret;
34051da177e4SLinus Torvalds 
34061da177e4SLinus Torvalds 	METHOD_TRACE("ips_send_wait", 1);
34071da177e4SLinus Torvalds 
34081da177e4SLinus Torvalds 	if (intr != IPS_FFDC) {	/* Won't be Waiting if this is a Time Stamp */
34091da177e4SLinus Torvalds 		ha->waitflag = TRUE;
34101da177e4SLinus Torvalds 		ha->cmd_in_progress = scb->cdb[0];
34111da177e4SLinus Torvalds 	}
34121da177e4SLinus Torvalds 	scb->callback = ipsintr_blocking;
34131da177e4SLinus Torvalds 	ret = ips_send_cmd(ha, scb);
34141da177e4SLinus Torvalds 
34151da177e4SLinus Torvalds 	if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
34161da177e4SLinus Torvalds 		return (ret);
34171da177e4SLinus Torvalds 
34181da177e4SLinus Torvalds 	if (intr != IPS_FFDC)	/* Don't Wait around if this is a Time Stamp */
34191da177e4SLinus Torvalds 		ret = ips_wait(ha, timeout, intr);
34201da177e4SLinus Torvalds 
34211da177e4SLinus Torvalds 	return (ret);
34221da177e4SLinus Torvalds }
34231da177e4SLinus Torvalds 
34241da177e4SLinus Torvalds /****************************************************************************/
34251da177e4SLinus Torvalds /*                                                                          */
34261da177e4SLinus Torvalds /* Routine Name: ips_scmd_buf_write                                         */
34271da177e4SLinus Torvalds /*                                                                          */
34281da177e4SLinus Torvalds /* Routine Description:                                                     */
34291516b55dSHenne /*  Write data to struct scsi_cmnd request_buffer at proper offsets	    */
34301da177e4SLinus Torvalds /****************************************************************************/
34311da177e4SLinus Torvalds static void
34321516b55dSHenne ips_scmd_buf_write(struct scsi_cmnd *scmd, void *data, unsigned int count)
34331da177e4SLinus Torvalds {
3434a3632fa3SJack Hammer 	unsigned long flags;
34352f4cf91cSFUJITA Tomonori 
3436a3632fa3SJack Hammer 	local_irq_save(flags);
34376690bae7SFUJITA Tomonori 	scsi_sg_copy_from_buffer(scmd, data, count);
3438a3632fa3SJack Hammer 	local_irq_restore(flags);
34391da177e4SLinus Torvalds }
34401da177e4SLinus Torvalds 
34411da177e4SLinus Torvalds /****************************************************************************/
34421da177e4SLinus Torvalds /*                                                                          */
34431da177e4SLinus Torvalds /* Routine Name: ips_scmd_buf_read                                          */
34441da177e4SLinus Torvalds /*                                                                          */
34451da177e4SLinus Torvalds /* Routine Description:                                                     */
34461516b55dSHenne /*  Copy data from a struct scsi_cmnd to a new, linear buffer		    */
34471da177e4SLinus Torvalds /****************************************************************************/
34481da177e4SLinus Torvalds static void
34491516b55dSHenne ips_scmd_buf_read(struct scsi_cmnd *scmd, void *data, unsigned int count)
34501da177e4SLinus Torvalds {
3451a3632fa3SJack Hammer 	unsigned long flags;
34522f4cf91cSFUJITA Tomonori 
3453a3632fa3SJack Hammer 	local_irq_save(flags);
34546690bae7SFUJITA Tomonori 	scsi_sg_copy_to_buffer(scmd, data, count);
3455a3632fa3SJack Hammer 	local_irq_restore(flags);
34561da177e4SLinus Torvalds }
34571da177e4SLinus Torvalds 
34581da177e4SLinus Torvalds /****************************************************************************/
34591da177e4SLinus Torvalds /*                                                                          */
34601da177e4SLinus Torvalds /* Routine Name: ips_send_cmd                                               */
34611da177e4SLinus Torvalds /*                                                                          */
34621da177e4SLinus Torvalds /* Routine Description:                                                     */
34631da177e4SLinus Torvalds /*                                                                          */
34641da177e4SLinus Torvalds /*   Map SCSI commands to ServeRAID commands for logical drives             */
34651da177e4SLinus Torvalds /*                                                                          */
34661da177e4SLinus Torvalds /****************************************************************************/
34671da177e4SLinus Torvalds static int
34681da177e4SLinus Torvalds ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
34691da177e4SLinus Torvalds {
34701da177e4SLinus Torvalds 	int ret;
34711da177e4SLinus Torvalds 	char *sp;
34721da177e4SLinus Torvalds 	int device_error;
34731da177e4SLinus Torvalds 	IPS_DCDB_TABLE_TAPE *tapeDCDB;
34741da177e4SLinus Torvalds 	int TimeOut;
34751da177e4SLinus Torvalds 
34761da177e4SLinus Torvalds 	METHOD_TRACE("ips_send_cmd", 1);
34771da177e4SLinus Torvalds 
34781da177e4SLinus Torvalds 	ret = IPS_SUCCESS;
34791da177e4SLinus Torvalds 
34801da177e4SLinus Torvalds 	if (!scb->scsi_cmd) {
34811da177e4SLinus Torvalds 		/* internal command */
34821da177e4SLinus Torvalds 
34831da177e4SLinus Torvalds 		if (scb->bus > 0) {
34841da177e4SLinus Torvalds 			/* Controller commands can't be issued */
34851da177e4SLinus Torvalds 			/* to real devices -- fail them        */
34861da177e4SLinus Torvalds 			if ((ha->waitflag == TRUE) &&
34871da177e4SLinus Torvalds 			    (ha->cmd_in_progress == scb->cdb[0])) {
34881da177e4SLinus Torvalds 				ha->waitflag = FALSE;
34891da177e4SLinus Torvalds 			}
34901da177e4SLinus Torvalds 
34911da177e4SLinus Torvalds 			return (1);
34921da177e4SLinus Torvalds 		}
34931da177e4SLinus Torvalds 	} else if ((scb->bus == 0) && (!ips_is_passthru(scb->scsi_cmd))) {
34941da177e4SLinus Torvalds 		/* command to logical bus -- interpret */
34951da177e4SLinus Torvalds 		ret = IPS_SUCCESS_IMM;
34961da177e4SLinus Torvalds 
34971da177e4SLinus Torvalds 		switch (scb->scsi_cmd->cmnd[0]) {
34981da177e4SLinus Torvalds 		case ALLOW_MEDIUM_REMOVAL:
34991da177e4SLinus Torvalds 		case REZERO_UNIT:
35001da177e4SLinus Torvalds 		case ERASE:
35011da177e4SLinus Torvalds 		case WRITE_FILEMARKS:
35021da177e4SLinus Torvalds 		case SPACE:
35031da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
35041da177e4SLinus Torvalds 			break;
35051da177e4SLinus Torvalds 
35061da177e4SLinus Torvalds 		case START_STOP:
35071da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
35081da177e4SLinus Torvalds 
35091da177e4SLinus Torvalds 		case TEST_UNIT_READY:
35101da177e4SLinus Torvalds 		case INQUIRY:
35111da177e4SLinus Torvalds 			if (scb->target_id == IPS_ADAPTER_ID) {
35121da177e4SLinus Torvalds 				/*
35131da177e4SLinus Torvalds 				 * Either we have a TUR
35141da177e4SLinus Torvalds 				 * or we have a SCSI inquiry
35151da177e4SLinus Torvalds 				 */
35161da177e4SLinus Torvalds 				if (scb->scsi_cmd->cmnd[0] == TEST_UNIT_READY)
35171da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_OK << 16;
35181da177e4SLinus Torvalds 
35191da177e4SLinus Torvalds 				if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
35201da177e4SLinus Torvalds 					IPS_SCSI_INQ_DATA inquiry;
35211da177e4SLinus Torvalds 
35221da177e4SLinus Torvalds 					memset(&inquiry, 0,
35231da177e4SLinus Torvalds 					       sizeof (IPS_SCSI_INQ_DATA));
35241da177e4SLinus Torvalds 
35251da177e4SLinus Torvalds 					inquiry.DeviceType =
35261da177e4SLinus Torvalds 					    IPS_SCSI_INQ_TYPE_PROCESSOR;
35271da177e4SLinus Torvalds 					inquiry.DeviceTypeQualifier =
35281da177e4SLinus Torvalds 					    IPS_SCSI_INQ_LU_CONNECTED;
35291da177e4SLinus Torvalds 					inquiry.Version = IPS_SCSI_INQ_REV2;
35301da177e4SLinus Torvalds 					inquiry.ResponseDataFormat =
35311da177e4SLinus Torvalds 					    IPS_SCSI_INQ_RD_REV2;
35321da177e4SLinus Torvalds 					inquiry.AdditionalLength = 31;
35331da177e4SLinus Torvalds 					inquiry.Flags[0] =
35341da177e4SLinus Torvalds 					    IPS_SCSI_INQ_Address16;
35351da177e4SLinus Torvalds 					inquiry.Flags[1] =
35361da177e4SLinus Torvalds 					    IPS_SCSI_INQ_WBus16 |
35371da177e4SLinus Torvalds 					    IPS_SCSI_INQ_Sync;
35381da177e4SLinus Torvalds 					strncpy(inquiry.VendorId, "IBM     ",
35391da177e4SLinus Torvalds 						8);
35401da177e4SLinus Torvalds 					strncpy(inquiry.ProductId,
35411da177e4SLinus Torvalds 						"SERVERAID       ", 16);
35421da177e4SLinus Torvalds 					strncpy(inquiry.ProductRevisionLevel,
35431da177e4SLinus Torvalds 						"1.00", 4);
35441da177e4SLinus Torvalds 
35451da177e4SLinus Torvalds 					ips_scmd_buf_write(scb->scsi_cmd,
35461da177e4SLinus Torvalds 							   &inquiry,
35471da177e4SLinus Torvalds 							   sizeof (inquiry));
35481da177e4SLinus Torvalds 
35491da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_OK << 16;
35501da177e4SLinus Torvalds 				}
35511da177e4SLinus Torvalds 			} else {
35521da177e4SLinus Torvalds 				scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
35531da177e4SLinus Torvalds 				scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
35541da177e4SLinus Torvalds 				scb->cmd.logical_info.reserved = 0;
35551da177e4SLinus Torvalds 				scb->cmd.logical_info.reserved2 = 0;
35561da177e4SLinus Torvalds 				scb->data_len = sizeof (IPS_LD_INFO);
35571da177e4SLinus Torvalds 				scb->data_busaddr = ha->logical_drive_info_dma_addr;
35581da177e4SLinus Torvalds 				scb->flags = 0;
35591da177e4SLinus Torvalds 				scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
35601da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
35611da177e4SLinus Torvalds 			}
35621da177e4SLinus Torvalds 
35631da177e4SLinus Torvalds 			break;
35641da177e4SLinus Torvalds 
35651da177e4SLinus Torvalds 		case REQUEST_SENSE:
35661da177e4SLinus Torvalds 			ips_reqsen(ha, scb);
35671da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
35681da177e4SLinus Torvalds 			break;
35691da177e4SLinus Torvalds 
35701da177e4SLinus Torvalds 		case READ_6:
35711da177e4SLinus Torvalds 		case WRITE_6:
35721da177e4SLinus Torvalds 			if (!scb->sg_len) {
35731da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
35741da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
35751da177e4SLinus Torvalds 				     READ_6) ? IPS_CMD_READ : IPS_CMD_WRITE;
35761da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg = 0;
35771da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
35781da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
35791da177e4SLinus Torvalds 			} else {
35801da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
35811da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
35821da177e4SLinus Torvalds 				     READ_6) ? IPS_CMD_READ_SG :
35831da177e4SLinus Torvalds 				    IPS_CMD_WRITE_SG;
35841da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg =
35851da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
35861da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
35871da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
35881da177e4SLinus Torvalds 			}
35891da177e4SLinus Torvalds 
35901da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
35911da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
35921da177e4SLinus Torvalds 			scb->cmd.basic_io.log_drv = scb->target_id;
35931da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_count = scb->sg_len;
35941da177e4SLinus Torvalds 
35951da177e4SLinus Torvalds 			if (scb->cmd.basic_io.lba)
359636b8dd1bSMarcin Slusarz 				le32_add_cpu(&scb->cmd.basic_io.lba,
35971da177e4SLinus Torvalds 						le16_to_cpu(scb->cmd.basic_io.
35981da177e4SLinus Torvalds 							    sector_count));
35991da177e4SLinus Torvalds 			else
36001da177e4SLinus Torvalds 				scb->cmd.basic_io.lba =
36011da177e4SLinus Torvalds 				    (((scb->scsi_cmd->
36021da177e4SLinus Torvalds 				       cmnd[1] & 0x1f) << 16) | (scb->scsi_cmd->
36031da177e4SLinus Torvalds 								 cmnd[2] << 8) |
36041da177e4SLinus Torvalds 				     (scb->scsi_cmd->cmnd[3]));
36051da177e4SLinus Torvalds 
36061da177e4SLinus Torvalds 			scb->cmd.basic_io.sector_count =
36071da177e4SLinus Torvalds 			    cpu_to_le16(scb->data_len / IPS_BLKSIZE);
36081da177e4SLinus Torvalds 
36091da177e4SLinus Torvalds 			if (le16_to_cpu(scb->cmd.basic_io.sector_count) == 0)
36101da177e4SLinus Torvalds 				scb->cmd.basic_io.sector_count =
36111da177e4SLinus Torvalds 				    cpu_to_le16(256);
36121da177e4SLinus Torvalds 
36131da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
36141da177e4SLinus Torvalds 			break;
36151da177e4SLinus Torvalds 
36161da177e4SLinus Torvalds 		case READ_10:
36171da177e4SLinus Torvalds 		case WRITE_10:
36181da177e4SLinus Torvalds 			if (!scb->sg_len) {
36191da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
36201da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
36211da177e4SLinus Torvalds 				     READ_10) ? IPS_CMD_READ : IPS_CMD_WRITE;
36221da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg = 0;
36231da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
36241da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
36251da177e4SLinus Torvalds 			} else {
36261da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
36271da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
36281da177e4SLinus Torvalds 				     READ_10) ? IPS_CMD_READ_SG :
36291da177e4SLinus Torvalds 				    IPS_CMD_WRITE_SG;
36301da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg =
36311da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
36321da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
36331da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
36341da177e4SLinus Torvalds 			}
36351da177e4SLinus Torvalds 
36361da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
36371da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
36381da177e4SLinus Torvalds 			scb->cmd.basic_io.log_drv = scb->target_id;
36391da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_count = scb->sg_len;
36401da177e4SLinus Torvalds 
36411da177e4SLinus Torvalds 			if (scb->cmd.basic_io.lba)
364236b8dd1bSMarcin Slusarz 				le32_add_cpu(&scb->cmd.basic_io.lba,
36431da177e4SLinus Torvalds 						le16_to_cpu(scb->cmd.basic_io.
36441da177e4SLinus Torvalds 							    sector_count));
36451da177e4SLinus Torvalds 			else
36461da177e4SLinus Torvalds 				scb->cmd.basic_io.lba =
36471da177e4SLinus Torvalds 				    ((scb->scsi_cmd->cmnd[2] << 24) | (scb->
36481da177e4SLinus Torvalds 								       scsi_cmd->
36491da177e4SLinus Torvalds 								       cmnd[3]
36501da177e4SLinus Torvalds 								       << 16) |
36511da177e4SLinus Torvalds 				     (scb->scsi_cmd->cmnd[4] << 8) | scb->
36521da177e4SLinus Torvalds 				     scsi_cmd->cmnd[5]);
36531da177e4SLinus Torvalds 
36541da177e4SLinus Torvalds 			scb->cmd.basic_io.sector_count =
36551da177e4SLinus Torvalds 			    cpu_to_le16(scb->data_len / IPS_BLKSIZE);
36561da177e4SLinus Torvalds 
36571da177e4SLinus Torvalds 			if (cpu_to_le16(scb->cmd.basic_io.sector_count) == 0) {
36581da177e4SLinus Torvalds 				/*
36591da177e4SLinus Torvalds 				 * This is a null condition
36601da177e4SLinus Torvalds 				 * we don't have to do anything
36611da177e4SLinus Torvalds 				 * so just return
36621da177e4SLinus Torvalds 				 */
36631da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_OK << 16;
36641da177e4SLinus Torvalds 			} else
36651da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
36661da177e4SLinus Torvalds 
36671da177e4SLinus Torvalds 			break;
36681da177e4SLinus Torvalds 
36691da177e4SLinus Torvalds 		case RESERVE:
36701da177e4SLinus Torvalds 		case RELEASE:
36711da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
36721da177e4SLinus Torvalds 			break;
36731da177e4SLinus Torvalds 
36741da177e4SLinus Torvalds 		case MODE_SENSE:
36751da177e4SLinus Torvalds 			scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
36761da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
36771da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
36781da177e4SLinus Torvalds 			scb->cmd.basic_io.enhanced_sg = 0;
36791da177e4SLinus Torvalds 			scb->data_len = sizeof (*ha->enq);
36801da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
36811da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
36821da177e4SLinus Torvalds 			break;
36831da177e4SLinus Torvalds 
36841da177e4SLinus Torvalds 		case READ_CAPACITY:
36851da177e4SLinus Torvalds 			scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
36861da177e4SLinus Torvalds 			scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
36871da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved = 0;
36881da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved2 = 0;
36891da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved3 = 0;
36901da177e4SLinus Torvalds 			scb->data_len = sizeof (IPS_LD_INFO);
36911da177e4SLinus Torvalds 			scb->data_busaddr = ha->logical_drive_info_dma_addr;
36921da177e4SLinus Torvalds 			scb->flags = 0;
36931da177e4SLinus Torvalds 			scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
36941da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
36951da177e4SLinus Torvalds 			break;
36961da177e4SLinus Torvalds 
36971da177e4SLinus Torvalds 		case SEND_DIAGNOSTIC:
36981da177e4SLinus Torvalds 		case REASSIGN_BLOCKS:
36991da177e4SLinus Torvalds 		case FORMAT_UNIT:
37001da177e4SLinus Torvalds 		case SEEK_10:
37011da177e4SLinus Torvalds 		case VERIFY:
37021da177e4SLinus Torvalds 		case READ_DEFECT_DATA:
37031da177e4SLinus Torvalds 		case READ_BUFFER:
37041da177e4SLinus Torvalds 		case WRITE_BUFFER:
37051da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
37061da177e4SLinus Torvalds 			break;
37071da177e4SLinus Torvalds 
37081da177e4SLinus Torvalds 		default:
37091da177e4SLinus Torvalds 			/* Set the Return Info to appear like the Command was */
37101da177e4SLinus Torvalds 			/* attempted, a Check Condition occurred, and Sense   */
37111da177e4SLinus Torvalds 			/* Data indicating an Invalid CDB OpCode is returned. */
37121da177e4SLinus Torvalds 			sp = (char *) scb->scsi_cmd->sense_buffer;
37131da177e4SLinus Torvalds 
37141da177e4SLinus Torvalds 			sp[0] = 0x70;	/* Error Code               */
37151da177e4SLinus Torvalds 			sp[2] = ILLEGAL_REQUEST;	/* Sense Key 5 Illegal Req. */
37161da177e4SLinus Torvalds 			sp[7] = 0x0A;	/* Additional Sense Length  */
37171da177e4SLinus Torvalds 			sp[12] = 0x20;	/* ASC = Invalid OpCode     */
37181da177e4SLinus Torvalds 			sp[13] = 0x00;	/* ASCQ                     */
37191da177e4SLinus Torvalds 
37201da177e4SLinus Torvalds 			device_error = 2;	/* Indicate Check Condition */
37211da177e4SLinus Torvalds 			scb->scsi_cmd->result = device_error | (DID_OK << 16);
37221da177e4SLinus Torvalds 			break;
37231da177e4SLinus Torvalds 		}		/* end switch */
37241da177e4SLinus Torvalds 	}
37251da177e4SLinus Torvalds 	/* end if */
37261da177e4SLinus Torvalds 	if (ret == IPS_SUCCESS_IMM)
37271da177e4SLinus Torvalds 		return (ret);
37281da177e4SLinus Torvalds 
37291da177e4SLinus Torvalds 	/* setup DCDB */
37301da177e4SLinus Torvalds 	if (scb->bus > 0) {
37311da177e4SLinus Torvalds 
37321da177e4SLinus Torvalds 		/* If we already know the Device is Not there, no need to attempt a Command   */
37331da177e4SLinus Torvalds 		/* This also protects an NT FailOver Controller from getting CDB's sent to it */
37341da177e4SLinus Torvalds 		if (ha->conf->dev[scb->bus - 1][scb->target_id].ucState == 0) {
37351da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_NO_CONNECT << 16;
37361da177e4SLinus Torvalds 			return (IPS_SUCCESS_IMM);
37371da177e4SLinus Torvalds 		}
37381da177e4SLinus Torvalds 
37391da177e4SLinus Torvalds 		ha->dcdb_active[scb->bus - 1] |= (1 << scb->target_id);
37401da177e4SLinus Torvalds 		scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb);
37411da177e4SLinus Torvalds 		scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
37421da177e4SLinus Torvalds 							 (unsigned long) &scb->
37431da177e4SLinus Torvalds 							 dcdb -
37441da177e4SLinus Torvalds 							 (unsigned long) scb);
37451da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved = 0;
37461da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved2 = 0;
37471da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved3 = 0;
37481da177e4SLinus Torvalds 		scb->cmd.dcdb.segment_4G = 0;
37491da177e4SLinus Torvalds 		scb->cmd.dcdb.enhanced_sg = 0;
37501da177e4SLinus Torvalds 
3751242f9dcbSJens Axboe 		TimeOut = scb->scsi_cmd->request->timeout;
37521da177e4SLinus Torvalds 
37531da177e4SLinus Torvalds 		if (ha->subsys->param[4] & 0x00100000) {	/* If NEW Tape DCDB is Supported */
37541da177e4SLinus Torvalds 			if (!scb->sg_len) {
37551da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_EXTENDED_DCDB;
37561da177e4SLinus Torvalds 			} else {
37571da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code =
37581da177e4SLinus Torvalds 				    IPS_CMD_EXTENDED_DCDB_SG;
37591da177e4SLinus Torvalds 				scb->cmd.dcdb.enhanced_sg =
37601da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
37611da177e4SLinus Torvalds 			}
37621da177e4SLinus Torvalds 
37631da177e4SLinus Torvalds 			tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;	/* Use Same Data Area as Old DCDB Struct */
37641da177e4SLinus Torvalds 			tapeDCDB->device_address =
37651da177e4SLinus Torvalds 			    ((scb->bus - 1) << 4) | scb->target_id;
37661da177e4SLinus Torvalds 			tapeDCDB->cmd_attribute |= IPS_DISCONNECT_ALLOWED;
37671da177e4SLinus Torvalds 			tapeDCDB->cmd_attribute &= ~IPS_TRANSFER64K;	/* Always Turn OFF 64K Size Flag */
37681da177e4SLinus Torvalds 
37691da177e4SLinus Torvalds 			if (TimeOut) {
37701da177e4SLinus Torvalds 				if (TimeOut < (10 * HZ))
37711da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */
37721da177e4SLinus Torvalds 				else if (TimeOut < (60 * HZ))
37731da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */
37741da177e4SLinus Torvalds 				else if (TimeOut < (1200 * HZ))
37751da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */
37761da177e4SLinus Torvalds 			}
37771da177e4SLinus Torvalds 
37781da177e4SLinus Torvalds 			tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len;
37791da177e4SLinus Torvalds 			tapeDCDB->reserved_for_LUN = 0;
37801da177e4SLinus Torvalds 			tapeDCDB->transfer_length = scb->data_len;
37811da177e4SLinus Torvalds 			if (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG)
37821da177e4SLinus Torvalds 				tapeDCDB->buffer_pointer =
37831da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
37841da177e4SLinus Torvalds 			else
37851da177e4SLinus Torvalds 				tapeDCDB->buffer_pointer =
37861da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
37871da177e4SLinus Torvalds 			tapeDCDB->sg_count = scb->sg_len;
37881da177e4SLinus Torvalds 			tapeDCDB->sense_length = sizeof (tapeDCDB->sense_info);
37891da177e4SLinus Torvalds 			tapeDCDB->scsi_status = 0;
37901da177e4SLinus Torvalds 			tapeDCDB->reserved = 0;
37911da177e4SLinus Torvalds 			memcpy(tapeDCDB->scsi_cdb, scb->scsi_cmd->cmnd,
37921da177e4SLinus Torvalds 			       scb->scsi_cmd->cmd_len);
37931da177e4SLinus Torvalds 		} else {
37941da177e4SLinus Torvalds 			if (!scb->sg_len) {
37951da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_DCDB;
37961da177e4SLinus Torvalds 			} else {
37971da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_DCDB_SG;
37981da177e4SLinus Torvalds 				scb->cmd.dcdb.enhanced_sg =
37991da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
38001da177e4SLinus Torvalds 			}
38011da177e4SLinus Torvalds 
38021da177e4SLinus Torvalds 			scb->dcdb.device_address =
38031da177e4SLinus Torvalds 			    ((scb->bus - 1) << 4) | scb->target_id;
38041da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_DISCONNECT_ALLOWED;
38051da177e4SLinus Torvalds 
38061da177e4SLinus Torvalds 			if (TimeOut) {
38071da177e4SLinus Torvalds 				if (TimeOut < (10 * HZ))
38081da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */
38091da177e4SLinus Torvalds 				else if (TimeOut < (60 * HZ))
38101da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */
38111da177e4SLinus Torvalds 				else if (TimeOut < (1200 * HZ))
38121da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */
38131da177e4SLinus Torvalds 			}
38141da177e4SLinus Torvalds 
38151da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
38161da177e4SLinus Torvalds 			if (scb->dcdb.cmd_attribute & IPS_TRANSFER64K)
38171da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
38181da177e4SLinus Torvalds 			if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB_SG)
38191da177e4SLinus Torvalds 				scb->dcdb.buffer_pointer =
38201da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
38211da177e4SLinus Torvalds 			else
38221da177e4SLinus Torvalds 				scb->dcdb.buffer_pointer =
38231da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
38241da177e4SLinus Torvalds 			scb->dcdb.cdb_length = scb->scsi_cmd->cmd_len;
38251da177e4SLinus Torvalds 			scb->dcdb.sense_length = sizeof (scb->dcdb.sense_info);
38261da177e4SLinus Torvalds 			scb->dcdb.sg_count = scb->sg_len;
38271da177e4SLinus Torvalds 			scb->dcdb.reserved = 0;
38281da177e4SLinus Torvalds 			memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd,
38291da177e4SLinus Torvalds 			       scb->scsi_cmd->cmd_len);
38301da177e4SLinus Torvalds 			scb->dcdb.scsi_status = 0;
38311da177e4SLinus Torvalds 			scb->dcdb.reserved2[0] = 0;
38321da177e4SLinus Torvalds 			scb->dcdb.reserved2[1] = 0;
38331da177e4SLinus Torvalds 			scb->dcdb.reserved2[2] = 0;
38341da177e4SLinus Torvalds 		}
38351da177e4SLinus Torvalds 	}
38361da177e4SLinus Torvalds 
38371da177e4SLinus Torvalds 	return ((*ha->func.issue) (ha, scb));
38381da177e4SLinus Torvalds }
38391da177e4SLinus Torvalds 
38401da177e4SLinus Torvalds /****************************************************************************/
38411da177e4SLinus Torvalds /*                                                                          */
38421da177e4SLinus Torvalds /* Routine Name: ips_chk_status                                             */
38431da177e4SLinus Torvalds /*                                                                          */
38441da177e4SLinus Torvalds /* Routine Description:                                                     */
38451da177e4SLinus Torvalds /*                                                                          */
38461da177e4SLinus Torvalds /*   Check the status of commands to logical drives                         */
38471da177e4SLinus Torvalds /*   Assumed to be called with the HA lock                                  */
38481da177e4SLinus Torvalds /****************************************************************************/
38491da177e4SLinus Torvalds static void
38501da177e4SLinus Torvalds ips_chkstatus(ips_ha_t * ha, IPS_STATUS * pstatus)
38511da177e4SLinus Torvalds {
38521da177e4SLinus Torvalds 	ips_scb_t *scb;
38531da177e4SLinus Torvalds 	ips_stat_t *sp;
38541da177e4SLinus Torvalds 	uint8_t basic_status;
38551da177e4SLinus Torvalds 	uint8_t ext_status;
38561da177e4SLinus Torvalds 	int errcode;
3857a5b3c86eSJack Hammer 	IPS_SCSI_INQ_DATA inquiryData;
38581da177e4SLinus Torvalds 
38591da177e4SLinus Torvalds 	METHOD_TRACE("ips_chkstatus", 1);
38601da177e4SLinus Torvalds 
38611da177e4SLinus Torvalds 	scb = &ha->scbs[pstatus->fields.command_id];
38621da177e4SLinus Torvalds 	scb->basic_status = basic_status =
38631da177e4SLinus Torvalds 	    pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
38641da177e4SLinus Torvalds 	scb->extended_status = ext_status = pstatus->fields.extended_status;
38651da177e4SLinus Torvalds 
38661da177e4SLinus Torvalds 	sp = &ha->sp;
38671da177e4SLinus Torvalds 	sp->residue_len = 0;
38681da177e4SLinus Torvalds 	sp->scb_addr = (void *) scb;
38691da177e4SLinus Torvalds 
38701da177e4SLinus Torvalds 	/* Remove the item from the active queue */
38711da177e4SLinus Torvalds 	ips_removeq_scb(&ha->scb_activelist, scb);
38721da177e4SLinus Torvalds 
38731da177e4SLinus Torvalds 	if (!scb->scsi_cmd)
38741da177e4SLinus Torvalds 		/* internal commands are handled in do_ipsintr */
38751da177e4SLinus Torvalds 		return;
38761da177e4SLinus Torvalds 
38771da177e4SLinus Torvalds 	DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
38781da177e4SLinus Torvalds 		  ips_name,
38791da177e4SLinus Torvalds 		  ha->host_num,
38801da177e4SLinus Torvalds 		  scb->cdb[0],
38811da177e4SLinus Torvalds 		  scb->cmd.basic_io.command_id,
38821da177e4SLinus Torvalds 		  scb->bus, scb->target_id, scb->lun);
38831da177e4SLinus Torvalds 
38841da177e4SLinus Torvalds 	if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
38851da177e4SLinus Torvalds 		/* passthru - just returns the raw result */
38861da177e4SLinus Torvalds 		return;
38871da177e4SLinus Torvalds 
38881da177e4SLinus Torvalds 	errcode = DID_OK;
38891da177e4SLinus Torvalds 
38901da177e4SLinus Torvalds 	if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
38911da177e4SLinus Torvalds 	    ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
38921da177e4SLinus Torvalds 
38931da177e4SLinus Torvalds 		if (scb->bus == 0) {
38941da177e4SLinus Torvalds 			if ((basic_status & IPS_GSC_STATUS_MASK) ==
38951da177e4SLinus Torvalds 			    IPS_CMD_RECOVERED_ERROR) {
38961da177e4SLinus Torvalds 				DEBUG_VAR(1,
38971da177e4SLinus Torvalds 					  "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
38981da177e4SLinus Torvalds 					  ips_name, ha->host_num,
38991da177e4SLinus Torvalds 					  scb->cmd.basic_io.op_code,
39001da177e4SLinus Torvalds 					  basic_status, ext_status);
39011da177e4SLinus Torvalds 			}
39021da177e4SLinus Torvalds 
39031da177e4SLinus Torvalds 			switch (scb->scsi_cmd->cmnd[0]) {
39041da177e4SLinus Torvalds 			case ALLOW_MEDIUM_REMOVAL:
39051da177e4SLinus Torvalds 			case REZERO_UNIT:
39061da177e4SLinus Torvalds 			case ERASE:
39071da177e4SLinus Torvalds 			case WRITE_FILEMARKS:
39081da177e4SLinus Torvalds 			case SPACE:
39091da177e4SLinus Torvalds 				errcode = DID_ERROR;
39101da177e4SLinus Torvalds 				break;
39111da177e4SLinus Torvalds 
39121da177e4SLinus Torvalds 			case START_STOP:
39131da177e4SLinus Torvalds 				break;
39141da177e4SLinus Torvalds 
39151da177e4SLinus Torvalds 			case TEST_UNIT_READY:
39161da177e4SLinus Torvalds 				if (!ips_online(ha, scb)) {
39171da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
39181da177e4SLinus Torvalds 				}
39191da177e4SLinus Torvalds 				break;
39201da177e4SLinus Torvalds 
39211da177e4SLinus Torvalds 			case INQUIRY:
39221da177e4SLinus Torvalds 				if (ips_online(ha, scb)) {
39231da177e4SLinus Torvalds 					ips_inquiry(ha, scb);
39241da177e4SLinus Torvalds 				} else {
39251da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
39261da177e4SLinus Torvalds 				}
39271da177e4SLinus Torvalds 				break;
39281da177e4SLinus Torvalds 
39291da177e4SLinus Torvalds 			case REQUEST_SENSE:
39301da177e4SLinus Torvalds 				ips_reqsen(ha, scb);
39311da177e4SLinus Torvalds 				break;
39321da177e4SLinus Torvalds 
39331da177e4SLinus Torvalds 			case READ_6:
39341da177e4SLinus Torvalds 			case WRITE_6:
39351da177e4SLinus Torvalds 			case READ_10:
39361da177e4SLinus Torvalds 			case WRITE_10:
39371da177e4SLinus Torvalds 			case RESERVE:
39381da177e4SLinus Torvalds 			case RELEASE:
39391da177e4SLinus Torvalds 				break;
39401da177e4SLinus Torvalds 
39411da177e4SLinus Torvalds 			case MODE_SENSE:
39421da177e4SLinus Torvalds 				if (!ips_online(ha, scb)
39431da177e4SLinus Torvalds 				    || !ips_msense(ha, scb)) {
39441da177e4SLinus Torvalds 					errcode = DID_ERROR;
39451da177e4SLinus Torvalds 				}
39461da177e4SLinus Torvalds 				break;
39471da177e4SLinus Torvalds 
39481da177e4SLinus Torvalds 			case READ_CAPACITY:
39491da177e4SLinus Torvalds 				if (ips_online(ha, scb))
39501da177e4SLinus Torvalds 					ips_rdcap(ha, scb);
39511da177e4SLinus Torvalds 				else {
39521da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
39531da177e4SLinus Torvalds 				}
39541da177e4SLinus Torvalds 				break;
39551da177e4SLinus Torvalds 
39561da177e4SLinus Torvalds 			case SEND_DIAGNOSTIC:
39571da177e4SLinus Torvalds 			case REASSIGN_BLOCKS:
39581da177e4SLinus Torvalds 				break;
39591da177e4SLinus Torvalds 
39601da177e4SLinus Torvalds 			case FORMAT_UNIT:
39611da177e4SLinus Torvalds 				errcode = DID_ERROR;
39621da177e4SLinus Torvalds 				break;
39631da177e4SLinus Torvalds 
39641da177e4SLinus Torvalds 			case SEEK_10:
39651da177e4SLinus Torvalds 			case VERIFY:
39661da177e4SLinus Torvalds 			case READ_DEFECT_DATA:
39671da177e4SLinus Torvalds 			case READ_BUFFER:
39681da177e4SLinus Torvalds 			case WRITE_BUFFER:
39691da177e4SLinus Torvalds 				break;
39701da177e4SLinus Torvalds 
39711da177e4SLinus Torvalds 			default:
39721da177e4SLinus Torvalds 				errcode = DID_ERROR;
39731da177e4SLinus Torvalds 			}	/* end switch */
39741da177e4SLinus Torvalds 
39751da177e4SLinus Torvalds 			scb->scsi_cmd->result = errcode << 16;
39761da177e4SLinus Torvalds 		} else {	/* bus == 0 */
39771da177e4SLinus Torvalds 			/* restrict access to physical drives */
3978a5b3c86eSJack Hammer 			if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
3979a5b3c86eSJack Hammer 			    ips_scmd_buf_read(scb->scsi_cmd,
3980a5b3c86eSJack Hammer                                   &inquiryData, sizeof (inquiryData));
3981a5b3c86eSJack Hammer 			    if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK)
39821da177e4SLinus Torvalds 			        scb->scsi_cmd->result = DID_TIME_OUT << 16;
39831da177e4SLinus Torvalds 			}
39841da177e4SLinus Torvalds 		}		/* else */
39851da177e4SLinus Torvalds 	} else {		/* recovered error / success */
39861da177e4SLinus Torvalds 		if (scb->bus == 0) {
39871da177e4SLinus Torvalds 			DEBUG_VAR(1,
39881da177e4SLinus Torvalds 				  "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
39891da177e4SLinus Torvalds 				  ips_name, ha->host_num,
39901da177e4SLinus Torvalds 				  scb->cmd.basic_io.op_code, basic_status,
39911da177e4SLinus Torvalds 				  ext_status);
39921da177e4SLinus Torvalds 		}
39931da177e4SLinus Torvalds 
39941da177e4SLinus Torvalds 		ips_map_status(ha, scb, sp);
39951da177e4SLinus Torvalds 	}			/* else */
39961da177e4SLinus Torvalds }
39971da177e4SLinus Torvalds 
39981da177e4SLinus Torvalds /****************************************************************************/
39991da177e4SLinus Torvalds /*                                                                          */
40001da177e4SLinus Torvalds /* Routine Name: ips_online                                                 */
40011da177e4SLinus Torvalds /*                                                                          */
40021da177e4SLinus Torvalds /* Routine Description:                                                     */
40031da177e4SLinus Torvalds /*                                                                          */
40041da177e4SLinus Torvalds /*   Determine if a logical drive is online                                 */
40051da177e4SLinus Torvalds /*                                                                          */
40061da177e4SLinus Torvalds /****************************************************************************/
40071da177e4SLinus Torvalds static int
40081da177e4SLinus Torvalds ips_online(ips_ha_t * ha, ips_scb_t * scb)
40091da177e4SLinus Torvalds {
40101da177e4SLinus Torvalds 	METHOD_TRACE("ips_online", 1);
40111da177e4SLinus Torvalds 
40121da177e4SLinus Torvalds 	if (scb->target_id >= IPS_MAX_LD)
40131da177e4SLinus Torvalds 		return (0);
40141da177e4SLinus Torvalds 
40151da177e4SLinus Torvalds 	if ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1) {
40161da177e4SLinus Torvalds 		memset(ha->logical_drive_info, 0, sizeof (IPS_LD_INFO));
40171da177e4SLinus Torvalds 		return (0);
40181da177e4SLinus Torvalds 	}
40191da177e4SLinus Torvalds 
40201da177e4SLinus Torvalds 	if (ha->logical_drive_info->drive_info[scb->target_id].state !=
40211da177e4SLinus Torvalds 	    IPS_LD_OFFLINE
40221da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40231da177e4SLinus Torvalds 	    IPS_LD_FREE
40241da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40251da177e4SLinus Torvalds 	    IPS_LD_CRS
40261da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40271da177e4SLinus Torvalds 	    IPS_LD_SYS)
40281da177e4SLinus Torvalds 		return (1);
40291da177e4SLinus Torvalds 	else
40301da177e4SLinus Torvalds 		return (0);
40311da177e4SLinus Torvalds }
40321da177e4SLinus Torvalds 
40331da177e4SLinus Torvalds /****************************************************************************/
40341da177e4SLinus Torvalds /*                                                                          */
40351da177e4SLinus Torvalds /* Routine Name: ips_inquiry                                                */
40361da177e4SLinus Torvalds /*                                                                          */
40371da177e4SLinus Torvalds /* Routine Description:                                                     */
40381da177e4SLinus Torvalds /*                                                                          */
40391da177e4SLinus Torvalds /*   Simulate an inquiry command to a logical drive                         */
40401da177e4SLinus Torvalds /*                                                                          */
40411da177e4SLinus Torvalds /****************************************************************************/
40421da177e4SLinus Torvalds static int
40431da177e4SLinus Torvalds ips_inquiry(ips_ha_t * ha, ips_scb_t * scb)
40441da177e4SLinus Torvalds {
40451da177e4SLinus Torvalds 	IPS_SCSI_INQ_DATA inquiry;
40461da177e4SLinus Torvalds 
40471da177e4SLinus Torvalds 	METHOD_TRACE("ips_inquiry", 1);
40481da177e4SLinus Torvalds 
40491da177e4SLinus Torvalds 	memset(&inquiry, 0, sizeof (IPS_SCSI_INQ_DATA));
40501da177e4SLinus Torvalds 
40511da177e4SLinus Torvalds 	inquiry.DeviceType = IPS_SCSI_INQ_TYPE_DASD;
40521da177e4SLinus Torvalds 	inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED;
40531da177e4SLinus Torvalds 	inquiry.Version = IPS_SCSI_INQ_REV2;
40541da177e4SLinus Torvalds 	inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2;
40551da177e4SLinus Torvalds 	inquiry.AdditionalLength = 31;
40561da177e4SLinus Torvalds 	inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
40571da177e4SLinus Torvalds 	inquiry.Flags[1] =
40581da177e4SLinus Torvalds 	    IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync | IPS_SCSI_INQ_CmdQue;
40591da177e4SLinus Torvalds 	strncpy(inquiry.VendorId, "IBM     ", 8);
40601da177e4SLinus Torvalds 	strncpy(inquiry.ProductId, "SERVERAID       ", 16);
40611da177e4SLinus Torvalds 	strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
40621da177e4SLinus Torvalds 
40631da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &inquiry, sizeof (inquiry));
40641da177e4SLinus Torvalds 
40651da177e4SLinus Torvalds 	return (1);
40661da177e4SLinus Torvalds }
40671da177e4SLinus Torvalds 
40681da177e4SLinus Torvalds /****************************************************************************/
40691da177e4SLinus Torvalds /*                                                                          */
40701da177e4SLinus Torvalds /* Routine Name: ips_rdcap                                                  */
40711da177e4SLinus Torvalds /*                                                                          */
40721da177e4SLinus Torvalds /* Routine Description:                                                     */
40731da177e4SLinus Torvalds /*                                                                          */
40741da177e4SLinus Torvalds /*   Simulate a read capacity command to a logical drive                    */
40751da177e4SLinus Torvalds /*                                                                          */
40761da177e4SLinus Torvalds /****************************************************************************/
40771da177e4SLinus Torvalds static int
40781da177e4SLinus Torvalds ips_rdcap(ips_ha_t * ha, ips_scb_t * scb)
40791da177e4SLinus Torvalds {
40801da177e4SLinus Torvalds 	IPS_SCSI_CAPACITY cap;
40811da177e4SLinus Torvalds 
40821da177e4SLinus Torvalds 	METHOD_TRACE("ips_rdcap", 1);
40831da177e4SLinus Torvalds 
40842f4cf91cSFUJITA Tomonori 	if (scsi_bufflen(scb->scsi_cmd) < 8)
40851da177e4SLinus Torvalds 		return (0);
40861da177e4SLinus Torvalds 
40871da177e4SLinus Torvalds 	cap.lba =
40881da177e4SLinus Torvalds 	    cpu_to_be32(le32_to_cpu
40891da177e4SLinus Torvalds 			(ha->logical_drive_info->
40901da177e4SLinus Torvalds 			 drive_info[scb->target_id].sector_count) - 1);
40911da177e4SLinus Torvalds 	cap.len = cpu_to_be32((uint32_t) IPS_BLKSIZE);
40921da177e4SLinus Torvalds 
40931da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &cap, sizeof (cap));
40941da177e4SLinus Torvalds 
40951da177e4SLinus Torvalds 	return (1);
40961da177e4SLinus Torvalds }
40971da177e4SLinus Torvalds 
40981da177e4SLinus Torvalds /****************************************************************************/
40991da177e4SLinus Torvalds /*                                                                          */
41001da177e4SLinus Torvalds /* Routine Name: ips_msense                                                 */
41011da177e4SLinus Torvalds /*                                                                          */
41021da177e4SLinus Torvalds /* Routine Description:                                                     */
41031da177e4SLinus Torvalds /*                                                                          */
41041da177e4SLinus Torvalds /*   Simulate a mode sense command to a logical drive                       */
41051da177e4SLinus Torvalds /*                                                                          */
41061da177e4SLinus Torvalds /****************************************************************************/
41071da177e4SLinus Torvalds static int
41081da177e4SLinus Torvalds ips_msense(ips_ha_t * ha, ips_scb_t * scb)
41091da177e4SLinus Torvalds {
41101da177e4SLinus Torvalds 	uint16_t heads;
41111da177e4SLinus Torvalds 	uint16_t sectors;
41121da177e4SLinus Torvalds 	uint32_t cylinders;
41131da177e4SLinus Torvalds 	IPS_SCSI_MODE_PAGE_DATA mdata;
41141da177e4SLinus Torvalds 
41151da177e4SLinus Torvalds 	METHOD_TRACE("ips_msense", 1);
41161da177e4SLinus Torvalds 
41171da177e4SLinus Torvalds 	if (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) > 0x400000 &&
41181da177e4SLinus Torvalds 	    (ha->enq->ucMiscFlag & 0x8) == 0) {
41191da177e4SLinus Torvalds 		heads = IPS_NORM_HEADS;
41201da177e4SLinus Torvalds 		sectors = IPS_NORM_SECTORS;
41211da177e4SLinus Torvalds 	} else {
41221da177e4SLinus Torvalds 		heads = IPS_COMP_HEADS;
41231da177e4SLinus Torvalds 		sectors = IPS_COMP_SECTORS;
41241da177e4SLinus Torvalds 	}
41251da177e4SLinus Torvalds 
41261da177e4SLinus Torvalds 	cylinders =
41271da177e4SLinus Torvalds 	    (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) -
41281da177e4SLinus Torvalds 	     1) / (heads * sectors);
41291da177e4SLinus Torvalds 
41301da177e4SLinus Torvalds 	memset(&mdata, 0, sizeof (IPS_SCSI_MODE_PAGE_DATA));
41311da177e4SLinus Torvalds 
41321da177e4SLinus Torvalds 	mdata.hdr.BlockDescLength = 8;
41331da177e4SLinus Torvalds 
41341da177e4SLinus Torvalds 	switch (scb->scsi_cmd->cmnd[2] & 0x3f) {
41351da177e4SLinus Torvalds 	case 0x03:		/* page 3 */
41361da177e4SLinus Torvalds 		mdata.pdata.pg3.PageCode = 3;
41371da177e4SLinus Torvalds 		mdata.pdata.pg3.PageLength = sizeof (IPS_SCSI_MODE_PAGE3);
41381da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41391da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg3.PageLength;
41401da177e4SLinus Torvalds 		mdata.pdata.pg3.TracksPerZone = 0;
41411da177e4SLinus Torvalds 		mdata.pdata.pg3.AltSectorsPerZone = 0;
41421da177e4SLinus Torvalds 		mdata.pdata.pg3.AltTracksPerZone = 0;
41431da177e4SLinus Torvalds 		mdata.pdata.pg3.AltTracksPerVolume = 0;
41441da177e4SLinus Torvalds 		mdata.pdata.pg3.SectorsPerTrack = cpu_to_be16(sectors);
41451da177e4SLinus Torvalds 		mdata.pdata.pg3.BytesPerSector = cpu_to_be16(IPS_BLKSIZE);
41461da177e4SLinus Torvalds 		mdata.pdata.pg3.Interleave = cpu_to_be16(1);
41471da177e4SLinus Torvalds 		mdata.pdata.pg3.TrackSkew = 0;
41481da177e4SLinus Torvalds 		mdata.pdata.pg3.CylinderSkew = 0;
41491da177e4SLinus Torvalds 		mdata.pdata.pg3.flags = IPS_SCSI_MP3_SoftSector;
41501da177e4SLinus Torvalds 		break;
41511da177e4SLinus Torvalds 
41521da177e4SLinus Torvalds 	case 0x4:
41531da177e4SLinus Torvalds 		mdata.pdata.pg4.PageCode = 4;
41541da177e4SLinus Torvalds 		mdata.pdata.pg4.PageLength = sizeof (IPS_SCSI_MODE_PAGE4);
41551da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41561da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg4.PageLength;
41571da177e4SLinus Torvalds 		mdata.pdata.pg4.CylindersHigh =
41581da177e4SLinus Torvalds 		    cpu_to_be16((cylinders >> 8) & 0xFFFF);
41591da177e4SLinus Torvalds 		mdata.pdata.pg4.CylindersLow = (cylinders & 0xFF);
41601da177e4SLinus Torvalds 		mdata.pdata.pg4.Heads = heads;
41611da177e4SLinus Torvalds 		mdata.pdata.pg4.WritePrecompHigh = 0;
41621da177e4SLinus Torvalds 		mdata.pdata.pg4.WritePrecompLow = 0;
41631da177e4SLinus Torvalds 		mdata.pdata.pg4.ReducedWriteCurrentHigh = 0;
41641da177e4SLinus Torvalds 		mdata.pdata.pg4.ReducedWriteCurrentLow = 0;
41651da177e4SLinus Torvalds 		mdata.pdata.pg4.StepRate = cpu_to_be16(1);
41661da177e4SLinus Torvalds 		mdata.pdata.pg4.LandingZoneHigh = 0;
41671da177e4SLinus Torvalds 		mdata.pdata.pg4.LandingZoneLow = 0;
41681da177e4SLinus Torvalds 		mdata.pdata.pg4.flags = 0;
41691da177e4SLinus Torvalds 		mdata.pdata.pg4.RotationalOffset = 0;
41701da177e4SLinus Torvalds 		mdata.pdata.pg4.MediumRotationRate = 0;
41711da177e4SLinus Torvalds 		break;
41721da177e4SLinus Torvalds 	case 0x8:
41731da177e4SLinus Torvalds 		mdata.pdata.pg8.PageCode = 8;
41741da177e4SLinus Torvalds 		mdata.pdata.pg8.PageLength = sizeof (IPS_SCSI_MODE_PAGE8);
41751da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41761da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg8.PageLength;
41771da177e4SLinus Torvalds 		/* everything else is left set to 0 */
41781da177e4SLinus Torvalds 		break;
41791da177e4SLinus Torvalds 
41801da177e4SLinus Torvalds 	default:
41811da177e4SLinus Torvalds 		return (0);
41821da177e4SLinus Torvalds 	}			/* end switch */
41831da177e4SLinus Torvalds 
41841da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &mdata, sizeof (mdata));
41851da177e4SLinus Torvalds 
41861da177e4SLinus Torvalds 	return (1);
41871da177e4SLinus Torvalds }
41881da177e4SLinus Torvalds 
41891da177e4SLinus Torvalds /****************************************************************************/
41901da177e4SLinus Torvalds /*                                                                          */
41911da177e4SLinus Torvalds /* Routine Name: ips_reqsen                                                 */
41921da177e4SLinus Torvalds /*                                                                          */
41931da177e4SLinus Torvalds /* Routine Description:                                                     */
41941da177e4SLinus Torvalds /*                                                                          */
41951da177e4SLinus Torvalds /*   Simulate a request sense command to a logical drive                    */
41961da177e4SLinus Torvalds /*                                                                          */
41971da177e4SLinus Torvalds /****************************************************************************/
41981da177e4SLinus Torvalds static int
41991da177e4SLinus Torvalds ips_reqsen(ips_ha_t * ha, ips_scb_t * scb)
42001da177e4SLinus Torvalds {
42011da177e4SLinus Torvalds 	IPS_SCSI_REQSEN reqsen;
42021da177e4SLinus Torvalds 
42031da177e4SLinus Torvalds 	METHOD_TRACE("ips_reqsen", 1);
42041da177e4SLinus Torvalds 
42051da177e4SLinus Torvalds 	memset(&reqsen, 0, sizeof (IPS_SCSI_REQSEN));
42061da177e4SLinus Torvalds 
42071da177e4SLinus Torvalds 	reqsen.ResponseCode =
42081da177e4SLinus Torvalds 	    IPS_SCSI_REQSEN_VALID | IPS_SCSI_REQSEN_CURRENT_ERR;
42091da177e4SLinus Torvalds 	reqsen.AdditionalLength = 10;
42101da177e4SLinus Torvalds 	reqsen.AdditionalSenseCode = IPS_SCSI_REQSEN_NO_SENSE;
42111da177e4SLinus Torvalds 	reqsen.AdditionalSenseCodeQual = IPS_SCSI_REQSEN_NO_SENSE;
42121da177e4SLinus Torvalds 
42131da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &reqsen, sizeof (reqsen));
42141da177e4SLinus Torvalds 
42151da177e4SLinus Torvalds 	return (1);
42161da177e4SLinus Torvalds }
42171da177e4SLinus Torvalds 
42181da177e4SLinus Torvalds /****************************************************************************/
42191da177e4SLinus Torvalds /*                                                                          */
42201da177e4SLinus Torvalds /* Routine Name: ips_free                                                   */
42211da177e4SLinus Torvalds /*                                                                          */
42221da177e4SLinus Torvalds /* Routine Description:                                                     */
42231da177e4SLinus Torvalds /*                                                                          */
42241da177e4SLinus Torvalds /*   Free any allocated space for this controller                           */
42251da177e4SLinus Torvalds /*                                                                          */
42261da177e4SLinus Torvalds /****************************************************************************/
42271da177e4SLinus Torvalds static void
42281da177e4SLinus Torvalds ips_free(ips_ha_t * ha)
42291da177e4SLinus Torvalds {
42301da177e4SLinus Torvalds 
42311da177e4SLinus Torvalds 	METHOD_TRACE("ips_free", 1);
42321da177e4SLinus Torvalds 
42331da177e4SLinus Torvalds 	if (ha) {
42341da177e4SLinus Torvalds 		if (ha->enq) {
42351da177e4SLinus Torvalds 			pci_free_consistent(ha->pcidev, sizeof(IPS_ENQ),
42361da177e4SLinus Torvalds 					    ha->enq, ha->enq_busaddr);
42371da177e4SLinus Torvalds 			ha->enq = NULL;
42381da177e4SLinus Torvalds 		}
42391da177e4SLinus Torvalds 
42401da177e4SLinus Torvalds 		kfree(ha->conf);
42411da177e4SLinus Torvalds 		ha->conf = NULL;
42421da177e4SLinus Torvalds 
42431da177e4SLinus Torvalds 		if (ha->adapt) {
42441da177e4SLinus Torvalds 			pci_free_consistent(ha->pcidev,
42451da177e4SLinus Torvalds 					    sizeof (IPS_ADAPTER) +
42461da177e4SLinus Torvalds 					    sizeof (IPS_IO_CMD), ha->adapt,
42471da177e4SLinus Torvalds 					    ha->adapt->hw_status_start);
42481da177e4SLinus Torvalds 			ha->adapt = NULL;
42491da177e4SLinus Torvalds 		}
42501da177e4SLinus Torvalds 
42511da177e4SLinus Torvalds 		if (ha->logical_drive_info) {
42521da177e4SLinus Torvalds 			pci_free_consistent(ha->pcidev,
42531da177e4SLinus Torvalds 					    sizeof (IPS_LD_INFO),
42541da177e4SLinus Torvalds 					    ha->logical_drive_info,
42551da177e4SLinus Torvalds 					    ha->logical_drive_info_dma_addr);
42561da177e4SLinus Torvalds 			ha->logical_drive_info = NULL;
42571da177e4SLinus Torvalds 		}
42581da177e4SLinus Torvalds 
42591da177e4SLinus Torvalds 		kfree(ha->nvram);
42601da177e4SLinus Torvalds 		ha->nvram = NULL;
42611da177e4SLinus Torvalds 
42621da177e4SLinus Torvalds 		kfree(ha->subsys);
42631da177e4SLinus Torvalds 		ha->subsys = NULL;
42641da177e4SLinus Torvalds 
42651da177e4SLinus Torvalds 		if (ha->ioctl_data) {
42661da177e4SLinus Torvalds 			pci_free_consistent(ha->pcidev, ha->ioctl_len,
42671da177e4SLinus Torvalds 					    ha->ioctl_data, ha->ioctl_busaddr);
42681da177e4SLinus Torvalds 			ha->ioctl_data = NULL;
42691da177e4SLinus Torvalds 			ha->ioctl_datasize = 0;
42701da177e4SLinus Torvalds 			ha->ioctl_len = 0;
42711da177e4SLinus Torvalds 		}
42721da177e4SLinus Torvalds 		ips_deallocatescbs(ha, ha->max_cmds);
42731da177e4SLinus Torvalds 
42741da177e4SLinus Torvalds 		/* free memory mapped (if applicable) */
42751da177e4SLinus Torvalds 		if (ha->mem_ptr) {
42761da177e4SLinus Torvalds 			iounmap(ha->ioremap_ptr);
42771da177e4SLinus Torvalds 			ha->ioremap_ptr = NULL;
42781da177e4SLinus Torvalds 			ha->mem_ptr = NULL;
42791da177e4SLinus Torvalds 		}
42801da177e4SLinus Torvalds 
42811da177e4SLinus Torvalds 		ha->mem_addr = 0;
42821da177e4SLinus Torvalds 
42831da177e4SLinus Torvalds 	}
42841da177e4SLinus Torvalds }
42851da177e4SLinus Torvalds 
42861da177e4SLinus Torvalds /****************************************************************************/
42871da177e4SLinus Torvalds /*                                                                          */
42881da177e4SLinus Torvalds /* Routine Name: ips_deallocatescbs                                         */
42891da177e4SLinus Torvalds /*                                                                          */
42901da177e4SLinus Torvalds /* Routine Description:                                                     */
42911da177e4SLinus Torvalds /*                                                                          */
42921da177e4SLinus Torvalds /*   Free the command blocks                                                */
42931da177e4SLinus Torvalds /*                                                                          */
42941da177e4SLinus Torvalds /****************************************************************************/
42951da177e4SLinus Torvalds static int
42961da177e4SLinus Torvalds ips_deallocatescbs(ips_ha_t * ha, int cmds)
42971da177e4SLinus Torvalds {
42981da177e4SLinus Torvalds 	if (ha->scbs) {
42991da177e4SLinus Torvalds 		pci_free_consistent(ha->pcidev,
43001da177e4SLinus Torvalds 				    IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * cmds,
43011da177e4SLinus Torvalds 				    ha->scbs->sg_list.list,
43021da177e4SLinus Torvalds 				    ha->scbs->sg_busaddr);
43031da177e4SLinus Torvalds 		pci_free_consistent(ha->pcidev, sizeof (ips_scb_t) * cmds,
43041da177e4SLinus Torvalds 				    ha->scbs, ha->scbs->scb_busaddr);
43051da177e4SLinus Torvalds 		ha->scbs = NULL;
43061da177e4SLinus Torvalds 	}			/* end if */
43071da177e4SLinus Torvalds 	return 1;
43081da177e4SLinus Torvalds }
43091da177e4SLinus Torvalds 
43101da177e4SLinus Torvalds /****************************************************************************/
43111da177e4SLinus Torvalds /*                                                                          */
43121da177e4SLinus Torvalds /* Routine Name: ips_allocatescbs                                           */
43131da177e4SLinus Torvalds /*                                                                          */
43141da177e4SLinus Torvalds /* Routine Description:                                                     */
43151da177e4SLinus Torvalds /*                                                                          */
43161da177e4SLinus Torvalds /*   Allocate the command blocks                                            */
43171da177e4SLinus Torvalds /*                                                                          */
43181da177e4SLinus Torvalds /****************************************************************************/
43191da177e4SLinus Torvalds static int
43201da177e4SLinus Torvalds ips_allocatescbs(ips_ha_t * ha)
43211da177e4SLinus Torvalds {
43221da177e4SLinus Torvalds 	ips_scb_t *scb_p;
43231da177e4SLinus Torvalds 	IPS_SG_LIST ips_sg;
43241da177e4SLinus Torvalds 	int i;
43251da177e4SLinus Torvalds 	dma_addr_t command_dma, sg_dma;
43261da177e4SLinus Torvalds 
43271da177e4SLinus Torvalds 	METHOD_TRACE("ips_allocatescbs", 1);
43281da177e4SLinus Torvalds 
43291da177e4SLinus Torvalds 	/* Allocate memory for the SCBs */
43301da177e4SLinus Torvalds 	ha->scbs =
43311da177e4SLinus Torvalds 	    pci_alloc_consistent(ha->pcidev, ha->max_cmds * sizeof (ips_scb_t),
43321da177e4SLinus Torvalds 				 &command_dma);
43331da177e4SLinus Torvalds 	if (ha->scbs == NULL)
43341da177e4SLinus Torvalds 		return 0;
43351da177e4SLinus Torvalds 	ips_sg.list =
43361da177e4SLinus Torvalds 	    pci_alloc_consistent(ha->pcidev,
43371da177e4SLinus Torvalds 				 IPS_SGLIST_SIZE(ha) * IPS_MAX_SG *
43381da177e4SLinus Torvalds 				 ha->max_cmds, &sg_dma);
43391da177e4SLinus Torvalds 	if (ips_sg.list == NULL) {
43401da177e4SLinus Torvalds 		pci_free_consistent(ha->pcidev,
43411da177e4SLinus Torvalds 				    ha->max_cmds * sizeof (ips_scb_t), ha->scbs,
43421da177e4SLinus Torvalds 				    command_dma);
43431da177e4SLinus Torvalds 		return 0;
43441da177e4SLinus Torvalds 	}
43451da177e4SLinus Torvalds 
43461da177e4SLinus Torvalds 	memset(ha->scbs, 0, ha->max_cmds * sizeof (ips_scb_t));
43471da177e4SLinus Torvalds 
43481da177e4SLinus Torvalds 	for (i = 0; i < ha->max_cmds; i++) {
43491da177e4SLinus Torvalds 		scb_p = &ha->scbs[i];
43501da177e4SLinus Torvalds 		scb_p->scb_busaddr = command_dma + sizeof (ips_scb_t) * i;
43511da177e4SLinus Torvalds 		/* set up S/G list */
43521da177e4SLinus Torvalds 		if (IPS_USE_ENH_SGLIST(ha)) {
43531da177e4SLinus Torvalds 			scb_p->sg_list.enh_list =
43541da177e4SLinus Torvalds 			    ips_sg.enh_list + i * IPS_MAX_SG;
43551da177e4SLinus Torvalds 			scb_p->sg_busaddr =
43561da177e4SLinus Torvalds 			    sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
43571da177e4SLinus Torvalds 		} else {
43581da177e4SLinus Torvalds 			scb_p->sg_list.std_list =
43591da177e4SLinus Torvalds 			    ips_sg.std_list + i * IPS_MAX_SG;
43601da177e4SLinus Torvalds 			scb_p->sg_busaddr =
43611da177e4SLinus Torvalds 			    sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
43621da177e4SLinus Torvalds 		}
43631da177e4SLinus Torvalds 
43641da177e4SLinus Torvalds 		/* add to the free list */
43651da177e4SLinus Torvalds 		if (i < ha->max_cmds - 1) {
43661da177e4SLinus Torvalds 			scb_p->q_next = ha->scb_freelist;
43671da177e4SLinus Torvalds 			ha->scb_freelist = scb_p;
43681da177e4SLinus Torvalds 		}
43691da177e4SLinus Torvalds 	}
43701da177e4SLinus Torvalds 
43711da177e4SLinus Torvalds 	/* success */
43721da177e4SLinus Torvalds 	return (1);
43731da177e4SLinus Torvalds }
43741da177e4SLinus Torvalds 
43751da177e4SLinus Torvalds /****************************************************************************/
43761da177e4SLinus Torvalds /*                                                                          */
43771da177e4SLinus Torvalds /* Routine Name: ips_init_scb                                               */
43781da177e4SLinus Torvalds /*                                                                          */
43791da177e4SLinus Torvalds /* Routine Description:                                                     */
43801da177e4SLinus Torvalds /*                                                                          */
43811da177e4SLinus Torvalds /*   Initialize a CCB to default values                                     */
43821da177e4SLinus Torvalds /*                                                                          */
43831da177e4SLinus Torvalds /****************************************************************************/
43841da177e4SLinus Torvalds static void
43851da177e4SLinus Torvalds ips_init_scb(ips_ha_t * ha, ips_scb_t * scb)
43861da177e4SLinus Torvalds {
43871da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
43881da177e4SLinus Torvalds 	uint32_t cmd_busaddr, sg_busaddr;
43891da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_scb", 1);
43901da177e4SLinus Torvalds 
43911da177e4SLinus Torvalds 	if (scb == NULL)
43921da177e4SLinus Torvalds 		return;
43931da177e4SLinus Torvalds 
43941da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
43951da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
43961da177e4SLinus Torvalds 	sg_busaddr = scb->sg_busaddr;
43971da177e4SLinus Torvalds 	/* zero fill */
43981da177e4SLinus Torvalds 	memset(scb, 0, sizeof (ips_scb_t));
43991da177e4SLinus Torvalds 	memset(ha->dummy, 0, sizeof (IPS_IO_CMD));
44001da177e4SLinus Torvalds 
44011da177e4SLinus Torvalds 	/* Initialize dummy command bucket */
44021da177e4SLinus Torvalds 	ha->dummy->op_code = 0xFF;
44031da177e4SLinus Torvalds 	ha->dummy->ccsar = cpu_to_le32(ha->adapt->hw_status_start
44041da177e4SLinus Torvalds 				       + sizeof (IPS_ADAPTER));
44051da177e4SLinus Torvalds 	ha->dummy->command_id = IPS_MAX_CMDS;
44061da177e4SLinus Torvalds 
44071da177e4SLinus Torvalds 	/* set bus address of scb */
44081da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
44091da177e4SLinus Torvalds 	scb->sg_busaddr = sg_busaddr;
44101da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
44111da177e4SLinus Torvalds 
44121da177e4SLinus Torvalds 	/* Neptune Fix */
44131da177e4SLinus Torvalds 	scb->cmd.basic_io.cccr = cpu_to_le32((uint32_t) IPS_BIT_ILE);
44141da177e4SLinus Torvalds 	scb->cmd.basic_io.ccsar = cpu_to_le32(ha->adapt->hw_status_start
44151da177e4SLinus Torvalds 					      + sizeof (IPS_ADAPTER));
44161da177e4SLinus Torvalds }
44171da177e4SLinus Torvalds 
44181da177e4SLinus Torvalds /****************************************************************************/
44191da177e4SLinus Torvalds /*                                                                          */
44201da177e4SLinus Torvalds /* Routine Name: ips_get_scb                                                */
44211da177e4SLinus Torvalds /*                                                                          */
44221da177e4SLinus Torvalds /* Routine Description:                                                     */
44231da177e4SLinus Torvalds /*                                                                          */
44241da177e4SLinus Torvalds /*   Initialize a CCB to default values                                     */
44251da177e4SLinus Torvalds /*                                                                          */
442642b2aa86SJustin P. Mattock /* ASSUMED to be called from within a lock                                 */
44271da177e4SLinus Torvalds /*                                                                          */
44281da177e4SLinus Torvalds /****************************************************************************/
44291da177e4SLinus Torvalds static ips_scb_t *
44301da177e4SLinus Torvalds ips_getscb(ips_ha_t * ha)
44311da177e4SLinus Torvalds {
44321da177e4SLinus Torvalds 	ips_scb_t *scb;
44331da177e4SLinus Torvalds 
44341da177e4SLinus Torvalds 	METHOD_TRACE("ips_getscb", 1);
44351da177e4SLinus Torvalds 
44361da177e4SLinus Torvalds 	if ((scb = ha->scb_freelist) == NULL) {
44371da177e4SLinus Torvalds 
44381da177e4SLinus Torvalds 		return (NULL);
44391da177e4SLinus Torvalds 	}
44401da177e4SLinus Torvalds 
44411da177e4SLinus Torvalds 	ha->scb_freelist = scb->q_next;
44421da177e4SLinus Torvalds 	scb->flags = 0;
44431da177e4SLinus Torvalds 	scb->q_next = NULL;
44441da177e4SLinus Torvalds 
44451da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
44461da177e4SLinus Torvalds 
44471da177e4SLinus Torvalds 	return (scb);
44481da177e4SLinus Torvalds }
44491da177e4SLinus Torvalds 
44501da177e4SLinus Torvalds /****************************************************************************/
44511da177e4SLinus Torvalds /*                                                                          */
44521da177e4SLinus Torvalds /* Routine Name: ips_free_scb                                               */
44531da177e4SLinus Torvalds /*                                                                          */
44541da177e4SLinus Torvalds /* Routine Description:                                                     */
44551da177e4SLinus Torvalds /*                                                                          */
44561da177e4SLinus Torvalds /*   Return an unused CCB back to the free list                             */
44571da177e4SLinus Torvalds /*                                                                          */
44581da177e4SLinus Torvalds /* ASSUMED to be called from within a lock                                  */
44591da177e4SLinus Torvalds /*                                                                          */
44601da177e4SLinus Torvalds /****************************************************************************/
44611da177e4SLinus Torvalds static void
44621da177e4SLinus Torvalds ips_freescb(ips_ha_t * ha, ips_scb_t * scb)
44631da177e4SLinus Torvalds {
44641da177e4SLinus Torvalds 
44651da177e4SLinus Torvalds 	METHOD_TRACE("ips_freescb", 1);
44661da177e4SLinus Torvalds 	if (scb->flags & IPS_SCB_MAP_SG)
44672f4cf91cSFUJITA Tomonori                 scsi_dma_unmap(scb->scsi_cmd);
44681da177e4SLinus Torvalds 	else if (scb->flags & IPS_SCB_MAP_SINGLE)
44691da177e4SLinus Torvalds 		pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len,
44701da177e4SLinus Torvalds 				 IPS_DMA_DIR(scb));
44711da177e4SLinus Torvalds 
44721da177e4SLinus Torvalds 	/* check to make sure this is not our "special" scb */
44731da177e4SLinus Torvalds 	if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
44741da177e4SLinus Torvalds 		scb->q_next = ha->scb_freelist;
44751da177e4SLinus Torvalds 		ha->scb_freelist = scb;
44761da177e4SLinus Torvalds 	}
44771da177e4SLinus Torvalds }
44781da177e4SLinus Torvalds 
44791da177e4SLinus Torvalds /****************************************************************************/
44801da177e4SLinus Torvalds /*                                                                          */
44811da177e4SLinus Torvalds /* Routine Name: ips_isinit_copperhead                                      */
44821da177e4SLinus Torvalds /*                                                                          */
44831da177e4SLinus Torvalds /* Routine Description:                                                     */
44841da177e4SLinus Torvalds /*                                                                          */
44851da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
44861da177e4SLinus Torvalds /*                                                                          */
44871da177e4SLinus Torvalds /****************************************************************************/
44881da177e4SLinus Torvalds static int
44891da177e4SLinus Torvalds ips_isinit_copperhead(ips_ha_t * ha)
44901da177e4SLinus Torvalds {
44911da177e4SLinus Torvalds 	uint8_t scpr;
44921da177e4SLinus Torvalds 	uint8_t isr;
44931da177e4SLinus Torvalds 
44941da177e4SLinus Torvalds 	METHOD_TRACE("ips_isinit_copperhead", 1);
44951da177e4SLinus Torvalds 
44961da177e4SLinus Torvalds 	isr = inb(ha->io_addr + IPS_REG_HISR);
44971da177e4SLinus Torvalds 	scpr = inb(ha->io_addr + IPS_REG_SCPR);
44981da177e4SLinus Torvalds 
44991da177e4SLinus Torvalds 	if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
45001da177e4SLinus Torvalds 		return (0);
45011da177e4SLinus Torvalds 	else
45021da177e4SLinus Torvalds 		return (1);
45031da177e4SLinus Torvalds }
45041da177e4SLinus Torvalds 
45051da177e4SLinus Torvalds /****************************************************************************/
45061da177e4SLinus Torvalds /*                                                                          */
45071da177e4SLinus Torvalds /* Routine Name: ips_isinit_copperhead_memio                                */
45081da177e4SLinus Torvalds /*                                                                          */
45091da177e4SLinus Torvalds /* Routine Description:                                                     */
45101da177e4SLinus Torvalds /*                                                                          */
45111da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
45121da177e4SLinus Torvalds /*                                                                          */
45131da177e4SLinus Torvalds /****************************************************************************/
45141da177e4SLinus Torvalds static int
45151da177e4SLinus Torvalds ips_isinit_copperhead_memio(ips_ha_t * ha)
45161da177e4SLinus Torvalds {
45171da177e4SLinus Torvalds 	uint8_t isr = 0;
45181da177e4SLinus Torvalds 	uint8_t scpr;
45191da177e4SLinus Torvalds 
45201da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_init_copperhead_memio", 1);
45211da177e4SLinus Torvalds 
45221da177e4SLinus Torvalds 	isr = readb(ha->mem_ptr + IPS_REG_HISR);
45231da177e4SLinus Torvalds 	scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
45241da177e4SLinus Torvalds 
45251da177e4SLinus Torvalds 	if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
45261da177e4SLinus Torvalds 		return (0);
45271da177e4SLinus Torvalds 	else
45281da177e4SLinus Torvalds 		return (1);
45291da177e4SLinus Torvalds }
45301da177e4SLinus Torvalds 
45311da177e4SLinus Torvalds /****************************************************************************/
45321da177e4SLinus Torvalds /*                                                                          */
45331da177e4SLinus Torvalds /* Routine Name: ips_isinit_morpheus                                        */
45341da177e4SLinus Torvalds /*                                                                          */
45351da177e4SLinus Torvalds /* Routine Description:                                                     */
45361da177e4SLinus Torvalds /*                                                                          */
45371da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
45381da177e4SLinus Torvalds /*                                                                          */
45391da177e4SLinus Torvalds /****************************************************************************/
45401da177e4SLinus Torvalds static int
45411da177e4SLinus Torvalds ips_isinit_morpheus(ips_ha_t * ha)
45421da177e4SLinus Torvalds {
45431da177e4SLinus Torvalds 	uint32_t post;
45441da177e4SLinus Torvalds 	uint32_t bits;
45451da177e4SLinus Torvalds 
45461da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_init_morpheus", 1);
45471da177e4SLinus Torvalds 
4548ee807c2dSJack Hammer 	if (ips_isintr_morpheus(ha))
4549ee807c2dSJack Hammer 	    ips_flush_and_reset(ha);
4550ee807c2dSJack Hammer 
45511da177e4SLinus Torvalds 	post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
45521da177e4SLinus Torvalds 	bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
45531da177e4SLinus Torvalds 
45541da177e4SLinus Torvalds 	if (post == 0)
45551da177e4SLinus Torvalds 		return (0);
45561da177e4SLinus Torvalds 	else if (bits & 0x3)
45571da177e4SLinus Torvalds 		return (0);
45581da177e4SLinus Torvalds 	else
45591da177e4SLinus Torvalds 		return (1);
45601da177e4SLinus Torvalds }
45611da177e4SLinus Torvalds 
45621da177e4SLinus Torvalds /****************************************************************************/
45631da177e4SLinus Torvalds /*                                                                          */
4564ee807c2dSJack Hammer /* Routine Name: ips_flush_and_reset                                        */
4565ee807c2dSJack Hammer /*                                                                          */
4566ee807c2dSJack Hammer /* Routine Description:                                                     */
4567ee807c2dSJack Hammer /*                                                                          */
4568ee807c2dSJack Hammer /*   Perform cleanup ( FLUSH and RESET ) when the adapter is in an unknown  */
4569ee807c2dSJack Hammer /*   state ( was trying to INIT and an interrupt was already pending ) ...  */
4570ee807c2dSJack Hammer /*                                                                          */
4571ee807c2dSJack Hammer /****************************************************************************/
4572ee807c2dSJack Hammer static void
4573ee807c2dSJack Hammer ips_flush_and_reset(ips_ha_t *ha)
4574ee807c2dSJack Hammer {
4575ee807c2dSJack Hammer 	ips_scb_t *scb;
4576ee807c2dSJack Hammer 	int  ret;
4577ee807c2dSJack Hammer  	int  time;
4578ee807c2dSJack Hammer 	int  done;
4579ee807c2dSJack Hammer 	dma_addr_t command_dma;
4580ee807c2dSJack Hammer 
4581ee807c2dSJack Hammer 	/* Create a usuable SCB */
4582ee807c2dSJack Hammer 	scb = pci_alloc_consistent(ha->pcidev, sizeof(ips_scb_t), &command_dma);
4583ee807c2dSJack Hammer 	if (scb) {
4584ee807c2dSJack Hammer 	    memset(scb, 0, sizeof(ips_scb_t));
4585ee807c2dSJack Hammer 	    ips_init_scb(ha, scb);
4586ee807c2dSJack Hammer 	    scb->scb_busaddr = command_dma;
4587ee807c2dSJack Hammer 
4588ee807c2dSJack Hammer 	    scb->timeout = ips_cmd_timeout;
4589ee807c2dSJack Hammer 	    scb->cdb[0] = IPS_CMD_FLUSH;
4590ee807c2dSJack Hammer 
4591ee807c2dSJack Hammer 	    scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
4592ee807c2dSJack Hammer 	    scb->cmd.flush_cache.command_id = IPS_MAX_CMDS;   /* Use an ID that would otherwise not exist */
4593ee807c2dSJack Hammer 	    scb->cmd.flush_cache.state = IPS_NORM_STATE;
4594ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved = 0;
4595ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved2 = 0;
4596ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved3 = 0;
4597ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved4 = 0;
4598ee807c2dSJack Hammer 
4599ee807c2dSJack Hammer 	    ret = ips_send_cmd(ha, scb);                      /* Send the Flush Command */
4600ee807c2dSJack Hammer 
4601ee807c2dSJack Hammer 	    if (ret == IPS_SUCCESS) {
4602ee807c2dSJack Hammer 	        time = 60 * IPS_ONE_SEC;	              /* Max Wait time is 60 seconds */
4603ee807c2dSJack Hammer 	        done = 0;
4604ee807c2dSJack Hammer 
4605ee807c2dSJack Hammer 	        while ((time > 0) && (!done)) {
4606ee807c2dSJack Hammer 		   done = ips_poll_for_flush_complete(ha);
4607ee807c2dSJack Hammer 	           /* This may look evil, but it's only done during extremely rare start-up conditions ! */
4608ee807c2dSJack Hammer 	           udelay(1000);
4609ee807c2dSJack Hammer 	           time--;
4610ee807c2dSJack Hammer 	        }
4611ee807c2dSJack Hammer         }
4612ee807c2dSJack Hammer 	}
4613ee807c2dSJack Hammer 
4614ee807c2dSJack Hammer 	/* Now RESET and INIT the adapter */
4615ee807c2dSJack Hammer 	(*ha->func.reset) (ha);
4616ee807c2dSJack Hammer 
4617ee807c2dSJack Hammer 	pci_free_consistent(ha->pcidev, sizeof(ips_scb_t), scb, command_dma);
4618ee807c2dSJack Hammer 	return;
4619ee807c2dSJack Hammer }
4620ee807c2dSJack Hammer 
4621ee807c2dSJack Hammer /****************************************************************************/
4622ee807c2dSJack Hammer /*                                                                          */
4623ee807c2dSJack Hammer /* Routine Name: ips_poll_for_flush_complete                                */
4624ee807c2dSJack Hammer /*                                                                          */
4625ee807c2dSJack Hammer /* Routine Description:                                                     */
4626ee807c2dSJack Hammer /*                                                                          */
4627ee807c2dSJack Hammer /*   Poll for the Flush Command issued by ips_flush_and_reset() to complete */
4628ee807c2dSJack Hammer /*   All other responses are just taken off the queue and ignored           */
4629ee807c2dSJack Hammer /*                                                                          */
4630ee807c2dSJack Hammer /****************************************************************************/
4631ee807c2dSJack Hammer static int
4632ee807c2dSJack Hammer ips_poll_for_flush_complete(ips_ha_t * ha)
4633ee807c2dSJack Hammer {
4634ee807c2dSJack Hammer 	IPS_STATUS cstatus;
4635ee807c2dSJack Hammer 
4636ee807c2dSJack Hammer 	while (TRUE) {
4637ee807c2dSJack Hammer 	    cstatus.value = (*ha->func.statupd) (ha);
4638ee807c2dSJack Hammer 
4639ee807c2dSJack Hammer 	    if (cstatus.value == 0xffffffff)      /* If No Interrupt to process */
4640ee807c2dSJack Hammer 			break;
4641ee807c2dSJack Hammer 
4642ee807c2dSJack Hammer 	    /* Success is when we see the Flush Command ID */
4643ee807c2dSJack Hammer 	    if (cstatus.fields.command_id == IPS_MAX_CMDS)
4644ee807c2dSJack Hammer 	        return 1;
4645ee807c2dSJack Hammer 	 }
4646ee807c2dSJack Hammer 
4647ee807c2dSJack Hammer 	return 0;
46480ee957cbSJames Bottomley }
4649ee807c2dSJack Hammer 
4650ee807c2dSJack Hammer /****************************************************************************/
4651ee807c2dSJack Hammer /*                                                                          */
46521da177e4SLinus Torvalds /* Routine Name: ips_enable_int_copperhead                                  */
46531da177e4SLinus Torvalds /*                                                                          */
46541da177e4SLinus Torvalds /* Routine Description:                                                     */
46551da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46561da177e4SLinus Torvalds /*                                                                          */
46571da177e4SLinus Torvalds /****************************************************************************/
46581da177e4SLinus Torvalds static void
46591da177e4SLinus Torvalds ips_enable_int_copperhead(ips_ha_t * ha)
46601da177e4SLinus Torvalds {
46611da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_copperhead", 1);
46621da177e4SLinus Torvalds 
46631da177e4SLinus Torvalds 	outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
46641da177e4SLinus Torvalds 	inb(ha->io_addr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/
46651da177e4SLinus Torvalds }
46661da177e4SLinus Torvalds 
46671da177e4SLinus Torvalds /****************************************************************************/
46681da177e4SLinus Torvalds /*                                                                          */
46691da177e4SLinus Torvalds /* Routine Name: ips_enable_int_copperhead_memio                            */
46701da177e4SLinus Torvalds /*                                                                          */
46711da177e4SLinus Torvalds /* Routine Description:                                                     */
46721da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46731da177e4SLinus Torvalds /*                                                                          */
46741da177e4SLinus Torvalds /****************************************************************************/
46751da177e4SLinus Torvalds static void
46761da177e4SLinus Torvalds ips_enable_int_copperhead_memio(ips_ha_t * ha)
46771da177e4SLinus Torvalds {
46781da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
46791da177e4SLinus Torvalds 
46801da177e4SLinus Torvalds 	writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
46811da177e4SLinus Torvalds 	readb(ha->mem_ptr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/
46821da177e4SLinus Torvalds }
46831da177e4SLinus Torvalds 
46841da177e4SLinus Torvalds /****************************************************************************/
46851da177e4SLinus Torvalds /*                                                                          */
46861da177e4SLinus Torvalds /* Routine Name: ips_enable_int_morpheus                                    */
46871da177e4SLinus Torvalds /*                                                                          */
46881da177e4SLinus Torvalds /* Routine Description:                                                     */
46891da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46901da177e4SLinus Torvalds /*                                                                          */
46911da177e4SLinus Torvalds /****************************************************************************/
46921da177e4SLinus Torvalds static void
46931da177e4SLinus Torvalds ips_enable_int_morpheus(ips_ha_t * ha)
46941da177e4SLinus Torvalds {
46951da177e4SLinus Torvalds 	uint32_t Oimr;
46961da177e4SLinus Torvalds 
46971da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_morpheus", 1);
46981da177e4SLinus Torvalds 
46991da177e4SLinus Torvalds 	Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
47001da177e4SLinus Torvalds 	Oimr &= ~0x08;
47011da177e4SLinus Torvalds 	writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
47021da177e4SLinus Torvalds 	readl(ha->mem_ptr + IPS_REG_I960_OIMR);	/*Ensure PCI Posting Completes*/
47031da177e4SLinus Torvalds }
47041da177e4SLinus Torvalds 
47051da177e4SLinus Torvalds /****************************************************************************/
47061da177e4SLinus Torvalds /*                                                                          */
47071da177e4SLinus Torvalds /* Routine Name: ips_init_copperhead                                        */
47081da177e4SLinus Torvalds /*                                                                          */
47091da177e4SLinus Torvalds /* Routine Description:                                                     */
47101da177e4SLinus Torvalds /*                                                                          */
47111da177e4SLinus Torvalds /*   Initialize a copperhead controller                                     */
47121da177e4SLinus Torvalds /*                                                                          */
47131da177e4SLinus Torvalds /****************************************************************************/
47141da177e4SLinus Torvalds static int
47151da177e4SLinus Torvalds ips_init_copperhead(ips_ha_t * ha)
47161da177e4SLinus Torvalds {
47171da177e4SLinus Torvalds 	uint8_t Isr;
47181da177e4SLinus Torvalds 	uint8_t Cbsp;
47191da177e4SLinus Torvalds 	uint8_t PostByte[IPS_MAX_POST_BYTES];
47201da177e4SLinus Torvalds 	uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
47211da177e4SLinus Torvalds 	int i, j;
47221da177e4SLinus Torvalds 
47231da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_copperhead", 1);
47241da177e4SLinus Torvalds 
47251da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
47261da177e4SLinus Torvalds 		for (j = 0; j < 45; j++) {
47271da177e4SLinus Torvalds 			Isr = inb(ha->io_addr + IPS_REG_HISR);
47281da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
47291da177e4SLinus Torvalds 				break;
47301da177e4SLinus Torvalds 
47311da177e4SLinus Torvalds 			/* Delay for 1 Second */
4732bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
47331da177e4SLinus Torvalds 		}
47341da177e4SLinus Torvalds 
47351da177e4SLinus Torvalds 		if (j >= 45)
47361da177e4SLinus Torvalds 			/* error occurred */
47371da177e4SLinus Torvalds 			return (0);
47381da177e4SLinus Torvalds 
47391da177e4SLinus Torvalds 		PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
47401da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
47411da177e4SLinus Torvalds 	}
47421da177e4SLinus Torvalds 
47431da177e4SLinus Torvalds 	if (PostByte[0] < IPS_GOOD_POST_STATUS) {
47441da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
47451da177e4SLinus Torvalds 			   "reset controller fails (post status %x %x).\n",
47461da177e4SLinus Torvalds 			   PostByte[0], PostByte[1]);
47471da177e4SLinus Torvalds 
47481da177e4SLinus Torvalds 		return (0);
47491da177e4SLinus Torvalds 	}
47501da177e4SLinus Torvalds 
47511da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
47521da177e4SLinus Torvalds 		for (j = 0; j < 240; j++) {
47531da177e4SLinus Torvalds 			Isr = inb(ha->io_addr + IPS_REG_HISR);
47541da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
47551da177e4SLinus Torvalds 				break;
47561da177e4SLinus Torvalds 
47571da177e4SLinus Torvalds 			/* Delay for 1 Second */
4758bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
47591da177e4SLinus Torvalds 		}
47601da177e4SLinus Torvalds 
47611da177e4SLinus Torvalds 		if (j >= 240)
47621da177e4SLinus Torvalds 			/* error occurred */
47631da177e4SLinus Torvalds 			return (0);
47641da177e4SLinus Torvalds 
47651da177e4SLinus Torvalds 		ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
47661da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
47671da177e4SLinus Torvalds 	}
47681da177e4SLinus Torvalds 
47691da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
47701da177e4SLinus Torvalds 		Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
47711da177e4SLinus Torvalds 
47721da177e4SLinus Torvalds 		if ((Cbsp & IPS_BIT_OP) == 0)
47731da177e4SLinus Torvalds 			break;
47741da177e4SLinus Torvalds 
47751da177e4SLinus Torvalds 		/* Delay for 1 Second */
4776bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
47771da177e4SLinus Torvalds 	}
47781da177e4SLinus Torvalds 
47791da177e4SLinus Torvalds 	if (i >= 240)
47801da177e4SLinus Torvalds 		/* reset failed */
47811da177e4SLinus Torvalds 		return (0);
47821da177e4SLinus Torvalds 
47831da177e4SLinus Torvalds 	/* setup CCCR */
4784db3cc200SJames Bottomley 	outl(0x1010, ha->io_addr + IPS_REG_CCCR);
47851da177e4SLinus Torvalds 
47861da177e4SLinus Torvalds 	/* Enable busmastering */
47871da177e4SLinus Torvalds 	outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
47881da177e4SLinus Torvalds 
47898a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
47901da177e4SLinus Torvalds 		/* fix for anaconda64 */
47911da177e4SLinus Torvalds 		outl(0, ha->io_addr + IPS_REG_NDAE);
47921da177e4SLinus Torvalds 
47931da177e4SLinus Torvalds 	/* Enable interrupts */
47941da177e4SLinus Torvalds 	outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
47951da177e4SLinus Torvalds 
47961da177e4SLinus Torvalds 	return (1);
47971da177e4SLinus Torvalds }
47981da177e4SLinus Torvalds 
47991da177e4SLinus Torvalds /****************************************************************************/
48001da177e4SLinus Torvalds /*                                                                          */
48011da177e4SLinus Torvalds /* Routine Name: ips_init_copperhead_memio                                  */
48021da177e4SLinus Torvalds /*                                                                          */
48031da177e4SLinus Torvalds /* Routine Description:                                                     */
48041da177e4SLinus Torvalds /*                                                                          */
48051da177e4SLinus Torvalds /*   Initialize a copperhead controller with memory mapped I/O              */
48061da177e4SLinus Torvalds /*                                                                          */
48071da177e4SLinus Torvalds /****************************************************************************/
48081da177e4SLinus Torvalds static int
48091da177e4SLinus Torvalds ips_init_copperhead_memio(ips_ha_t * ha)
48101da177e4SLinus Torvalds {
48111da177e4SLinus Torvalds 	uint8_t Isr = 0;
48121da177e4SLinus Torvalds 	uint8_t Cbsp;
48131da177e4SLinus Torvalds 	uint8_t PostByte[IPS_MAX_POST_BYTES];
48141da177e4SLinus Torvalds 	uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
48151da177e4SLinus Torvalds 	int i, j;
48161da177e4SLinus Torvalds 
48171da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_copperhead_memio", 1);
48181da177e4SLinus Torvalds 
48191da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
48201da177e4SLinus Torvalds 		for (j = 0; j < 45; j++) {
48211da177e4SLinus Torvalds 			Isr = readb(ha->mem_ptr + IPS_REG_HISR);
48221da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
48231da177e4SLinus Torvalds 				break;
48241da177e4SLinus Torvalds 
48251da177e4SLinus Torvalds 			/* Delay for 1 Second */
4826bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
48271da177e4SLinus Torvalds 		}
48281da177e4SLinus Torvalds 
48291da177e4SLinus Torvalds 		if (j >= 45)
48301da177e4SLinus Torvalds 			/* error occurred */
48311da177e4SLinus Torvalds 			return (0);
48321da177e4SLinus Torvalds 
48331da177e4SLinus Torvalds 		PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
48341da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
48351da177e4SLinus Torvalds 	}
48361da177e4SLinus Torvalds 
48371da177e4SLinus Torvalds 	if (PostByte[0] < IPS_GOOD_POST_STATUS) {
48381da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
48391da177e4SLinus Torvalds 			   "reset controller fails (post status %x %x).\n",
48401da177e4SLinus Torvalds 			   PostByte[0], PostByte[1]);
48411da177e4SLinus Torvalds 
48421da177e4SLinus Torvalds 		return (0);
48431da177e4SLinus Torvalds 	}
48441da177e4SLinus Torvalds 
48451da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
48461da177e4SLinus Torvalds 		for (j = 0; j < 240; j++) {
48471da177e4SLinus Torvalds 			Isr = readb(ha->mem_ptr + IPS_REG_HISR);
48481da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
48491da177e4SLinus Torvalds 				break;
48501da177e4SLinus Torvalds 
48511da177e4SLinus Torvalds 			/* Delay for 1 Second */
4852bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
48531da177e4SLinus Torvalds 		}
48541da177e4SLinus Torvalds 
48551da177e4SLinus Torvalds 		if (j >= 240)
48561da177e4SLinus Torvalds 			/* error occurred */
48571da177e4SLinus Torvalds 			return (0);
48581da177e4SLinus Torvalds 
48591da177e4SLinus Torvalds 		ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
48601da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
48611da177e4SLinus Torvalds 	}
48621da177e4SLinus Torvalds 
48631da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
48641da177e4SLinus Torvalds 		Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
48651da177e4SLinus Torvalds 
48661da177e4SLinus Torvalds 		if ((Cbsp & IPS_BIT_OP) == 0)
48671da177e4SLinus Torvalds 			break;
48681da177e4SLinus Torvalds 
48691da177e4SLinus Torvalds 		/* Delay for 1 Second */
4870bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
48711da177e4SLinus Torvalds 	}
48721da177e4SLinus Torvalds 
48731da177e4SLinus Torvalds 	if (i >= 240)
48741da177e4SLinus Torvalds 		/* error occurred */
48751da177e4SLinus Torvalds 		return (0);
48761da177e4SLinus Torvalds 
48771da177e4SLinus Torvalds 	/* setup CCCR */
48781da177e4SLinus Torvalds 	writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
48791da177e4SLinus Torvalds 
48801da177e4SLinus Torvalds 	/* Enable busmastering */
48811da177e4SLinus Torvalds 	writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
48821da177e4SLinus Torvalds 
48838a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
48841da177e4SLinus Torvalds 		/* fix for anaconda64 */
48851da177e4SLinus Torvalds 		writel(0, ha->mem_ptr + IPS_REG_NDAE);
48861da177e4SLinus Torvalds 
48871da177e4SLinus Torvalds 	/* Enable interrupts */
48881da177e4SLinus Torvalds 	writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
48891da177e4SLinus Torvalds 
48901da177e4SLinus Torvalds 	/* if we get here then everything went OK */
48911da177e4SLinus Torvalds 	return (1);
48921da177e4SLinus Torvalds }
48931da177e4SLinus Torvalds 
48941da177e4SLinus Torvalds /****************************************************************************/
48951da177e4SLinus Torvalds /*                                                                          */
48961da177e4SLinus Torvalds /* Routine Name: ips_init_morpheus                                          */
48971da177e4SLinus Torvalds /*                                                                          */
48981da177e4SLinus Torvalds /* Routine Description:                                                     */
48991da177e4SLinus Torvalds /*                                                                          */
49001da177e4SLinus Torvalds /*   Initialize a morpheus controller                                       */
49011da177e4SLinus Torvalds /*                                                                          */
49021da177e4SLinus Torvalds /****************************************************************************/
49031da177e4SLinus Torvalds static int
49041da177e4SLinus Torvalds ips_init_morpheus(ips_ha_t * ha)
49051da177e4SLinus Torvalds {
49061da177e4SLinus Torvalds 	uint32_t Post;
49071da177e4SLinus Torvalds 	uint32_t Config;
49081da177e4SLinus Torvalds 	uint32_t Isr;
49091da177e4SLinus Torvalds 	uint32_t Oimr;
49101da177e4SLinus Torvalds 	int i;
49111da177e4SLinus Torvalds 
49121da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_morpheus", 1);
49131da177e4SLinus Torvalds 
49141da177e4SLinus Torvalds 	/* Wait up to 45 secs for Post */
49151da177e4SLinus Torvalds 	for (i = 0; i < 45; i++) {
49161da177e4SLinus Torvalds 		Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
49171da177e4SLinus Torvalds 
49181da177e4SLinus Torvalds 		if (Isr & IPS_BIT_I960_MSG0I)
49191da177e4SLinus Torvalds 			break;
49201da177e4SLinus Torvalds 
49211da177e4SLinus Torvalds 		/* Delay for 1 Second */
4922bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
49231da177e4SLinus Torvalds 	}
49241da177e4SLinus Torvalds 
49251da177e4SLinus Torvalds 	if (i >= 45) {
49261da177e4SLinus Torvalds 		/* error occurred */
49271da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49281da177e4SLinus Torvalds 			   "timeout waiting for post.\n");
49291da177e4SLinus Torvalds 
49301da177e4SLinus Torvalds 		return (0);
49311da177e4SLinus Torvalds 	}
49321da177e4SLinus Torvalds 
49331da177e4SLinus Torvalds 	Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
49341da177e4SLinus Torvalds 
49351da177e4SLinus Torvalds 	if (Post == 0x4F00) {	/* If Flashing the Battery PIC         */
49361da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49371da177e4SLinus Torvalds 			   "Flashing Battery PIC, Please wait ...\n");
49381da177e4SLinus Torvalds 
49391da177e4SLinus Torvalds 		/* Clear the interrupt bit */
49401da177e4SLinus Torvalds 		Isr = (uint32_t) IPS_BIT_I960_MSG0I;
49411da177e4SLinus Torvalds 		writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49421da177e4SLinus Torvalds 
49431da177e4SLinus Torvalds 		for (i = 0; i < 120; i++) {	/*    Wait Up to 2 Min. for Completion */
49441da177e4SLinus Torvalds 			Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
49451da177e4SLinus Torvalds 			if (Post != 0x4F00)
49461da177e4SLinus Torvalds 				break;
49471da177e4SLinus Torvalds 			/* Delay for 1 Second */
4948bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
49491da177e4SLinus Torvalds 		}
49501da177e4SLinus Torvalds 
49511da177e4SLinus Torvalds 		if (i >= 120) {
49521da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
49531da177e4SLinus Torvalds 				   "timeout waiting for Battery PIC Flash\n");
49541da177e4SLinus Torvalds 			return (0);
49551da177e4SLinus Torvalds 		}
49561da177e4SLinus Torvalds 
49571da177e4SLinus Torvalds 	}
49581da177e4SLinus Torvalds 
49591da177e4SLinus Torvalds 	/* Clear the interrupt bit */
49601da177e4SLinus Torvalds 	Isr = (uint32_t) IPS_BIT_I960_MSG0I;
49611da177e4SLinus Torvalds 	writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49621da177e4SLinus Torvalds 
49631da177e4SLinus Torvalds 	if (Post < (IPS_GOOD_POST_STATUS << 8)) {
49641da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49651da177e4SLinus Torvalds 			   "reset controller fails (post status %x).\n", Post);
49661da177e4SLinus Torvalds 
49671da177e4SLinus Torvalds 		return (0);
49681da177e4SLinus Torvalds 	}
49691da177e4SLinus Torvalds 
49701da177e4SLinus Torvalds 	/* Wait up to 240 secs for config bytes */
49711da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
49721da177e4SLinus Torvalds 		Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
49731da177e4SLinus Torvalds 
49741da177e4SLinus Torvalds 		if (Isr & IPS_BIT_I960_MSG1I)
49751da177e4SLinus Torvalds 			break;
49761da177e4SLinus Torvalds 
49771da177e4SLinus Torvalds 		/* Delay for 1 Second */
4978bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
49791da177e4SLinus Torvalds 	}
49801da177e4SLinus Torvalds 
49811da177e4SLinus Torvalds 	if (i >= 240) {
49821da177e4SLinus Torvalds 		/* error occurred */
49831da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49841da177e4SLinus Torvalds 			   "timeout waiting for config.\n");
49851da177e4SLinus Torvalds 
49861da177e4SLinus Torvalds 		return (0);
49871da177e4SLinus Torvalds 	}
49881da177e4SLinus Torvalds 
49891da177e4SLinus Torvalds 	Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
49901da177e4SLinus Torvalds 
49911da177e4SLinus Torvalds 	/* Clear interrupt bit */
49921da177e4SLinus Torvalds 	Isr = (uint32_t) IPS_BIT_I960_MSG1I;
49931da177e4SLinus Torvalds 	writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49941da177e4SLinus Torvalds 
49951da177e4SLinus Torvalds 	/* Turn on the interrupts */
49961da177e4SLinus Torvalds 	Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
49971da177e4SLinus Torvalds 	Oimr &= ~0x8;
49981da177e4SLinus Torvalds 	writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
49991da177e4SLinus Torvalds 
50001da177e4SLinus Torvalds 	/* if we get here then everything went OK */
50011da177e4SLinus Torvalds 
50021da177e4SLinus Torvalds 	/* Since we did a RESET, an EraseStripeLock may be needed */
50031da177e4SLinus Torvalds 	if (Post == 0xEF10) {
50041da177e4SLinus Torvalds 		if ((Config == 0x000F) || (Config == 0x0009))
50051da177e4SLinus Torvalds 			ha->requires_esl = 1;
50061da177e4SLinus Torvalds 	}
50071da177e4SLinus Torvalds 
50081da177e4SLinus Torvalds 	return (1);
50091da177e4SLinus Torvalds }
50101da177e4SLinus Torvalds 
50111da177e4SLinus Torvalds /****************************************************************************/
50121da177e4SLinus Torvalds /*                                                                          */
50131da177e4SLinus Torvalds /* Routine Name: ips_reset_copperhead                                       */
50141da177e4SLinus Torvalds /*                                                                          */
50151da177e4SLinus Torvalds /* Routine Description:                                                     */
50161da177e4SLinus Torvalds /*                                                                          */
50171da177e4SLinus Torvalds /*   Reset the controller                                                   */
50181da177e4SLinus Torvalds /*                                                                          */
50191da177e4SLinus Torvalds /****************************************************************************/
50201da177e4SLinus Torvalds static int
50211da177e4SLinus Torvalds ips_reset_copperhead(ips_ha_t * ha)
50221da177e4SLinus Torvalds {
50231da177e4SLinus Torvalds 	int reset_counter;
50241da177e4SLinus Torvalds 
50251da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_copperhead", 1);
50261da177e4SLinus Torvalds 
50271da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
50288a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->io_addr, ha->pcidev->irq);
50291da177e4SLinus Torvalds 
50301da177e4SLinus Torvalds 	reset_counter = 0;
50311da177e4SLinus Torvalds 
50321da177e4SLinus Torvalds 	while (reset_counter < 2) {
50331da177e4SLinus Torvalds 		reset_counter++;
50341da177e4SLinus Torvalds 
50351da177e4SLinus Torvalds 		outb(IPS_BIT_RST, ha->io_addr + IPS_REG_SCPR);
50361da177e4SLinus Torvalds 
50371da177e4SLinus Torvalds 		/* Delay for 1 Second */
5038bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50391da177e4SLinus Torvalds 
50401da177e4SLinus Torvalds 		outb(0, ha->io_addr + IPS_REG_SCPR);
50411da177e4SLinus Torvalds 
50421da177e4SLinus Torvalds 		/* Delay for 1 Second */
5043bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50441da177e4SLinus Torvalds 
50451da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
50461da177e4SLinus Torvalds 			break;
50471da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
50481da177e4SLinus Torvalds 
50491da177e4SLinus Torvalds 			return (0);
50501da177e4SLinus Torvalds 		}
50511da177e4SLinus Torvalds 	}
50521da177e4SLinus Torvalds 
50531da177e4SLinus Torvalds 	return (1);
50541da177e4SLinus Torvalds }
50551da177e4SLinus Torvalds 
50561da177e4SLinus Torvalds /****************************************************************************/
50571da177e4SLinus Torvalds /*                                                                          */
50581da177e4SLinus Torvalds /* Routine Name: ips_reset_copperhead_memio                                 */
50591da177e4SLinus Torvalds /*                                                                          */
50601da177e4SLinus Torvalds /* Routine Description:                                                     */
50611da177e4SLinus Torvalds /*                                                                          */
50621da177e4SLinus Torvalds /*   Reset the controller                                                   */
50631da177e4SLinus Torvalds /*                                                                          */
50641da177e4SLinus Torvalds /****************************************************************************/
50651da177e4SLinus Torvalds static int
50661da177e4SLinus Torvalds ips_reset_copperhead_memio(ips_ha_t * ha)
50671da177e4SLinus Torvalds {
50681da177e4SLinus Torvalds 	int reset_counter;
50691da177e4SLinus Torvalds 
50701da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_copperhead_memio", 1);
50711da177e4SLinus Torvalds 
50721da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
50738a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq);
50741da177e4SLinus Torvalds 
50751da177e4SLinus Torvalds 	reset_counter = 0;
50761da177e4SLinus Torvalds 
50771da177e4SLinus Torvalds 	while (reset_counter < 2) {
50781da177e4SLinus Torvalds 		reset_counter++;
50791da177e4SLinus Torvalds 
50801da177e4SLinus Torvalds 		writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
50811da177e4SLinus Torvalds 
50821da177e4SLinus Torvalds 		/* Delay for 1 Second */
5083bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50841da177e4SLinus Torvalds 
50851da177e4SLinus Torvalds 		writeb(0, ha->mem_ptr + IPS_REG_SCPR);
50861da177e4SLinus Torvalds 
50871da177e4SLinus Torvalds 		/* Delay for 1 Second */
5088bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50891da177e4SLinus Torvalds 
50901da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
50911da177e4SLinus Torvalds 			break;
50921da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
50931da177e4SLinus Torvalds 
50941da177e4SLinus Torvalds 			return (0);
50951da177e4SLinus Torvalds 		}
50961da177e4SLinus Torvalds 	}
50971da177e4SLinus Torvalds 
50981da177e4SLinus Torvalds 	return (1);
50991da177e4SLinus Torvalds }
51001da177e4SLinus Torvalds 
51011da177e4SLinus Torvalds /****************************************************************************/
51021da177e4SLinus Torvalds /*                                                                          */
51031da177e4SLinus Torvalds /* Routine Name: ips_reset_morpheus                                         */
51041da177e4SLinus Torvalds /*                                                                          */
51051da177e4SLinus Torvalds /* Routine Description:                                                     */
51061da177e4SLinus Torvalds /*                                                                          */
51071da177e4SLinus Torvalds /*   Reset the controller                                                   */
51081da177e4SLinus Torvalds /*                                                                          */
51091da177e4SLinus Torvalds /****************************************************************************/
51101da177e4SLinus Torvalds static int
51111da177e4SLinus Torvalds ips_reset_morpheus(ips_ha_t * ha)
51121da177e4SLinus Torvalds {
51131da177e4SLinus Torvalds 	int reset_counter;
51141da177e4SLinus Torvalds 	uint8_t junk;
51151da177e4SLinus Torvalds 
51161da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_morpheus", 1);
51171da177e4SLinus Torvalds 
51181da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
51198a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq);
51201da177e4SLinus Torvalds 
51211da177e4SLinus Torvalds 	reset_counter = 0;
51221da177e4SLinus Torvalds 
51231da177e4SLinus Torvalds 	while (reset_counter < 2) {
51241da177e4SLinus Torvalds 		reset_counter++;
51251da177e4SLinus Torvalds 
51261da177e4SLinus Torvalds 		writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
51271da177e4SLinus Torvalds 
51281da177e4SLinus Torvalds 		/* Delay for 5 Seconds */
5129bf471341SAndrew Morton 		MDELAY(5 * IPS_ONE_SEC);
51301da177e4SLinus Torvalds 
51311da177e4SLinus Torvalds 		/* Do a PCI config read to wait for adapter */
51321da177e4SLinus Torvalds 		pci_read_config_byte(ha->pcidev, 4, &junk);
51331da177e4SLinus Torvalds 
51341da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
51351da177e4SLinus Torvalds 			break;
51361da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
51371da177e4SLinus Torvalds 
51381da177e4SLinus Torvalds 			return (0);
51391da177e4SLinus Torvalds 		}
51401da177e4SLinus Torvalds 	}
51411da177e4SLinus Torvalds 
51421da177e4SLinus Torvalds 	return (1);
51431da177e4SLinus Torvalds }
51441da177e4SLinus Torvalds 
51451da177e4SLinus Torvalds /****************************************************************************/
51461da177e4SLinus Torvalds /*                                                                          */
51471da177e4SLinus Torvalds /* Routine Name: ips_statinit                                               */
51481da177e4SLinus Torvalds /*                                                                          */
51491da177e4SLinus Torvalds /* Routine Description:                                                     */
51501da177e4SLinus Torvalds /*                                                                          */
51511da177e4SLinus Torvalds /*   Initialize the status queues on the controller                         */
51521da177e4SLinus Torvalds /*                                                                          */
51531da177e4SLinus Torvalds /****************************************************************************/
51541da177e4SLinus Torvalds static void
51551da177e4SLinus Torvalds ips_statinit(ips_ha_t * ha)
51561da177e4SLinus Torvalds {
51571da177e4SLinus Torvalds 	uint32_t phys_status_start;
51581da177e4SLinus Torvalds 
51591da177e4SLinus Torvalds 	METHOD_TRACE("ips_statinit", 1);
51601da177e4SLinus Torvalds 
51611da177e4SLinus Torvalds 	ha->adapt->p_status_start = ha->adapt->status;
51621da177e4SLinus Torvalds 	ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
51631da177e4SLinus Torvalds 	ha->adapt->p_status_tail = ha->adapt->status;
51641da177e4SLinus Torvalds 
51651da177e4SLinus Torvalds 	phys_status_start = ha->adapt->hw_status_start;
5166db3cc200SJames Bottomley 	outl(phys_status_start, ha->io_addr + IPS_REG_SQSR);
5167db3cc200SJames Bottomley 	outl(phys_status_start + IPS_STATUS_Q_SIZE,
51681da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQER);
5169db3cc200SJames Bottomley 	outl(phys_status_start + IPS_STATUS_SIZE,
51701da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQHR);
5171db3cc200SJames Bottomley 	outl(phys_status_start, ha->io_addr + IPS_REG_SQTR);
51721da177e4SLinus Torvalds 
51731da177e4SLinus Torvalds 	ha->adapt->hw_status_tail = phys_status_start;
51741da177e4SLinus Torvalds }
51751da177e4SLinus Torvalds 
51761da177e4SLinus Torvalds /****************************************************************************/
51771da177e4SLinus Torvalds /*                                                                          */
51781da177e4SLinus Torvalds /* Routine Name: ips_statinit_memio                                         */
51791da177e4SLinus Torvalds /*                                                                          */
51801da177e4SLinus Torvalds /* Routine Description:                                                     */
51811da177e4SLinus Torvalds /*                                                                          */
51821da177e4SLinus Torvalds /*   Initialize the status queues on the controller                         */
51831da177e4SLinus Torvalds /*                                                                          */
51841da177e4SLinus Torvalds /****************************************************************************/
51851da177e4SLinus Torvalds static void
51861da177e4SLinus Torvalds ips_statinit_memio(ips_ha_t * ha)
51871da177e4SLinus Torvalds {
51881da177e4SLinus Torvalds 	uint32_t phys_status_start;
51891da177e4SLinus Torvalds 
51901da177e4SLinus Torvalds 	METHOD_TRACE("ips_statinit_memio", 1);
51911da177e4SLinus Torvalds 
51921da177e4SLinus Torvalds 	ha->adapt->p_status_start = ha->adapt->status;
51931da177e4SLinus Torvalds 	ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
51941da177e4SLinus Torvalds 	ha->adapt->p_status_tail = ha->adapt->status;
51951da177e4SLinus Torvalds 
51961da177e4SLinus Torvalds 	phys_status_start = ha->adapt->hw_status_start;
51971da177e4SLinus Torvalds 	writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
51981da177e4SLinus Torvalds 	writel(phys_status_start + IPS_STATUS_Q_SIZE,
51991da177e4SLinus Torvalds 	       ha->mem_ptr + IPS_REG_SQER);
52001da177e4SLinus Torvalds 	writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
52011da177e4SLinus Torvalds 	writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
52021da177e4SLinus Torvalds 
52031da177e4SLinus Torvalds 	ha->adapt->hw_status_tail = phys_status_start;
52041da177e4SLinus Torvalds }
52051da177e4SLinus Torvalds 
52061da177e4SLinus Torvalds /****************************************************************************/
52071da177e4SLinus Torvalds /*                                                                          */
52081da177e4SLinus Torvalds /* Routine Name: ips_statupd_copperhead                                     */
52091da177e4SLinus Torvalds /*                                                                          */
52101da177e4SLinus Torvalds /* Routine Description:                                                     */
52111da177e4SLinus Torvalds /*                                                                          */
52121da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
52131da177e4SLinus Torvalds /*                                                                          */
52141da177e4SLinus Torvalds /****************************************************************************/
52151da177e4SLinus Torvalds static uint32_t
52161da177e4SLinus Torvalds ips_statupd_copperhead(ips_ha_t * ha)
52171da177e4SLinus Torvalds {
52181da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_copperhead", 1);
52191da177e4SLinus Torvalds 
52201da177e4SLinus Torvalds 	if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
52211da177e4SLinus Torvalds 		ha->adapt->p_status_tail++;
52221da177e4SLinus Torvalds 		ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
52231da177e4SLinus Torvalds 	} else {
52241da177e4SLinus Torvalds 		ha->adapt->p_status_tail = ha->adapt->p_status_start;
52251da177e4SLinus Torvalds 		ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
52261da177e4SLinus Torvalds 	}
52271da177e4SLinus Torvalds 
5228db3cc200SJames Bottomley 	outl(ha->adapt->hw_status_tail,
52291da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQTR);
52301da177e4SLinus Torvalds 
52311da177e4SLinus Torvalds 	return (ha->adapt->p_status_tail->value);
52321da177e4SLinus Torvalds }
52331da177e4SLinus Torvalds 
52341da177e4SLinus Torvalds /****************************************************************************/
52351da177e4SLinus Torvalds /*                                                                          */
52361da177e4SLinus Torvalds /* Routine Name: ips_statupd_copperhead_memio                               */
52371da177e4SLinus Torvalds /*                                                                          */
52381da177e4SLinus Torvalds /* Routine Description:                                                     */
52391da177e4SLinus Torvalds /*                                                                          */
52401da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
52411da177e4SLinus Torvalds /*                                                                          */
52421da177e4SLinus Torvalds /****************************************************************************/
52431da177e4SLinus Torvalds static uint32_t
52441da177e4SLinus Torvalds ips_statupd_copperhead_memio(ips_ha_t * ha)
52451da177e4SLinus Torvalds {
52461da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_copperhead_memio", 1);
52471da177e4SLinus Torvalds 
52481da177e4SLinus Torvalds 	if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
52491da177e4SLinus Torvalds 		ha->adapt->p_status_tail++;
52501da177e4SLinus Torvalds 		ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
52511da177e4SLinus Torvalds 	} else {
52521da177e4SLinus Torvalds 		ha->adapt->p_status_tail = ha->adapt->p_status_start;
52531da177e4SLinus Torvalds 		ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
52541da177e4SLinus Torvalds 	}
52551da177e4SLinus Torvalds 
52561da177e4SLinus Torvalds 	writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
52571da177e4SLinus Torvalds 
52581da177e4SLinus Torvalds 	return (ha->adapt->p_status_tail->value);
52591da177e4SLinus Torvalds }
52601da177e4SLinus Torvalds 
52611da177e4SLinus Torvalds /****************************************************************************/
52621da177e4SLinus Torvalds /*                                                                          */
52631da177e4SLinus Torvalds /* Routine Name: ips_statupd_morpheus                                       */
52641da177e4SLinus Torvalds /*                                                                          */
52651da177e4SLinus Torvalds /* Routine Description:                                                     */
52661da177e4SLinus Torvalds /*                                                                          */
52671da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
52681da177e4SLinus Torvalds /*                                                                          */
52691da177e4SLinus Torvalds /****************************************************************************/
52701da177e4SLinus Torvalds static uint32_t
52711da177e4SLinus Torvalds ips_statupd_morpheus(ips_ha_t * ha)
52721da177e4SLinus Torvalds {
52731da177e4SLinus Torvalds 	uint32_t val;
52741da177e4SLinus Torvalds 
52751da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_morpheus", 1);
52761da177e4SLinus Torvalds 
52771da177e4SLinus Torvalds 	val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
52781da177e4SLinus Torvalds 
52791da177e4SLinus Torvalds 	return (val);
52801da177e4SLinus Torvalds }
52811da177e4SLinus Torvalds 
52821da177e4SLinus Torvalds /****************************************************************************/
52831da177e4SLinus Torvalds /*                                                                          */
52841da177e4SLinus Torvalds /* Routine Name: ips_issue_copperhead                                       */
52851da177e4SLinus Torvalds /*                                                                          */
52861da177e4SLinus Torvalds /* Routine Description:                                                     */
52871da177e4SLinus Torvalds /*                                                                          */
52881da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
52891da177e4SLinus Torvalds /*                                                                          */
52901da177e4SLinus Torvalds /****************************************************************************/
52911da177e4SLinus Torvalds static int
52921da177e4SLinus Torvalds ips_issue_copperhead(ips_ha_t * ha, ips_scb_t * scb)
52931da177e4SLinus Torvalds {
52941da177e4SLinus Torvalds 	uint32_t TimeOut;
52951da177e4SLinus Torvalds 	uint32_t val;
52961da177e4SLinus Torvalds 
52971da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_copperhead", 1);
52981da177e4SLinus Torvalds 
52991da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
53001da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
53011da177e4SLinus Torvalds 			  ips_name,
53021da177e4SLinus Torvalds 			  ha->host_num,
53031da177e4SLinus Torvalds 			  scb->cdb[0],
53041da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
53051da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
53061da177e4SLinus Torvalds 	} else {
53071da177e4SLinus Torvalds 		DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
53081da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
53091da177e4SLinus Torvalds 	}
53101da177e4SLinus Torvalds 
53111da177e4SLinus Torvalds 	TimeOut = 0;
53121da177e4SLinus Torvalds 
53131da177e4SLinus Torvalds 	while ((val =
53141da177e4SLinus Torvalds 		le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) {
53151da177e4SLinus Torvalds 		udelay(1000);
53161da177e4SLinus Torvalds 
53171da177e4SLinus Torvalds 		if (++TimeOut >= IPS_SEM_TIMEOUT) {
53181da177e4SLinus Torvalds 			if (!(val & IPS_BIT_START_STOP))
53191da177e4SLinus Torvalds 				break;
53201da177e4SLinus Torvalds 
53211da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53221da177e4SLinus Torvalds 				   "ips_issue val [0x%x].\n", val);
53231da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53241da177e4SLinus Torvalds 				   "ips_issue semaphore chk timeout.\n");
53251da177e4SLinus Torvalds 
53261da177e4SLinus Torvalds 			return (IPS_FAILURE);
53271da177e4SLinus Torvalds 		}		/* end if */
53281da177e4SLinus Torvalds 	}			/* end while */
53291da177e4SLinus Torvalds 
5330db3cc200SJames Bottomley 	outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR);
5331db3cc200SJames Bottomley 	outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR);
53321da177e4SLinus Torvalds 
53331da177e4SLinus Torvalds 	return (IPS_SUCCESS);
53341da177e4SLinus Torvalds }
53351da177e4SLinus Torvalds 
53361da177e4SLinus Torvalds /****************************************************************************/
53371da177e4SLinus Torvalds /*                                                                          */
53381da177e4SLinus Torvalds /* Routine Name: ips_issue_copperhead_memio                                 */
53391da177e4SLinus Torvalds /*                                                                          */
53401da177e4SLinus Torvalds /* Routine Description:                                                     */
53411da177e4SLinus Torvalds /*                                                                          */
53421da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
53431da177e4SLinus Torvalds /*                                                                          */
53441da177e4SLinus Torvalds /****************************************************************************/
53451da177e4SLinus Torvalds static int
53461da177e4SLinus Torvalds ips_issue_copperhead_memio(ips_ha_t * ha, ips_scb_t * scb)
53471da177e4SLinus Torvalds {
53481da177e4SLinus Torvalds 	uint32_t TimeOut;
53491da177e4SLinus Torvalds 	uint32_t val;
53501da177e4SLinus Torvalds 
53511da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_copperhead_memio", 1);
53521da177e4SLinus Torvalds 
53531da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
53541da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
53551da177e4SLinus Torvalds 			  ips_name,
53561da177e4SLinus Torvalds 			  ha->host_num,
53571da177e4SLinus Torvalds 			  scb->cdb[0],
53581da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
53591da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
53601da177e4SLinus Torvalds 	} else {
53611da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
53621da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
53631da177e4SLinus Torvalds 	}
53641da177e4SLinus Torvalds 
53651da177e4SLinus Torvalds 	TimeOut = 0;
53661da177e4SLinus Torvalds 
53671da177e4SLinus Torvalds 	while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
53681da177e4SLinus Torvalds 		udelay(1000);
53691da177e4SLinus Torvalds 
53701da177e4SLinus Torvalds 		if (++TimeOut >= IPS_SEM_TIMEOUT) {
53711da177e4SLinus Torvalds 			if (!(val & IPS_BIT_START_STOP))
53721da177e4SLinus Torvalds 				break;
53731da177e4SLinus Torvalds 
53741da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53751da177e4SLinus Torvalds 				   "ips_issue val [0x%x].\n", val);
53761da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53771da177e4SLinus Torvalds 				   "ips_issue semaphore chk timeout.\n");
53781da177e4SLinus Torvalds 
53791da177e4SLinus Torvalds 			return (IPS_FAILURE);
53801da177e4SLinus Torvalds 		}		/* end if */
53811da177e4SLinus Torvalds 	}			/* end while */
53821da177e4SLinus Torvalds 
53831da177e4SLinus Torvalds 	writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
53841da177e4SLinus Torvalds 	writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
53851da177e4SLinus Torvalds 
53861da177e4SLinus Torvalds 	return (IPS_SUCCESS);
53871da177e4SLinus Torvalds }
53881da177e4SLinus Torvalds 
53891da177e4SLinus Torvalds /****************************************************************************/
53901da177e4SLinus Torvalds /*                                                                          */
53911da177e4SLinus Torvalds /* Routine Name: ips_issue_i2o                                              */
53921da177e4SLinus Torvalds /*                                                                          */
53931da177e4SLinus Torvalds /* Routine Description:                                                     */
53941da177e4SLinus Torvalds /*                                                                          */
53951da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
53961da177e4SLinus Torvalds /*                                                                          */
53971da177e4SLinus Torvalds /****************************************************************************/
53981da177e4SLinus Torvalds static int
53991da177e4SLinus Torvalds ips_issue_i2o(ips_ha_t * ha, ips_scb_t * scb)
54001da177e4SLinus Torvalds {
54011da177e4SLinus Torvalds 
54021da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_i2o", 1);
54031da177e4SLinus Torvalds 
54041da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
54051da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
54061da177e4SLinus Torvalds 			  ips_name,
54071da177e4SLinus Torvalds 			  ha->host_num,
54081da177e4SLinus Torvalds 			  scb->cdb[0],
54091da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
54101da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
54111da177e4SLinus Torvalds 	} else {
54121da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
54131da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
54141da177e4SLinus Torvalds 	}
54151da177e4SLinus Torvalds 
5416db3cc200SJames Bottomley 	outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ);
54171da177e4SLinus Torvalds 
54181da177e4SLinus Torvalds 	return (IPS_SUCCESS);
54191da177e4SLinus Torvalds }
54201da177e4SLinus Torvalds 
54211da177e4SLinus Torvalds /****************************************************************************/
54221da177e4SLinus Torvalds /*                                                                          */
54231da177e4SLinus Torvalds /* Routine Name: ips_issue_i2o_memio                                        */
54241da177e4SLinus Torvalds /*                                                                          */
54251da177e4SLinus Torvalds /* Routine Description:                                                     */
54261da177e4SLinus Torvalds /*                                                                          */
54271da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
54281da177e4SLinus Torvalds /*                                                                          */
54291da177e4SLinus Torvalds /****************************************************************************/
54301da177e4SLinus Torvalds static int
54311da177e4SLinus Torvalds ips_issue_i2o_memio(ips_ha_t * ha, ips_scb_t * scb)
54321da177e4SLinus Torvalds {
54331da177e4SLinus Torvalds 
54341da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_i2o_memio", 1);
54351da177e4SLinus Torvalds 
54361da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
54371da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
54381da177e4SLinus Torvalds 			  ips_name,
54391da177e4SLinus Torvalds 			  ha->host_num,
54401da177e4SLinus Torvalds 			  scb->cdb[0],
54411da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
54421da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
54431da177e4SLinus Torvalds 	} else {
54441da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
54451da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
54461da177e4SLinus Torvalds 	}
54471da177e4SLinus Torvalds 
54481da177e4SLinus Torvalds 	writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
54491da177e4SLinus Torvalds 
54501da177e4SLinus Torvalds 	return (IPS_SUCCESS);
54511da177e4SLinus Torvalds }
54521da177e4SLinus Torvalds 
54531da177e4SLinus Torvalds /****************************************************************************/
54541da177e4SLinus Torvalds /*                                                                          */
54551da177e4SLinus Torvalds /* Routine Name: ips_isintr_copperhead                                      */
54561da177e4SLinus Torvalds /*                                                                          */
54571da177e4SLinus Torvalds /* Routine Description:                                                     */
54581da177e4SLinus Torvalds /*                                                                          */
54591da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
54601da177e4SLinus Torvalds /*                                                                          */
54611da177e4SLinus Torvalds /****************************************************************************/
54621da177e4SLinus Torvalds static int
54631da177e4SLinus Torvalds ips_isintr_copperhead(ips_ha_t * ha)
54641da177e4SLinus Torvalds {
54651da177e4SLinus Torvalds 	uint8_t Isr;
54661da177e4SLinus Torvalds 
54671da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_copperhead", 2);
54681da177e4SLinus Torvalds 
54691da177e4SLinus Torvalds 	Isr = inb(ha->io_addr + IPS_REG_HISR);
54701da177e4SLinus Torvalds 
54711da177e4SLinus Torvalds 	if (Isr == 0xFF)
54721da177e4SLinus Torvalds 		/* ?!?! Nothing really there */
54731da177e4SLinus Torvalds 		return (0);
54741da177e4SLinus Torvalds 
54751da177e4SLinus Torvalds 	if (Isr & IPS_BIT_SCE)
54761da177e4SLinus Torvalds 		return (1);
54771da177e4SLinus Torvalds 	else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
54781da177e4SLinus Torvalds 		/* status queue overflow or GHI */
54791da177e4SLinus Torvalds 		/* just clear the interrupt */
54801da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
54811da177e4SLinus Torvalds 	}
54821da177e4SLinus Torvalds 
54831da177e4SLinus Torvalds 	return (0);
54841da177e4SLinus Torvalds }
54851da177e4SLinus Torvalds 
54861da177e4SLinus Torvalds /****************************************************************************/
54871da177e4SLinus Torvalds /*                                                                          */
54881da177e4SLinus Torvalds /* Routine Name: ips_isintr_copperhead_memio                                */
54891da177e4SLinus Torvalds /*                                                                          */
54901da177e4SLinus Torvalds /* Routine Description:                                                     */
54911da177e4SLinus Torvalds /*                                                                          */
54921da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
54931da177e4SLinus Torvalds /*                                                                          */
54941da177e4SLinus Torvalds /****************************************************************************/
54951da177e4SLinus Torvalds static int
54961da177e4SLinus Torvalds ips_isintr_copperhead_memio(ips_ha_t * ha)
54971da177e4SLinus Torvalds {
54981da177e4SLinus Torvalds 	uint8_t Isr;
54991da177e4SLinus Torvalds 
55001da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_memio", 2);
55011da177e4SLinus Torvalds 
55021da177e4SLinus Torvalds 	Isr = readb(ha->mem_ptr + IPS_REG_HISR);
55031da177e4SLinus Torvalds 
55041da177e4SLinus Torvalds 	if (Isr == 0xFF)
55051da177e4SLinus Torvalds 		/* ?!?! Nothing really there */
55061da177e4SLinus Torvalds 		return (0);
55071da177e4SLinus Torvalds 
55081da177e4SLinus Torvalds 	if (Isr & IPS_BIT_SCE)
55091da177e4SLinus Torvalds 		return (1);
55101da177e4SLinus Torvalds 	else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
55111da177e4SLinus Torvalds 		/* status queue overflow or GHI */
55121da177e4SLinus Torvalds 		/* just clear the interrupt */
55131da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
55141da177e4SLinus Torvalds 	}
55151da177e4SLinus Torvalds 
55161da177e4SLinus Torvalds 	return (0);
55171da177e4SLinus Torvalds }
55181da177e4SLinus Torvalds 
55191da177e4SLinus Torvalds /****************************************************************************/
55201da177e4SLinus Torvalds /*                                                                          */
55211da177e4SLinus Torvalds /* Routine Name: ips_isintr_morpheus                                        */
55221da177e4SLinus Torvalds /*                                                                          */
55231da177e4SLinus Torvalds /* Routine Description:                                                     */
55241da177e4SLinus Torvalds /*                                                                          */
55251da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
55261da177e4SLinus Torvalds /*                                                                          */
55271da177e4SLinus Torvalds /****************************************************************************/
55281da177e4SLinus Torvalds static int
55291da177e4SLinus Torvalds ips_isintr_morpheus(ips_ha_t * ha)
55301da177e4SLinus Torvalds {
55311da177e4SLinus Torvalds 	uint32_t Isr;
55321da177e4SLinus Torvalds 
55331da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_morpheus", 2);
55341da177e4SLinus Torvalds 
55351da177e4SLinus Torvalds 	Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
55361da177e4SLinus Torvalds 
55371da177e4SLinus Torvalds 	if (Isr & IPS_BIT_I2O_OPQI)
55381da177e4SLinus Torvalds 		return (1);
55391da177e4SLinus Torvalds 	else
55401da177e4SLinus Torvalds 		return (0);
55411da177e4SLinus Torvalds }
55421da177e4SLinus Torvalds 
55431da177e4SLinus Torvalds /****************************************************************************/
55441da177e4SLinus Torvalds /*                                                                          */
55451da177e4SLinus Torvalds /* Routine Name: ips_wait                                                   */
55461da177e4SLinus Torvalds /*                                                                          */
55471da177e4SLinus Torvalds /* Routine Description:                                                     */
55481da177e4SLinus Torvalds /*                                                                          */
55491da177e4SLinus Torvalds /*   Wait for a command to complete                                         */
55501da177e4SLinus Torvalds /*                                                                          */
55511da177e4SLinus Torvalds /****************************************************************************/
55521da177e4SLinus Torvalds static int
55531da177e4SLinus Torvalds ips_wait(ips_ha_t * ha, int time, int intr)
55541da177e4SLinus Torvalds {
55551da177e4SLinus Torvalds 	int ret;
55561da177e4SLinus Torvalds 	int done;
55571da177e4SLinus Torvalds 
55581da177e4SLinus Torvalds 	METHOD_TRACE("ips_wait", 1);
55591da177e4SLinus Torvalds 
55601da177e4SLinus Torvalds 	ret = IPS_FAILURE;
55611da177e4SLinus Torvalds 	done = FALSE;
55621da177e4SLinus Torvalds 
55631da177e4SLinus Torvalds 	time *= IPS_ONE_SEC;	/* convert seconds */
55641da177e4SLinus Torvalds 
55651da177e4SLinus Torvalds 	while ((time > 0) && (!done)) {
55661da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON) {
55671da177e4SLinus Torvalds 			if (ha->waitflag == FALSE) {
55681da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
55691da177e4SLinus Torvalds 				done = TRUE;
55701da177e4SLinus Torvalds 				break;
55711da177e4SLinus Torvalds 			}
55721da177e4SLinus Torvalds 		} else if (intr == IPS_INTR_IORL) {
55731da177e4SLinus Torvalds 			if (ha->waitflag == FALSE) {
55741da177e4SLinus Torvalds 				/*
55751da177e4SLinus Torvalds 				 * controller generated an interrupt to
55761da177e4SLinus Torvalds 				 * acknowledge completion of the command
55771da177e4SLinus Torvalds 				 * and ips_intr() has serviced the interrupt.
55781da177e4SLinus Torvalds 				 */
55791da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
55801da177e4SLinus Torvalds 				done = TRUE;
55811da177e4SLinus Torvalds 				break;
55821da177e4SLinus Torvalds 			}
55831da177e4SLinus Torvalds 
55841da177e4SLinus Torvalds 			/*
55851da177e4SLinus Torvalds 			 * NOTE: we already have the io_request_lock so
55861da177e4SLinus Torvalds 			 * even if we get an interrupt it won't get serviced
55871da177e4SLinus Torvalds 			 * until after we finish.
55881da177e4SLinus Torvalds 			 */
55891da177e4SLinus Torvalds 
55901da177e4SLinus Torvalds 			(*ha->func.intr) (ha);
55911da177e4SLinus Torvalds 		}
55921da177e4SLinus Torvalds 
55931da177e4SLinus Torvalds 		/* This looks like a very evil loop, but it only does this during start-up */
55941da177e4SLinus Torvalds 		udelay(1000);
55951da177e4SLinus Torvalds 		time--;
55961da177e4SLinus Torvalds 	}
55971da177e4SLinus Torvalds 
55981da177e4SLinus Torvalds 	return (ret);
55991da177e4SLinus Torvalds }
56001da177e4SLinus Torvalds 
56011da177e4SLinus Torvalds /****************************************************************************/
56021da177e4SLinus Torvalds /*                                                                          */
56031da177e4SLinus Torvalds /* Routine Name: ips_write_driver_status                                    */
56041da177e4SLinus Torvalds /*                                                                          */
56051da177e4SLinus Torvalds /* Routine Description:                                                     */
56061da177e4SLinus Torvalds /*                                                                          */
56071da177e4SLinus Torvalds /*   Write OS/Driver version to Page 5 of the nvram on the controller       */
56081da177e4SLinus Torvalds /*                                                                          */
56091da177e4SLinus Torvalds /****************************************************************************/
56101da177e4SLinus Torvalds static int
56111da177e4SLinus Torvalds ips_write_driver_status(ips_ha_t * ha, int intr)
56121da177e4SLinus Torvalds {
56131da177e4SLinus Torvalds 	METHOD_TRACE("ips_write_driver_status", 1);
56141da177e4SLinus Torvalds 
56151da177e4SLinus Torvalds 	if (!ips_readwrite_page5(ha, FALSE, intr)) {
56161da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
56171da177e4SLinus Torvalds 			   "unable to read NVRAM page 5.\n");
56181da177e4SLinus Torvalds 
56191da177e4SLinus Torvalds 		return (0);
56201da177e4SLinus Torvalds 	}
56211da177e4SLinus Torvalds 
56221da177e4SLinus Torvalds 	/* check to make sure the page has a valid */
56231da177e4SLinus Torvalds 	/* signature */
56241da177e4SLinus Torvalds 	if (le32_to_cpu(ha->nvram->signature) != IPS_NVRAM_P5_SIG) {
56251da177e4SLinus Torvalds 		DEBUG_VAR(1,
56261da177e4SLinus Torvalds 			  "(%s%d) NVRAM page 5 has an invalid signature: %X.",
56271da177e4SLinus Torvalds 			  ips_name, ha->host_num, ha->nvram->signature);
56281da177e4SLinus Torvalds 		ha->nvram->signature = IPS_NVRAM_P5_SIG;
56291da177e4SLinus Torvalds 	}
56301da177e4SLinus Torvalds 
56311da177e4SLinus Torvalds 	DEBUG_VAR(2,
56321da177e4SLinus Torvalds 		  "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
56331da177e4SLinus Torvalds 		  ips_name, ha->host_num, le16_to_cpu(ha->nvram->adapter_type),
56341da177e4SLinus Torvalds 		  ha->nvram->adapter_slot, ha->nvram->bios_high[0],
56351da177e4SLinus Torvalds 		  ha->nvram->bios_high[1], ha->nvram->bios_high[2],
56361da177e4SLinus Torvalds 		  ha->nvram->bios_high[3], ha->nvram->bios_low[0],
56371da177e4SLinus Torvalds 		  ha->nvram->bios_low[1], ha->nvram->bios_low[2],
56381da177e4SLinus Torvalds 		  ha->nvram->bios_low[3]);
56391da177e4SLinus Torvalds 
56401da177e4SLinus Torvalds 	ips_get_bios_version(ha, intr);
56411da177e4SLinus Torvalds 
56421da177e4SLinus Torvalds 	/* change values (as needed) */
56431da177e4SLinus Torvalds 	ha->nvram->operating_system = IPS_OS_LINUX;
56441da177e4SLinus Torvalds 	ha->nvram->adapter_type = ha->ad_type;
56451da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4);
56461da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4);
56471da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->bios_high, ha->bios_version, 4);
56481da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4);
56491da177e4SLinus Torvalds 
5650a60768e2SJack Hammer 	ha->nvram->versioning = 0;	/* Indicate the Driver Does Not Support Versioning */
56511da177e4SLinus Torvalds 
56521da177e4SLinus Torvalds 	/* now update the page */
56531da177e4SLinus Torvalds 	if (!ips_readwrite_page5(ha, TRUE, intr)) {
56541da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
56551da177e4SLinus Torvalds 			   "unable to write NVRAM page 5.\n");
56561da177e4SLinus Torvalds 
56571da177e4SLinus Torvalds 		return (0);
56581da177e4SLinus Torvalds 	}
56591da177e4SLinus Torvalds 
56601da177e4SLinus Torvalds 	/* IF NVRAM Page 5 is OK, Use it for Slot Number Info Because Linux Doesn't Do Slots */
56611da177e4SLinus Torvalds 	ha->slot_num = ha->nvram->adapter_slot;
56621da177e4SLinus Torvalds 
56631da177e4SLinus Torvalds 	return (1);
56641da177e4SLinus Torvalds }
56651da177e4SLinus Torvalds 
56661da177e4SLinus Torvalds /****************************************************************************/
56671da177e4SLinus Torvalds /*                                                                          */
56681da177e4SLinus Torvalds /* Routine Name: ips_read_adapter_status                                    */
56691da177e4SLinus Torvalds /*                                                                          */
56701da177e4SLinus Torvalds /* Routine Description:                                                     */
56711da177e4SLinus Torvalds /*                                                                          */
56721da177e4SLinus Torvalds /*   Do an Inquiry command to the adapter                                   */
56731da177e4SLinus Torvalds /*                                                                          */
56741da177e4SLinus Torvalds /****************************************************************************/
56751da177e4SLinus Torvalds static int
56761da177e4SLinus Torvalds ips_read_adapter_status(ips_ha_t * ha, int intr)
56771da177e4SLinus Torvalds {
56781da177e4SLinus Torvalds 	ips_scb_t *scb;
56791da177e4SLinus Torvalds 	int ret;
56801da177e4SLinus Torvalds 
56811da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_adapter_status", 1);
56821da177e4SLinus Torvalds 
56831da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
56841da177e4SLinus Torvalds 
56851da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
56861da177e4SLinus Torvalds 
56871da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
56881da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_ENQUIRY;
56891da177e4SLinus Torvalds 
56901da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
56911da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
56921da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_count = 0;
56931da177e4SLinus Torvalds 	scb->cmd.basic_io.lba = 0;
56941da177e4SLinus Torvalds 	scb->cmd.basic_io.sector_count = 0;
56951da177e4SLinus Torvalds 	scb->cmd.basic_io.log_drv = 0;
56961da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->enq);
56971da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
56981da177e4SLinus Torvalds 
56991da177e4SLinus Torvalds 	/* send command */
57001da177e4SLinus Torvalds 	if (((ret =
57011da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
57021da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
57031da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
57041da177e4SLinus Torvalds 		return (0);
57051da177e4SLinus Torvalds 
57061da177e4SLinus Torvalds 	return (1);
57071da177e4SLinus Torvalds }
57081da177e4SLinus Torvalds 
57091da177e4SLinus Torvalds /****************************************************************************/
57101da177e4SLinus Torvalds /*                                                                          */
57111da177e4SLinus Torvalds /* Routine Name: ips_read_subsystem_parameters                              */
57121da177e4SLinus Torvalds /*                                                                          */
57131da177e4SLinus Torvalds /* Routine Description:                                                     */
57141da177e4SLinus Torvalds /*                                                                          */
57151da177e4SLinus Torvalds /*   Read subsystem parameters from the adapter                             */
57161da177e4SLinus Torvalds /*                                                                          */
57171da177e4SLinus Torvalds /****************************************************************************/
57181da177e4SLinus Torvalds static int
57191da177e4SLinus Torvalds ips_read_subsystem_parameters(ips_ha_t * ha, int intr)
57201da177e4SLinus Torvalds {
57211da177e4SLinus Torvalds 	ips_scb_t *scb;
57221da177e4SLinus Torvalds 	int ret;
57231da177e4SLinus Torvalds 
57241da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_subsystem_parameters", 1);
57251da177e4SLinus Torvalds 
57261da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
57271da177e4SLinus Torvalds 
57281da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
57291da177e4SLinus Torvalds 
57301da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
57311da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_GET_SUBSYS;
57321da177e4SLinus Torvalds 
57331da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS;
57341da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
57351da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_count = 0;
57361da177e4SLinus Torvalds 	scb->cmd.basic_io.lba = 0;
57371da177e4SLinus Torvalds 	scb->cmd.basic_io.sector_count = 0;
57381da177e4SLinus Torvalds 	scb->cmd.basic_io.log_drv = 0;
57391da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->subsys);
57401da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
57411da177e4SLinus Torvalds 
57421da177e4SLinus Torvalds 	/* send command */
57431da177e4SLinus Torvalds 	if (((ret =
57441da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
57451da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
57461da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
57471da177e4SLinus Torvalds 		return (0);
57481da177e4SLinus Torvalds 
57491da177e4SLinus Torvalds 	memcpy(ha->subsys, ha->ioctl_data, sizeof(*ha->subsys));
57501da177e4SLinus Torvalds 	return (1);
57511da177e4SLinus Torvalds }
57521da177e4SLinus Torvalds 
57531da177e4SLinus Torvalds /****************************************************************************/
57541da177e4SLinus Torvalds /*                                                                          */
57551da177e4SLinus Torvalds /* Routine Name: ips_read_config                                            */
57561da177e4SLinus Torvalds /*                                                                          */
57571da177e4SLinus Torvalds /* Routine Description:                                                     */
57581da177e4SLinus Torvalds /*                                                                          */
57591da177e4SLinus Torvalds /*   Read the configuration on the adapter                                  */
57601da177e4SLinus Torvalds /*                                                                          */
57611da177e4SLinus Torvalds /****************************************************************************/
57621da177e4SLinus Torvalds static int
57631da177e4SLinus Torvalds ips_read_config(ips_ha_t * ha, int intr)
57641da177e4SLinus Torvalds {
57651da177e4SLinus Torvalds 	ips_scb_t *scb;
57661da177e4SLinus Torvalds 	int i;
57671da177e4SLinus Torvalds 	int ret;
57681da177e4SLinus Torvalds 
57691da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_config", 1);
57701da177e4SLinus Torvalds 
57711da177e4SLinus Torvalds 	/* set defaults for initiator IDs */
57721da177e4SLinus Torvalds 	for (i = 0; i < 4; i++)
57731da177e4SLinus Torvalds 		ha->conf->init_id[i] = 7;
57741da177e4SLinus Torvalds 
57751da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
57761da177e4SLinus Torvalds 
57771da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
57781da177e4SLinus Torvalds 
57791da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
57801da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_READ_CONF;
57811da177e4SLinus Torvalds 
57821da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF;
57831da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
57841da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->conf);
57851da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
57861da177e4SLinus Torvalds 
57871da177e4SLinus Torvalds 	/* send command */
57881da177e4SLinus Torvalds 	if (((ret =
57891da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
57901da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
57911da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
57921da177e4SLinus Torvalds 
57931da177e4SLinus Torvalds 		memset(ha->conf, 0, sizeof (IPS_CONF));
57941da177e4SLinus Torvalds 
57951da177e4SLinus Torvalds 		/* reset initiator IDs */
57961da177e4SLinus Torvalds 		for (i = 0; i < 4; i++)
57971da177e4SLinus Torvalds 			ha->conf->init_id[i] = 7;
57981da177e4SLinus Torvalds 
57991da177e4SLinus Torvalds 		/* Allow Completed with Errors, so JCRM can access the Adapter to fix the problems */
58001da177e4SLinus Torvalds 		if ((scb->basic_status & IPS_GSC_STATUS_MASK) ==
58011da177e4SLinus Torvalds 		    IPS_CMD_CMPLT_WERROR)
58021da177e4SLinus Torvalds 			return (1);
58031da177e4SLinus Torvalds 
58041da177e4SLinus Torvalds 		return (0);
58051da177e4SLinus Torvalds 	}
58061da177e4SLinus Torvalds 
58071da177e4SLinus Torvalds 	memcpy(ha->conf, ha->ioctl_data, sizeof(*ha->conf));
58081da177e4SLinus Torvalds 	return (1);
58091da177e4SLinus Torvalds }
58101da177e4SLinus Torvalds 
58111da177e4SLinus Torvalds /****************************************************************************/
58121da177e4SLinus Torvalds /*                                                                          */
58131da177e4SLinus Torvalds /* Routine Name: ips_readwrite_page5                                        */
58141da177e4SLinus Torvalds /*                                                                          */
58151da177e4SLinus Torvalds /* Routine Description:                                                     */
58161da177e4SLinus Torvalds /*                                                                          */
58171da177e4SLinus Torvalds /*   Read nvram page 5 from the adapter                                     */
58181da177e4SLinus Torvalds /*                                                                          */
58191da177e4SLinus Torvalds /****************************************************************************/
58201da177e4SLinus Torvalds static int
58211da177e4SLinus Torvalds ips_readwrite_page5(ips_ha_t * ha, int write, int intr)
58221da177e4SLinus Torvalds {
58231da177e4SLinus Torvalds 	ips_scb_t *scb;
58241da177e4SLinus Torvalds 	int ret;
58251da177e4SLinus Torvalds 
58261da177e4SLinus Torvalds 	METHOD_TRACE("ips_readwrite_page5", 1);
58271da177e4SLinus Torvalds 
58281da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
58291da177e4SLinus Torvalds 
58301da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
58311da177e4SLinus Torvalds 
58321da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
58331da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_RW_NVRAM_PAGE;
58341da177e4SLinus Torvalds 
58351da177e4SLinus Torvalds 	scb->cmd.nvram.op_code = IPS_CMD_RW_NVRAM_PAGE;
58361da177e4SLinus Torvalds 	scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb);
58371da177e4SLinus Torvalds 	scb->cmd.nvram.page = 5;
58381da177e4SLinus Torvalds 	scb->cmd.nvram.write = write;
58391da177e4SLinus Torvalds 	scb->cmd.nvram.reserved = 0;
58401da177e4SLinus Torvalds 	scb->cmd.nvram.reserved2 = 0;
58411da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->nvram);
58421da177e4SLinus Torvalds 	scb->cmd.nvram.buffer_addr = ha->ioctl_busaddr;
58431da177e4SLinus Torvalds 	if (write)
58441da177e4SLinus Torvalds 		memcpy(ha->ioctl_data, ha->nvram, sizeof(*ha->nvram));
58451da177e4SLinus Torvalds 
58461da177e4SLinus Torvalds 	/* issue the command */
58471da177e4SLinus Torvalds 	if (((ret =
58481da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
58491da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
58501da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
58511da177e4SLinus Torvalds 
58521da177e4SLinus Torvalds 		memset(ha->nvram, 0, sizeof (IPS_NVRAM_P5));
58531da177e4SLinus Torvalds 
58541da177e4SLinus Torvalds 		return (0);
58551da177e4SLinus Torvalds 	}
58561da177e4SLinus Torvalds 	if (!write)
58571da177e4SLinus Torvalds 		memcpy(ha->nvram, ha->ioctl_data, sizeof(*ha->nvram));
58581da177e4SLinus Torvalds 	return (1);
58591da177e4SLinus Torvalds }
58601da177e4SLinus Torvalds 
58611da177e4SLinus Torvalds /****************************************************************************/
58621da177e4SLinus Torvalds /*                                                                          */
58631da177e4SLinus Torvalds /* Routine Name: ips_clear_adapter                                          */
58641da177e4SLinus Torvalds /*                                                                          */
58651da177e4SLinus Torvalds /* Routine Description:                                                     */
58661da177e4SLinus Torvalds /*                                                                          */
58671da177e4SLinus Torvalds /*   Clear the stripe lock tables                                           */
58681da177e4SLinus Torvalds /*                                                                          */
58691da177e4SLinus Torvalds /****************************************************************************/
58701da177e4SLinus Torvalds static int
58711da177e4SLinus Torvalds ips_clear_adapter(ips_ha_t * ha, int intr)
58721da177e4SLinus Torvalds {
58731da177e4SLinus Torvalds 	ips_scb_t *scb;
58741da177e4SLinus Torvalds 	int ret;
58751da177e4SLinus Torvalds 
58761da177e4SLinus Torvalds 	METHOD_TRACE("ips_clear_adapter", 1);
58771da177e4SLinus Torvalds 
58781da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
58791da177e4SLinus Torvalds 
58801da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
58811da177e4SLinus Torvalds 
58821da177e4SLinus Torvalds 	scb->timeout = ips_reset_timeout;
58831da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_CONFIG_SYNC;
58841da177e4SLinus Torvalds 
58851da177e4SLinus Torvalds 	scb->cmd.config_sync.op_code = IPS_CMD_CONFIG_SYNC;
58861da177e4SLinus Torvalds 	scb->cmd.config_sync.command_id = IPS_COMMAND_ID(ha, scb);
58871da177e4SLinus Torvalds 	scb->cmd.config_sync.channel = 0;
58881da177e4SLinus Torvalds 	scb->cmd.config_sync.source_target = IPS_POCL;
58891da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved = 0;
58901da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved2 = 0;
58911da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved3 = 0;
58921da177e4SLinus Torvalds 
58931da177e4SLinus Torvalds 	/* issue command */
58941da177e4SLinus Torvalds 	if (((ret =
58951da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE)
58961da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
58971da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
58981da177e4SLinus Torvalds 		return (0);
58991da177e4SLinus Torvalds 
59001da177e4SLinus Torvalds 	/* send unlock stripe command */
59011da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
59021da177e4SLinus Torvalds 
59031da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_ERROR_TABLE;
59041da177e4SLinus Torvalds 	scb->timeout = ips_reset_timeout;
59051da177e4SLinus Torvalds 
59061da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.op_code = IPS_CMD_ERROR_TABLE;
59071da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.command_id = IPS_COMMAND_ID(ha, scb);
59081da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.log_drv = 0;
59091da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.control = IPS_CSL;
59101da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved = 0;
59111da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved2 = 0;
59121da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved3 = 0;
59131da177e4SLinus Torvalds 
59141da177e4SLinus Torvalds 	/* issue command */
59151da177e4SLinus Torvalds 	if (((ret =
59161da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
59171da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
59181da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
59191da177e4SLinus Torvalds 		return (0);
59201da177e4SLinus Torvalds 
59211da177e4SLinus Torvalds 	return (1);
59221da177e4SLinus Torvalds }
59231da177e4SLinus Torvalds 
59241da177e4SLinus Torvalds /****************************************************************************/
59251da177e4SLinus Torvalds /*                                                                          */
59261da177e4SLinus Torvalds /* Routine Name: ips_ffdc_reset                                             */
59271da177e4SLinus Torvalds /*                                                                          */
59281da177e4SLinus Torvalds /* Routine Description:                                                     */
59291da177e4SLinus Torvalds /*                                                                          */
59301da177e4SLinus Torvalds /*   FFDC: write reset info                                                 */
59311da177e4SLinus Torvalds /*                                                                          */
59321da177e4SLinus Torvalds /****************************************************************************/
59331da177e4SLinus Torvalds static void
59341da177e4SLinus Torvalds ips_ffdc_reset(ips_ha_t * ha, int intr)
59351da177e4SLinus Torvalds {
59361da177e4SLinus Torvalds 	ips_scb_t *scb;
59371da177e4SLinus Torvalds 
59381da177e4SLinus Torvalds 	METHOD_TRACE("ips_ffdc_reset", 1);
59391da177e4SLinus Torvalds 
59401da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
59411da177e4SLinus Torvalds 
59421da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
59431da177e4SLinus Torvalds 
59441da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
59451da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FFDC;
59461da177e4SLinus Torvalds 	scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
59471da177e4SLinus Torvalds 	scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
59481da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_count = ha->reset_count;
59491da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_type = 0x80;
59501da177e4SLinus Torvalds 
59511da177e4SLinus Torvalds 	/* convert time to what the card wants */
59521da177e4SLinus Torvalds 	ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
59531da177e4SLinus Torvalds 
59541da177e4SLinus Torvalds 	/* issue command */
59551da177e4SLinus Torvalds 	ips_send_wait(ha, scb, ips_cmd_timeout, intr);
59561da177e4SLinus Torvalds }
59571da177e4SLinus Torvalds 
59581da177e4SLinus Torvalds /****************************************************************************/
59591da177e4SLinus Torvalds /*                                                                          */
59601da177e4SLinus Torvalds /* Routine Name: ips_ffdc_time                                              */
59611da177e4SLinus Torvalds /*                                                                          */
59621da177e4SLinus Torvalds /* Routine Description:                                                     */
59631da177e4SLinus Torvalds /*                                                                          */
59641da177e4SLinus Torvalds /*   FFDC: write time info                                                  */
59651da177e4SLinus Torvalds /*                                                                          */
59661da177e4SLinus Torvalds /****************************************************************************/
59671da177e4SLinus Torvalds static void
59681da177e4SLinus Torvalds ips_ffdc_time(ips_ha_t * ha)
59691da177e4SLinus Torvalds {
59701da177e4SLinus Torvalds 	ips_scb_t *scb;
59711da177e4SLinus Torvalds 
59721da177e4SLinus Torvalds 	METHOD_TRACE("ips_ffdc_time", 1);
59731da177e4SLinus Torvalds 
59741da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) Sending time update.", ips_name, ha->host_num);
59751da177e4SLinus Torvalds 
59761da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
59771da177e4SLinus Torvalds 
59781da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
59791da177e4SLinus Torvalds 
59801da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
59811da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FFDC;
59821da177e4SLinus Torvalds 	scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
59831da177e4SLinus Torvalds 	scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
59841da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_count = 0;
59851da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_type = 0;
59861da177e4SLinus Torvalds 
59871da177e4SLinus Torvalds 	/* convert time to what the card wants */
59881da177e4SLinus Torvalds 	ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
59891da177e4SLinus Torvalds 
59901da177e4SLinus Torvalds 	/* issue command */
59911da177e4SLinus Torvalds 	ips_send_wait(ha, scb, ips_cmd_timeout, IPS_FFDC);
59921da177e4SLinus Torvalds }
59931da177e4SLinus Torvalds 
59941da177e4SLinus Torvalds /****************************************************************************/
59951da177e4SLinus Torvalds /*                                                                          */
59961da177e4SLinus Torvalds /* Routine Name: ips_fix_ffdc_time                                          */
59971da177e4SLinus Torvalds /*                                                                          */
59981da177e4SLinus Torvalds /* Routine Description:                                                     */
59991da177e4SLinus Torvalds /*   Adjust time_t to what the card wants                                   */
60001da177e4SLinus Torvalds /*                                                                          */
60011da177e4SLinus Torvalds /****************************************************************************/
60021da177e4SLinus Torvalds static void
60031da177e4SLinus Torvalds ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time_t current_time)
60041da177e4SLinus Torvalds {
60051da177e4SLinus Torvalds 	long days;
60061da177e4SLinus Torvalds 	long rem;
60071da177e4SLinus Torvalds 	int i;
60081da177e4SLinus Torvalds 	int year;
60091da177e4SLinus Torvalds 	int yleap;
60101da177e4SLinus Torvalds 	int year_lengths[2] = { IPS_DAYS_NORMAL_YEAR, IPS_DAYS_LEAP_YEAR };
60111da177e4SLinus Torvalds 	int month_lengths[12][2] = { {31, 31},
60121da177e4SLinus Torvalds 	{28, 29},
60131da177e4SLinus Torvalds 	{31, 31},
60141da177e4SLinus Torvalds 	{30, 30},
60151da177e4SLinus Torvalds 	{31, 31},
60161da177e4SLinus Torvalds 	{30, 30},
60171da177e4SLinus Torvalds 	{31, 31},
60181da177e4SLinus Torvalds 	{31, 31},
60191da177e4SLinus Torvalds 	{30, 30},
60201da177e4SLinus Torvalds 	{31, 31},
60211da177e4SLinus Torvalds 	{30, 30},
60221da177e4SLinus Torvalds 	{31, 31}
60231da177e4SLinus Torvalds 	};
60241da177e4SLinus Torvalds 
60251da177e4SLinus Torvalds 	METHOD_TRACE("ips_fix_ffdc_time", 1);
60261da177e4SLinus Torvalds 
60271da177e4SLinus Torvalds 	days = current_time / IPS_SECS_DAY;
60281da177e4SLinus Torvalds 	rem = current_time % IPS_SECS_DAY;
60291da177e4SLinus Torvalds 
60301da177e4SLinus Torvalds 	scb->cmd.ffdc.hour = (rem / IPS_SECS_HOUR);
60311da177e4SLinus Torvalds 	rem = rem % IPS_SECS_HOUR;
60321da177e4SLinus Torvalds 	scb->cmd.ffdc.minute = (rem / IPS_SECS_MIN);
60331da177e4SLinus Torvalds 	scb->cmd.ffdc.second = (rem % IPS_SECS_MIN);
60341da177e4SLinus Torvalds 
60351da177e4SLinus Torvalds 	year = IPS_EPOCH_YEAR;
60361da177e4SLinus Torvalds 	while (days < 0 || days >= year_lengths[yleap = IPS_IS_LEAP_YEAR(year)]) {
60371da177e4SLinus Torvalds 		int newy;
60381da177e4SLinus Torvalds 
60391da177e4SLinus Torvalds 		newy = year + (days / IPS_DAYS_NORMAL_YEAR);
60401da177e4SLinus Torvalds 		if (days < 0)
60411da177e4SLinus Torvalds 			--newy;
60421da177e4SLinus Torvalds 		days -= (newy - year) * IPS_DAYS_NORMAL_YEAR +
60431da177e4SLinus Torvalds 		    IPS_NUM_LEAP_YEARS_THROUGH(newy - 1) -
60441da177e4SLinus Torvalds 		    IPS_NUM_LEAP_YEARS_THROUGH(year - 1);
60451da177e4SLinus Torvalds 		year = newy;
60461da177e4SLinus Torvalds 	}
60471da177e4SLinus Torvalds 
60481da177e4SLinus Torvalds 	scb->cmd.ffdc.yearH = year / 100;
60491da177e4SLinus Torvalds 	scb->cmd.ffdc.yearL = year % 100;
60501da177e4SLinus Torvalds 
60511da177e4SLinus Torvalds 	for (i = 0; days >= month_lengths[i][yleap]; ++i)
60521da177e4SLinus Torvalds 		days -= month_lengths[i][yleap];
60531da177e4SLinus Torvalds 
60541da177e4SLinus Torvalds 	scb->cmd.ffdc.month = i + 1;
60551da177e4SLinus Torvalds 	scb->cmd.ffdc.day = days + 1;
60561da177e4SLinus Torvalds }
60571da177e4SLinus Torvalds 
60581da177e4SLinus Torvalds /****************************************************************************
60591da177e4SLinus Torvalds  * BIOS Flash Routines                                                      *
60601da177e4SLinus Torvalds  ****************************************************************************/
60611da177e4SLinus Torvalds 
60621da177e4SLinus Torvalds /****************************************************************************/
60631da177e4SLinus Torvalds /*                                                                          */
60641da177e4SLinus Torvalds /* Routine Name: ips_erase_bios                                             */
60651da177e4SLinus Torvalds /*                                                                          */
60661da177e4SLinus Torvalds /* Routine Description:                                                     */
60671da177e4SLinus Torvalds /*   Erase the BIOS on the adapter                                          */
60681da177e4SLinus Torvalds /*                                                                          */
60691da177e4SLinus Torvalds /****************************************************************************/
60701da177e4SLinus Torvalds static int
60711da177e4SLinus Torvalds ips_erase_bios(ips_ha_t * ha)
60721da177e4SLinus Torvalds {
60731da177e4SLinus Torvalds 	int timeout;
60741da177e4SLinus Torvalds 	uint8_t status = 0;
60751da177e4SLinus Torvalds 
60761da177e4SLinus Torvalds 	METHOD_TRACE("ips_erase_bios", 1);
60771da177e4SLinus Torvalds 
60781da177e4SLinus Torvalds 	status = 0;
60791da177e4SLinus Torvalds 
60801da177e4SLinus Torvalds 	/* Clear the status register */
60811da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
60828a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60831da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60841da177e4SLinus Torvalds 
60851da177e4SLinus Torvalds 	outb(0x50, ha->io_addr + IPS_REG_FLDP);
60868a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60871da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60881da177e4SLinus Torvalds 
60891da177e4SLinus Torvalds 	/* Erase Setup */
60901da177e4SLinus Torvalds 	outb(0x20, ha->io_addr + IPS_REG_FLDP);
60918a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60921da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60931da177e4SLinus Torvalds 
60941da177e4SLinus Torvalds 	/* Erase Confirm */
60951da177e4SLinus Torvalds 	outb(0xD0, ha->io_addr + IPS_REG_FLDP);
60968a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60971da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60981da177e4SLinus Torvalds 
60991da177e4SLinus Torvalds 	/* Erase Status */
61001da177e4SLinus Torvalds 	outb(0x70, ha->io_addr + IPS_REG_FLDP);
61018a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61021da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61031da177e4SLinus Torvalds 
61041da177e4SLinus Torvalds 	timeout = 80000;	/* 80 seconds */
61051da177e4SLinus Torvalds 
61061da177e4SLinus Torvalds 	while (timeout > 0) {
61078a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
61081da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
61091da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
61101da177e4SLinus Torvalds 		}
61111da177e4SLinus Torvalds 
61121da177e4SLinus Torvalds 		status = inb(ha->io_addr + IPS_REG_FLDP);
61131da177e4SLinus Torvalds 
61141da177e4SLinus Torvalds 		if (status & 0x80)
61151da177e4SLinus Torvalds 			break;
61161da177e4SLinus Torvalds 
61171da177e4SLinus Torvalds 		MDELAY(1);
61181da177e4SLinus Torvalds 		timeout--;
61191da177e4SLinus Torvalds 	}
61201da177e4SLinus Torvalds 
61211da177e4SLinus Torvalds 	/* check for timeout */
61221da177e4SLinus Torvalds 	if (timeout <= 0) {
61231da177e4SLinus Torvalds 		/* timeout */
61241da177e4SLinus Torvalds 
61251da177e4SLinus Torvalds 		/* try to suspend the erase */
61261da177e4SLinus Torvalds 		outb(0xB0, ha->io_addr + IPS_REG_FLDP);
61278a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61281da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
61291da177e4SLinus Torvalds 
61301da177e4SLinus Torvalds 		/* wait for 10 seconds */
61311da177e4SLinus Torvalds 		timeout = 10000;
61321da177e4SLinus Torvalds 		while (timeout > 0) {
61338a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
61341da177e4SLinus Torvalds 				outl(0, ha->io_addr + IPS_REG_FLAP);
61351da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
61361da177e4SLinus Torvalds 			}
61371da177e4SLinus Torvalds 
61381da177e4SLinus Torvalds 			status = inb(ha->io_addr + IPS_REG_FLDP);
61391da177e4SLinus Torvalds 
61401da177e4SLinus Torvalds 			if (status & 0xC0)
61411da177e4SLinus Torvalds 				break;
61421da177e4SLinus Torvalds 
61431da177e4SLinus Torvalds 			MDELAY(1);
61441da177e4SLinus Torvalds 			timeout--;
61451da177e4SLinus Torvalds 		}
61461da177e4SLinus Torvalds 
61471da177e4SLinus Torvalds 		return (1);
61481da177e4SLinus Torvalds 	}
61491da177e4SLinus Torvalds 
61501da177e4SLinus Torvalds 	/* check for valid VPP */
61511da177e4SLinus Torvalds 	if (status & 0x08)
61521da177e4SLinus Torvalds 		/* VPP failure */
61531da177e4SLinus Torvalds 		return (1);
61541da177e4SLinus Torvalds 
6155d6e05edcSAndreas Mohr 	/* check for successful flash */
61561da177e4SLinus Torvalds 	if (status & 0x30)
61571da177e4SLinus Torvalds 		/* sequence error */
61581da177e4SLinus Torvalds 		return (1);
61591da177e4SLinus Torvalds 
61601da177e4SLinus Torvalds 	/* Otherwise, we were successful */
61611da177e4SLinus Torvalds 	/* clear status */
61621da177e4SLinus Torvalds 	outb(0x50, ha->io_addr + IPS_REG_FLDP);
61638a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61641da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61651da177e4SLinus Torvalds 
61661da177e4SLinus Torvalds 	/* enable reads */
61671da177e4SLinus Torvalds 	outb(0xFF, ha->io_addr + IPS_REG_FLDP);
61688a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61691da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61701da177e4SLinus Torvalds 
61711da177e4SLinus Torvalds 	return (0);
61721da177e4SLinus Torvalds }
61731da177e4SLinus Torvalds 
61741da177e4SLinus Torvalds /****************************************************************************/
61751da177e4SLinus Torvalds /*                                                                          */
61761da177e4SLinus Torvalds /* Routine Name: ips_erase_bios_memio                                       */
61771da177e4SLinus Torvalds /*                                                                          */
61781da177e4SLinus Torvalds /* Routine Description:                                                     */
61791da177e4SLinus Torvalds /*   Erase the BIOS on the adapter                                          */
61801da177e4SLinus Torvalds /*                                                                          */
61811da177e4SLinus Torvalds /****************************************************************************/
61821da177e4SLinus Torvalds static int
61831da177e4SLinus Torvalds ips_erase_bios_memio(ips_ha_t * ha)
61841da177e4SLinus Torvalds {
61851da177e4SLinus Torvalds 	int timeout;
61861da177e4SLinus Torvalds 	uint8_t status;
61871da177e4SLinus Torvalds 
61881da177e4SLinus Torvalds 	METHOD_TRACE("ips_erase_bios_memio", 1);
61891da177e4SLinus Torvalds 
61901da177e4SLinus Torvalds 	status = 0;
61911da177e4SLinus Torvalds 
61921da177e4SLinus Torvalds 	/* Clear the status register */
61931da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
61948a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61951da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61961da177e4SLinus Torvalds 
61971da177e4SLinus Torvalds 	writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
61988a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61991da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62001da177e4SLinus Torvalds 
62011da177e4SLinus Torvalds 	/* Erase Setup */
62021da177e4SLinus Torvalds 	writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
62038a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62041da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62051da177e4SLinus Torvalds 
62061da177e4SLinus Torvalds 	/* Erase Confirm */
62071da177e4SLinus Torvalds 	writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
62088a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62091da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62101da177e4SLinus Torvalds 
62111da177e4SLinus Torvalds 	/* Erase Status */
62121da177e4SLinus Torvalds 	writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
62138a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62141da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62151da177e4SLinus Torvalds 
62161da177e4SLinus Torvalds 	timeout = 80000;	/* 80 seconds */
62171da177e4SLinus Torvalds 
62181da177e4SLinus Torvalds 	while (timeout > 0) {
62198a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
62201da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
62211da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
62221da177e4SLinus Torvalds 		}
62231da177e4SLinus Torvalds 
62241da177e4SLinus Torvalds 		status = readb(ha->mem_ptr + IPS_REG_FLDP);
62251da177e4SLinus Torvalds 
62261da177e4SLinus Torvalds 		if (status & 0x80)
62271da177e4SLinus Torvalds 			break;
62281da177e4SLinus Torvalds 
62291da177e4SLinus Torvalds 		MDELAY(1);
62301da177e4SLinus Torvalds 		timeout--;
62311da177e4SLinus Torvalds 	}
62321da177e4SLinus Torvalds 
62331da177e4SLinus Torvalds 	/* check for timeout */
62341da177e4SLinus Torvalds 	if (timeout <= 0) {
62351da177e4SLinus Torvalds 		/* timeout */
62361da177e4SLinus Torvalds 
62371da177e4SLinus Torvalds 		/* try to suspend the erase */
62381da177e4SLinus Torvalds 		writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
62398a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62401da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
62411da177e4SLinus Torvalds 
62421da177e4SLinus Torvalds 		/* wait for 10 seconds */
62431da177e4SLinus Torvalds 		timeout = 10000;
62441da177e4SLinus Torvalds 		while (timeout > 0) {
62458a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
62461da177e4SLinus Torvalds 				writel(0, ha->mem_ptr + IPS_REG_FLAP);
62471da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
62481da177e4SLinus Torvalds 			}
62491da177e4SLinus Torvalds 
62501da177e4SLinus Torvalds 			status = readb(ha->mem_ptr + IPS_REG_FLDP);
62511da177e4SLinus Torvalds 
62521da177e4SLinus Torvalds 			if (status & 0xC0)
62531da177e4SLinus Torvalds 				break;
62541da177e4SLinus Torvalds 
62551da177e4SLinus Torvalds 			MDELAY(1);
62561da177e4SLinus Torvalds 			timeout--;
62571da177e4SLinus Torvalds 		}
62581da177e4SLinus Torvalds 
62591da177e4SLinus Torvalds 		return (1);
62601da177e4SLinus Torvalds 	}
62611da177e4SLinus Torvalds 
62621da177e4SLinus Torvalds 	/* check for valid VPP */
62631da177e4SLinus Torvalds 	if (status & 0x08)
62641da177e4SLinus Torvalds 		/* VPP failure */
62651da177e4SLinus Torvalds 		return (1);
62661da177e4SLinus Torvalds 
6267d6e05edcSAndreas Mohr 	/* check for successful flash */
62681da177e4SLinus Torvalds 	if (status & 0x30)
62691da177e4SLinus Torvalds 		/* sequence error */
62701da177e4SLinus Torvalds 		return (1);
62711da177e4SLinus Torvalds 
62721da177e4SLinus Torvalds 	/* Otherwise, we were successful */
62731da177e4SLinus Torvalds 	/* clear status */
62741da177e4SLinus Torvalds 	writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
62758a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62761da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62771da177e4SLinus Torvalds 
62781da177e4SLinus Torvalds 	/* enable reads */
62791da177e4SLinus Torvalds 	writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
62808a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62811da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62821da177e4SLinus Torvalds 
62831da177e4SLinus Torvalds 	return (0);
62841da177e4SLinus Torvalds }
62851da177e4SLinus Torvalds 
62861da177e4SLinus Torvalds /****************************************************************************/
62871da177e4SLinus Torvalds /*                                                                          */
62881da177e4SLinus Torvalds /* Routine Name: ips_program_bios                                           */
62891da177e4SLinus Torvalds /*                                                                          */
62901da177e4SLinus Torvalds /* Routine Description:                                                     */
62911da177e4SLinus Torvalds /*   Program the BIOS on the adapter                                        */
62921da177e4SLinus Torvalds /*                                                                          */
62931da177e4SLinus Torvalds /****************************************************************************/
62941da177e4SLinus Torvalds static int
62951da177e4SLinus Torvalds ips_program_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
62961da177e4SLinus Torvalds 		 uint32_t offset)
62971da177e4SLinus Torvalds {
62981da177e4SLinus Torvalds 	int i;
62991da177e4SLinus Torvalds 	int timeout;
63001da177e4SLinus Torvalds 	uint8_t status = 0;
63011da177e4SLinus Torvalds 
63021da177e4SLinus Torvalds 	METHOD_TRACE("ips_program_bios", 1);
63031da177e4SLinus Torvalds 
63041da177e4SLinus Torvalds 	status = 0;
63051da177e4SLinus Torvalds 
63061da177e4SLinus Torvalds 	for (i = 0; i < buffersize; i++) {
63071da177e4SLinus Torvalds 		/* write a byte */
6308db3cc200SJames Bottomley 		outl(i + offset, ha->io_addr + IPS_REG_FLAP);
63098a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63101da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63111da177e4SLinus Torvalds 
63121da177e4SLinus Torvalds 		outb(0x40, ha->io_addr + IPS_REG_FLDP);
63138a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63141da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63151da177e4SLinus Torvalds 
63161da177e4SLinus Torvalds 		outb(buffer[i], ha->io_addr + IPS_REG_FLDP);
63178a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63181da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63191da177e4SLinus Torvalds 
63201da177e4SLinus Torvalds 		/* wait up to one second */
63211da177e4SLinus Torvalds 		timeout = 1000;
63221da177e4SLinus Torvalds 		while (timeout > 0) {
63238a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
63241da177e4SLinus Torvalds 				outl(0, ha->io_addr + IPS_REG_FLAP);
63251da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63261da177e4SLinus Torvalds 			}
63271da177e4SLinus Torvalds 
63281da177e4SLinus Torvalds 			status = inb(ha->io_addr + IPS_REG_FLDP);
63291da177e4SLinus Torvalds 
63301da177e4SLinus Torvalds 			if (status & 0x80)
63311da177e4SLinus Torvalds 				break;
63321da177e4SLinus Torvalds 
63331da177e4SLinus Torvalds 			MDELAY(1);
63341da177e4SLinus Torvalds 			timeout--;
63351da177e4SLinus Torvalds 		}
63361da177e4SLinus Torvalds 
63371da177e4SLinus Torvalds 		if (timeout == 0) {
63381da177e4SLinus Torvalds 			/* timeout error */
63391da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
63408a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63411da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63421da177e4SLinus Torvalds 
63431da177e4SLinus Torvalds 			outb(0xFF, ha->io_addr + IPS_REG_FLDP);
63448a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63451da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63461da177e4SLinus Torvalds 
63471da177e4SLinus Torvalds 			return (1);
63481da177e4SLinus Torvalds 		}
63491da177e4SLinus Torvalds 
63501da177e4SLinus Torvalds 		/* check the status */
63511da177e4SLinus Torvalds 		if (status & 0x18) {
63521da177e4SLinus Torvalds 			/* programming error */
63531da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
63548a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63551da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63561da177e4SLinus Torvalds 
63571da177e4SLinus Torvalds 			outb(0xFF, ha->io_addr + IPS_REG_FLDP);
63588a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63591da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63601da177e4SLinus Torvalds 
63611da177e4SLinus Torvalds 			return (1);
63621da177e4SLinus Torvalds 		}
63631da177e4SLinus Torvalds 	}			/* end for */
63641da177e4SLinus Torvalds 
63651da177e4SLinus Torvalds 	/* Enable reading */
63661da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
63678a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63681da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
63691da177e4SLinus Torvalds 
63701da177e4SLinus Torvalds 	outb(0xFF, ha->io_addr + IPS_REG_FLDP);
63718a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63721da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
63731da177e4SLinus Torvalds 
63741da177e4SLinus Torvalds 	return (0);
63751da177e4SLinus Torvalds }
63761da177e4SLinus Torvalds 
63771da177e4SLinus Torvalds /****************************************************************************/
63781da177e4SLinus Torvalds /*                                                                          */
63791da177e4SLinus Torvalds /* Routine Name: ips_program_bios_memio                                     */
63801da177e4SLinus Torvalds /*                                                                          */
63811da177e4SLinus Torvalds /* Routine Description:                                                     */
63821da177e4SLinus Torvalds /*   Program the BIOS on the adapter                                        */
63831da177e4SLinus Torvalds /*                                                                          */
63841da177e4SLinus Torvalds /****************************************************************************/
63851da177e4SLinus Torvalds static int
63861da177e4SLinus Torvalds ips_program_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
63871da177e4SLinus Torvalds 		       uint32_t offset)
63881da177e4SLinus Torvalds {
63891da177e4SLinus Torvalds 	int i;
63901da177e4SLinus Torvalds 	int timeout;
63911da177e4SLinus Torvalds 	uint8_t status = 0;
63921da177e4SLinus Torvalds 
63931da177e4SLinus Torvalds 	METHOD_TRACE("ips_program_bios_memio", 1);
63941da177e4SLinus Torvalds 
63951da177e4SLinus Torvalds 	status = 0;
63961da177e4SLinus Torvalds 
63971da177e4SLinus Torvalds 	for (i = 0; i < buffersize; i++) {
63981da177e4SLinus Torvalds 		/* write a byte */
63991da177e4SLinus Torvalds 		writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
64008a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64011da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
64021da177e4SLinus Torvalds 
64031da177e4SLinus Torvalds 		writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
64048a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64051da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
64061da177e4SLinus Torvalds 
64071da177e4SLinus Torvalds 		writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
64088a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64091da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
64101da177e4SLinus Torvalds 
64111da177e4SLinus Torvalds 		/* wait up to one second */
64121da177e4SLinus Torvalds 		timeout = 1000;
64131da177e4SLinus Torvalds 		while (timeout > 0) {
64148a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
64151da177e4SLinus Torvalds 				writel(0, ha->mem_ptr + IPS_REG_FLAP);
64161da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
64171da177e4SLinus Torvalds 			}
64181da177e4SLinus Torvalds 
64191da177e4SLinus Torvalds 			status = readb(ha->mem_ptr + IPS_REG_FLDP);
64201da177e4SLinus Torvalds 
64211da177e4SLinus Torvalds 			if (status & 0x80)
64221da177e4SLinus Torvalds 				break;
64231da177e4SLinus Torvalds 
64241da177e4SLinus Torvalds 			MDELAY(1);
64251da177e4SLinus Torvalds 			timeout--;
64261da177e4SLinus Torvalds 		}
64271da177e4SLinus Torvalds 
64281da177e4SLinus Torvalds 		if (timeout == 0) {
64291da177e4SLinus Torvalds 			/* timeout error */
64301da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
64318a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64321da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
64331da177e4SLinus Torvalds 
64341da177e4SLinus Torvalds 			writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
64358a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64361da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
64371da177e4SLinus Torvalds 
64381da177e4SLinus Torvalds 			return (1);
64391da177e4SLinus Torvalds 		}
64401da177e4SLinus Torvalds 
64411da177e4SLinus Torvalds 		/* check the status */
64421da177e4SLinus Torvalds 		if (status & 0x18) {
64431da177e4SLinus Torvalds 			/* programming error */
64441da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
64458a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64461da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
64471da177e4SLinus Torvalds 
64481da177e4SLinus Torvalds 			writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
64498a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64501da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
64511da177e4SLinus Torvalds 
64521da177e4SLinus Torvalds 			return (1);
64531da177e4SLinus Torvalds 		}
64541da177e4SLinus Torvalds 	}			/* end for */
64551da177e4SLinus Torvalds 
64561da177e4SLinus Torvalds 	/* Enable reading */
64571da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
64588a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64591da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64601da177e4SLinus Torvalds 
64611da177e4SLinus Torvalds 	writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
64628a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64631da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64641da177e4SLinus Torvalds 
64651da177e4SLinus Torvalds 	return (0);
64661da177e4SLinus Torvalds }
64671da177e4SLinus Torvalds 
64681da177e4SLinus Torvalds /****************************************************************************/
64691da177e4SLinus Torvalds /*                                                                          */
64701da177e4SLinus Torvalds /* Routine Name: ips_verify_bios                                            */
64711da177e4SLinus Torvalds /*                                                                          */
64721da177e4SLinus Torvalds /* Routine Description:                                                     */
64731da177e4SLinus Torvalds /*   Verify the BIOS on the adapter                                         */
64741da177e4SLinus Torvalds /*                                                                          */
64751da177e4SLinus Torvalds /****************************************************************************/
64761da177e4SLinus Torvalds static int
64771da177e4SLinus Torvalds ips_verify_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
64781da177e4SLinus Torvalds 		uint32_t offset)
64791da177e4SLinus Torvalds {
64801da177e4SLinus Torvalds 	uint8_t checksum;
64811da177e4SLinus Torvalds 	int i;
64821da177e4SLinus Torvalds 
64831da177e4SLinus Torvalds 	METHOD_TRACE("ips_verify_bios", 1);
64841da177e4SLinus Torvalds 
64851da177e4SLinus Torvalds 	/* test 1st byte */
64861da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
64878a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64881da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64891da177e4SLinus Torvalds 
64901da177e4SLinus Torvalds 	if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
64911da177e4SLinus Torvalds 		return (1);
64921da177e4SLinus Torvalds 
6493db3cc200SJames Bottomley 	outl(1, ha->io_addr + IPS_REG_FLAP);
64948a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64951da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64961da177e4SLinus Torvalds 	if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
64971da177e4SLinus Torvalds 		return (1);
64981da177e4SLinus Torvalds 
64991da177e4SLinus Torvalds 	checksum = 0xff;
65001da177e4SLinus Torvalds 	for (i = 2; i < buffersize; i++) {
65011da177e4SLinus Torvalds 
6502db3cc200SJames Bottomley 		outl(i + offset, ha->io_addr + IPS_REG_FLAP);
65038a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
65041da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
65051da177e4SLinus Torvalds 
65061da177e4SLinus Torvalds 		checksum = (uint8_t) checksum + inb(ha->io_addr + IPS_REG_FLDP);
65071da177e4SLinus Torvalds 	}
65081da177e4SLinus Torvalds 
65091da177e4SLinus Torvalds 	if (checksum != 0)
65101da177e4SLinus Torvalds 		/* failure */
65111da177e4SLinus Torvalds 		return (1);
65121da177e4SLinus Torvalds 	else
65131da177e4SLinus Torvalds 		/* success */
65141da177e4SLinus Torvalds 		return (0);
65151da177e4SLinus Torvalds }
65161da177e4SLinus Torvalds 
65171da177e4SLinus Torvalds /****************************************************************************/
65181da177e4SLinus Torvalds /*                                                                          */
65191da177e4SLinus Torvalds /* Routine Name: ips_verify_bios_memio                                      */
65201da177e4SLinus Torvalds /*                                                                          */
65211da177e4SLinus Torvalds /* Routine Description:                                                     */
65221da177e4SLinus Torvalds /*   Verify the BIOS on the adapter                                         */
65231da177e4SLinus Torvalds /*                                                                          */
65241da177e4SLinus Torvalds /****************************************************************************/
65251da177e4SLinus Torvalds static int
65261da177e4SLinus Torvalds ips_verify_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
65271da177e4SLinus Torvalds 		      uint32_t offset)
65281da177e4SLinus Torvalds {
65291da177e4SLinus Torvalds 	uint8_t checksum;
65301da177e4SLinus Torvalds 	int i;
65311da177e4SLinus Torvalds 
65321da177e4SLinus Torvalds 	METHOD_TRACE("ips_verify_bios_memio", 1);
65331da177e4SLinus Torvalds 
65341da177e4SLinus Torvalds 	/* test 1st byte */
65351da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
65368a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
65371da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
65381da177e4SLinus Torvalds 
65391da177e4SLinus Torvalds 	if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
65401da177e4SLinus Torvalds 		return (1);
65411da177e4SLinus Torvalds 
65421da177e4SLinus Torvalds 	writel(1, ha->mem_ptr + IPS_REG_FLAP);
65438a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
65441da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
65451da177e4SLinus Torvalds 	if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
65461da177e4SLinus Torvalds 		return (1);
65471da177e4SLinus Torvalds 
65481da177e4SLinus Torvalds 	checksum = 0xff;
65491da177e4SLinus Torvalds 	for (i = 2; i < buffersize; i++) {
65501da177e4SLinus Torvalds 
65511da177e4SLinus Torvalds 		writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
65528a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
65531da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
65541da177e4SLinus Torvalds 
65551da177e4SLinus Torvalds 		checksum =
65561da177e4SLinus Torvalds 		    (uint8_t) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
65571da177e4SLinus Torvalds 	}
65581da177e4SLinus Torvalds 
65591da177e4SLinus Torvalds 	if (checksum != 0)
65601da177e4SLinus Torvalds 		/* failure */
65611da177e4SLinus Torvalds 		return (1);
65621da177e4SLinus Torvalds 	else
65631da177e4SLinus Torvalds 		/* success */
65641da177e4SLinus Torvalds 		return (0);
65651da177e4SLinus Torvalds }
65661da177e4SLinus Torvalds 
65671da177e4SLinus Torvalds /****************************************************************************/
65681da177e4SLinus Torvalds /*                                                                          */
65691da177e4SLinus Torvalds /* Routine Name: ips_abort_init                                             */
65701da177e4SLinus Torvalds /*                                                                          */
65711da177e4SLinus Torvalds /* Routine Description:                                                     */
65721da177e4SLinus Torvalds /*   cleanup routine for a failed adapter initialization                    */
65731da177e4SLinus Torvalds /****************************************************************************/
65741da177e4SLinus Torvalds static int
65751da177e4SLinus Torvalds ips_abort_init(ips_ha_t * ha, int index)
65761da177e4SLinus Torvalds {
65771da177e4SLinus Torvalds 	ha->active = 0;
65781da177e4SLinus Torvalds 	ips_free(ha);
65791da177e4SLinus Torvalds 	ips_ha[index] = NULL;
65801da177e4SLinus Torvalds 	ips_sh[index] = NULL;
65811da177e4SLinus Torvalds 	return -1;
65821da177e4SLinus Torvalds }
65831da177e4SLinus Torvalds 
65841da177e4SLinus Torvalds /****************************************************************************/
65851da177e4SLinus Torvalds /*                                                                          */
65861da177e4SLinus Torvalds /* Routine Name: ips_shift_controllers                                      */
65871da177e4SLinus Torvalds /*                                                                          */
65881da177e4SLinus Torvalds /* Routine Description:                                                     */
65891da177e4SLinus Torvalds /*   helper function for ordering adapters                                  */
65901da177e4SLinus Torvalds /****************************************************************************/
65911da177e4SLinus Torvalds static void
65921da177e4SLinus Torvalds ips_shift_controllers(int lowindex, int highindex)
65931da177e4SLinus Torvalds {
65941da177e4SLinus Torvalds 	ips_ha_t *ha_sav = ips_ha[highindex];
65951da177e4SLinus Torvalds 	struct Scsi_Host *sh_sav = ips_sh[highindex];
65961da177e4SLinus Torvalds 	int i;
65971da177e4SLinus Torvalds 
65981da177e4SLinus Torvalds 	for (i = highindex; i > lowindex; i--) {
65991da177e4SLinus Torvalds 		ips_ha[i] = ips_ha[i - 1];
66001da177e4SLinus Torvalds 		ips_sh[i] = ips_sh[i - 1];
66011da177e4SLinus Torvalds 		ips_ha[i]->host_num = i;
66021da177e4SLinus Torvalds 	}
66031da177e4SLinus Torvalds 	ha_sav->host_num = lowindex;
66041da177e4SLinus Torvalds 	ips_ha[lowindex] = ha_sav;
66051da177e4SLinus Torvalds 	ips_sh[lowindex] = sh_sav;
66061da177e4SLinus Torvalds }
66071da177e4SLinus Torvalds 
66081da177e4SLinus Torvalds /****************************************************************************/
66091da177e4SLinus Torvalds /*                                                                          */
66101da177e4SLinus Torvalds /* Routine Name: ips_order_controllers                                      */
66111da177e4SLinus Torvalds /*                                                                          */
66121da177e4SLinus Torvalds /* Routine Description:                                                     */
66131da177e4SLinus Torvalds /*   place controllers is the "proper" boot order                           */
66141da177e4SLinus Torvalds /****************************************************************************/
66151da177e4SLinus Torvalds static void
66161da177e4SLinus Torvalds ips_order_controllers(void)
66171da177e4SLinus Torvalds {
66181da177e4SLinus Torvalds 	int i, j, tmp, position = 0;
66191da177e4SLinus Torvalds 	IPS_NVRAM_P5 *nvram;
66201da177e4SLinus Torvalds 	if (!ips_ha[0])
66211da177e4SLinus Torvalds 		return;
66221da177e4SLinus Torvalds 	nvram = ips_ha[0]->nvram;
66231da177e4SLinus Torvalds 
66241da177e4SLinus Torvalds 	if (nvram->adapter_order[0]) {
66251da177e4SLinus Torvalds 		for (i = 1; i <= nvram->adapter_order[0]; i++) {
66261da177e4SLinus Torvalds 			for (j = position; j < ips_num_controllers; j++) {
66271da177e4SLinus Torvalds 				switch (ips_ha[j]->ad_type) {
66281da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID6M:
66291da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID7M:
66301da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'M') {
66311da177e4SLinus Torvalds 						ips_shift_controllers(position,
66321da177e4SLinus Torvalds 								      j);
66331da177e4SLinus Torvalds 						position++;
66341da177e4SLinus Torvalds 					}
66351da177e4SLinus Torvalds 					break;
66361da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4L:
66371da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4M:
66381da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4MX:
66391da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4LX:
66401da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'N') {
66411da177e4SLinus Torvalds 						ips_shift_controllers(position,
66421da177e4SLinus Torvalds 								      j);
66431da177e4SLinus Torvalds 						position++;
66441da177e4SLinus Torvalds 					}
66451da177e4SLinus Torvalds 					break;
66461da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID6I:
66471da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID5I2:
66481da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID5I1:
66491da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID7k:
66501da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'S') {
66511da177e4SLinus Torvalds 						ips_shift_controllers(position,
66521da177e4SLinus Torvalds 								      j);
66531da177e4SLinus Torvalds 						position++;
66541da177e4SLinus Torvalds 					}
66551da177e4SLinus Torvalds 					break;
66561da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID:
66571da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID2:
66581da177e4SLinus Torvalds 				case IPS_ADTYPE_NAVAJO:
66591da177e4SLinus Torvalds 				case IPS_ADTYPE_KIOWA:
66601da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID3L:
66611da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID3:
66621da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4H:
66631da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'A') {
66641da177e4SLinus Torvalds 						ips_shift_controllers(position,
66651da177e4SLinus Torvalds 								      j);
66661da177e4SLinus Torvalds 						position++;
66671da177e4SLinus Torvalds 					}
66681da177e4SLinus Torvalds 					break;
66691da177e4SLinus Torvalds 				default:
66701da177e4SLinus Torvalds 					break;
66711da177e4SLinus Torvalds 				}
66721da177e4SLinus Torvalds 			}
66731da177e4SLinus Torvalds 		}
66741da177e4SLinus Torvalds 		/* if adapter_order[0], then ordering is complete */
66751da177e4SLinus Torvalds 		return;
66761da177e4SLinus Torvalds 	}
66771da177e4SLinus Torvalds 	/* old bios, use older ordering */
66781da177e4SLinus Torvalds 	tmp = 0;
66791da177e4SLinus Torvalds 	for (i = position; i < ips_num_controllers; i++) {
66801da177e4SLinus Torvalds 		if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I2 ||
66811da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I1) {
66821da177e4SLinus Torvalds 			ips_shift_controllers(position, i);
66831da177e4SLinus Torvalds 			position++;
66841da177e4SLinus Torvalds 			tmp = 1;
66851da177e4SLinus Torvalds 		}
66861da177e4SLinus Torvalds 	}
66871da177e4SLinus Torvalds 	/* if there were no 5I cards, then don't do any extra ordering */
66881da177e4SLinus Torvalds 	if (!tmp)
66891da177e4SLinus Torvalds 		return;
66901da177e4SLinus Torvalds 	for (i = position; i < ips_num_controllers; i++) {
66911da177e4SLinus Torvalds 		if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4L ||
66921da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4M ||
66931da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4LX ||
66941da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4MX) {
66951da177e4SLinus Torvalds 			ips_shift_controllers(position, i);
66961da177e4SLinus Torvalds 			position++;
66971da177e4SLinus Torvalds 		}
66981da177e4SLinus Torvalds 	}
66991da177e4SLinus Torvalds 
67001da177e4SLinus Torvalds 	return;
67011da177e4SLinus Torvalds }
67021da177e4SLinus Torvalds 
67031da177e4SLinus Torvalds /****************************************************************************/
67041da177e4SLinus Torvalds /*                                                                          */
67051da177e4SLinus Torvalds /* Routine Name: ips_register_scsi                                          */
67061da177e4SLinus Torvalds /*                                                                          */
67071da177e4SLinus Torvalds /* Routine Description:                                                     */
67081da177e4SLinus Torvalds /*   perform any registration and setup with the scsi layer                 */
67091da177e4SLinus Torvalds /****************************************************************************/
67101da177e4SLinus Torvalds static int
67111da177e4SLinus Torvalds ips_register_scsi(int index)
67121da177e4SLinus Torvalds {
67131da177e4SLinus Torvalds 	struct Scsi_Host *sh;
67141da177e4SLinus Torvalds 	ips_ha_t *ha, *oldha = ips_ha[index];
67151da177e4SLinus Torvalds 	sh = scsi_host_alloc(&ips_driver_template, sizeof (ips_ha_t));
67161da177e4SLinus Torvalds 	if (!sh) {
67171da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, oldha->pcidev,
67181da177e4SLinus Torvalds 			   "Unable to register controller with SCSI subsystem\n");
67191da177e4SLinus Torvalds 		return -1;
67201da177e4SLinus Torvalds 	}
67211da177e4SLinus Torvalds 	ha = IPS_HA(sh);
67221da177e4SLinus Torvalds 	memcpy(ha, oldha, sizeof (ips_ha_t));
67238a694cc8SJeff Garzik 	free_irq(oldha->pcidev->irq, oldha);
67241da177e4SLinus Torvalds 	/* Install the interrupt handler with the new ha */
67258a694cc8SJeff Garzik 	if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) {
67261da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
67271da177e4SLinus Torvalds 			   "Unable to install interrupt handler\n");
67282551a13eSJeff Garzik 		goto err_out_sh;
67291da177e4SLinus Torvalds 	}
67301da177e4SLinus Torvalds 
67311da177e4SLinus Torvalds 	kfree(oldha);
67321da177e4SLinus Torvalds 
67331da177e4SLinus Torvalds 	/* Store away needed values for later use */
67341da177e4SLinus Torvalds 	sh->unique_id = (ha->io_addr) ? ha->io_addr : ha->mem_addr;
67351da177e4SLinus Torvalds 	sh->sg_tablesize = sh->hostt->sg_tablesize;
67361da177e4SLinus Torvalds 	sh->can_queue = sh->hostt->can_queue;
67371da177e4SLinus Torvalds 	sh->cmd_per_lun = sh->hostt->cmd_per_lun;
67381da177e4SLinus Torvalds 	sh->use_clustering = sh->hostt->use_clustering;
67391da177e4SLinus Torvalds 	sh->max_sectors = 128;
67401da177e4SLinus Torvalds 
67411da177e4SLinus Torvalds 	sh->max_id = ha->ntargets;
67421da177e4SLinus Torvalds 	sh->max_lun = ha->nlun;
67431da177e4SLinus Torvalds 	sh->max_channel = ha->nbus - 1;
67441da177e4SLinus Torvalds 	sh->can_queue = ha->max_cmds - 1;
67451da177e4SLinus Torvalds 
67462551a13eSJeff Garzik 	if (scsi_add_host(sh, &ha->pcidev->dev))
67472551a13eSJeff Garzik 		goto err_out;
67482551a13eSJeff Garzik 
67492551a13eSJeff Garzik 	ips_sh[index] = sh;
67502551a13eSJeff Garzik 	ips_ha[index] = ha;
67512551a13eSJeff Garzik 
6752c6a6c81cSAdrian Bunk 	scsi_scan_host(sh);
6753c6a6c81cSAdrian Bunk 
67541da177e4SLinus Torvalds 	return 0;
67552551a13eSJeff Garzik 
67562551a13eSJeff Garzik err_out:
67572551a13eSJeff Garzik 	free_irq(ha->pcidev->irq, ha);
67582551a13eSJeff Garzik err_out_sh:
67592551a13eSJeff Garzik 	scsi_host_put(sh);
67602551a13eSJeff Garzik 	return -1;
67611da177e4SLinus Torvalds }
67621da177e4SLinus Torvalds 
67631da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67641da177e4SLinus Torvalds /*   Routine Name: ips_remove_device                                         */
67651da177e4SLinus Torvalds /*                                                                           */
67661da177e4SLinus Torvalds /*   Routine Description:                                                    */
67671da177e4SLinus Torvalds /*     Remove one Adapter ( Hot Plugging )                                   */
67681da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67696f039790SGreg Kroah-Hartman static void
67701da177e4SLinus Torvalds ips_remove_device(struct pci_dev *pci_dev)
67711da177e4SLinus Torvalds {
677221e1a5f2SJeff Garzik 	struct Scsi_Host *sh = pci_get_drvdata(pci_dev);
67731da177e4SLinus Torvalds 
677421e1a5f2SJeff Garzik 	pci_set_drvdata(pci_dev, NULL);
677521e1a5f2SJeff Garzik 
67761da177e4SLinus Torvalds 	ips_release(sh);
677721e1a5f2SJeff Garzik 
677821e1a5f2SJeff Garzik 	pci_release_regions(pci_dev);
677921e1a5f2SJeff Garzik 	pci_disable_device(pci_dev);
67801da177e4SLinus Torvalds }
67811da177e4SLinus Torvalds 
67821da177e4SLinus Torvalds /****************************************************************************/
67831da177e4SLinus Torvalds /*                                                                          */
67841da177e4SLinus Torvalds /* Routine Name: ips_module_init                                            */
67851da177e4SLinus Torvalds /*                                                                          */
67861da177e4SLinus Torvalds /* Routine Description:                                                     */
67871da177e4SLinus Torvalds /*   function called on module load                                         */
67881da177e4SLinus Torvalds /****************************************************************************/
67891da177e4SLinus Torvalds static int __init
67901da177e4SLinus Torvalds ips_module_init(void)
67911da177e4SLinus Torvalds {
679202a0fa67SAlan Cox 	if (pci_register_driver(&ips_pci_driver) < 0)
67931da177e4SLinus Torvalds 		return -ENODEV;
67941da177e4SLinus Torvalds 	ips_driver_template.module = THIS_MODULE;
67951da177e4SLinus Torvalds 	ips_order_controllers();
6796c6a6c81cSAdrian Bunk 	if (!ips_detect(&ips_driver_template)) {
67971da177e4SLinus Torvalds 		pci_unregister_driver(&ips_pci_driver);
67981da177e4SLinus Torvalds 		return -ENODEV;
67991da177e4SLinus Torvalds 	}
68001da177e4SLinus Torvalds 	register_reboot_notifier(&ips_notifier);
68011da177e4SLinus Torvalds 	return 0;
68021da177e4SLinus Torvalds }
68031da177e4SLinus Torvalds 
68041da177e4SLinus Torvalds /****************************************************************************/
68051da177e4SLinus Torvalds /*                                                                          */
68061da177e4SLinus Torvalds /* Routine Name: ips_module_exit                                            */
68071da177e4SLinus Torvalds /*                                                                          */
68081da177e4SLinus Torvalds /* Routine Description:                                                     */
68091da177e4SLinus Torvalds /*   function called on module unload                                       */
68101da177e4SLinus Torvalds /****************************************************************************/
68111da177e4SLinus Torvalds static void __exit
68121da177e4SLinus Torvalds ips_module_exit(void)
68131da177e4SLinus Torvalds {
68141da177e4SLinus Torvalds 	pci_unregister_driver(&ips_pci_driver);
68151da177e4SLinus Torvalds 	unregister_reboot_notifier(&ips_notifier);
68161da177e4SLinus Torvalds }
68171da177e4SLinus Torvalds 
68181da177e4SLinus Torvalds module_init(ips_module_init);
68191da177e4SLinus Torvalds module_exit(ips_module_exit);
68201da177e4SLinus Torvalds 
68211da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68221da177e4SLinus Torvalds /*   Routine Name: ips_insert_device                                         */
68231da177e4SLinus Torvalds /*                                                                           */
68241da177e4SLinus Torvalds /*   Routine Description:                                                    */
68251da177e4SLinus Torvalds /*     Add One Adapter ( Hot Plug )                                          */
68261da177e4SLinus Torvalds /*                                                                           */
68271da177e4SLinus Torvalds /*   Return Value:                                                           */
68281da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
68291da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68306f039790SGreg Kroah-Hartman static int
68311da177e4SLinus Torvalds ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent)
68321da177e4SLinus Torvalds {
683321e1a5f2SJeff Garzik 	int index = -1;
68341da177e4SLinus Torvalds 	int rc;
68351da177e4SLinus Torvalds 
68361da177e4SLinus Torvalds 	METHOD_TRACE("ips_insert_device", 1);
683721e1a5f2SJeff Garzik 	rc = pci_enable_device(pci_dev);
683821e1a5f2SJeff Garzik 	if (rc)
683921e1a5f2SJeff Garzik 		return rc;
684021e1a5f2SJeff Garzik 
684121e1a5f2SJeff Garzik 	rc = pci_request_regions(pci_dev, "ips");
684221e1a5f2SJeff Garzik 	if (rc)
684321e1a5f2SJeff Garzik 		goto err_out;
68441da177e4SLinus Torvalds 
68451da177e4SLinus Torvalds 	rc = ips_init_phase1(pci_dev, &index);
68461da177e4SLinus Torvalds 	if (rc == SUCCESS)
68471da177e4SLinus Torvalds 		rc = ips_init_phase2(index);
68481da177e4SLinus Torvalds 
68491da177e4SLinus Torvalds 	if (ips_hotplug)
68501da177e4SLinus Torvalds 		if (ips_register_scsi(index)) {
68511da177e4SLinus Torvalds 			ips_free(ips_ha[index]);
68521da177e4SLinus Torvalds 			rc = -1;
68531da177e4SLinus Torvalds 		}
68541da177e4SLinus Torvalds 
68551da177e4SLinus Torvalds 	if (rc == SUCCESS)
68561da177e4SLinus Torvalds 		ips_num_controllers++;
68571da177e4SLinus Torvalds 
68581da177e4SLinus Torvalds 	ips_next_controller = ips_num_controllers;
685921e1a5f2SJeff Garzik 
686021e1a5f2SJeff Garzik 	if (rc < 0) {
686121e1a5f2SJeff Garzik 		rc = -ENODEV;
686221e1a5f2SJeff Garzik 		goto err_out_regions;
686321e1a5f2SJeff Garzik 	}
686421e1a5f2SJeff Garzik 
686521e1a5f2SJeff Garzik 	pci_set_drvdata(pci_dev, ips_sh[index]);
686621e1a5f2SJeff Garzik 	return 0;
686721e1a5f2SJeff Garzik 
686821e1a5f2SJeff Garzik err_out_regions:
686921e1a5f2SJeff Garzik 	pci_release_regions(pci_dev);
687021e1a5f2SJeff Garzik err_out:
687121e1a5f2SJeff Garzik 	pci_disable_device(pci_dev);
68721da177e4SLinus Torvalds 	return rc;
68731da177e4SLinus Torvalds }
68741da177e4SLinus Torvalds 
68751da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68761da177e4SLinus Torvalds /*   Routine Name: ips_init_phase1                                           */
68771da177e4SLinus Torvalds /*                                                                           */
68781da177e4SLinus Torvalds /*   Routine Description:                                                    */
68791da177e4SLinus Torvalds /*     Adapter Initialization                                                */
68801da177e4SLinus Torvalds /*                                                                           */
68811da177e4SLinus Torvalds /*   Return Value:                                                           */
68821da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
68831da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68841da177e4SLinus Torvalds static int
68851da177e4SLinus Torvalds ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
68861da177e4SLinus Torvalds {
68871da177e4SLinus Torvalds 	ips_ha_t *ha;
68881da177e4SLinus Torvalds 	uint32_t io_addr;
68891da177e4SLinus Torvalds 	uint32_t mem_addr;
68901da177e4SLinus Torvalds 	uint32_t io_len;
68911da177e4SLinus Torvalds 	uint32_t mem_len;
68921da177e4SLinus Torvalds 	uint8_t bus;
68931da177e4SLinus Torvalds 	uint8_t func;
68941da177e4SLinus Torvalds 	int j;
68951da177e4SLinus Torvalds 	int index;
68961da177e4SLinus Torvalds 	dma_addr_t dma_address;
68971da177e4SLinus Torvalds 	char __iomem *ioremap_ptr;
68981da177e4SLinus Torvalds 	char __iomem *mem_ptr;
68991da177e4SLinus Torvalds 	uint32_t IsDead;
69001da177e4SLinus Torvalds 
69011da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_phase1", 1);
69021da177e4SLinus Torvalds 	index = IPS_MAX_ADAPTERS;
69031da177e4SLinus Torvalds 	for (j = 0; j < IPS_MAX_ADAPTERS; j++) {
690421e1a5f2SJeff Garzik 		if (ips_ha[j] == NULL) {
69051da177e4SLinus Torvalds 			index = j;
69061da177e4SLinus Torvalds 			break;
69071da177e4SLinus Torvalds 		}
69081da177e4SLinus Torvalds 	}
69091da177e4SLinus Torvalds 
69101da177e4SLinus Torvalds 	if (index >= IPS_MAX_ADAPTERS)
69111da177e4SLinus Torvalds 		return -1;
69121da177e4SLinus Torvalds 
69131da177e4SLinus Torvalds 	/* stuff that we get in dev */
69141da177e4SLinus Torvalds 	bus = pci_dev->bus->number;
69151da177e4SLinus Torvalds 	func = pci_dev->devfn;
69161da177e4SLinus Torvalds 
69171da177e4SLinus Torvalds 	/* Init MEM/IO addresses to 0 */
69181da177e4SLinus Torvalds 	mem_addr = 0;
69191da177e4SLinus Torvalds 	io_addr = 0;
69201da177e4SLinus Torvalds 	mem_len = 0;
69211da177e4SLinus Torvalds 	io_len = 0;
69221da177e4SLinus Torvalds 
69231da177e4SLinus Torvalds 	for (j = 0; j < 2; j++) {
69241da177e4SLinus Torvalds 		if (!pci_resource_start(pci_dev, j))
69251da177e4SLinus Torvalds 			break;
69261da177e4SLinus Torvalds 
69271da177e4SLinus Torvalds 		if (pci_resource_flags(pci_dev, j) & IORESOURCE_IO) {
69281da177e4SLinus Torvalds 			io_addr = pci_resource_start(pci_dev, j);
69291da177e4SLinus Torvalds 			io_len = pci_resource_len(pci_dev, j);
69301da177e4SLinus Torvalds 		} else {
69311da177e4SLinus Torvalds 			mem_addr = pci_resource_start(pci_dev, j);
69321da177e4SLinus Torvalds 			mem_len = pci_resource_len(pci_dev, j);
69331da177e4SLinus Torvalds 		}
69341da177e4SLinus Torvalds 	}
69351da177e4SLinus Torvalds 
69361da177e4SLinus Torvalds 	/* setup memory mapped area (if applicable) */
69371da177e4SLinus Torvalds 	if (mem_addr) {
69381da177e4SLinus Torvalds 		uint32_t base;
69391da177e4SLinus Torvalds 		uint32_t offs;
69401da177e4SLinus Torvalds 
69411da177e4SLinus Torvalds 		base = mem_addr & PAGE_MASK;
69421da177e4SLinus Torvalds 		offs = mem_addr - base;
69431da177e4SLinus Torvalds 		ioremap_ptr = ioremap(base, PAGE_SIZE);
694421e1a5f2SJeff Garzik 		if (!ioremap_ptr)
694521e1a5f2SJeff Garzik 			return -1;
69461da177e4SLinus Torvalds 		mem_ptr = ioremap_ptr + offs;
69471da177e4SLinus Torvalds 	} else {
69481da177e4SLinus Torvalds 		ioremap_ptr = NULL;
69491da177e4SLinus Torvalds 		mem_ptr = NULL;
69501da177e4SLinus Torvalds 	}
69511da177e4SLinus Torvalds 
69521da177e4SLinus Torvalds 	/* found a controller */
6953dd00cc48SYoann Padioleau 	ha = kzalloc(sizeof (ips_ha_t), GFP_KERNEL);
69541da177e4SLinus Torvalds 	if (ha == NULL) {
69551da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69561da177e4SLinus Torvalds 			   "Unable to allocate temporary ha struct\n");
69571da177e4SLinus Torvalds 		return -1;
69581da177e4SLinus Torvalds 	}
69591da177e4SLinus Torvalds 
69601da177e4SLinus Torvalds 	ips_sh[index] = NULL;
69611da177e4SLinus Torvalds 	ips_ha[index] = ha;
69621da177e4SLinus Torvalds 	ha->active = 1;
69631da177e4SLinus Torvalds 
69641da177e4SLinus Torvalds 	/* Store info in HA structure */
69651da177e4SLinus Torvalds 	ha->io_addr = io_addr;
69661da177e4SLinus Torvalds 	ha->io_len = io_len;
69671da177e4SLinus Torvalds 	ha->mem_addr = mem_addr;
69681da177e4SLinus Torvalds 	ha->mem_len = mem_len;
69691da177e4SLinus Torvalds 	ha->mem_ptr = mem_ptr;
69701da177e4SLinus Torvalds 	ha->ioremap_ptr = ioremap_ptr;
69711da177e4SLinus Torvalds 	ha->host_num = (uint32_t) index;
69721da177e4SLinus Torvalds 	ha->slot_num = PCI_SLOT(pci_dev->devfn);
69731da177e4SLinus Torvalds 	ha->pcidev = pci_dev;
69741da177e4SLinus Torvalds 
69751da177e4SLinus Torvalds 	/*
69761da177e4SLinus Torvalds 	 * Set the pci_dev's dma_mask.  Not all adapters support 64bit
69771da177e4SLinus Torvalds 	 * addressing so don't enable it if the adapter can't support
69781da177e4SLinus Torvalds 	 * it!  Also, don't use 64bit addressing if dma addresses
69791da177e4SLinus Torvalds 	 * are guaranteed to be < 4G.
69801da177e4SLinus Torvalds 	 */
69811da177e4SLinus Torvalds 	if (IPS_ENABLE_DMA64 && IPS_HAS_ENH_SGLIST(ha) &&
69826a35528aSYang Hongyang 	    !pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(64))) {
69831da177e4SLinus Torvalds 		(ha)->flags |= IPS_HA_ENH_SG;
69841da177e4SLinus Torvalds 	} else {
6985284901a9SYang Hongyang 		if (pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(32)) != 0) {
69861da177e4SLinus Torvalds 			printk(KERN_WARNING "Unable to set DMA Mask\n");
69871da177e4SLinus Torvalds 			return ips_abort_init(ha, index);
69881da177e4SLinus Torvalds 		}
69891da177e4SLinus Torvalds 	}
69901da177e4SLinus Torvalds 	if(ips_cd_boot && !ips_FlashData){
69911da177e4SLinus Torvalds 		ips_FlashData = pci_alloc_consistent(pci_dev, PAGE_SIZE << 7,
69921da177e4SLinus Torvalds 						     &ips_flashbusaddr);
69931da177e4SLinus Torvalds 	}
69941da177e4SLinus Torvalds 
69951da177e4SLinus Torvalds 	ha->enq = pci_alloc_consistent(pci_dev, sizeof (IPS_ENQ),
69961da177e4SLinus Torvalds 				       &ha->enq_busaddr);
69971da177e4SLinus Torvalds 	if (!ha->enq) {
69981da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69991da177e4SLinus Torvalds 			   "Unable to allocate host inquiry structure\n");
70001da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70011da177e4SLinus Torvalds 	}
70021da177e4SLinus Torvalds 
70031da177e4SLinus Torvalds 	ha->adapt = pci_alloc_consistent(pci_dev, sizeof (IPS_ADAPTER) +
70041da177e4SLinus Torvalds 					 sizeof (IPS_IO_CMD), &dma_address);
70051da177e4SLinus Torvalds 	if (!ha->adapt) {
70061da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70071da177e4SLinus Torvalds 			   "Unable to allocate host adapt & dummy structures\n");
70081da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70091da177e4SLinus Torvalds 	}
70101da177e4SLinus Torvalds 	ha->adapt->hw_status_start = dma_address;
70111da177e4SLinus Torvalds 	ha->dummy = (void *) (ha->adapt + 1);
70121da177e4SLinus Torvalds 
70131da177e4SLinus Torvalds 
70141da177e4SLinus Torvalds 
70151da177e4SLinus Torvalds 	ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), &dma_address);
70161da177e4SLinus Torvalds 	if (!ha->logical_drive_info) {
70171da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70181da177e4SLinus Torvalds 			   "Unable to allocate logical drive info structure\n");
70191da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70201da177e4SLinus Torvalds 	}
70211da177e4SLinus Torvalds 	ha->logical_drive_info_dma_addr = dma_address;
70221da177e4SLinus Torvalds 
70231da177e4SLinus Torvalds 
70241da177e4SLinus Torvalds 	ha->conf = kmalloc(sizeof (IPS_CONF), GFP_KERNEL);
70251da177e4SLinus Torvalds 
70261da177e4SLinus Torvalds 	if (!ha->conf) {
70271da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70281da177e4SLinus Torvalds 			   "Unable to allocate host conf structure\n");
70291da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70301da177e4SLinus Torvalds 	}
70311da177e4SLinus Torvalds 
70321da177e4SLinus Torvalds 	ha->nvram = kmalloc(sizeof (IPS_NVRAM_P5), GFP_KERNEL);
70331da177e4SLinus Torvalds 
70341da177e4SLinus Torvalds 	if (!ha->nvram) {
70351da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70361da177e4SLinus Torvalds 			   "Unable to allocate host NVRAM structure\n");
70371da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70381da177e4SLinus Torvalds 	}
70391da177e4SLinus Torvalds 
70401da177e4SLinus Torvalds 	ha->subsys = kmalloc(sizeof (IPS_SUBSYS), GFP_KERNEL);
70411da177e4SLinus Torvalds 
70421da177e4SLinus Torvalds 	if (!ha->subsys) {
70431da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70441da177e4SLinus Torvalds 			   "Unable to allocate host subsystem structure\n");
70451da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70461da177e4SLinus Torvalds 	}
70471da177e4SLinus Torvalds 
70481da177e4SLinus Torvalds 	/* the ioctl buffer is now used during adapter initialization, so its
70491da177e4SLinus Torvalds 	 * successful allocation is now required */
70501da177e4SLinus Torvalds 	if (ips_ioctlsize < PAGE_SIZE)
70511da177e4SLinus Torvalds 		ips_ioctlsize = PAGE_SIZE;
70521da177e4SLinus Torvalds 
70531da177e4SLinus Torvalds 	ha->ioctl_data = pci_alloc_consistent(pci_dev, ips_ioctlsize,
70541da177e4SLinus Torvalds 					      &ha->ioctl_busaddr);
70551da177e4SLinus Torvalds 	ha->ioctl_len = ips_ioctlsize;
70561da177e4SLinus Torvalds 	if (!ha->ioctl_data) {
70571da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70581da177e4SLinus Torvalds 			   "Unable to allocate IOCTL data\n");
70591da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70601da177e4SLinus Torvalds 	}
70611da177e4SLinus Torvalds 
70621da177e4SLinus Torvalds 	/*
70631da177e4SLinus Torvalds 	 * Setup Functions
70641da177e4SLinus Torvalds 	 */
70651da177e4SLinus Torvalds 	ips_setup_funclist(ha);
70661da177e4SLinus Torvalds 
70671da177e4SLinus Torvalds 	if ((IPS_IS_MORPHEUS(ha)) || (IPS_IS_MARCO(ha))) {
70681da177e4SLinus Torvalds 		/* If Morpheus appears dead, reset it */
70691da177e4SLinus Torvalds 		IsDead = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
70701da177e4SLinus Torvalds 		if (IsDead == 0xDEADBEEF) {
70711da177e4SLinus Torvalds 			ips_reset_morpheus(ha);
70721da177e4SLinus Torvalds 		}
70731da177e4SLinus Torvalds 	}
70741da177e4SLinus Torvalds 
70751da177e4SLinus Torvalds 	/*
70761da177e4SLinus Torvalds 	 * Initialize the card if it isn't already
70771da177e4SLinus Torvalds 	 */
70781da177e4SLinus Torvalds 
70791da177e4SLinus Torvalds 	if (!(*ha->func.isinit) (ha)) {
70801da177e4SLinus Torvalds 		if (!(*ha->func.init) (ha)) {
70811da177e4SLinus Torvalds 			/*
70821da177e4SLinus Torvalds 			 * Initialization failed
70831da177e4SLinus Torvalds 			 */
70841da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, pci_dev,
70851da177e4SLinus Torvalds 				   "Unable to initialize controller\n");
70861da177e4SLinus Torvalds 			return ips_abort_init(ha, index);
70871da177e4SLinus Torvalds 		}
70881da177e4SLinus Torvalds 	}
70891da177e4SLinus Torvalds 
70901da177e4SLinus Torvalds 	*indexPtr = index;
70911da177e4SLinus Torvalds 	return SUCCESS;
70921da177e4SLinus Torvalds }
70931da177e4SLinus Torvalds 
70941da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
70951da177e4SLinus Torvalds /*   Routine Name: ips_init_phase2                                           */
70961da177e4SLinus Torvalds /*                                                                           */
70971da177e4SLinus Torvalds /*   Routine Description:                                                    */
70981da177e4SLinus Torvalds /*     Adapter Initialization Phase 2                                        */
70991da177e4SLinus Torvalds /*                                                                           */
71001da177e4SLinus Torvalds /*   Return Value:                                                           */
71011da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
71021da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
71031da177e4SLinus Torvalds static int
71041da177e4SLinus Torvalds ips_init_phase2(int index)
71051da177e4SLinus Torvalds {
71061da177e4SLinus Torvalds 	ips_ha_t *ha;
71071da177e4SLinus Torvalds 
71081da177e4SLinus Torvalds 	ha = ips_ha[index];
71091da177e4SLinus Torvalds 
71101da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_phase2", 1);
71111da177e4SLinus Torvalds 	if (!ha->active) {
71121da177e4SLinus Torvalds 		ips_ha[index] = NULL;
71131da177e4SLinus Torvalds 		return -1;
71141da177e4SLinus Torvalds 	}
71151da177e4SLinus Torvalds 
71161da177e4SLinus Torvalds 	/* Install the interrupt handler */
71178a694cc8SJeff Garzik 	if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) {
71181da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
71191da177e4SLinus Torvalds 			   "Unable to install interrupt handler\n");
71201da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
71211da177e4SLinus Torvalds 	}
71221da177e4SLinus Torvalds 
71231da177e4SLinus Torvalds 	/*
71241da177e4SLinus Torvalds 	 * Allocate a temporary SCB for initialization
71251da177e4SLinus Torvalds 	 */
71261da177e4SLinus Torvalds 	ha->max_cmds = 1;
71271da177e4SLinus Torvalds 	if (!ips_allocatescbs(ha)) {
71281da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
71291da177e4SLinus Torvalds 			   "Unable to allocate a CCB\n");
71308a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
71311da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
71321da177e4SLinus Torvalds 	}
71331da177e4SLinus Torvalds 
71341da177e4SLinus Torvalds 	if (!ips_hainit(ha)) {
71351da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
71361da177e4SLinus Torvalds 			   "Unable to initialize controller\n");
71378a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
71381da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
71391da177e4SLinus Torvalds 	}
71401da177e4SLinus Torvalds 	/* Free the temporary SCB */
71411da177e4SLinus Torvalds 	ips_deallocatescbs(ha, 1);
71421da177e4SLinus Torvalds 
71431da177e4SLinus Torvalds 	/* allocate CCBs */
71441da177e4SLinus Torvalds 	if (!ips_allocatescbs(ha)) {
71451da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
71461da177e4SLinus Torvalds 			   "Unable to allocate CCBs\n");
71478a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
71481da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
71491da177e4SLinus Torvalds 	}
71501da177e4SLinus Torvalds 
71511da177e4SLinus Torvalds 	return SUCCESS;
71521da177e4SLinus Torvalds }
71531da177e4SLinus Torvalds 
71541da177e4SLinus Torvalds MODULE_LICENSE("GPL");
71551da177e4SLinus Torvalds MODULE_DESCRIPTION("IBM ServeRAID Adapter Driver " IPS_VER_STRING);
71561da177e4SLinus Torvalds MODULE_VERSION(IPS_VER_STRING);
71571da177e4SLinus Torvalds 
71581da177e4SLinus Torvalds 
71591da177e4SLinus Torvalds /*
71601da177e4SLinus Torvalds  * Overrides for Emacs so that we almost follow Linus's tabbing style.
71611da177e4SLinus Torvalds  * Emacs will notice this stuff at the end of the file and automatically
71621da177e4SLinus Torvalds  * adjust the settings for this buffer only.  This must remain at the end
71631da177e4SLinus Torvalds  * of the file.
71641da177e4SLinus Torvalds  * ---------------------------------------------------------------------------
71651da177e4SLinus Torvalds  * Local variables:
71661da177e4SLinus Torvalds  * c-indent-level: 2
71671da177e4SLinus Torvalds  * c-brace-imaginary-offset: 0
71681da177e4SLinus Torvalds  * c-brace-offset: -2
71691da177e4SLinus Torvalds  * c-argdecl-indent: 2
71701da177e4SLinus Torvalds  * c-label-offset: -2
71711da177e4SLinus Torvalds  * c-continued-statement-offset: 2
71721da177e4SLinus Torvalds  * c-continued-brace-offset: 0
71731da177e4SLinus Torvalds  * indent-tabs-mode: nil
71741da177e4SLinus Torvalds  * tab-width: 8
71751da177e4SLinus Torvalds  * End:
71761da177e4SLinus Torvalds  */
7177