xref: /openbmc/linux/drivers/scsi/ips.c (revision 88693b3c9681a0c10b8fe73a0be9bfa0ea63de1f)
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 #define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
210be7db055S                          DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
21132e76961SChristoph Hellwig                          DMA_BIDIRECTIONAL : \
212be7db055S                          scb->scsi_cmd->sc_data_direction)
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds #ifdef IPS_DEBUG
2151da177e4SLinus Torvalds #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
2161da177e4SLinus Torvalds #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n");
2171da177e4SLinus Torvalds #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
2181da177e4SLinus Torvalds #else
2191da177e4SLinus Torvalds #define METHOD_TRACE(s, i)
2201da177e4SLinus Torvalds #define DEBUG(i, s)
2211da177e4SLinus Torvalds #define DEBUG_VAR(i, s, v...)
2221da177e4SLinus Torvalds #endif
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /*
2251da177e4SLinus Torvalds  * Function prototypes
2261da177e4SLinus Torvalds  */
2271516b55dSHenne static int ips_eh_abort(struct scsi_cmnd *);
2281516b55dSHenne static int ips_eh_reset(struct scsi_cmnd *);
229f281233dSJeff Garzik static int ips_queue(struct Scsi_Host *, struct scsi_cmnd *);
2301da177e4SLinus Torvalds static const char *ips_info(struct Scsi_Host *);
2317d12e780SDavid Howells static irqreturn_t do_ipsintr(int, void *);
2321da177e4SLinus Torvalds static int ips_hainit(ips_ha_t *);
2331da177e4SLinus Torvalds static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
2341da177e4SLinus Torvalds static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
2351da177e4SLinus Torvalds static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
2361da177e4SLinus Torvalds static int ips_online(ips_ha_t *, ips_scb_t *);
2371da177e4SLinus Torvalds static int ips_inquiry(ips_ha_t *, ips_scb_t *);
2381da177e4SLinus Torvalds static int ips_rdcap(ips_ha_t *, ips_scb_t *);
2391da177e4SLinus Torvalds static int ips_msense(ips_ha_t *, ips_scb_t *);
2401da177e4SLinus Torvalds static int ips_reqsen(ips_ha_t *, ips_scb_t *);
2411da177e4SLinus Torvalds static int ips_deallocatescbs(ips_ha_t *, int);
2421da177e4SLinus Torvalds static int ips_allocatescbs(ips_ha_t *);
2431da177e4SLinus Torvalds static int ips_reset_copperhead(ips_ha_t *);
2441da177e4SLinus Torvalds static int ips_reset_copperhead_memio(ips_ha_t *);
2451da177e4SLinus Torvalds static int ips_reset_morpheus(ips_ha_t *);
2461da177e4SLinus Torvalds static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
2471da177e4SLinus Torvalds static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
2481da177e4SLinus Torvalds static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
2491da177e4SLinus Torvalds static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
2501da177e4SLinus Torvalds static int ips_isintr_copperhead(ips_ha_t *);
2511da177e4SLinus Torvalds static int ips_isintr_copperhead_memio(ips_ha_t *);
2521da177e4SLinus Torvalds static int ips_isintr_morpheus(ips_ha_t *);
2531da177e4SLinus Torvalds static int ips_wait(ips_ha_t *, int, int);
2541da177e4SLinus Torvalds static int ips_write_driver_status(ips_ha_t *, int);
2551da177e4SLinus Torvalds static int ips_read_adapter_status(ips_ha_t *, int);
2561da177e4SLinus Torvalds static int ips_read_subsystem_parameters(ips_ha_t *, int);
2571da177e4SLinus Torvalds static int ips_read_config(ips_ha_t *, int);
2581da177e4SLinus Torvalds static int ips_clear_adapter(ips_ha_t *, int);
2591da177e4SLinus Torvalds static int ips_readwrite_page5(ips_ha_t *, int, int);
2601da177e4SLinus Torvalds static int ips_init_copperhead(ips_ha_t *);
2611da177e4SLinus Torvalds static int ips_init_copperhead_memio(ips_ha_t *);
2621da177e4SLinus Torvalds static int ips_init_morpheus(ips_ha_t *);
2631da177e4SLinus Torvalds static int ips_isinit_copperhead(ips_ha_t *);
2641da177e4SLinus Torvalds static int ips_isinit_copperhead_memio(ips_ha_t *);
2651da177e4SLinus Torvalds static int ips_isinit_morpheus(ips_ha_t *);
2661da177e4SLinus Torvalds static int ips_erase_bios(ips_ha_t *);
2671da177e4SLinus Torvalds static int ips_program_bios(ips_ha_t *, char *, uint32_t, uint32_t);
2681da177e4SLinus Torvalds static int ips_verify_bios(ips_ha_t *, char *, uint32_t, uint32_t);
2691da177e4SLinus Torvalds static int ips_erase_bios_memio(ips_ha_t *);
2701da177e4SLinus Torvalds static int ips_program_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
2711da177e4SLinus Torvalds static int ips_verify_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
2721da177e4SLinus Torvalds static int ips_flash_copperhead(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2731da177e4SLinus Torvalds static int ips_flash_bios(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2741da177e4SLinus Torvalds static int ips_flash_firmware(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
2751da177e4SLinus Torvalds static void ips_free_flash_copperhead(ips_ha_t * ha);
2761da177e4SLinus Torvalds static void ips_get_bios_version(ips_ha_t *, int);
2771da177e4SLinus Torvalds static void ips_identify_controller(ips_ha_t *);
2781da177e4SLinus Torvalds static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
2791da177e4SLinus Torvalds static void ips_enable_int_copperhead(ips_ha_t *);
2801da177e4SLinus Torvalds static void ips_enable_int_copperhead_memio(ips_ha_t *);
2811da177e4SLinus Torvalds static void ips_enable_int_morpheus(ips_ha_t *);
2821da177e4SLinus Torvalds static int ips_intr_copperhead(ips_ha_t *);
2831da177e4SLinus Torvalds static int ips_intr_morpheus(ips_ha_t *);
2841da177e4SLinus Torvalds static void ips_next(ips_ha_t *, int);
2851da177e4SLinus Torvalds static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
2861da177e4SLinus Torvalds static void ipsintr_done(ips_ha_t *, struct ips_scb *);
2871da177e4SLinus Torvalds static void ips_done(ips_ha_t *, ips_scb_t *);
2881da177e4SLinus Torvalds static void ips_free(ips_ha_t *);
2891da177e4SLinus Torvalds static void ips_init_scb(ips_ha_t *, ips_scb_t *);
2901da177e4SLinus Torvalds static void ips_freescb(ips_ha_t *, ips_scb_t *);
2911da177e4SLinus Torvalds static void ips_setup_funclist(ips_ha_t *);
2921da177e4SLinus Torvalds static void ips_statinit(ips_ha_t *);
2931da177e4SLinus Torvalds static void ips_statinit_memio(ips_ha_t *);
294f990bee3SArnd Bergmann static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time64_t);
2951da177e4SLinus Torvalds static void ips_ffdc_reset(ips_ha_t *, int);
2961da177e4SLinus Torvalds static void ips_ffdc_time(ips_ha_t *);
2971da177e4SLinus Torvalds static uint32_t ips_statupd_copperhead(ips_ha_t *);
2981da177e4SLinus Torvalds static uint32_t ips_statupd_copperhead_memio(ips_ha_t *);
2991da177e4SLinus Torvalds static uint32_t ips_statupd_morpheus(ips_ha_t *);
3001da177e4SLinus Torvalds static ips_scb_t *ips_getscb(ips_ha_t *);
3011da177e4SLinus Torvalds static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
302ac6424b9SIngo Molnar static void ips_putq_wait_tail(ips_wait_queue_entry_t *, struct scsi_cmnd *);
3031da177e4SLinus Torvalds static void ips_putq_copp_tail(ips_copp_queue_t *,
3041da177e4SLinus Torvalds 				      ips_copp_wait_item_t *);
3051da177e4SLinus Torvalds static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *);
3061da177e4SLinus Torvalds static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
307ac6424b9SIngo Molnar static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_entry_t *);
308ac6424b9SIngo Molnar static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_entry_t *,
3091516b55dSHenne 					  struct scsi_cmnd *);
3101da177e4SLinus Torvalds static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *,
3111da177e4SLinus Torvalds 						     ips_copp_wait_item_t *);
3121da177e4SLinus Torvalds static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *);
3131da177e4SLinus Torvalds 
3141516b55dSHenne static int ips_is_passthru(struct scsi_cmnd *);
3151516b55dSHenne static int ips_make_passthru(ips_ha_t *, struct scsi_cmnd *, ips_scb_t *, int);
3161da177e4SLinus Torvalds static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
3171da177e4SLinus Torvalds static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *);
3181516b55dSHenne static void ips_scmd_buf_write(struct scsi_cmnd * scmd, void *data,
3191da177e4SLinus Torvalds 			       unsigned int count);
3201516b55dSHenne static void ips_scmd_buf_read(struct scsi_cmnd * scmd, void *data,
3211516b55dSHenne 			      unsigned int count);
3221da177e4SLinus Torvalds 
323aacce706SAl Viro static int ips_write_info(struct Scsi_Host *, char *, int);
324aacce706SAl Viro static int ips_show_info(struct seq_file *, struct Scsi_Host *);
325aacce706SAl Viro static int ips_host_info(ips_ha_t *, struct seq_file *);
3261da177e4SLinus Torvalds static int ips_abort_init(ips_ha_t * ha, int index);
3271da177e4SLinus Torvalds static int ips_init_phase2(int index);
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
3301da177e4SLinus Torvalds static int ips_register_scsi(int index);
3311da177e4SLinus Torvalds 
332ee807c2dSJack Hammer static int  ips_poll_for_flush_complete(ips_ha_t * ha);
333ee807c2dSJack Hammer static void ips_flush_and_reset(ips_ha_t *ha);
334ee807c2dSJack Hammer 
3351da177e4SLinus Torvalds /*
3361da177e4SLinus Torvalds  * global variables
3371da177e4SLinus Torvalds  */
3381da177e4SLinus Torvalds static const char ips_name[] = "ips";
3391da177e4SLinus Torvalds static struct Scsi_Host *ips_sh[IPS_MAX_ADAPTERS];	/* Array of host controller structures */
3401da177e4SLinus Torvalds static ips_ha_t *ips_ha[IPS_MAX_ADAPTERS];	/* Array of HA structures */
3411da177e4SLinus Torvalds static unsigned int ips_next_controller;
3421da177e4SLinus Torvalds static unsigned int ips_num_controllers;
3431da177e4SLinus Torvalds static unsigned int ips_released_controllers;
3441da177e4SLinus Torvalds static int ips_hotplug;
3451da177e4SLinus Torvalds static int ips_cmd_timeout = 60;
3461da177e4SLinus Torvalds static int ips_reset_timeout = 60 * 5;
3471da177e4SLinus Torvalds static int ips_force_memio = 1;		/* Always use Memory Mapped I/O    */
3481da177e4SLinus Torvalds static int ips_force_i2o = 1;	/* Always use I2O command delivery */
3491da177e4SLinus Torvalds static int ips_ioctlsize = IPS_IOCTL_SIZE;	/* Size of the ioctl buffer        */
3501da177e4SLinus Torvalds static int ips_cd_boot;			/* Booting from Manager CD         */
3511da177e4SLinus Torvalds static char *ips_FlashData = NULL;	/* CD Boot - Flash Data Buffer      */
3521da177e4SLinus Torvalds static dma_addr_t ips_flashbusaddr;
3531da177e4SLinus Torvalds static long ips_FlashDataInUse;		/* CD Boot - Flash Data In Use Flag */
3541da177e4SLinus Torvalds static uint32_t MaxLiteCmds = 32;	/* Max Active Cmds for a Lite Adapter */
355d0be4a7dSChristoph Hellwig static struct scsi_host_template ips_driver_template = {
3561da177e4SLinus Torvalds 	.info			= ips_info,
3571da177e4SLinus Torvalds 	.queuecommand		= ips_queue,
3581da177e4SLinus Torvalds 	.eh_abort_handler	= ips_eh_abort,
3591da177e4SLinus Torvalds 	.eh_host_reset_handler	= ips_eh_reset,
3601da177e4SLinus Torvalds 	.proc_name		= "ips",
361aacce706SAl Viro 	.show_info		= ips_show_info,
362aacce706SAl Viro 	.write_info		= ips_write_info,
3631da177e4SLinus Torvalds 	.slave_configure	= ips_slave_configure,
3641da177e4SLinus Torvalds 	.bios_param		= ips_biosparam,
3651da177e4SLinus Torvalds 	.this_id		= -1,
3661da177e4SLinus Torvalds 	.sg_tablesize		= IPS_MAX_SG,
3671da177e4SLinus Torvalds 	.cmd_per_lun		= 3,
3681da177e4SLinus Torvalds 	.use_clustering		= ENABLE_CLUSTERING,
36954b2b50cSMartin K. Petersen 	.no_write_same		= 1,
3701da177e4SLinus Torvalds };
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds /* This table describes all ServeRAID Adapters */
3741da177e4SLinus Torvalds static struct  pci_device_id  ips_pci_table[] = {
3751da177e4SLinus Torvalds 	{ 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3761da177e4SLinus Torvalds 	{ 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3771da177e4SLinus Torvalds 	{ 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
3781da177e4SLinus Torvalds 	{ 0, }
3791da177e4SLinus Torvalds };
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds MODULE_DEVICE_TABLE( pci, ips_pci_table );
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds static char ips_hot_plug_name[] = "ips";
3841da177e4SLinus Torvalds 
3856f039790SGreg Kroah-Hartman static int  ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
3866f039790SGreg Kroah-Hartman static void ips_remove_device(struct pci_dev *pci_dev);
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds static struct pci_driver ips_pci_driver = {
3891da177e4SLinus Torvalds 	.name		= ips_hot_plug_name,
3901da177e4SLinus Torvalds 	.id_table	= ips_pci_table,
3911da177e4SLinus Torvalds 	.probe		= ips_insert_device,
3926f039790SGreg Kroah-Hartman 	.remove		= ips_remove_device,
3931da177e4SLinus Torvalds };
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds /*
3971da177e4SLinus Torvalds  * Necessary forward function protoypes
3981da177e4SLinus Torvalds  */
3991da177e4SLinus Torvalds static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds #define MAX_ADAPTER_NAME 15
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds static char ips_adapter_name[][30] = {
4041da177e4SLinus Torvalds 	"ServeRAID",
4051da177e4SLinus Torvalds 	"ServeRAID II",
4061da177e4SLinus Torvalds 	"ServeRAID on motherboard",
4071da177e4SLinus Torvalds 	"ServeRAID on motherboard",
4081da177e4SLinus Torvalds 	"ServeRAID 3H",
4091da177e4SLinus Torvalds 	"ServeRAID 3L",
4101da177e4SLinus Torvalds 	"ServeRAID 4H",
4111da177e4SLinus Torvalds 	"ServeRAID 4M",
4121da177e4SLinus Torvalds 	"ServeRAID 4L",
4131da177e4SLinus Torvalds 	"ServeRAID 4Mx",
4141da177e4SLinus Torvalds 	"ServeRAID 4Lx",
4151da177e4SLinus Torvalds 	"ServeRAID 5i",
4161da177e4SLinus Torvalds 	"ServeRAID 5i",
4171da177e4SLinus Torvalds 	"ServeRAID 6M",
4181da177e4SLinus Torvalds 	"ServeRAID 6i",
4191da177e4SLinus Torvalds 	"ServeRAID 7t",
4201da177e4SLinus Torvalds 	"ServeRAID 7k",
4211da177e4SLinus Torvalds 	"ServeRAID 7M"
4221da177e4SLinus Torvalds };
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds static struct notifier_block ips_notifier = {
4251da177e4SLinus Torvalds 	ips_halt, NULL, 0
4261da177e4SLinus Torvalds };
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds /*
4291da177e4SLinus Torvalds  * Direction table
4301da177e4SLinus Torvalds  */
4311da177e4SLinus Torvalds static char ips_command_direction[] = {
4321da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT,
4331da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK,
4341da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4351da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
4361da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT,
4371da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
4381da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN,
4391da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
4401da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK,
4411da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
4421da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE,
4431da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
4441da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT,
4451da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE,
4461da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK,
4471da177e4SLinus Torvalds 	IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
4481da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4491da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4501da177e4SLinus Torvalds 	IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4511da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4521da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4531da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4541da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4551da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, 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_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4581da177e4SLinus Torvalds 	IPS_DATA_UNK, 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_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE,
4661da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT,
4671da177e4SLinus Torvalds 	IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE,
4681da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN,
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_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4741da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4751da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
4761da177e4SLinus Torvalds 	IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
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_OUT,
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 };
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds /****************************************************************************/
4871da177e4SLinus Torvalds /*                                                                          */
4881da177e4SLinus Torvalds /* Routine Name: ips_setup                                                  */
4891da177e4SLinus Torvalds /*                                                                          */
4901da177e4SLinus Torvalds /* Routine Description:                                                     */
4911da177e4SLinus Torvalds /*                                                                          */
4921da177e4SLinus Torvalds /*   setup parameters to the driver                                         */
4931da177e4SLinus Torvalds /*                                                                          */
4941da177e4SLinus Torvalds /****************************************************************************/
4951da177e4SLinus Torvalds static int
4961da177e4SLinus Torvalds ips_setup(char *ips_str)
4971da177e4SLinus Torvalds {
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	int i;
5001da177e4SLinus Torvalds 	char *key;
5011da177e4SLinus Torvalds 	char *value;
5021da177e4SLinus Torvalds 	IPS_OPTION options[] = {
5031da177e4SLinus Torvalds 		{"noi2o", &ips_force_i2o, 0},
5041da177e4SLinus Torvalds 		{"nommap", &ips_force_memio, 0},
5051da177e4SLinus Torvalds 		{"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
5061da177e4SLinus Torvalds 		{"cdboot", &ips_cd_boot, 0},
5071da177e4SLinus Torvalds 		{"maxcmds", &MaxLiteCmds, 32},
5081da177e4SLinus Torvalds 	};
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	/* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */
5111da177e4SLinus Torvalds 	/* Search for value */
5121da177e4SLinus Torvalds 	while ((key = strsep(&ips_str, ",."))) {
5131da177e4SLinus Torvalds 		if (!*key)
5141da177e4SLinus Torvalds 			continue;
5151da177e4SLinus Torvalds 		value = strchr(key, ':');
5161da177e4SLinus Torvalds 		if (value)
5171da177e4SLinus Torvalds 			*value++ = '\0';
5181da177e4SLinus Torvalds 		/*
5191da177e4SLinus Torvalds 		 * We now have key/value pairs.
5201da177e4SLinus Torvalds 		 * Update the variables
5211da177e4SLinus Torvalds 		 */
5226391a113STobias Klauser 		for (i = 0; i < ARRAY_SIZE(options); i++) {
52348a96876SRasmus Villemoes 			if (strncasecmp
5241da177e4SLinus Torvalds 			    (key, options[i].option_name,
5251da177e4SLinus Torvalds 			     strlen(options[i].option_name)) == 0) {
5261da177e4SLinus Torvalds 				if (value)
5271da177e4SLinus Torvalds 					*options[i].option_flag =
5281da177e4SLinus Torvalds 					    simple_strtoul(value, NULL, 0);
5291da177e4SLinus Torvalds 				else
5301da177e4SLinus Torvalds 					*options[i].option_flag =
5311da177e4SLinus Torvalds 					    options[i].option_value;
5321da177e4SLinus Torvalds 				break;
5331da177e4SLinus Torvalds 			}
5341da177e4SLinus Torvalds 		}
5351da177e4SLinus Torvalds 	}
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	return (1);
5381da177e4SLinus Torvalds }
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds __setup("ips=", ips_setup);
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds /****************************************************************************/
5431da177e4SLinus Torvalds /*                                                                          */
5441da177e4SLinus Torvalds /* Routine Name: ips_detect                                                 */
5451da177e4SLinus Torvalds /*                                                                          */
5461da177e4SLinus Torvalds /* Routine Description:                                                     */
5471da177e4SLinus Torvalds /*                                                                          */
5481da177e4SLinus Torvalds /*   Detect and initialize the driver                                       */
5491da177e4SLinus Torvalds /*                                                                          */
5501da177e4SLinus Torvalds /* NOTE: this routine is called under the io_request_lock spinlock          */
5511da177e4SLinus Torvalds /*                                                                          */
5521da177e4SLinus Torvalds /****************************************************************************/
5531da177e4SLinus Torvalds static int
554d0be4a7dSChristoph Hellwig ips_detect(struct scsi_host_template * SHT)
5551da177e4SLinus Torvalds {
5561da177e4SLinus Torvalds 	int i;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	METHOD_TRACE("ips_detect", 1);
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds #ifdef MODULE
5611da177e4SLinus Torvalds 	if (ips)
5621da177e4SLinus Torvalds 		ips_setup(ips);
5631da177e4SLinus Torvalds #endif
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	for (i = 0; i < ips_num_controllers; i++) {
5661da177e4SLinus Torvalds 		if (ips_register_scsi(i))
5671da177e4SLinus Torvalds 			ips_free(ips_ha[i]);
5681da177e4SLinus Torvalds 		ips_released_controllers++;
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 	ips_hotplug = 1;
5711da177e4SLinus Torvalds 	return (ips_num_controllers);
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds /****************************************************************************/
5751da177e4SLinus Torvalds /*   configure the function pointers to use the functions that will work    */
5761da177e4SLinus Torvalds /*   with the found version of the adapter                                  */
5771da177e4SLinus Torvalds /****************************************************************************/
5781da177e4SLinus Torvalds static void
5791da177e4SLinus Torvalds ips_setup_funclist(ips_ha_t * ha)
5801da177e4SLinus Torvalds {
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 	/*
5831da177e4SLinus Torvalds 	 * Setup Functions
5841da177e4SLinus Torvalds 	 */
5851da177e4SLinus Torvalds 	if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) {
5861da177e4SLinus Torvalds 		/* morpheus / marco / sebring */
5871da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_morpheus;
5881da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_morpheus;
5891da177e4SLinus Torvalds 		ha->func.issue = ips_issue_i2o_memio;
5901da177e4SLinus Torvalds 		ha->func.init = ips_init_morpheus;
5911da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_morpheus;
5921da177e4SLinus Torvalds 		ha->func.reset = ips_reset_morpheus;
5931da177e4SLinus Torvalds 		ha->func.intr = ips_intr_morpheus;
5941da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_morpheus;
5951da177e4SLinus Torvalds 	} else if (IPS_USE_MEMIO(ha)) {
5961da177e4SLinus Torvalds 		/* copperhead w/MEMIO */
5971da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_copperhead_memio;
5981da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_copperhead_memio;
5991da177e4SLinus Torvalds 		ha->func.init = ips_init_copperhead_memio;
6001da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_copperhead_memio;
6011da177e4SLinus Torvalds 		ha->func.statinit = ips_statinit_memio;
6021da177e4SLinus Torvalds 		ha->func.reset = ips_reset_copperhead_memio;
6031da177e4SLinus Torvalds 		ha->func.intr = ips_intr_copperhead;
6041da177e4SLinus Torvalds 		ha->func.erasebios = ips_erase_bios_memio;
6051da177e4SLinus Torvalds 		ha->func.programbios = ips_program_bios_memio;
6061da177e4SLinus Torvalds 		ha->func.verifybios = ips_verify_bios_memio;
6071da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_copperhead_memio;
6081da177e4SLinus Torvalds 		if (IPS_USE_I2O_DELIVER(ha))
6091da177e4SLinus Torvalds 			ha->func.issue = ips_issue_i2o_memio;
6101da177e4SLinus Torvalds 		else
6111da177e4SLinus Torvalds 			ha->func.issue = ips_issue_copperhead_memio;
6121da177e4SLinus Torvalds 	} else {
6131da177e4SLinus Torvalds 		/* copperhead */
6141da177e4SLinus Torvalds 		ha->func.isintr = ips_isintr_copperhead;
6151da177e4SLinus Torvalds 		ha->func.isinit = ips_isinit_copperhead;
6161da177e4SLinus Torvalds 		ha->func.init = ips_init_copperhead;
6171da177e4SLinus Torvalds 		ha->func.statupd = ips_statupd_copperhead;
6181da177e4SLinus Torvalds 		ha->func.statinit = ips_statinit;
6191da177e4SLinus Torvalds 		ha->func.reset = ips_reset_copperhead;
6201da177e4SLinus Torvalds 		ha->func.intr = ips_intr_copperhead;
6211da177e4SLinus Torvalds 		ha->func.erasebios = ips_erase_bios;
6221da177e4SLinus Torvalds 		ha->func.programbios = ips_program_bios;
6231da177e4SLinus Torvalds 		ha->func.verifybios = ips_verify_bios;
6241da177e4SLinus Torvalds 		ha->func.enableint = ips_enable_int_copperhead;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 		if (IPS_USE_I2O_DELIVER(ha))
6271da177e4SLinus Torvalds 			ha->func.issue = ips_issue_i2o;
6281da177e4SLinus Torvalds 		else
6291da177e4SLinus Torvalds 			ha->func.issue = ips_issue_copperhead;
6301da177e4SLinus Torvalds 	}
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds /****************************************************************************/
6341da177e4SLinus Torvalds /*                                                                          */
6351da177e4SLinus Torvalds /* Routine Name: ips_release                                                */
6361da177e4SLinus Torvalds /*                                                                          */
6371da177e4SLinus Torvalds /* Routine Description:                                                     */
6381da177e4SLinus Torvalds /*                                                                          */
6391da177e4SLinus Torvalds /*   Remove a driver                                                        */
6401da177e4SLinus Torvalds /*                                                                          */
6411da177e4SLinus Torvalds /****************************************************************************/
6421da177e4SLinus Torvalds static int
6431da177e4SLinus Torvalds ips_release(struct Scsi_Host *sh)
6441da177e4SLinus Torvalds {
6451da177e4SLinus Torvalds 	ips_scb_t *scb;
6461da177e4SLinus Torvalds 	ips_ha_t *ha;
6471da177e4SLinus Torvalds 	int i;
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	METHOD_TRACE("ips_release", 1);
6501da177e4SLinus Torvalds 
651a50ee7a7SMatthew Wilcox 	scsi_remove_host(sh);
652a50ee7a7SMatthew Wilcox 
6531da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++) ;
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	if (i == IPS_MAX_ADAPTERS) {
6561da177e4SLinus Torvalds 		printk(KERN_WARNING
6571da177e4SLinus Torvalds 		       "(%s) release, invalid Scsi_Host pointer.\n", ips_name);
6581da177e4SLinus Torvalds 		BUG();
6591da177e4SLinus Torvalds 		return (FALSE);
6601da177e4SLinus Torvalds 	}
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	ha = IPS_HA(sh);
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	if (!ha)
6651da177e4SLinus Torvalds 		return (FALSE);
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds 	/* flush the cache on the controller */
6681da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
6731da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FLUSH;
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 	scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
6761da177e4SLinus Torvalds 	scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
6771da177e4SLinus Torvalds 	scb->cmd.flush_cache.state = IPS_NORM_STATE;
6781da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved = 0;
6791da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved2 = 0;
6801da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved3 = 0;
6811da177e4SLinus Torvalds 	scb->cmd.flush_cache.reserved4 = 0;
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 	IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 	/* send command */
6861da177e4SLinus Torvalds 	if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
6871da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n");
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n");
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	ips_sh[i] = NULL;
6921da177e4SLinus Torvalds 	ips_ha[i] = NULL;
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	/* free extra memory */
6951da177e4SLinus Torvalds 	ips_free(ha);
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	/* free IRQ */
6988a694cc8SJeff Garzik 	free_irq(ha->pcidev->irq, ha);
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	scsi_host_put(sh);
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	ips_released_controllers++;
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	return (FALSE);
7051da177e4SLinus Torvalds }
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds /****************************************************************************/
7081da177e4SLinus Torvalds /*                                                                          */
7091da177e4SLinus Torvalds /* Routine Name: ips_halt                                                   */
7101da177e4SLinus Torvalds /*                                                                          */
7111da177e4SLinus Torvalds /* Routine Description:                                                     */
7121da177e4SLinus Torvalds /*                                                                          */
7131da177e4SLinus Torvalds /*   Perform cleanup when the system reboots                                */
7141da177e4SLinus Torvalds /*                                                                          */
7151da177e4SLinus Torvalds /****************************************************************************/
7161da177e4SLinus Torvalds static int
7171da177e4SLinus Torvalds ips_halt(struct notifier_block *nb, ulong event, void *buf)
7181da177e4SLinus Torvalds {
7191da177e4SLinus Torvalds 	ips_scb_t *scb;
7201da177e4SLinus Torvalds 	ips_ha_t *ha;
7211da177e4SLinus Torvalds 	int i;
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	if ((event != SYS_RESTART) && (event != SYS_HALT) &&
7241da177e4SLinus Torvalds 	    (event != SYS_POWER_OFF))
7251da177e4SLinus Torvalds 		return (NOTIFY_DONE);
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 	for (i = 0; i < ips_next_controller; i++) {
7281da177e4SLinus Torvalds 		ha = (ips_ha_t *) ips_ha[i];
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 		if (!ha)
7311da177e4SLinus Torvalds 			continue;
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 		if (!ha->active)
7341da177e4SLinus Torvalds 			continue;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 		/* flush the cache on the controller */
7371da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
7421da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_FLUSH;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 		scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
7451da177e4SLinus Torvalds 		scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
7461da177e4SLinus Torvalds 		scb->cmd.flush_cache.state = IPS_NORM_STATE;
7471da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved = 0;
7481da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved2 = 0;
7491da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved3 = 0;
7501da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved4 = 0;
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 		/* send command */
7551da177e4SLinus Torvalds 		if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) ==
7561da177e4SLinus Torvalds 		    IPS_FAILURE)
7571da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
7581da177e4SLinus Torvalds 				   "Incomplete Flush.\n");
7591da177e4SLinus Torvalds 		else
7601da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
7611da177e4SLinus Torvalds 				   "Flushing Complete.\n");
7621da177e4SLinus Torvalds 	}
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	return (NOTIFY_OK);
7651da177e4SLinus Torvalds }
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds /****************************************************************************/
7681da177e4SLinus Torvalds /*                                                                          */
7691da177e4SLinus Torvalds /* Routine Name: ips_eh_abort                                               */
7701da177e4SLinus Torvalds /*                                                                          */
7711da177e4SLinus Torvalds /* Routine Description:                                                     */
7721da177e4SLinus Torvalds /*                                                                          */
7731da177e4SLinus Torvalds /*   Abort a command (using the new error code stuff)                       */
7741da177e4SLinus Torvalds /* Note: this routine is called under the io_request_lock                   */
7751da177e4SLinus Torvalds /****************************************************************************/
7761516b55dSHenne int ips_eh_abort(struct scsi_cmnd *SC)
7771da177e4SLinus Torvalds {
7781da177e4SLinus Torvalds 	ips_ha_t *ha;
7791da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
7801da177e4SLinus Torvalds 	int ret;
7818fa728a2SJeff Garzik  	struct Scsi_Host *host;
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	METHOD_TRACE("ips_eh_abort", 1);
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 	if (!SC)
7861da177e4SLinus Torvalds 		return (FAILED);
7871da177e4SLinus Torvalds 
7888fa728a2SJeff Garzik  	host = SC->device->host;
7891da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds 	if (!ha)
7921da177e4SLinus Torvalds 		return (FAILED);
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	if (!ha->active)
7951da177e4SLinus Torvalds 		return (FAILED);
7961da177e4SLinus Torvalds 
797c6a6c81cSAdrian Bunk 	spin_lock(host->host_lock);
7988fa728a2SJeff Garzik  
7991da177e4SLinus Torvalds 	/* See if the command is on the copp queue */
8001da177e4SLinus Torvalds 	item = ha->copp_waitlist.head;
8011da177e4SLinus Torvalds 	while ((item) && (item->scsi_cmd != SC))
8021da177e4SLinus Torvalds 		item = item->next;
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 	if (item) {
8051da177e4SLinus Torvalds 		/* Found it */
8061da177e4SLinus Torvalds 		ips_removeq_copp(&ha->copp_waitlist, item);
8071da177e4SLinus Torvalds 		ret = (SUCCESS);
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds 		/* See if the command is on the wait queue */
8101da177e4SLinus Torvalds 	} else if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
8111da177e4SLinus Torvalds 		/* command not sent yet */
8121da177e4SLinus Torvalds 		ret = (SUCCESS);
8131da177e4SLinus Torvalds 	} else {
8141da177e4SLinus Torvalds 		/* command must have already been sent */
8151da177e4SLinus Torvalds 		ret = (FAILED);
8161da177e4SLinus Torvalds 	}
8178fa728a2SJeff Garzik  
818c6a6c81cSAdrian Bunk 	spin_unlock(host->host_lock);
8191da177e4SLinus Torvalds 	return ret;
8201da177e4SLinus Torvalds }
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds /****************************************************************************/
8231da177e4SLinus Torvalds /*                                                                          */
8241da177e4SLinus Torvalds /* Routine Name: ips_eh_reset                                               */
8251da177e4SLinus Torvalds /*                                                                          */
8261da177e4SLinus Torvalds /* Routine Description:                                                     */
8271da177e4SLinus Torvalds /*                                                                          */
8281da177e4SLinus Torvalds /*   Reset the controller (with new eh error code)                          */
8291da177e4SLinus Torvalds /*                                                                          */
8301da177e4SLinus Torvalds /* NOTE: this routine is called under the io_request_lock spinlock          */
8311da177e4SLinus Torvalds /*                                                                          */
8321da177e4SLinus Torvalds /****************************************************************************/
8331516b55dSHenne static int __ips_eh_reset(struct scsi_cmnd *SC)
8341da177e4SLinus Torvalds {
8351da177e4SLinus Torvalds 	int ret;
8361da177e4SLinus Torvalds 	int i;
8371da177e4SLinus Torvalds 	ips_ha_t *ha;
8381da177e4SLinus Torvalds 	ips_scb_t *scb;
8391da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 	METHOD_TRACE("ips_eh_reset", 1);
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds #ifdef NO_IPS_RESET
8441da177e4SLinus Torvalds 	return (FAILED);
8451da177e4SLinus Torvalds #else
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds 	if (!SC) {
8481da177e4SLinus Torvalds 		DEBUG(1, "Reset called with NULL scsi command");
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 		return (FAILED);
8511da177e4SLinus Torvalds 	}
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds 	if (!ha) {
8561da177e4SLinus Torvalds 		DEBUG(1, "Reset called with NULL ha struct");
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 		return (FAILED);
8591da177e4SLinus Torvalds 	}
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds 	if (!ha->active)
8621da177e4SLinus Torvalds 		return (FAILED);
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	/* See if the command is on the copp queue */
8651da177e4SLinus Torvalds 	item = ha->copp_waitlist.head;
8661da177e4SLinus Torvalds 	while ((item) && (item->scsi_cmd != SC))
8671da177e4SLinus Torvalds 		item = item->next;
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 	if (item) {
8701da177e4SLinus Torvalds 		/* Found it */
8711da177e4SLinus Torvalds 		ips_removeq_copp(&ha->copp_waitlist, item);
8721da177e4SLinus Torvalds 		return (SUCCESS);
8731da177e4SLinus Torvalds 	}
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	/* See if the command is on the wait queue */
8761da177e4SLinus Torvalds 	if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
8771da177e4SLinus Torvalds 		/* command not sent yet */
8781da177e4SLinus Torvalds 		return (SUCCESS);
8791da177e4SLinus Torvalds 	}
8801da177e4SLinus Torvalds 
8811da177e4SLinus Torvalds 	/* An explanation for the casual observer:                              */
8821da177e4SLinus Torvalds 	/* Part of the function of a RAID controller is automatic error         */
8831da177e4SLinus Torvalds 	/* detection and recovery.  As such, the only problem that physically   */
8841da177e4SLinus Torvalds 	/* resetting an adapter will ever fix is when, for some reason,         */
8851da177e4SLinus Torvalds 	/* the driver is not successfully communicating with the adapter.       */
8861da177e4SLinus Torvalds 	/* Therefore, we will attempt to flush this adapter.  If that succeeds, */
8871da177e4SLinus Torvalds 	/* then there's no real purpose in a physical reset. This will complete */
8881da177e4SLinus Torvalds 	/* much faster and avoids any problems that might be caused by a        */
8891da177e4SLinus Torvalds 	/* physical reset ( such as having to fail all the outstanding I/O's ). */
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	if (ha->ioctl_reset == 0) {	/* IF Not an IOCTL Requested Reset */
8921da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
8971da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_FLUSH;
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 		scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
9001da177e4SLinus Torvalds 		scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
9011da177e4SLinus Torvalds 		scb->cmd.flush_cache.state = IPS_NORM_STATE;
9021da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved = 0;
9031da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved2 = 0;
9041da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved3 = 0;
9051da177e4SLinus Torvalds 		scb->cmd.flush_cache.reserved4 = 0;
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 		/* Attempt the flush command */
9081da177e4SLinus Torvalds 		ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL);
9091da177e4SLinus Torvalds 		if (ret == IPS_SUCCESS) {
9101da177e4SLinus Torvalds 			IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9111da177e4SLinus Torvalds 				   "Reset Request - Flushed Cache\n");
9121da177e4SLinus Torvalds 			return (SUCCESS);
9131da177e4SLinus Torvalds 		}
9141da177e4SLinus Torvalds 	}
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	/* Either we can't communicate with the adapter or it's an IOCTL request */
9171da177e4SLinus Torvalds 	/* from a utility.  A physical reset is needed at this point.            */
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds 	ha->ioctl_reset = 0;	/* Reset the IOCTL Requested Reset Flag */
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	/*
9221da177e4SLinus Torvalds 	 * command must have already been sent
9231da177e4SLinus Torvalds 	 * reset the controller
9241da177e4SLinus Torvalds 	 */
9251da177e4SLinus Torvalds 	IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Resetting controller.\n");
9261da177e4SLinus Torvalds 	ret = (*ha->func.reset) (ha);
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	if (!ret) {
9291516b55dSHenne 		struct scsi_cmnd *scsi_cmd;
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 		IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9321da177e4SLinus Torvalds 			   "Controller reset failed - controller now offline.\n");
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 		/* Now fail all of the active commands */
9351da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing active commands",
9361da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 		while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
9391da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
9401da177e4SLinus Torvalds 			scb->scsi_cmd->scsi_done(scb->scsi_cmd);
9411da177e4SLinus Torvalds 			ips_freescb(ha, scb);
9421da177e4SLinus Torvalds 		}
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 		/* Now fail all of the pending commands */
9451da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing pending commands",
9461da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 		while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
9491da177e4SLinus Torvalds 			scsi_cmd->result = DID_ERROR;
9501da177e4SLinus Torvalds 			scsi_cmd->scsi_done(scsi_cmd);
9511da177e4SLinus Torvalds 		}
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 		ha->active = FALSE;
9541da177e4SLinus Torvalds 		return (FAILED);
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds 
9571da177e4SLinus Torvalds 	if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
9581516b55dSHenne 		struct scsi_cmnd *scsi_cmd;
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 		IPS_PRINTK(KERN_NOTICE, ha->pcidev,
9611da177e4SLinus Torvalds 			   "Controller reset failed - controller now offline.\n");
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 		/* Now fail all of the active commands */
9641da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing active commands",
9651da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 		while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
9681da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
9691da177e4SLinus Torvalds 			scb->scsi_cmd->scsi_done(scb->scsi_cmd);
9701da177e4SLinus Torvalds 			ips_freescb(ha, scb);
9711da177e4SLinus Torvalds 		}
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 		/* Now fail all of the pending commands */
9741da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Failing pending commands",
9751da177e4SLinus Torvalds 			  ips_name, ha->host_num);
9761da177e4SLinus Torvalds 
9771da177e4SLinus Torvalds 		while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
9781da177e4SLinus Torvalds 			scsi_cmd->result = DID_ERROR << 16;
9791da177e4SLinus Torvalds 			scsi_cmd->scsi_done(scsi_cmd);
9801da177e4SLinus Torvalds 		}
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 		ha->active = FALSE;
9831da177e4SLinus Torvalds 		return (FAILED);
9841da177e4SLinus Torvalds 	}
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	/* FFDC */
9871da177e4SLinus Torvalds 	if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) {
988f990bee3SArnd Bergmann 		ha->last_ffdc = ktime_get_real_seconds();
9891da177e4SLinus Torvalds 		ha->reset_count++;
9901da177e4SLinus Torvalds 		ips_ffdc_reset(ha, IPS_INTR_IORL);
9911da177e4SLinus Torvalds 	}
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	/* Now fail all of the active commands */
9941da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num);
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds 	while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
9971c9fbafcSMartin K. Petersen 		scb->scsi_cmd->result = DID_RESET << 16;
9981da177e4SLinus Torvalds 		scb->scsi_cmd->scsi_done(scb->scsi_cmd);
9991da177e4SLinus Torvalds 		ips_freescb(ha, scb);
10001da177e4SLinus Torvalds 	}
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds 	/* Reset DCDB active command bits */
10031da177e4SLinus Torvalds 	for (i = 1; i < ha->nbus; i++)
10041da177e4SLinus Torvalds 		ha->dcdb_active[i - 1] = 0;
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	/* Reset the number of active IOCTLs */
10071da177e4SLinus Torvalds 	ha->num_ioctl = 0;
10081da177e4SLinus Torvalds 
10091da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_IORL);
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 	return (SUCCESS);
10121da177e4SLinus Torvalds #endif				/* NO_IPS_RESET */
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds }
10151da177e4SLinus Torvalds 
10161516b55dSHenne static int ips_eh_reset(struct scsi_cmnd *SC)
1017df0ae249SJeff Garzik  {
1018df0ae249SJeff Garzik  	int rc;
1019df0ae249SJeff Garzik  
1020df0ae249SJeff Garzik  	spin_lock_irq(SC->device->host->host_lock);
1021df0ae249SJeff Garzik  	rc = __ips_eh_reset(SC);
1022df0ae249SJeff Garzik  	spin_unlock_irq(SC->device->host->host_lock);
1023df0ae249SJeff Garzik  
1024df0ae249SJeff Garzik  	return rc;
1025df0ae249SJeff Garzik  }
1026df0ae249SJeff Garzik  
10271da177e4SLinus Torvalds /****************************************************************************/
10281da177e4SLinus Torvalds /*                                                                          */
10291da177e4SLinus Torvalds /* Routine Name: ips_queue                                                  */
10301da177e4SLinus Torvalds /*                                                                          */
10311da177e4SLinus Torvalds /* Routine Description:                                                     */
10321da177e4SLinus Torvalds /*                                                                          */
10331da177e4SLinus Torvalds /*   Send a command to the controller                                       */
10341da177e4SLinus Torvalds /*                                                                          */
10351da177e4SLinus Torvalds /* NOTE:                                                                    */
10361da177e4SLinus Torvalds /*    Linux obtains io_request_lock before calling this function            */
10371da177e4SLinus Torvalds /*                                                                          */
10381da177e4SLinus Torvalds /****************************************************************************/
1039f281233dSJeff Garzik static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *))
10401da177e4SLinus Torvalds {
10411da177e4SLinus Torvalds 	ips_ha_t *ha;
10421da177e4SLinus Torvalds 	ips_passthru_t *pt;
10431da177e4SLinus Torvalds 
10441da177e4SLinus Torvalds 	METHOD_TRACE("ips_queue", 1);
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds 	ha = (ips_ha_t *) SC->device->host->hostdata;
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	if (!ha)
10491da177e4SLinus Torvalds 		return (1);
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	if (!ha->active)
10521da177e4SLinus Torvalds 		return (DID_ERROR);
10531da177e4SLinus Torvalds 
10541da177e4SLinus Torvalds 	if (ips_is_passthru(SC)) {
10551da177e4SLinus Torvalds 		if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
10561da177e4SLinus Torvalds 			SC->result = DID_BUS_BUSY << 16;
10571da177e4SLinus Torvalds 			done(SC);
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 			return (0);
10601da177e4SLinus Torvalds 		}
10611da177e4SLinus Torvalds 	} else if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
10621da177e4SLinus Torvalds 		SC->result = DID_BUS_BUSY << 16;
10631da177e4SLinus Torvalds 		done(SC);
10641da177e4SLinus Torvalds 
10651da177e4SLinus Torvalds 		return (0);
10661da177e4SLinus Torvalds 	}
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	SC->scsi_done = done;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
10711da177e4SLinus Torvalds 		  ips_name,
10721da177e4SLinus Torvalds 		  ha->host_num,
10731da177e4SLinus Torvalds 		  SC->cmnd[0],
10741da177e4SLinus Torvalds 		  SC->device->channel, SC->device->id, SC->device->lun);
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	/* Check for command to initiator IDs */
1077422c0d61SJeff Garzik 	if ((scmd_channel(SC) > 0)
1078422c0d61SJeff Garzik 	    && (scmd_id(SC) == ha->ha_id[scmd_channel(SC)])) {
10791da177e4SLinus Torvalds 		SC->result = DID_NO_CONNECT << 16;
10801da177e4SLinus Torvalds 		done(SC);
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds 		return (0);
10831da177e4SLinus Torvalds 	}
10841da177e4SLinus Torvalds 
10851da177e4SLinus Torvalds 	if (ips_is_passthru(SC)) {
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 		ips_copp_wait_item_t *scratch;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 		/* A Reset IOCTL is only sent by the boot CD in extreme cases.           */
10901da177e4SLinus Torvalds 		/* There can never be any system activity ( network or disk ), but check */
10911da177e4SLinus Torvalds 		/* anyway just as a good practice.                                       */
10922f4cf91cSFUJITA Tomonori 		pt = (ips_passthru_t *) scsi_sglist(SC);
10931da177e4SLinus Torvalds 		if ((pt->CoppCP.cmd.reset.op_code == IPS_CMD_RESET_CHANNEL) &&
10941da177e4SLinus Torvalds 		    (pt->CoppCP.cmd.reset.adapter_flag == 1)) {
10951da177e4SLinus Torvalds 			if (ha->scb_activelist.count != 0) {
10961da177e4SLinus Torvalds 				SC->result = DID_BUS_BUSY << 16;
10971da177e4SLinus Torvalds 				done(SC);
10981da177e4SLinus Torvalds 				return (0);
10991da177e4SLinus Torvalds 			}
11001da177e4SLinus Torvalds 			ha->ioctl_reset = 1;	/* This reset request is from an IOCTL */
1101ba3af0afSMike Christie 			__ips_eh_reset(SC);
11021da177e4SLinus Torvalds 			SC->result = DID_OK << 16;
11031da177e4SLinus Torvalds 			SC->scsi_done(SC);
11041da177e4SLinus Torvalds 			return (0);
11051da177e4SLinus Torvalds 		}
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 		/* allocate space for the scribble */
11081da177e4SLinus Torvalds 		scratch = kmalloc(sizeof (ips_copp_wait_item_t), GFP_ATOMIC);
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds 		if (!scratch) {
11111da177e4SLinus Torvalds 			SC->result = DID_ERROR << 16;
11121da177e4SLinus Torvalds 			done(SC);
11131da177e4SLinus Torvalds 
11141da177e4SLinus Torvalds 			return (0);
11151da177e4SLinus Torvalds 		}
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds 		scratch->scsi_cmd = SC;
11181da177e4SLinus Torvalds 		scratch->next = NULL;
11191da177e4SLinus Torvalds 
11201da177e4SLinus Torvalds 		ips_putq_copp_tail(&ha->copp_waitlist, scratch);
11211da177e4SLinus Torvalds 	} else {
11221da177e4SLinus Torvalds 		ips_putq_wait_tail(&ha->scb_waitlist, SC);
11231da177e4SLinus Torvalds 	}
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_IORL);
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds 	return (0);
11281da177e4SLinus Torvalds }
11291da177e4SLinus Torvalds 
1130f281233dSJeff Garzik static DEF_SCSI_QCMD(ips_queue)
1131f281233dSJeff Garzik 
11321da177e4SLinus Torvalds /****************************************************************************/
11331da177e4SLinus Torvalds /*                                                                          */
11341da177e4SLinus Torvalds /* Routine Name: ips_biosparam                                              */
11351da177e4SLinus Torvalds /*                                                                          */
11361da177e4SLinus Torvalds /* Routine Description:                                                     */
11371da177e4SLinus Torvalds /*                                                                          */
11381da177e4SLinus Torvalds /*   Set bios geometry for the controller                                   */
11391da177e4SLinus Torvalds /*                                                                          */
11401da177e4SLinus Torvalds /****************************************************************************/
1141c6a6c81cSAdrian Bunk static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
11421da177e4SLinus Torvalds 			 sector_t capacity, int geom[])
11431da177e4SLinus Torvalds {
11441da177e4SLinus Torvalds 	ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata;
11451da177e4SLinus Torvalds 	int heads;
11461da177e4SLinus Torvalds 	int sectors;
11471da177e4SLinus Torvalds 	int cylinders;
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds 	METHOD_TRACE("ips_biosparam", 1);
11501da177e4SLinus Torvalds 
11511da177e4SLinus Torvalds 	if (!ha)
11521da177e4SLinus Torvalds 		/* ?!?! host adater info invalid */
11531da177e4SLinus Torvalds 		return (0);
11541da177e4SLinus Torvalds 
11551da177e4SLinus Torvalds 	if (!ha->active)
11561da177e4SLinus Torvalds 		return (0);
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	if (!ips_read_adapter_status(ha, IPS_INTR_ON))
11591da177e4SLinus Torvalds 		/* ?!?! Enquiry command failed */
11601da177e4SLinus Torvalds 		return (0);
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	if ((capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) {
11631da177e4SLinus Torvalds 		heads = IPS_NORM_HEADS;
11641da177e4SLinus Torvalds 		sectors = IPS_NORM_SECTORS;
11651da177e4SLinus Torvalds 	} else {
11661da177e4SLinus Torvalds 		heads = IPS_COMP_HEADS;
11671da177e4SLinus Torvalds 		sectors = IPS_COMP_SECTORS;
11681da177e4SLinus Torvalds 	}
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	cylinders = (unsigned long) capacity / (heads * sectors);
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds 	DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
11731da177e4SLinus Torvalds 		  heads, sectors, cylinders);
11741da177e4SLinus Torvalds 
11751da177e4SLinus Torvalds 	geom[0] = heads;
11761da177e4SLinus Torvalds 	geom[1] = sectors;
11771da177e4SLinus Torvalds 	geom[2] = cylinders;
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds 	return (0);
11801da177e4SLinus Torvalds }
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds /****************************************************************************/
11831da177e4SLinus Torvalds /*                                                                          */
11841da177e4SLinus Torvalds /* Routine Name: ips_slave_configure                                        */
11851da177e4SLinus Torvalds /*                                                                          */
11861da177e4SLinus Torvalds /* Routine Description:                                                     */
11871da177e4SLinus Torvalds /*                                                                          */
11881da177e4SLinus Torvalds /*   Set queue depths on devices once scan is complete                      */
11891da177e4SLinus Torvalds /*                                                                          */
11901da177e4SLinus Torvalds /****************************************************************************/
11911da177e4SLinus Torvalds static int
1192f64a181dSChristoph Hellwig ips_slave_configure(struct scsi_device * SDptr)
11931da177e4SLinus Torvalds {
11941da177e4SLinus Torvalds 	ips_ha_t *ha;
11951da177e4SLinus Torvalds 	int min;
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds 	ha = IPS_HA(SDptr->host);
11981da177e4SLinus Torvalds 	if (SDptr->tagged_supported && SDptr->type == TYPE_DISK) {
11991da177e4SLinus Torvalds 		min = ha->max_cmds / 2;
12001da177e4SLinus Torvalds 		if (ha->enq->ucLogDriveCount <= 2)
12011da177e4SLinus Torvalds 			min = ha->max_cmds - 1;
1202db5ed4dfSChristoph Hellwig 		scsi_change_queue_depth(SDptr, min);
12031da177e4SLinus Torvalds 	}
1204560c26c8SJack Hammer 
1205560c26c8SJack Hammer 	SDptr->skip_ms_page_8 = 1;
1206560c26c8SJack Hammer 	SDptr->skip_ms_page_3f = 1;
12071da177e4SLinus Torvalds 	return 0;
12081da177e4SLinus Torvalds }
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds /****************************************************************************/
12111da177e4SLinus Torvalds /*                                                                          */
12121da177e4SLinus Torvalds /* Routine Name: do_ipsintr                                                 */
12131da177e4SLinus Torvalds /*                                                                          */
12141da177e4SLinus Torvalds /* Routine Description:                                                     */
12151da177e4SLinus Torvalds /*                                                                          */
12161da177e4SLinus Torvalds /*   Wrapper for the interrupt handler                                      */
12171da177e4SLinus Torvalds /*                                                                          */
12181da177e4SLinus Torvalds /****************************************************************************/
12191da177e4SLinus Torvalds static irqreturn_t
12207d12e780SDavid Howells do_ipsintr(int irq, void *dev_id)
12211da177e4SLinus Torvalds {
12221da177e4SLinus Torvalds 	ips_ha_t *ha;
12231da177e4SLinus Torvalds 	struct Scsi_Host *host;
12241da177e4SLinus Torvalds 	int irqstatus;
12251da177e4SLinus Torvalds 
12261da177e4SLinus Torvalds 	METHOD_TRACE("do_ipsintr", 2);
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds 	ha = (ips_ha_t *) dev_id;
12291da177e4SLinus Torvalds 	if (!ha)
12301da177e4SLinus Torvalds 		return IRQ_NONE;
12311da177e4SLinus Torvalds 	host = ips_sh[ha->host_num];
12321da177e4SLinus Torvalds 	/* interrupt during initialization */
12331da177e4SLinus Torvalds 	if (!host) {
12341da177e4SLinus Torvalds 		(*ha->func.intr) (ha);
12351da177e4SLinus Torvalds 		return IRQ_HANDLED;
12361da177e4SLinus Torvalds 	}
12371da177e4SLinus Torvalds 
1238c6a6c81cSAdrian Bunk 	spin_lock(host->host_lock);
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds 	if (!ha->active) {
1241c6a6c81cSAdrian Bunk 		spin_unlock(host->host_lock);
12421da177e4SLinus Torvalds 		return IRQ_HANDLED;
12431da177e4SLinus Torvalds 	}
12441da177e4SLinus Torvalds 
12451da177e4SLinus Torvalds 	irqstatus = (*ha->func.intr) (ha);
12461da177e4SLinus Torvalds 
1247c6a6c81cSAdrian Bunk 	spin_unlock(host->host_lock);
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 	/* start the next command */
12501da177e4SLinus Torvalds 	ips_next(ha, IPS_INTR_ON);
12511da177e4SLinus Torvalds 	return IRQ_RETVAL(irqstatus);
12521da177e4SLinus Torvalds }
12531da177e4SLinus Torvalds 
12541da177e4SLinus Torvalds /****************************************************************************/
12551da177e4SLinus Torvalds /*                                                                          */
12561da177e4SLinus Torvalds /* Routine Name: ips_intr_copperhead                                        */
12571da177e4SLinus Torvalds /*                                                                          */
12581da177e4SLinus Torvalds /* Routine Description:                                                     */
12591da177e4SLinus Torvalds /*                                                                          */
12601da177e4SLinus Torvalds /*   Polling interrupt handler                                              */
12611da177e4SLinus Torvalds /*                                                                          */
12621da177e4SLinus Torvalds /*   ASSUMES interrupts are disabled                                        */
12631da177e4SLinus Torvalds /*                                                                          */
12641da177e4SLinus Torvalds /****************************************************************************/
12651da177e4SLinus Torvalds int
12661da177e4SLinus Torvalds ips_intr_copperhead(ips_ha_t * ha)
12671da177e4SLinus Torvalds {
12681da177e4SLinus Torvalds 	ips_stat_t *sp;
12691da177e4SLinus Torvalds 	ips_scb_t *scb;
12701da177e4SLinus Torvalds 	IPS_STATUS cstatus;
12711da177e4SLinus Torvalds 	int intrstatus;
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds 	METHOD_TRACE("ips_intr", 2);
12741da177e4SLinus Torvalds 
12751da177e4SLinus Torvalds 	if (!ha)
12761da177e4SLinus Torvalds 		return 0;
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	if (!ha->active)
12791da177e4SLinus Torvalds 		return 0;
12801da177e4SLinus Torvalds 
12811da177e4SLinus Torvalds 	intrstatus = (*ha->func.isintr) (ha);
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds 	if (!intrstatus) {
12841da177e4SLinus Torvalds 		/*
12851da177e4SLinus Torvalds 		 * Unexpected/Shared interrupt
12861da177e4SLinus Torvalds 		 */
12871da177e4SLinus Torvalds 
12881da177e4SLinus Torvalds 		return 0;
12891da177e4SLinus Torvalds 	}
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds 	while (TRUE) {
12921da177e4SLinus Torvalds 		sp = &ha->sp;
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 		intrstatus = (*ha->func.isintr) (ha);
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 		if (!intrstatus)
12971da177e4SLinus Torvalds 			break;
12981da177e4SLinus Torvalds 		else
12991da177e4SLinus Torvalds 			cstatus.value = (*ha->func.statupd) (ha);
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 		if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
1302b1c11812SJoe Perches 			/* Spurious Interrupt ? */
13031da177e4SLinus Torvalds 			continue;
13041da177e4SLinus Torvalds 		}
13051da177e4SLinus Torvalds 
13061da177e4SLinus Torvalds 		ips_chkstatus(ha, &cstatus);
13071da177e4SLinus Torvalds 		scb = (ips_scb_t *) sp->scb_addr;
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds 		/*
13101da177e4SLinus Torvalds 		 * use the callback function to finish things up
13111da177e4SLinus Torvalds 		 * NOTE: interrupts are OFF for this
13121da177e4SLinus Torvalds 		 */
13131da177e4SLinus Torvalds 		(*scb->callback) (ha, scb);
13141da177e4SLinus Torvalds 	}			/* end while */
13151da177e4SLinus Torvalds 	return 1;
13161da177e4SLinus Torvalds }
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds /****************************************************************************/
13191da177e4SLinus Torvalds /*                                                                          */
13201da177e4SLinus Torvalds /* Routine Name: ips_intr_morpheus                                          */
13211da177e4SLinus Torvalds /*                                                                          */
13221da177e4SLinus Torvalds /* Routine Description:                                                     */
13231da177e4SLinus Torvalds /*                                                                          */
13241da177e4SLinus Torvalds /*   Polling interrupt handler                                              */
13251da177e4SLinus Torvalds /*                                                                          */
13261da177e4SLinus Torvalds /*   ASSUMES interrupts are disabled                                        */
13271da177e4SLinus Torvalds /*                                                                          */
13281da177e4SLinus Torvalds /****************************************************************************/
13291da177e4SLinus Torvalds int
13301da177e4SLinus Torvalds ips_intr_morpheus(ips_ha_t * ha)
13311da177e4SLinus Torvalds {
13321da177e4SLinus Torvalds 	ips_stat_t *sp;
13331da177e4SLinus Torvalds 	ips_scb_t *scb;
13341da177e4SLinus Torvalds 	IPS_STATUS cstatus;
13351da177e4SLinus Torvalds 	int intrstatus;
13361da177e4SLinus Torvalds 
13371da177e4SLinus Torvalds 	METHOD_TRACE("ips_intr_morpheus", 2);
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 	if (!ha)
13401da177e4SLinus Torvalds 		return 0;
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	if (!ha->active)
13431da177e4SLinus Torvalds 		return 0;
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 	intrstatus = (*ha->func.isintr) (ha);
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 	if (!intrstatus) {
13481da177e4SLinus Torvalds 		/*
13491da177e4SLinus Torvalds 		 * Unexpected/Shared interrupt
13501da177e4SLinus Torvalds 		 */
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 		return 0;
13531da177e4SLinus Torvalds 	}
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	while (TRUE) {
13561da177e4SLinus Torvalds 		sp = &ha->sp;
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 		intrstatus = (*ha->func.isintr) (ha);
13591da177e4SLinus Torvalds 
13601da177e4SLinus Torvalds 		if (!intrstatus)
13611da177e4SLinus Torvalds 			break;
13621da177e4SLinus Torvalds 		else
13631da177e4SLinus Torvalds 			cstatus.value = (*ha->func.statupd) (ha);
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (cstatus.value == 0xffffffff)
13661da177e4SLinus Torvalds 			/* No more to process */
13671da177e4SLinus Torvalds 			break;
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds 		if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
13701da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
13711da177e4SLinus Torvalds 				   "Spurious interrupt; no ccb.\n");
13721da177e4SLinus Torvalds 
13731da177e4SLinus Torvalds 			continue;
13741da177e4SLinus Torvalds 		}
13751da177e4SLinus Torvalds 
13761da177e4SLinus Torvalds 		ips_chkstatus(ha, &cstatus);
13771da177e4SLinus Torvalds 		scb = (ips_scb_t *) sp->scb_addr;
13781da177e4SLinus Torvalds 
13791da177e4SLinus Torvalds 		/*
13801da177e4SLinus Torvalds 		 * use the callback function to finish things up
13811da177e4SLinus Torvalds 		 * NOTE: interrupts are OFF for this
13821da177e4SLinus Torvalds 		 */
13831da177e4SLinus Torvalds 		(*scb->callback) (ha, scb);
13841da177e4SLinus Torvalds 	}			/* end while */
13851da177e4SLinus Torvalds 	return 1;
13861da177e4SLinus Torvalds }
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds /****************************************************************************/
13891da177e4SLinus Torvalds /*                                                                          */
13901da177e4SLinus Torvalds /* Routine Name: ips_info                                                   */
13911da177e4SLinus Torvalds /*                                                                          */
13921da177e4SLinus Torvalds /* Routine Description:                                                     */
13931da177e4SLinus Torvalds /*                                                                          */
13941da177e4SLinus Torvalds /*   Return info about the driver                                           */
13951da177e4SLinus Torvalds /*                                                                          */
13961da177e4SLinus Torvalds /****************************************************************************/
13971da177e4SLinus Torvalds static const char *
13981da177e4SLinus Torvalds ips_info(struct Scsi_Host *SH)
13991da177e4SLinus Torvalds {
14001da177e4SLinus Torvalds 	static char buffer[256];
14011da177e4SLinus Torvalds 	char *bp;
14021da177e4SLinus Torvalds 	ips_ha_t *ha;
14031da177e4SLinus Torvalds 
14041da177e4SLinus Torvalds 	METHOD_TRACE("ips_info", 1);
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds 	ha = IPS_HA(SH);
14071da177e4SLinus Torvalds 
14081da177e4SLinus Torvalds 	if (!ha)
14091da177e4SLinus Torvalds 		return (NULL);
14101da177e4SLinus Torvalds 
14111da177e4SLinus Torvalds 	bp = &buffer[0];
14121da177e4SLinus Torvalds 	memset(bp, 0, sizeof (buffer));
14131da177e4SLinus Torvalds 
14141da177e4SLinus Torvalds 	sprintf(bp, "%s%s%s Build %d", "IBM PCI ServeRAID ",
14151da177e4SLinus Torvalds 		IPS_VERSION_HIGH, IPS_VERSION_LOW, IPS_BUILD_IDENT);
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 	if (ha->ad_type > 0 && ha->ad_type <= MAX_ADAPTER_NAME) {
14181da177e4SLinus Torvalds 		strcat(bp, " <");
14191da177e4SLinus Torvalds 		strcat(bp, ips_adapter_name[ha->ad_type - 1]);
14201da177e4SLinus Torvalds 		strcat(bp, ">");
14211da177e4SLinus Torvalds 	}
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 	return (bp);
14241da177e4SLinus Torvalds }
14251da177e4SLinus Torvalds 
14261da177e4SLinus Torvalds static int
1427aacce706SAl Viro ips_write_info(struct Scsi_Host *host, char *buffer, int length)
14281da177e4SLinus Torvalds {
14291da177e4SLinus Torvalds 	int i;
14301da177e4SLinus Torvalds 	ips_ha_t *ha = NULL;
14311da177e4SLinus Torvalds 
14321da177e4SLinus Torvalds 	/* Find our host structure */
14331da177e4SLinus Torvalds 	for (i = 0; i < ips_next_controller; i++) {
14341da177e4SLinus Torvalds 		if (ips_sh[i]) {
14351da177e4SLinus Torvalds 			if (ips_sh[i] == host) {
14361da177e4SLinus Torvalds 				ha = (ips_ha_t *) ips_sh[i]->hostdata;
14371da177e4SLinus Torvalds 				break;
14381da177e4SLinus Torvalds 			}
14391da177e4SLinus Torvalds 		}
14401da177e4SLinus Torvalds 	}
14411da177e4SLinus Torvalds 
14421da177e4SLinus Torvalds 	if (!ha)
14431da177e4SLinus Torvalds 		return (-EINVAL);
14441da177e4SLinus Torvalds 
1445aacce706SAl Viro 	return 0;
14461da177e4SLinus Torvalds }
1447aacce706SAl Viro 
1448aacce706SAl Viro static int
1449aacce706SAl Viro ips_show_info(struct seq_file *m, struct Scsi_Host *host)
1450aacce706SAl Viro {
1451aacce706SAl Viro 	int i;
1452aacce706SAl Viro 	ips_ha_t *ha = NULL;
1453aacce706SAl Viro 
1454aacce706SAl Viro 	/* Find our host structure */
1455aacce706SAl Viro 	for (i = 0; i < ips_next_controller; i++) {
1456aacce706SAl Viro 		if (ips_sh[i]) {
1457aacce706SAl Viro 			if (ips_sh[i] == host) {
1458aacce706SAl Viro 				ha = (ips_ha_t *) ips_sh[i]->hostdata;
1459aacce706SAl Viro 				break;
1460aacce706SAl Viro 			}
1461aacce706SAl Viro 		}
1462aacce706SAl Viro 	}
1463aacce706SAl Viro 
1464aacce706SAl Viro 	if (!ha)
1465aacce706SAl Viro 		return (-EINVAL);
1466aacce706SAl Viro 
1467aacce706SAl Viro 	return ips_host_info(ha, m);
14681da177e4SLinus Torvalds }
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds /*--------------------------------------------------------------------------*/
14711da177e4SLinus Torvalds /* Helper Functions                                                         */
14721da177e4SLinus Torvalds /*--------------------------------------------------------------------------*/
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds /****************************************************************************/
14751da177e4SLinus Torvalds /*                                                                          */
14761da177e4SLinus Torvalds /* Routine Name: ips_is_passthru                                            */
14771da177e4SLinus Torvalds /*                                                                          */
14781da177e4SLinus Torvalds /* Routine Description:                                                     */
14791da177e4SLinus Torvalds /*                                                                          */
14801da177e4SLinus Torvalds /*   Determine if the specified SCSI command is really a passthru command   */
14811da177e4SLinus Torvalds /*                                                                          */
14821da177e4SLinus Torvalds /****************************************************************************/
14831516b55dSHenne static int ips_is_passthru(struct scsi_cmnd *SC)
14841da177e4SLinus Torvalds {
1485a3632fa3SJack Hammer 	unsigned long flags;
1486a3632fa3SJack Hammer 
14871da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_passthru", 1);
14881da177e4SLinus Torvalds 
14891da177e4SLinus Torvalds 	if (!SC)
14901da177e4SLinus Torvalds 		return (0);
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 	if ((SC->cmnd[0] == IPS_IOCTL_COMMAND) &&
14931da177e4SLinus Torvalds 	    (SC->device->channel == 0) &&
14941da177e4SLinus Torvalds 	    (SC->device->id == IPS_ADAPTER_ID) &&
14952f4cf91cSFUJITA Tomonori 	    (SC->device->lun == 0) && scsi_sglist(SC)) {
14962f4cf91cSFUJITA Tomonori                 struct scatterlist *sg = scsi_sglist(SC);
1497a3632fa3SJack Hammer                 char  *buffer;
1498a3632fa3SJack Hammer 
1499a3632fa3SJack Hammer                 /* kmap_atomic() ensures addressability of the user buffer.*/
1500a3632fa3SJack Hammer                 /* local_irq_save() protects the KM_IRQ0 address slot.     */
1501a3632fa3SJack Hammer                 local_irq_save(flags);
150277dfce07SCong Wang                 buffer = kmap_atomic(sg_page(sg)) + sg->offset;
15031da177e4SLinus Torvalds                 if (buffer && buffer[0] == 'C' && buffer[1] == 'O' &&
1504a3632fa3SJack Hammer                     buffer[2] == 'P' && buffer[3] == 'P') {
150577dfce07SCong Wang                         kunmap_atomic(buffer - sg->offset);
1506a3632fa3SJack Hammer                         local_irq_restore(flags);
15071da177e4SLinus Torvalds                         return 1;
15081da177e4SLinus Torvalds                 }
150977dfce07SCong Wang                 kunmap_atomic(buffer - sg->offset);
1510a3632fa3SJack Hammer                 local_irq_restore(flags);
1511a3632fa3SJack Hammer 	}
15121da177e4SLinus Torvalds 	return 0;
15131da177e4SLinus Torvalds }
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds /****************************************************************************/
15161da177e4SLinus Torvalds /*                                                                          */
15171da177e4SLinus Torvalds /* Routine Name: ips_alloc_passthru_buffer                                  */
15181da177e4SLinus Torvalds /*                                                                          */
15191da177e4SLinus Torvalds /* Routine Description:                                                     */
15201da177e4SLinus Torvalds /*   allocate a buffer large enough for the ioctl data if the ioctl buffer  */
15211da177e4SLinus Torvalds /*   is too small or doesn't exist                                          */
15221da177e4SLinus Torvalds /****************************************************************************/
15231da177e4SLinus Torvalds static int
15241da177e4SLinus Torvalds ips_alloc_passthru_buffer(ips_ha_t * ha, int length)
15251da177e4SLinus Torvalds {
15261da177e4SLinus Torvalds 	void *bigger_buf;
15271da177e4SLinus Torvalds 	dma_addr_t dma_busaddr;
15281da177e4SLinus Torvalds 
15291da177e4SLinus Torvalds 	if (ha->ioctl_data && length <= ha->ioctl_len)
15301da177e4SLinus Torvalds 		return 0;
15311da177e4SLinus Torvalds 	/* there is no buffer or it's not big enough, allocate a new one */
153232e76961SChristoph Hellwig 	bigger_buf = dma_alloc_coherent(&ha->pcidev->dev, length, &dma_busaddr,
153332e76961SChristoph Hellwig 			GFP_KERNEL);
15341da177e4SLinus Torvalds 	if (bigger_buf) {
15351da177e4SLinus Torvalds 		/* free the old memory */
153632e76961SChristoph Hellwig 		dma_free_coherent(&ha->pcidev->dev, ha->ioctl_len,
153732e76961SChristoph Hellwig 				  ha->ioctl_data, ha->ioctl_busaddr);
15381da177e4SLinus Torvalds 		/* use the new memory */
15391da177e4SLinus Torvalds 		ha->ioctl_data = (char *) bigger_buf;
15401da177e4SLinus Torvalds 		ha->ioctl_len = length;
15411da177e4SLinus Torvalds 		ha->ioctl_busaddr = dma_busaddr;
15421da177e4SLinus Torvalds 	} else {
15431da177e4SLinus Torvalds 		return -1;
15441da177e4SLinus Torvalds 	}
15451da177e4SLinus Torvalds 	return 0;
15461da177e4SLinus Torvalds }
15471da177e4SLinus Torvalds 
15481da177e4SLinus Torvalds /****************************************************************************/
15491da177e4SLinus Torvalds /*                                                                          */
15501da177e4SLinus Torvalds /* Routine Name: ips_make_passthru                                          */
15511da177e4SLinus Torvalds /*                                                                          */
15521da177e4SLinus Torvalds /* Routine Description:                                                     */
15531da177e4SLinus Torvalds /*                                                                          */
15541da177e4SLinus Torvalds /*   Make a passthru command out of the info in the Scsi block              */
15551da177e4SLinus Torvalds /*                                                                          */
15561da177e4SLinus Torvalds /****************************************************************************/
15571da177e4SLinus Torvalds static int
15581516b55dSHenne ips_make_passthru(ips_ha_t *ha, struct scsi_cmnd *SC, ips_scb_t *scb, int intr)
15591da177e4SLinus Torvalds {
15601da177e4SLinus Torvalds 	ips_passthru_t *pt;
15611da177e4SLinus Torvalds 	int length = 0;
15622f4cf91cSFUJITA Tomonori 	int i, ret;
15632f4cf91cSFUJITA Tomonori         struct scatterlist *sg = scsi_sglist(SC);
15641da177e4SLinus Torvalds 
15651da177e4SLinus Torvalds 	METHOD_TRACE("ips_make_passthru", 1);
15661da177e4SLinus Torvalds 
15672f4cf91cSFUJITA Tomonori         scsi_for_each_sg(SC, sg, scsi_sg_count(SC), i)
15682b28a472SFUJITA Tomonori 		length += sg->length;
15692f4cf91cSFUJITA Tomonori 
15701da177e4SLinus Torvalds 	if (length < sizeof (ips_passthru_t)) {
15711da177e4SLinus Torvalds 		/* wrong size */
15721da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
15731da177e4SLinus Torvalds 			  ips_name, ha->host_num);
15741da177e4SLinus Torvalds 		return (IPS_FAILURE);
15751da177e4SLinus Torvalds 	}
15761da177e4SLinus Torvalds 	if (ips_alloc_passthru_buffer(ha, length)) {
15771da177e4SLinus Torvalds 		/* allocation failure!  If ha->ioctl_data exists, use it to return
15781da177e4SLinus Torvalds 		   some error codes.  Return a failed command to the scsi layer. */
15791da177e4SLinus Torvalds 		if (ha->ioctl_data) {
15801da177e4SLinus Torvalds 			pt = (ips_passthru_t *) ha->ioctl_data;
15811da177e4SLinus Torvalds 			ips_scmd_buf_read(SC, pt, sizeof (ips_passthru_t));
15821da177e4SLinus Torvalds 			pt->BasicStatus = 0x0B;
15831da177e4SLinus Torvalds 			pt->ExtendedStatus = 0x00;
15841da177e4SLinus Torvalds 			ips_scmd_buf_write(SC, pt, sizeof (ips_passthru_t));
15851da177e4SLinus Torvalds 		}
15861da177e4SLinus Torvalds 		return IPS_FAILURE;
15871da177e4SLinus Torvalds 	}
15881da177e4SLinus Torvalds 	ha->ioctl_datasize = length;
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds 	ips_scmd_buf_read(SC, ha->ioctl_data, ha->ioctl_datasize);
15911da177e4SLinus Torvalds 	pt = (ips_passthru_t *) ha->ioctl_data;
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 	/*
15941da177e4SLinus Torvalds 	 * Some notes about the passthru interface used
15951da177e4SLinus Torvalds 	 *
15961da177e4SLinus Torvalds 	 * IF the scsi op_code == 0x0d then we assume
15971da177e4SLinus Torvalds 	 * that the data came along with/goes with the
15981da177e4SLinus Torvalds 	 * packet we received from the sg driver. In this
15991da177e4SLinus Torvalds 	 * case the CmdBSize field of the pt structure is
16001da177e4SLinus Torvalds 	 * used for the size of the buffer.
16011da177e4SLinus Torvalds 	 */
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	switch (pt->CoppCmd) {
16041da177e4SLinus Torvalds 	case IPS_NUMCTRLS:
16051da177e4SLinus Torvalds 		memcpy(ha->ioctl_data + sizeof (ips_passthru_t),
16061da177e4SLinus Torvalds 		       &ips_num_controllers, sizeof (int));
16071da177e4SLinus Torvalds 		ips_scmd_buf_write(SC, ha->ioctl_data,
16081da177e4SLinus Torvalds 				   sizeof (ips_passthru_t) + sizeof (int));
16091da177e4SLinus Torvalds 		SC->result = DID_OK << 16;
16101da177e4SLinus Torvalds 
16111da177e4SLinus Torvalds 		return (IPS_SUCCESS_IMM);
16121da177e4SLinus Torvalds 
16131da177e4SLinus Torvalds 	case IPS_COPPUSRCMD:
16141da177e4SLinus Torvalds 	case IPS_COPPIOCCMD:
16151da177e4SLinus Torvalds 		if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
16161da177e4SLinus Torvalds 			if (length < (sizeof (ips_passthru_t) + pt->CmdBSize)) {
16171da177e4SLinus Torvalds 				/* wrong size */
16181da177e4SLinus Torvalds 				DEBUG_VAR(1,
16191da177e4SLinus Torvalds 					  "(%s%d) Passthru structure wrong size",
16201da177e4SLinus Torvalds 					  ips_name, ha->host_num);
16211da177e4SLinus Torvalds 
16221da177e4SLinus Torvalds 				return (IPS_FAILURE);
16231da177e4SLinus Torvalds 			}
16241da177e4SLinus Torvalds 
16258a694cc8SJeff Garzik 			if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD &&
16261da177e4SLinus Torvalds 			    pt->CoppCP.cmd.flashfw.op_code ==
16271da177e4SLinus Torvalds 			    IPS_CMD_RW_BIOSFW) {
16281da177e4SLinus Torvalds 				ret = ips_flash_copperhead(ha, pt, scb);
16291da177e4SLinus Torvalds 				ips_scmd_buf_write(SC, ha->ioctl_data,
16301da177e4SLinus Torvalds 						   sizeof (ips_passthru_t));
16311da177e4SLinus Torvalds 				return ret;
16321da177e4SLinus Torvalds 			}
16331da177e4SLinus Torvalds 			if (ips_usrcmd(ha, pt, scb))
16341da177e4SLinus Torvalds 				return (IPS_SUCCESS);
16351da177e4SLinus Torvalds 			else
16361da177e4SLinus Torvalds 				return (IPS_FAILURE);
16371da177e4SLinus Torvalds 		}
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds 		break;
16401da177e4SLinus Torvalds 
16411da177e4SLinus Torvalds 	}			/* end switch */
16421da177e4SLinus Torvalds 
16431da177e4SLinus Torvalds 	return (IPS_FAILURE);
16441da177e4SLinus Torvalds }
16451da177e4SLinus Torvalds 
16461da177e4SLinus Torvalds /****************************************************************************/
16471da177e4SLinus Torvalds /* Routine Name: ips_flash_copperhead                                       */
16481da177e4SLinus Torvalds /* Routine Description:                                                     */
16491da177e4SLinus Torvalds /*   Flash the BIOS/FW on a Copperhead style controller                     */
16501da177e4SLinus Torvalds /****************************************************************************/
16511da177e4SLinus Torvalds static int
16521da177e4SLinus Torvalds ips_flash_copperhead(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
16531da177e4SLinus Torvalds {
16541da177e4SLinus Torvalds 	int datasize;
16551da177e4SLinus Torvalds 
16561da177e4SLinus Torvalds 	/* Trombone is the only copperhead that can do packet flash, but only
165725985edcSLucas De Marchi 	 * for firmware. No one said it had to make sense. */
16581da177e4SLinus Torvalds 	if (IPS_IS_TROMBONE(ha) && pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE) {
16591da177e4SLinus Torvalds 		if (ips_usrcmd(ha, pt, scb))
16601da177e4SLinus Torvalds 			return IPS_SUCCESS;
16611da177e4SLinus Torvalds 		else
16621da177e4SLinus Torvalds 			return IPS_FAILURE;
16631da177e4SLinus Torvalds 	}
16641da177e4SLinus Torvalds 	pt->BasicStatus = 0x0B;
16651da177e4SLinus Torvalds 	pt->ExtendedStatus = 0;
16661da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
16671da177e4SLinus Torvalds 	/* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can     */
16681da177e4SLinus Torvalds 	/* avoid allocating a huge buffer per adapter ( which can fail ). */
16691da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
16701da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
16711da177e4SLinus Torvalds 		pt->BasicStatus = 0;
16721da177e4SLinus Torvalds 		return ips_flash_bios(ha, pt, scb);
16731da177e4SLinus Torvalds 	} else if (pt->CoppCP.cmd.flashfw.packet_num == 0) {
16741da177e4SLinus Torvalds 		if (ips_FlashData && !test_and_set_bit(0, &ips_FlashDataInUse)){
16751da177e4SLinus Torvalds 			ha->flash_data = ips_FlashData;
16761da177e4SLinus Torvalds 			ha->flash_busaddr = ips_flashbusaddr;
16771da177e4SLinus Torvalds 			ha->flash_len = PAGE_SIZE << 7;
16781da177e4SLinus Torvalds 			ha->flash_datasize = 0;
16791da177e4SLinus Torvalds 		} else if (!ha->flash_data) {
16801da177e4SLinus Torvalds 			datasize = pt->CoppCP.cmd.flashfw.total_packets *
16811da177e4SLinus Torvalds 			    pt->CoppCP.cmd.flashfw.count;
168232e76961SChristoph Hellwig 			ha->flash_data = dma_alloc_coherent(&ha->pcidev->dev,
168332e76961SChristoph Hellwig 					datasize, &ha->flash_busaddr, GFP_KERNEL);
16841da177e4SLinus Torvalds 			if (!ha->flash_data){
16851da177e4SLinus Torvalds 				printk(KERN_WARNING "Unable to allocate a flash buffer\n");
16861da177e4SLinus Torvalds 				return IPS_FAILURE;
16871da177e4SLinus Torvalds 			}
16881da177e4SLinus Torvalds 			ha->flash_datasize = 0;
16891da177e4SLinus Torvalds 			ha->flash_len = datasize;
16901da177e4SLinus Torvalds 		} else
16911da177e4SLinus Torvalds 			return IPS_FAILURE;
16921da177e4SLinus Torvalds 	} else {
16931da177e4SLinus Torvalds 		if (pt->CoppCP.cmd.flashfw.count + ha->flash_datasize >
16941da177e4SLinus Torvalds 		    ha->flash_len) {
16951da177e4SLinus Torvalds 			ips_free_flash_copperhead(ha);
16961da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
16971da177e4SLinus Torvalds 				   "failed size sanity check\n");
16981da177e4SLinus Torvalds 			return IPS_FAILURE;
16991da177e4SLinus Torvalds 		}
17001da177e4SLinus Torvalds 	}
17011da177e4SLinus Torvalds 	if (!ha->flash_data)
17021da177e4SLinus Torvalds 		return IPS_FAILURE;
17031da177e4SLinus Torvalds 	pt->BasicStatus = 0;
17041da177e4SLinus Torvalds 	memcpy(&ha->flash_data[ha->flash_datasize], pt + 1,
17051da177e4SLinus Torvalds 	       pt->CoppCP.cmd.flashfw.count);
17061da177e4SLinus Torvalds 	ha->flash_datasize += pt->CoppCP.cmd.flashfw.count;
17071da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.packet_num ==
17081da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.total_packets - 1) {
17091da177e4SLinus Torvalds 		if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE)
17101da177e4SLinus Torvalds 			return ips_flash_bios(ha, pt, scb);
17111da177e4SLinus Torvalds 		else if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE)
17121da177e4SLinus Torvalds 			return ips_flash_firmware(ha, pt, scb);
17131da177e4SLinus Torvalds 	}
17141da177e4SLinus Torvalds 	return IPS_SUCCESS_IMM;
17151da177e4SLinus Torvalds }
17161da177e4SLinus Torvalds 
17171da177e4SLinus Torvalds /****************************************************************************/
17181da177e4SLinus Torvalds /* Routine Name: ips_flash_bios                                             */
17191da177e4SLinus Torvalds /* Routine Description:                                                     */
17201da177e4SLinus Torvalds /*   flashes the bios of a copperhead adapter                               */
17211da177e4SLinus Torvalds /****************************************************************************/
17221da177e4SLinus Torvalds static int
17231da177e4SLinus Torvalds ips_flash_bios(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
17241da177e4SLinus Torvalds {
17251da177e4SLinus Torvalds 
17261da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
17271da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_BIOS) {
17281da177e4SLinus Torvalds 		if ((!ha->func.programbios) || (!ha->func.erasebios) ||
17291da177e4SLinus Torvalds 		    (!ha->func.verifybios))
17301da177e4SLinus Torvalds 			goto error;
17311da177e4SLinus Torvalds 		if ((*ha->func.erasebios) (ha)) {
17321da177e4SLinus Torvalds 			DEBUG_VAR(1,
17331da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to erase flash",
17341da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17351da177e4SLinus Torvalds 			goto error;
17361da177e4SLinus Torvalds 		} else
17371da177e4SLinus Torvalds 		    if ((*ha->func.programbios) (ha,
17381da177e4SLinus Torvalds 						 ha->flash_data +
17391da177e4SLinus Torvalds 						 IPS_BIOS_HEADER,
17401da177e4SLinus Torvalds 						 ha->flash_datasize -
17411da177e4SLinus Torvalds 						 IPS_BIOS_HEADER, 0)) {
17421da177e4SLinus Torvalds 			DEBUG_VAR(1,
17431da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to flash",
17441da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17451da177e4SLinus Torvalds 			goto error;
17461da177e4SLinus Torvalds 		} else
17471da177e4SLinus Torvalds 		    if ((*ha->func.verifybios) (ha,
17481da177e4SLinus Torvalds 						ha->flash_data +
17491da177e4SLinus Torvalds 						IPS_BIOS_HEADER,
17501da177e4SLinus Torvalds 						ha->flash_datasize -
17511da177e4SLinus Torvalds 						IPS_BIOS_HEADER, 0)) {
17521da177e4SLinus Torvalds 			DEBUG_VAR(1,
17531da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to verify flash",
17541da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17551da177e4SLinus Torvalds 			goto error;
17561da177e4SLinus Torvalds 		}
17571da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
17581da177e4SLinus Torvalds 		return IPS_SUCCESS_IMM;
17591da177e4SLinus Torvalds 	} else if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
17601da177e4SLinus Torvalds 		   pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
17611da177e4SLinus Torvalds 		if (!ha->func.erasebios)
17621da177e4SLinus Torvalds 			goto error;
17631da177e4SLinus Torvalds 		if ((*ha->func.erasebios) (ha)) {
17641da177e4SLinus Torvalds 			DEBUG_VAR(1,
17651da177e4SLinus Torvalds 				  "(%s%d) flash bios failed - unable to erase flash",
17661da177e4SLinus Torvalds 				  ips_name, ha->host_num);
17671da177e4SLinus Torvalds 			goto error;
17681da177e4SLinus Torvalds 		}
17691da177e4SLinus Torvalds 		return IPS_SUCCESS_IMM;
17701da177e4SLinus Torvalds 	}
17711da177e4SLinus Torvalds       error:
17721da177e4SLinus Torvalds 	pt->BasicStatus = 0x0B;
17731da177e4SLinus Torvalds 	pt->ExtendedStatus = 0x00;
17741da177e4SLinus Torvalds 	ips_free_flash_copperhead(ha);
17751da177e4SLinus Torvalds 	return IPS_FAILURE;
17761da177e4SLinus Torvalds }
17771da177e4SLinus Torvalds 
17781da177e4SLinus Torvalds /****************************************************************************/
17791da177e4SLinus Torvalds /*                                                                          */
17801da177e4SLinus Torvalds /* Routine Name: ips_fill_scb_sg_single                                     */
17811da177e4SLinus Torvalds /*                                                                          */
17821da177e4SLinus Torvalds /* Routine Description:                                                     */
17831da177e4SLinus Torvalds /*   Fill in a single scb sg_list element from an address                   */
17841da177e4SLinus Torvalds /*   return a -1 if a breakup occurred                                      */
17851da177e4SLinus Torvalds /****************************************************************************/
17861da177e4SLinus Torvalds static int
17871da177e4SLinus Torvalds ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr,
17881da177e4SLinus Torvalds 		       ips_scb_t * scb, int indx, unsigned int e_len)
17891da177e4SLinus Torvalds {
17901da177e4SLinus Torvalds 
17911da177e4SLinus Torvalds 	int ret_val = 0;
17921da177e4SLinus Torvalds 
17931da177e4SLinus Torvalds 	if ((scb->data_len + e_len) > ha->max_xfer) {
17941da177e4SLinus Torvalds 		e_len = ha->max_xfer - scb->data_len;
17951da177e4SLinus Torvalds 		scb->breakup = indx;
17961da177e4SLinus Torvalds 		++scb->sg_break;
17971da177e4SLinus Torvalds 		ret_val = -1;
17981da177e4SLinus Torvalds 	} else {
17991da177e4SLinus Torvalds 		scb->breakup = 0;
18001da177e4SLinus Torvalds 		scb->sg_break = 0;
18011da177e4SLinus Torvalds 	}
18021da177e4SLinus Torvalds 	if (IPS_USE_ENH_SGLIST(ha)) {
18031da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].address_lo =
18046f6eb3ccSChristoph Hellwig 		    cpu_to_le32(lower_32_bits(busaddr));
18051da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].address_hi =
18066f6eb3ccSChristoph Hellwig 		    cpu_to_le32(upper_32_bits(busaddr));
18071da177e4SLinus Torvalds 		scb->sg_list.enh_list[indx].length = cpu_to_le32(e_len);
18081da177e4SLinus Torvalds 	} else {
18091da177e4SLinus Torvalds 		scb->sg_list.std_list[indx].address =
18106f6eb3ccSChristoph Hellwig 		    cpu_to_le32(lower_32_bits(busaddr));
18111da177e4SLinus Torvalds 		scb->sg_list.std_list[indx].length = cpu_to_le32(e_len);
18121da177e4SLinus Torvalds 	}
18131da177e4SLinus Torvalds 
18141da177e4SLinus Torvalds 	++scb->sg_len;
18151da177e4SLinus Torvalds 	scb->data_len += e_len;
18161da177e4SLinus Torvalds 	return ret_val;
18171da177e4SLinus Torvalds }
18181da177e4SLinus Torvalds 
18191da177e4SLinus Torvalds /****************************************************************************/
18201da177e4SLinus Torvalds /* Routine Name: ips_flash_firmware                                         */
18211da177e4SLinus Torvalds /* Routine Description:                                                     */
18221da177e4SLinus Torvalds /*   flashes the firmware of a copperhead adapter                           */
18231da177e4SLinus Torvalds /****************************************************************************/
18241da177e4SLinus Torvalds static int
18251da177e4SLinus Torvalds ips_flash_firmware(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
18261da177e4SLinus Torvalds {
18271da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
18281da177e4SLinus Torvalds 	uint32_t cmd_busaddr;
18291da177e4SLinus Torvalds 
18301da177e4SLinus Torvalds 	if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE &&
18311da177e4SLinus Torvalds 	    pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_FW) {
18321da177e4SLinus Torvalds 		memset(&pt->CoppCP.cmd, 0, sizeof (IPS_HOST_COMMAND));
18331da177e4SLinus Torvalds 		pt->CoppCP.cmd.flashfw.op_code = IPS_CMD_DOWNLOAD;
18341da177e4SLinus Torvalds 		pt->CoppCP.cmd.flashfw.count = cpu_to_le32(ha->flash_datasize);
18351da177e4SLinus Torvalds 	} else {
18361da177e4SLinus Torvalds 		pt->BasicStatus = 0x0B;
18371da177e4SLinus Torvalds 		pt->ExtendedStatus = 0x00;
18381da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
18391da177e4SLinus Torvalds 		return IPS_FAILURE;
18401da177e4SLinus Torvalds 	}
18411da177e4SLinus Torvalds 	/* Save the S/G list pointer so it doesn't get clobbered */
18421da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
18431da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
18441da177e4SLinus Torvalds 	/* copy in the CP */
18451da177e4SLinus Torvalds 	memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
18461da177e4SLinus Torvalds 	/* FIX stuff that might be wrong */
18471da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
18481da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
18491da177e4SLinus Torvalds 	scb->bus = scb->scsi_cmd->device->channel;
18501da177e4SLinus Torvalds 	scb->target_id = scb->scsi_cmd->device->id;
18511da177e4SLinus Torvalds 	scb->lun = scb->scsi_cmd->device->lun;
18521da177e4SLinus Torvalds 	scb->sg_len = 0;
18531da177e4SLinus Torvalds 	scb->data_len = 0;
18541da177e4SLinus Torvalds 	scb->flags = 0;
18551da177e4SLinus Torvalds 	scb->op_code = 0;
18561da177e4SLinus Torvalds 	scb->callback = ipsintr_done;
18571da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
18581da177e4SLinus Torvalds 
18591da177e4SLinus Torvalds 	scb->data_len = ha->flash_datasize;
18601da177e4SLinus Torvalds 	scb->data_busaddr =
186132e76961SChristoph Hellwig 	    dma_map_single(&ha->pcidev->dev, ha->flash_data, scb->data_len,
18621da177e4SLinus Torvalds 			   IPS_DMA_DIR(scb));
18631da177e4SLinus Torvalds 	scb->flags |= IPS_SCB_MAP_SINGLE;
18641da177e4SLinus Torvalds 	scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
18651da177e4SLinus Torvalds 	scb->cmd.flashfw.buffer_addr = cpu_to_le32(scb->data_busaddr);
18661da177e4SLinus Torvalds 	if (pt->TimeOut)
18671da177e4SLinus Torvalds 		scb->timeout = pt->TimeOut;
18681da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
18691da177e4SLinus Torvalds 	return IPS_SUCCESS;
18701da177e4SLinus Torvalds }
18711da177e4SLinus Torvalds 
18721da177e4SLinus Torvalds /****************************************************************************/
18731da177e4SLinus Torvalds /* Routine Name: ips_free_flash_copperhead                                  */
18741da177e4SLinus Torvalds /* Routine Description:                                                     */
18751da177e4SLinus Torvalds /*   release the memory resources used to hold the flash image              */
18761da177e4SLinus Torvalds /****************************************************************************/
18771da177e4SLinus Torvalds static void
18781da177e4SLinus Torvalds ips_free_flash_copperhead(ips_ha_t * ha)
18791da177e4SLinus Torvalds {
18801da177e4SLinus Torvalds 	if (ha->flash_data == ips_FlashData)
18811da177e4SLinus Torvalds 		test_and_clear_bit(0, &ips_FlashDataInUse);
18821da177e4SLinus Torvalds 	else if (ha->flash_data)
188332e76961SChristoph Hellwig 		dma_free_coherent(&ha->pcidev->dev, ha->flash_len,
188432e76961SChristoph Hellwig 				  ha->flash_data, ha->flash_busaddr);
18851da177e4SLinus Torvalds 	ha->flash_data = NULL;
18861da177e4SLinus Torvalds }
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds /****************************************************************************/
18891da177e4SLinus Torvalds /*                                                                          */
18901da177e4SLinus Torvalds /* Routine Name: ips_usrcmd                                                 */
18911da177e4SLinus Torvalds /*                                                                          */
18921da177e4SLinus Torvalds /* Routine Description:                                                     */
18931da177e4SLinus Torvalds /*                                                                          */
18941da177e4SLinus Torvalds /*   Process a user command and make it ready to send                       */
18951da177e4SLinus Torvalds /*                                                                          */
18961da177e4SLinus Torvalds /****************************************************************************/
18971da177e4SLinus Torvalds static int
18981da177e4SLinus Torvalds ips_usrcmd(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
18991da177e4SLinus Torvalds {
19001da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
19011da177e4SLinus Torvalds 	uint32_t cmd_busaddr;
19021da177e4SLinus Torvalds 
19031da177e4SLinus Torvalds 	METHOD_TRACE("ips_usrcmd", 1);
19041da177e4SLinus Torvalds 
19051da177e4SLinus Torvalds 	if ((!scb) || (!pt) || (!ha))
19061da177e4SLinus Torvalds 		return (0);
19071da177e4SLinus Torvalds 
19081da177e4SLinus Torvalds 	/* Save the S/G list pointer so it doesn't get clobbered */
19091da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
19101da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
19111da177e4SLinus Torvalds 	/* copy in the CP */
19121da177e4SLinus Torvalds 	memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
19131da177e4SLinus Torvalds 	memcpy(&scb->dcdb, &pt->CoppCP.dcdb, sizeof (IPS_DCDB_TABLE));
19141da177e4SLinus Torvalds 
19151da177e4SLinus Torvalds 	/* FIX stuff that might be wrong */
19161da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
19171da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
19181da177e4SLinus Torvalds 	scb->bus = scb->scsi_cmd->device->channel;
19191da177e4SLinus Torvalds 	scb->target_id = scb->scsi_cmd->device->id;
19201da177e4SLinus Torvalds 	scb->lun = scb->scsi_cmd->device->lun;
19211da177e4SLinus Torvalds 	scb->sg_len = 0;
19221da177e4SLinus Torvalds 	scb->data_len = 0;
19231da177e4SLinus Torvalds 	scb->flags = 0;
19241da177e4SLinus Torvalds 	scb->op_code = 0;
19251da177e4SLinus Torvalds 	scb->callback = ipsintr_done;
19261da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
19271da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
19281da177e4SLinus Torvalds 
19291da177e4SLinus Torvalds 	/* we don't support DCDB/READ/WRITE Scatter Gather */
19301da177e4SLinus Torvalds 	if ((scb->cmd.basic_io.op_code == IPS_CMD_READ_SG) ||
19311da177e4SLinus Torvalds 	    (scb->cmd.basic_io.op_code == IPS_CMD_WRITE_SG) ||
19321da177e4SLinus Torvalds 	    (scb->cmd.basic_io.op_code == IPS_CMD_DCDB_SG))
19331da177e4SLinus Torvalds 		return (0);
19341da177e4SLinus Torvalds 
19351da177e4SLinus Torvalds 	if (pt->CmdBSize) {
19361da177e4SLinus Torvalds 		scb->data_len = pt->CmdBSize;
19371da177e4SLinus Torvalds 		scb->data_busaddr = ha->ioctl_busaddr + sizeof (ips_passthru_t);
19381da177e4SLinus Torvalds 	} else {
19391da177e4SLinus Torvalds 		scb->data_busaddr = 0L;
19401da177e4SLinus Torvalds 	}
19411da177e4SLinus Torvalds 
19421da177e4SLinus Torvalds 	if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
19431da177e4SLinus Torvalds 		scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
19441da177e4SLinus Torvalds 							 (unsigned long) &scb->
19451da177e4SLinus Torvalds 							 dcdb -
19461da177e4SLinus Torvalds 							 (unsigned long) scb);
19471da177e4SLinus Torvalds 
19481da177e4SLinus Torvalds 	if (pt->CmdBSize) {
19491da177e4SLinus Torvalds 		if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
19501da177e4SLinus Torvalds 			scb->dcdb.buffer_pointer =
19511da177e4SLinus Torvalds 			    cpu_to_le32(scb->data_busaddr);
19521da177e4SLinus Torvalds 		else
19531da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_addr =
19541da177e4SLinus Torvalds 			    cpu_to_le32(scb->data_busaddr);
19551da177e4SLinus Torvalds 	}
19561da177e4SLinus Torvalds 
19571da177e4SLinus Torvalds 	/* set timeouts */
19581da177e4SLinus Torvalds 	if (pt->TimeOut) {
19591da177e4SLinus Torvalds 		scb->timeout = pt->TimeOut;
19601da177e4SLinus Torvalds 
19611da177e4SLinus Torvalds 		if (pt->TimeOut <= 10)
19621da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;
19631da177e4SLinus Torvalds 		else if (pt->TimeOut <= 60)
19641da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;
19651da177e4SLinus Torvalds 		else
19661da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;
19671da177e4SLinus Torvalds 	}
19681da177e4SLinus Torvalds 
19691da177e4SLinus Torvalds 	/* assume success */
19701da177e4SLinus Torvalds 	scb->scsi_cmd->result = DID_OK << 16;
19711da177e4SLinus Torvalds 
19721da177e4SLinus Torvalds 	/* success */
19731da177e4SLinus Torvalds 	return (1);
19741da177e4SLinus Torvalds }
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds /****************************************************************************/
19771da177e4SLinus Torvalds /*                                                                          */
19781da177e4SLinus Torvalds /* Routine Name: ips_cleanup_passthru                                       */
19791da177e4SLinus Torvalds /*                                                                          */
19801da177e4SLinus Torvalds /* Routine Description:                                                     */
19811da177e4SLinus Torvalds /*                                                                          */
19821da177e4SLinus Torvalds /*   Cleanup after a passthru command                                       */
19831da177e4SLinus Torvalds /*                                                                          */
19841da177e4SLinus Torvalds /****************************************************************************/
19851da177e4SLinus Torvalds static void
19861da177e4SLinus Torvalds ips_cleanup_passthru(ips_ha_t * ha, ips_scb_t * scb)
19871da177e4SLinus Torvalds {
19881da177e4SLinus Torvalds 	ips_passthru_t *pt;
19891da177e4SLinus Torvalds 
19901da177e4SLinus Torvalds 	METHOD_TRACE("ips_cleanup_passthru", 1);
19911da177e4SLinus Torvalds 
19922f4cf91cSFUJITA Tomonori 	if ((!scb) || (!scb->scsi_cmd) || (!scsi_sglist(scb->scsi_cmd))) {
19931da177e4SLinus Torvalds 		DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
19941da177e4SLinus Torvalds 			  ips_name, ha->host_num);
19951da177e4SLinus Torvalds 
19961da177e4SLinus Torvalds 		return;
19971da177e4SLinus Torvalds 	}
19981da177e4SLinus Torvalds 	pt = (ips_passthru_t *) ha->ioctl_data;
19991da177e4SLinus Torvalds 
20001da177e4SLinus Torvalds 	/* Copy data back to the user */
20011da177e4SLinus Torvalds 	if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)	/* Copy DCDB Back to Caller's Area */
20021da177e4SLinus Torvalds 		memcpy(&pt->CoppCP.dcdb, &scb->dcdb, sizeof (IPS_DCDB_TABLE));
20031da177e4SLinus Torvalds 
20041da177e4SLinus Torvalds 	pt->BasicStatus = scb->basic_status;
20051da177e4SLinus Torvalds 	pt->ExtendedStatus = scb->extended_status;
20061da177e4SLinus Torvalds 	pt->AdapterType = ha->ad_type;
20071da177e4SLinus Torvalds 
20088a694cc8SJeff Garzik 	if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD &&
20091da177e4SLinus Torvalds 	    (scb->cmd.flashfw.op_code == IPS_CMD_DOWNLOAD ||
20101da177e4SLinus Torvalds 	     scb->cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW))
20111da177e4SLinus Torvalds 		ips_free_flash_copperhead(ha);
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, ha->ioctl_data, ha->ioctl_datasize);
20141da177e4SLinus Torvalds }
20151da177e4SLinus Torvalds 
20161da177e4SLinus Torvalds /****************************************************************************/
20171da177e4SLinus Torvalds /*                                                                          */
20181da177e4SLinus Torvalds /* Routine Name: ips_host_info                                              */
20191da177e4SLinus Torvalds /*                                                                          */
20201da177e4SLinus Torvalds /* Routine Description:                                                     */
20211da177e4SLinus Torvalds /*                                                                          */
20221da177e4SLinus Torvalds /*   The passthru interface for the driver                                  */
20231da177e4SLinus Torvalds /*                                                                          */
20241da177e4SLinus Torvalds /****************************************************************************/
20251da177e4SLinus Torvalds static int
2026aacce706SAl Viro ips_host_info(ips_ha_t *ha, struct seq_file *m)
20271da177e4SLinus Torvalds {
20281da177e4SLinus Torvalds 	METHOD_TRACE("ips_host_info", 1);
20291da177e4SLinus Torvalds 
203091c40f24SRasmus Villemoes 	seq_puts(m, "\nIBM ServeRAID General Information:\n\n");
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds 	if ((le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) &&
20331da177e4SLinus Torvalds 	    (le16_to_cpu(ha->nvram->adapter_type) != 0))
2034aacce706SAl Viro 		seq_printf(m, "\tController Type                   : %s\n",
20351da177e4SLinus Torvalds 			  ips_adapter_name[ha->ad_type - 1]);
20361da177e4SLinus Torvalds 	else
203791c40f24SRasmus Villemoes 		seq_puts(m, "\tController Type                   : Unknown\n");
20381da177e4SLinus Torvalds 
20391da177e4SLinus Torvalds 	if (ha->io_addr)
2040aacce706SAl Viro 		seq_printf(m,
20411bb7109aSAl Viro 			  "\tIO region                         : 0x%x (%d bytes)\n",
20421da177e4SLinus Torvalds 			  ha->io_addr, ha->io_len);
20431da177e4SLinus Torvalds 
20441da177e4SLinus Torvalds 	if (ha->mem_addr) {
2045aacce706SAl Viro 		seq_printf(m,
20461bb7109aSAl Viro 			  "\tMemory region                     : 0x%x (%d bytes)\n",
20471da177e4SLinus Torvalds 			  ha->mem_addr, ha->mem_len);
2048aacce706SAl Viro 		seq_printf(m,
20491da177e4SLinus Torvalds 			  "\tShared memory address             : 0x%lx\n",
20501bb7109aSAl Viro 			  (unsigned long)ha->mem_ptr);
20511da177e4SLinus Torvalds 	}
20521da177e4SLinus Torvalds 
2053aacce706SAl Viro 	seq_printf(m, "\tIRQ number                        : %d\n", ha->pcidev->irq);
20541da177e4SLinus Torvalds 
20551da177e4SLinus Torvalds     /* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */
20561da177e4SLinus Torvalds     /* That keeps everything happy for "text" operations on the proc file.                    */
20571da177e4SLinus Torvalds 
20581da177e4SLinus Torvalds 	if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) {
20591da177e4SLinus Torvalds 	if (ha->nvram->bios_low[3] == 0) {
2060aacce706SAl Viro 		seq_printf(m,
20611da177e4SLinus Torvalds 			  "\tBIOS Version                      : %c%c%c%c%c%c%c\n",
20621da177e4SLinus Torvalds 			  ha->nvram->bios_high[0], ha->nvram->bios_high[1],
20631da177e4SLinus Torvalds 			  ha->nvram->bios_high[2], ha->nvram->bios_high[3],
20641da177e4SLinus Torvalds 			  ha->nvram->bios_low[0], ha->nvram->bios_low[1],
20651da177e4SLinus Torvalds 			  ha->nvram->bios_low[2]);
20661da177e4SLinus Torvalds 
20671da177e4SLinus Torvalds         } else {
2068aacce706SAl Viro 		seq_printf(m,
20691da177e4SLinus Torvalds 			  "\tBIOS Version                      : %c%c%c%c%c%c%c%c\n",
20701da177e4SLinus Torvalds 			  ha->nvram->bios_high[0], ha->nvram->bios_high[1],
20711da177e4SLinus Torvalds 			  ha->nvram->bios_high[2], ha->nvram->bios_high[3],
20721da177e4SLinus Torvalds 			  ha->nvram->bios_low[0], ha->nvram->bios_low[1],
20731da177e4SLinus Torvalds 			  ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
20741da177e4SLinus Torvalds         }
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds     }
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds     if (ha->enq->CodeBlkVersion[7] == 0) {
2079aacce706SAl Viro         seq_printf(m,
20801da177e4SLinus Torvalds 		  "\tFirmware Version                  : %c%c%c%c%c%c%c\n",
20811da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
20821da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
20831da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
20841da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[6]);
20851da177e4SLinus Torvalds     } else {
2086aacce706SAl Viro 	seq_printf(m,
20871da177e4SLinus Torvalds 		  "\tFirmware Version                  : %c%c%c%c%c%c%c%c\n",
20881da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
20891da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
20901da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
20911da177e4SLinus Torvalds 		  ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]);
20921da177e4SLinus Torvalds     }
20931da177e4SLinus Torvalds 
20941da177e4SLinus Torvalds     if (ha->enq->BootBlkVersion[7] == 0) {
2095aacce706SAl Viro         seq_printf(m,
20961da177e4SLinus Torvalds 		  "\tBoot Block Version                : %c%c%c%c%c%c%c\n",
20971da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
20981da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
20991da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
21001da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[6]);
21011da177e4SLinus Torvalds     } else {
2102aacce706SAl Viro         seq_printf(m,
21031da177e4SLinus Torvalds 		  "\tBoot Block Version                : %c%c%c%c%c%c%c%c\n",
21041da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
21051da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
21061da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
21071da177e4SLinus Torvalds 		  ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]);
21081da177e4SLinus Torvalds     }
21091da177e4SLinus Torvalds 
2110aacce706SAl Viro 	seq_printf(m, "\tDriver Version                    : %s%s\n",
21111da177e4SLinus Torvalds 		  IPS_VERSION_HIGH, IPS_VERSION_LOW);
21121da177e4SLinus Torvalds 
2113aacce706SAl Viro 	seq_printf(m, "\tDriver Build                      : %d\n",
21141da177e4SLinus Torvalds 		  IPS_BUILD_IDENT);
21151da177e4SLinus Torvalds 
2116aacce706SAl Viro 	seq_printf(m, "\tMax Physical Devices              : %d\n",
21171da177e4SLinus Torvalds 		  ha->enq->ucMaxPhysicalDevices);
2118aacce706SAl Viro 	seq_printf(m, "\tMax Active Commands               : %d\n",
21191da177e4SLinus Torvalds 		  ha->max_cmds);
2120aacce706SAl Viro 	seq_printf(m, "\tCurrent Queued Commands           : %d\n",
21211da177e4SLinus Torvalds 		  ha->scb_waitlist.count);
2122aacce706SAl Viro 	seq_printf(m, "\tCurrent Active Commands           : %d\n",
21231da177e4SLinus Torvalds 		  ha->scb_activelist.count - ha->num_ioctl);
2124aacce706SAl Viro 	seq_printf(m, "\tCurrent Queued PT Commands        : %d\n",
21251da177e4SLinus Torvalds 		  ha->copp_waitlist.count);
2126aacce706SAl Viro 	seq_printf(m, "\tCurrent Active PT Commands        : %d\n",
21271da177e4SLinus Torvalds 		  ha->num_ioctl);
21281da177e4SLinus Torvalds 
2129f50332ffSRasmus Villemoes 	seq_putc(m, '\n');
21301da177e4SLinus Torvalds 
2131aacce706SAl Viro 	return 0;
21321da177e4SLinus Torvalds }
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds /****************************************************************************/
21351da177e4SLinus Torvalds /*                                                                          */
21361da177e4SLinus Torvalds /* Routine Name: ips_identify_controller                                    */
21371da177e4SLinus Torvalds /*                                                                          */
21381da177e4SLinus Torvalds /* Routine Description:                                                     */
21391da177e4SLinus Torvalds /*                                                                          */
21401da177e4SLinus Torvalds /*   Identify this controller                                               */
21411da177e4SLinus Torvalds /*                                                                          */
21421da177e4SLinus Torvalds /****************************************************************************/
21431da177e4SLinus Torvalds static void
21441da177e4SLinus Torvalds ips_identify_controller(ips_ha_t * ha)
21451da177e4SLinus Torvalds {
21461da177e4SLinus Torvalds 	METHOD_TRACE("ips_identify_controller", 1);
21471da177e4SLinus Torvalds 
21488a694cc8SJeff Garzik 	switch (ha->pcidev->device) {
21491da177e4SLinus Torvalds 	case IPS_DEVICEID_COPPERHEAD:
21508a694cc8SJeff Garzik 		if (ha->pcidev->revision <= IPS_REVID_SERVERAID) {
21511da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID;
21528a694cc8SJeff Garzik 		} else if (ha->pcidev->revision == IPS_REVID_SERVERAID2) {
21531da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID2;
21548a694cc8SJeff Garzik 		} else if (ha->pcidev->revision == IPS_REVID_NAVAJO) {
21551da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_NAVAJO;
21568a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision == IPS_REVID_SERVERAID2)
21571da177e4SLinus Torvalds 			   && (ha->slot_num == 0)) {
21581da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_KIOWA;
21598a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision >= IPS_REVID_CLARINETP1) &&
21608a694cc8SJeff Garzik 			   (ha->pcidev->revision <= IPS_REVID_CLARINETP3)) {
21611da177e4SLinus Torvalds 			if (ha->enq->ucMaxPhysicalDevices == 15)
21621da177e4SLinus Torvalds 				ha->ad_type = IPS_ADTYPE_SERVERAID3L;
21631da177e4SLinus Torvalds 			else
21641da177e4SLinus Torvalds 				ha->ad_type = IPS_ADTYPE_SERVERAID3;
21658a694cc8SJeff Garzik 		} else if ((ha->pcidev->revision >= IPS_REVID_TROMBONE32) &&
21668a694cc8SJeff Garzik 			   (ha->pcidev->revision <= IPS_REVID_TROMBONE64)) {
21671da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4H;
21681da177e4SLinus Torvalds 		}
21691da177e4SLinus Torvalds 		break;
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds 	case IPS_DEVICEID_MORPHEUS:
21728a694cc8SJeff Garzik 		switch (ha->pcidev->subsystem_device) {
21731da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4L:
21741da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4L;
21751da177e4SLinus Torvalds 			break;
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4M:
21781da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4M;
21791da177e4SLinus Torvalds 			break;
21801da177e4SLinus Torvalds 
21811da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4MX:
21821da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4MX;
21831da177e4SLinus Torvalds 			break;
21841da177e4SLinus Torvalds 
21851da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_4LX:
21861da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID4LX;
21871da177e4SLinus Torvalds 			break;
21881da177e4SLinus Torvalds 
21891da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_5I2:
21901da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID5I2;
21911da177e4SLinus Torvalds 			break;
21921da177e4SLinus Torvalds 
21931da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_5I1:
21941da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID5I1;
21951da177e4SLinus Torvalds 			break;
21961da177e4SLinus Torvalds 		}
21971da177e4SLinus Torvalds 
21981da177e4SLinus Torvalds 		break;
21991da177e4SLinus Torvalds 
22001da177e4SLinus Torvalds 	case IPS_DEVICEID_MARCO:
22018a694cc8SJeff Garzik 		switch (ha->pcidev->subsystem_device) {
22021da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_6M:
22031da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID6M;
22041da177e4SLinus Torvalds 			break;
22051da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_6I:
22061da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID6I;
22071da177e4SLinus Torvalds 			break;
22081da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_7k:
22091da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID7k;
22101da177e4SLinus Torvalds 			break;
22111da177e4SLinus Torvalds 		case IPS_SUBDEVICEID_7M:
22121da177e4SLinus Torvalds 			ha->ad_type = IPS_ADTYPE_SERVERAID7M;
22131da177e4SLinus Torvalds 			break;
22141da177e4SLinus Torvalds 		}
22151da177e4SLinus Torvalds 		break;
22161da177e4SLinus Torvalds 	}
22171da177e4SLinus Torvalds }
22181da177e4SLinus Torvalds 
22191da177e4SLinus Torvalds /****************************************************************************/
22201da177e4SLinus Torvalds /*                                                                          */
22211da177e4SLinus Torvalds /* Routine Name: ips_get_bios_version                                       */
22221da177e4SLinus Torvalds /*                                                                          */
22231da177e4SLinus Torvalds /* Routine Description:                                                     */
22241da177e4SLinus Torvalds /*                                                                          */
22251da177e4SLinus Torvalds /*   Get the BIOS revision number                                           */
22261da177e4SLinus Torvalds /*                                                                          */
22271da177e4SLinus Torvalds /****************************************************************************/
22281da177e4SLinus Torvalds static void
22291da177e4SLinus Torvalds ips_get_bios_version(ips_ha_t * ha, int intr)
22301da177e4SLinus Torvalds {
22311da177e4SLinus Torvalds 	ips_scb_t *scb;
22321da177e4SLinus Torvalds 	int ret;
22331da177e4SLinus Torvalds 	uint8_t major;
22341da177e4SLinus Torvalds 	uint8_t minor;
22351da177e4SLinus Torvalds 	uint8_t subminor;
22361da177e4SLinus Torvalds 	uint8_t *buffer;
22371da177e4SLinus Torvalds 
22381da177e4SLinus Torvalds 	METHOD_TRACE("ips_get_bios_version", 1);
22391da177e4SLinus Torvalds 
22401da177e4SLinus Torvalds 	major = 0;
22411da177e4SLinus Torvalds 	minor = 0;
22421da177e4SLinus Torvalds 
22431da177e4SLinus Torvalds 	strncpy(ha->bios_version, "       ?", 8);
22441da177e4SLinus Torvalds 
22458a694cc8SJeff Garzik 	if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD) {
22461da177e4SLinus Torvalds 		if (IPS_USE_MEMIO(ha)) {
22471da177e4SLinus Torvalds 			/* Memory Mapped I/O */
22481da177e4SLinus Torvalds 
22491da177e4SLinus Torvalds 			/* test 1st byte */
22501da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
22518a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22521da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22531da177e4SLinus Torvalds 
22541da177e4SLinus Torvalds 			if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
22551da177e4SLinus Torvalds 				return;
22561da177e4SLinus Torvalds 
22571da177e4SLinus Torvalds 			writel(1, ha->mem_ptr + IPS_REG_FLAP);
22588a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22591da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22601da177e4SLinus Torvalds 
22611da177e4SLinus Torvalds 			if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
22621da177e4SLinus Torvalds 				return;
22631da177e4SLinus Torvalds 
22641da177e4SLinus Torvalds 			/* Get Major version */
22651da177e4SLinus Torvalds 			writel(0x1FF, 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 			major = readb(ha->mem_ptr + IPS_REG_FLDP);
22701da177e4SLinus Torvalds 
22711da177e4SLinus Torvalds 			/* Get Minor version */
22721da177e4SLinus Torvalds 			writel(0x1FE, ha->mem_ptr + IPS_REG_FLAP);
22738a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22741da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22751da177e4SLinus Torvalds 			minor = readb(ha->mem_ptr + IPS_REG_FLDP);
22761da177e4SLinus Torvalds 
22771da177e4SLinus Torvalds 			/* Get SubMinor version */
22781da177e4SLinus Torvalds 			writel(0x1FD, ha->mem_ptr + IPS_REG_FLAP);
22798a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22801da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22811da177e4SLinus Torvalds 			subminor = readb(ha->mem_ptr + IPS_REG_FLDP);
22821da177e4SLinus Torvalds 
22831da177e4SLinus Torvalds 		} else {
22841da177e4SLinus Torvalds 			/* Programmed I/O */
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds 			/* test 1st byte */
22871da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
22888a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22891da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22901da177e4SLinus Torvalds 
22911da177e4SLinus Torvalds 			if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
22921da177e4SLinus Torvalds 				return;
22931da177e4SLinus Torvalds 
2294db3cc200SJames Bottomley 			outl(1, ha->io_addr + IPS_REG_FLAP);
22958a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
22961da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
22971da177e4SLinus Torvalds 
22981da177e4SLinus Torvalds 			if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
22991da177e4SLinus Torvalds 				return;
23001da177e4SLinus Torvalds 
23011da177e4SLinus Torvalds 			/* Get Major version */
2302db3cc200SJames Bottomley 			outl(0x1FF, 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 			major = inb(ha->io_addr + IPS_REG_FLDP);
23071da177e4SLinus Torvalds 
23081da177e4SLinus Torvalds 			/* Get Minor version */
2309db3cc200SJames Bottomley 			outl(0x1FE, 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 			minor = inb(ha->io_addr + IPS_REG_FLDP);
23141da177e4SLinus Torvalds 
23151da177e4SLinus Torvalds 			/* Get SubMinor version */
2316db3cc200SJames Bottomley 			outl(0x1FD, ha->io_addr + IPS_REG_FLAP);
23178a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
23181da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
23191da177e4SLinus Torvalds 
23201da177e4SLinus Torvalds 			subminor = inb(ha->io_addr + IPS_REG_FLDP);
23211da177e4SLinus Torvalds 
23221da177e4SLinus Torvalds 		}
23231da177e4SLinus Torvalds 	} else {
23241da177e4SLinus Torvalds 		/* Morpheus Family - Send Command to the card */
23251da177e4SLinus Torvalds 
23261da177e4SLinus Torvalds 		buffer = ha->ioctl_data;
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 		memset(buffer, 0, 0x1000);
23291da177e4SLinus Torvalds 
23301da177e4SLinus Torvalds 		scb = &ha->scbs[ha->max_cmds - 1];
23311da177e4SLinus Torvalds 
23321da177e4SLinus Torvalds 		ips_init_scb(ha, scb);
23331da177e4SLinus Torvalds 
23341da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
23351da177e4SLinus Torvalds 		scb->cdb[0] = IPS_CMD_RW_BIOSFW;
23361da177e4SLinus Torvalds 
23371da177e4SLinus Torvalds 		scb->cmd.flashfw.op_code = IPS_CMD_RW_BIOSFW;
23381da177e4SLinus Torvalds 		scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
23391da177e4SLinus Torvalds 		scb->cmd.flashfw.type = 1;
23401da177e4SLinus Torvalds 		scb->cmd.flashfw.direction = 0;
23411da177e4SLinus Torvalds 		scb->cmd.flashfw.count = cpu_to_le32(0x800);
23421da177e4SLinus Torvalds 		scb->cmd.flashfw.total_packets = 1;
23431da177e4SLinus Torvalds 		scb->cmd.flashfw.packet_num = 0;
23441da177e4SLinus Torvalds 		scb->data_len = 0x1000;
23451da177e4SLinus Torvalds 		scb->cmd.flashfw.buffer_addr = ha->ioctl_busaddr;
23461da177e4SLinus Torvalds 
23471da177e4SLinus Torvalds 		/* issue the command */
23481da177e4SLinus Torvalds 		if (((ret =
23491da177e4SLinus Torvalds 		      ips_send_wait(ha, scb, ips_cmd_timeout,
23501da177e4SLinus Torvalds 				    intr)) == IPS_FAILURE)
23511da177e4SLinus Torvalds 		    || (ret == IPS_SUCCESS_IMM)
23521da177e4SLinus Torvalds 		    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
23531da177e4SLinus Torvalds 			/* Error occurred */
23541da177e4SLinus Torvalds 
23551da177e4SLinus Torvalds 			return;
23561da177e4SLinus Torvalds 		}
23571da177e4SLinus Torvalds 
23581da177e4SLinus Torvalds 		if ((buffer[0xC0] == 0x55) && (buffer[0xC1] == 0xAA)) {
23591da177e4SLinus Torvalds 			major = buffer[0x1ff + 0xC0];	/* Offset 0x1ff after the header (0xc0) */
23601da177e4SLinus Torvalds 			minor = buffer[0x1fe + 0xC0];	/* Offset 0x1fe after the header (0xc0) */
23611da177e4SLinus Torvalds 			subminor = buffer[0x1fd + 0xC0];	/* Offset 0x1fd after the header (0xc0) */
23621da177e4SLinus Torvalds 		} else {
23631da177e4SLinus Torvalds 			return;
23641da177e4SLinus Torvalds 		}
23651da177e4SLinus Torvalds 	}
23661da177e4SLinus Torvalds 
23671bc50455SAndy Shevchenko 	ha->bios_version[0] = hex_asc_upper_hi(major);
23681da177e4SLinus Torvalds 	ha->bios_version[1] = '.';
23691bc50455SAndy Shevchenko 	ha->bios_version[2] = hex_asc_upper_lo(major);
23701bc50455SAndy Shevchenko 	ha->bios_version[3] = hex_asc_upper_lo(subminor);
23711da177e4SLinus Torvalds 	ha->bios_version[4] = '.';
23721bc50455SAndy Shevchenko 	ha->bios_version[5] = hex_asc_upper_hi(minor);
23731bc50455SAndy Shevchenko 	ha->bios_version[6] = hex_asc_upper_lo(minor);
23741da177e4SLinus Torvalds 	ha->bios_version[7] = 0;
23751da177e4SLinus Torvalds }
23761da177e4SLinus Torvalds 
23771da177e4SLinus Torvalds /****************************************************************************/
23781da177e4SLinus Torvalds /*                                                                          */
23791da177e4SLinus Torvalds /* Routine Name: ips_hainit                                                 */
23801da177e4SLinus Torvalds /*                                                                          */
23811da177e4SLinus Torvalds /* Routine Description:                                                     */
23821da177e4SLinus Torvalds /*                                                                          */
23831da177e4SLinus Torvalds /*   Initialize the controller                                              */
23841da177e4SLinus Torvalds /*                                                                          */
23851da177e4SLinus Torvalds /* NOTE: Assumes to be called from with a lock                              */
23861da177e4SLinus Torvalds /*                                                                          */
23871da177e4SLinus Torvalds /****************************************************************************/
23881da177e4SLinus Torvalds static int
23891da177e4SLinus Torvalds ips_hainit(ips_ha_t * ha)
23901da177e4SLinus Torvalds {
23911da177e4SLinus Torvalds 	int i;
23921da177e4SLinus Torvalds 
23931da177e4SLinus Torvalds 	METHOD_TRACE("ips_hainit", 1);
23941da177e4SLinus Torvalds 
23951da177e4SLinus Torvalds 	if (!ha)
23961da177e4SLinus Torvalds 		return (0);
23971da177e4SLinus Torvalds 
23981da177e4SLinus Torvalds 	if (ha->func.statinit)
23991da177e4SLinus Torvalds 		(*ha->func.statinit) (ha);
24001da177e4SLinus Torvalds 
24011da177e4SLinus Torvalds 	if (ha->func.enableint)
24021da177e4SLinus Torvalds 		(*ha->func.enableint) (ha);
24031da177e4SLinus Torvalds 
24041da177e4SLinus Torvalds 	/* Send FFDC */
24051da177e4SLinus Torvalds 	ha->reset_count = 1;
2406f990bee3SArnd Bergmann 	ha->last_ffdc = ktime_get_real_seconds();
24071da177e4SLinus Torvalds 	ips_ffdc_reset(ha, IPS_INTR_IORL);
24081da177e4SLinus Torvalds 
24091da177e4SLinus Torvalds 	if (!ips_read_config(ha, IPS_INTR_IORL)) {
24101da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24111da177e4SLinus Torvalds 			   "unable to read config from controller.\n");
24121da177e4SLinus Torvalds 
24131da177e4SLinus Torvalds 		return (0);
24141da177e4SLinus Torvalds 	}
24151da177e4SLinus Torvalds 	/* end if */
24161da177e4SLinus Torvalds 	if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
24171da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24181da177e4SLinus Torvalds 			   "unable to read controller status.\n");
24191da177e4SLinus Torvalds 
24201da177e4SLinus Torvalds 		return (0);
24211da177e4SLinus Torvalds 	}
24221da177e4SLinus Torvalds 
24231da177e4SLinus Torvalds 	/* Identify this controller */
24241da177e4SLinus Torvalds 	ips_identify_controller(ha);
24251da177e4SLinus Torvalds 
24261da177e4SLinus Torvalds 	if (!ips_read_subsystem_parameters(ha, IPS_INTR_IORL)) {
24271da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24281da177e4SLinus Torvalds 			   "unable to read subsystem parameters.\n");
24291da177e4SLinus Torvalds 
24301da177e4SLinus Torvalds 		return (0);
24311da177e4SLinus Torvalds 	}
24321da177e4SLinus Torvalds 
24331da177e4SLinus Torvalds 	/* write nvram user page 5 */
24341da177e4SLinus Torvalds 	if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
24351da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
24361da177e4SLinus Torvalds 			   "unable to write driver info to controller.\n");
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds 		return (0);
24391da177e4SLinus Torvalds 	}
24401da177e4SLinus Torvalds 
24411da177e4SLinus Torvalds 	/* If there are Logical Drives and a Reset Occurred, then an EraseStripeLock is Needed */
24421da177e4SLinus Torvalds 	if ((ha->conf->ucLogDriveCount > 0) && (ha->requires_esl == 1))
24431da177e4SLinus Torvalds 		ips_clear_adapter(ha, IPS_INTR_IORL);
24441da177e4SLinus Torvalds 
24451da177e4SLinus Torvalds 	/* set limits on SID, LUN, BUS */
24461da177e4SLinus Torvalds 	ha->ntargets = IPS_MAX_TARGETS + 1;
24471da177e4SLinus Torvalds 	ha->nlun = 1;
24481da177e4SLinus Torvalds 	ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
24491da177e4SLinus Torvalds 
24501da177e4SLinus Torvalds 	switch (ha->conf->logical_drive[0].ucStripeSize) {
24511da177e4SLinus Torvalds 	case 4:
24521da177e4SLinus Torvalds 		ha->max_xfer = 0x10000;
24531da177e4SLinus Torvalds 		break;
24541da177e4SLinus Torvalds 
24551da177e4SLinus Torvalds 	case 5:
24561da177e4SLinus Torvalds 		ha->max_xfer = 0x20000;
24571da177e4SLinus Torvalds 		break;
24581da177e4SLinus Torvalds 
24591da177e4SLinus Torvalds 	case 6:
24601da177e4SLinus Torvalds 		ha->max_xfer = 0x40000;
24611da177e4SLinus Torvalds 		break;
24621da177e4SLinus Torvalds 
24631da177e4SLinus Torvalds 	case 7:
24641da177e4SLinus Torvalds 	default:
24651da177e4SLinus Torvalds 		ha->max_xfer = 0x80000;
24661da177e4SLinus Torvalds 		break;
24671da177e4SLinus Torvalds 	}
24681da177e4SLinus Torvalds 
24691da177e4SLinus Torvalds 	/* setup max concurrent commands */
24701da177e4SLinus Torvalds 	if (le32_to_cpu(ha->subsys->param[4]) & 0x1) {
24711da177e4SLinus Torvalds 		/* Use the new method */
24721da177e4SLinus Torvalds 		ha->max_cmds = ha->enq->ucConcurrentCmdCount;
24731da177e4SLinus Torvalds 	} else {
24741da177e4SLinus Torvalds 		/* use the old method */
24751da177e4SLinus Torvalds 		switch (ha->conf->logical_drive[0].ucStripeSize) {
24761da177e4SLinus Torvalds 		case 4:
24771da177e4SLinus Torvalds 			ha->max_cmds = 32;
24781da177e4SLinus Torvalds 			break;
24791da177e4SLinus Torvalds 
24801da177e4SLinus Torvalds 		case 5:
24811da177e4SLinus Torvalds 			ha->max_cmds = 16;
24821da177e4SLinus Torvalds 			break;
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds 		case 6:
24851da177e4SLinus Torvalds 			ha->max_cmds = 8;
24861da177e4SLinus Torvalds 			break;
24871da177e4SLinus Torvalds 
24881da177e4SLinus Torvalds 		case 7:
24891da177e4SLinus Torvalds 		default:
24901da177e4SLinus Torvalds 			ha->max_cmds = 4;
24911da177e4SLinus Torvalds 			break;
24921da177e4SLinus Torvalds 		}
24931da177e4SLinus Torvalds 	}
24941da177e4SLinus Torvalds 
24951da177e4SLinus Torvalds 	/* Limit the Active Commands on a Lite Adapter */
24961da177e4SLinus Torvalds 	if ((ha->ad_type == IPS_ADTYPE_SERVERAID3L) ||
24971da177e4SLinus Torvalds 	    (ha->ad_type == IPS_ADTYPE_SERVERAID4L) ||
24981da177e4SLinus Torvalds 	    (ha->ad_type == IPS_ADTYPE_SERVERAID4LX)) {
24991da177e4SLinus Torvalds 		if ((ha->max_cmds > MaxLiteCmds) && (MaxLiteCmds))
25001da177e4SLinus Torvalds 			ha->max_cmds = MaxLiteCmds;
25011da177e4SLinus Torvalds 	}
25021da177e4SLinus Torvalds 
25031da177e4SLinus Torvalds 	/* set controller IDs */
25041da177e4SLinus Torvalds 	ha->ha_id[0] = IPS_ADAPTER_ID;
25051da177e4SLinus Torvalds 	for (i = 1; i < ha->nbus; i++) {
25061da177e4SLinus Torvalds 		ha->ha_id[i] = ha->conf->init_id[i - 1] & 0x1f;
25071da177e4SLinus Torvalds 		ha->dcdb_active[i - 1] = 0;
25081da177e4SLinus Torvalds 	}
25091da177e4SLinus Torvalds 
25101da177e4SLinus Torvalds 	return (1);
25111da177e4SLinus Torvalds }
25121da177e4SLinus Torvalds 
25131da177e4SLinus Torvalds /****************************************************************************/
25141da177e4SLinus Torvalds /*                                                                          */
25151da177e4SLinus Torvalds /* Routine Name: ips_next                                                   */
25161da177e4SLinus Torvalds /*                                                                          */
25171da177e4SLinus Torvalds /* Routine Description:                                                     */
25181da177e4SLinus Torvalds /*                                                                          */
25191da177e4SLinus Torvalds /*   Take the next command off the queue and send it to the controller      */
25201da177e4SLinus Torvalds /*                                                                          */
25211da177e4SLinus Torvalds /****************************************************************************/
25221da177e4SLinus Torvalds static void
25231da177e4SLinus Torvalds ips_next(ips_ha_t * ha, int intr)
25241da177e4SLinus Torvalds {
25251da177e4SLinus Torvalds 	ips_scb_t *scb;
25261516b55dSHenne 	struct scsi_cmnd *SC;
25271516b55dSHenne 	struct scsi_cmnd *p;
25281516b55dSHenne 	struct scsi_cmnd *q;
25291da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
25301da177e4SLinus Torvalds 	int ret;
25311da177e4SLinus Torvalds 	struct Scsi_Host *host;
25321da177e4SLinus Torvalds 	METHOD_TRACE("ips_next", 1);
25331da177e4SLinus Torvalds 
25341da177e4SLinus Torvalds 	if (!ha)
25351da177e4SLinus Torvalds 		return;
25361da177e4SLinus Torvalds 	host = ips_sh[ha->host_num];
25371da177e4SLinus Torvalds 	/*
25381da177e4SLinus Torvalds 	 * Block access to the queue function so
25391da177e4SLinus Torvalds 	 * this command won't time out
25401da177e4SLinus Torvalds 	 */
25411da177e4SLinus Torvalds 	if (intr == IPS_INTR_ON)
2542c6a6c81cSAdrian Bunk 		spin_lock(host->host_lock);
25431da177e4SLinus Torvalds 
25441da177e4SLinus Torvalds 	if ((ha->subsys->param[3] & 0x300000)
25451da177e4SLinus Torvalds 	    && (ha->scb_activelist.count == 0)) {
2546f990bee3SArnd Bergmann 		time64_t now = ktime_get_real_seconds();
2547f990bee3SArnd Bergmann 		if (now - ha->last_ffdc > IPS_SECS_8HOURS) {
2548f990bee3SArnd Bergmann 			ha->last_ffdc = now;
25491da177e4SLinus Torvalds 			ips_ffdc_time(ha);
25501da177e4SLinus Torvalds 		}
25511da177e4SLinus Torvalds 	}
25521da177e4SLinus Torvalds 
25531da177e4SLinus Torvalds 	/*
25541da177e4SLinus Torvalds 	 * Send passthru commands
25551da177e4SLinus Torvalds 	 * These have priority over normal I/O
25561da177e4SLinus Torvalds 	 * but shouldn't affect performance too much
25571da177e4SLinus Torvalds 	 * since we limit the number that can be active
25581da177e4SLinus Torvalds 	 * on the card at any one time
25591da177e4SLinus Torvalds 	 */
25601da177e4SLinus Torvalds 	while ((ha->num_ioctl < IPS_MAX_IOCTL) &&
25611da177e4SLinus Torvalds 	       (ha->copp_waitlist.head) && (scb = ips_getscb(ha))) {
25621da177e4SLinus Torvalds 
25631da177e4SLinus Torvalds 		item = ips_removeq_copp_head(&ha->copp_waitlist);
25641da177e4SLinus Torvalds 		ha->num_ioctl++;
25651da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2566c6a6c81cSAdrian Bunk 			spin_unlock(host->host_lock);
25671da177e4SLinus Torvalds 		scb->scsi_cmd = item->scsi_cmd;
25681da177e4SLinus Torvalds 		kfree(item);
25691da177e4SLinus Torvalds 
25701da177e4SLinus Torvalds 		ret = ips_make_passthru(ha, scb->scsi_cmd, scb, intr);
25711da177e4SLinus Torvalds 
25721da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2573c6a6c81cSAdrian Bunk 			spin_lock(host->host_lock);
25741da177e4SLinus Torvalds 		switch (ret) {
25751da177e4SLinus Torvalds 		case IPS_FAILURE:
25761da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
25771da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
25781da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
25791da177e4SLinus Torvalds 			}
25801da177e4SLinus Torvalds 
25811da177e4SLinus Torvalds 			ips_freescb(ha, scb);
25821da177e4SLinus Torvalds 			break;
25831da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
25841da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
25851da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_OK << 16;
25861da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
25871da177e4SLinus Torvalds 			}
25881da177e4SLinus Torvalds 
25891da177e4SLinus Torvalds 			ips_freescb(ha, scb);
25901da177e4SLinus Torvalds 			break;
25911da177e4SLinus Torvalds 		default:
25921da177e4SLinus Torvalds 			break;
25931da177e4SLinus Torvalds 		}		/* end case */
25941da177e4SLinus Torvalds 
25951da177e4SLinus Torvalds 		if (ret != IPS_SUCCESS) {
25961da177e4SLinus Torvalds 			ha->num_ioctl--;
25971da177e4SLinus Torvalds 			continue;
25981da177e4SLinus Torvalds 		}
25991da177e4SLinus Torvalds 
26001da177e4SLinus Torvalds 		ret = ips_send_cmd(ha, scb);
26011da177e4SLinus Torvalds 
26021da177e4SLinus Torvalds 		if (ret == IPS_SUCCESS)
26031da177e4SLinus Torvalds 			ips_putq_scb_head(&ha->scb_activelist, scb);
26041da177e4SLinus Torvalds 		else
26051da177e4SLinus Torvalds 			ha->num_ioctl--;
26061da177e4SLinus Torvalds 
26071da177e4SLinus Torvalds 		switch (ret) {
26081da177e4SLinus Torvalds 		case IPS_FAILURE:
26091da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
26101da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
26111da177e4SLinus Torvalds 			}
26121da177e4SLinus Torvalds 
26131da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26141da177e4SLinus Torvalds 			break;
26151da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
26161da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26171da177e4SLinus Torvalds 			break;
26181da177e4SLinus Torvalds 		default:
26191da177e4SLinus Torvalds 			break;
26201da177e4SLinus Torvalds 		}		/* end case */
26211da177e4SLinus Torvalds 
26221da177e4SLinus Torvalds 	}
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	/*
26251da177e4SLinus Torvalds 	 * Send "Normal" I/O commands
26261da177e4SLinus Torvalds 	 */
26271da177e4SLinus Torvalds 
26281da177e4SLinus Torvalds 	p = ha->scb_waitlist.head;
26291da177e4SLinus Torvalds 	while ((p) && (scb = ips_getscb(ha))) {
2630422c0d61SJeff Garzik 		if ((scmd_channel(p) > 0)
26311da177e4SLinus Torvalds 		    && (ha->
2632422c0d61SJeff Garzik 			dcdb_active[scmd_channel(p) -
2633422c0d61SJeff Garzik 				    1] & (1 << scmd_id(p)))) {
26341da177e4SLinus Torvalds 			ips_freescb(ha, scb);
26351516b55dSHenne 			p = (struct scsi_cmnd *) p->host_scribble;
26361da177e4SLinus Torvalds 			continue;
26371da177e4SLinus Torvalds 		}
26381da177e4SLinus Torvalds 
26391da177e4SLinus Torvalds 		q = p;
26401da177e4SLinus Torvalds 		SC = ips_removeq_wait(&ha->scb_waitlist, q);
26411da177e4SLinus Torvalds 
26421da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2643c6a6c81cSAdrian Bunk 			spin_unlock(host->host_lock);	/* Unlock HA after command is taken off queue */
26441da177e4SLinus Torvalds 
26451da177e4SLinus Torvalds 		SC->result = DID_OK;
26461da177e4SLinus Torvalds 		SC->host_scribble = NULL;
26471da177e4SLinus Torvalds 
26481da177e4SLinus Torvalds 		scb->target_id = SC->device->id;
26491da177e4SLinus Torvalds 		scb->lun = SC->device->lun;
26501da177e4SLinus Torvalds 		scb->bus = SC->device->channel;
26511da177e4SLinus Torvalds 		scb->scsi_cmd = SC;
26521da177e4SLinus Torvalds 		scb->breakup = 0;
26531da177e4SLinus Torvalds 		scb->data_len = 0;
26541da177e4SLinus Torvalds 		scb->callback = ipsintr_done;
26551da177e4SLinus Torvalds 		scb->timeout = ips_cmd_timeout;
26561da177e4SLinus Torvalds 		memset(&scb->cmd, 0, 16);
26571da177e4SLinus Torvalds 
26581da177e4SLinus Torvalds 		/* copy in the CDB */
26591da177e4SLinus Torvalds 		memcpy(scb->cdb, SC->cmnd, SC->cmd_len);
26601da177e4SLinus Torvalds 
26612f4cf91cSFUJITA Tomonori                 scb->sg_count = scsi_dma_map(SC);
26622f4cf91cSFUJITA Tomonori                 BUG_ON(scb->sg_count < 0);
26632f4cf91cSFUJITA Tomonori 		if (scb->sg_count) {
26641da177e4SLinus Torvalds 			struct scatterlist *sg;
26651da177e4SLinus Torvalds 			int i;
26661da177e4SLinus Torvalds 
26671da177e4SLinus Torvalds 			scb->flags |= IPS_SCB_MAP_SG;
26682f4cf91cSFUJITA Tomonori 
26692f4cf91cSFUJITA Tomonori                         scsi_for_each_sg(SC, sg, scb->sg_count, i) {
26701da177e4SLinus Torvalds 				if (ips_fill_scb_sg_single
26712f4cf91cSFUJITA Tomonori 				    (ha, sg_dma_address(sg), scb, i,
26722f4cf91cSFUJITA Tomonori 				     sg_dma_len(sg)) < 0)
26731da177e4SLinus Torvalds 					break;
26741da177e4SLinus Torvalds 			}
26751da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
26761da177e4SLinus Torvalds 		} else {
26771da177e4SLinus Torvalds                         scb->data_busaddr = 0L;
26781da177e4SLinus Torvalds                         scb->sg_len = 0;
26791da177e4SLinus Torvalds                         scb->data_len = 0;
26801da177e4SLinus Torvalds                         scb->dcdb.transfer_length = 0;
26811da177e4SLinus Torvalds 		}
26821da177e4SLinus Torvalds 
26831da177e4SLinus Torvalds 		scb->dcdb.cmd_attribute =
26841da177e4SLinus Torvalds 		    ips_command_direction[scb->scsi_cmd->cmnd[0]];
26851da177e4SLinus Torvalds 
26861da177e4SLinus Torvalds 		/* Allow a WRITE BUFFER Command to Have no Data */
26871da177e4SLinus Torvalds 		/* This is Used by Tape Flash Utilites          */
26882f277d6aSJeff Garzik 		if ((scb->scsi_cmd->cmnd[0] == WRITE_BUFFER) &&
26892f277d6aSJeff Garzik 				(scb->data_len == 0))
26901da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute = 0;
26911da177e4SLinus Torvalds 
26921da177e4SLinus Torvalds 		if (!(scb->dcdb.cmd_attribute & 0x3))
26931da177e4SLinus Torvalds 			scb->dcdb.transfer_length = 0;
26941da177e4SLinus Torvalds 
26951da177e4SLinus Torvalds 		if (scb->data_len >= IPS_MAX_XFER) {
26961da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
26971da177e4SLinus Torvalds 			scb->dcdb.transfer_length = 0;
26981da177e4SLinus Torvalds 		}
26991da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON)
2700c6a6c81cSAdrian Bunk 			spin_lock(host->host_lock);
27011da177e4SLinus Torvalds 
27021da177e4SLinus Torvalds 		ret = ips_send_cmd(ha, scb);
27031da177e4SLinus Torvalds 
27041da177e4SLinus Torvalds 		switch (ret) {
27051da177e4SLinus Torvalds 		case IPS_SUCCESS:
27061da177e4SLinus Torvalds 			ips_putq_scb_head(&ha->scb_activelist, scb);
27071da177e4SLinus Torvalds 			break;
27081da177e4SLinus Torvalds 		case IPS_FAILURE:
27091da177e4SLinus Torvalds 			if (scb->scsi_cmd) {
27101da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_ERROR << 16;
27111da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
27121da177e4SLinus Torvalds 			}
27131da177e4SLinus Torvalds 
27141da177e4SLinus Torvalds 			if (scb->bus)
27151da177e4SLinus Torvalds 				ha->dcdb_active[scb->bus - 1] &=
27161da177e4SLinus Torvalds 				    ~(1 << scb->target_id);
27171da177e4SLinus Torvalds 
27181da177e4SLinus Torvalds 			ips_freescb(ha, scb);
27191da177e4SLinus Torvalds 			break;
27201da177e4SLinus Torvalds 		case IPS_SUCCESS_IMM:
27211da177e4SLinus Torvalds 			if (scb->scsi_cmd)
27221da177e4SLinus Torvalds 				scb->scsi_cmd->scsi_done(scb->scsi_cmd);
27231da177e4SLinus Torvalds 
27241da177e4SLinus Torvalds 			if (scb->bus)
27251da177e4SLinus Torvalds 				ha->dcdb_active[scb->bus - 1] &=
27261da177e4SLinus Torvalds 				    ~(1 << scb->target_id);
27271da177e4SLinus Torvalds 
27281da177e4SLinus Torvalds 			ips_freescb(ha, scb);
27291da177e4SLinus Torvalds 			break;
27301da177e4SLinus Torvalds 		default:
27311da177e4SLinus Torvalds 			break;
27321da177e4SLinus Torvalds 		}		/* end case */
27331da177e4SLinus Torvalds 
27341516b55dSHenne 		p = (struct scsi_cmnd *) p->host_scribble;
27351da177e4SLinus Torvalds 
27361da177e4SLinus Torvalds 	}			/* end while */
27371da177e4SLinus Torvalds 
27381da177e4SLinus Torvalds 	if (intr == IPS_INTR_ON)
2739c6a6c81cSAdrian Bunk 		spin_unlock(host->host_lock);
27401da177e4SLinus Torvalds }
27411da177e4SLinus Torvalds 
27421da177e4SLinus Torvalds /****************************************************************************/
27431da177e4SLinus Torvalds /*                                                                          */
27441da177e4SLinus Torvalds /* Routine Name: ips_putq_scb_head                                          */
27451da177e4SLinus Torvalds /*                                                                          */
27461da177e4SLinus Torvalds /* Routine Description:                                                     */
27471da177e4SLinus Torvalds /*                                                                          */
27481da177e4SLinus Torvalds /*   Add an item to the head of the queue                                   */
27491da177e4SLinus Torvalds /*                                                                          */
27501da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
27511da177e4SLinus Torvalds /*                                                                          */
27521da177e4SLinus Torvalds /****************************************************************************/
27531da177e4SLinus Torvalds static void
27541da177e4SLinus Torvalds ips_putq_scb_head(ips_scb_queue_t * queue, ips_scb_t * item)
27551da177e4SLinus Torvalds {
27561da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_scb_head", 1);
27571da177e4SLinus Torvalds 
27581da177e4SLinus Torvalds 	if (!item)
27591da177e4SLinus Torvalds 		return;
27601da177e4SLinus Torvalds 
27611da177e4SLinus Torvalds 	item->q_next = queue->head;
27621da177e4SLinus Torvalds 	queue->head = item;
27631da177e4SLinus Torvalds 
27641da177e4SLinus Torvalds 	if (!queue->tail)
27651da177e4SLinus Torvalds 		queue->tail = item;
27661da177e4SLinus Torvalds 
27671da177e4SLinus Torvalds 	queue->count++;
27681da177e4SLinus Torvalds }
27691da177e4SLinus Torvalds 
27701da177e4SLinus Torvalds /****************************************************************************/
27711da177e4SLinus Torvalds /*                                                                          */
27721da177e4SLinus Torvalds /* Routine Name: ips_removeq_scb_head                                       */
27731da177e4SLinus Torvalds /*                                                                          */
27741da177e4SLinus Torvalds /* Routine Description:                                                     */
27751da177e4SLinus Torvalds /*                                                                          */
27761da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
27771da177e4SLinus Torvalds /*                                                                          */
27781da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
27791da177e4SLinus Torvalds /*                                                                          */
27801da177e4SLinus Torvalds /****************************************************************************/
27811da177e4SLinus Torvalds static ips_scb_t *
27821da177e4SLinus Torvalds ips_removeq_scb_head(ips_scb_queue_t * queue)
27831da177e4SLinus Torvalds {
27841da177e4SLinus Torvalds 	ips_scb_t *item;
27851da177e4SLinus Torvalds 
27861da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_scb_head", 1);
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds 	item = queue->head;
27891da177e4SLinus Torvalds 
27901da177e4SLinus Torvalds 	if (!item) {
27911da177e4SLinus Torvalds 		return (NULL);
27921da177e4SLinus Torvalds 	}
27931da177e4SLinus Torvalds 
27941da177e4SLinus Torvalds 	queue->head = item->q_next;
27951da177e4SLinus Torvalds 	item->q_next = NULL;
27961da177e4SLinus Torvalds 
27971da177e4SLinus Torvalds 	if (queue->tail == item)
27981da177e4SLinus Torvalds 		queue->tail = NULL;
27991da177e4SLinus Torvalds 
28001da177e4SLinus Torvalds 	queue->count--;
28011da177e4SLinus Torvalds 
28021da177e4SLinus Torvalds 	return (item);
28031da177e4SLinus Torvalds }
28041da177e4SLinus Torvalds 
28051da177e4SLinus Torvalds /****************************************************************************/
28061da177e4SLinus Torvalds /*                                                                          */
28071da177e4SLinus Torvalds /* Routine Name: ips_removeq_scb                                            */
28081da177e4SLinus Torvalds /*                                                                          */
28091da177e4SLinus Torvalds /* Routine Description:                                                     */
28101da177e4SLinus Torvalds /*                                                                          */
28111da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
28121da177e4SLinus Torvalds /*                                                                          */
28131da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
28141da177e4SLinus Torvalds /*                                                                          */
28151da177e4SLinus Torvalds /****************************************************************************/
28161da177e4SLinus Torvalds static ips_scb_t *
28171da177e4SLinus Torvalds ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item)
28181da177e4SLinus Torvalds {
28191da177e4SLinus Torvalds 	ips_scb_t *p;
28201da177e4SLinus Torvalds 
28211da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_scb", 1);
28221da177e4SLinus Torvalds 
28231da177e4SLinus Torvalds 	if (!item)
28241da177e4SLinus Torvalds 		return (NULL);
28251da177e4SLinus Torvalds 
28261da177e4SLinus Torvalds 	if (item == queue->head) {
28271da177e4SLinus Torvalds 		return (ips_removeq_scb_head(queue));
28281da177e4SLinus Torvalds 	}
28291da177e4SLinus Torvalds 
28301da177e4SLinus Torvalds 	p = queue->head;
28311da177e4SLinus Torvalds 
28321da177e4SLinus Torvalds 	while ((p) && (item != p->q_next))
28331da177e4SLinus Torvalds 		p = p->q_next;
28341da177e4SLinus Torvalds 
28351da177e4SLinus Torvalds 	if (p) {
28361da177e4SLinus Torvalds 		/* found a match */
28371da177e4SLinus Torvalds 		p->q_next = item->q_next;
28381da177e4SLinus Torvalds 
28391da177e4SLinus Torvalds 		if (!item->q_next)
28401da177e4SLinus Torvalds 			queue->tail = p;
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 		item->q_next = NULL;
28431da177e4SLinus Torvalds 		queue->count--;
28441da177e4SLinus Torvalds 
28451da177e4SLinus Torvalds 		return (item);
28461da177e4SLinus Torvalds 	}
28471da177e4SLinus Torvalds 
28481da177e4SLinus Torvalds 	return (NULL);
28491da177e4SLinus Torvalds }
28501da177e4SLinus Torvalds 
28511da177e4SLinus Torvalds /****************************************************************************/
28521da177e4SLinus Torvalds /*                                                                          */
28531da177e4SLinus Torvalds /* Routine Name: ips_putq_wait_tail                                         */
28541da177e4SLinus Torvalds /*                                                                          */
28551da177e4SLinus Torvalds /* Routine Description:                                                     */
28561da177e4SLinus Torvalds /*                                                                          */
28571da177e4SLinus Torvalds /*   Add an item to the tail of the queue                                   */
28581da177e4SLinus Torvalds /*                                                                          */
28591da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
28601da177e4SLinus Torvalds /*                                                                          */
28611da177e4SLinus Torvalds /****************************************************************************/
2862ac6424b9SIngo Molnar static void ips_putq_wait_tail(ips_wait_queue_entry_t *queue, struct scsi_cmnd *item)
28631da177e4SLinus Torvalds {
28641da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_wait_tail", 1);
28651da177e4SLinus Torvalds 
28661da177e4SLinus Torvalds 	if (!item)
28671da177e4SLinus Torvalds 		return;
28681da177e4SLinus Torvalds 
28691da177e4SLinus Torvalds 	item->host_scribble = NULL;
28701da177e4SLinus Torvalds 
28711da177e4SLinus Torvalds 	if (queue->tail)
28721da177e4SLinus Torvalds 		queue->tail->host_scribble = (char *) item;
28731da177e4SLinus Torvalds 
28741da177e4SLinus Torvalds 	queue->tail = item;
28751da177e4SLinus Torvalds 
28761da177e4SLinus Torvalds 	if (!queue->head)
28771da177e4SLinus Torvalds 		queue->head = item;
28781da177e4SLinus Torvalds 
28791da177e4SLinus Torvalds 	queue->count++;
28801da177e4SLinus Torvalds }
28811da177e4SLinus Torvalds 
28821da177e4SLinus Torvalds /****************************************************************************/
28831da177e4SLinus Torvalds /*                                                                          */
28841da177e4SLinus Torvalds /* Routine Name: ips_removeq_wait_head                                      */
28851da177e4SLinus Torvalds /*                                                                          */
28861da177e4SLinus Torvalds /* Routine Description:                                                     */
28871da177e4SLinus Torvalds /*                                                                          */
28881da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
28891da177e4SLinus Torvalds /*                                                                          */
28901da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
28911da177e4SLinus Torvalds /*                                                                          */
28921da177e4SLinus Torvalds /****************************************************************************/
2893ac6424b9SIngo Molnar static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_entry_t *queue)
28941da177e4SLinus Torvalds {
28951516b55dSHenne 	struct scsi_cmnd *item;
28961da177e4SLinus Torvalds 
28971da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_wait_head", 1);
28981da177e4SLinus Torvalds 
28991da177e4SLinus Torvalds 	item = queue->head;
29001da177e4SLinus Torvalds 
29011da177e4SLinus Torvalds 	if (!item) {
29021da177e4SLinus Torvalds 		return (NULL);
29031da177e4SLinus Torvalds 	}
29041da177e4SLinus Torvalds 
29051516b55dSHenne 	queue->head = (struct scsi_cmnd *) item->host_scribble;
29061da177e4SLinus Torvalds 	item->host_scribble = NULL;
29071da177e4SLinus Torvalds 
29081da177e4SLinus Torvalds 	if (queue->tail == item)
29091da177e4SLinus Torvalds 		queue->tail = NULL;
29101da177e4SLinus Torvalds 
29111da177e4SLinus Torvalds 	queue->count--;
29121da177e4SLinus Torvalds 
29131da177e4SLinus Torvalds 	return (item);
29141da177e4SLinus Torvalds }
29151da177e4SLinus Torvalds 
29161da177e4SLinus Torvalds /****************************************************************************/
29171da177e4SLinus Torvalds /*                                                                          */
29181da177e4SLinus Torvalds /* Routine Name: ips_removeq_wait                                           */
29191da177e4SLinus Torvalds /*                                                                          */
29201da177e4SLinus Torvalds /* Routine Description:                                                     */
29211da177e4SLinus Torvalds /*                                                                          */
29221da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
29231da177e4SLinus Torvalds /*                                                                          */
29241da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
29251da177e4SLinus Torvalds /*                                                                          */
29261da177e4SLinus Torvalds /****************************************************************************/
2927ac6424b9SIngo Molnar static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_entry_t *queue,
29281516b55dSHenne 					  struct scsi_cmnd *item)
29291da177e4SLinus Torvalds {
29301516b55dSHenne 	struct scsi_cmnd *p;
29311da177e4SLinus Torvalds 
29321da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_wait", 1);
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds 	if (!item)
29351da177e4SLinus Torvalds 		return (NULL);
29361da177e4SLinus Torvalds 
29371da177e4SLinus Torvalds 	if (item == queue->head) {
29381da177e4SLinus Torvalds 		return (ips_removeq_wait_head(queue));
29391da177e4SLinus Torvalds 	}
29401da177e4SLinus Torvalds 
29411da177e4SLinus Torvalds 	p = queue->head;
29421da177e4SLinus Torvalds 
29431516b55dSHenne 	while ((p) && (item != (struct scsi_cmnd *) p->host_scribble))
29441516b55dSHenne 		p = (struct scsi_cmnd *) p->host_scribble;
29451da177e4SLinus Torvalds 
29461da177e4SLinus Torvalds 	if (p) {
29471da177e4SLinus Torvalds 		/* found a match */
29481da177e4SLinus Torvalds 		p->host_scribble = item->host_scribble;
29491da177e4SLinus Torvalds 
29501da177e4SLinus Torvalds 		if (!item->host_scribble)
29511da177e4SLinus Torvalds 			queue->tail = p;
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds 		item->host_scribble = NULL;
29541da177e4SLinus Torvalds 		queue->count--;
29551da177e4SLinus Torvalds 
29561da177e4SLinus Torvalds 		return (item);
29571da177e4SLinus Torvalds 	}
29581da177e4SLinus Torvalds 
29591da177e4SLinus Torvalds 	return (NULL);
29601da177e4SLinus Torvalds }
29611da177e4SLinus Torvalds 
29621da177e4SLinus Torvalds /****************************************************************************/
29631da177e4SLinus Torvalds /*                                                                          */
29641da177e4SLinus Torvalds /* Routine Name: ips_putq_copp_tail                                         */
29651da177e4SLinus Torvalds /*                                                                          */
29661da177e4SLinus Torvalds /* Routine Description:                                                     */
29671da177e4SLinus Torvalds /*                                                                          */
29681da177e4SLinus Torvalds /*   Add an item to the tail of the queue                                   */
29691da177e4SLinus Torvalds /*                                                                          */
29701da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
29711da177e4SLinus Torvalds /*                                                                          */
29721da177e4SLinus Torvalds /****************************************************************************/
29731da177e4SLinus Torvalds static void
29741da177e4SLinus Torvalds ips_putq_copp_tail(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
29751da177e4SLinus Torvalds {
29761da177e4SLinus Torvalds 	METHOD_TRACE("ips_putq_copp_tail", 1);
29771da177e4SLinus Torvalds 
29781da177e4SLinus Torvalds 	if (!item)
29791da177e4SLinus Torvalds 		return;
29801da177e4SLinus Torvalds 
29811da177e4SLinus Torvalds 	item->next = NULL;
29821da177e4SLinus Torvalds 
29831da177e4SLinus Torvalds 	if (queue->tail)
29841da177e4SLinus Torvalds 		queue->tail->next = item;
29851da177e4SLinus Torvalds 
29861da177e4SLinus Torvalds 	queue->tail = item;
29871da177e4SLinus Torvalds 
29881da177e4SLinus Torvalds 	if (!queue->head)
29891da177e4SLinus Torvalds 		queue->head = item;
29901da177e4SLinus Torvalds 
29911da177e4SLinus Torvalds 	queue->count++;
29921da177e4SLinus Torvalds }
29931da177e4SLinus Torvalds 
29941da177e4SLinus Torvalds /****************************************************************************/
29951da177e4SLinus Torvalds /*                                                                          */
29961da177e4SLinus Torvalds /* Routine Name: ips_removeq_copp_head                                      */
29971da177e4SLinus Torvalds /*                                                                          */
29981da177e4SLinus Torvalds /* Routine Description:                                                     */
29991da177e4SLinus Torvalds /*                                                                          */
30001da177e4SLinus Torvalds /*   Remove the head of the queue                                           */
30011da177e4SLinus Torvalds /*                                                                          */
30021da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
30031da177e4SLinus Torvalds /*                                                                          */
30041da177e4SLinus Torvalds /****************************************************************************/
30051da177e4SLinus Torvalds static ips_copp_wait_item_t *
30061da177e4SLinus Torvalds ips_removeq_copp_head(ips_copp_queue_t * queue)
30071da177e4SLinus Torvalds {
30081da177e4SLinus Torvalds 	ips_copp_wait_item_t *item;
30091da177e4SLinus Torvalds 
30101da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_copp_head", 1);
30111da177e4SLinus Torvalds 
30121da177e4SLinus Torvalds 	item = queue->head;
30131da177e4SLinus Torvalds 
30141da177e4SLinus Torvalds 	if (!item) {
30151da177e4SLinus Torvalds 		return (NULL);
30161da177e4SLinus Torvalds 	}
30171da177e4SLinus Torvalds 
30181da177e4SLinus Torvalds 	queue->head = item->next;
30191da177e4SLinus Torvalds 	item->next = NULL;
30201da177e4SLinus Torvalds 
30211da177e4SLinus Torvalds 	if (queue->tail == item)
30221da177e4SLinus Torvalds 		queue->tail = NULL;
30231da177e4SLinus Torvalds 
30241da177e4SLinus Torvalds 	queue->count--;
30251da177e4SLinus Torvalds 
30261da177e4SLinus Torvalds 	return (item);
30271da177e4SLinus Torvalds }
30281da177e4SLinus Torvalds 
30291da177e4SLinus Torvalds /****************************************************************************/
30301da177e4SLinus Torvalds /*                                                                          */
30311da177e4SLinus Torvalds /* Routine Name: ips_removeq_copp                                           */
30321da177e4SLinus Torvalds /*                                                                          */
30331da177e4SLinus Torvalds /* Routine Description:                                                     */
30341da177e4SLinus Torvalds /*                                                                          */
30351da177e4SLinus Torvalds /*   Remove an item from a queue                                            */
30361da177e4SLinus Torvalds /*                                                                          */
30371da177e4SLinus Torvalds /* ASSUMED to be called from within the HA lock                             */
30381da177e4SLinus Torvalds /*                                                                          */
30391da177e4SLinus Torvalds /****************************************************************************/
30401da177e4SLinus Torvalds static ips_copp_wait_item_t *
30411da177e4SLinus Torvalds ips_removeq_copp(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
30421da177e4SLinus Torvalds {
30431da177e4SLinus Torvalds 	ips_copp_wait_item_t *p;
30441da177e4SLinus Torvalds 
30451da177e4SLinus Torvalds 	METHOD_TRACE("ips_removeq_copp", 1);
30461da177e4SLinus Torvalds 
30471da177e4SLinus Torvalds 	if (!item)
30481da177e4SLinus Torvalds 		return (NULL);
30491da177e4SLinus Torvalds 
30501da177e4SLinus Torvalds 	if (item == queue->head) {
30511da177e4SLinus Torvalds 		return (ips_removeq_copp_head(queue));
30521da177e4SLinus Torvalds 	}
30531da177e4SLinus Torvalds 
30541da177e4SLinus Torvalds 	p = queue->head;
30551da177e4SLinus Torvalds 
30561da177e4SLinus Torvalds 	while ((p) && (item != p->next))
30571da177e4SLinus Torvalds 		p = p->next;
30581da177e4SLinus Torvalds 
30591da177e4SLinus Torvalds 	if (p) {
30601da177e4SLinus Torvalds 		/* found a match */
30611da177e4SLinus Torvalds 		p->next = item->next;
30621da177e4SLinus Torvalds 
30631da177e4SLinus Torvalds 		if (!item->next)
30641da177e4SLinus Torvalds 			queue->tail = p;
30651da177e4SLinus Torvalds 
30661da177e4SLinus Torvalds 		item->next = NULL;
30671da177e4SLinus Torvalds 		queue->count--;
30681da177e4SLinus Torvalds 
30691da177e4SLinus Torvalds 		return (item);
30701da177e4SLinus Torvalds 	}
30711da177e4SLinus Torvalds 
30721da177e4SLinus Torvalds 	return (NULL);
30731da177e4SLinus Torvalds }
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds /****************************************************************************/
30761da177e4SLinus Torvalds /*                                                                          */
30771da177e4SLinus Torvalds /* Routine Name: ipsintr_blocking                                           */
30781da177e4SLinus Torvalds /*                                                                          */
30791da177e4SLinus Torvalds /* Routine Description:                                                     */
30801da177e4SLinus Torvalds /*                                                                          */
30811da177e4SLinus Torvalds /*   Finalize an interrupt for internal commands                            */
30821da177e4SLinus Torvalds /*                                                                          */
30831da177e4SLinus Torvalds /****************************************************************************/
30841da177e4SLinus Torvalds static void
30851da177e4SLinus Torvalds ipsintr_blocking(ips_ha_t * ha, ips_scb_t * scb)
30861da177e4SLinus Torvalds {
30871da177e4SLinus Torvalds 	METHOD_TRACE("ipsintr_blocking", 2);
30881da177e4SLinus Torvalds 
30891da177e4SLinus Torvalds 	ips_freescb(ha, scb);
30901da177e4SLinus Torvalds 	if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) {
30911da177e4SLinus Torvalds 		ha->waitflag = FALSE;
30921da177e4SLinus Torvalds 
30931da177e4SLinus Torvalds 		return;
30941da177e4SLinus Torvalds 	}
30951da177e4SLinus Torvalds }
30961da177e4SLinus Torvalds 
30971da177e4SLinus Torvalds /****************************************************************************/
30981da177e4SLinus Torvalds /*                                                                          */
30991da177e4SLinus Torvalds /* Routine Name: ipsintr_done                                               */
31001da177e4SLinus Torvalds /*                                                                          */
31011da177e4SLinus Torvalds /* Routine Description:                                                     */
31021da177e4SLinus Torvalds /*                                                                          */
31031da177e4SLinus Torvalds /*   Finalize an interrupt for non-internal commands                        */
31041da177e4SLinus Torvalds /*                                                                          */
31051da177e4SLinus Torvalds /****************************************************************************/
31061da177e4SLinus Torvalds static void
31071da177e4SLinus Torvalds ipsintr_done(ips_ha_t * ha, ips_scb_t * scb)
31081da177e4SLinus Torvalds {
31091da177e4SLinus Torvalds 	METHOD_TRACE("ipsintr_done", 2);
31101da177e4SLinus Torvalds 
31111da177e4SLinus Torvalds 	if (!scb) {
31121da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
31131da177e4SLinus Torvalds 			   "Spurious interrupt; scb NULL.\n");
31141da177e4SLinus Torvalds 
31151da177e4SLinus Torvalds 		return;
31161da177e4SLinus Torvalds 	}
31171da177e4SLinus Torvalds 
31181da177e4SLinus Torvalds 	if (scb->scsi_cmd == NULL) {
31191da177e4SLinus Torvalds 		/* unexpected interrupt */
31201da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
31211da177e4SLinus Torvalds 			   "Spurious interrupt; scsi_cmd not set.\n");
31221da177e4SLinus Torvalds 
31231da177e4SLinus Torvalds 		return;
31241da177e4SLinus Torvalds 	}
31251da177e4SLinus Torvalds 
31261da177e4SLinus Torvalds 	ips_done(ha, scb);
31271da177e4SLinus Torvalds }
31281da177e4SLinus Torvalds 
31291da177e4SLinus Torvalds /****************************************************************************/
31301da177e4SLinus Torvalds /*                                                                          */
31311da177e4SLinus Torvalds /* Routine Name: ips_done                                                   */
31321da177e4SLinus Torvalds /*                                                                          */
31331da177e4SLinus Torvalds /* Routine Description:                                                     */
31341da177e4SLinus Torvalds /*                                                                          */
31351da177e4SLinus Torvalds /*   Do housekeeping on completed commands                                  */
31361da177e4SLinus Torvalds /*  ASSUMED to be called form within the request lock                       */
31371da177e4SLinus Torvalds /****************************************************************************/
31381da177e4SLinus Torvalds static void
31391da177e4SLinus Torvalds ips_done(ips_ha_t * ha, ips_scb_t * scb)
31401da177e4SLinus Torvalds {
31411da177e4SLinus Torvalds 	int ret;
31421da177e4SLinus Torvalds 
31431da177e4SLinus Torvalds 	METHOD_TRACE("ips_done", 1);
31441da177e4SLinus Torvalds 
31451da177e4SLinus Torvalds 	if (!scb)
31461da177e4SLinus Torvalds 		return;
31471da177e4SLinus Torvalds 
31481da177e4SLinus Torvalds 	if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) {
31491da177e4SLinus Torvalds 		ips_cleanup_passthru(ha, scb);
31501da177e4SLinus Torvalds 		ha->num_ioctl--;
31511da177e4SLinus Torvalds 	} else {
31521da177e4SLinus Torvalds 		/*
31531da177e4SLinus Torvalds 		 * Check to see if this command had too much
31541da177e4SLinus Torvalds 		 * data and had to be broke up.  If so, queue
31551da177e4SLinus Torvalds 		 * the rest of the data and continue.
31561da177e4SLinus Torvalds 		 */
31571da177e4SLinus Torvalds 		if ((scb->breakup) || (scb->sg_break)) {
31582f4cf91cSFUJITA Tomonori                         struct scatterlist *sg;
3159e0eaf888SFUJITA Tomonori                         int i, sg_dma_index, ips_sg_index = 0;
31602f4cf91cSFUJITA Tomonori 
31611da177e4SLinus Torvalds 			/* we had a data breakup */
31621da177e4SLinus Torvalds 			scb->data_len = 0;
31631da177e4SLinus Torvalds 
31642f4cf91cSFUJITA Tomonori                         sg = scsi_sglist(scb->scsi_cmd);
31651da177e4SLinus Torvalds 
31661da177e4SLinus Torvalds                         /* Spin forward to last dma chunk */
31671da177e4SLinus Torvalds                         sg_dma_index = scb->breakup;
3168e0eaf888SFUJITA Tomonori                         for (i = 0; i < scb->breakup; i++)
3169e0eaf888SFUJITA Tomonori                                 sg = sg_next(sg);
31701da177e4SLinus Torvalds 
31711da177e4SLinus Torvalds 			/* Take care of possible partial on last chunk */
31721da177e4SLinus Torvalds                         ips_fill_scb_sg_single(ha,
3173e0eaf888SFUJITA Tomonori                                                sg_dma_address(sg),
31741da177e4SLinus Torvalds                                                scb, ips_sg_index++,
3175e0eaf888SFUJITA Tomonori                                                sg_dma_len(sg));
31761da177e4SLinus Torvalds 
31772f4cf91cSFUJITA Tomonori                         for (; sg_dma_index < scsi_sg_count(scb->scsi_cmd);
3178e0eaf888SFUJITA Tomonori                              sg_dma_index++, sg = sg_next(sg)) {
31791da177e4SLinus Torvalds                                 if (ips_fill_scb_sg_single
31801da177e4SLinus Torvalds                                     (ha,
3181e0eaf888SFUJITA Tomonori                                      sg_dma_address(sg),
31821da177e4SLinus Torvalds                                      scb, ips_sg_index++,
3183e0eaf888SFUJITA Tomonori                                      sg_dma_len(sg)) < 0)
31841da177e4SLinus Torvalds                                         break;
31851da177e4SLinus Torvalds                         }
31861da177e4SLinus Torvalds 
31871da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
31881da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |=
31891da177e4SLinus Torvalds 			    ips_command_direction[scb->scsi_cmd->cmnd[0]];
31901da177e4SLinus Torvalds 
31911da177e4SLinus Torvalds 			if (!(scb->dcdb.cmd_attribute & 0x3))
31921da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
31931da177e4SLinus Torvalds 
31941da177e4SLinus Torvalds 			if (scb->data_len >= IPS_MAX_XFER) {
31951da177e4SLinus Torvalds 				scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
31961da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
31971da177e4SLinus Torvalds 			}
31981da177e4SLinus Torvalds 
31991da177e4SLinus Torvalds 			ret = ips_send_cmd(ha, scb);
32001da177e4SLinus Torvalds 
32011da177e4SLinus Torvalds 			switch (ret) {
32021da177e4SLinus Torvalds 			case IPS_FAILURE:
32031da177e4SLinus Torvalds 				if (scb->scsi_cmd) {
32041da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_ERROR << 16;
32051da177e4SLinus Torvalds 					scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32061da177e4SLinus Torvalds 				}
32071da177e4SLinus Torvalds 
32081da177e4SLinus Torvalds 				ips_freescb(ha, scb);
32091da177e4SLinus Torvalds 				break;
32101da177e4SLinus Torvalds 			case IPS_SUCCESS_IMM:
32111da177e4SLinus Torvalds 				if (scb->scsi_cmd) {
32121da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_ERROR << 16;
32131da177e4SLinus Torvalds 					scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32141da177e4SLinus Torvalds 				}
32151da177e4SLinus Torvalds 
32161da177e4SLinus Torvalds 				ips_freescb(ha, scb);
32171da177e4SLinus Torvalds 				break;
32181da177e4SLinus Torvalds 			default:
32191da177e4SLinus Torvalds 				break;
32201da177e4SLinus Torvalds 			}	/* end case */
32211da177e4SLinus Torvalds 
32221da177e4SLinus Torvalds 			return;
32231da177e4SLinus Torvalds 		}
32241da177e4SLinus Torvalds 	}			/* end if passthru */
32251da177e4SLinus Torvalds 
32261da177e4SLinus Torvalds 	if (scb->bus) {
32271da177e4SLinus Torvalds 		ha->dcdb_active[scb->bus - 1] &= ~(1 << scb->target_id);
32281da177e4SLinus Torvalds 	}
32291da177e4SLinus Torvalds 
32301da177e4SLinus Torvalds 	scb->scsi_cmd->scsi_done(scb->scsi_cmd);
32311da177e4SLinus Torvalds 
32321da177e4SLinus Torvalds 	ips_freescb(ha, scb);
32331da177e4SLinus Torvalds }
32341da177e4SLinus Torvalds 
32351da177e4SLinus Torvalds /****************************************************************************/
32361da177e4SLinus Torvalds /*                                                                          */
32371da177e4SLinus Torvalds /* Routine Name: ips_map_status                                             */
32381da177e4SLinus Torvalds /*                                                                          */
32391da177e4SLinus Torvalds /* Routine Description:                                                     */
32401da177e4SLinus Torvalds /*                                                                          */
32411da177e4SLinus Torvalds /*   Map Controller Error codes to Linux Error Codes                        */
32421da177e4SLinus Torvalds /*                                                                          */
32431da177e4SLinus Torvalds /****************************************************************************/
32441da177e4SLinus Torvalds static int
32451da177e4SLinus Torvalds ips_map_status(ips_ha_t * ha, ips_scb_t * scb, ips_stat_t * sp)
32461da177e4SLinus Torvalds {
32471da177e4SLinus Torvalds 	int errcode;
32481da177e4SLinus Torvalds 	int device_error;
32491da177e4SLinus Torvalds 	uint32_t transfer_len;
32501da177e4SLinus Torvalds 	IPS_DCDB_TABLE_TAPE *tapeDCDB;
3251a5b3c86eSJack Hammer 	IPS_SCSI_INQ_DATA inquiryData;
32521da177e4SLinus Torvalds 
32531da177e4SLinus Torvalds 	METHOD_TRACE("ips_map_status", 1);
32541da177e4SLinus Torvalds 
32551da177e4SLinus Torvalds 	if (scb->bus) {
32561da177e4SLinus Torvalds 		DEBUG_VAR(2,
32571da177e4SLinus Torvalds 			  "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
32581da177e4SLinus Torvalds 			  ips_name, ha->host_num,
32591da177e4SLinus Torvalds 			  scb->scsi_cmd->device->channel,
32601da177e4SLinus Torvalds 			  scb->scsi_cmd->device->id, scb->scsi_cmd->device->lun,
32611da177e4SLinus Torvalds 			  scb->basic_status, scb->extended_status,
32621da177e4SLinus Torvalds 			  scb->extended_status ==
32631da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
32641da177e4SLinus Torvalds 			  scb->extended_status ==
32651da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
32661da177e4SLinus Torvalds 			  scb->extended_status ==
32671da177e4SLinus Torvalds 			  IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
32681da177e4SLinus Torvalds 	}
32691da177e4SLinus Torvalds 
32701da177e4SLinus Torvalds 	/* default driver error */
32711da177e4SLinus Torvalds 	errcode = DID_ERROR;
32721da177e4SLinus Torvalds 	device_error = 0;
32731da177e4SLinus Torvalds 
32741da177e4SLinus Torvalds 	switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
32751da177e4SLinus Torvalds 	case IPS_CMD_TIMEOUT:
32761da177e4SLinus Torvalds 		errcode = DID_TIME_OUT;
32771da177e4SLinus Torvalds 		break;
32781da177e4SLinus Torvalds 
32791da177e4SLinus Torvalds 	case IPS_INVAL_OPCO:
32801da177e4SLinus Torvalds 	case IPS_INVAL_CMD_BLK:
32811da177e4SLinus Torvalds 	case IPS_INVAL_PARM_BLK:
32821da177e4SLinus Torvalds 	case IPS_LD_ERROR:
32831da177e4SLinus Torvalds 	case IPS_CMD_CMPLT_WERROR:
32841da177e4SLinus Torvalds 		break;
32851da177e4SLinus Torvalds 
32861da177e4SLinus Torvalds 	case IPS_PHYS_DRV_ERROR:
32871da177e4SLinus Torvalds 		switch (scb->extended_status) {
32881da177e4SLinus Torvalds 		case IPS_ERR_SEL_TO:
32891da177e4SLinus Torvalds 			if (scb->bus)
32901da177e4SLinus Torvalds 				errcode = DID_NO_CONNECT;
32911da177e4SLinus Torvalds 
32921da177e4SLinus Torvalds 			break;
32931da177e4SLinus Torvalds 
32941da177e4SLinus Torvalds 		case IPS_ERR_OU_RUN:
32951da177e4SLinus Torvalds 			if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) ||
32961da177e4SLinus Torvalds 			    (scb->cmd.dcdb.op_code ==
32971da177e4SLinus Torvalds 			     IPS_CMD_EXTENDED_DCDB_SG)) {
32981da177e4SLinus Torvalds 				tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
32991da177e4SLinus Torvalds 				transfer_len = tapeDCDB->transfer_length;
33001da177e4SLinus Torvalds 			} else {
33011da177e4SLinus Torvalds 				transfer_len =
33021da177e4SLinus Torvalds 				    (uint32_t) scb->dcdb.transfer_length;
33031da177e4SLinus Torvalds 			}
33041da177e4SLinus Torvalds 
33051da177e4SLinus Torvalds 			if ((scb->bus) && (transfer_len < scb->data_len)) {
33061da177e4SLinus Torvalds 				/* Underrun - set default to no error */
33071da177e4SLinus Torvalds 				errcode = DID_OK;
33081da177e4SLinus Torvalds 
33091da177e4SLinus Torvalds 				/* Restrict access to physical DASD */
3310a5b3c86eSJack Hammer 				if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
3311a5b3c86eSJack Hammer 				    ips_scmd_buf_read(scb->scsi_cmd,
3312a5b3c86eSJack Hammer                                       &inquiryData, sizeof (inquiryData));
3313a5b3c86eSJack Hammer  				    if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK) {
33141da177e4SLinus Torvalds 				        errcode = DID_TIME_OUT;
33151da177e4SLinus Torvalds 				        break;
33161da177e4SLinus Torvalds 				    }
3317a5b3c86eSJack Hammer 				}
33181da177e4SLinus Torvalds 			} else
33191da177e4SLinus Torvalds 				errcode = DID_ERROR;
33201da177e4SLinus Torvalds 
33211da177e4SLinus Torvalds 			break;
33221da177e4SLinus Torvalds 
33231da177e4SLinus Torvalds 		case IPS_ERR_RECOVERY:
33241da177e4SLinus Torvalds 			/* don't fail recovered errors */
33251da177e4SLinus Torvalds 			if (scb->bus)
33261da177e4SLinus Torvalds 				errcode = DID_OK;
33271da177e4SLinus Torvalds 
33281da177e4SLinus Torvalds 			break;
33291da177e4SLinus Torvalds 
33301da177e4SLinus Torvalds 		case IPS_ERR_HOST_RESET:
33311da177e4SLinus Torvalds 		case IPS_ERR_DEV_RESET:
33321da177e4SLinus Torvalds 			errcode = DID_RESET;
33331da177e4SLinus Torvalds 			break;
33341da177e4SLinus Torvalds 
33351da177e4SLinus Torvalds 		case IPS_ERR_CKCOND:
33361da177e4SLinus Torvalds 			if (scb->bus) {
33371da177e4SLinus Torvalds 				if ((scb->cmd.dcdb.op_code ==
33381da177e4SLinus Torvalds 				     IPS_CMD_EXTENDED_DCDB)
33391da177e4SLinus Torvalds 				    || (scb->cmd.dcdb.op_code ==
33401da177e4SLinus Torvalds 					IPS_CMD_EXTENDED_DCDB_SG)) {
33411da177e4SLinus Torvalds 					tapeDCDB =
33421da177e4SLinus Torvalds 					    (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
33431da177e4SLinus Torvalds 					memcpy(scb->scsi_cmd->sense_buffer,
33441da177e4SLinus Torvalds 					       tapeDCDB->sense_info,
3345b80ca4f7SFUJITA Tomonori 					       SCSI_SENSE_BUFFERSIZE);
33461da177e4SLinus Torvalds 				} else {
33471da177e4SLinus Torvalds 					memcpy(scb->scsi_cmd->sense_buffer,
33481da177e4SLinus Torvalds 					       scb->dcdb.sense_info,
3349b80ca4f7SFUJITA Tomonori 					       SCSI_SENSE_BUFFERSIZE);
33501da177e4SLinus Torvalds 				}
33511da177e4SLinus Torvalds 				device_error = 2;	/* check condition */
33521da177e4SLinus Torvalds 			}
33531da177e4SLinus Torvalds 
33541da177e4SLinus Torvalds 			errcode = DID_OK;
33551da177e4SLinus Torvalds 
33561da177e4SLinus Torvalds 			break;
33571da177e4SLinus Torvalds 
33581da177e4SLinus Torvalds 		default:
33591da177e4SLinus Torvalds 			errcode = DID_ERROR;
33601da177e4SLinus Torvalds 			break;
33611da177e4SLinus Torvalds 
33621da177e4SLinus Torvalds 		}		/* end switch */
33631da177e4SLinus Torvalds 	}			/* end switch */
33641da177e4SLinus Torvalds 
33651da177e4SLinus Torvalds 	scb->scsi_cmd->result = device_error | (errcode << 16);
33661da177e4SLinus Torvalds 
33671da177e4SLinus Torvalds 	return (1);
33681da177e4SLinus Torvalds }
33691da177e4SLinus Torvalds 
33701da177e4SLinus Torvalds /****************************************************************************/
33711da177e4SLinus Torvalds /*                                                                          */
33721da177e4SLinus Torvalds /* Routine Name: ips_send_wait                                              */
33731da177e4SLinus Torvalds /*                                                                          */
33741da177e4SLinus Torvalds /* Routine Description:                                                     */
33751da177e4SLinus Torvalds /*                                                                          */
33761da177e4SLinus Torvalds /*   Send a command to the controller and wait for it to return             */
33771da177e4SLinus Torvalds /*                                                                          */
33781da177e4SLinus Torvalds /*   The FFDC Time Stamp use this function for the callback, but doesn't    */
33791da177e4SLinus Torvalds /*   actually need to wait.                                                 */
33801da177e4SLinus Torvalds /****************************************************************************/
33811da177e4SLinus Torvalds static int
33821da177e4SLinus Torvalds ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr)
33831da177e4SLinus Torvalds {
33841da177e4SLinus Torvalds 	int ret;
33851da177e4SLinus Torvalds 
33861da177e4SLinus Torvalds 	METHOD_TRACE("ips_send_wait", 1);
33871da177e4SLinus Torvalds 
33881da177e4SLinus Torvalds 	if (intr != IPS_FFDC) {	/* Won't be Waiting if this is a Time Stamp */
33891da177e4SLinus Torvalds 		ha->waitflag = TRUE;
33901da177e4SLinus Torvalds 		ha->cmd_in_progress = scb->cdb[0];
33911da177e4SLinus Torvalds 	}
33921da177e4SLinus Torvalds 	scb->callback = ipsintr_blocking;
33931da177e4SLinus Torvalds 	ret = ips_send_cmd(ha, scb);
33941da177e4SLinus Torvalds 
33951da177e4SLinus Torvalds 	if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
33961da177e4SLinus Torvalds 		return (ret);
33971da177e4SLinus Torvalds 
33981da177e4SLinus Torvalds 	if (intr != IPS_FFDC)	/* Don't Wait around if this is a Time Stamp */
33991da177e4SLinus Torvalds 		ret = ips_wait(ha, timeout, intr);
34001da177e4SLinus Torvalds 
34011da177e4SLinus Torvalds 	return (ret);
34021da177e4SLinus Torvalds }
34031da177e4SLinus Torvalds 
34041da177e4SLinus Torvalds /****************************************************************************/
34051da177e4SLinus Torvalds /*                                                                          */
34061da177e4SLinus Torvalds /* Routine Name: ips_scmd_buf_write                                         */
34071da177e4SLinus Torvalds /*                                                                          */
34081da177e4SLinus Torvalds /* Routine Description:                                                     */
34091516b55dSHenne /*  Write data to struct scsi_cmnd request_buffer at proper offsets	    */
34101da177e4SLinus Torvalds /****************************************************************************/
34111da177e4SLinus Torvalds static void
34121516b55dSHenne ips_scmd_buf_write(struct scsi_cmnd *scmd, void *data, unsigned int count)
34131da177e4SLinus Torvalds {
3414a3632fa3SJack Hammer 	unsigned long flags;
34152f4cf91cSFUJITA Tomonori 
3416a3632fa3SJack Hammer 	local_irq_save(flags);
34176690bae7SFUJITA Tomonori 	scsi_sg_copy_from_buffer(scmd, data, count);
3418a3632fa3SJack Hammer 	local_irq_restore(flags);
34191da177e4SLinus Torvalds }
34201da177e4SLinus Torvalds 
34211da177e4SLinus Torvalds /****************************************************************************/
34221da177e4SLinus Torvalds /*                                                                          */
34231da177e4SLinus Torvalds /* Routine Name: ips_scmd_buf_read                                          */
34241da177e4SLinus Torvalds /*                                                                          */
34251da177e4SLinus Torvalds /* Routine Description:                                                     */
34261516b55dSHenne /*  Copy data from a struct scsi_cmnd to a new, linear buffer		    */
34271da177e4SLinus Torvalds /****************************************************************************/
34281da177e4SLinus Torvalds static void
34291516b55dSHenne ips_scmd_buf_read(struct scsi_cmnd *scmd, void *data, unsigned int count)
34301da177e4SLinus Torvalds {
3431a3632fa3SJack Hammer 	unsigned long flags;
34322f4cf91cSFUJITA Tomonori 
3433a3632fa3SJack Hammer 	local_irq_save(flags);
34346690bae7SFUJITA Tomonori 	scsi_sg_copy_to_buffer(scmd, data, count);
3435a3632fa3SJack Hammer 	local_irq_restore(flags);
34361da177e4SLinus Torvalds }
34371da177e4SLinus Torvalds 
34381da177e4SLinus Torvalds /****************************************************************************/
34391da177e4SLinus Torvalds /*                                                                          */
34401da177e4SLinus Torvalds /* Routine Name: ips_send_cmd                                               */
34411da177e4SLinus Torvalds /*                                                                          */
34421da177e4SLinus Torvalds /* Routine Description:                                                     */
34431da177e4SLinus Torvalds /*                                                                          */
34441da177e4SLinus Torvalds /*   Map SCSI commands to ServeRAID commands for logical drives             */
34451da177e4SLinus Torvalds /*                                                                          */
34461da177e4SLinus Torvalds /****************************************************************************/
34471da177e4SLinus Torvalds static int
34481da177e4SLinus Torvalds ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
34491da177e4SLinus Torvalds {
34501da177e4SLinus Torvalds 	int ret;
34511da177e4SLinus Torvalds 	char *sp;
34521da177e4SLinus Torvalds 	int device_error;
34531da177e4SLinus Torvalds 	IPS_DCDB_TABLE_TAPE *tapeDCDB;
34541da177e4SLinus Torvalds 	int TimeOut;
34551da177e4SLinus Torvalds 
34561da177e4SLinus Torvalds 	METHOD_TRACE("ips_send_cmd", 1);
34571da177e4SLinus Torvalds 
34581da177e4SLinus Torvalds 	ret = IPS_SUCCESS;
34591da177e4SLinus Torvalds 
34601da177e4SLinus Torvalds 	if (!scb->scsi_cmd) {
34611da177e4SLinus Torvalds 		/* internal command */
34621da177e4SLinus Torvalds 
34631da177e4SLinus Torvalds 		if (scb->bus > 0) {
34641da177e4SLinus Torvalds 			/* Controller commands can't be issued */
34651da177e4SLinus Torvalds 			/* to real devices -- fail them        */
34661da177e4SLinus Torvalds 			if ((ha->waitflag == TRUE) &&
34671da177e4SLinus Torvalds 			    (ha->cmd_in_progress == scb->cdb[0])) {
34681da177e4SLinus Torvalds 				ha->waitflag = FALSE;
34691da177e4SLinus Torvalds 			}
34701da177e4SLinus Torvalds 
34711da177e4SLinus Torvalds 			return (1);
34721da177e4SLinus Torvalds 		}
34731da177e4SLinus Torvalds 	} else if ((scb->bus == 0) && (!ips_is_passthru(scb->scsi_cmd))) {
34741da177e4SLinus Torvalds 		/* command to logical bus -- interpret */
34751da177e4SLinus Torvalds 		ret = IPS_SUCCESS_IMM;
34761da177e4SLinus Torvalds 
34771da177e4SLinus Torvalds 		switch (scb->scsi_cmd->cmnd[0]) {
34781da177e4SLinus Torvalds 		case ALLOW_MEDIUM_REMOVAL:
34791da177e4SLinus Torvalds 		case REZERO_UNIT:
34801da177e4SLinus Torvalds 		case ERASE:
34811da177e4SLinus Torvalds 		case WRITE_FILEMARKS:
34821da177e4SLinus Torvalds 		case SPACE:
34831da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_ERROR << 16;
34841da177e4SLinus Torvalds 			break;
34851da177e4SLinus Torvalds 
34861da177e4SLinus Torvalds 		case START_STOP:
34871da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
34885d25ff7aSGustavo A. R. Silva 			break;
34891da177e4SLinus Torvalds 
34901da177e4SLinus Torvalds 		case TEST_UNIT_READY:
34911da177e4SLinus Torvalds 		case INQUIRY:
34921da177e4SLinus Torvalds 			if (scb->target_id == IPS_ADAPTER_ID) {
34931da177e4SLinus Torvalds 				/*
34941da177e4SLinus Torvalds 				 * Either we have a TUR
34951da177e4SLinus Torvalds 				 * or we have a SCSI inquiry
34961da177e4SLinus Torvalds 				 */
34971da177e4SLinus Torvalds 				if (scb->scsi_cmd->cmnd[0] == TEST_UNIT_READY)
34981da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_OK << 16;
34991da177e4SLinus Torvalds 
35001da177e4SLinus Torvalds 				if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
35011da177e4SLinus Torvalds 					IPS_SCSI_INQ_DATA inquiry;
35021da177e4SLinus Torvalds 
35031da177e4SLinus Torvalds 					memset(&inquiry, 0,
35041da177e4SLinus Torvalds 					       sizeof (IPS_SCSI_INQ_DATA));
35051da177e4SLinus Torvalds 
35061da177e4SLinus Torvalds 					inquiry.DeviceType =
35071da177e4SLinus Torvalds 					    IPS_SCSI_INQ_TYPE_PROCESSOR;
35081da177e4SLinus Torvalds 					inquiry.DeviceTypeQualifier =
35091da177e4SLinus Torvalds 					    IPS_SCSI_INQ_LU_CONNECTED;
35101da177e4SLinus Torvalds 					inquiry.Version = IPS_SCSI_INQ_REV2;
35111da177e4SLinus Torvalds 					inquiry.ResponseDataFormat =
35121da177e4SLinus Torvalds 					    IPS_SCSI_INQ_RD_REV2;
35131da177e4SLinus Torvalds 					inquiry.AdditionalLength = 31;
35141da177e4SLinus Torvalds 					inquiry.Flags[0] =
35151da177e4SLinus Torvalds 					    IPS_SCSI_INQ_Address16;
35161da177e4SLinus Torvalds 					inquiry.Flags[1] =
35171da177e4SLinus Torvalds 					    IPS_SCSI_INQ_WBus16 |
35181da177e4SLinus Torvalds 					    IPS_SCSI_INQ_Sync;
35191da177e4SLinus Torvalds 					strncpy(inquiry.VendorId, "IBM     ",
35201da177e4SLinus Torvalds 						8);
35211da177e4SLinus Torvalds 					strncpy(inquiry.ProductId,
35221da177e4SLinus Torvalds 						"SERVERAID       ", 16);
35231da177e4SLinus Torvalds 					strncpy(inquiry.ProductRevisionLevel,
35241da177e4SLinus Torvalds 						"1.00", 4);
35251da177e4SLinus Torvalds 
35261da177e4SLinus Torvalds 					ips_scmd_buf_write(scb->scsi_cmd,
35271da177e4SLinus Torvalds 							   &inquiry,
35281da177e4SLinus Torvalds 							   sizeof (inquiry));
35291da177e4SLinus Torvalds 
35301da177e4SLinus Torvalds 					scb->scsi_cmd->result = DID_OK << 16;
35311da177e4SLinus Torvalds 				}
35321da177e4SLinus Torvalds 			} else {
35331da177e4SLinus Torvalds 				scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
35341da177e4SLinus Torvalds 				scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
35351da177e4SLinus Torvalds 				scb->cmd.logical_info.reserved = 0;
35361da177e4SLinus Torvalds 				scb->cmd.logical_info.reserved2 = 0;
35371da177e4SLinus Torvalds 				scb->data_len = sizeof (IPS_LD_INFO);
35381da177e4SLinus Torvalds 				scb->data_busaddr = ha->logical_drive_info_dma_addr;
35391da177e4SLinus Torvalds 				scb->flags = 0;
35401da177e4SLinus Torvalds 				scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
35411da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
35421da177e4SLinus Torvalds 			}
35431da177e4SLinus Torvalds 
35441da177e4SLinus Torvalds 			break;
35451da177e4SLinus Torvalds 
35461da177e4SLinus Torvalds 		case REQUEST_SENSE:
35471da177e4SLinus Torvalds 			ips_reqsen(ha, scb);
35481da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
35491da177e4SLinus Torvalds 			break;
35501da177e4SLinus Torvalds 
35511da177e4SLinus Torvalds 		case READ_6:
35521da177e4SLinus Torvalds 		case WRITE_6:
35531da177e4SLinus Torvalds 			if (!scb->sg_len) {
35541da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
35551da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
35561da177e4SLinus Torvalds 				     READ_6) ? IPS_CMD_READ : IPS_CMD_WRITE;
35571da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg = 0;
35581da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
35591da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
35601da177e4SLinus Torvalds 			} else {
35611da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
35621da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
35631da177e4SLinus Torvalds 				     READ_6) ? IPS_CMD_READ_SG :
35641da177e4SLinus Torvalds 				    IPS_CMD_WRITE_SG;
35651da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg =
35661da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
35671da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
35681da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
35691da177e4SLinus Torvalds 			}
35701da177e4SLinus Torvalds 
35711da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
35721da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
35731da177e4SLinus Torvalds 			scb->cmd.basic_io.log_drv = scb->target_id;
35741da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_count = scb->sg_len;
35751da177e4SLinus Torvalds 
35761da177e4SLinus Torvalds 			if (scb->cmd.basic_io.lba)
357736b8dd1bSMarcin Slusarz 				le32_add_cpu(&scb->cmd.basic_io.lba,
35781da177e4SLinus Torvalds 						le16_to_cpu(scb->cmd.basic_io.
35791da177e4SLinus Torvalds 							    sector_count));
35801da177e4SLinus Torvalds 			else
35811da177e4SLinus Torvalds 				scb->cmd.basic_io.lba =
35821da177e4SLinus Torvalds 				    (((scb->scsi_cmd->
35831da177e4SLinus Torvalds 				       cmnd[1] & 0x1f) << 16) | (scb->scsi_cmd->
35841da177e4SLinus Torvalds 								 cmnd[2] << 8) |
35851da177e4SLinus Torvalds 				     (scb->scsi_cmd->cmnd[3]));
35861da177e4SLinus Torvalds 
35871da177e4SLinus Torvalds 			scb->cmd.basic_io.sector_count =
35881da177e4SLinus Torvalds 			    cpu_to_le16(scb->data_len / IPS_BLKSIZE);
35891da177e4SLinus Torvalds 
35901da177e4SLinus Torvalds 			if (le16_to_cpu(scb->cmd.basic_io.sector_count) == 0)
35911da177e4SLinus Torvalds 				scb->cmd.basic_io.sector_count =
35921da177e4SLinus Torvalds 				    cpu_to_le16(256);
35931da177e4SLinus Torvalds 
35941da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
35951da177e4SLinus Torvalds 			break;
35961da177e4SLinus Torvalds 
35971da177e4SLinus Torvalds 		case READ_10:
35981da177e4SLinus Torvalds 		case WRITE_10:
35991da177e4SLinus Torvalds 			if (!scb->sg_len) {
36001da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
36011da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
36021da177e4SLinus Torvalds 				     READ_10) ? IPS_CMD_READ : IPS_CMD_WRITE;
36031da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg = 0;
36041da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
36051da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
36061da177e4SLinus Torvalds 			} else {
36071da177e4SLinus Torvalds 				scb->cmd.basic_io.op_code =
36081da177e4SLinus Torvalds 				    (scb->scsi_cmd->cmnd[0] ==
36091da177e4SLinus Torvalds 				     READ_10) ? IPS_CMD_READ_SG :
36101da177e4SLinus Torvalds 				    IPS_CMD_WRITE_SG;
36111da177e4SLinus Torvalds 				scb->cmd.basic_io.enhanced_sg =
36121da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
36131da177e4SLinus Torvalds 				scb->cmd.basic_io.sg_addr =
36141da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
36151da177e4SLinus Torvalds 			}
36161da177e4SLinus Torvalds 
36171da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
36181da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
36191da177e4SLinus Torvalds 			scb->cmd.basic_io.log_drv = scb->target_id;
36201da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_count = scb->sg_len;
36211da177e4SLinus Torvalds 
36221da177e4SLinus Torvalds 			if (scb->cmd.basic_io.lba)
362336b8dd1bSMarcin Slusarz 				le32_add_cpu(&scb->cmd.basic_io.lba,
36241da177e4SLinus Torvalds 						le16_to_cpu(scb->cmd.basic_io.
36251da177e4SLinus Torvalds 							    sector_count));
36261da177e4SLinus Torvalds 			else
36271da177e4SLinus Torvalds 				scb->cmd.basic_io.lba =
36281da177e4SLinus Torvalds 				    ((scb->scsi_cmd->cmnd[2] << 24) | (scb->
36291da177e4SLinus Torvalds 								       scsi_cmd->
36301da177e4SLinus Torvalds 								       cmnd[3]
36311da177e4SLinus Torvalds 								       << 16) |
36321da177e4SLinus Torvalds 				     (scb->scsi_cmd->cmnd[4] << 8) | scb->
36331da177e4SLinus Torvalds 				     scsi_cmd->cmnd[5]);
36341da177e4SLinus Torvalds 
36351da177e4SLinus Torvalds 			scb->cmd.basic_io.sector_count =
36361da177e4SLinus Torvalds 			    cpu_to_le16(scb->data_len / IPS_BLKSIZE);
36371da177e4SLinus Torvalds 
36381da177e4SLinus Torvalds 			if (cpu_to_le16(scb->cmd.basic_io.sector_count) == 0) {
36391da177e4SLinus Torvalds 				/*
36401da177e4SLinus Torvalds 				 * This is a null condition
36411da177e4SLinus Torvalds 				 * we don't have to do anything
36421da177e4SLinus Torvalds 				 * so just return
36431da177e4SLinus Torvalds 				 */
36441da177e4SLinus Torvalds 				scb->scsi_cmd->result = DID_OK << 16;
36451da177e4SLinus Torvalds 			} else
36461da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
36471da177e4SLinus Torvalds 
36481da177e4SLinus Torvalds 			break;
36491da177e4SLinus Torvalds 
36501da177e4SLinus Torvalds 		case RESERVE:
36511da177e4SLinus Torvalds 		case RELEASE:
36521da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
36531da177e4SLinus Torvalds 			break;
36541da177e4SLinus Torvalds 
36551da177e4SLinus Torvalds 		case MODE_SENSE:
36561da177e4SLinus Torvalds 			scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
36571da177e4SLinus Torvalds 			scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
36581da177e4SLinus Torvalds 			scb->cmd.basic_io.segment_4G = 0;
36591da177e4SLinus Torvalds 			scb->cmd.basic_io.enhanced_sg = 0;
36601da177e4SLinus Torvalds 			scb->data_len = sizeof (*ha->enq);
36611da177e4SLinus Torvalds 			scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
36621da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
36631da177e4SLinus Torvalds 			break;
36641da177e4SLinus Torvalds 
36651da177e4SLinus Torvalds 		case READ_CAPACITY:
36661da177e4SLinus Torvalds 			scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
36671da177e4SLinus Torvalds 			scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
36681da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved = 0;
36691da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved2 = 0;
36701da177e4SLinus Torvalds 			scb->cmd.logical_info.reserved3 = 0;
36711da177e4SLinus Torvalds 			scb->data_len = sizeof (IPS_LD_INFO);
36721da177e4SLinus Torvalds 			scb->data_busaddr = ha->logical_drive_info_dma_addr;
36731da177e4SLinus Torvalds 			scb->flags = 0;
36741da177e4SLinus Torvalds 			scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
36751da177e4SLinus Torvalds 			ret = IPS_SUCCESS;
36761da177e4SLinus Torvalds 			break;
36771da177e4SLinus Torvalds 
36781da177e4SLinus Torvalds 		case SEND_DIAGNOSTIC:
36791da177e4SLinus Torvalds 		case REASSIGN_BLOCKS:
36801da177e4SLinus Torvalds 		case FORMAT_UNIT:
36811da177e4SLinus Torvalds 		case SEEK_10:
36821da177e4SLinus Torvalds 		case VERIFY:
36831da177e4SLinus Torvalds 		case READ_DEFECT_DATA:
36841da177e4SLinus Torvalds 		case READ_BUFFER:
36851da177e4SLinus Torvalds 		case WRITE_BUFFER:
36861da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_OK << 16;
36871da177e4SLinus Torvalds 			break;
36881da177e4SLinus Torvalds 
36891da177e4SLinus Torvalds 		default:
36901da177e4SLinus Torvalds 			/* Set the Return Info to appear like the Command was */
36911da177e4SLinus Torvalds 			/* attempted, a Check Condition occurred, and Sense   */
36921da177e4SLinus Torvalds 			/* Data indicating an Invalid CDB OpCode is returned. */
36931da177e4SLinus Torvalds 			sp = (char *) scb->scsi_cmd->sense_buffer;
36941da177e4SLinus Torvalds 
36951da177e4SLinus Torvalds 			sp[0] = 0x70;	/* Error Code               */
36961da177e4SLinus Torvalds 			sp[2] = ILLEGAL_REQUEST;	/* Sense Key 5 Illegal Req. */
36971da177e4SLinus Torvalds 			sp[7] = 0x0A;	/* Additional Sense Length  */
36981da177e4SLinus Torvalds 			sp[12] = 0x20;	/* ASC = Invalid OpCode     */
36991da177e4SLinus Torvalds 			sp[13] = 0x00;	/* ASCQ                     */
37001da177e4SLinus Torvalds 
37011da177e4SLinus Torvalds 			device_error = 2;	/* Indicate Check Condition */
37021da177e4SLinus Torvalds 			scb->scsi_cmd->result = device_error | (DID_OK << 16);
37031da177e4SLinus Torvalds 			break;
37041da177e4SLinus Torvalds 		}		/* end switch */
37051da177e4SLinus Torvalds 	}
37061da177e4SLinus Torvalds 	/* end if */
37071da177e4SLinus Torvalds 	if (ret == IPS_SUCCESS_IMM)
37081da177e4SLinus Torvalds 		return (ret);
37091da177e4SLinus Torvalds 
37101da177e4SLinus Torvalds 	/* setup DCDB */
37111da177e4SLinus Torvalds 	if (scb->bus > 0) {
37121da177e4SLinus Torvalds 
37131da177e4SLinus Torvalds 		/* If we already know the Device is Not there, no need to attempt a Command   */
37141da177e4SLinus Torvalds 		/* This also protects an NT FailOver Controller from getting CDB's sent to it */
37151da177e4SLinus Torvalds 		if (ha->conf->dev[scb->bus - 1][scb->target_id].ucState == 0) {
37161da177e4SLinus Torvalds 			scb->scsi_cmd->result = DID_NO_CONNECT << 16;
37171da177e4SLinus Torvalds 			return (IPS_SUCCESS_IMM);
37181da177e4SLinus Torvalds 		}
37191da177e4SLinus Torvalds 
37201da177e4SLinus Torvalds 		ha->dcdb_active[scb->bus - 1] |= (1 << scb->target_id);
37211da177e4SLinus Torvalds 		scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb);
37221da177e4SLinus Torvalds 		scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
37231da177e4SLinus Torvalds 							 (unsigned long) &scb->
37241da177e4SLinus Torvalds 							 dcdb -
37251da177e4SLinus Torvalds 							 (unsigned long) scb);
37261da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved = 0;
37271da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved2 = 0;
37281da177e4SLinus Torvalds 		scb->cmd.dcdb.reserved3 = 0;
37291da177e4SLinus Torvalds 		scb->cmd.dcdb.segment_4G = 0;
37301da177e4SLinus Torvalds 		scb->cmd.dcdb.enhanced_sg = 0;
37311da177e4SLinus Torvalds 
3732242f9dcbSJens Axboe 		TimeOut = scb->scsi_cmd->request->timeout;
37331da177e4SLinus Torvalds 
37341da177e4SLinus Torvalds 		if (ha->subsys->param[4] & 0x00100000) {	/* If NEW Tape DCDB is Supported */
37351da177e4SLinus Torvalds 			if (!scb->sg_len) {
37361da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_EXTENDED_DCDB;
37371da177e4SLinus Torvalds 			} else {
37381da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code =
37391da177e4SLinus Torvalds 				    IPS_CMD_EXTENDED_DCDB_SG;
37401da177e4SLinus Torvalds 				scb->cmd.dcdb.enhanced_sg =
37411da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
37421da177e4SLinus Torvalds 			}
37431da177e4SLinus Torvalds 
37441da177e4SLinus Torvalds 			tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;	/* Use Same Data Area as Old DCDB Struct */
37451da177e4SLinus Torvalds 			tapeDCDB->device_address =
37461da177e4SLinus Torvalds 			    ((scb->bus - 1) << 4) | scb->target_id;
37471da177e4SLinus Torvalds 			tapeDCDB->cmd_attribute |= IPS_DISCONNECT_ALLOWED;
37481da177e4SLinus Torvalds 			tapeDCDB->cmd_attribute &= ~IPS_TRANSFER64K;	/* Always Turn OFF 64K Size Flag */
37491da177e4SLinus Torvalds 
37501da177e4SLinus Torvalds 			if (TimeOut) {
37511da177e4SLinus Torvalds 				if (TimeOut < (10 * HZ))
37521da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */
37531da177e4SLinus Torvalds 				else if (TimeOut < (60 * HZ))
37541da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */
37551da177e4SLinus Torvalds 				else if (TimeOut < (1200 * HZ))
37561da177e4SLinus Torvalds 					tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */
37571da177e4SLinus Torvalds 			}
37581da177e4SLinus Torvalds 
37591da177e4SLinus Torvalds 			tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len;
37601da177e4SLinus Torvalds 			tapeDCDB->reserved_for_LUN = 0;
37611da177e4SLinus Torvalds 			tapeDCDB->transfer_length = scb->data_len;
37621da177e4SLinus Torvalds 			if (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG)
37631da177e4SLinus Torvalds 				tapeDCDB->buffer_pointer =
37641da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
37651da177e4SLinus Torvalds 			else
37661da177e4SLinus Torvalds 				tapeDCDB->buffer_pointer =
37671da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
37681da177e4SLinus Torvalds 			tapeDCDB->sg_count = scb->sg_len;
37691da177e4SLinus Torvalds 			tapeDCDB->sense_length = sizeof (tapeDCDB->sense_info);
37701da177e4SLinus Torvalds 			tapeDCDB->scsi_status = 0;
37711da177e4SLinus Torvalds 			tapeDCDB->reserved = 0;
37721da177e4SLinus Torvalds 			memcpy(tapeDCDB->scsi_cdb, scb->scsi_cmd->cmnd,
37731da177e4SLinus Torvalds 			       scb->scsi_cmd->cmd_len);
37741da177e4SLinus Torvalds 		} else {
37751da177e4SLinus Torvalds 			if (!scb->sg_len) {
37761da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_DCDB;
37771da177e4SLinus Torvalds 			} else {
37781da177e4SLinus Torvalds 				scb->cmd.dcdb.op_code = IPS_CMD_DCDB_SG;
37791da177e4SLinus Torvalds 				scb->cmd.dcdb.enhanced_sg =
37801da177e4SLinus Torvalds 				    IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
37811da177e4SLinus Torvalds 			}
37821da177e4SLinus Torvalds 
37831da177e4SLinus Torvalds 			scb->dcdb.device_address =
37841da177e4SLinus Torvalds 			    ((scb->bus - 1) << 4) | scb->target_id;
37851da177e4SLinus Torvalds 			scb->dcdb.cmd_attribute |= IPS_DISCONNECT_ALLOWED;
37861da177e4SLinus Torvalds 
37871da177e4SLinus Torvalds 			if (TimeOut) {
37881da177e4SLinus Torvalds 				if (TimeOut < (10 * HZ))
37891da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;	/* TimeOut is 10 Seconds */
37901da177e4SLinus Torvalds 				else if (TimeOut < (60 * HZ))
37911da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;	/* TimeOut is 60 Seconds */
37921da177e4SLinus Torvalds 				else if (TimeOut < (1200 * HZ))
37931da177e4SLinus Torvalds 					scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;	/* TimeOut is 20 Minutes */
37941da177e4SLinus Torvalds 			}
37951da177e4SLinus Torvalds 
37961da177e4SLinus Torvalds 			scb->dcdb.transfer_length = scb->data_len;
37971da177e4SLinus Torvalds 			if (scb->dcdb.cmd_attribute & IPS_TRANSFER64K)
37981da177e4SLinus Torvalds 				scb->dcdb.transfer_length = 0;
37991da177e4SLinus Torvalds 			if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB_SG)
38001da177e4SLinus Torvalds 				scb->dcdb.buffer_pointer =
38011da177e4SLinus Torvalds 				    cpu_to_le32(scb->sg_busaddr);
38021da177e4SLinus Torvalds 			else
38031da177e4SLinus Torvalds 				scb->dcdb.buffer_pointer =
38041da177e4SLinus Torvalds 				    cpu_to_le32(scb->data_busaddr);
38051da177e4SLinus Torvalds 			scb->dcdb.cdb_length = scb->scsi_cmd->cmd_len;
38061da177e4SLinus Torvalds 			scb->dcdb.sense_length = sizeof (scb->dcdb.sense_info);
38071da177e4SLinus Torvalds 			scb->dcdb.sg_count = scb->sg_len;
38081da177e4SLinus Torvalds 			scb->dcdb.reserved = 0;
38091da177e4SLinus Torvalds 			memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd,
38101da177e4SLinus Torvalds 			       scb->scsi_cmd->cmd_len);
38111da177e4SLinus Torvalds 			scb->dcdb.scsi_status = 0;
38121da177e4SLinus Torvalds 			scb->dcdb.reserved2[0] = 0;
38131da177e4SLinus Torvalds 			scb->dcdb.reserved2[1] = 0;
38141da177e4SLinus Torvalds 			scb->dcdb.reserved2[2] = 0;
38151da177e4SLinus Torvalds 		}
38161da177e4SLinus Torvalds 	}
38171da177e4SLinus Torvalds 
38181da177e4SLinus Torvalds 	return ((*ha->func.issue) (ha, scb));
38191da177e4SLinus Torvalds }
38201da177e4SLinus Torvalds 
38211da177e4SLinus Torvalds /****************************************************************************/
38221da177e4SLinus Torvalds /*                                                                          */
38231da177e4SLinus Torvalds /* Routine Name: ips_chk_status                                             */
38241da177e4SLinus Torvalds /*                                                                          */
38251da177e4SLinus Torvalds /* Routine Description:                                                     */
38261da177e4SLinus Torvalds /*                                                                          */
38271da177e4SLinus Torvalds /*   Check the status of commands to logical drives                         */
38281da177e4SLinus Torvalds /*   Assumed to be called with the HA lock                                  */
38291da177e4SLinus Torvalds /****************************************************************************/
38301da177e4SLinus Torvalds static void
38311da177e4SLinus Torvalds ips_chkstatus(ips_ha_t * ha, IPS_STATUS * pstatus)
38321da177e4SLinus Torvalds {
38331da177e4SLinus Torvalds 	ips_scb_t *scb;
38341da177e4SLinus Torvalds 	ips_stat_t *sp;
38351da177e4SLinus Torvalds 	uint8_t basic_status;
38361da177e4SLinus Torvalds 	uint8_t ext_status;
38371da177e4SLinus Torvalds 	int errcode;
3838a5b3c86eSJack Hammer 	IPS_SCSI_INQ_DATA inquiryData;
38391da177e4SLinus Torvalds 
38401da177e4SLinus Torvalds 	METHOD_TRACE("ips_chkstatus", 1);
38411da177e4SLinus Torvalds 
38421da177e4SLinus Torvalds 	scb = &ha->scbs[pstatus->fields.command_id];
38431da177e4SLinus Torvalds 	scb->basic_status = basic_status =
38441da177e4SLinus Torvalds 	    pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
38451da177e4SLinus Torvalds 	scb->extended_status = ext_status = pstatus->fields.extended_status;
38461da177e4SLinus Torvalds 
38471da177e4SLinus Torvalds 	sp = &ha->sp;
38481da177e4SLinus Torvalds 	sp->residue_len = 0;
38491da177e4SLinus Torvalds 	sp->scb_addr = (void *) scb;
38501da177e4SLinus Torvalds 
38511da177e4SLinus Torvalds 	/* Remove the item from the active queue */
38521da177e4SLinus Torvalds 	ips_removeq_scb(&ha->scb_activelist, scb);
38531da177e4SLinus Torvalds 
38541da177e4SLinus Torvalds 	if (!scb->scsi_cmd)
38551da177e4SLinus Torvalds 		/* internal commands are handled in do_ipsintr */
38561da177e4SLinus Torvalds 		return;
38571da177e4SLinus Torvalds 
38581da177e4SLinus Torvalds 	DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
38591da177e4SLinus Torvalds 		  ips_name,
38601da177e4SLinus Torvalds 		  ha->host_num,
38611da177e4SLinus Torvalds 		  scb->cdb[0],
38621da177e4SLinus Torvalds 		  scb->cmd.basic_io.command_id,
38631da177e4SLinus Torvalds 		  scb->bus, scb->target_id, scb->lun);
38641da177e4SLinus Torvalds 
38651da177e4SLinus Torvalds 	if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
38661da177e4SLinus Torvalds 		/* passthru - just returns the raw result */
38671da177e4SLinus Torvalds 		return;
38681da177e4SLinus Torvalds 
38691da177e4SLinus Torvalds 	errcode = DID_OK;
38701da177e4SLinus Torvalds 
38711da177e4SLinus Torvalds 	if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
38721da177e4SLinus Torvalds 	    ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
38731da177e4SLinus Torvalds 
38741da177e4SLinus Torvalds 		if (scb->bus == 0) {
38751da177e4SLinus Torvalds 			if ((basic_status & IPS_GSC_STATUS_MASK) ==
38761da177e4SLinus Torvalds 			    IPS_CMD_RECOVERED_ERROR) {
38771da177e4SLinus Torvalds 				DEBUG_VAR(1,
38781da177e4SLinus Torvalds 					  "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
38791da177e4SLinus Torvalds 					  ips_name, ha->host_num,
38801da177e4SLinus Torvalds 					  scb->cmd.basic_io.op_code,
38811da177e4SLinus Torvalds 					  basic_status, ext_status);
38821da177e4SLinus Torvalds 			}
38831da177e4SLinus Torvalds 
38841da177e4SLinus Torvalds 			switch (scb->scsi_cmd->cmnd[0]) {
38851da177e4SLinus Torvalds 			case ALLOW_MEDIUM_REMOVAL:
38861da177e4SLinus Torvalds 			case REZERO_UNIT:
38871da177e4SLinus Torvalds 			case ERASE:
38881da177e4SLinus Torvalds 			case WRITE_FILEMARKS:
38891da177e4SLinus Torvalds 			case SPACE:
38901da177e4SLinus Torvalds 				errcode = DID_ERROR;
38911da177e4SLinus Torvalds 				break;
38921da177e4SLinus Torvalds 
38931da177e4SLinus Torvalds 			case START_STOP:
38941da177e4SLinus Torvalds 				break;
38951da177e4SLinus Torvalds 
38961da177e4SLinus Torvalds 			case TEST_UNIT_READY:
38971da177e4SLinus Torvalds 				if (!ips_online(ha, scb)) {
38981da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
38991da177e4SLinus Torvalds 				}
39001da177e4SLinus Torvalds 				break;
39011da177e4SLinus Torvalds 
39021da177e4SLinus Torvalds 			case INQUIRY:
39031da177e4SLinus Torvalds 				if (ips_online(ha, scb)) {
39041da177e4SLinus Torvalds 					ips_inquiry(ha, scb);
39051da177e4SLinus Torvalds 				} else {
39061da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
39071da177e4SLinus Torvalds 				}
39081da177e4SLinus Torvalds 				break;
39091da177e4SLinus Torvalds 
39101da177e4SLinus Torvalds 			case REQUEST_SENSE:
39111da177e4SLinus Torvalds 				ips_reqsen(ha, scb);
39121da177e4SLinus Torvalds 				break;
39131da177e4SLinus Torvalds 
39141da177e4SLinus Torvalds 			case READ_6:
39151da177e4SLinus Torvalds 			case WRITE_6:
39161da177e4SLinus Torvalds 			case READ_10:
39171da177e4SLinus Torvalds 			case WRITE_10:
39181da177e4SLinus Torvalds 			case RESERVE:
39191da177e4SLinus Torvalds 			case RELEASE:
39201da177e4SLinus Torvalds 				break;
39211da177e4SLinus Torvalds 
39221da177e4SLinus Torvalds 			case MODE_SENSE:
39231da177e4SLinus Torvalds 				if (!ips_online(ha, scb)
39241da177e4SLinus Torvalds 				    || !ips_msense(ha, scb)) {
39251da177e4SLinus Torvalds 					errcode = DID_ERROR;
39261da177e4SLinus Torvalds 				}
39271da177e4SLinus Torvalds 				break;
39281da177e4SLinus Torvalds 
39291da177e4SLinus Torvalds 			case READ_CAPACITY:
39301da177e4SLinus Torvalds 				if (ips_online(ha, scb))
39311da177e4SLinus Torvalds 					ips_rdcap(ha, scb);
39321da177e4SLinus Torvalds 				else {
39331da177e4SLinus Torvalds 					errcode = DID_TIME_OUT;
39341da177e4SLinus Torvalds 				}
39351da177e4SLinus Torvalds 				break;
39361da177e4SLinus Torvalds 
39371da177e4SLinus Torvalds 			case SEND_DIAGNOSTIC:
39381da177e4SLinus Torvalds 			case REASSIGN_BLOCKS:
39391da177e4SLinus Torvalds 				break;
39401da177e4SLinus Torvalds 
39411da177e4SLinus Torvalds 			case FORMAT_UNIT:
39421da177e4SLinus Torvalds 				errcode = DID_ERROR;
39431da177e4SLinus Torvalds 				break;
39441da177e4SLinus Torvalds 
39451da177e4SLinus Torvalds 			case SEEK_10:
39461da177e4SLinus Torvalds 			case VERIFY:
39471da177e4SLinus Torvalds 			case READ_DEFECT_DATA:
39481da177e4SLinus Torvalds 			case READ_BUFFER:
39491da177e4SLinus Torvalds 			case WRITE_BUFFER:
39501da177e4SLinus Torvalds 				break;
39511da177e4SLinus Torvalds 
39521da177e4SLinus Torvalds 			default:
39531da177e4SLinus Torvalds 				errcode = DID_ERROR;
39541da177e4SLinus Torvalds 			}	/* end switch */
39551da177e4SLinus Torvalds 
39561da177e4SLinus Torvalds 			scb->scsi_cmd->result = errcode << 16;
39571da177e4SLinus Torvalds 		} else {	/* bus == 0 */
39581da177e4SLinus Torvalds 			/* restrict access to physical drives */
3959a5b3c86eSJack Hammer 			if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
3960a5b3c86eSJack Hammer 			    ips_scmd_buf_read(scb->scsi_cmd,
3961a5b3c86eSJack Hammer                                   &inquiryData, sizeof (inquiryData));
3962a5b3c86eSJack Hammer 			    if ((inquiryData.DeviceType & 0x1f) == TYPE_DISK)
39631da177e4SLinus Torvalds 			        scb->scsi_cmd->result = DID_TIME_OUT << 16;
39641da177e4SLinus Torvalds 			}
39651da177e4SLinus Torvalds 		}		/* else */
39661da177e4SLinus Torvalds 	} else {		/* recovered error / success */
39671da177e4SLinus Torvalds 		if (scb->bus == 0) {
39681da177e4SLinus Torvalds 			DEBUG_VAR(1,
39691da177e4SLinus Torvalds 				  "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
39701da177e4SLinus Torvalds 				  ips_name, ha->host_num,
39711da177e4SLinus Torvalds 				  scb->cmd.basic_io.op_code, basic_status,
39721da177e4SLinus Torvalds 				  ext_status);
39731da177e4SLinus Torvalds 		}
39741da177e4SLinus Torvalds 
39751da177e4SLinus Torvalds 		ips_map_status(ha, scb, sp);
39761da177e4SLinus Torvalds 	}			/* else */
39771da177e4SLinus Torvalds }
39781da177e4SLinus Torvalds 
39791da177e4SLinus Torvalds /****************************************************************************/
39801da177e4SLinus Torvalds /*                                                                          */
39811da177e4SLinus Torvalds /* Routine Name: ips_online                                                 */
39821da177e4SLinus Torvalds /*                                                                          */
39831da177e4SLinus Torvalds /* Routine Description:                                                     */
39841da177e4SLinus Torvalds /*                                                                          */
39851da177e4SLinus Torvalds /*   Determine if a logical drive is online                                 */
39861da177e4SLinus Torvalds /*                                                                          */
39871da177e4SLinus Torvalds /****************************************************************************/
39881da177e4SLinus Torvalds static int
39891da177e4SLinus Torvalds ips_online(ips_ha_t * ha, ips_scb_t * scb)
39901da177e4SLinus Torvalds {
39911da177e4SLinus Torvalds 	METHOD_TRACE("ips_online", 1);
39921da177e4SLinus Torvalds 
39931da177e4SLinus Torvalds 	if (scb->target_id >= IPS_MAX_LD)
39941da177e4SLinus Torvalds 		return (0);
39951da177e4SLinus Torvalds 
39961da177e4SLinus Torvalds 	if ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1) {
39971da177e4SLinus Torvalds 		memset(ha->logical_drive_info, 0, sizeof (IPS_LD_INFO));
39981da177e4SLinus Torvalds 		return (0);
39991da177e4SLinus Torvalds 	}
40001da177e4SLinus Torvalds 
40011da177e4SLinus Torvalds 	if (ha->logical_drive_info->drive_info[scb->target_id].state !=
40021da177e4SLinus Torvalds 	    IPS_LD_OFFLINE
40031da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40041da177e4SLinus Torvalds 	    IPS_LD_FREE
40051da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40061da177e4SLinus Torvalds 	    IPS_LD_CRS
40071da177e4SLinus Torvalds 	    && ha->logical_drive_info->drive_info[scb->target_id].state !=
40081da177e4SLinus Torvalds 	    IPS_LD_SYS)
40091da177e4SLinus Torvalds 		return (1);
40101da177e4SLinus Torvalds 	else
40111da177e4SLinus Torvalds 		return (0);
40121da177e4SLinus Torvalds }
40131da177e4SLinus Torvalds 
40141da177e4SLinus Torvalds /****************************************************************************/
40151da177e4SLinus Torvalds /*                                                                          */
40161da177e4SLinus Torvalds /* Routine Name: ips_inquiry                                                */
40171da177e4SLinus Torvalds /*                                                                          */
40181da177e4SLinus Torvalds /* Routine Description:                                                     */
40191da177e4SLinus Torvalds /*                                                                          */
40201da177e4SLinus Torvalds /*   Simulate an inquiry command to a logical drive                         */
40211da177e4SLinus Torvalds /*                                                                          */
40221da177e4SLinus Torvalds /****************************************************************************/
40231da177e4SLinus Torvalds static int
40241da177e4SLinus Torvalds ips_inquiry(ips_ha_t * ha, ips_scb_t * scb)
40251da177e4SLinus Torvalds {
40261da177e4SLinus Torvalds 	IPS_SCSI_INQ_DATA inquiry;
40271da177e4SLinus Torvalds 
40281da177e4SLinus Torvalds 	METHOD_TRACE("ips_inquiry", 1);
40291da177e4SLinus Torvalds 
40301da177e4SLinus Torvalds 	memset(&inquiry, 0, sizeof (IPS_SCSI_INQ_DATA));
40311da177e4SLinus Torvalds 
40321da177e4SLinus Torvalds 	inquiry.DeviceType = IPS_SCSI_INQ_TYPE_DASD;
40331da177e4SLinus Torvalds 	inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED;
40341da177e4SLinus Torvalds 	inquiry.Version = IPS_SCSI_INQ_REV2;
40351da177e4SLinus Torvalds 	inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2;
40361da177e4SLinus Torvalds 	inquiry.AdditionalLength = 31;
40371da177e4SLinus Torvalds 	inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
40381da177e4SLinus Torvalds 	inquiry.Flags[1] =
40391da177e4SLinus Torvalds 	    IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync | IPS_SCSI_INQ_CmdQue;
40401da177e4SLinus Torvalds 	strncpy(inquiry.VendorId, "IBM     ", 8);
40411da177e4SLinus Torvalds 	strncpy(inquiry.ProductId, "SERVERAID       ", 16);
40421da177e4SLinus Torvalds 	strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
40431da177e4SLinus Torvalds 
40441da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &inquiry, sizeof (inquiry));
40451da177e4SLinus Torvalds 
40461da177e4SLinus Torvalds 	return (1);
40471da177e4SLinus Torvalds }
40481da177e4SLinus Torvalds 
40491da177e4SLinus Torvalds /****************************************************************************/
40501da177e4SLinus Torvalds /*                                                                          */
40511da177e4SLinus Torvalds /* Routine Name: ips_rdcap                                                  */
40521da177e4SLinus Torvalds /*                                                                          */
40531da177e4SLinus Torvalds /* Routine Description:                                                     */
40541da177e4SLinus Torvalds /*                                                                          */
40551da177e4SLinus Torvalds /*   Simulate a read capacity command to a logical drive                    */
40561da177e4SLinus Torvalds /*                                                                          */
40571da177e4SLinus Torvalds /****************************************************************************/
40581da177e4SLinus Torvalds static int
40591da177e4SLinus Torvalds ips_rdcap(ips_ha_t * ha, ips_scb_t * scb)
40601da177e4SLinus Torvalds {
40611da177e4SLinus Torvalds 	IPS_SCSI_CAPACITY cap;
40621da177e4SLinus Torvalds 
40631da177e4SLinus Torvalds 	METHOD_TRACE("ips_rdcap", 1);
40641da177e4SLinus Torvalds 
40652f4cf91cSFUJITA Tomonori 	if (scsi_bufflen(scb->scsi_cmd) < 8)
40661da177e4SLinus Torvalds 		return (0);
40671da177e4SLinus Torvalds 
40681da177e4SLinus Torvalds 	cap.lba =
40691da177e4SLinus Torvalds 	    cpu_to_be32(le32_to_cpu
40701da177e4SLinus Torvalds 			(ha->logical_drive_info->
40711da177e4SLinus Torvalds 			 drive_info[scb->target_id].sector_count) - 1);
40721da177e4SLinus Torvalds 	cap.len = cpu_to_be32((uint32_t) IPS_BLKSIZE);
40731da177e4SLinus Torvalds 
40741da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &cap, sizeof (cap));
40751da177e4SLinus Torvalds 
40761da177e4SLinus Torvalds 	return (1);
40771da177e4SLinus Torvalds }
40781da177e4SLinus Torvalds 
40791da177e4SLinus Torvalds /****************************************************************************/
40801da177e4SLinus Torvalds /*                                                                          */
40811da177e4SLinus Torvalds /* Routine Name: ips_msense                                                 */
40821da177e4SLinus Torvalds /*                                                                          */
40831da177e4SLinus Torvalds /* Routine Description:                                                     */
40841da177e4SLinus Torvalds /*                                                                          */
40851da177e4SLinus Torvalds /*   Simulate a mode sense command to a logical drive                       */
40861da177e4SLinus Torvalds /*                                                                          */
40871da177e4SLinus Torvalds /****************************************************************************/
40881da177e4SLinus Torvalds static int
40891da177e4SLinus Torvalds ips_msense(ips_ha_t * ha, ips_scb_t * scb)
40901da177e4SLinus Torvalds {
40911da177e4SLinus Torvalds 	uint16_t heads;
40921da177e4SLinus Torvalds 	uint16_t sectors;
40931da177e4SLinus Torvalds 	uint32_t cylinders;
40941da177e4SLinus Torvalds 	IPS_SCSI_MODE_PAGE_DATA mdata;
40951da177e4SLinus Torvalds 
40961da177e4SLinus Torvalds 	METHOD_TRACE("ips_msense", 1);
40971da177e4SLinus Torvalds 
40981da177e4SLinus Torvalds 	if (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) > 0x400000 &&
40991da177e4SLinus Torvalds 	    (ha->enq->ucMiscFlag & 0x8) == 0) {
41001da177e4SLinus Torvalds 		heads = IPS_NORM_HEADS;
41011da177e4SLinus Torvalds 		sectors = IPS_NORM_SECTORS;
41021da177e4SLinus Torvalds 	} else {
41031da177e4SLinus Torvalds 		heads = IPS_COMP_HEADS;
41041da177e4SLinus Torvalds 		sectors = IPS_COMP_SECTORS;
41051da177e4SLinus Torvalds 	}
41061da177e4SLinus Torvalds 
41071da177e4SLinus Torvalds 	cylinders =
41081da177e4SLinus Torvalds 	    (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) -
41091da177e4SLinus Torvalds 	     1) / (heads * sectors);
41101da177e4SLinus Torvalds 
41111da177e4SLinus Torvalds 	memset(&mdata, 0, sizeof (IPS_SCSI_MODE_PAGE_DATA));
41121da177e4SLinus Torvalds 
41131da177e4SLinus Torvalds 	mdata.hdr.BlockDescLength = 8;
41141da177e4SLinus Torvalds 
41151da177e4SLinus Torvalds 	switch (scb->scsi_cmd->cmnd[2] & 0x3f) {
41161da177e4SLinus Torvalds 	case 0x03:		/* page 3 */
41171da177e4SLinus Torvalds 		mdata.pdata.pg3.PageCode = 3;
41181da177e4SLinus Torvalds 		mdata.pdata.pg3.PageLength = sizeof (IPS_SCSI_MODE_PAGE3);
41191da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41201da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg3.PageLength;
41211da177e4SLinus Torvalds 		mdata.pdata.pg3.TracksPerZone = 0;
41221da177e4SLinus Torvalds 		mdata.pdata.pg3.AltSectorsPerZone = 0;
41231da177e4SLinus Torvalds 		mdata.pdata.pg3.AltTracksPerZone = 0;
41241da177e4SLinus Torvalds 		mdata.pdata.pg3.AltTracksPerVolume = 0;
41251da177e4SLinus Torvalds 		mdata.pdata.pg3.SectorsPerTrack = cpu_to_be16(sectors);
41261da177e4SLinus Torvalds 		mdata.pdata.pg3.BytesPerSector = cpu_to_be16(IPS_BLKSIZE);
41271da177e4SLinus Torvalds 		mdata.pdata.pg3.Interleave = cpu_to_be16(1);
41281da177e4SLinus Torvalds 		mdata.pdata.pg3.TrackSkew = 0;
41291da177e4SLinus Torvalds 		mdata.pdata.pg3.CylinderSkew = 0;
41301da177e4SLinus Torvalds 		mdata.pdata.pg3.flags = IPS_SCSI_MP3_SoftSector;
41311da177e4SLinus Torvalds 		break;
41321da177e4SLinus Torvalds 
41331da177e4SLinus Torvalds 	case 0x4:
41341da177e4SLinus Torvalds 		mdata.pdata.pg4.PageCode = 4;
41351da177e4SLinus Torvalds 		mdata.pdata.pg4.PageLength = sizeof (IPS_SCSI_MODE_PAGE4);
41361da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41371da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg4.PageLength;
41381da177e4SLinus Torvalds 		mdata.pdata.pg4.CylindersHigh =
41391da177e4SLinus Torvalds 		    cpu_to_be16((cylinders >> 8) & 0xFFFF);
41401da177e4SLinus Torvalds 		mdata.pdata.pg4.CylindersLow = (cylinders & 0xFF);
41411da177e4SLinus Torvalds 		mdata.pdata.pg4.Heads = heads;
41421da177e4SLinus Torvalds 		mdata.pdata.pg4.WritePrecompHigh = 0;
41431da177e4SLinus Torvalds 		mdata.pdata.pg4.WritePrecompLow = 0;
41441da177e4SLinus Torvalds 		mdata.pdata.pg4.ReducedWriteCurrentHigh = 0;
41451da177e4SLinus Torvalds 		mdata.pdata.pg4.ReducedWriteCurrentLow = 0;
41461da177e4SLinus Torvalds 		mdata.pdata.pg4.StepRate = cpu_to_be16(1);
41471da177e4SLinus Torvalds 		mdata.pdata.pg4.LandingZoneHigh = 0;
41481da177e4SLinus Torvalds 		mdata.pdata.pg4.LandingZoneLow = 0;
41491da177e4SLinus Torvalds 		mdata.pdata.pg4.flags = 0;
41501da177e4SLinus Torvalds 		mdata.pdata.pg4.RotationalOffset = 0;
41511da177e4SLinus Torvalds 		mdata.pdata.pg4.MediumRotationRate = 0;
41521da177e4SLinus Torvalds 		break;
41531da177e4SLinus Torvalds 	case 0x8:
41541da177e4SLinus Torvalds 		mdata.pdata.pg8.PageCode = 8;
41551da177e4SLinus Torvalds 		mdata.pdata.pg8.PageLength = sizeof (IPS_SCSI_MODE_PAGE8);
41561da177e4SLinus Torvalds 		mdata.hdr.DataLength =
41571da177e4SLinus Torvalds 		    3 + mdata.hdr.BlockDescLength + mdata.pdata.pg8.PageLength;
41581da177e4SLinus Torvalds 		/* everything else is left set to 0 */
41591da177e4SLinus Torvalds 		break;
41601da177e4SLinus Torvalds 
41611da177e4SLinus Torvalds 	default:
41621da177e4SLinus Torvalds 		return (0);
41631da177e4SLinus Torvalds 	}			/* end switch */
41641da177e4SLinus Torvalds 
41651da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &mdata, sizeof (mdata));
41661da177e4SLinus Torvalds 
41671da177e4SLinus Torvalds 	return (1);
41681da177e4SLinus Torvalds }
41691da177e4SLinus Torvalds 
41701da177e4SLinus Torvalds /****************************************************************************/
41711da177e4SLinus Torvalds /*                                                                          */
41721da177e4SLinus Torvalds /* Routine Name: ips_reqsen                                                 */
41731da177e4SLinus Torvalds /*                                                                          */
41741da177e4SLinus Torvalds /* Routine Description:                                                     */
41751da177e4SLinus Torvalds /*                                                                          */
41761da177e4SLinus Torvalds /*   Simulate a request sense command to a logical drive                    */
41771da177e4SLinus Torvalds /*                                                                          */
41781da177e4SLinus Torvalds /****************************************************************************/
41791da177e4SLinus Torvalds static int
41801da177e4SLinus Torvalds ips_reqsen(ips_ha_t * ha, ips_scb_t * scb)
41811da177e4SLinus Torvalds {
41821da177e4SLinus Torvalds 	IPS_SCSI_REQSEN reqsen;
41831da177e4SLinus Torvalds 
41841da177e4SLinus Torvalds 	METHOD_TRACE("ips_reqsen", 1);
41851da177e4SLinus Torvalds 
41861da177e4SLinus Torvalds 	memset(&reqsen, 0, sizeof (IPS_SCSI_REQSEN));
41871da177e4SLinus Torvalds 
41881da177e4SLinus Torvalds 	reqsen.ResponseCode =
41891da177e4SLinus Torvalds 	    IPS_SCSI_REQSEN_VALID | IPS_SCSI_REQSEN_CURRENT_ERR;
41901da177e4SLinus Torvalds 	reqsen.AdditionalLength = 10;
41911da177e4SLinus Torvalds 	reqsen.AdditionalSenseCode = IPS_SCSI_REQSEN_NO_SENSE;
41921da177e4SLinus Torvalds 	reqsen.AdditionalSenseCodeQual = IPS_SCSI_REQSEN_NO_SENSE;
41931da177e4SLinus Torvalds 
41941da177e4SLinus Torvalds 	ips_scmd_buf_write(scb->scsi_cmd, &reqsen, sizeof (reqsen));
41951da177e4SLinus Torvalds 
41961da177e4SLinus Torvalds 	return (1);
41971da177e4SLinus Torvalds }
41981da177e4SLinus Torvalds 
41991da177e4SLinus Torvalds /****************************************************************************/
42001da177e4SLinus Torvalds /*                                                                          */
42011da177e4SLinus Torvalds /* Routine Name: ips_free                                                   */
42021da177e4SLinus Torvalds /*                                                                          */
42031da177e4SLinus Torvalds /* Routine Description:                                                     */
42041da177e4SLinus Torvalds /*                                                                          */
42051da177e4SLinus Torvalds /*   Free any allocated space for this controller                           */
42061da177e4SLinus Torvalds /*                                                                          */
42071da177e4SLinus Torvalds /****************************************************************************/
42081da177e4SLinus Torvalds static void
42091da177e4SLinus Torvalds ips_free(ips_ha_t * ha)
42101da177e4SLinus Torvalds {
42111da177e4SLinus Torvalds 
42121da177e4SLinus Torvalds 	METHOD_TRACE("ips_free", 1);
42131da177e4SLinus Torvalds 
42141da177e4SLinus Torvalds 	if (ha) {
42151da177e4SLinus Torvalds 		if (ha->enq) {
421632e76961SChristoph Hellwig 			dma_free_coherent(&ha->pcidev->dev, sizeof(IPS_ENQ),
42171da177e4SLinus Torvalds 					    ha->enq, ha->enq_busaddr);
42181da177e4SLinus Torvalds 			ha->enq = NULL;
42191da177e4SLinus Torvalds 		}
42201da177e4SLinus Torvalds 
42211da177e4SLinus Torvalds 		kfree(ha->conf);
42221da177e4SLinus Torvalds 		ha->conf = NULL;
42231da177e4SLinus Torvalds 
42241da177e4SLinus Torvalds 		if (ha->adapt) {
422532e76961SChristoph Hellwig 			dma_free_coherent(&ha->pcidev->dev,
42261da177e4SLinus Torvalds 					    sizeof (IPS_ADAPTER) +
42271da177e4SLinus Torvalds 					    sizeof (IPS_IO_CMD), ha->adapt,
42281da177e4SLinus Torvalds 					    ha->adapt->hw_status_start);
42291da177e4SLinus Torvalds 			ha->adapt = NULL;
42301da177e4SLinus Torvalds 		}
42311da177e4SLinus Torvalds 
42321da177e4SLinus Torvalds 		if (ha->logical_drive_info) {
423332e76961SChristoph Hellwig 			dma_free_coherent(&ha->pcidev->dev,
42341da177e4SLinus Torvalds 					    sizeof (IPS_LD_INFO),
42351da177e4SLinus Torvalds 					    ha->logical_drive_info,
42361da177e4SLinus Torvalds 					    ha->logical_drive_info_dma_addr);
42371da177e4SLinus Torvalds 			ha->logical_drive_info = NULL;
42381da177e4SLinus Torvalds 		}
42391da177e4SLinus Torvalds 
42401da177e4SLinus Torvalds 		kfree(ha->nvram);
42411da177e4SLinus Torvalds 		ha->nvram = NULL;
42421da177e4SLinus Torvalds 
42431da177e4SLinus Torvalds 		kfree(ha->subsys);
42441da177e4SLinus Torvalds 		ha->subsys = NULL;
42451da177e4SLinus Torvalds 
42461da177e4SLinus Torvalds 		if (ha->ioctl_data) {
424732e76961SChristoph Hellwig 			dma_free_coherent(&ha->pcidev->dev, ha->ioctl_len,
42481da177e4SLinus Torvalds 					    ha->ioctl_data, ha->ioctl_busaddr);
42491da177e4SLinus Torvalds 			ha->ioctl_data = NULL;
42501da177e4SLinus Torvalds 			ha->ioctl_datasize = 0;
42511da177e4SLinus Torvalds 			ha->ioctl_len = 0;
42521da177e4SLinus Torvalds 		}
42531da177e4SLinus Torvalds 		ips_deallocatescbs(ha, ha->max_cmds);
42541da177e4SLinus Torvalds 
42551da177e4SLinus Torvalds 		/* free memory mapped (if applicable) */
42561da177e4SLinus Torvalds 		if (ha->mem_ptr) {
42571da177e4SLinus Torvalds 			iounmap(ha->ioremap_ptr);
42581da177e4SLinus Torvalds 			ha->ioremap_ptr = NULL;
42591da177e4SLinus Torvalds 			ha->mem_ptr = NULL;
42601da177e4SLinus Torvalds 		}
42611da177e4SLinus Torvalds 
42621da177e4SLinus Torvalds 		ha->mem_addr = 0;
42631da177e4SLinus Torvalds 
42641da177e4SLinus Torvalds 	}
42651da177e4SLinus Torvalds }
42661da177e4SLinus Torvalds 
42671da177e4SLinus Torvalds /****************************************************************************/
42681da177e4SLinus Torvalds /*                                                                          */
42691da177e4SLinus Torvalds /* Routine Name: ips_deallocatescbs                                         */
42701da177e4SLinus Torvalds /*                                                                          */
42711da177e4SLinus Torvalds /* Routine Description:                                                     */
42721da177e4SLinus Torvalds /*                                                                          */
42731da177e4SLinus Torvalds /*   Free the command blocks                                                */
42741da177e4SLinus Torvalds /*                                                                          */
42751da177e4SLinus Torvalds /****************************************************************************/
42761da177e4SLinus Torvalds static int
42771da177e4SLinus Torvalds ips_deallocatescbs(ips_ha_t * ha, int cmds)
42781da177e4SLinus Torvalds {
42791da177e4SLinus Torvalds 	if (ha->scbs) {
428032e76961SChristoph Hellwig 		dma_free_coherent(&ha->pcidev->dev,
42811da177e4SLinus Torvalds 				    IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * cmds,
42821da177e4SLinus Torvalds 				    ha->scbs->sg_list.list,
42831da177e4SLinus Torvalds 				    ha->scbs->sg_busaddr);
428432e76961SChristoph Hellwig 		dma_free_coherent(&ha->pcidev->dev, sizeof (ips_scb_t) * cmds,
42851da177e4SLinus Torvalds 				    ha->scbs, ha->scbs->scb_busaddr);
42861da177e4SLinus Torvalds 		ha->scbs = NULL;
42871da177e4SLinus Torvalds 	}			/* end if */
42881da177e4SLinus Torvalds 	return 1;
42891da177e4SLinus Torvalds }
42901da177e4SLinus Torvalds 
42911da177e4SLinus Torvalds /****************************************************************************/
42921da177e4SLinus Torvalds /*                                                                          */
42931da177e4SLinus Torvalds /* Routine Name: ips_allocatescbs                                           */
42941da177e4SLinus Torvalds /*                                                                          */
42951da177e4SLinus Torvalds /* Routine Description:                                                     */
42961da177e4SLinus Torvalds /*                                                                          */
42971da177e4SLinus Torvalds /*   Allocate the command blocks                                            */
42981da177e4SLinus Torvalds /*                                                                          */
42991da177e4SLinus Torvalds /****************************************************************************/
43001da177e4SLinus Torvalds static int
43011da177e4SLinus Torvalds ips_allocatescbs(ips_ha_t * ha)
43021da177e4SLinus Torvalds {
43031da177e4SLinus Torvalds 	ips_scb_t *scb_p;
43041da177e4SLinus Torvalds 	IPS_SG_LIST ips_sg;
43051da177e4SLinus Torvalds 	int i;
43061da177e4SLinus Torvalds 	dma_addr_t command_dma, sg_dma;
43071da177e4SLinus Torvalds 
43081da177e4SLinus Torvalds 	METHOD_TRACE("ips_allocatescbs", 1);
43091da177e4SLinus Torvalds 
43101da177e4SLinus Torvalds 	/* Allocate memory for the SCBs */
431132e76961SChristoph Hellwig 	ha->scbs = dma_alloc_coherent(&ha->pcidev->dev,
431232e76961SChristoph Hellwig 			ha->max_cmds * sizeof (ips_scb_t),
431332e76961SChristoph Hellwig 			&command_dma, GFP_KERNEL);
43141da177e4SLinus Torvalds 	if (ha->scbs == NULL)
43151da177e4SLinus Torvalds 		return 0;
431632e76961SChristoph Hellwig 	ips_sg.list = dma_alloc_coherent(&ha->pcidev->dev,
431732e76961SChristoph Hellwig 			IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * ha->max_cmds,
431832e76961SChristoph Hellwig 			&sg_dma, GFP_KERNEL);
43191da177e4SLinus Torvalds 	if (ips_sg.list == NULL) {
432032e76961SChristoph Hellwig 		dma_free_coherent(&ha->pcidev->dev,
43211da177e4SLinus Torvalds 				    ha->max_cmds * sizeof (ips_scb_t), ha->scbs,
43221da177e4SLinus Torvalds 				    command_dma);
43231da177e4SLinus Torvalds 		return 0;
43241da177e4SLinus Torvalds 	}
43251da177e4SLinus Torvalds 
43261da177e4SLinus Torvalds 	memset(ha->scbs, 0, ha->max_cmds * sizeof (ips_scb_t));
43271da177e4SLinus Torvalds 
43281da177e4SLinus Torvalds 	for (i = 0; i < ha->max_cmds; i++) {
43291da177e4SLinus Torvalds 		scb_p = &ha->scbs[i];
43301da177e4SLinus Torvalds 		scb_p->scb_busaddr = command_dma + sizeof (ips_scb_t) * i;
43311da177e4SLinus Torvalds 		/* set up S/G list */
43321da177e4SLinus Torvalds 		if (IPS_USE_ENH_SGLIST(ha)) {
43331da177e4SLinus Torvalds 			scb_p->sg_list.enh_list =
43341da177e4SLinus Torvalds 			    ips_sg.enh_list + i * IPS_MAX_SG;
43351da177e4SLinus Torvalds 			scb_p->sg_busaddr =
43361da177e4SLinus Torvalds 			    sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
43371da177e4SLinus Torvalds 		} else {
43381da177e4SLinus Torvalds 			scb_p->sg_list.std_list =
43391da177e4SLinus Torvalds 			    ips_sg.std_list + i * IPS_MAX_SG;
43401da177e4SLinus Torvalds 			scb_p->sg_busaddr =
43411da177e4SLinus Torvalds 			    sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
43421da177e4SLinus Torvalds 		}
43431da177e4SLinus Torvalds 
43441da177e4SLinus Torvalds 		/* add to the free list */
43451da177e4SLinus Torvalds 		if (i < ha->max_cmds - 1) {
43461da177e4SLinus Torvalds 			scb_p->q_next = ha->scb_freelist;
43471da177e4SLinus Torvalds 			ha->scb_freelist = scb_p;
43481da177e4SLinus Torvalds 		}
43491da177e4SLinus Torvalds 	}
43501da177e4SLinus Torvalds 
43511da177e4SLinus Torvalds 	/* success */
43521da177e4SLinus Torvalds 	return (1);
43531da177e4SLinus Torvalds }
43541da177e4SLinus Torvalds 
43551da177e4SLinus Torvalds /****************************************************************************/
43561da177e4SLinus Torvalds /*                                                                          */
43571da177e4SLinus Torvalds /* Routine Name: ips_init_scb                                               */
43581da177e4SLinus Torvalds /*                                                                          */
43591da177e4SLinus Torvalds /* Routine Description:                                                     */
43601da177e4SLinus Torvalds /*                                                                          */
43611da177e4SLinus Torvalds /*   Initialize a CCB to default values                                     */
43621da177e4SLinus Torvalds /*                                                                          */
43631da177e4SLinus Torvalds /****************************************************************************/
43641da177e4SLinus Torvalds static void
43651da177e4SLinus Torvalds ips_init_scb(ips_ha_t * ha, ips_scb_t * scb)
43661da177e4SLinus Torvalds {
43671da177e4SLinus Torvalds 	IPS_SG_LIST sg_list;
43681da177e4SLinus Torvalds 	uint32_t cmd_busaddr, sg_busaddr;
43691da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_scb", 1);
43701da177e4SLinus Torvalds 
43711da177e4SLinus Torvalds 	if (scb == NULL)
43721da177e4SLinus Torvalds 		return;
43731da177e4SLinus Torvalds 
43741da177e4SLinus Torvalds 	sg_list.list = scb->sg_list.list;
43751da177e4SLinus Torvalds 	cmd_busaddr = scb->scb_busaddr;
43761da177e4SLinus Torvalds 	sg_busaddr = scb->sg_busaddr;
43771da177e4SLinus Torvalds 	/* zero fill */
43781da177e4SLinus Torvalds 	memset(scb, 0, sizeof (ips_scb_t));
43791da177e4SLinus Torvalds 	memset(ha->dummy, 0, sizeof (IPS_IO_CMD));
43801da177e4SLinus Torvalds 
43811da177e4SLinus Torvalds 	/* Initialize dummy command bucket */
43821da177e4SLinus Torvalds 	ha->dummy->op_code = 0xFF;
43831da177e4SLinus Torvalds 	ha->dummy->ccsar = cpu_to_le32(ha->adapt->hw_status_start
43841da177e4SLinus Torvalds 				       + sizeof (IPS_ADAPTER));
43851da177e4SLinus Torvalds 	ha->dummy->command_id = IPS_MAX_CMDS;
43861da177e4SLinus Torvalds 
43871da177e4SLinus Torvalds 	/* set bus address of scb */
43881da177e4SLinus Torvalds 	scb->scb_busaddr = cmd_busaddr;
43891da177e4SLinus Torvalds 	scb->sg_busaddr = sg_busaddr;
43901da177e4SLinus Torvalds 	scb->sg_list.list = sg_list.list;
43911da177e4SLinus Torvalds 
43921da177e4SLinus Torvalds 	/* Neptune Fix */
43931da177e4SLinus Torvalds 	scb->cmd.basic_io.cccr = cpu_to_le32((uint32_t) IPS_BIT_ILE);
43941da177e4SLinus Torvalds 	scb->cmd.basic_io.ccsar = cpu_to_le32(ha->adapt->hw_status_start
43951da177e4SLinus Torvalds 					      + sizeof (IPS_ADAPTER));
43961da177e4SLinus Torvalds }
43971da177e4SLinus Torvalds 
43981da177e4SLinus Torvalds /****************************************************************************/
43991da177e4SLinus Torvalds /*                                                                          */
44001da177e4SLinus Torvalds /* Routine Name: ips_get_scb                                                */
44011da177e4SLinus Torvalds /*                                                                          */
44021da177e4SLinus Torvalds /* Routine Description:                                                     */
44031da177e4SLinus Torvalds /*                                                                          */
44041da177e4SLinus Torvalds /*   Initialize a CCB to default values                                     */
44051da177e4SLinus Torvalds /*                                                                          */
440642b2aa86SJustin P. Mattock /* ASSUMED to be called from within a lock                                 */
44071da177e4SLinus Torvalds /*                                                                          */
44081da177e4SLinus Torvalds /****************************************************************************/
44091da177e4SLinus Torvalds static ips_scb_t *
44101da177e4SLinus Torvalds ips_getscb(ips_ha_t * ha)
44111da177e4SLinus Torvalds {
44121da177e4SLinus Torvalds 	ips_scb_t *scb;
44131da177e4SLinus Torvalds 
44141da177e4SLinus Torvalds 	METHOD_TRACE("ips_getscb", 1);
44151da177e4SLinus Torvalds 
44161da177e4SLinus Torvalds 	if ((scb = ha->scb_freelist) == NULL) {
44171da177e4SLinus Torvalds 
44181da177e4SLinus Torvalds 		return (NULL);
44191da177e4SLinus Torvalds 	}
44201da177e4SLinus Torvalds 
44211da177e4SLinus Torvalds 	ha->scb_freelist = scb->q_next;
44221da177e4SLinus Torvalds 	scb->flags = 0;
44231da177e4SLinus Torvalds 	scb->q_next = NULL;
44241da177e4SLinus Torvalds 
44251da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
44261da177e4SLinus Torvalds 
44271da177e4SLinus Torvalds 	return (scb);
44281da177e4SLinus Torvalds }
44291da177e4SLinus Torvalds 
44301da177e4SLinus Torvalds /****************************************************************************/
44311da177e4SLinus Torvalds /*                                                                          */
44321da177e4SLinus Torvalds /* Routine Name: ips_free_scb                                               */
44331da177e4SLinus Torvalds /*                                                                          */
44341da177e4SLinus Torvalds /* Routine Description:                                                     */
44351da177e4SLinus Torvalds /*                                                                          */
44361da177e4SLinus Torvalds /*   Return an unused CCB back to the free list                             */
44371da177e4SLinus Torvalds /*                                                                          */
44381da177e4SLinus Torvalds /* ASSUMED to be called from within a lock                                  */
44391da177e4SLinus Torvalds /*                                                                          */
44401da177e4SLinus Torvalds /****************************************************************************/
44411da177e4SLinus Torvalds static void
44421da177e4SLinus Torvalds ips_freescb(ips_ha_t * ha, ips_scb_t * scb)
44431da177e4SLinus Torvalds {
44441da177e4SLinus Torvalds 
44451da177e4SLinus Torvalds 	METHOD_TRACE("ips_freescb", 1);
44461da177e4SLinus Torvalds 	if (scb->flags & IPS_SCB_MAP_SG)
44472f4cf91cSFUJITA Tomonori                 scsi_dma_unmap(scb->scsi_cmd);
44481da177e4SLinus Torvalds 	else if (scb->flags & IPS_SCB_MAP_SINGLE)
444932e76961SChristoph Hellwig 		dma_unmap_single(&ha->pcidev->dev, scb->data_busaddr,
445032e76961SChristoph Hellwig 				 scb->data_len, IPS_DMA_DIR(scb));
44511da177e4SLinus Torvalds 
44521da177e4SLinus Torvalds 	/* check to make sure this is not our "special" scb */
44531da177e4SLinus Torvalds 	if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
44541da177e4SLinus Torvalds 		scb->q_next = ha->scb_freelist;
44551da177e4SLinus Torvalds 		ha->scb_freelist = scb;
44561da177e4SLinus Torvalds 	}
44571da177e4SLinus Torvalds }
44581da177e4SLinus Torvalds 
44591da177e4SLinus Torvalds /****************************************************************************/
44601da177e4SLinus Torvalds /*                                                                          */
44611da177e4SLinus Torvalds /* Routine Name: ips_isinit_copperhead                                      */
44621da177e4SLinus Torvalds /*                                                                          */
44631da177e4SLinus Torvalds /* Routine Description:                                                     */
44641da177e4SLinus Torvalds /*                                                                          */
44651da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
44661da177e4SLinus Torvalds /*                                                                          */
44671da177e4SLinus Torvalds /****************************************************************************/
44681da177e4SLinus Torvalds static int
44691da177e4SLinus Torvalds ips_isinit_copperhead(ips_ha_t * ha)
44701da177e4SLinus Torvalds {
44711da177e4SLinus Torvalds 	uint8_t scpr;
44721da177e4SLinus Torvalds 	uint8_t isr;
44731da177e4SLinus Torvalds 
44741da177e4SLinus Torvalds 	METHOD_TRACE("ips_isinit_copperhead", 1);
44751da177e4SLinus Torvalds 
44761da177e4SLinus Torvalds 	isr = inb(ha->io_addr + IPS_REG_HISR);
44771da177e4SLinus Torvalds 	scpr = inb(ha->io_addr + IPS_REG_SCPR);
44781da177e4SLinus Torvalds 
44791da177e4SLinus Torvalds 	if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
44801da177e4SLinus Torvalds 		return (0);
44811da177e4SLinus Torvalds 	else
44821da177e4SLinus Torvalds 		return (1);
44831da177e4SLinus Torvalds }
44841da177e4SLinus Torvalds 
44851da177e4SLinus Torvalds /****************************************************************************/
44861da177e4SLinus Torvalds /*                                                                          */
44871da177e4SLinus Torvalds /* Routine Name: ips_isinit_copperhead_memio                                */
44881da177e4SLinus Torvalds /*                                                                          */
44891da177e4SLinus Torvalds /* Routine Description:                                                     */
44901da177e4SLinus Torvalds /*                                                                          */
44911da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
44921da177e4SLinus Torvalds /*                                                                          */
44931da177e4SLinus Torvalds /****************************************************************************/
44941da177e4SLinus Torvalds static int
44951da177e4SLinus Torvalds ips_isinit_copperhead_memio(ips_ha_t * ha)
44961da177e4SLinus Torvalds {
44971da177e4SLinus Torvalds 	uint8_t isr = 0;
44981da177e4SLinus Torvalds 	uint8_t scpr;
44991da177e4SLinus Torvalds 
45001da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_init_copperhead_memio", 1);
45011da177e4SLinus Torvalds 
45021da177e4SLinus Torvalds 	isr = readb(ha->mem_ptr + IPS_REG_HISR);
45031da177e4SLinus Torvalds 	scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
45041da177e4SLinus Torvalds 
45051da177e4SLinus Torvalds 	if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
45061da177e4SLinus Torvalds 		return (0);
45071da177e4SLinus Torvalds 	else
45081da177e4SLinus Torvalds 		return (1);
45091da177e4SLinus Torvalds }
45101da177e4SLinus Torvalds 
45111da177e4SLinus Torvalds /****************************************************************************/
45121da177e4SLinus Torvalds /*                                                                          */
45131da177e4SLinus Torvalds /* Routine Name: ips_isinit_morpheus                                        */
45141da177e4SLinus Torvalds /*                                                                          */
45151da177e4SLinus Torvalds /* Routine Description:                                                     */
45161da177e4SLinus Torvalds /*                                                                          */
45171da177e4SLinus Torvalds /*   Is controller initialized ?                                            */
45181da177e4SLinus Torvalds /*                                                                          */
45191da177e4SLinus Torvalds /****************************************************************************/
45201da177e4SLinus Torvalds static int
45211da177e4SLinus Torvalds ips_isinit_morpheus(ips_ha_t * ha)
45221da177e4SLinus Torvalds {
45231da177e4SLinus Torvalds 	uint32_t post;
45241da177e4SLinus Torvalds 	uint32_t bits;
45251da177e4SLinus Torvalds 
45261da177e4SLinus Torvalds 	METHOD_TRACE("ips_is_init_morpheus", 1);
45271da177e4SLinus Torvalds 
4528ee807c2dSJack Hammer 	if (ips_isintr_morpheus(ha))
4529ee807c2dSJack Hammer 	    ips_flush_and_reset(ha);
4530ee807c2dSJack Hammer 
45311da177e4SLinus Torvalds 	post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
45321da177e4SLinus Torvalds 	bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
45331da177e4SLinus Torvalds 
45341da177e4SLinus Torvalds 	if (post == 0)
45351da177e4SLinus Torvalds 		return (0);
45361da177e4SLinus Torvalds 	else if (bits & 0x3)
45371da177e4SLinus Torvalds 		return (0);
45381da177e4SLinus Torvalds 	else
45391da177e4SLinus Torvalds 		return (1);
45401da177e4SLinus Torvalds }
45411da177e4SLinus Torvalds 
45421da177e4SLinus Torvalds /****************************************************************************/
45431da177e4SLinus Torvalds /*                                                                          */
4544ee807c2dSJack Hammer /* Routine Name: ips_flush_and_reset                                        */
4545ee807c2dSJack Hammer /*                                                                          */
4546ee807c2dSJack Hammer /* Routine Description:                                                     */
4547ee807c2dSJack Hammer /*                                                                          */
4548ee807c2dSJack Hammer /*   Perform cleanup ( FLUSH and RESET ) when the adapter is in an unknown  */
4549ee807c2dSJack Hammer /*   state ( was trying to INIT and an interrupt was already pending ) ...  */
4550ee807c2dSJack Hammer /*                                                                          */
4551ee807c2dSJack Hammer /****************************************************************************/
4552ee807c2dSJack Hammer static void
4553ee807c2dSJack Hammer ips_flush_and_reset(ips_ha_t *ha)
4554ee807c2dSJack Hammer {
4555ee807c2dSJack Hammer 	ips_scb_t *scb;
4556ee807c2dSJack Hammer 	int  ret;
4557ee807c2dSJack Hammer  	int  time;
4558ee807c2dSJack Hammer 	int  done;
4559ee807c2dSJack Hammer 	dma_addr_t command_dma;
4560ee807c2dSJack Hammer 
4561ee807c2dSJack Hammer 	/* Create a usuable SCB */
456232e76961SChristoph Hellwig 	scb = dma_alloc_coherent(&ha->pcidev->dev, sizeof(ips_scb_t),
456332e76961SChristoph Hellwig 			&command_dma, GFP_KERNEL);
4564ee807c2dSJack Hammer 	if (scb) {
4565ee807c2dSJack Hammer 	    memset(scb, 0, sizeof(ips_scb_t));
4566ee807c2dSJack Hammer 	    ips_init_scb(ha, scb);
4567ee807c2dSJack Hammer 	    scb->scb_busaddr = command_dma;
4568ee807c2dSJack Hammer 
4569ee807c2dSJack Hammer 	    scb->timeout = ips_cmd_timeout;
4570ee807c2dSJack Hammer 	    scb->cdb[0] = IPS_CMD_FLUSH;
4571ee807c2dSJack Hammer 
4572ee807c2dSJack Hammer 	    scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
4573ee807c2dSJack Hammer 	    scb->cmd.flush_cache.command_id = IPS_MAX_CMDS;   /* Use an ID that would otherwise not exist */
4574ee807c2dSJack Hammer 	    scb->cmd.flush_cache.state = IPS_NORM_STATE;
4575ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved = 0;
4576ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved2 = 0;
4577ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved3 = 0;
4578ee807c2dSJack Hammer 	    scb->cmd.flush_cache.reserved4 = 0;
4579ee807c2dSJack Hammer 
4580ee807c2dSJack Hammer 	    ret = ips_send_cmd(ha, scb);                      /* Send the Flush Command */
4581ee807c2dSJack Hammer 
4582ee807c2dSJack Hammer 	    if (ret == IPS_SUCCESS) {
4583ee807c2dSJack Hammer 	        time = 60 * IPS_ONE_SEC;	              /* Max Wait time is 60 seconds */
4584ee807c2dSJack Hammer 	        done = 0;
4585ee807c2dSJack Hammer 
4586ee807c2dSJack Hammer 	        while ((time > 0) && (!done)) {
4587ee807c2dSJack Hammer 		   done = ips_poll_for_flush_complete(ha);
4588ee807c2dSJack Hammer 	           /* This may look evil, but it's only done during extremely rare start-up conditions ! */
4589ee807c2dSJack Hammer 	           udelay(1000);
4590ee807c2dSJack Hammer 	           time--;
4591ee807c2dSJack Hammer 	        }
4592ee807c2dSJack Hammer         }
4593ee807c2dSJack Hammer 	}
4594ee807c2dSJack Hammer 
4595ee807c2dSJack Hammer 	/* Now RESET and INIT the adapter */
4596ee807c2dSJack Hammer 	(*ha->func.reset) (ha);
4597ee807c2dSJack Hammer 
459832e76961SChristoph Hellwig 	dma_free_coherent(&ha->pcidev->dev, sizeof(ips_scb_t), scb, command_dma);
4599ee807c2dSJack Hammer 	return;
4600ee807c2dSJack Hammer }
4601ee807c2dSJack Hammer 
4602ee807c2dSJack Hammer /****************************************************************************/
4603ee807c2dSJack Hammer /*                                                                          */
4604ee807c2dSJack Hammer /* Routine Name: ips_poll_for_flush_complete                                */
4605ee807c2dSJack Hammer /*                                                                          */
4606ee807c2dSJack Hammer /* Routine Description:                                                     */
4607ee807c2dSJack Hammer /*                                                                          */
4608ee807c2dSJack Hammer /*   Poll for the Flush Command issued by ips_flush_and_reset() to complete */
4609ee807c2dSJack Hammer /*   All other responses are just taken off the queue and ignored           */
4610ee807c2dSJack Hammer /*                                                                          */
4611ee807c2dSJack Hammer /****************************************************************************/
4612ee807c2dSJack Hammer static int
4613ee807c2dSJack Hammer ips_poll_for_flush_complete(ips_ha_t * ha)
4614ee807c2dSJack Hammer {
4615ee807c2dSJack Hammer 	IPS_STATUS cstatus;
4616ee807c2dSJack Hammer 
4617ee807c2dSJack Hammer 	while (TRUE) {
4618ee807c2dSJack Hammer 	    cstatus.value = (*ha->func.statupd) (ha);
4619ee807c2dSJack Hammer 
4620ee807c2dSJack Hammer 	    if (cstatus.value == 0xffffffff)      /* If No Interrupt to process */
4621ee807c2dSJack Hammer 			break;
4622ee807c2dSJack Hammer 
4623ee807c2dSJack Hammer 	    /* Success is when we see the Flush Command ID */
4624ee807c2dSJack Hammer 	    if (cstatus.fields.command_id == IPS_MAX_CMDS)
4625ee807c2dSJack Hammer 	        return 1;
4626ee807c2dSJack Hammer 	 }
4627ee807c2dSJack Hammer 
4628ee807c2dSJack Hammer 	return 0;
46290ee957cbSJames Bottomley }
4630ee807c2dSJack Hammer 
4631ee807c2dSJack Hammer /****************************************************************************/
4632ee807c2dSJack Hammer /*                                                                          */
46331da177e4SLinus Torvalds /* Routine Name: ips_enable_int_copperhead                                  */
46341da177e4SLinus Torvalds /*                                                                          */
46351da177e4SLinus Torvalds /* Routine Description:                                                     */
46361da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46371da177e4SLinus Torvalds /*                                                                          */
46381da177e4SLinus Torvalds /****************************************************************************/
46391da177e4SLinus Torvalds static void
46401da177e4SLinus Torvalds ips_enable_int_copperhead(ips_ha_t * ha)
46411da177e4SLinus Torvalds {
46421da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_copperhead", 1);
46431da177e4SLinus Torvalds 
46441da177e4SLinus Torvalds 	outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
46451da177e4SLinus Torvalds 	inb(ha->io_addr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/
46461da177e4SLinus Torvalds }
46471da177e4SLinus Torvalds 
46481da177e4SLinus Torvalds /****************************************************************************/
46491da177e4SLinus Torvalds /*                                                                          */
46501da177e4SLinus Torvalds /* Routine Name: ips_enable_int_copperhead_memio                            */
46511da177e4SLinus Torvalds /*                                                                          */
46521da177e4SLinus Torvalds /* Routine Description:                                                     */
46531da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46541da177e4SLinus Torvalds /*                                                                          */
46551da177e4SLinus Torvalds /****************************************************************************/
46561da177e4SLinus Torvalds static void
46571da177e4SLinus Torvalds ips_enable_int_copperhead_memio(ips_ha_t * ha)
46581da177e4SLinus Torvalds {
46591da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
46601da177e4SLinus Torvalds 
46611da177e4SLinus Torvalds 	writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
46621da177e4SLinus Torvalds 	readb(ha->mem_ptr + IPS_REG_HISR);	/*Ensure PCI Posting Completes*/
46631da177e4SLinus Torvalds }
46641da177e4SLinus Torvalds 
46651da177e4SLinus Torvalds /****************************************************************************/
46661da177e4SLinus Torvalds /*                                                                          */
46671da177e4SLinus Torvalds /* Routine Name: ips_enable_int_morpheus                                    */
46681da177e4SLinus Torvalds /*                                                                          */
46691da177e4SLinus Torvalds /* Routine Description:                                                     */
46701da177e4SLinus Torvalds /*   Turn on interrupts                                                     */
46711da177e4SLinus Torvalds /*                                                                          */
46721da177e4SLinus Torvalds /****************************************************************************/
46731da177e4SLinus Torvalds static void
46741da177e4SLinus Torvalds ips_enable_int_morpheus(ips_ha_t * ha)
46751da177e4SLinus Torvalds {
46761da177e4SLinus Torvalds 	uint32_t Oimr;
46771da177e4SLinus Torvalds 
46781da177e4SLinus Torvalds 	METHOD_TRACE("ips_enable_int_morpheus", 1);
46791da177e4SLinus Torvalds 
46801da177e4SLinus Torvalds 	Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
46811da177e4SLinus Torvalds 	Oimr &= ~0x08;
46821da177e4SLinus Torvalds 	writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
46831da177e4SLinus Torvalds 	readl(ha->mem_ptr + IPS_REG_I960_OIMR);	/*Ensure PCI Posting Completes*/
46841da177e4SLinus Torvalds }
46851da177e4SLinus Torvalds 
46861da177e4SLinus Torvalds /****************************************************************************/
46871da177e4SLinus Torvalds /*                                                                          */
46881da177e4SLinus Torvalds /* Routine Name: ips_init_copperhead                                        */
46891da177e4SLinus Torvalds /*                                                                          */
46901da177e4SLinus Torvalds /* Routine Description:                                                     */
46911da177e4SLinus Torvalds /*                                                                          */
46921da177e4SLinus Torvalds /*   Initialize a copperhead controller                                     */
46931da177e4SLinus Torvalds /*                                                                          */
46941da177e4SLinus Torvalds /****************************************************************************/
46951da177e4SLinus Torvalds static int
46961da177e4SLinus Torvalds ips_init_copperhead(ips_ha_t * ha)
46971da177e4SLinus Torvalds {
46981da177e4SLinus Torvalds 	uint8_t Isr;
46991da177e4SLinus Torvalds 	uint8_t Cbsp;
47001da177e4SLinus Torvalds 	uint8_t PostByte[IPS_MAX_POST_BYTES];
47011da177e4SLinus Torvalds 	uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
47021da177e4SLinus Torvalds 	int i, j;
47031da177e4SLinus Torvalds 
47041da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_copperhead", 1);
47051da177e4SLinus Torvalds 
47061da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
47071da177e4SLinus Torvalds 		for (j = 0; j < 45; j++) {
47081da177e4SLinus Torvalds 			Isr = inb(ha->io_addr + IPS_REG_HISR);
47091da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
47101da177e4SLinus Torvalds 				break;
47111da177e4SLinus Torvalds 
47121da177e4SLinus Torvalds 			/* Delay for 1 Second */
4713bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
47141da177e4SLinus Torvalds 		}
47151da177e4SLinus Torvalds 
47161da177e4SLinus Torvalds 		if (j >= 45)
47171da177e4SLinus Torvalds 			/* error occurred */
47181da177e4SLinus Torvalds 			return (0);
47191da177e4SLinus Torvalds 
47201da177e4SLinus Torvalds 		PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
47211da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
47221da177e4SLinus Torvalds 	}
47231da177e4SLinus Torvalds 
47241da177e4SLinus Torvalds 	if (PostByte[0] < IPS_GOOD_POST_STATUS) {
47251da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
47261da177e4SLinus Torvalds 			   "reset controller fails (post status %x %x).\n",
47271da177e4SLinus Torvalds 			   PostByte[0], PostByte[1]);
47281da177e4SLinus Torvalds 
47291da177e4SLinus Torvalds 		return (0);
47301da177e4SLinus Torvalds 	}
47311da177e4SLinus Torvalds 
47321da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
47331da177e4SLinus Torvalds 		for (j = 0; j < 240; j++) {
47341da177e4SLinus Torvalds 			Isr = inb(ha->io_addr + IPS_REG_HISR);
47351da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
47361da177e4SLinus Torvalds 				break;
47371da177e4SLinus Torvalds 
47381da177e4SLinus Torvalds 			/* Delay for 1 Second */
4739bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
47401da177e4SLinus Torvalds 		}
47411da177e4SLinus Torvalds 
47421da177e4SLinus Torvalds 		if (j >= 240)
47431da177e4SLinus Torvalds 			/* error occurred */
47441da177e4SLinus Torvalds 			return (0);
47451da177e4SLinus Torvalds 
47461da177e4SLinus Torvalds 		ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
47471da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
47481da177e4SLinus Torvalds 	}
47491da177e4SLinus Torvalds 
47501da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
47511da177e4SLinus Torvalds 		Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
47521da177e4SLinus Torvalds 
47531da177e4SLinus Torvalds 		if ((Cbsp & IPS_BIT_OP) == 0)
47541da177e4SLinus Torvalds 			break;
47551da177e4SLinus Torvalds 
47561da177e4SLinus Torvalds 		/* Delay for 1 Second */
4757bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
47581da177e4SLinus Torvalds 	}
47591da177e4SLinus Torvalds 
47601da177e4SLinus Torvalds 	if (i >= 240)
47611da177e4SLinus Torvalds 		/* reset failed */
47621da177e4SLinus Torvalds 		return (0);
47631da177e4SLinus Torvalds 
47641da177e4SLinus Torvalds 	/* setup CCCR */
4765db3cc200SJames Bottomley 	outl(0x1010, ha->io_addr + IPS_REG_CCCR);
47661da177e4SLinus Torvalds 
47671da177e4SLinus Torvalds 	/* Enable busmastering */
47681da177e4SLinus Torvalds 	outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
47691da177e4SLinus Torvalds 
47708a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
47711da177e4SLinus Torvalds 		/* fix for anaconda64 */
47721da177e4SLinus Torvalds 		outl(0, ha->io_addr + IPS_REG_NDAE);
47731da177e4SLinus Torvalds 
47741da177e4SLinus Torvalds 	/* Enable interrupts */
47751da177e4SLinus Torvalds 	outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
47761da177e4SLinus Torvalds 
47771da177e4SLinus Torvalds 	return (1);
47781da177e4SLinus Torvalds }
47791da177e4SLinus Torvalds 
47801da177e4SLinus Torvalds /****************************************************************************/
47811da177e4SLinus Torvalds /*                                                                          */
47821da177e4SLinus Torvalds /* Routine Name: ips_init_copperhead_memio                                  */
47831da177e4SLinus Torvalds /*                                                                          */
47841da177e4SLinus Torvalds /* Routine Description:                                                     */
47851da177e4SLinus Torvalds /*                                                                          */
47861da177e4SLinus Torvalds /*   Initialize a copperhead controller with memory mapped I/O              */
47871da177e4SLinus Torvalds /*                                                                          */
47881da177e4SLinus Torvalds /****************************************************************************/
47891da177e4SLinus Torvalds static int
47901da177e4SLinus Torvalds ips_init_copperhead_memio(ips_ha_t * ha)
47911da177e4SLinus Torvalds {
47921da177e4SLinus Torvalds 	uint8_t Isr = 0;
47931da177e4SLinus Torvalds 	uint8_t Cbsp;
47941da177e4SLinus Torvalds 	uint8_t PostByte[IPS_MAX_POST_BYTES];
47951da177e4SLinus Torvalds 	uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
47961da177e4SLinus Torvalds 	int i, j;
47971da177e4SLinus Torvalds 
47981da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_copperhead_memio", 1);
47991da177e4SLinus Torvalds 
48001da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
48011da177e4SLinus Torvalds 		for (j = 0; j < 45; j++) {
48021da177e4SLinus Torvalds 			Isr = readb(ha->mem_ptr + IPS_REG_HISR);
48031da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
48041da177e4SLinus Torvalds 				break;
48051da177e4SLinus Torvalds 
48061da177e4SLinus Torvalds 			/* Delay for 1 Second */
4807bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
48081da177e4SLinus Torvalds 		}
48091da177e4SLinus Torvalds 
48101da177e4SLinus Torvalds 		if (j >= 45)
48111da177e4SLinus Torvalds 			/* error occurred */
48121da177e4SLinus Torvalds 			return (0);
48131da177e4SLinus Torvalds 
48141da177e4SLinus Torvalds 		PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
48151da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
48161da177e4SLinus Torvalds 	}
48171da177e4SLinus Torvalds 
48181da177e4SLinus Torvalds 	if (PostByte[0] < IPS_GOOD_POST_STATUS) {
48191da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
48201da177e4SLinus Torvalds 			   "reset controller fails (post status %x %x).\n",
48211da177e4SLinus Torvalds 			   PostByte[0], PostByte[1]);
48221da177e4SLinus Torvalds 
48231da177e4SLinus Torvalds 		return (0);
48241da177e4SLinus Torvalds 	}
48251da177e4SLinus Torvalds 
48261da177e4SLinus Torvalds 	for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
48271da177e4SLinus Torvalds 		for (j = 0; j < 240; j++) {
48281da177e4SLinus Torvalds 			Isr = readb(ha->mem_ptr + IPS_REG_HISR);
48291da177e4SLinus Torvalds 			if (Isr & IPS_BIT_GHI)
48301da177e4SLinus Torvalds 				break;
48311da177e4SLinus Torvalds 
48321da177e4SLinus Torvalds 			/* Delay for 1 Second */
4833bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
48341da177e4SLinus Torvalds 		}
48351da177e4SLinus Torvalds 
48361da177e4SLinus Torvalds 		if (j >= 240)
48371da177e4SLinus Torvalds 			/* error occurred */
48381da177e4SLinus Torvalds 			return (0);
48391da177e4SLinus Torvalds 
48401da177e4SLinus Torvalds 		ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
48411da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
48421da177e4SLinus Torvalds 	}
48431da177e4SLinus Torvalds 
48441da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
48451da177e4SLinus Torvalds 		Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
48461da177e4SLinus Torvalds 
48471da177e4SLinus Torvalds 		if ((Cbsp & IPS_BIT_OP) == 0)
48481da177e4SLinus Torvalds 			break;
48491da177e4SLinus Torvalds 
48501da177e4SLinus Torvalds 		/* Delay for 1 Second */
4851bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
48521da177e4SLinus Torvalds 	}
48531da177e4SLinus Torvalds 
48541da177e4SLinus Torvalds 	if (i >= 240)
48551da177e4SLinus Torvalds 		/* error occurred */
48561da177e4SLinus Torvalds 		return (0);
48571da177e4SLinus Torvalds 
48581da177e4SLinus Torvalds 	/* setup CCCR */
48591da177e4SLinus Torvalds 	writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
48601da177e4SLinus Torvalds 
48611da177e4SLinus Torvalds 	/* Enable busmastering */
48621da177e4SLinus Torvalds 	writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
48631da177e4SLinus Torvalds 
48648a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
48651da177e4SLinus Torvalds 		/* fix for anaconda64 */
48661da177e4SLinus Torvalds 		writel(0, ha->mem_ptr + IPS_REG_NDAE);
48671da177e4SLinus Torvalds 
48681da177e4SLinus Torvalds 	/* Enable interrupts */
48691da177e4SLinus Torvalds 	writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
48701da177e4SLinus Torvalds 
48711da177e4SLinus Torvalds 	/* if we get here then everything went OK */
48721da177e4SLinus Torvalds 	return (1);
48731da177e4SLinus Torvalds }
48741da177e4SLinus Torvalds 
48751da177e4SLinus Torvalds /****************************************************************************/
48761da177e4SLinus Torvalds /*                                                                          */
48771da177e4SLinus Torvalds /* Routine Name: ips_init_morpheus                                          */
48781da177e4SLinus Torvalds /*                                                                          */
48791da177e4SLinus Torvalds /* Routine Description:                                                     */
48801da177e4SLinus Torvalds /*                                                                          */
48811da177e4SLinus Torvalds /*   Initialize a morpheus controller                                       */
48821da177e4SLinus Torvalds /*                                                                          */
48831da177e4SLinus Torvalds /****************************************************************************/
48841da177e4SLinus Torvalds static int
48851da177e4SLinus Torvalds ips_init_morpheus(ips_ha_t * ha)
48861da177e4SLinus Torvalds {
48871da177e4SLinus Torvalds 	uint32_t Post;
48881da177e4SLinus Torvalds 	uint32_t Config;
48891da177e4SLinus Torvalds 	uint32_t Isr;
48901da177e4SLinus Torvalds 	uint32_t Oimr;
48911da177e4SLinus Torvalds 	int i;
48921da177e4SLinus Torvalds 
48931da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_morpheus", 1);
48941da177e4SLinus Torvalds 
48951da177e4SLinus Torvalds 	/* Wait up to 45 secs for Post */
48961da177e4SLinus Torvalds 	for (i = 0; i < 45; i++) {
48971da177e4SLinus Torvalds 		Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
48981da177e4SLinus Torvalds 
48991da177e4SLinus Torvalds 		if (Isr & IPS_BIT_I960_MSG0I)
49001da177e4SLinus Torvalds 			break;
49011da177e4SLinus Torvalds 
49021da177e4SLinus Torvalds 		/* Delay for 1 Second */
4903bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
49041da177e4SLinus Torvalds 	}
49051da177e4SLinus Torvalds 
49061da177e4SLinus Torvalds 	if (i >= 45) {
49071da177e4SLinus Torvalds 		/* error occurred */
49081da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49091da177e4SLinus Torvalds 			   "timeout waiting for post.\n");
49101da177e4SLinus Torvalds 
49111da177e4SLinus Torvalds 		return (0);
49121da177e4SLinus Torvalds 	}
49131da177e4SLinus Torvalds 
49141da177e4SLinus Torvalds 	Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
49151da177e4SLinus Torvalds 
49161da177e4SLinus Torvalds 	if (Post == 0x4F00) {	/* If Flashing the Battery PIC         */
49171da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49181da177e4SLinus Torvalds 			   "Flashing Battery PIC, Please wait ...\n");
49191da177e4SLinus Torvalds 
49201da177e4SLinus Torvalds 		/* Clear the interrupt bit */
49211da177e4SLinus Torvalds 		Isr = (uint32_t) IPS_BIT_I960_MSG0I;
49221da177e4SLinus Torvalds 		writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49231da177e4SLinus Torvalds 
49241da177e4SLinus Torvalds 		for (i = 0; i < 120; i++) {	/*    Wait Up to 2 Min. for Completion */
49251da177e4SLinus Torvalds 			Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
49261da177e4SLinus Torvalds 			if (Post != 0x4F00)
49271da177e4SLinus Torvalds 				break;
49281da177e4SLinus Torvalds 			/* Delay for 1 Second */
4929bf471341SAndrew Morton 			MDELAY(IPS_ONE_SEC);
49301da177e4SLinus Torvalds 		}
49311da177e4SLinus Torvalds 
49321da177e4SLinus Torvalds 		if (i >= 120) {
49331da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
49341da177e4SLinus Torvalds 				   "timeout waiting for Battery PIC Flash\n");
49351da177e4SLinus Torvalds 			return (0);
49361da177e4SLinus Torvalds 		}
49371da177e4SLinus Torvalds 
49381da177e4SLinus Torvalds 	}
49391da177e4SLinus Torvalds 
49401da177e4SLinus Torvalds 	/* Clear the interrupt bit */
49411da177e4SLinus Torvalds 	Isr = (uint32_t) IPS_BIT_I960_MSG0I;
49421da177e4SLinus Torvalds 	writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49431da177e4SLinus Torvalds 
49441da177e4SLinus Torvalds 	if (Post < (IPS_GOOD_POST_STATUS << 8)) {
49451da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49461da177e4SLinus Torvalds 			   "reset controller fails (post status %x).\n", Post);
49471da177e4SLinus Torvalds 
49481da177e4SLinus Torvalds 		return (0);
49491da177e4SLinus Torvalds 	}
49501da177e4SLinus Torvalds 
49511da177e4SLinus Torvalds 	/* Wait up to 240 secs for config bytes */
49521da177e4SLinus Torvalds 	for (i = 0; i < 240; i++) {
49531da177e4SLinus Torvalds 		Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
49541da177e4SLinus Torvalds 
49551da177e4SLinus Torvalds 		if (Isr & IPS_BIT_I960_MSG1I)
49561da177e4SLinus Torvalds 			break;
49571da177e4SLinus Torvalds 
49581da177e4SLinus Torvalds 		/* Delay for 1 Second */
4959bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
49601da177e4SLinus Torvalds 	}
49611da177e4SLinus Torvalds 
49621da177e4SLinus Torvalds 	if (i >= 240) {
49631da177e4SLinus Torvalds 		/* error occurred */
49641da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
49651da177e4SLinus Torvalds 			   "timeout waiting for config.\n");
49661da177e4SLinus Torvalds 
49671da177e4SLinus Torvalds 		return (0);
49681da177e4SLinus Torvalds 	}
49691da177e4SLinus Torvalds 
49701da177e4SLinus Torvalds 	Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
49711da177e4SLinus Torvalds 
49721da177e4SLinus Torvalds 	/* Clear interrupt bit */
49731da177e4SLinus Torvalds 	Isr = (uint32_t) IPS_BIT_I960_MSG1I;
49741da177e4SLinus Torvalds 	writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
49751da177e4SLinus Torvalds 
49761da177e4SLinus Torvalds 	/* Turn on the interrupts */
49771da177e4SLinus Torvalds 	Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
49781da177e4SLinus Torvalds 	Oimr &= ~0x8;
49791da177e4SLinus Torvalds 	writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
49801da177e4SLinus Torvalds 
49811da177e4SLinus Torvalds 	/* if we get here then everything went OK */
49821da177e4SLinus Torvalds 
49831da177e4SLinus Torvalds 	/* Since we did a RESET, an EraseStripeLock may be needed */
49841da177e4SLinus Torvalds 	if (Post == 0xEF10) {
49851da177e4SLinus Torvalds 		if ((Config == 0x000F) || (Config == 0x0009))
49861da177e4SLinus Torvalds 			ha->requires_esl = 1;
49871da177e4SLinus Torvalds 	}
49881da177e4SLinus Torvalds 
49891da177e4SLinus Torvalds 	return (1);
49901da177e4SLinus Torvalds }
49911da177e4SLinus Torvalds 
49921da177e4SLinus Torvalds /****************************************************************************/
49931da177e4SLinus Torvalds /*                                                                          */
49941da177e4SLinus Torvalds /* Routine Name: ips_reset_copperhead                                       */
49951da177e4SLinus Torvalds /*                                                                          */
49961da177e4SLinus Torvalds /* Routine Description:                                                     */
49971da177e4SLinus Torvalds /*                                                                          */
49981da177e4SLinus Torvalds /*   Reset the controller                                                   */
49991da177e4SLinus Torvalds /*                                                                          */
50001da177e4SLinus Torvalds /****************************************************************************/
50011da177e4SLinus Torvalds static int
50021da177e4SLinus Torvalds ips_reset_copperhead(ips_ha_t * ha)
50031da177e4SLinus Torvalds {
50041da177e4SLinus Torvalds 	int reset_counter;
50051da177e4SLinus Torvalds 
50061da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_copperhead", 1);
50071da177e4SLinus Torvalds 
50081da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
50098a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->io_addr, ha->pcidev->irq);
50101da177e4SLinus Torvalds 
50111da177e4SLinus Torvalds 	reset_counter = 0;
50121da177e4SLinus Torvalds 
50131da177e4SLinus Torvalds 	while (reset_counter < 2) {
50141da177e4SLinus Torvalds 		reset_counter++;
50151da177e4SLinus Torvalds 
50161da177e4SLinus Torvalds 		outb(IPS_BIT_RST, ha->io_addr + IPS_REG_SCPR);
50171da177e4SLinus Torvalds 
50181da177e4SLinus Torvalds 		/* Delay for 1 Second */
5019bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50201da177e4SLinus Torvalds 
50211da177e4SLinus Torvalds 		outb(0, ha->io_addr + IPS_REG_SCPR);
50221da177e4SLinus Torvalds 
50231da177e4SLinus Torvalds 		/* Delay for 1 Second */
5024bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50251da177e4SLinus Torvalds 
50261da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
50271da177e4SLinus Torvalds 			break;
50281da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
50291da177e4SLinus Torvalds 
50301da177e4SLinus Torvalds 			return (0);
50311da177e4SLinus Torvalds 		}
50321da177e4SLinus Torvalds 	}
50331da177e4SLinus Torvalds 
50341da177e4SLinus Torvalds 	return (1);
50351da177e4SLinus Torvalds }
50361da177e4SLinus Torvalds 
50371da177e4SLinus Torvalds /****************************************************************************/
50381da177e4SLinus Torvalds /*                                                                          */
50391da177e4SLinus Torvalds /* Routine Name: ips_reset_copperhead_memio                                 */
50401da177e4SLinus Torvalds /*                                                                          */
50411da177e4SLinus Torvalds /* Routine Description:                                                     */
50421da177e4SLinus Torvalds /*                                                                          */
50431da177e4SLinus Torvalds /*   Reset the controller                                                   */
50441da177e4SLinus Torvalds /*                                                                          */
50451da177e4SLinus Torvalds /****************************************************************************/
50461da177e4SLinus Torvalds static int
50471da177e4SLinus Torvalds ips_reset_copperhead_memio(ips_ha_t * ha)
50481da177e4SLinus Torvalds {
50491da177e4SLinus Torvalds 	int reset_counter;
50501da177e4SLinus Torvalds 
50511da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_copperhead_memio", 1);
50521da177e4SLinus Torvalds 
50531da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
50548a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq);
50551da177e4SLinus Torvalds 
50561da177e4SLinus Torvalds 	reset_counter = 0;
50571da177e4SLinus Torvalds 
50581da177e4SLinus Torvalds 	while (reset_counter < 2) {
50591da177e4SLinus Torvalds 		reset_counter++;
50601da177e4SLinus Torvalds 
50611da177e4SLinus Torvalds 		writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
50621da177e4SLinus Torvalds 
50631da177e4SLinus Torvalds 		/* Delay for 1 Second */
5064bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50651da177e4SLinus Torvalds 
50661da177e4SLinus Torvalds 		writeb(0, ha->mem_ptr + IPS_REG_SCPR);
50671da177e4SLinus Torvalds 
50681da177e4SLinus Torvalds 		/* Delay for 1 Second */
5069bf471341SAndrew Morton 		MDELAY(IPS_ONE_SEC);
50701da177e4SLinus Torvalds 
50711da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
50721da177e4SLinus Torvalds 			break;
50731da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
50741da177e4SLinus Torvalds 
50751da177e4SLinus Torvalds 			return (0);
50761da177e4SLinus Torvalds 		}
50771da177e4SLinus Torvalds 	}
50781da177e4SLinus Torvalds 
50791da177e4SLinus Torvalds 	return (1);
50801da177e4SLinus Torvalds }
50811da177e4SLinus Torvalds 
50821da177e4SLinus Torvalds /****************************************************************************/
50831da177e4SLinus Torvalds /*                                                                          */
50841da177e4SLinus Torvalds /* Routine Name: ips_reset_morpheus                                         */
50851da177e4SLinus Torvalds /*                                                                          */
50861da177e4SLinus Torvalds /* Routine Description:                                                     */
50871da177e4SLinus Torvalds /*                                                                          */
50881da177e4SLinus Torvalds /*   Reset the controller                                                   */
50891da177e4SLinus Torvalds /*                                                                          */
50901da177e4SLinus Torvalds /****************************************************************************/
50911da177e4SLinus Torvalds static int
50921da177e4SLinus Torvalds ips_reset_morpheus(ips_ha_t * ha)
50931da177e4SLinus Torvalds {
50941da177e4SLinus Torvalds 	int reset_counter;
50951da177e4SLinus Torvalds 	uint8_t junk;
50961da177e4SLinus Torvalds 
50971da177e4SLinus Torvalds 	METHOD_TRACE("ips_reset_morpheus", 1);
50981da177e4SLinus Torvalds 
50991da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
51008a694cc8SJeff Garzik 		  ips_name, ha->host_num, ha->mem_addr, ha->pcidev->irq);
51011da177e4SLinus Torvalds 
51021da177e4SLinus Torvalds 	reset_counter = 0;
51031da177e4SLinus Torvalds 
51041da177e4SLinus Torvalds 	while (reset_counter < 2) {
51051da177e4SLinus Torvalds 		reset_counter++;
51061da177e4SLinus Torvalds 
51071da177e4SLinus Torvalds 		writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
51081da177e4SLinus Torvalds 
51091da177e4SLinus Torvalds 		/* Delay for 5 Seconds */
5110bf471341SAndrew Morton 		MDELAY(5 * IPS_ONE_SEC);
51111da177e4SLinus Torvalds 
51121da177e4SLinus Torvalds 		/* Do a PCI config read to wait for adapter */
51131da177e4SLinus Torvalds 		pci_read_config_byte(ha->pcidev, 4, &junk);
51141da177e4SLinus Torvalds 
51151da177e4SLinus Torvalds 		if ((*ha->func.init) (ha))
51161da177e4SLinus Torvalds 			break;
51171da177e4SLinus Torvalds 		else if (reset_counter >= 2) {
51181da177e4SLinus Torvalds 
51191da177e4SLinus Torvalds 			return (0);
51201da177e4SLinus Torvalds 		}
51211da177e4SLinus Torvalds 	}
51221da177e4SLinus Torvalds 
51231da177e4SLinus Torvalds 	return (1);
51241da177e4SLinus Torvalds }
51251da177e4SLinus Torvalds 
51261da177e4SLinus Torvalds /****************************************************************************/
51271da177e4SLinus Torvalds /*                                                                          */
51281da177e4SLinus Torvalds /* Routine Name: ips_statinit                                               */
51291da177e4SLinus Torvalds /*                                                                          */
51301da177e4SLinus Torvalds /* Routine Description:                                                     */
51311da177e4SLinus Torvalds /*                                                                          */
51321da177e4SLinus Torvalds /*   Initialize the status queues on the controller                         */
51331da177e4SLinus Torvalds /*                                                                          */
51341da177e4SLinus Torvalds /****************************************************************************/
51351da177e4SLinus Torvalds static void
51361da177e4SLinus Torvalds ips_statinit(ips_ha_t * ha)
51371da177e4SLinus Torvalds {
51381da177e4SLinus Torvalds 	uint32_t phys_status_start;
51391da177e4SLinus Torvalds 
51401da177e4SLinus Torvalds 	METHOD_TRACE("ips_statinit", 1);
51411da177e4SLinus Torvalds 
51421da177e4SLinus Torvalds 	ha->adapt->p_status_start = ha->adapt->status;
51431da177e4SLinus Torvalds 	ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
51441da177e4SLinus Torvalds 	ha->adapt->p_status_tail = ha->adapt->status;
51451da177e4SLinus Torvalds 
51461da177e4SLinus Torvalds 	phys_status_start = ha->adapt->hw_status_start;
5147db3cc200SJames Bottomley 	outl(phys_status_start, ha->io_addr + IPS_REG_SQSR);
5148db3cc200SJames Bottomley 	outl(phys_status_start + IPS_STATUS_Q_SIZE,
51491da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQER);
5150db3cc200SJames Bottomley 	outl(phys_status_start + IPS_STATUS_SIZE,
51511da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQHR);
5152db3cc200SJames Bottomley 	outl(phys_status_start, ha->io_addr + IPS_REG_SQTR);
51531da177e4SLinus Torvalds 
51541da177e4SLinus Torvalds 	ha->adapt->hw_status_tail = phys_status_start;
51551da177e4SLinus Torvalds }
51561da177e4SLinus Torvalds 
51571da177e4SLinus Torvalds /****************************************************************************/
51581da177e4SLinus Torvalds /*                                                                          */
51591da177e4SLinus Torvalds /* Routine Name: ips_statinit_memio                                         */
51601da177e4SLinus Torvalds /*                                                                          */
51611da177e4SLinus Torvalds /* Routine Description:                                                     */
51621da177e4SLinus Torvalds /*                                                                          */
51631da177e4SLinus Torvalds /*   Initialize the status queues on the controller                         */
51641da177e4SLinus Torvalds /*                                                                          */
51651da177e4SLinus Torvalds /****************************************************************************/
51661da177e4SLinus Torvalds static void
51671da177e4SLinus Torvalds ips_statinit_memio(ips_ha_t * ha)
51681da177e4SLinus Torvalds {
51691da177e4SLinus Torvalds 	uint32_t phys_status_start;
51701da177e4SLinus Torvalds 
51711da177e4SLinus Torvalds 	METHOD_TRACE("ips_statinit_memio", 1);
51721da177e4SLinus Torvalds 
51731da177e4SLinus Torvalds 	ha->adapt->p_status_start = ha->adapt->status;
51741da177e4SLinus Torvalds 	ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
51751da177e4SLinus Torvalds 	ha->adapt->p_status_tail = ha->adapt->status;
51761da177e4SLinus Torvalds 
51771da177e4SLinus Torvalds 	phys_status_start = ha->adapt->hw_status_start;
51781da177e4SLinus Torvalds 	writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
51791da177e4SLinus Torvalds 	writel(phys_status_start + IPS_STATUS_Q_SIZE,
51801da177e4SLinus Torvalds 	       ha->mem_ptr + IPS_REG_SQER);
51811da177e4SLinus Torvalds 	writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
51821da177e4SLinus Torvalds 	writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
51831da177e4SLinus Torvalds 
51841da177e4SLinus Torvalds 	ha->adapt->hw_status_tail = phys_status_start;
51851da177e4SLinus Torvalds }
51861da177e4SLinus Torvalds 
51871da177e4SLinus Torvalds /****************************************************************************/
51881da177e4SLinus Torvalds /*                                                                          */
51891da177e4SLinus Torvalds /* Routine Name: ips_statupd_copperhead                                     */
51901da177e4SLinus Torvalds /*                                                                          */
51911da177e4SLinus Torvalds /* Routine Description:                                                     */
51921da177e4SLinus Torvalds /*                                                                          */
51931da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
51941da177e4SLinus Torvalds /*                                                                          */
51951da177e4SLinus Torvalds /****************************************************************************/
51961da177e4SLinus Torvalds static uint32_t
51971da177e4SLinus Torvalds ips_statupd_copperhead(ips_ha_t * ha)
51981da177e4SLinus Torvalds {
51991da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_copperhead", 1);
52001da177e4SLinus Torvalds 
52011da177e4SLinus Torvalds 	if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
52021da177e4SLinus Torvalds 		ha->adapt->p_status_tail++;
52031da177e4SLinus Torvalds 		ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
52041da177e4SLinus Torvalds 	} else {
52051da177e4SLinus Torvalds 		ha->adapt->p_status_tail = ha->adapt->p_status_start;
52061da177e4SLinus Torvalds 		ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
52071da177e4SLinus Torvalds 	}
52081da177e4SLinus Torvalds 
5209db3cc200SJames Bottomley 	outl(ha->adapt->hw_status_tail,
52101da177e4SLinus Torvalds 	     ha->io_addr + IPS_REG_SQTR);
52111da177e4SLinus Torvalds 
52121da177e4SLinus Torvalds 	return (ha->adapt->p_status_tail->value);
52131da177e4SLinus Torvalds }
52141da177e4SLinus Torvalds 
52151da177e4SLinus Torvalds /****************************************************************************/
52161da177e4SLinus Torvalds /*                                                                          */
52171da177e4SLinus Torvalds /* Routine Name: ips_statupd_copperhead_memio                               */
52181da177e4SLinus Torvalds /*                                                                          */
52191da177e4SLinus Torvalds /* Routine Description:                                                     */
52201da177e4SLinus Torvalds /*                                                                          */
52211da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
52221da177e4SLinus Torvalds /*                                                                          */
52231da177e4SLinus Torvalds /****************************************************************************/
52241da177e4SLinus Torvalds static uint32_t
52251da177e4SLinus Torvalds ips_statupd_copperhead_memio(ips_ha_t * ha)
52261da177e4SLinus Torvalds {
52271da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_copperhead_memio", 1);
52281da177e4SLinus Torvalds 
52291da177e4SLinus Torvalds 	if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
52301da177e4SLinus Torvalds 		ha->adapt->p_status_tail++;
52311da177e4SLinus Torvalds 		ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
52321da177e4SLinus Torvalds 	} else {
52331da177e4SLinus Torvalds 		ha->adapt->p_status_tail = ha->adapt->p_status_start;
52341da177e4SLinus Torvalds 		ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
52351da177e4SLinus Torvalds 	}
52361da177e4SLinus Torvalds 
52371da177e4SLinus Torvalds 	writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
52381da177e4SLinus Torvalds 
52391da177e4SLinus Torvalds 	return (ha->adapt->p_status_tail->value);
52401da177e4SLinus Torvalds }
52411da177e4SLinus Torvalds 
52421da177e4SLinus Torvalds /****************************************************************************/
52431da177e4SLinus Torvalds /*                                                                          */
52441da177e4SLinus Torvalds /* Routine Name: ips_statupd_morpheus                                       */
52451da177e4SLinus Torvalds /*                                                                          */
52461da177e4SLinus Torvalds /* Routine Description:                                                     */
52471da177e4SLinus Torvalds /*                                                                          */
52481da177e4SLinus Torvalds /*   Remove an element from the status queue                                */
52491da177e4SLinus Torvalds /*                                                                          */
52501da177e4SLinus Torvalds /****************************************************************************/
52511da177e4SLinus Torvalds static uint32_t
52521da177e4SLinus Torvalds ips_statupd_morpheus(ips_ha_t * ha)
52531da177e4SLinus Torvalds {
52541da177e4SLinus Torvalds 	uint32_t val;
52551da177e4SLinus Torvalds 
52561da177e4SLinus Torvalds 	METHOD_TRACE("ips_statupd_morpheus", 1);
52571da177e4SLinus Torvalds 
52581da177e4SLinus Torvalds 	val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
52591da177e4SLinus Torvalds 
52601da177e4SLinus Torvalds 	return (val);
52611da177e4SLinus Torvalds }
52621da177e4SLinus Torvalds 
52631da177e4SLinus Torvalds /****************************************************************************/
52641da177e4SLinus Torvalds /*                                                                          */
52651da177e4SLinus Torvalds /* Routine Name: ips_issue_copperhead                                       */
52661da177e4SLinus Torvalds /*                                                                          */
52671da177e4SLinus Torvalds /* Routine Description:                                                     */
52681da177e4SLinus Torvalds /*                                                                          */
52691da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
52701da177e4SLinus Torvalds /*                                                                          */
52711da177e4SLinus Torvalds /****************************************************************************/
52721da177e4SLinus Torvalds static int
52731da177e4SLinus Torvalds ips_issue_copperhead(ips_ha_t * ha, ips_scb_t * scb)
52741da177e4SLinus Torvalds {
52751da177e4SLinus Torvalds 	uint32_t TimeOut;
52761da177e4SLinus Torvalds 	uint32_t val;
52771da177e4SLinus Torvalds 
52781da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_copperhead", 1);
52791da177e4SLinus Torvalds 
52801da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
52811da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
52821da177e4SLinus Torvalds 			  ips_name,
52831da177e4SLinus Torvalds 			  ha->host_num,
52841da177e4SLinus Torvalds 			  scb->cdb[0],
52851da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
52861da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
52871da177e4SLinus Torvalds 	} else {
52881da177e4SLinus Torvalds 		DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
52891da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
52901da177e4SLinus Torvalds 	}
52911da177e4SLinus Torvalds 
52921da177e4SLinus Torvalds 	TimeOut = 0;
52931da177e4SLinus Torvalds 
52941da177e4SLinus Torvalds 	while ((val =
52951da177e4SLinus Torvalds 		le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) {
52961da177e4SLinus Torvalds 		udelay(1000);
52971da177e4SLinus Torvalds 
52981da177e4SLinus Torvalds 		if (++TimeOut >= IPS_SEM_TIMEOUT) {
52991da177e4SLinus Torvalds 			if (!(val & IPS_BIT_START_STOP))
53001da177e4SLinus Torvalds 				break;
53011da177e4SLinus Torvalds 
53021da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53031da177e4SLinus Torvalds 				   "ips_issue val [0x%x].\n", val);
53041da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53051da177e4SLinus Torvalds 				   "ips_issue semaphore chk timeout.\n");
53061da177e4SLinus Torvalds 
53071da177e4SLinus Torvalds 			return (IPS_FAILURE);
53081da177e4SLinus Torvalds 		}		/* end if */
53091da177e4SLinus Torvalds 	}			/* end while */
53101da177e4SLinus Torvalds 
5311db3cc200SJames Bottomley 	outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR);
5312db3cc200SJames Bottomley 	outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR);
53131da177e4SLinus Torvalds 
53141da177e4SLinus Torvalds 	return (IPS_SUCCESS);
53151da177e4SLinus Torvalds }
53161da177e4SLinus Torvalds 
53171da177e4SLinus Torvalds /****************************************************************************/
53181da177e4SLinus Torvalds /*                                                                          */
53191da177e4SLinus Torvalds /* Routine Name: ips_issue_copperhead_memio                                 */
53201da177e4SLinus Torvalds /*                                                                          */
53211da177e4SLinus Torvalds /* Routine Description:                                                     */
53221da177e4SLinus Torvalds /*                                                                          */
53231da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
53241da177e4SLinus Torvalds /*                                                                          */
53251da177e4SLinus Torvalds /****************************************************************************/
53261da177e4SLinus Torvalds static int
53271da177e4SLinus Torvalds ips_issue_copperhead_memio(ips_ha_t * ha, ips_scb_t * scb)
53281da177e4SLinus Torvalds {
53291da177e4SLinus Torvalds 	uint32_t TimeOut;
53301da177e4SLinus Torvalds 	uint32_t val;
53311da177e4SLinus Torvalds 
53321da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_copperhead_memio", 1);
53331da177e4SLinus Torvalds 
53341da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
53351da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
53361da177e4SLinus Torvalds 			  ips_name,
53371da177e4SLinus Torvalds 			  ha->host_num,
53381da177e4SLinus Torvalds 			  scb->cdb[0],
53391da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
53401da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
53411da177e4SLinus Torvalds 	} else {
53421da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
53431da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
53441da177e4SLinus Torvalds 	}
53451da177e4SLinus Torvalds 
53461da177e4SLinus Torvalds 	TimeOut = 0;
53471da177e4SLinus Torvalds 
53481da177e4SLinus Torvalds 	while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
53491da177e4SLinus Torvalds 		udelay(1000);
53501da177e4SLinus Torvalds 
53511da177e4SLinus Torvalds 		if (++TimeOut >= IPS_SEM_TIMEOUT) {
53521da177e4SLinus Torvalds 			if (!(val & IPS_BIT_START_STOP))
53531da177e4SLinus Torvalds 				break;
53541da177e4SLinus Torvalds 
53551da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53561da177e4SLinus Torvalds 				   "ips_issue val [0x%x].\n", val);
53571da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, ha->pcidev,
53581da177e4SLinus Torvalds 				   "ips_issue semaphore chk timeout.\n");
53591da177e4SLinus Torvalds 
53601da177e4SLinus Torvalds 			return (IPS_FAILURE);
53611da177e4SLinus Torvalds 		}		/* end if */
53621da177e4SLinus Torvalds 	}			/* end while */
53631da177e4SLinus Torvalds 
53641da177e4SLinus Torvalds 	writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
53651da177e4SLinus Torvalds 	writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
53661da177e4SLinus Torvalds 
53671da177e4SLinus Torvalds 	return (IPS_SUCCESS);
53681da177e4SLinus Torvalds }
53691da177e4SLinus Torvalds 
53701da177e4SLinus Torvalds /****************************************************************************/
53711da177e4SLinus Torvalds /*                                                                          */
53721da177e4SLinus Torvalds /* Routine Name: ips_issue_i2o                                              */
53731da177e4SLinus Torvalds /*                                                                          */
53741da177e4SLinus Torvalds /* Routine Description:                                                     */
53751da177e4SLinus Torvalds /*                                                                          */
53761da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
53771da177e4SLinus Torvalds /*                                                                          */
53781da177e4SLinus Torvalds /****************************************************************************/
53791da177e4SLinus Torvalds static int
53801da177e4SLinus Torvalds ips_issue_i2o(ips_ha_t * ha, ips_scb_t * scb)
53811da177e4SLinus Torvalds {
53821da177e4SLinus Torvalds 
53831da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_i2o", 1);
53841da177e4SLinus Torvalds 
53851da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
53861da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
53871da177e4SLinus Torvalds 			  ips_name,
53881da177e4SLinus Torvalds 			  ha->host_num,
53891da177e4SLinus Torvalds 			  scb->cdb[0],
53901da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
53911da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
53921da177e4SLinus Torvalds 	} else {
53931da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
53941da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
53951da177e4SLinus Torvalds 	}
53961da177e4SLinus Torvalds 
5397db3cc200SJames Bottomley 	outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ);
53981da177e4SLinus Torvalds 
53991da177e4SLinus Torvalds 	return (IPS_SUCCESS);
54001da177e4SLinus Torvalds }
54011da177e4SLinus Torvalds 
54021da177e4SLinus Torvalds /****************************************************************************/
54031da177e4SLinus Torvalds /*                                                                          */
54041da177e4SLinus Torvalds /* Routine Name: ips_issue_i2o_memio                                        */
54051da177e4SLinus Torvalds /*                                                                          */
54061da177e4SLinus Torvalds /* Routine Description:                                                     */
54071da177e4SLinus Torvalds /*                                                                          */
54081da177e4SLinus Torvalds /*   Send a command down to the controller                                  */
54091da177e4SLinus Torvalds /*                                                                          */
54101da177e4SLinus Torvalds /****************************************************************************/
54111da177e4SLinus Torvalds static int
54121da177e4SLinus Torvalds ips_issue_i2o_memio(ips_ha_t * ha, ips_scb_t * scb)
54131da177e4SLinus Torvalds {
54141da177e4SLinus Torvalds 
54151da177e4SLinus Torvalds 	METHOD_TRACE("ips_issue_i2o_memio", 1);
54161da177e4SLinus Torvalds 
54171da177e4SLinus Torvalds 	if (scb->scsi_cmd) {
54181da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
54191da177e4SLinus Torvalds 			  ips_name,
54201da177e4SLinus Torvalds 			  ha->host_num,
54211da177e4SLinus Torvalds 			  scb->cdb[0],
54221da177e4SLinus Torvalds 			  scb->cmd.basic_io.command_id,
54231da177e4SLinus Torvalds 			  scb->bus, scb->target_id, scb->lun);
54241da177e4SLinus Torvalds 	} else {
54251da177e4SLinus Torvalds 		DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
54261da177e4SLinus Torvalds 			  ips_name, ha->host_num, scb->cmd.basic_io.command_id);
54271da177e4SLinus Torvalds 	}
54281da177e4SLinus Torvalds 
54291da177e4SLinus Torvalds 	writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
54301da177e4SLinus Torvalds 
54311da177e4SLinus Torvalds 	return (IPS_SUCCESS);
54321da177e4SLinus Torvalds }
54331da177e4SLinus Torvalds 
54341da177e4SLinus Torvalds /****************************************************************************/
54351da177e4SLinus Torvalds /*                                                                          */
54361da177e4SLinus Torvalds /* Routine Name: ips_isintr_copperhead                                      */
54371da177e4SLinus Torvalds /*                                                                          */
54381da177e4SLinus Torvalds /* Routine Description:                                                     */
54391da177e4SLinus Torvalds /*                                                                          */
54401da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
54411da177e4SLinus Torvalds /*                                                                          */
54421da177e4SLinus Torvalds /****************************************************************************/
54431da177e4SLinus Torvalds static int
54441da177e4SLinus Torvalds ips_isintr_copperhead(ips_ha_t * ha)
54451da177e4SLinus Torvalds {
54461da177e4SLinus Torvalds 	uint8_t Isr;
54471da177e4SLinus Torvalds 
54481da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_copperhead", 2);
54491da177e4SLinus Torvalds 
54501da177e4SLinus Torvalds 	Isr = inb(ha->io_addr + IPS_REG_HISR);
54511da177e4SLinus Torvalds 
54521da177e4SLinus Torvalds 	if (Isr == 0xFF)
54531da177e4SLinus Torvalds 		/* ?!?! Nothing really there */
54541da177e4SLinus Torvalds 		return (0);
54551da177e4SLinus Torvalds 
54561da177e4SLinus Torvalds 	if (Isr & IPS_BIT_SCE)
54571da177e4SLinus Torvalds 		return (1);
54581da177e4SLinus Torvalds 	else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
54591da177e4SLinus Torvalds 		/* status queue overflow or GHI */
54601da177e4SLinus Torvalds 		/* just clear the interrupt */
54611da177e4SLinus Torvalds 		outb(Isr, ha->io_addr + IPS_REG_HISR);
54621da177e4SLinus Torvalds 	}
54631da177e4SLinus Torvalds 
54641da177e4SLinus Torvalds 	return (0);
54651da177e4SLinus Torvalds }
54661da177e4SLinus Torvalds 
54671da177e4SLinus Torvalds /****************************************************************************/
54681da177e4SLinus Torvalds /*                                                                          */
54691da177e4SLinus Torvalds /* Routine Name: ips_isintr_copperhead_memio                                */
54701da177e4SLinus Torvalds /*                                                                          */
54711da177e4SLinus Torvalds /* Routine Description:                                                     */
54721da177e4SLinus Torvalds /*                                                                          */
54731da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
54741da177e4SLinus Torvalds /*                                                                          */
54751da177e4SLinus Torvalds /****************************************************************************/
54761da177e4SLinus Torvalds static int
54771da177e4SLinus Torvalds ips_isintr_copperhead_memio(ips_ha_t * ha)
54781da177e4SLinus Torvalds {
54791da177e4SLinus Torvalds 	uint8_t Isr;
54801da177e4SLinus Torvalds 
54811da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_memio", 2);
54821da177e4SLinus Torvalds 
54831da177e4SLinus Torvalds 	Isr = readb(ha->mem_ptr + IPS_REG_HISR);
54841da177e4SLinus Torvalds 
54851da177e4SLinus Torvalds 	if (Isr == 0xFF)
54861da177e4SLinus Torvalds 		/* ?!?! Nothing really there */
54871da177e4SLinus Torvalds 		return (0);
54881da177e4SLinus Torvalds 
54891da177e4SLinus Torvalds 	if (Isr & IPS_BIT_SCE)
54901da177e4SLinus Torvalds 		return (1);
54911da177e4SLinus Torvalds 	else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
54921da177e4SLinus Torvalds 		/* status queue overflow or GHI */
54931da177e4SLinus Torvalds 		/* just clear the interrupt */
54941da177e4SLinus Torvalds 		writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
54951da177e4SLinus Torvalds 	}
54961da177e4SLinus Torvalds 
54971da177e4SLinus Torvalds 	return (0);
54981da177e4SLinus Torvalds }
54991da177e4SLinus Torvalds 
55001da177e4SLinus Torvalds /****************************************************************************/
55011da177e4SLinus Torvalds /*                                                                          */
55021da177e4SLinus Torvalds /* Routine Name: ips_isintr_morpheus                                        */
55031da177e4SLinus Torvalds /*                                                                          */
55041da177e4SLinus Torvalds /* Routine Description:                                                     */
55051da177e4SLinus Torvalds /*                                                                          */
55061da177e4SLinus Torvalds /*   Test to see if an interrupt is for us                                  */
55071da177e4SLinus Torvalds /*                                                                          */
55081da177e4SLinus Torvalds /****************************************************************************/
55091da177e4SLinus Torvalds static int
55101da177e4SLinus Torvalds ips_isintr_morpheus(ips_ha_t * ha)
55111da177e4SLinus Torvalds {
55121da177e4SLinus Torvalds 	uint32_t Isr;
55131da177e4SLinus Torvalds 
55141da177e4SLinus Torvalds 	METHOD_TRACE("ips_isintr_morpheus", 2);
55151da177e4SLinus Torvalds 
55161da177e4SLinus Torvalds 	Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
55171da177e4SLinus Torvalds 
55181da177e4SLinus Torvalds 	if (Isr & IPS_BIT_I2O_OPQI)
55191da177e4SLinus Torvalds 		return (1);
55201da177e4SLinus Torvalds 	else
55211da177e4SLinus Torvalds 		return (0);
55221da177e4SLinus Torvalds }
55231da177e4SLinus Torvalds 
55241da177e4SLinus Torvalds /****************************************************************************/
55251da177e4SLinus Torvalds /*                                                                          */
55261da177e4SLinus Torvalds /* Routine Name: ips_wait                                                   */
55271da177e4SLinus Torvalds /*                                                                          */
55281da177e4SLinus Torvalds /* Routine Description:                                                     */
55291da177e4SLinus Torvalds /*                                                                          */
55301da177e4SLinus Torvalds /*   Wait for a command to complete                                         */
55311da177e4SLinus Torvalds /*                                                                          */
55321da177e4SLinus Torvalds /****************************************************************************/
55331da177e4SLinus Torvalds static int
55341da177e4SLinus Torvalds ips_wait(ips_ha_t * ha, int time, int intr)
55351da177e4SLinus Torvalds {
55361da177e4SLinus Torvalds 	int ret;
55371da177e4SLinus Torvalds 	int done;
55381da177e4SLinus Torvalds 
55391da177e4SLinus Torvalds 	METHOD_TRACE("ips_wait", 1);
55401da177e4SLinus Torvalds 
55411da177e4SLinus Torvalds 	ret = IPS_FAILURE;
55421da177e4SLinus Torvalds 	done = FALSE;
55431da177e4SLinus Torvalds 
55441da177e4SLinus Torvalds 	time *= IPS_ONE_SEC;	/* convert seconds */
55451da177e4SLinus Torvalds 
55461da177e4SLinus Torvalds 	while ((time > 0) && (!done)) {
55471da177e4SLinus Torvalds 		if (intr == IPS_INTR_ON) {
55481da177e4SLinus Torvalds 			if (ha->waitflag == FALSE) {
55491da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
55501da177e4SLinus Torvalds 				done = TRUE;
55511da177e4SLinus Torvalds 				break;
55521da177e4SLinus Torvalds 			}
55531da177e4SLinus Torvalds 		} else if (intr == IPS_INTR_IORL) {
55541da177e4SLinus Torvalds 			if (ha->waitflag == FALSE) {
55551da177e4SLinus Torvalds 				/*
55561da177e4SLinus Torvalds 				 * controller generated an interrupt to
55571da177e4SLinus Torvalds 				 * acknowledge completion of the command
55581da177e4SLinus Torvalds 				 * and ips_intr() has serviced the interrupt.
55591da177e4SLinus Torvalds 				 */
55601da177e4SLinus Torvalds 				ret = IPS_SUCCESS;
55611da177e4SLinus Torvalds 				done = TRUE;
55621da177e4SLinus Torvalds 				break;
55631da177e4SLinus Torvalds 			}
55641da177e4SLinus Torvalds 
55651da177e4SLinus Torvalds 			/*
55661da177e4SLinus Torvalds 			 * NOTE: we already have the io_request_lock so
55671da177e4SLinus Torvalds 			 * even if we get an interrupt it won't get serviced
55681da177e4SLinus Torvalds 			 * until after we finish.
55691da177e4SLinus Torvalds 			 */
55701da177e4SLinus Torvalds 
55711da177e4SLinus Torvalds 			(*ha->func.intr) (ha);
55721da177e4SLinus Torvalds 		}
55731da177e4SLinus Torvalds 
55741da177e4SLinus Torvalds 		/* This looks like a very evil loop, but it only does this during start-up */
55751da177e4SLinus Torvalds 		udelay(1000);
55761da177e4SLinus Torvalds 		time--;
55771da177e4SLinus Torvalds 	}
55781da177e4SLinus Torvalds 
55791da177e4SLinus Torvalds 	return (ret);
55801da177e4SLinus Torvalds }
55811da177e4SLinus Torvalds 
55821da177e4SLinus Torvalds /****************************************************************************/
55831da177e4SLinus Torvalds /*                                                                          */
55841da177e4SLinus Torvalds /* Routine Name: ips_write_driver_status                                    */
55851da177e4SLinus Torvalds /*                                                                          */
55861da177e4SLinus Torvalds /* Routine Description:                                                     */
55871da177e4SLinus Torvalds /*                                                                          */
55881da177e4SLinus Torvalds /*   Write OS/Driver version to Page 5 of the nvram on the controller       */
55891da177e4SLinus Torvalds /*                                                                          */
55901da177e4SLinus Torvalds /****************************************************************************/
55911da177e4SLinus Torvalds static int
55921da177e4SLinus Torvalds ips_write_driver_status(ips_ha_t * ha, int intr)
55931da177e4SLinus Torvalds {
55941da177e4SLinus Torvalds 	METHOD_TRACE("ips_write_driver_status", 1);
55951da177e4SLinus Torvalds 
55961da177e4SLinus Torvalds 	if (!ips_readwrite_page5(ha, FALSE, intr)) {
55971da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
55981da177e4SLinus Torvalds 			   "unable to read NVRAM page 5.\n");
55991da177e4SLinus Torvalds 
56001da177e4SLinus Torvalds 		return (0);
56011da177e4SLinus Torvalds 	}
56021da177e4SLinus Torvalds 
56031da177e4SLinus Torvalds 	/* check to make sure the page has a valid */
56041da177e4SLinus Torvalds 	/* signature */
56051da177e4SLinus Torvalds 	if (le32_to_cpu(ha->nvram->signature) != IPS_NVRAM_P5_SIG) {
56061da177e4SLinus Torvalds 		DEBUG_VAR(1,
56071da177e4SLinus Torvalds 			  "(%s%d) NVRAM page 5 has an invalid signature: %X.",
56081da177e4SLinus Torvalds 			  ips_name, ha->host_num, ha->nvram->signature);
56091da177e4SLinus Torvalds 		ha->nvram->signature = IPS_NVRAM_P5_SIG;
56101da177e4SLinus Torvalds 	}
56111da177e4SLinus Torvalds 
56121da177e4SLinus Torvalds 	DEBUG_VAR(2,
56131da177e4SLinus Torvalds 		  "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
56141da177e4SLinus Torvalds 		  ips_name, ha->host_num, le16_to_cpu(ha->nvram->adapter_type),
56151da177e4SLinus Torvalds 		  ha->nvram->adapter_slot, ha->nvram->bios_high[0],
56161da177e4SLinus Torvalds 		  ha->nvram->bios_high[1], ha->nvram->bios_high[2],
56171da177e4SLinus Torvalds 		  ha->nvram->bios_high[3], ha->nvram->bios_low[0],
56181da177e4SLinus Torvalds 		  ha->nvram->bios_low[1], ha->nvram->bios_low[2],
56191da177e4SLinus Torvalds 		  ha->nvram->bios_low[3]);
56201da177e4SLinus Torvalds 
56211da177e4SLinus Torvalds 	ips_get_bios_version(ha, intr);
56221da177e4SLinus Torvalds 
56231da177e4SLinus Torvalds 	/* change values (as needed) */
56241da177e4SLinus Torvalds 	ha->nvram->operating_system = IPS_OS_LINUX;
56251da177e4SLinus Torvalds 	ha->nvram->adapter_type = ha->ad_type;
56261da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4);
56271da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4);
56281da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->bios_high, ha->bios_version, 4);
56291da177e4SLinus Torvalds 	strncpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4);
56301da177e4SLinus Torvalds 
5631a60768e2SJack Hammer 	ha->nvram->versioning = 0;	/* Indicate the Driver Does Not Support Versioning */
56321da177e4SLinus Torvalds 
56331da177e4SLinus Torvalds 	/* now update the page */
56341da177e4SLinus Torvalds 	if (!ips_readwrite_page5(ha, TRUE, intr)) {
56351da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
56361da177e4SLinus Torvalds 			   "unable to write NVRAM page 5.\n");
56371da177e4SLinus Torvalds 
56381da177e4SLinus Torvalds 		return (0);
56391da177e4SLinus Torvalds 	}
56401da177e4SLinus Torvalds 
56411da177e4SLinus Torvalds 	/* IF NVRAM Page 5 is OK, Use it for Slot Number Info Because Linux Doesn't Do Slots */
56421da177e4SLinus Torvalds 	ha->slot_num = ha->nvram->adapter_slot;
56431da177e4SLinus Torvalds 
56441da177e4SLinus Torvalds 	return (1);
56451da177e4SLinus Torvalds }
56461da177e4SLinus Torvalds 
56471da177e4SLinus Torvalds /****************************************************************************/
56481da177e4SLinus Torvalds /*                                                                          */
56491da177e4SLinus Torvalds /* Routine Name: ips_read_adapter_status                                    */
56501da177e4SLinus Torvalds /*                                                                          */
56511da177e4SLinus Torvalds /* Routine Description:                                                     */
56521da177e4SLinus Torvalds /*                                                                          */
56531da177e4SLinus Torvalds /*   Do an Inquiry command to the adapter                                   */
56541da177e4SLinus Torvalds /*                                                                          */
56551da177e4SLinus Torvalds /****************************************************************************/
56561da177e4SLinus Torvalds static int
56571da177e4SLinus Torvalds ips_read_adapter_status(ips_ha_t * ha, int intr)
56581da177e4SLinus Torvalds {
56591da177e4SLinus Torvalds 	ips_scb_t *scb;
56601da177e4SLinus Torvalds 	int ret;
56611da177e4SLinus Torvalds 
56621da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_adapter_status", 1);
56631da177e4SLinus Torvalds 
56641da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
56651da177e4SLinus Torvalds 
56661da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
56671da177e4SLinus Torvalds 
56681da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
56691da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_ENQUIRY;
56701da177e4SLinus Torvalds 
56711da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
56721da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
56731da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_count = 0;
56741da177e4SLinus Torvalds 	scb->cmd.basic_io.lba = 0;
56751da177e4SLinus Torvalds 	scb->cmd.basic_io.sector_count = 0;
56761da177e4SLinus Torvalds 	scb->cmd.basic_io.log_drv = 0;
56771da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->enq);
56781da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
56791da177e4SLinus Torvalds 
56801da177e4SLinus Torvalds 	/* send command */
56811da177e4SLinus Torvalds 	if (((ret =
56821da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
56831da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
56841da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
56851da177e4SLinus Torvalds 		return (0);
56861da177e4SLinus Torvalds 
56871da177e4SLinus Torvalds 	return (1);
56881da177e4SLinus Torvalds }
56891da177e4SLinus Torvalds 
56901da177e4SLinus Torvalds /****************************************************************************/
56911da177e4SLinus Torvalds /*                                                                          */
56921da177e4SLinus Torvalds /* Routine Name: ips_read_subsystem_parameters                              */
56931da177e4SLinus Torvalds /*                                                                          */
56941da177e4SLinus Torvalds /* Routine Description:                                                     */
56951da177e4SLinus Torvalds /*                                                                          */
56961da177e4SLinus Torvalds /*   Read subsystem parameters from the adapter                             */
56971da177e4SLinus Torvalds /*                                                                          */
56981da177e4SLinus Torvalds /****************************************************************************/
56991da177e4SLinus Torvalds static int
57001da177e4SLinus Torvalds ips_read_subsystem_parameters(ips_ha_t * ha, int intr)
57011da177e4SLinus Torvalds {
57021da177e4SLinus Torvalds 	ips_scb_t *scb;
57031da177e4SLinus Torvalds 	int ret;
57041da177e4SLinus Torvalds 
57051da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_subsystem_parameters", 1);
57061da177e4SLinus Torvalds 
57071da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
57081da177e4SLinus Torvalds 
57091da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
57101da177e4SLinus Torvalds 
57111da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
57121da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_GET_SUBSYS;
57131da177e4SLinus Torvalds 
57141da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS;
57151da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
57161da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_count = 0;
57171da177e4SLinus Torvalds 	scb->cmd.basic_io.lba = 0;
57181da177e4SLinus Torvalds 	scb->cmd.basic_io.sector_count = 0;
57191da177e4SLinus Torvalds 	scb->cmd.basic_io.log_drv = 0;
57201da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->subsys);
57211da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
57221da177e4SLinus Torvalds 
57231da177e4SLinus Torvalds 	/* send command */
57241da177e4SLinus Torvalds 	if (((ret =
57251da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
57261da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
57271da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
57281da177e4SLinus Torvalds 		return (0);
57291da177e4SLinus Torvalds 
57301da177e4SLinus Torvalds 	memcpy(ha->subsys, ha->ioctl_data, sizeof(*ha->subsys));
57311da177e4SLinus Torvalds 	return (1);
57321da177e4SLinus Torvalds }
57331da177e4SLinus Torvalds 
57341da177e4SLinus Torvalds /****************************************************************************/
57351da177e4SLinus Torvalds /*                                                                          */
57361da177e4SLinus Torvalds /* Routine Name: ips_read_config                                            */
57371da177e4SLinus Torvalds /*                                                                          */
57381da177e4SLinus Torvalds /* Routine Description:                                                     */
57391da177e4SLinus Torvalds /*                                                                          */
57401da177e4SLinus Torvalds /*   Read the configuration on the adapter                                  */
57411da177e4SLinus Torvalds /*                                                                          */
57421da177e4SLinus Torvalds /****************************************************************************/
57431da177e4SLinus Torvalds static int
57441da177e4SLinus Torvalds ips_read_config(ips_ha_t * ha, int intr)
57451da177e4SLinus Torvalds {
57461da177e4SLinus Torvalds 	ips_scb_t *scb;
57471da177e4SLinus Torvalds 	int i;
57481da177e4SLinus Torvalds 	int ret;
57491da177e4SLinus Torvalds 
57501da177e4SLinus Torvalds 	METHOD_TRACE("ips_read_config", 1);
57511da177e4SLinus Torvalds 
57521da177e4SLinus Torvalds 	/* set defaults for initiator IDs */
57531da177e4SLinus Torvalds 	for (i = 0; i < 4; i++)
57541da177e4SLinus Torvalds 		ha->conf->init_id[i] = 7;
57551da177e4SLinus Torvalds 
57561da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
57571da177e4SLinus Torvalds 
57581da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
57591da177e4SLinus Torvalds 
57601da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
57611da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_READ_CONF;
57621da177e4SLinus Torvalds 
57631da177e4SLinus Torvalds 	scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF;
57641da177e4SLinus Torvalds 	scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
57651da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->conf);
57661da177e4SLinus Torvalds 	scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
57671da177e4SLinus Torvalds 
57681da177e4SLinus Torvalds 	/* send command */
57691da177e4SLinus Torvalds 	if (((ret =
57701da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
57711da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
57721da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
57731da177e4SLinus Torvalds 
57741da177e4SLinus Torvalds 		memset(ha->conf, 0, sizeof (IPS_CONF));
57751da177e4SLinus Torvalds 
57761da177e4SLinus Torvalds 		/* reset initiator IDs */
57771da177e4SLinus Torvalds 		for (i = 0; i < 4; i++)
57781da177e4SLinus Torvalds 			ha->conf->init_id[i] = 7;
57791da177e4SLinus Torvalds 
57801da177e4SLinus Torvalds 		/* Allow Completed with Errors, so JCRM can access the Adapter to fix the problems */
57811da177e4SLinus Torvalds 		if ((scb->basic_status & IPS_GSC_STATUS_MASK) ==
57821da177e4SLinus Torvalds 		    IPS_CMD_CMPLT_WERROR)
57831da177e4SLinus Torvalds 			return (1);
57841da177e4SLinus Torvalds 
57851da177e4SLinus Torvalds 		return (0);
57861da177e4SLinus Torvalds 	}
57871da177e4SLinus Torvalds 
57881da177e4SLinus Torvalds 	memcpy(ha->conf, ha->ioctl_data, sizeof(*ha->conf));
57891da177e4SLinus Torvalds 	return (1);
57901da177e4SLinus Torvalds }
57911da177e4SLinus Torvalds 
57921da177e4SLinus Torvalds /****************************************************************************/
57931da177e4SLinus Torvalds /*                                                                          */
57941da177e4SLinus Torvalds /* Routine Name: ips_readwrite_page5                                        */
57951da177e4SLinus Torvalds /*                                                                          */
57961da177e4SLinus Torvalds /* Routine Description:                                                     */
57971da177e4SLinus Torvalds /*                                                                          */
57981da177e4SLinus Torvalds /*   Read nvram page 5 from the adapter                                     */
57991da177e4SLinus Torvalds /*                                                                          */
58001da177e4SLinus Torvalds /****************************************************************************/
58011da177e4SLinus Torvalds static int
58021da177e4SLinus Torvalds ips_readwrite_page5(ips_ha_t * ha, int write, int intr)
58031da177e4SLinus Torvalds {
58041da177e4SLinus Torvalds 	ips_scb_t *scb;
58051da177e4SLinus Torvalds 	int ret;
58061da177e4SLinus Torvalds 
58071da177e4SLinus Torvalds 	METHOD_TRACE("ips_readwrite_page5", 1);
58081da177e4SLinus Torvalds 
58091da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
58101da177e4SLinus Torvalds 
58111da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
58121da177e4SLinus Torvalds 
58131da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
58141da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_RW_NVRAM_PAGE;
58151da177e4SLinus Torvalds 
58161da177e4SLinus Torvalds 	scb->cmd.nvram.op_code = IPS_CMD_RW_NVRAM_PAGE;
58171da177e4SLinus Torvalds 	scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb);
58181da177e4SLinus Torvalds 	scb->cmd.nvram.page = 5;
58191da177e4SLinus Torvalds 	scb->cmd.nvram.write = write;
58201da177e4SLinus Torvalds 	scb->cmd.nvram.reserved = 0;
58211da177e4SLinus Torvalds 	scb->cmd.nvram.reserved2 = 0;
58221da177e4SLinus Torvalds 	scb->data_len = sizeof (*ha->nvram);
58231da177e4SLinus Torvalds 	scb->cmd.nvram.buffer_addr = ha->ioctl_busaddr;
58241da177e4SLinus Torvalds 	if (write)
58251da177e4SLinus Torvalds 		memcpy(ha->ioctl_data, ha->nvram, sizeof(*ha->nvram));
58261da177e4SLinus Torvalds 
58271da177e4SLinus Torvalds 	/* issue the command */
58281da177e4SLinus Torvalds 	if (((ret =
58291da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
58301da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
58311da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
58321da177e4SLinus Torvalds 
58331da177e4SLinus Torvalds 		memset(ha->nvram, 0, sizeof (IPS_NVRAM_P5));
58341da177e4SLinus Torvalds 
58351da177e4SLinus Torvalds 		return (0);
58361da177e4SLinus Torvalds 	}
58371da177e4SLinus Torvalds 	if (!write)
58381da177e4SLinus Torvalds 		memcpy(ha->nvram, ha->ioctl_data, sizeof(*ha->nvram));
58391da177e4SLinus Torvalds 	return (1);
58401da177e4SLinus Torvalds }
58411da177e4SLinus Torvalds 
58421da177e4SLinus Torvalds /****************************************************************************/
58431da177e4SLinus Torvalds /*                                                                          */
58441da177e4SLinus Torvalds /* Routine Name: ips_clear_adapter                                          */
58451da177e4SLinus Torvalds /*                                                                          */
58461da177e4SLinus Torvalds /* Routine Description:                                                     */
58471da177e4SLinus Torvalds /*                                                                          */
58481da177e4SLinus Torvalds /*   Clear the stripe lock tables                                           */
58491da177e4SLinus Torvalds /*                                                                          */
58501da177e4SLinus Torvalds /****************************************************************************/
58511da177e4SLinus Torvalds static int
58521da177e4SLinus Torvalds ips_clear_adapter(ips_ha_t * ha, int intr)
58531da177e4SLinus Torvalds {
58541da177e4SLinus Torvalds 	ips_scb_t *scb;
58551da177e4SLinus Torvalds 	int ret;
58561da177e4SLinus Torvalds 
58571da177e4SLinus Torvalds 	METHOD_TRACE("ips_clear_adapter", 1);
58581da177e4SLinus Torvalds 
58591da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
58601da177e4SLinus Torvalds 
58611da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
58621da177e4SLinus Torvalds 
58631da177e4SLinus Torvalds 	scb->timeout = ips_reset_timeout;
58641da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_CONFIG_SYNC;
58651da177e4SLinus Torvalds 
58661da177e4SLinus Torvalds 	scb->cmd.config_sync.op_code = IPS_CMD_CONFIG_SYNC;
58671da177e4SLinus Torvalds 	scb->cmd.config_sync.command_id = IPS_COMMAND_ID(ha, scb);
58681da177e4SLinus Torvalds 	scb->cmd.config_sync.channel = 0;
58691da177e4SLinus Torvalds 	scb->cmd.config_sync.source_target = IPS_POCL;
58701da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved = 0;
58711da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved2 = 0;
58721da177e4SLinus Torvalds 	scb->cmd.config_sync.reserved3 = 0;
58731da177e4SLinus Torvalds 
58741da177e4SLinus Torvalds 	/* issue command */
58751da177e4SLinus Torvalds 	if (((ret =
58761da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE)
58771da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
58781da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
58791da177e4SLinus Torvalds 		return (0);
58801da177e4SLinus Torvalds 
58811da177e4SLinus Torvalds 	/* send unlock stripe command */
58821da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
58831da177e4SLinus Torvalds 
58841da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_ERROR_TABLE;
58851da177e4SLinus Torvalds 	scb->timeout = ips_reset_timeout;
58861da177e4SLinus Torvalds 
58871da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.op_code = IPS_CMD_ERROR_TABLE;
58881da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.command_id = IPS_COMMAND_ID(ha, scb);
58891da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.log_drv = 0;
58901da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.control = IPS_CSL;
58911da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved = 0;
58921da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved2 = 0;
58931da177e4SLinus Torvalds 	scb->cmd.unlock_stripe.reserved3 = 0;
58941da177e4SLinus Torvalds 
58951da177e4SLinus Torvalds 	/* issue command */
58961da177e4SLinus Torvalds 	if (((ret =
58971da177e4SLinus Torvalds 	      ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
58981da177e4SLinus Torvalds 	    || (ret == IPS_SUCCESS_IMM)
58991da177e4SLinus Torvalds 	    || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
59001da177e4SLinus Torvalds 		return (0);
59011da177e4SLinus Torvalds 
59021da177e4SLinus Torvalds 	return (1);
59031da177e4SLinus Torvalds }
59041da177e4SLinus Torvalds 
59051da177e4SLinus Torvalds /****************************************************************************/
59061da177e4SLinus Torvalds /*                                                                          */
59071da177e4SLinus Torvalds /* Routine Name: ips_ffdc_reset                                             */
59081da177e4SLinus Torvalds /*                                                                          */
59091da177e4SLinus Torvalds /* Routine Description:                                                     */
59101da177e4SLinus Torvalds /*                                                                          */
59111da177e4SLinus Torvalds /*   FFDC: write reset info                                                 */
59121da177e4SLinus Torvalds /*                                                                          */
59131da177e4SLinus Torvalds /****************************************************************************/
59141da177e4SLinus Torvalds static void
59151da177e4SLinus Torvalds ips_ffdc_reset(ips_ha_t * ha, int intr)
59161da177e4SLinus Torvalds {
59171da177e4SLinus Torvalds 	ips_scb_t *scb;
59181da177e4SLinus Torvalds 
59191da177e4SLinus Torvalds 	METHOD_TRACE("ips_ffdc_reset", 1);
59201da177e4SLinus Torvalds 
59211da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
59221da177e4SLinus Torvalds 
59231da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
59241da177e4SLinus Torvalds 
59251da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
59261da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FFDC;
59271da177e4SLinus Torvalds 	scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
59281da177e4SLinus Torvalds 	scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
59291da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_count = ha->reset_count;
59301da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_type = 0x80;
59311da177e4SLinus Torvalds 
59321da177e4SLinus Torvalds 	/* convert time to what the card wants */
59331da177e4SLinus Torvalds 	ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
59341da177e4SLinus Torvalds 
59351da177e4SLinus Torvalds 	/* issue command */
59361da177e4SLinus Torvalds 	ips_send_wait(ha, scb, ips_cmd_timeout, intr);
59371da177e4SLinus Torvalds }
59381da177e4SLinus Torvalds 
59391da177e4SLinus Torvalds /****************************************************************************/
59401da177e4SLinus Torvalds /*                                                                          */
59411da177e4SLinus Torvalds /* Routine Name: ips_ffdc_time                                              */
59421da177e4SLinus Torvalds /*                                                                          */
59431da177e4SLinus Torvalds /* Routine Description:                                                     */
59441da177e4SLinus Torvalds /*                                                                          */
59451da177e4SLinus Torvalds /*   FFDC: write time info                                                  */
59461da177e4SLinus Torvalds /*                                                                          */
59471da177e4SLinus Torvalds /****************************************************************************/
59481da177e4SLinus Torvalds static void
59491da177e4SLinus Torvalds ips_ffdc_time(ips_ha_t * ha)
59501da177e4SLinus Torvalds {
59511da177e4SLinus Torvalds 	ips_scb_t *scb;
59521da177e4SLinus Torvalds 
59531da177e4SLinus Torvalds 	METHOD_TRACE("ips_ffdc_time", 1);
59541da177e4SLinus Torvalds 
59551da177e4SLinus Torvalds 	DEBUG_VAR(1, "(%s%d) Sending time update.", ips_name, ha->host_num);
59561da177e4SLinus Torvalds 
59571da177e4SLinus Torvalds 	scb = &ha->scbs[ha->max_cmds - 1];
59581da177e4SLinus Torvalds 
59591da177e4SLinus Torvalds 	ips_init_scb(ha, scb);
59601da177e4SLinus Torvalds 
59611da177e4SLinus Torvalds 	scb->timeout = ips_cmd_timeout;
59621da177e4SLinus Torvalds 	scb->cdb[0] = IPS_CMD_FFDC;
59631da177e4SLinus Torvalds 	scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
59641da177e4SLinus Torvalds 	scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
59651da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_count = 0;
59661da177e4SLinus Torvalds 	scb->cmd.ffdc.reset_type = 0;
59671da177e4SLinus Torvalds 
59681da177e4SLinus Torvalds 	/* convert time to what the card wants */
59691da177e4SLinus Torvalds 	ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
59701da177e4SLinus Torvalds 
59711da177e4SLinus Torvalds 	/* issue command */
59721da177e4SLinus Torvalds 	ips_send_wait(ha, scb, ips_cmd_timeout, IPS_FFDC);
59731da177e4SLinus Torvalds }
59741da177e4SLinus Torvalds 
59751da177e4SLinus Torvalds /****************************************************************************/
59761da177e4SLinus Torvalds /*                                                                          */
59771da177e4SLinus Torvalds /* Routine Name: ips_fix_ffdc_time                                          */
59781da177e4SLinus Torvalds /*                                                                          */
59791da177e4SLinus Torvalds /* Routine Description:                                                     */
59801da177e4SLinus Torvalds /*   Adjust time_t to what the card wants                                   */
59811da177e4SLinus Torvalds /*                                                                          */
59821da177e4SLinus Torvalds /****************************************************************************/
59831da177e4SLinus Torvalds static void
5984f990bee3SArnd Bergmann ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time64_t current_time)
59851da177e4SLinus Torvalds {
5986f990bee3SArnd Bergmann 	struct tm tm;
59871da177e4SLinus Torvalds 
59881da177e4SLinus Torvalds 	METHOD_TRACE("ips_fix_ffdc_time", 1);
59891da177e4SLinus Torvalds 
5990f990bee3SArnd Bergmann 	time64_to_tm(current_time, 0, &tm);
59911da177e4SLinus Torvalds 
5992f990bee3SArnd Bergmann 	scb->cmd.ffdc.hour   = tm.tm_hour;
5993f990bee3SArnd Bergmann 	scb->cmd.ffdc.minute = tm.tm_min;
5994f990bee3SArnd Bergmann 	scb->cmd.ffdc.second = tm.tm_sec;
5995f990bee3SArnd Bergmann 	scb->cmd.ffdc.yearH  = (tm.tm_year + 1900) / 100;
5996f990bee3SArnd Bergmann 	scb->cmd.ffdc.yearL  = tm.tm_year % 100;
5997f990bee3SArnd Bergmann 	scb->cmd.ffdc.month  = tm.tm_mon + 1;
5998f990bee3SArnd Bergmann 	scb->cmd.ffdc.day    = tm.tm_mday;
59991da177e4SLinus Torvalds }
60001da177e4SLinus Torvalds 
60011da177e4SLinus Torvalds /****************************************************************************
60021da177e4SLinus Torvalds  * BIOS Flash Routines                                                      *
60031da177e4SLinus Torvalds  ****************************************************************************/
60041da177e4SLinus Torvalds 
60051da177e4SLinus Torvalds /****************************************************************************/
60061da177e4SLinus Torvalds /*                                                                          */
60071da177e4SLinus Torvalds /* Routine Name: ips_erase_bios                                             */
60081da177e4SLinus Torvalds /*                                                                          */
60091da177e4SLinus Torvalds /* Routine Description:                                                     */
60101da177e4SLinus Torvalds /*   Erase the BIOS on the adapter                                          */
60111da177e4SLinus Torvalds /*                                                                          */
60121da177e4SLinus Torvalds /****************************************************************************/
60131da177e4SLinus Torvalds static int
60141da177e4SLinus Torvalds ips_erase_bios(ips_ha_t * ha)
60151da177e4SLinus Torvalds {
60161da177e4SLinus Torvalds 	int timeout;
60171da177e4SLinus Torvalds 	uint8_t status = 0;
60181da177e4SLinus Torvalds 
60191da177e4SLinus Torvalds 	METHOD_TRACE("ips_erase_bios", 1);
60201da177e4SLinus Torvalds 
60211da177e4SLinus Torvalds 	status = 0;
60221da177e4SLinus Torvalds 
60231da177e4SLinus Torvalds 	/* Clear the status register */
60241da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
60258a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60261da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60271da177e4SLinus Torvalds 
60281da177e4SLinus Torvalds 	outb(0x50, ha->io_addr + IPS_REG_FLDP);
60298a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60301da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60311da177e4SLinus Torvalds 
60321da177e4SLinus Torvalds 	/* Erase Setup */
60331da177e4SLinus Torvalds 	outb(0x20, ha->io_addr + IPS_REG_FLDP);
60348a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60351da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60361da177e4SLinus Torvalds 
60371da177e4SLinus Torvalds 	/* Erase Confirm */
60381da177e4SLinus Torvalds 	outb(0xD0, ha->io_addr + IPS_REG_FLDP);
60398a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60401da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60411da177e4SLinus Torvalds 
60421da177e4SLinus Torvalds 	/* Erase Status */
60431da177e4SLinus Torvalds 	outb(0x70, ha->io_addr + IPS_REG_FLDP);
60448a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60451da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
60461da177e4SLinus Torvalds 
60471da177e4SLinus Torvalds 	timeout = 80000;	/* 80 seconds */
60481da177e4SLinus Torvalds 
60491da177e4SLinus Torvalds 	while (timeout > 0) {
60508a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
60511da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
60521da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
60531da177e4SLinus Torvalds 		}
60541da177e4SLinus Torvalds 
60551da177e4SLinus Torvalds 		status = inb(ha->io_addr + IPS_REG_FLDP);
60561da177e4SLinus Torvalds 
60571da177e4SLinus Torvalds 		if (status & 0x80)
60581da177e4SLinus Torvalds 			break;
60591da177e4SLinus Torvalds 
60601da177e4SLinus Torvalds 		MDELAY(1);
60611da177e4SLinus Torvalds 		timeout--;
60621da177e4SLinus Torvalds 	}
60631da177e4SLinus Torvalds 
60641da177e4SLinus Torvalds 	/* check for timeout */
60651da177e4SLinus Torvalds 	if (timeout <= 0) {
60661da177e4SLinus Torvalds 		/* timeout */
60671da177e4SLinus Torvalds 
60681da177e4SLinus Torvalds 		/* try to suspend the erase */
60691da177e4SLinus Torvalds 		outb(0xB0, ha->io_addr + IPS_REG_FLDP);
60708a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
60711da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
60721da177e4SLinus Torvalds 
60731da177e4SLinus Torvalds 		/* wait for 10 seconds */
60741da177e4SLinus Torvalds 		timeout = 10000;
60751da177e4SLinus Torvalds 		while (timeout > 0) {
60768a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
60771da177e4SLinus Torvalds 				outl(0, ha->io_addr + IPS_REG_FLAP);
60781da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
60791da177e4SLinus Torvalds 			}
60801da177e4SLinus Torvalds 
60811da177e4SLinus Torvalds 			status = inb(ha->io_addr + IPS_REG_FLDP);
60821da177e4SLinus Torvalds 
60831da177e4SLinus Torvalds 			if (status & 0xC0)
60841da177e4SLinus Torvalds 				break;
60851da177e4SLinus Torvalds 
60861da177e4SLinus Torvalds 			MDELAY(1);
60871da177e4SLinus Torvalds 			timeout--;
60881da177e4SLinus Torvalds 		}
60891da177e4SLinus Torvalds 
60901da177e4SLinus Torvalds 		return (1);
60911da177e4SLinus Torvalds 	}
60921da177e4SLinus Torvalds 
60931da177e4SLinus Torvalds 	/* check for valid VPP */
60941da177e4SLinus Torvalds 	if (status & 0x08)
60951da177e4SLinus Torvalds 		/* VPP failure */
60961da177e4SLinus Torvalds 		return (1);
60971da177e4SLinus Torvalds 
6098d6e05edcSAndreas Mohr 	/* check for successful flash */
60991da177e4SLinus Torvalds 	if (status & 0x30)
61001da177e4SLinus Torvalds 		/* sequence error */
61011da177e4SLinus Torvalds 		return (1);
61021da177e4SLinus Torvalds 
61031da177e4SLinus Torvalds 	/* Otherwise, we were successful */
61041da177e4SLinus Torvalds 	/* clear status */
61051da177e4SLinus Torvalds 	outb(0x50, ha->io_addr + IPS_REG_FLDP);
61068a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61071da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61081da177e4SLinus Torvalds 
61091da177e4SLinus Torvalds 	/* enable reads */
61101da177e4SLinus Torvalds 	outb(0xFF, ha->io_addr + IPS_REG_FLDP);
61118a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61121da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61131da177e4SLinus Torvalds 
61141da177e4SLinus Torvalds 	return (0);
61151da177e4SLinus Torvalds }
61161da177e4SLinus Torvalds 
61171da177e4SLinus Torvalds /****************************************************************************/
61181da177e4SLinus Torvalds /*                                                                          */
61191da177e4SLinus Torvalds /* Routine Name: ips_erase_bios_memio                                       */
61201da177e4SLinus Torvalds /*                                                                          */
61211da177e4SLinus Torvalds /* Routine Description:                                                     */
61221da177e4SLinus Torvalds /*   Erase the BIOS on the adapter                                          */
61231da177e4SLinus Torvalds /*                                                                          */
61241da177e4SLinus Torvalds /****************************************************************************/
61251da177e4SLinus Torvalds static int
61261da177e4SLinus Torvalds ips_erase_bios_memio(ips_ha_t * ha)
61271da177e4SLinus Torvalds {
61281da177e4SLinus Torvalds 	int timeout;
61291da177e4SLinus Torvalds 	uint8_t status;
61301da177e4SLinus Torvalds 
61311da177e4SLinus Torvalds 	METHOD_TRACE("ips_erase_bios_memio", 1);
61321da177e4SLinus Torvalds 
61331da177e4SLinus Torvalds 	status = 0;
61341da177e4SLinus Torvalds 
61351da177e4SLinus Torvalds 	/* Clear the status register */
61361da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
61378a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61381da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61391da177e4SLinus Torvalds 
61401da177e4SLinus Torvalds 	writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
61418a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61421da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61431da177e4SLinus Torvalds 
61441da177e4SLinus Torvalds 	/* Erase Setup */
61451da177e4SLinus Torvalds 	writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
61468a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61471da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61481da177e4SLinus Torvalds 
61491da177e4SLinus Torvalds 	/* Erase Confirm */
61501da177e4SLinus Torvalds 	writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
61518a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61521da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61531da177e4SLinus Torvalds 
61541da177e4SLinus Torvalds 	/* Erase Status */
61551da177e4SLinus Torvalds 	writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
61568a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61571da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
61581da177e4SLinus Torvalds 
61591da177e4SLinus Torvalds 	timeout = 80000;	/* 80 seconds */
61601da177e4SLinus Torvalds 
61611da177e4SLinus Torvalds 	while (timeout > 0) {
61628a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
61631da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
61641da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
61651da177e4SLinus Torvalds 		}
61661da177e4SLinus Torvalds 
61671da177e4SLinus Torvalds 		status = readb(ha->mem_ptr + IPS_REG_FLDP);
61681da177e4SLinus Torvalds 
61691da177e4SLinus Torvalds 		if (status & 0x80)
61701da177e4SLinus Torvalds 			break;
61711da177e4SLinus Torvalds 
61721da177e4SLinus Torvalds 		MDELAY(1);
61731da177e4SLinus Torvalds 		timeout--;
61741da177e4SLinus Torvalds 	}
61751da177e4SLinus Torvalds 
61761da177e4SLinus Torvalds 	/* check for timeout */
61771da177e4SLinus Torvalds 	if (timeout <= 0) {
61781da177e4SLinus Torvalds 		/* timeout */
61791da177e4SLinus Torvalds 
61801da177e4SLinus Torvalds 		/* try to suspend the erase */
61811da177e4SLinus Torvalds 		writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
61828a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
61831da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
61841da177e4SLinus Torvalds 
61851da177e4SLinus Torvalds 		/* wait for 10 seconds */
61861da177e4SLinus Torvalds 		timeout = 10000;
61871da177e4SLinus Torvalds 		while (timeout > 0) {
61888a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
61891da177e4SLinus Torvalds 				writel(0, ha->mem_ptr + IPS_REG_FLAP);
61901da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
61911da177e4SLinus Torvalds 			}
61921da177e4SLinus Torvalds 
61931da177e4SLinus Torvalds 			status = readb(ha->mem_ptr + IPS_REG_FLDP);
61941da177e4SLinus Torvalds 
61951da177e4SLinus Torvalds 			if (status & 0xC0)
61961da177e4SLinus Torvalds 				break;
61971da177e4SLinus Torvalds 
61981da177e4SLinus Torvalds 			MDELAY(1);
61991da177e4SLinus Torvalds 			timeout--;
62001da177e4SLinus Torvalds 		}
62011da177e4SLinus Torvalds 
62021da177e4SLinus Torvalds 		return (1);
62031da177e4SLinus Torvalds 	}
62041da177e4SLinus Torvalds 
62051da177e4SLinus Torvalds 	/* check for valid VPP */
62061da177e4SLinus Torvalds 	if (status & 0x08)
62071da177e4SLinus Torvalds 		/* VPP failure */
62081da177e4SLinus Torvalds 		return (1);
62091da177e4SLinus Torvalds 
6210d6e05edcSAndreas Mohr 	/* check for successful flash */
62111da177e4SLinus Torvalds 	if (status & 0x30)
62121da177e4SLinus Torvalds 		/* sequence error */
62131da177e4SLinus Torvalds 		return (1);
62141da177e4SLinus Torvalds 
62151da177e4SLinus Torvalds 	/* Otherwise, we were successful */
62161da177e4SLinus Torvalds 	/* clear status */
62171da177e4SLinus Torvalds 	writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
62188a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62191da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62201da177e4SLinus Torvalds 
62211da177e4SLinus Torvalds 	/* enable reads */
62221da177e4SLinus Torvalds 	writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
62238a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62241da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
62251da177e4SLinus Torvalds 
62261da177e4SLinus Torvalds 	return (0);
62271da177e4SLinus Torvalds }
62281da177e4SLinus Torvalds 
62291da177e4SLinus Torvalds /****************************************************************************/
62301da177e4SLinus Torvalds /*                                                                          */
62311da177e4SLinus Torvalds /* Routine Name: ips_program_bios                                           */
62321da177e4SLinus Torvalds /*                                                                          */
62331da177e4SLinus Torvalds /* Routine Description:                                                     */
62341da177e4SLinus Torvalds /*   Program the BIOS on the adapter                                        */
62351da177e4SLinus Torvalds /*                                                                          */
62361da177e4SLinus Torvalds /****************************************************************************/
62371da177e4SLinus Torvalds static int
62381da177e4SLinus Torvalds ips_program_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
62391da177e4SLinus Torvalds 		 uint32_t offset)
62401da177e4SLinus Torvalds {
62411da177e4SLinus Torvalds 	int i;
62421da177e4SLinus Torvalds 	int timeout;
62431da177e4SLinus Torvalds 	uint8_t status = 0;
62441da177e4SLinus Torvalds 
62451da177e4SLinus Torvalds 	METHOD_TRACE("ips_program_bios", 1);
62461da177e4SLinus Torvalds 
62471da177e4SLinus Torvalds 	status = 0;
62481da177e4SLinus Torvalds 
62491da177e4SLinus Torvalds 	for (i = 0; i < buffersize; i++) {
62501da177e4SLinus Torvalds 		/* write a byte */
6251db3cc200SJames Bottomley 		outl(i + offset, ha->io_addr + IPS_REG_FLAP);
62528a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62531da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
62541da177e4SLinus Torvalds 
62551da177e4SLinus Torvalds 		outb(0x40, ha->io_addr + IPS_REG_FLDP);
62568a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62571da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
62581da177e4SLinus Torvalds 
62591da177e4SLinus Torvalds 		outb(buffer[i], ha->io_addr + IPS_REG_FLDP);
62608a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62611da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
62621da177e4SLinus Torvalds 
62631da177e4SLinus Torvalds 		/* wait up to one second */
62641da177e4SLinus Torvalds 		timeout = 1000;
62651da177e4SLinus Torvalds 		while (timeout > 0) {
62668a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
62671da177e4SLinus Torvalds 				outl(0, ha->io_addr + IPS_REG_FLAP);
62681da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
62691da177e4SLinus Torvalds 			}
62701da177e4SLinus Torvalds 
62711da177e4SLinus Torvalds 			status = inb(ha->io_addr + IPS_REG_FLDP);
62721da177e4SLinus Torvalds 
62731da177e4SLinus Torvalds 			if (status & 0x80)
62741da177e4SLinus Torvalds 				break;
62751da177e4SLinus Torvalds 
62761da177e4SLinus Torvalds 			MDELAY(1);
62771da177e4SLinus Torvalds 			timeout--;
62781da177e4SLinus Torvalds 		}
62791da177e4SLinus Torvalds 
62801da177e4SLinus Torvalds 		if (timeout == 0) {
62811da177e4SLinus Torvalds 			/* timeout error */
62821da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
62838a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62841da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
62851da177e4SLinus Torvalds 
62861da177e4SLinus Torvalds 			outb(0xFF, ha->io_addr + IPS_REG_FLDP);
62878a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62881da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
62891da177e4SLinus Torvalds 
62901da177e4SLinus Torvalds 			return (1);
62911da177e4SLinus Torvalds 		}
62921da177e4SLinus Torvalds 
62931da177e4SLinus Torvalds 		/* check the status */
62941da177e4SLinus Torvalds 		if (status & 0x18) {
62951da177e4SLinus Torvalds 			/* programming error */
62961da177e4SLinus Torvalds 			outl(0, ha->io_addr + IPS_REG_FLAP);
62978a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
62981da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
62991da177e4SLinus Torvalds 
63001da177e4SLinus Torvalds 			outb(0xFF, ha->io_addr + IPS_REG_FLDP);
63018a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63021da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63031da177e4SLinus Torvalds 
63041da177e4SLinus Torvalds 			return (1);
63051da177e4SLinus Torvalds 		}
63061da177e4SLinus Torvalds 	}			/* end for */
63071da177e4SLinus Torvalds 
63081da177e4SLinus Torvalds 	/* Enable reading */
63091da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
63108a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63111da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
63121da177e4SLinus Torvalds 
63131da177e4SLinus Torvalds 	outb(0xFF, ha->io_addr + IPS_REG_FLDP);
63148a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63151da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
63161da177e4SLinus Torvalds 
63171da177e4SLinus Torvalds 	return (0);
63181da177e4SLinus Torvalds }
63191da177e4SLinus Torvalds 
63201da177e4SLinus Torvalds /****************************************************************************/
63211da177e4SLinus Torvalds /*                                                                          */
63221da177e4SLinus Torvalds /* Routine Name: ips_program_bios_memio                                     */
63231da177e4SLinus Torvalds /*                                                                          */
63241da177e4SLinus Torvalds /* Routine Description:                                                     */
63251da177e4SLinus Torvalds /*   Program the BIOS on the adapter                                        */
63261da177e4SLinus Torvalds /*                                                                          */
63271da177e4SLinus Torvalds /****************************************************************************/
63281da177e4SLinus Torvalds static int
63291da177e4SLinus Torvalds ips_program_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
63301da177e4SLinus Torvalds 		       uint32_t offset)
63311da177e4SLinus Torvalds {
63321da177e4SLinus Torvalds 	int i;
63331da177e4SLinus Torvalds 	int timeout;
63341da177e4SLinus Torvalds 	uint8_t status = 0;
63351da177e4SLinus Torvalds 
63361da177e4SLinus Torvalds 	METHOD_TRACE("ips_program_bios_memio", 1);
63371da177e4SLinus Torvalds 
63381da177e4SLinus Torvalds 	status = 0;
63391da177e4SLinus Torvalds 
63401da177e4SLinus Torvalds 	for (i = 0; i < buffersize; i++) {
63411da177e4SLinus Torvalds 		/* write a byte */
63421da177e4SLinus Torvalds 		writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
63438a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63441da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63451da177e4SLinus Torvalds 
63461da177e4SLinus Torvalds 		writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
63478a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63481da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63491da177e4SLinus Torvalds 
63501da177e4SLinus Torvalds 		writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
63518a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63521da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
63531da177e4SLinus Torvalds 
63541da177e4SLinus Torvalds 		/* wait up to one second */
63551da177e4SLinus Torvalds 		timeout = 1000;
63561da177e4SLinus Torvalds 		while (timeout > 0) {
63578a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64) {
63581da177e4SLinus Torvalds 				writel(0, ha->mem_ptr + IPS_REG_FLAP);
63591da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63601da177e4SLinus Torvalds 			}
63611da177e4SLinus Torvalds 
63621da177e4SLinus Torvalds 			status = readb(ha->mem_ptr + IPS_REG_FLDP);
63631da177e4SLinus Torvalds 
63641da177e4SLinus Torvalds 			if (status & 0x80)
63651da177e4SLinus Torvalds 				break;
63661da177e4SLinus Torvalds 
63671da177e4SLinus Torvalds 			MDELAY(1);
63681da177e4SLinus Torvalds 			timeout--;
63691da177e4SLinus Torvalds 		}
63701da177e4SLinus Torvalds 
63711da177e4SLinus Torvalds 		if (timeout == 0) {
63721da177e4SLinus Torvalds 			/* timeout error */
63731da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
63748a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63751da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63761da177e4SLinus Torvalds 
63771da177e4SLinus Torvalds 			writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
63788a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63791da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63801da177e4SLinus Torvalds 
63811da177e4SLinus Torvalds 			return (1);
63821da177e4SLinus Torvalds 		}
63831da177e4SLinus Torvalds 
63841da177e4SLinus Torvalds 		/* check the status */
63851da177e4SLinus Torvalds 		if (status & 0x18) {
63861da177e4SLinus Torvalds 			/* programming error */
63871da177e4SLinus Torvalds 			writel(0, ha->mem_ptr + IPS_REG_FLAP);
63888a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63891da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63901da177e4SLinus Torvalds 
63911da177e4SLinus Torvalds 			writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
63928a694cc8SJeff Garzik 			if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
63931da177e4SLinus Torvalds 				udelay(25);	/* 25 us */
63941da177e4SLinus Torvalds 
63951da177e4SLinus Torvalds 			return (1);
63961da177e4SLinus Torvalds 		}
63971da177e4SLinus Torvalds 	}			/* end for */
63981da177e4SLinus Torvalds 
63991da177e4SLinus Torvalds 	/* Enable reading */
64001da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
64018a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64021da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64031da177e4SLinus Torvalds 
64041da177e4SLinus Torvalds 	writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
64058a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64061da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64071da177e4SLinus Torvalds 
64081da177e4SLinus Torvalds 	return (0);
64091da177e4SLinus Torvalds }
64101da177e4SLinus Torvalds 
64111da177e4SLinus Torvalds /****************************************************************************/
64121da177e4SLinus Torvalds /*                                                                          */
64131da177e4SLinus Torvalds /* Routine Name: ips_verify_bios                                            */
64141da177e4SLinus Torvalds /*                                                                          */
64151da177e4SLinus Torvalds /* Routine Description:                                                     */
64161da177e4SLinus Torvalds /*   Verify the BIOS on the adapter                                         */
64171da177e4SLinus Torvalds /*                                                                          */
64181da177e4SLinus Torvalds /****************************************************************************/
64191da177e4SLinus Torvalds static int
64201da177e4SLinus Torvalds ips_verify_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
64211da177e4SLinus Torvalds 		uint32_t offset)
64221da177e4SLinus Torvalds {
64231da177e4SLinus Torvalds 	uint8_t checksum;
64241da177e4SLinus Torvalds 	int i;
64251da177e4SLinus Torvalds 
64261da177e4SLinus Torvalds 	METHOD_TRACE("ips_verify_bios", 1);
64271da177e4SLinus Torvalds 
64281da177e4SLinus Torvalds 	/* test 1st byte */
64291da177e4SLinus Torvalds 	outl(0, ha->io_addr + IPS_REG_FLAP);
64308a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64311da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64321da177e4SLinus Torvalds 
64331da177e4SLinus Torvalds 	if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
64341da177e4SLinus Torvalds 		return (1);
64351da177e4SLinus Torvalds 
6436db3cc200SJames Bottomley 	outl(1, ha->io_addr + IPS_REG_FLAP);
64378a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64381da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64391da177e4SLinus Torvalds 	if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
64401da177e4SLinus Torvalds 		return (1);
64411da177e4SLinus Torvalds 
64421da177e4SLinus Torvalds 	checksum = 0xff;
64431da177e4SLinus Torvalds 	for (i = 2; i < buffersize; i++) {
64441da177e4SLinus Torvalds 
6445db3cc200SJames Bottomley 		outl(i + offset, ha->io_addr + IPS_REG_FLAP);
64468a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64471da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
64481da177e4SLinus Torvalds 
64491da177e4SLinus Torvalds 		checksum = (uint8_t) checksum + inb(ha->io_addr + IPS_REG_FLDP);
64501da177e4SLinus Torvalds 	}
64511da177e4SLinus Torvalds 
64521da177e4SLinus Torvalds 	if (checksum != 0)
64531da177e4SLinus Torvalds 		/* failure */
64541da177e4SLinus Torvalds 		return (1);
64551da177e4SLinus Torvalds 	else
64561da177e4SLinus Torvalds 		/* success */
64571da177e4SLinus Torvalds 		return (0);
64581da177e4SLinus Torvalds }
64591da177e4SLinus Torvalds 
64601da177e4SLinus Torvalds /****************************************************************************/
64611da177e4SLinus Torvalds /*                                                                          */
64621da177e4SLinus Torvalds /* Routine Name: ips_verify_bios_memio                                      */
64631da177e4SLinus Torvalds /*                                                                          */
64641da177e4SLinus Torvalds /* Routine Description:                                                     */
64651da177e4SLinus Torvalds /*   Verify the BIOS on the adapter                                         */
64661da177e4SLinus Torvalds /*                                                                          */
64671da177e4SLinus Torvalds /****************************************************************************/
64681da177e4SLinus Torvalds static int
64691da177e4SLinus Torvalds ips_verify_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
64701da177e4SLinus Torvalds 		      uint32_t offset)
64711da177e4SLinus Torvalds {
64721da177e4SLinus Torvalds 	uint8_t checksum;
64731da177e4SLinus Torvalds 	int i;
64741da177e4SLinus Torvalds 
64751da177e4SLinus Torvalds 	METHOD_TRACE("ips_verify_bios_memio", 1);
64761da177e4SLinus Torvalds 
64771da177e4SLinus Torvalds 	/* test 1st byte */
64781da177e4SLinus Torvalds 	writel(0, ha->mem_ptr + IPS_REG_FLAP);
64798a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64801da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64811da177e4SLinus Torvalds 
64821da177e4SLinus Torvalds 	if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
64831da177e4SLinus Torvalds 		return (1);
64841da177e4SLinus Torvalds 
64851da177e4SLinus Torvalds 	writel(1, ha->mem_ptr + IPS_REG_FLAP);
64868a694cc8SJeff Garzik 	if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64871da177e4SLinus Torvalds 		udelay(25);	/* 25 us */
64881da177e4SLinus Torvalds 	if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
64891da177e4SLinus Torvalds 		return (1);
64901da177e4SLinus Torvalds 
64911da177e4SLinus Torvalds 	checksum = 0xff;
64921da177e4SLinus Torvalds 	for (i = 2; i < buffersize; i++) {
64931da177e4SLinus Torvalds 
64941da177e4SLinus Torvalds 		writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
64958a694cc8SJeff Garzik 		if (ha->pcidev->revision == IPS_REVID_TROMBONE64)
64961da177e4SLinus Torvalds 			udelay(25);	/* 25 us */
64971da177e4SLinus Torvalds 
64981da177e4SLinus Torvalds 		checksum =
64991da177e4SLinus Torvalds 		    (uint8_t) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
65001da177e4SLinus Torvalds 	}
65011da177e4SLinus Torvalds 
65021da177e4SLinus Torvalds 	if (checksum != 0)
65031da177e4SLinus Torvalds 		/* failure */
65041da177e4SLinus Torvalds 		return (1);
65051da177e4SLinus Torvalds 	else
65061da177e4SLinus Torvalds 		/* success */
65071da177e4SLinus Torvalds 		return (0);
65081da177e4SLinus Torvalds }
65091da177e4SLinus Torvalds 
65101da177e4SLinus Torvalds /****************************************************************************/
65111da177e4SLinus Torvalds /*                                                                          */
65121da177e4SLinus Torvalds /* Routine Name: ips_abort_init                                             */
65131da177e4SLinus Torvalds /*                                                                          */
65141da177e4SLinus Torvalds /* Routine Description:                                                     */
65151da177e4SLinus Torvalds /*   cleanup routine for a failed adapter initialization                    */
65161da177e4SLinus Torvalds /****************************************************************************/
65171da177e4SLinus Torvalds static int
65181da177e4SLinus Torvalds ips_abort_init(ips_ha_t * ha, int index)
65191da177e4SLinus Torvalds {
65201da177e4SLinus Torvalds 	ha->active = 0;
65211da177e4SLinus Torvalds 	ips_free(ha);
65221da177e4SLinus Torvalds 	ips_ha[index] = NULL;
65231da177e4SLinus Torvalds 	ips_sh[index] = NULL;
65241da177e4SLinus Torvalds 	return -1;
65251da177e4SLinus Torvalds }
65261da177e4SLinus Torvalds 
65271da177e4SLinus Torvalds /****************************************************************************/
65281da177e4SLinus Torvalds /*                                                                          */
65291da177e4SLinus Torvalds /* Routine Name: ips_shift_controllers                                      */
65301da177e4SLinus Torvalds /*                                                                          */
65311da177e4SLinus Torvalds /* Routine Description:                                                     */
65321da177e4SLinus Torvalds /*   helper function for ordering adapters                                  */
65331da177e4SLinus Torvalds /****************************************************************************/
65341da177e4SLinus Torvalds static void
65351da177e4SLinus Torvalds ips_shift_controllers(int lowindex, int highindex)
65361da177e4SLinus Torvalds {
65371da177e4SLinus Torvalds 	ips_ha_t *ha_sav = ips_ha[highindex];
65381da177e4SLinus Torvalds 	struct Scsi_Host *sh_sav = ips_sh[highindex];
65391da177e4SLinus Torvalds 	int i;
65401da177e4SLinus Torvalds 
65411da177e4SLinus Torvalds 	for (i = highindex; i > lowindex; i--) {
65421da177e4SLinus Torvalds 		ips_ha[i] = ips_ha[i - 1];
65431da177e4SLinus Torvalds 		ips_sh[i] = ips_sh[i - 1];
65441da177e4SLinus Torvalds 		ips_ha[i]->host_num = i;
65451da177e4SLinus Torvalds 	}
65461da177e4SLinus Torvalds 	ha_sav->host_num = lowindex;
65471da177e4SLinus Torvalds 	ips_ha[lowindex] = ha_sav;
65481da177e4SLinus Torvalds 	ips_sh[lowindex] = sh_sav;
65491da177e4SLinus Torvalds }
65501da177e4SLinus Torvalds 
65511da177e4SLinus Torvalds /****************************************************************************/
65521da177e4SLinus Torvalds /*                                                                          */
65531da177e4SLinus Torvalds /* Routine Name: ips_order_controllers                                      */
65541da177e4SLinus Torvalds /*                                                                          */
65551da177e4SLinus Torvalds /* Routine Description:                                                     */
65561da177e4SLinus Torvalds /*   place controllers is the "proper" boot order                           */
65571da177e4SLinus Torvalds /****************************************************************************/
65581da177e4SLinus Torvalds static void
65591da177e4SLinus Torvalds ips_order_controllers(void)
65601da177e4SLinus Torvalds {
65611da177e4SLinus Torvalds 	int i, j, tmp, position = 0;
65621da177e4SLinus Torvalds 	IPS_NVRAM_P5 *nvram;
65631da177e4SLinus Torvalds 	if (!ips_ha[0])
65641da177e4SLinus Torvalds 		return;
65651da177e4SLinus Torvalds 	nvram = ips_ha[0]->nvram;
65661da177e4SLinus Torvalds 
65671da177e4SLinus Torvalds 	if (nvram->adapter_order[0]) {
65681da177e4SLinus Torvalds 		for (i = 1; i <= nvram->adapter_order[0]; i++) {
65691da177e4SLinus Torvalds 			for (j = position; j < ips_num_controllers; j++) {
65701da177e4SLinus Torvalds 				switch (ips_ha[j]->ad_type) {
65711da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID6M:
65721da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID7M:
65731da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'M') {
65741da177e4SLinus Torvalds 						ips_shift_controllers(position,
65751da177e4SLinus Torvalds 								      j);
65761da177e4SLinus Torvalds 						position++;
65771da177e4SLinus Torvalds 					}
65781da177e4SLinus Torvalds 					break;
65791da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4L:
65801da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4M:
65811da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4MX:
65821da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4LX:
65831da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'N') {
65841da177e4SLinus Torvalds 						ips_shift_controllers(position,
65851da177e4SLinus Torvalds 								      j);
65861da177e4SLinus Torvalds 						position++;
65871da177e4SLinus Torvalds 					}
65881da177e4SLinus Torvalds 					break;
65891da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID6I:
65901da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID5I2:
65911da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID5I1:
65921da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID7k:
65931da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'S') {
65941da177e4SLinus Torvalds 						ips_shift_controllers(position,
65951da177e4SLinus Torvalds 								      j);
65961da177e4SLinus Torvalds 						position++;
65971da177e4SLinus Torvalds 					}
65981da177e4SLinus Torvalds 					break;
65991da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID:
66001da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID2:
66011da177e4SLinus Torvalds 				case IPS_ADTYPE_NAVAJO:
66021da177e4SLinus Torvalds 				case IPS_ADTYPE_KIOWA:
66031da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID3L:
66041da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID3:
66051da177e4SLinus Torvalds 				case IPS_ADTYPE_SERVERAID4H:
66061da177e4SLinus Torvalds 					if (nvram->adapter_order[i] == 'A') {
66071da177e4SLinus Torvalds 						ips_shift_controllers(position,
66081da177e4SLinus Torvalds 								      j);
66091da177e4SLinus Torvalds 						position++;
66101da177e4SLinus Torvalds 					}
66111da177e4SLinus Torvalds 					break;
66121da177e4SLinus Torvalds 				default:
66131da177e4SLinus Torvalds 					break;
66141da177e4SLinus Torvalds 				}
66151da177e4SLinus Torvalds 			}
66161da177e4SLinus Torvalds 		}
66171da177e4SLinus Torvalds 		/* if adapter_order[0], then ordering is complete */
66181da177e4SLinus Torvalds 		return;
66191da177e4SLinus Torvalds 	}
66201da177e4SLinus Torvalds 	/* old bios, use older ordering */
66211da177e4SLinus Torvalds 	tmp = 0;
66221da177e4SLinus Torvalds 	for (i = position; i < ips_num_controllers; i++) {
66231da177e4SLinus Torvalds 		if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I2 ||
66241da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I1) {
66251da177e4SLinus Torvalds 			ips_shift_controllers(position, i);
66261da177e4SLinus Torvalds 			position++;
66271da177e4SLinus Torvalds 			tmp = 1;
66281da177e4SLinus Torvalds 		}
66291da177e4SLinus Torvalds 	}
66301da177e4SLinus Torvalds 	/* if there were no 5I cards, then don't do any extra ordering */
66311da177e4SLinus Torvalds 	if (!tmp)
66321da177e4SLinus Torvalds 		return;
66331da177e4SLinus Torvalds 	for (i = position; i < ips_num_controllers; i++) {
66341da177e4SLinus Torvalds 		if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4L ||
66351da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4M ||
66361da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4LX ||
66371da177e4SLinus Torvalds 		    ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4MX) {
66381da177e4SLinus Torvalds 			ips_shift_controllers(position, i);
66391da177e4SLinus Torvalds 			position++;
66401da177e4SLinus Torvalds 		}
66411da177e4SLinus Torvalds 	}
66421da177e4SLinus Torvalds 
66431da177e4SLinus Torvalds 	return;
66441da177e4SLinus Torvalds }
66451da177e4SLinus Torvalds 
66461da177e4SLinus Torvalds /****************************************************************************/
66471da177e4SLinus Torvalds /*                                                                          */
66481da177e4SLinus Torvalds /* Routine Name: ips_register_scsi                                          */
66491da177e4SLinus Torvalds /*                                                                          */
66501da177e4SLinus Torvalds /* Routine Description:                                                     */
66511da177e4SLinus Torvalds /*   perform any registration and setup with the scsi layer                 */
66521da177e4SLinus Torvalds /****************************************************************************/
66531da177e4SLinus Torvalds static int
66541da177e4SLinus Torvalds ips_register_scsi(int index)
66551da177e4SLinus Torvalds {
66561da177e4SLinus Torvalds 	struct Scsi_Host *sh;
66571da177e4SLinus Torvalds 	ips_ha_t *ha, *oldha = ips_ha[index];
66581da177e4SLinus Torvalds 	sh = scsi_host_alloc(&ips_driver_template, sizeof (ips_ha_t));
66591da177e4SLinus Torvalds 	if (!sh) {
66601da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, oldha->pcidev,
66611da177e4SLinus Torvalds 			   "Unable to register controller with SCSI subsystem\n");
66621da177e4SLinus Torvalds 		return -1;
66631da177e4SLinus Torvalds 	}
66641da177e4SLinus Torvalds 	ha = IPS_HA(sh);
66651da177e4SLinus Torvalds 	memcpy(ha, oldha, sizeof (ips_ha_t));
66668a694cc8SJeff Garzik 	free_irq(oldha->pcidev->irq, oldha);
66671da177e4SLinus Torvalds 	/* Install the interrupt handler with the new ha */
66688a694cc8SJeff Garzik 	if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) {
66691da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
66701da177e4SLinus Torvalds 			   "Unable to install interrupt handler\n");
66712551a13eSJeff Garzik 		goto err_out_sh;
66721da177e4SLinus Torvalds 	}
66731da177e4SLinus Torvalds 
66741da177e4SLinus Torvalds 	kfree(oldha);
66751da177e4SLinus Torvalds 
66761da177e4SLinus Torvalds 	/* Store away needed values for later use */
66771da177e4SLinus Torvalds 	sh->unique_id = (ha->io_addr) ? ha->io_addr : ha->mem_addr;
66781da177e4SLinus Torvalds 	sh->sg_tablesize = sh->hostt->sg_tablesize;
66791da177e4SLinus Torvalds 	sh->can_queue = sh->hostt->can_queue;
66801da177e4SLinus Torvalds 	sh->cmd_per_lun = sh->hostt->cmd_per_lun;
66811da177e4SLinus Torvalds 	sh->use_clustering = sh->hostt->use_clustering;
66821da177e4SLinus Torvalds 	sh->max_sectors = 128;
66831da177e4SLinus Torvalds 
66841da177e4SLinus Torvalds 	sh->max_id = ha->ntargets;
66851da177e4SLinus Torvalds 	sh->max_lun = ha->nlun;
66861da177e4SLinus Torvalds 	sh->max_channel = ha->nbus - 1;
66871da177e4SLinus Torvalds 	sh->can_queue = ha->max_cmds - 1;
66881da177e4SLinus Torvalds 
66892551a13eSJeff Garzik 	if (scsi_add_host(sh, &ha->pcidev->dev))
66902551a13eSJeff Garzik 		goto err_out;
66912551a13eSJeff Garzik 
66922551a13eSJeff Garzik 	ips_sh[index] = sh;
66932551a13eSJeff Garzik 	ips_ha[index] = ha;
66942551a13eSJeff Garzik 
6695c6a6c81cSAdrian Bunk 	scsi_scan_host(sh);
6696c6a6c81cSAdrian Bunk 
66971da177e4SLinus Torvalds 	return 0;
66982551a13eSJeff Garzik 
66992551a13eSJeff Garzik err_out:
67002551a13eSJeff Garzik 	free_irq(ha->pcidev->irq, ha);
67012551a13eSJeff Garzik err_out_sh:
67022551a13eSJeff Garzik 	scsi_host_put(sh);
67032551a13eSJeff Garzik 	return -1;
67041da177e4SLinus Torvalds }
67051da177e4SLinus Torvalds 
67061da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67071da177e4SLinus Torvalds /*   Routine Name: ips_remove_device                                         */
67081da177e4SLinus Torvalds /*                                                                           */
67091da177e4SLinus Torvalds /*   Routine Description:                                                    */
67101da177e4SLinus Torvalds /*     Remove one Adapter ( Hot Plugging )                                   */
67111da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67126f039790SGreg Kroah-Hartman static void
67131da177e4SLinus Torvalds ips_remove_device(struct pci_dev *pci_dev)
67141da177e4SLinus Torvalds {
671521e1a5f2SJeff Garzik 	struct Scsi_Host *sh = pci_get_drvdata(pci_dev);
67161da177e4SLinus Torvalds 
671721e1a5f2SJeff Garzik 	pci_set_drvdata(pci_dev, NULL);
671821e1a5f2SJeff Garzik 
67191da177e4SLinus Torvalds 	ips_release(sh);
672021e1a5f2SJeff Garzik 
672121e1a5f2SJeff Garzik 	pci_release_regions(pci_dev);
672221e1a5f2SJeff Garzik 	pci_disable_device(pci_dev);
67231da177e4SLinus Torvalds }
67241da177e4SLinus Torvalds 
67251da177e4SLinus Torvalds /****************************************************************************/
67261da177e4SLinus Torvalds /*                                                                          */
67271da177e4SLinus Torvalds /* Routine Name: ips_module_init                                            */
67281da177e4SLinus Torvalds /*                                                                          */
67291da177e4SLinus Torvalds /* Routine Description:                                                     */
67301da177e4SLinus Torvalds /*   function called on module load                                         */
67311da177e4SLinus Torvalds /****************************************************************************/
67321da177e4SLinus Torvalds static int __init
67331da177e4SLinus Torvalds ips_module_init(void)
67341da177e4SLinus Torvalds {
6735e03c2da6SJames Bottomley #if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
6736e03c2da6SJames Bottomley 	printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n");
6737e03c2da6SJames Bottomley 	add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
6738e03c2da6SJames Bottomley #endif
6739e03c2da6SJames Bottomley 
674002a0fa67SAlan Cox 	if (pci_register_driver(&ips_pci_driver) < 0)
67411da177e4SLinus Torvalds 		return -ENODEV;
67421da177e4SLinus Torvalds 	ips_driver_template.module = THIS_MODULE;
67431da177e4SLinus Torvalds 	ips_order_controllers();
6744c6a6c81cSAdrian Bunk 	if (!ips_detect(&ips_driver_template)) {
67451da177e4SLinus Torvalds 		pci_unregister_driver(&ips_pci_driver);
67461da177e4SLinus Torvalds 		return -ENODEV;
67471da177e4SLinus Torvalds 	}
67481da177e4SLinus Torvalds 	register_reboot_notifier(&ips_notifier);
67491da177e4SLinus Torvalds 	return 0;
67501da177e4SLinus Torvalds }
67511da177e4SLinus Torvalds 
67521da177e4SLinus Torvalds /****************************************************************************/
67531da177e4SLinus Torvalds /*                                                                          */
67541da177e4SLinus Torvalds /* Routine Name: ips_module_exit                                            */
67551da177e4SLinus Torvalds /*                                                                          */
67561da177e4SLinus Torvalds /* Routine Description:                                                     */
67571da177e4SLinus Torvalds /*   function called on module unload                                       */
67581da177e4SLinus Torvalds /****************************************************************************/
67591da177e4SLinus Torvalds static void __exit
67601da177e4SLinus Torvalds ips_module_exit(void)
67611da177e4SLinus Torvalds {
67621da177e4SLinus Torvalds 	pci_unregister_driver(&ips_pci_driver);
67631da177e4SLinus Torvalds 	unregister_reboot_notifier(&ips_notifier);
67641da177e4SLinus Torvalds }
67651da177e4SLinus Torvalds 
67661da177e4SLinus Torvalds module_init(ips_module_init);
67671da177e4SLinus Torvalds module_exit(ips_module_exit);
67681da177e4SLinus Torvalds 
67691da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67701da177e4SLinus Torvalds /*   Routine Name: ips_insert_device                                         */
67711da177e4SLinus Torvalds /*                                                                           */
67721da177e4SLinus Torvalds /*   Routine Description:                                                    */
67731da177e4SLinus Torvalds /*     Add One Adapter ( Hot Plug )                                          */
67741da177e4SLinus Torvalds /*                                                                           */
67751da177e4SLinus Torvalds /*   Return Value:                                                           */
67761da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
67771da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
67786f039790SGreg Kroah-Hartman static int
67791da177e4SLinus Torvalds ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent)
67801da177e4SLinus Torvalds {
678121e1a5f2SJeff Garzik 	int index = -1;
67821da177e4SLinus Torvalds 	int rc;
67831da177e4SLinus Torvalds 
67841da177e4SLinus Torvalds 	METHOD_TRACE("ips_insert_device", 1);
678521e1a5f2SJeff Garzik 	rc = pci_enable_device(pci_dev);
678621e1a5f2SJeff Garzik 	if (rc)
678721e1a5f2SJeff Garzik 		return rc;
678821e1a5f2SJeff Garzik 
678921e1a5f2SJeff Garzik 	rc = pci_request_regions(pci_dev, "ips");
679021e1a5f2SJeff Garzik 	if (rc)
679121e1a5f2SJeff Garzik 		goto err_out;
67921da177e4SLinus Torvalds 
67931da177e4SLinus Torvalds 	rc = ips_init_phase1(pci_dev, &index);
67941da177e4SLinus Torvalds 	if (rc == SUCCESS)
67951da177e4SLinus Torvalds 		rc = ips_init_phase2(index);
67961da177e4SLinus Torvalds 
67971da177e4SLinus Torvalds 	if (ips_hotplug)
67981da177e4SLinus Torvalds 		if (ips_register_scsi(index)) {
67991da177e4SLinus Torvalds 			ips_free(ips_ha[index]);
68001da177e4SLinus Torvalds 			rc = -1;
68011da177e4SLinus Torvalds 		}
68021da177e4SLinus Torvalds 
68031da177e4SLinus Torvalds 	if (rc == SUCCESS)
68041da177e4SLinus Torvalds 		ips_num_controllers++;
68051da177e4SLinus Torvalds 
68061da177e4SLinus Torvalds 	ips_next_controller = ips_num_controllers;
680721e1a5f2SJeff Garzik 
680821e1a5f2SJeff Garzik 	if (rc < 0) {
680921e1a5f2SJeff Garzik 		rc = -ENODEV;
681021e1a5f2SJeff Garzik 		goto err_out_regions;
681121e1a5f2SJeff Garzik 	}
681221e1a5f2SJeff Garzik 
681321e1a5f2SJeff Garzik 	pci_set_drvdata(pci_dev, ips_sh[index]);
681421e1a5f2SJeff Garzik 	return 0;
681521e1a5f2SJeff Garzik 
681621e1a5f2SJeff Garzik err_out_regions:
681721e1a5f2SJeff Garzik 	pci_release_regions(pci_dev);
681821e1a5f2SJeff Garzik err_out:
681921e1a5f2SJeff Garzik 	pci_disable_device(pci_dev);
68201da177e4SLinus Torvalds 	return rc;
68211da177e4SLinus Torvalds }
68221da177e4SLinus Torvalds 
68231da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68241da177e4SLinus Torvalds /*   Routine Name: ips_init_phase1                                           */
68251da177e4SLinus Torvalds /*                                                                           */
68261da177e4SLinus Torvalds /*   Routine Description:                                                    */
68271da177e4SLinus Torvalds /*     Adapter Initialization                                                */
68281da177e4SLinus Torvalds /*                                                                           */
68291da177e4SLinus Torvalds /*   Return Value:                                                           */
68301da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
68311da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
68321da177e4SLinus Torvalds static int
68331da177e4SLinus Torvalds ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
68341da177e4SLinus Torvalds {
68351da177e4SLinus Torvalds 	ips_ha_t *ha;
68361da177e4SLinus Torvalds 	uint32_t io_addr;
68371da177e4SLinus Torvalds 	uint32_t mem_addr;
68381da177e4SLinus Torvalds 	uint32_t io_len;
68391da177e4SLinus Torvalds 	uint32_t mem_len;
68401da177e4SLinus Torvalds 	uint8_t bus;
68411da177e4SLinus Torvalds 	uint8_t func;
68421da177e4SLinus Torvalds 	int j;
68431da177e4SLinus Torvalds 	int index;
68441da177e4SLinus Torvalds 	dma_addr_t dma_address;
68451da177e4SLinus Torvalds 	char __iomem *ioremap_ptr;
68461da177e4SLinus Torvalds 	char __iomem *mem_ptr;
68471da177e4SLinus Torvalds 	uint32_t IsDead;
68481da177e4SLinus Torvalds 
68491da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_phase1", 1);
68501da177e4SLinus Torvalds 	index = IPS_MAX_ADAPTERS;
68511da177e4SLinus Torvalds 	for (j = 0; j < IPS_MAX_ADAPTERS; j++) {
685221e1a5f2SJeff Garzik 		if (ips_ha[j] == NULL) {
68531da177e4SLinus Torvalds 			index = j;
68541da177e4SLinus Torvalds 			break;
68551da177e4SLinus Torvalds 		}
68561da177e4SLinus Torvalds 	}
68571da177e4SLinus Torvalds 
68581da177e4SLinus Torvalds 	if (index >= IPS_MAX_ADAPTERS)
68591da177e4SLinus Torvalds 		return -1;
68601da177e4SLinus Torvalds 
68611da177e4SLinus Torvalds 	/* stuff that we get in dev */
68621da177e4SLinus Torvalds 	bus = pci_dev->bus->number;
68631da177e4SLinus Torvalds 	func = pci_dev->devfn;
68641da177e4SLinus Torvalds 
68651da177e4SLinus Torvalds 	/* Init MEM/IO addresses to 0 */
68661da177e4SLinus Torvalds 	mem_addr = 0;
68671da177e4SLinus Torvalds 	io_addr = 0;
68681da177e4SLinus Torvalds 	mem_len = 0;
68691da177e4SLinus Torvalds 	io_len = 0;
68701da177e4SLinus Torvalds 
68711da177e4SLinus Torvalds 	for (j = 0; j < 2; j++) {
68721da177e4SLinus Torvalds 		if (!pci_resource_start(pci_dev, j))
68731da177e4SLinus Torvalds 			break;
68741da177e4SLinus Torvalds 
68751da177e4SLinus Torvalds 		if (pci_resource_flags(pci_dev, j) & IORESOURCE_IO) {
68761da177e4SLinus Torvalds 			io_addr = pci_resource_start(pci_dev, j);
68771da177e4SLinus Torvalds 			io_len = pci_resource_len(pci_dev, j);
68781da177e4SLinus Torvalds 		} else {
68791da177e4SLinus Torvalds 			mem_addr = pci_resource_start(pci_dev, j);
68801da177e4SLinus Torvalds 			mem_len = pci_resource_len(pci_dev, j);
68811da177e4SLinus Torvalds 		}
68821da177e4SLinus Torvalds 	}
68831da177e4SLinus Torvalds 
68841da177e4SLinus Torvalds 	/* setup memory mapped area (if applicable) */
68851da177e4SLinus Torvalds 	if (mem_addr) {
68861da177e4SLinus Torvalds 		uint32_t base;
68871da177e4SLinus Torvalds 		uint32_t offs;
68881da177e4SLinus Torvalds 
68891da177e4SLinus Torvalds 		base = mem_addr & PAGE_MASK;
68901da177e4SLinus Torvalds 		offs = mem_addr - base;
68911da177e4SLinus Torvalds 		ioremap_ptr = ioremap(base, PAGE_SIZE);
689221e1a5f2SJeff Garzik 		if (!ioremap_ptr)
689321e1a5f2SJeff Garzik 			return -1;
68941da177e4SLinus Torvalds 		mem_ptr = ioremap_ptr + offs;
68951da177e4SLinus Torvalds 	} else {
68961da177e4SLinus Torvalds 		ioremap_ptr = NULL;
68971da177e4SLinus Torvalds 		mem_ptr = NULL;
68981da177e4SLinus Torvalds 	}
68991da177e4SLinus Torvalds 
69001da177e4SLinus Torvalds 	/* found a controller */
6901dd00cc48SYoann Padioleau 	ha = kzalloc(sizeof (ips_ha_t), GFP_KERNEL);
69021da177e4SLinus Torvalds 	if (ha == NULL) {
69031da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69041da177e4SLinus Torvalds 			   "Unable to allocate temporary ha struct\n");
69051da177e4SLinus Torvalds 		return -1;
69061da177e4SLinus Torvalds 	}
69071da177e4SLinus Torvalds 
69081da177e4SLinus Torvalds 	ips_sh[index] = NULL;
69091da177e4SLinus Torvalds 	ips_ha[index] = ha;
69101da177e4SLinus Torvalds 	ha->active = 1;
69111da177e4SLinus Torvalds 
69121da177e4SLinus Torvalds 	/* Store info in HA structure */
69131da177e4SLinus Torvalds 	ha->io_addr = io_addr;
69141da177e4SLinus Torvalds 	ha->io_len = io_len;
69151da177e4SLinus Torvalds 	ha->mem_addr = mem_addr;
69161da177e4SLinus Torvalds 	ha->mem_len = mem_len;
69171da177e4SLinus Torvalds 	ha->mem_ptr = mem_ptr;
69181da177e4SLinus Torvalds 	ha->ioremap_ptr = ioremap_ptr;
69191da177e4SLinus Torvalds 	ha->host_num = (uint32_t) index;
69201da177e4SLinus Torvalds 	ha->slot_num = PCI_SLOT(pci_dev->devfn);
69211da177e4SLinus Torvalds 	ha->pcidev = pci_dev;
69221da177e4SLinus Torvalds 
69231da177e4SLinus Torvalds 	/*
69241da177e4SLinus Torvalds 	 * Set the pci_dev's dma_mask.  Not all adapters support 64bit
69251da177e4SLinus Torvalds 	 * addressing so don't enable it if the adapter can't support
69261da177e4SLinus Torvalds 	 * it!  Also, don't use 64bit addressing if dma addresses
69271da177e4SLinus Torvalds 	 * are guaranteed to be < 4G.
69281da177e4SLinus Torvalds 	 */
6929*88693b3cSChristoph Hellwig 	if (sizeof(dma_addr_t) > 4 && IPS_HAS_ENH_SGLIST(ha) &&
693032e76961SChristoph Hellwig 	    !dma_set_mask(&ha->pcidev->dev, DMA_BIT_MASK(64))) {
69311da177e4SLinus Torvalds 		(ha)->flags |= IPS_HA_ENH_SG;
69321da177e4SLinus Torvalds 	} else {
693332e76961SChristoph Hellwig 		if (dma_set_mask(&ha->pcidev->dev, DMA_BIT_MASK(32)) != 0) {
69341da177e4SLinus Torvalds 			printk(KERN_WARNING "Unable to set DMA Mask\n");
69351da177e4SLinus Torvalds 			return ips_abort_init(ha, index);
69361da177e4SLinus Torvalds 		}
69371da177e4SLinus Torvalds 	}
69381da177e4SLinus Torvalds 	if(ips_cd_boot && !ips_FlashData){
693932e76961SChristoph Hellwig 		ips_FlashData = dma_alloc_coherent(&pci_dev->dev,
694032e76961SChristoph Hellwig 				PAGE_SIZE << 7, &ips_flashbusaddr, GFP_KERNEL);
69411da177e4SLinus Torvalds 	}
69421da177e4SLinus Torvalds 
694332e76961SChristoph Hellwig 	ha->enq = dma_alloc_coherent(&pci_dev->dev, sizeof (IPS_ENQ),
694432e76961SChristoph Hellwig 			&ha->enq_busaddr, GFP_KERNEL);
69451da177e4SLinus Torvalds 	if (!ha->enq) {
69461da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69471da177e4SLinus Torvalds 			   "Unable to allocate host inquiry structure\n");
69481da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69491da177e4SLinus Torvalds 	}
69501da177e4SLinus Torvalds 
695132e76961SChristoph Hellwig 	ha->adapt = dma_alloc_coherent(&pci_dev->dev,
695232e76961SChristoph Hellwig 			sizeof (IPS_ADAPTER) + sizeof (IPS_IO_CMD),
695332e76961SChristoph Hellwig 			&dma_address, GFP_KERNEL);
69541da177e4SLinus Torvalds 	if (!ha->adapt) {
69551da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69561da177e4SLinus Torvalds 			   "Unable to allocate host adapt & dummy structures\n");
69571da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69581da177e4SLinus Torvalds 	}
69591da177e4SLinus Torvalds 	ha->adapt->hw_status_start = dma_address;
69601da177e4SLinus Torvalds 	ha->dummy = (void *) (ha->adapt + 1);
69611da177e4SLinus Torvalds 
69621da177e4SLinus Torvalds 
69631da177e4SLinus Torvalds 
696432e76961SChristoph Hellwig 	ha->logical_drive_info = dma_alloc_coherent(&pci_dev->dev,
696532e76961SChristoph Hellwig 			sizeof (IPS_LD_INFO), &dma_address, GFP_KERNEL);
69661da177e4SLinus Torvalds 	if (!ha->logical_drive_info) {
69671da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69681da177e4SLinus Torvalds 			   "Unable to allocate logical drive info structure\n");
69691da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69701da177e4SLinus Torvalds 	}
69711da177e4SLinus Torvalds 	ha->logical_drive_info_dma_addr = dma_address;
69721da177e4SLinus Torvalds 
69731da177e4SLinus Torvalds 
69741da177e4SLinus Torvalds 	ha->conf = kmalloc(sizeof (IPS_CONF), GFP_KERNEL);
69751da177e4SLinus Torvalds 
69761da177e4SLinus Torvalds 	if (!ha->conf) {
69771da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69781da177e4SLinus Torvalds 			   "Unable to allocate host conf structure\n");
69791da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69801da177e4SLinus Torvalds 	}
69811da177e4SLinus Torvalds 
69821da177e4SLinus Torvalds 	ha->nvram = kmalloc(sizeof (IPS_NVRAM_P5), GFP_KERNEL);
69831da177e4SLinus Torvalds 
69841da177e4SLinus Torvalds 	if (!ha->nvram) {
69851da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69861da177e4SLinus Torvalds 			   "Unable to allocate host NVRAM structure\n");
69871da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69881da177e4SLinus Torvalds 	}
69891da177e4SLinus Torvalds 
69901da177e4SLinus Torvalds 	ha->subsys = kmalloc(sizeof (IPS_SUBSYS), GFP_KERNEL);
69911da177e4SLinus Torvalds 
69921da177e4SLinus Torvalds 	if (!ha->subsys) {
69931da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
69941da177e4SLinus Torvalds 			   "Unable to allocate host subsystem structure\n");
69951da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
69961da177e4SLinus Torvalds 	}
69971da177e4SLinus Torvalds 
69981da177e4SLinus Torvalds 	/* the ioctl buffer is now used during adapter initialization, so its
69991da177e4SLinus Torvalds 	 * successful allocation is now required */
70001da177e4SLinus Torvalds 	if (ips_ioctlsize < PAGE_SIZE)
70011da177e4SLinus Torvalds 		ips_ioctlsize = PAGE_SIZE;
70021da177e4SLinus Torvalds 
700332e76961SChristoph Hellwig 	ha->ioctl_data = dma_alloc_coherent(&pci_dev->dev, ips_ioctlsize,
700432e76961SChristoph Hellwig 			&ha->ioctl_busaddr, GFP_KERNEL);
70051da177e4SLinus Torvalds 	ha->ioctl_len = ips_ioctlsize;
70061da177e4SLinus Torvalds 	if (!ha->ioctl_data) {
70071da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, pci_dev,
70081da177e4SLinus Torvalds 			   "Unable to allocate IOCTL data\n");
70091da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70101da177e4SLinus Torvalds 	}
70111da177e4SLinus Torvalds 
70121da177e4SLinus Torvalds 	/*
70131da177e4SLinus Torvalds 	 * Setup Functions
70141da177e4SLinus Torvalds 	 */
70151da177e4SLinus Torvalds 	ips_setup_funclist(ha);
70161da177e4SLinus Torvalds 
70171da177e4SLinus Torvalds 	if ((IPS_IS_MORPHEUS(ha)) || (IPS_IS_MARCO(ha))) {
70181da177e4SLinus Torvalds 		/* If Morpheus appears dead, reset it */
70191da177e4SLinus Torvalds 		IsDead = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
70201da177e4SLinus Torvalds 		if (IsDead == 0xDEADBEEF) {
70211da177e4SLinus Torvalds 			ips_reset_morpheus(ha);
70221da177e4SLinus Torvalds 		}
70231da177e4SLinus Torvalds 	}
70241da177e4SLinus Torvalds 
70251da177e4SLinus Torvalds 	/*
70261da177e4SLinus Torvalds 	 * Initialize the card if it isn't already
70271da177e4SLinus Torvalds 	 */
70281da177e4SLinus Torvalds 
70291da177e4SLinus Torvalds 	if (!(*ha->func.isinit) (ha)) {
70301da177e4SLinus Torvalds 		if (!(*ha->func.init) (ha)) {
70311da177e4SLinus Torvalds 			/*
70321da177e4SLinus Torvalds 			 * Initialization failed
70331da177e4SLinus Torvalds 			 */
70341da177e4SLinus Torvalds 			IPS_PRINTK(KERN_WARNING, pci_dev,
70351da177e4SLinus Torvalds 				   "Unable to initialize controller\n");
70361da177e4SLinus Torvalds 			return ips_abort_init(ha, index);
70371da177e4SLinus Torvalds 		}
70381da177e4SLinus Torvalds 	}
70391da177e4SLinus Torvalds 
70401da177e4SLinus Torvalds 	*indexPtr = index;
70411da177e4SLinus Torvalds 	return SUCCESS;
70421da177e4SLinus Torvalds }
70431da177e4SLinus Torvalds 
70441da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
70451da177e4SLinus Torvalds /*   Routine Name: ips_init_phase2                                           */
70461da177e4SLinus Torvalds /*                                                                           */
70471da177e4SLinus Torvalds /*   Routine Description:                                                    */
70481da177e4SLinus Torvalds /*     Adapter Initialization Phase 2                                        */
70491da177e4SLinus Torvalds /*                                                                           */
70501da177e4SLinus Torvalds /*   Return Value:                                                           */
70511da177e4SLinus Torvalds /*     0 if Successful, else non-zero                                        */
70521da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
70531da177e4SLinus Torvalds static int
70541da177e4SLinus Torvalds ips_init_phase2(int index)
70551da177e4SLinus Torvalds {
70561da177e4SLinus Torvalds 	ips_ha_t *ha;
70571da177e4SLinus Torvalds 
70581da177e4SLinus Torvalds 	ha = ips_ha[index];
70591da177e4SLinus Torvalds 
70601da177e4SLinus Torvalds 	METHOD_TRACE("ips_init_phase2", 1);
70611da177e4SLinus Torvalds 	if (!ha->active) {
70621da177e4SLinus Torvalds 		ips_ha[index] = NULL;
70631da177e4SLinus Torvalds 		return -1;
70641da177e4SLinus Torvalds 	}
70651da177e4SLinus Torvalds 
70661da177e4SLinus Torvalds 	/* Install the interrupt handler */
70678a694cc8SJeff Garzik 	if (request_irq(ha->pcidev->irq, do_ipsintr, IRQF_SHARED, ips_name, ha)) {
70681da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
70691da177e4SLinus Torvalds 			   "Unable to install interrupt handler\n");
70701da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70711da177e4SLinus Torvalds 	}
70721da177e4SLinus Torvalds 
70731da177e4SLinus Torvalds 	/*
70741da177e4SLinus Torvalds 	 * Allocate a temporary SCB for initialization
70751da177e4SLinus Torvalds 	 */
70761da177e4SLinus Torvalds 	ha->max_cmds = 1;
70771da177e4SLinus Torvalds 	if (!ips_allocatescbs(ha)) {
70781da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
70791da177e4SLinus Torvalds 			   "Unable to allocate a CCB\n");
70808a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
70811da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70821da177e4SLinus Torvalds 	}
70831da177e4SLinus Torvalds 
70841da177e4SLinus Torvalds 	if (!ips_hainit(ha)) {
70851da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
70861da177e4SLinus Torvalds 			   "Unable to initialize controller\n");
70878a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
70881da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70891da177e4SLinus Torvalds 	}
70901da177e4SLinus Torvalds 	/* Free the temporary SCB */
70911da177e4SLinus Torvalds 	ips_deallocatescbs(ha, 1);
70921da177e4SLinus Torvalds 
70931da177e4SLinus Torvalds 	/* allocate CCBs */
70941da177e4SLinus Torvalds 	if (!ips_allocatescbs(ha)) {
70951da177e4SLinus Torvalds 		IPS_PRINTK(KERN_WARNING, ha->pcidev,
70961da177e4SLinus Torvalds 			   "Unable to allocate CCBs\n");
70978a694cc8SJeff Garzik 		free_irq(ha->pcidev->irq, ha);
70981da177e4SLinus Torvalds 		return ips_abort_init(ha, index);
70991da177e4SLinus Torvalds 	}
71001da177e4SLinus Torvalds 
71011da177e4SLinus Torvalds 	return SUCCESS;
71021da177e4SLinus Torvalds }
71031da177e4SLinus Torvalds 
71041da177e4SLinus Torvalds MODULE_LICENSE("GPL");
71051da177e4SLinus Torvalds MODULE_DESCRIPTION("IBM ServeRAID Adapter Driver " IPS_VER_STRING);
71061da177e4SLinus Torvalds MODULE_VERSION(IPS_VER_STRING);
71071da177e4SLinus Torvalds 
71081da177e4SLinus Torvalds 
71091da177e4SLinus Torvalds /*
71101da177e4SLinus Torvalds  * Overrides for Emacs so that we almost follow Linus's tabbing style.
71111da177e4SLinus Torvalds  * Emacs will notice this stuff at the end of the file and automatically
71121da177e4SLinus Torvalds  * adjust the settings for this buffer only.  This must remain at the end
71131da177e4SLinus Torvalds  * of the file.
71141da177e4SLinus Torvalds  * ---------------------------------------------------------------------------
71151da177e4SLinus Torvalds  * Local variables:
71161da177e4SLinus Torvalds  * c-indent-level: 2
71171da177e4SLinus Torvalds  * c-brace-imaginary-offset: 0
71181da177e4SLinus Torvalds  * c-brace-offset: -2
71191da177e4SLinus Torvalds  * c-argdecl-indent: 2
71201da177e4SLinus Torvalds  * c-label-offset: -2
71211da177e4SLinus Torvalds  * c-continued-statement-offset: 2
71221da177e4SLinus Torvalds  * c-continued-brace-offset: 0
71231da177e4SLinus Torvalds  * indent-tabs-mode: nil
71241da177e4SLinus Torvalds  * tab-width: 8
71251da177e4SLinus Torvalds  * End:
71261da177e4SLinus Torvalds  */
7127