xref: /openbmc/linux/drivers/scsi/3w-xxxx.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds    3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
31da177e4SLinus Torvalds 
42c9bce5bSadam radford    Written By: Adam Radford <aradford@gmail.com>
51da177e4SLinus Torvalds    Modifications By: Joel Jacobson <linux@3ware.com>
61da177e4SLinus Torvalds                      Arnaldo Carvalho de Melo <acme@conectiva.com.br>
71da177e4SLinus Torvalds                      Brad Strand <linux@3ware.com>
81da177e4SLinus Torvalds 
94deedd84Sadam radford    Copyright (C) 1999-2010 3ware Inc.
101da177e4SLinus Torvalds 
1106fe9fb4SDirk Hohndel    Kernel compatibility By:	Andre Hedrick <andre@suse.com>
121da177e4SLinus Torvalds    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds    Further tiny build fixes and trivial hoovering    Alan Cox
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds    This program is free software; you can redistribute it and/or modify
171da177e4SLinus Torvalds    it under the terms of the GNU General Public License as published by
181da177e4SLinus Torvalds    the Free Software Foundation; version 2 of the License.
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds    This program is distributed in the hope that it will be useful,
211da177e4SLinus Torvalds    but WITHOUT ANY WARRANTY; without even the implied warranty of
221da177e4SLinus Torvalds    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
231da177e4SLinus Torvalds    GNU General Public License for more details.
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds    NO WARRANTY
261da177e4SLinus Torvalds    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
271da177e4SLinus Torvalds    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
281da177e4SLinus Torvalds    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
291da177e4SLinus Torvalds    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
301da177e4SLinus Torvalds    solely responsible for determining the appropriateness of using and
311da177e4SLinus Torvalds    distributing the Program and assumes all risks associated with its
321da177e4SLinus Torvalds    exercise of rights under this Agreement, including but not limited to
331da177e4SLinus Torvalds    the risks and costs of program errors, damage to or loss of data,
341da177e4SLinus Torvalds    programs or equipment, and unavailability or interruption of operations.
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds    DISCLAIMER OF LIABILITY
371da177e4SLinus Torvalds    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
381da177e4SLinus Torvalds    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
391da177e4SLinus Torvalds    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
401da177e4SLinus Torvalds    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
411da177e4SLinus Torvalds    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
421da177e4SLinus Torvalds    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
431da177e4SLinus Torvalds    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds    You should have received a copy of the GNU General Public License
461da177e4SLinus Torvalds    along with this program; if not, write to the Free Software
471da177e4SLinus Torvalds    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds    Bugs/Comments/Suggestions should be mailed to:
501da177e4SLinus Torvalds 
512c9bce5bSadam radford    aradford@gmail.com
522c9bce5bSadam radford 
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds    History
551da177e4SLinus Torvalds    -------
561da177e4SLinus Torvalds    0.1.000 -     Initial release.
571da177e4SLinus Torvalds    0.4.000 -     Added support for Asynchronous Event Notification through
581da177e4SLinus Torvalds                  ioctls for 3DM.
591da177e4SLinus Torvalds    1.0.000 -     Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb
601da177e4SLinus Torvalds                  to disable drive write-cache before writes.
611da177e4SLinus Torvalds    1.1.000 -     Fixed performance bug with DPO & FUA not existing for WRITE_6.
621da177e4SLinus Torvalds    1.2.000 -     Added support for clean shutdown notification/feature table.
631da177e4SLinus Torvalds    1.02.00.001 - Added support for full command packet posts through ioctls
641da177e4SLinus Torvalds                  for 3DM.
651da177e4SLinus Torvalds                  Bug fix so hot spare drives don't show up.
661da177e4SLinus Torvalds    1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
671da177e4SLinus Torvalds                  systems.
681da177e4SLinus Torvalds    08/21/00    - release previously allocated resources on failure at
691da177e4SLinus Torvalds                  tw_allocate_memory (acme)
701da177e4SLinus Torvalds    1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
711da177e4SLinus Torvalds                  controller status is non-zero.
721da177e4SLinus Torvalds                  Added handling of request_sense opcode.
731da177e4SLinus Torvalds 		 Fix possible null pointer dereference in
741da177e4SLinus Torvalds                  tw_reset_device_extension()
751da177e4SLinus Torvalds    1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
761da177e4SLinus Torvalds                  Make tw_setfeature() call with interrupts disabled.
771da177e4SLinus Torvalds                  Register interrupt handler before enabling interrupts.
781da177e4SLinus Torvalds                  Clear attention interrupt before draining aen queue.
791da177e4SLinus Torvalds    1.02.00.005 - Allocate bounce buffers and custom queue depth for raid5 for
801da177e4SLinus Torvalds                  6000 and 5000 series controllers.
811da177e4SLinus Torvalds                  Reduce polling mdelays causing problems on some systems.
821da177e4SLinus Torvalds                  Fix use_sg = 1 calculation bug.
831da177e4SLinus Torvalds                  Check for scsi_register returning NULL.
841da177e4SLinus Torvalds                  Add aen count to /proc/scsi/3w-xxxx.
851da177e4SLinus Torvalds                  Remove aen code unit masking in tw_aen_complete().
861da177e4SLinus Torvalds    1.02.00.006 - Remove unit from printk in tw_scsi_eh_abort(), causing
871da177e4SLinus Torvalds                  possible oops.
881da177e4SLinus Torvalds                  Fix possible null pointer dereference in tw_scsi_queue()
891da177e4SLinus Torvalds                  if done function pointer was invalid.
901da177e4SLinus Torvalds    1.02.00.007 - Fix possible null pointer dereferences in tw_ioctl().
911da177e4SLinus Torvalds                  Remove check for invalid done function pointer from
921da177e4SLinus Torvalds                  tw_scsi_queue().
931da177e4SLinus Torvalds    1.02.00.008 - Set max sectors per io to TW_MAX_SECTORS in tw_findcards().
941da177e4SLinus Torvalds                  Add tw_decode_error() for printing readable error messages.
951da177e4SLinus Torvalds                  Print some useful information on certain aen codes.
961da177e4SLinus Torvalds                  Add tw_decode_bits() for interpreting status register output.
971da177e4SLinus Torvalds                  Make scsi_set_pci_device() for kernels >= 2.4.4
981da177e4SLinus Torvalds                  Fix bug where aen's could be lost before a reset.
991da177e4SLinus Torvalds                  Re-add spinlocks in tw_scsi_detect().
1001da177e4SLinus Torvalds                  Fix possible null pointer dereference in tw_aen_drain_queue()
1011da177e4SLinus Torvalds                  during initialization.
1021da177e4SLinus Torvalds                  Clear pci parity errors during initialization and during io.
1031da177e4SLinus Torvalds    1.02.00.009 - Remove redundant increment in tw_state_request_start().
1041da177e4SLinus Torvalds                  Add ioctl support for direct ATA command passthru.
1051da177e4SLinus Torvalds                  Add entire aen code string list.
1061da177e4SLinus Torvalds    1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
1071da177e4SLinus Torvalds                  Fix get_param for specific units.
1081da177e4SLinus Torvalds    1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
1091da177e4SLinus Torvalds                  Fix tw_aen_drain_queue() to display useful info at init.
1101da177e4SLinus Torvalds                  Set tw_host->max_id for 12 port cards.
1111da177e4SLinus Torvalds                  Add ioctl support for raw command packet post from userspace
1121da177e4SLinus Torvalds                  with sglist fragments (parameter and io).
1131da177e4SLinus Torvalds    1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
1141da177e4SLinus Torvalds                  last sector ioctl.
1151da177e4SLinus Torvalds    1.02.00.013 - Fix bug where more AEN codes weren't coming out during
1161da177e4SLinus Torvalds                  driver initialization.
1171da177e4SLinus Torvalds                  Improved handling of PCI aborts.
1181da177e4SLinus Torvalds    1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
1191da177e4SLinus Torvalds                  Increase timeout in tw_aen_drain_queue() to 30 seconds.
1201da177e4SLinus Torvalds    1.02.00.015 - Re-write raw command post with data ioctl method.
1211da177e4SLinus Torvalds                  Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5
1221da177e4SLinus Torvalds                  Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5
1231da177e4SLinus Torvalds                  Replace io_request_lock with host_lock for kernel 2.5
1241da177e4SLinus Torvalds                  Set max_cmd_len to 16 for 3dm for kernel 2.5
1251da177e4SLinus Torvalds    1.02.00.016 - Set host->max_sectors back up to 256.
1261da177e4SLinus Torvalds    1.02.00.017 - Modified pci parity error handling/clearing from config space
1271da177e4SLinus Torvalds                  during initialization.
1281da177e4SLinus Torvalds    1.02.00.018 - Better handling of request sense opcode and sense information
1291da177e4SLinus Torvalds                  for failed commands.  Add tw_decode_sense().
1301da177e4SLinus Torvalds                  Replace all mdelay()'s with scsi_sleep().
1311da177e4SLinus Torvalds    1.02.00.019 - Revert mdelay's and scsi_sleep's, this caused problems on
1321da177e4SLinus Torvalds                  some SMP systems.
1331da177e4SLinus Torvalds    1.02.00.020 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to
1341da177e4SLinus Torvalds                  pci_alloc/free_consistent().
1351da177e4SLinus Torvalds                  Better alignment checking in tw_allocate_memory().
1361da177e4SLinus Torvalds                  Cleanup tw_initialize_device_extension().
1371da177e4SLinus Torvalds    1.02.00.021 - Bump cmd_per_lun in SHT to 255 for better jbod performance.
1381da177e4SLinus Torvalds                  Improve handling of errors in tw_interrupt().
1391da177e4SLinus Torvalds                  Add handling/clearing of controller queue error.
1401da177e4SLinus Torvalds                  Empty stale responses before draining aen queue.
1411da177e4SLinus Torvalds                  Fix tw_scsi_eh_abort() to not reset on every io abort.
1421da177e4SLinus Torvalds                  Set can_queue in SHT to 255 to prevent hang from AEN.
1431da177e4SLinus Torvalds    1.02.00.022 - Fix possible null pointer dereference in tw_scsi_release().
1441da177e4SLinus Torvalds    1.02.00.023 - Fix bug in tw_aen_drain_queue() where unit # was always zero.
1451da177e4SLinus Torvalds    1.02.00.024 - Add severity levels to AEN strings.
1461da177e4SLinus Torvalds    1.02.00.025 - Fix command interrupt spurious error messages.
1471da177e4SLinus Torvalds                  Fix bug in raw command post with data ioctl method.
1481da177e4SLinus Torvalds                  Fix bug where rollcall sometimes failed with cable errors.
1491da177e4SLinus Torvalds                  Print unit # on all command timeouts.
1501da177e4SLinus Torvalds    1.02.00.026 - Fix possible infinite retry bug with power glitch induced
1511da177e4SLinus Torvalds                  drive timeouts.
1521da177e4SLinus Torvalds                  Cleanup some AEN severity levels.
1531da177e4SLinus Torvalds    1.02.00.027 - Add drive not supported AEN code for SATA controllers.
1541da177e4SLinus Torvalds                  Remove spurious unknown ioctl error message.
1551da177e4SLinus Torvalds    1.02.00.028 - Fix bug where multiple controllers with no units were the
1561da177e4SLinus Torvalds                  same card number.
1571da177e4SLinus Torvalds                  Fix bug where cards were being shut down more than once.
1581da177e4SLinus Torvalds    1.02.00.029 - Add missing pci_free_consistent() in tw_allocate_memory().
1591da177e4SLinus Torvalds                  Replace pci_map_single() with pci_map_page() for highmem.
1601da177e4SLinus Torvalds                  Check for tw_setfeature() failure.
1611da177e4SLinus Torvalds    1.02.00.030 - Make driver 64-bit clean.
1621da177e4SLinus Torvalds    1.02.00.031 - Cleanup polling timeouts/routines in several places.
1631da177e4SLinus Torvalds                  Add support for mode sense opcode.
1641da177e4SLinus Torvalds                  Add support for cache mode page.
1651da177e4SLinus Torvalds                  Add support for synchronize cache opcode.
1661da177e4SLinus Torvalds    1.02.00.032 - Fix small multicard rollcall bug.
1671da177e4SLinus Torvalds                  Make driver stay loaded with no units for hot add/swap.
1681da177e4SLinus Torvalds                  Add support for "twe" character device for ioctls.
1691da177e4SLinus Torvalds                  Clean up request_id queueing code.
1701da177e4SLinus Torvalds                  Fix tw_scsi_queue() spinlocks.
1711da177e4SLinus Torvalds    1.02.00.033 - Fix tw_aen_complete() to not queue 'queue empty' AEN's.
1721da177e4SLinus Torvalds                  Initialize queues correctly when loading with no valid units.
1731da177e4SLinus Torvalds    1.02.00.034 - Fix tw_decode_bits() to handle multiple errors.
1741da177e4SLinus Torvalds                  Add support for user configurable cmd_per_lun.
1751da177e4SLinus Torvalds                  Add support for sht->slave_configure().
1761da177e4SLinus Torvalds    1.02.00.035 - Improve tw_allocate_memory() memory allocation.
1771da177e4SLinus Torvalds                  Fix tw_chrdev_ioctl() to sleep correctly.
1781da177e4SLinus Torvalds    1.02.00.036 - Increase character ioctl timeout to 60 seconds.
1791da177e4SLinus Torvalds    1.02.00.037 - Fix tw_ioctl() to handle all non-data ATA passthru cmds
1801da177e4SLinus Torvalds                  for 'smartmontools' support.
1811da177e4SLinus Torvalds    1.26.00.038 - Roll driver minor version to 26 to denote kernel 2.6.
1821da177e4SLinus Torvalds                  Add support for cmds_per_lun module parameter.
1831da177e4SLinus Torvalds    1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code.
1841da177e4SLinus Torvalds                  Fix data_buffer_length usage in tw_chrdev_ioctl().
1851da177e4SLinus Torvalds                  Update contact information.
1861da177e4SLinus Torvalds    1.26.02.000 - Convert driver to pci_driver format.
1871da177e4SLinus Torvalds    1.26.02.001 - Increase max ioctl buffer size to 512 sectors.
1881da177e4SLinus Torvalds                  Make tw_scsi_queue() return 0 for 'Unknown scsi opcode'.
1891da177e4SLinus Torvalds                  Fix tw_remove() to free irq handler/unregister_chrdev()
1901da177e4SLinus Torvalds                  before shutting down card.
1911da177e4SLinus Torvalds                  Change to new 'change_queue_depth' api.
1921da177e4SLinus Torvalds                  Fix 'handled=1' ISR usage, remove bogus IRQ check.
1934fe48187Sadam radford    1.26.02.002 - Free irq handler in __tw_shutdown().
1944fe48187Sadam radford                  Turn on RCD bit for caching mode page.
1954fe48187Sadam radford                  Serialize reset code.
1964deedd84Sadam radford    1.26.02.003 - Force 60 second timeout default.
1971da177e4SLinus Torvalds */
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds #include <linux/module.h>
2001da177e4SLinus Torvalds #include <linux/reboot.h>
2011da177e4SLinus Torvalds #include <linux/spinlock.h>
2021da177e4SLinus Torvalds #include <linux/interrupt.h>
2031da177e4SLinus Torvalds #include <linux/moduleparam.h>
2041da177e4SLinus Torvalds #include <linux/errno.h>
2051da177e4SLinus Torvalds #include <linux/types.h>
2061da177e4SLinus Torvalds #include <linux/delay.h>
2075a0e3ad6STejun Heo #include <linux/gfp.h>
2081da177e4SLinus Torvalds #include <linux/pci.h>
2091da177e4SLinus Torvalds #include <linux/time.h>
210a12e25bdSJes Sorensen #include <linux/mutex.h>
2111da177e4SLinus Torvalds #include <asm/io.h>
2121da177e4SLinus Torvalds #include <asm/irq.h>
2137c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2141da177e4SLinus Torvalds #include <scsi/scsi.h>
2151da177e4SLinus Torvalds #include <scsi/scsi_host.h>
2161da177e4SLinus Torvalds #include <scsi/scsi_tcq.h>
2171da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h>
21828ce280fSMartin K. Petersen #include <scsi/scsi_eh.h>
2191da177e4SLinus Torvalds #include "3w-xxxx.h"
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds /* Globals */
2224deedd84Sadam radford #define TW_DRIVER_VERSION "1.26.02.003"
223c45d15d2SArnd Bergmann static DEFINE_MUTEX(tw_mutex);
2241da177e4SLinus Torvalds static TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
2251da177e4SLinus Torvalds static int tw_device_extension_count = 0;
2261da177e4SLinus Torvalds static int twe_major = -1;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds /* Module parameters */
2294deedd84Sadam radford MODULE_AUTHOR("LSI");
2301da177e4SLinus Torvalds MODULE_DESCRIPTION("3ware Storage Controller Linux Driver");
2311da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2321da177e4SLinus Torvalds MODULE_VERSION(TW_DRIVER_VERSION);
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds /* Function prototypes */
2354fe48187Sadam radford static int tw_reset_device_extension(TW_Device_Extension *tw_dev);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds /* Functions */
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds /* This function will check the status register for unexpected bits */
tw_check_bits(u32 status_reg_value)2401da177e4SLinus Torvalds static int tw_check_bits(u32 status_reg_value)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds 	if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
2431da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value);
2441da177e4SLinus Torvalds 		return 1;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) {
2471da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value);
2481da177e4SLinus Torvalds 		return 1;
2491da177e4SLinus Torvalds 	}
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	return 0;
2521da177e4SLinus Torvalds } /* End tw_check_bits() */
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds /* This function will print readable messages from status register errors */
tw_decode_bits(TW_Device_Extension * tw_dev,u32 status_reg_value,int print_host)2551da177e4SLinus Torvalds static int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	char host[16];
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	if (print_host)
2621da177e4SLinus Torvalds 		sprintf(host, " scsi%d:", tw_dev->host->host_no);
2631da177e4SLinus Torvalds 	else
2641da177e4SLinus Torvalds 		host[0] = '\0';
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
2671da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host);
2681da177e4SLinus Torvalds 		outl(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
2691da177e4SLinus Torvalds 	}
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_PCI_ABORT) {
2721da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host);
2731da177e4SLinus Torvalds 		outl(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
2741da177e4SLinus Torvalds 		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
2781da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host);
2791da177e4SLinus Torvalds 		outl(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
2801da177e4SLinus Torvalds 	}
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
2831da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host);
2841da177e4SLinus Torvalds 		outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
2851da177e4SLinus Torvalds 	}
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
2881da177e4SLinus Torvalds 		if (tw_dev->reset_print == 0) {
2891da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
2901da177e4SLinus Torvalds 			tw_dev->reset_print = 1;
2911da177e4SLinus Torvalds 		}
2921da177e4SLinus Torvalds 		return 1;
2931da177e4SLinus Torvalds 	}
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	return 0;
2961da177e4SLinus Torvalds } /* End tw_decode_bits() */
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds /* This function will poll the status register for a flag */
tw_poll_status(TW_Device_Extension * tw_dev,u32 flag,int seconds)2991da177e4SLinus Torvalds static int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	u32 status_reg_value;
3021da177e4SLinus Torvalds 	unsigned long before;
3031da177e4SLinus Torvalds 	int retval = 1;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
3061da177e4SLinus Torvalds 	before = jiffies;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	if (tw_check_bits(status_reg_value))
3091da177e4SLinus Torvalds 		tw_decode_bits(tw_dev, status_reg_value, 0);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	while ((status_reg_value & flag) != flag) {
3121da177e4SLinus Torvalds 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 		if (tw_check_bits(status_reg_value))
3151da177e4SLinus Torvalds 			tw_decode_bits(tw_dev, status_reg_value, 0);
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 		if (time_after(jiffies, before + HZ * seconds))
3181da177e4SLinus Torvalds 			goto out;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 		msleep(50);
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 	retval = 0;
3231da177e4SLinus Torvalds out:
3241da177e4SLinus Torvalds 	return retval;
3251da177e4SLinus Torvalds } /* End tw_poll_status() */
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds /* This function will poll the status register for disappearance of a flag */
tw_poll_status_gone(TW_Device_Extension * tw_dev,u32 flag,int seconds)3281da177e4SLinus Torvalds static int tw_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	u32 status_reg_value;
3311da177e4SLinus Torvalds 	unsigned long before;
3321da177e4SLinus Torvalds 	int retval = 1;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
3351da177e4SLinus Torvalds 	before = jiffies;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	if (tw_check_bits(status_reg_value))
3381da177e4SLinus Torvalds 		tw_decode_bits(tw_dev, status_reg_value, 0);
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	while ((status_reg_value & flag) != 0) {
3411da177e4SLinus Torvalds 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 		if (tw_check_bits(status_reg_value))
3441da177e4SLinus Torvalds 			tw_decode_bits(tw_dev, status_reg_value, 0);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 		if (time_after(jiffies, before + HZ * seconds))
3471da177e4SLinus Torvalds 			goto out;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 		msleep(50);
3501da177e4SLinus Torvalds 	}
3511da177e4SLinus Torvalds 	retval = 0;
3521da177e4SLinus Torvalds out:
3531da177e4SLinus Torvalds 	return retval;
3541da177e4SLinus Torvalds } /* End tw_poll_status_gone() */
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds /* This function will attempt to post a command packet to the board */
tw_post_command_packet(TW_Device_Extension * tw_dev,int request_id)3571da177e4SLinus Torvalds static int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
3581da177e4SLinus Torvalds {
3591da177e4SLinus Torvalds 	u32 status_reg_value;
3601da177e4SLinus Torvalds 	unsigned long command_que_value;
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n");
3631da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
3641da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 	if (tw_check_bits(status_reg_value)) {
3671da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n");
3681da177e4SLinus Torvalds 		tw_decode_bits(tw_dev, status_reg_value, 1);
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
3721da177e4SLinus Torvalds 		/* We successfully posted the command packet */
3731da177e4SLinus Torvalds 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
3741da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_POSTED;
3751da177e4SLinus Torvalds 		tw_dev->posted_request_count++;
3761da177e4SLinus Torvalds 		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
3771da177e4SLinus Torvalds 			tw_dev->max_posted_request_count = tw_dev->posted_request_count;
3781da177e4SLinus Torvalds 		}
3791da177e4SLinus Torvalds 	} else {
3801da177e4SLinus Torvalds 		/* Couldn't post the command packet, so we do it in the isr */
3811da177e4SLinus Torvalds 		if (tw_dev->state[request_id] != TW_S_PENDING) {
3821da177e4SLinus Torvalds 			tw_dev->state[request_id] = TW_S_PENDING;
3831da177e4SLinus Torvalds 			tw_dev->pending_request_count++;
3841da177e4SLinus Torvalds 			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
3851da177e4SLinus Torvalds 				tw_dev->max_pending_request_count = tw_dev->pending_request_count;
3861da177e4SLinus Torvalds 			}
3871da177e4SLinus Torvalds 			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
3881da177e4SLinus Torvalds 			if (tw_dev->pending_tail == TW_Q_LENGTH-1) {
3891da177e4SLinus Torvalds 				tw_dev->pending_tail = TW_Q_START;
3901da177e4SLinus Torvalds 			} else {
3911da177e4SLinus Torvalds 				tw_dev->pending_tail = tw_dev->pending_tail + 1;
3921da177e4SLinus Torvalds 			}
3931da177e4SLinus Torvalds 		}
3941da177e4SLinus Torvalds 		TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
3951da177e4SLinus Torvalds 		return 1;
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 	return 0;
3981da177e4SLinus Torvalds } /* End tw_post_command_packet() */
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds /* This function will return valid sense buffer information for failed cmds */
tw_decode_sense(TW_Device_Extension * tw_dev,int request_id,int fill_sense)4011da177e4SLinus Torvalds static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
4021da177e4SLinus Torvalds {
4031da177e4SLinus Torvalds 	int i;
4041da177e4SLinus Torvalds 	TW_Command *command;
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
4071da177e4SLinus Torvalds 	command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, TW_UNIT_OUT(command->unit__hostid));
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	/* Attempt to return intelligent sense information */
4121da177e4SLinus Torvalds 	if (fill_sense) {
4131da177e4SLinus Torvalds 		if ((command->status == 0xc7) || (command->status == 0xcb)) {
4146391a113STobias Klauser 			for (i = 0; i < ARRAY_SIZE(tw_sense_table); i++) {
4151da177e4SLinus Torvalds 				if (command->flags == tw_sense_table[i][0]) {
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 					/* Valid bit and 'current errors' */
4181da177e4SLinus Torvalds 					tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 					/* Sense key */
4211da177e4SLinus Torvalds 					tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 					/* Additional sense length */
4241da177e4SLinus Torvalds 					tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 					/* Additional sense code */
4271da177e4SLinus Torvalds 					tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 					/* Additional sense code qualifier */
4301da177e4SLinus Torvalds 					tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
4311da177e4SLinus Torvalds 
4323d45cefcSHannes Reinecke 					tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
4331da177e4SLinus Torvalds 					return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
4341da177e4SLinus Torvalds 				}
4351da177e4SLinus Torvalds 			}
4361da177e4SLinus Torvalds 		}
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 		/* If no table match, error so we get a reset */
4391da177e4SLinus Torvalds 		return 1;
4401da177e4SLinus Torvalds 	}
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	return 0;
4431da177e4SLinus Torvalds } /* End tw_decode_sense() */
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds /* This function will report controller error status */
tw_check_errors(TW_Device_Extension * tw_dev)4461da177e4SLinus Torvalds static int tw_check_errors(TW_Device_Extension *tw_dev)
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds 	u32 status_reg_value;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
4531da177e4SLinus Torvalds 		tw_decode_bits(tw_dev, status_reg_value, 0);
4541da177e4SLinus Torvalds 		return 1;
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	return 0;
4581da177e4SLinus Torvalds } /* End tw_check_errors() */
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds /* This function will empty the response que */
tw_empty_response_que(TW_Device_Extension * tw_dev)4611da177e4SLinus Torvalds static void tw_empty_response_que(TW_Device_Extension *tw_dev)
4621da177e4SLinus Torvalds {
4636c31cb74SLee Jones 	u32 status_reg_value;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
4686c31cb74SLee Jones 		inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
4691da177e4SLinus Torvalds 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
4701da177e4SLinus Torvalds 	}
4711da177e4SLinus Torvalds } /* End tw_empty_response_que() */
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds /* This function will free a request_id */
tw_state_request_finish(TW_Device_Extension * tw_dev,int request_id)4741da177e4SLinus Torvalds static void tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id)
4751da177e4SLinus Torvalds {
4761da177e4SLinus Torvalds 	tw_dev->free_queue[tw_dev->free_tail] = request_id;
4771da177e4SLinus Torvalds 	tw_dev->state[request_id] = TW_S_FINISHED;
4781da177e4SLinus Torvalds 	tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
4791da177e4SLinus Torvalds } /* End tw_state_request_finish() */
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds /* This function will assign an available request_id */
tw_state_request_start(TW_Device_Extension * tw_dev,int * request_id)4821da177e4SLinus Torvalds static void tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
4831da177e4SLinus Torvalds {
4841da177e4SLinus Torvalds 	*request_id = tw_dev->free_queue[tw_dev->free_head];
4851da177e4SLinus Torvalds 	tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
4861da177e4SLinus Torvalds 	tw_dev->state[*request_id] = TW_S_STARTED;
4871da177e4SLinus Torvalds } /* End tw_state_request_start() */
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds /* Show some statistics about the card */
tw_show_stats(struct device * dev,struct device_attribute * attr,char * buf)490ee959b00STony Jones static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
491ee959b00STony Jones 			     char *buf)
4921da177e4SLinus Torvalds {
493ee959b00STony Jones 	struct Scsi_Host *host = class_to_shost(dev);
4941da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
4951da177e4SLinus Torvalds 	unsigned long flags = 0;
4961da177e4SLinus Torvalds 	ssize_t len;
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 	spin_lock_irqsave(tw_dev->host->host_lock, flags);
4991da177e4SLinus Torvalds 	len = snprintf(buf, PAGE_SIZE, "3w-xxxx Driver version: %s\n"
5001da177e4SLinus Torvalds 		       "Current commands posted:   %4d\n"
5011da177e4SLinus Torvalds 		       "Max commands posted:       %4d\n"
5021da177e4SLinus Torvalds 		       "Current pending commands:  %4d\n"
5031da177e4SLinus Torvalds 		       "Max pending commands:      %4d\n"
5041da177e4SLinus Torvalds 		       "Last sgl length:           %4d\n"
5051da177e4SLinus Torvalds 		       "Max sgl length:            %4d\n"
5061da177e4SLinus Torvalds 		       "Last sector count:         %4d\n"
5071da177e4SLinus Torvalds 		       "Max sector count:          %4d\n"
5081da177e4SLinus Torvalds 		       "SCSI Host Resets:          %4d\n"
5091da177e4SLinus Torvalds 		       "AEN's:                     %4d\n",
5101da177e4SLinus Torvalds 		       TW_DRIVER_VERSION,
5111da177e4SLinus Torvalds 		       tw_dev->posted_request_count,
5121da177e4SLinus Torvalds 		       tw_dev->max_posted_request_count,
5131da177e4SLinus Torvalds 		       tw_dev->pending_request_count,
5141da177e4SLinus Torvalds 		       tw_dev->max_pending_request_count,
5151da177e4SLinus Torvalds 		       tw_dev->sgl_entries,
5161da177e4SLinus Torvalds 		       tw_dev->max_sgl_entries,
5171da177e4SLinus Torvalds 		       tw_dev->sector_count,
5181da177e4SLinus Torvalds 		       tw_dev->max_sector_count,
5191da177e4SLinus Torvalds 		       tw_dev->num_resets,
5201da177e4SLinus Torvalds 		       tw_dev->aen_count);
5211da177e4SLinus Torvalds 	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
5221da177e4SLinus Torvalds 	return len;
5231da177e4SLinus Torvalds } /* End tw_show_stats() */
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /* Create sysfs 'stats' entry */
526ee959b00STony Jones static struct device_attribute tw_host_stats_attr = {
5271da177e4SLinus Torvalds 	.attr = {
5281da177e4SLinus Torvalds 		.name =		"stats",
5291da177e4SLinus Torvalds 		.mode =		S_IRUGO,
5301da177e4SLinus Torvalds 	},
5311da177e4SLinus Torvalds 	.show = tw_show_stats
5321da177e4SLinus Torvalds };
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds /* Host attributes initializer */
53565bc2a7fSBart Van Assche static struct attribute *tw_host_attrs[] = {
53665bc2a7fSBart Van Assche 	&tw_host_stats_attr.attr,
5371da177e4SLinus Torvalds 	NULL,
5381da177e4SLinus Torvalds };
5391da177e4SLinus Torvalds 
54065bc2a7fSBart Van Assche ATTRIBUTE_GROUPS(tw_host);
54165bc2a7fSBart Van Assche 
5421da177e4SLinus Torvalds /* This function will read the aen queue from the isr */
tw_aen_read_queue(TW_Device_Extension * tw_dev,int request_id)5431da177e4SLinus Torvalds static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
5441da177e4SLinus Torvalds {
5451da177e4SLinus Torvalds 	TW_Command *command_packet;
5461da177e4SLinus Torvalds 	TW_Param *param;
5471da177e4SLinus Torvalds 	unsigned long command_que_value;
5481da177e4SLinus Torvalds 	u32 status_reg_value;
5491da177e4SLinus Torvalds 	unsigned long param_value = 0;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n");
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
5541da177e4SLinus Torvalds 	if (tw_check_bits(status_reg_value)) {
5551da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n");
5561da177e4SLinus Torvalds 		tw_decode_bits(tw_dev, status_reg_value, 1);
5571da177e4SLinus Torvalds 		return 1;
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
5601da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n");
5611da177e4SLinus Torvalds 		return 1;
5621da177e4SLinus Torvalds 	}
5631da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
5641da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
5651da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
5661da177e4SLinus Torvalds 	command_packet->size = 4;
5671da177e4SLinus Torvalds 	command_packet->request_id = request_id;
5681da177e4SLinus Torvalds 	command_packet->status = 0;
5691da177e4SLinus Torvalds 	command_packet->flags = 0;
5701da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
5711da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
5721da177e4SLinus Torvalds 	if (command_que_value == 0) {
5731da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n");
5741da177e4SLinus Torvalds 		return 1;
5751da177e4SLinus Torvalds 	}
5761da177e4SLinus Torvalds 	/* Now setup the param */
5771da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
5781da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n");
5791da177e4SLinus Torvalds 		return 1;
5801da177e4SLinus Torvalds 	}
5811da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
5821da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
5831da177e4SLinus Torvalds 	param->table_id = 0x401; /* AEN table */
5841da177e4SLinus Torvalds 	param->parameter_id = 2; /* Unit code */
5851da177e4SLinus Torvalds 	param->parameter_size_bytes = 2;
5861da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
5871da177e4SLinus Torvalds 	if (param_value == 0) {
5881da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n");
5891da177e4SLinus Torvalds 		return 1;
5901da177e4SLinus Torvalds 	}
5911da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
5921da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 	/* Now post the command packet */
5951da177e4SLinus Torvalds 	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
5961da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n");
5971da177e4SLinus Torvalds 		tw_dev->srb[request_id] = NULL; /* Flag internal command */
5981da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_POSTED;
5991da177e4SLinus Torvalds 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
6001da177e4SLinus Torvalds 	} else {
6011da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n");
6021da177e4SLinus Torvalds 		return 1;
6031da177e4SLinus Torvalds 	}
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 	return 0;
6061da177e4SLinus Torvalds } /* End tw_aen_read_queue() */
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds /* This function will complete an aen request from the isr */
tw_aen_complete(TW_Device_Extension * tw_dev,int request_id)6091da177e4SLinus Torvalds static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
6101da177e4SLinus Torvalds {
6111da177e4SLinus Torvalds 	TW_Param *param;
6121da177e4SLinus Torvalds 	unsigned short aen;
6131da177e4SLinus Torvalds 	int error = 0, table_max = 0;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
6161da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
6171da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n");
6181da177e4SLinus Torvalds 		return 1;
6191da177e4SLinus Torvalds 	}
6201da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
6211da177e4SLinus Torvalds 	aen = *(unsigned short *)(param->data);
6221da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen);
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* Print some useful info when certain aen codes come out */
6251da177e4SLinus Torvalds 	if (aen == 0x0ff) {
6261da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no);
6271da177e4SLinus Torvalds 	} else {
6286391a113STobias Klauser 		table_max = ARRAY_SIZE(tw_aen_string);
6291da177e4SLinus Torvalds 		if ((aen & 0x0ff) < table_max) {
6301da177e4SLinus Torvalds 			if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
6311da177e4SLinus Torvalds 				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
6321da177e4SLinus Torvalds 			} else {
6331da177e4SLinus Torvalds 				if (aen != 0x0)
6341da177e4SLinus Torvalds 					printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
6351da177e4SLinus Torvalds 			}
6361da177e4SLinus Torvalds 		} else {
6371da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
6381da177e4SLinus Torvalds 		}
6391da177e4SLinus Torvalds 	}
6401da177e4SLinus Torvalds 	if (aen != TW_AEN_QUEUE_EMPTY) {
6411da177e4SLinus Torvalds 		tw_dev->aen_count++;
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 		/* Now queue the code */
6441da177e4SLinus Torvalds 		tw_dev->aen_queue[tw_dev->aen_tail] = aen;
6451da177e4SLinus Torvalds 		if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
6461da177e4SLinus Torvalds 			tw_dev->aen_tail = TW_Q_START;
6471da177e4SLinus Torvalds 		} else {
6481da177e4SLinus Torvalds 			tw_dev->aen_tail = tw_dev->aen_tail + 1;
6491da177e4SLinus Torvalds 		}
6501da177e4SLinus Torvalds 		if (tw_dev->aen_head == tw_dev->aen_tail) {
6511da177e4SLinus Torvalds 			if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
6521da177e4SLinus Torvalds 				tw_dev->aen_head = TW_Q_START;
6531da177e4SLinus Torvalds 			} else {
6541da177e4SLinus Torvalds 				tw_dev->aen_head = tw_dev->aen_head + 1;
6551da177e4SLinus Torvalds 			}
6561da177e4SLinus Torvalds 		}
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 		error = tw_aen_read_queue(tw_dev, request_id);
6591da177e4SLinus Torvalds 		if (error) {
6601da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
6611da177e4SLinus Torvalds 			tw_dev->state[request_id] = TW_S_COMPLETED;
6621da177e4SLinus Torvalds 			tw_state_request_finish(tw_dev, request_id);
6631da177e4SLinus Torvalds 		}
6641da177e4SLinus Torvalds 	} else {
6651da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_COMPLETED;
6661da177e4SLinus Torvalds 		tw_state_request_finish(tw_dev, request_id);
6671da177e4SLinus Torvalds 	}
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	return 0;
6701da177e4SLinus Torvalds } /* End tw_aen_complete() */
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds /* This function will drain the aen queue after a soft reset */
tw_aen_drain_queue(TW_Device_Extension * tw_dev)6731da177e4SLinus Torvalds static int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	TW_Command *command_packet;
6761da177e4SLinus Torvalds 	TW_Param *param;
6771da177e4SLinus Torvalds 	int request_id = 0;
6781da177e4SLinus Torvalds 	unsigned long command_que_value;
6791da177e4SLinus Torvalds 	unsigned long param_value;
6801da177e4SLinus Torvalds 	TW_Response_Queue response_queue;
6811da177e4SLinus Torvalds 	unsigned short aen;
6821da177e4SLinus Torvalds 	unsigned short aen_code;
6831da177e4SLinus Torvalds 	int finished = 0;
6841da177e4SLinus Torvalds 	int first_reset = 0;
6851da177e4SLinus Torvalds 	int queue = 0;
6861da177e4SLinus Torvalds 	int found = 0, table_max = 0;
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n");
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT | TW_STATUS_MICROCONTROLLER_READY, 30)) {
6911da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
6921da177e4SLinus Torvalds 		return 1;
6931da177e4SLinus Torvalds 	}
6941da177e4SLinus Torvalds 	TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	/* Empty response queue */
6971da177e4SLinus Torvalds 	tw_empty_response_que(tw_dev);
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 	/* Initialize command packet */
7001da177e4SLinus Torvalds 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
7011da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n");
7021da177e4SLinus Torvalds 		return 1;
7031da177e4SLinus Torvalds 	}
7041da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
7051da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
7061da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
7071da177e4SLinus Torvalds 	command_packet->size = 4;
7081da177e4SLinus Torvalds 	command_packet->request_id = request_id;
7091da177e4SLinus Torvalds 	command_packet->status = 0;
7101da177e4SLinus Torvalds 	command_packet->flags = 0;
7111da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
7121da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
7131da177e4SLinus Torvalds 	if (command_que_value == 0) {
7141da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n");
7151da177e4SLinus Torvalds 		return 1;
7161da177e4SLinus Torvalds 	}
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 	/* Now setup the param */
7191da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
7201da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n");
7211da177e4SLinus Torvalds 		return 1;
7221da177e4SLinus Torvalds 	}
7231da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
7241da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
7251da177e4SLinus Torvalds 	param->table_id = 0x401; /* AEN table */
7261da177e4SLinus Torvalds 	param->parameter_id = 2; /* Unit code */
7271da177e4SLinus Torvalds 	param->parameter_size_bytes = 2;
7281da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
7291da177e4SLinus Torvalds 	if (param_value == 0) {
7301da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n");
7311da177e4SLinus Torvalds 		return 1;
7321da177e4SLinus Torvalds 	}
7331da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
7341da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	/* Now drain the controller's aen queue */
7371da177e4SLinus Torvalds 	do {
7381da177e4SLinus Torvalds 		/* Post command packet */
7391da177e4SLinus Torvalds 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 		/* Now poll for completion */
7421da177e4SLinus Torvalds 		if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
7431da177e4SLinus Torvalds 			response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
7441da177e4SLinus Torvalds 			request_id = TW_RESID_OUT(response_queue.response_id);
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 			if (request_id != 0) {
7471da177e4SLinus Torvalds 				/* Unexpected request id */
7481da177e4SLinus Torvalds 				printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n");
7491da177e4SLinus Torvalds 				return 1;
7501da177e4SLinus Torvalds 			}
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 			if (command_packet->status != 0) {
7531da177e4SLinus Torvalds 				if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
7541da177e4SLinus Torvalds 					/* Bad response */
7551da177e4SLinus Torvalds 					tw_decode_sense(tw_dev, request_id, 0);
7561da177e4SLinus Torvalds 					return 1;
7571da177e4SLinus Torvalds 				} else {
7581da177e4SLinus Torvalds 					/* We know this is a 3w-1x00, and doesn't support aen's */
7591da177e4SLinus Torvalds 					return 0;
7601da177e4SLinus Torvalds 				}
7611da177e4SLinus Torvalds 			}
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds 			/* Now check the aen */
7641da177e4SLinus Torvalds 			aen = *(unsigned short *)(param->data);
7651da177e4SLinus Torvalds 			aen_code = (aen & 0x0ff);
7661da177e4SLinus Torvalds 			queue = 0;
7671da177e4SLinus Torvalds 			switch (aen_code) {
7681da177e4SLinus Torvalds 				case TW_AEN_QUEUE_EMPTY:
7691da177e4SLinus Torvalds 					dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
7701da177e4SLinus Torvalds 					if (first_reset != 1) {
7711da177e4SLinus Torvalds 						return 1;
7721da177e4SLinus Torvalds 					} else {
7731da177e4SLinus Torvalds 						finished = 1;
7741da177e4SLinus Torvalds 					}
7751da177e4SLinus Torvalds 					break;
7761da177e4SLinus Torvalds 				case TW_AEN_SOFT_RESET:
7771da177e4SLinus Torvalds 					if (first_reset == 0) {
7781da177e4SLinus Torvalds 						first_reset = 1;
7791da177e4SLinus Torvalds 					} else {
7801da177e4SLinus Torvalds 						printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
7811da177e4SLinus Torvalds 						tw_dev->aen_count++;
7821da177e4SLinus Torvalds 						queue = 1;
7831da177e4SLinus Torvalds 					}
7841da177e4SLinus Torvalds 					break;
7851da177e4SLinus Torvalds 				default:
7861da177e4SLinus Torvalds 					if (aen == 0x0ff) {
7871da177e4SLinus Torvalds 						printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
7881da177e4SLinus Torvalds 					} else {
7896391a113STobias Klauser 						table_max = ARRAY_SIZE(tw_aen_string);
7901da177e4SLinus Torvalds 						if ((aen & 0x0ff) < table_max) {
7911da177e4SLinus Torvalds 							if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
7921da177e4SLinus Torvalds 								printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
7931da177e4SLinus Torvalds 							} else {
7941da177e4SLinus Torvalds 								printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
7951da177e4SLinus Torvalds 							}
7961da177e4SLinus Torvalds 						} else
7971da177e4SLinus Torvalds 							printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
7981da177e4SLinus Torvalds 					}
7991da177e4SLinus Torvalds 					tw_dev->aen_count++;
8001da177e4SLinus Torvalds 					queue = 1;
8011da177e4SLinus Torvalds 			}
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds 			/* Now put the aen on the aen_queue */
8041da177e4SLinus Torvalds 			if (queue == 1) {
8051da177e4SLinus Torvalds 				tw_dev->aen_queue[tw_dev->aen_tail] = aen;
8061da177e4SLinus Torvalds 				if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
8071da177e4SLinus Torvalds 					tw_dev->aen_tail = TW_Q_START;
8081da177e4SLinus Torvalds 				} else {
8091da177e4SLinus Torvalds 					tw_dev->aen_tail = tw_dev->aen_tail + 1;
8101da177e4SLinus Torvalds 				}
8111da177e4SLinus Torvalds 				if (tw_dev->aen_head == tw_dev->aen_tail) {
8121da177e4SLinus Torvalds 					if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
8131da177e4SLinus Torvalds 						tw_dev->aen_head = TW_Q_START;
8141da177e4SLinus Torvalds 					} else {
8151da177e4SLinus Torvalds 						tw_dev->aen_head = tw_dev->aen_head + 1;
8161da177e4SLinus Torvalds 					}
8171da177e4SLinus Torvalds 				}
8181da177e4SLinus Torvalds 			}
8191da177e4SLinus Torvalds 			found = 1;
8201da177e4SLinus Torvalds 		}
8211da177e4SLinus Torvalds 		if (found == 0) {
8221da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n");
8231da177e4SLinus Torvalds 			return 1;
8241da177e4SLinus Torvalds 		}
8251da177e4SLinus Torvalds 	} while (finished == 0);
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	return 0;
8281da177e4SLinus Torvalds } /* End tw_aen_drain_queue() */
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds /* This function will allocate memory */
tw_allocate_memory(TW_Device_Extension * tw_dev,int size,int which)8311da177e4SLinus Torvalds static int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
8321da177e4SLinus Torvalds {
8331da177e4SLinus Torvalds 	int i;
8341da177e4SLinus Torvalds 	dma_addr_t dma_handle;
8351da177e4SLinus Torvalds 	unsigned long *cpu_addr = NULL;
8361da177e4SLinus Torvalds 
8371da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
8381da177e4SLinus Torvalds 
839bd6cf46bSChristoph Hellwig 	cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
840bd6cf46bSChristoph Hellwig 			size * TW_Q_LENGTH, &dma_handle, GFP_KERNEL);
8411da177e4SLinus Torvalds 	if (cpu_addr == NULL) {
842bd6cf46bSChristoph Hellwig 		printk(KERN_WARNING "3w-xxxx: dma_alloc_coherent() failed.\n");
8431da177e4SLinus Torvalds 		return 1;
8441da177e4SLinus Torvalds 	}
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 	if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
8471da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
848bd6cf46bSChristoph Hellwig 		dma_free_coherent(&tw_dev->tw_pci_dev->dev, size * TW_Q_LENGTH,
849bd6cf46bSChristoph Hellwig 				cpu_addr, dma_handle);
8501da177e4SLinus Torvalds 		return 1;
8511da177e4SLinus Torvalds 	}
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	memset(cpu_addr, 0, size*TW_Q_LENGTH);
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds 	for (i=0;i<TW_Q_LENGTH;i++) {
8561da177e4SLinus Torvalds 		switch(which) {
8571da177e4SLinus Torvalds 		case 0:
8581da177e4SLinus Torvalds 			tw_dev->command_packet_physical_address[i] = dma_handle+(i*size);
8591da177e4SLinus Torvalds 			tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
8601da177e4SLinus Torvalds 			break;
8611da177e4SLinus Torvalds 		case 1:
8621da177e4SLinus Torvalds 			tw_dev->alignment_physical_address[i] = dma_handle+(i*size);
8631da177e4SLinus Torvalds 			tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
8641da177e4SLinus Torvalds 			break;
8651da177e4SLinus Torvalds 		default:
8661da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
8671da177e4SLinus Torvalds 			return 1;
8681da177e4SLinus Torvalds 		}
8691da177e4SLinus Torvalds 	}
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	return 0;
8721da177e4SLinus Torvalds } /* End tw_allocate_memory() */
8731da177e4SLinus Torvalds 
8741da177e4SLinus Torvalds /* This function handles ioctl for the character device */
tw_chrdev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)875f4927c45SArnd Bergmann static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
8761da177e4SLinus Torvalds {
8771da177e4SLinus Torvalds 	int request_id;
8781da177e4SLinus Torvalds 	dma_addr_t dma_handle;
8791da177e4SLinus Torvalds 	unsigned short tw_aen_code;
8801da177e4SLinus Torvalds 	unsigned long flags;
8811da177e4SLinus Torvalds 	unsigned int data_buffer_length = 0;
8821da177e4SLinus Torvalds 	unsigned long data_buffer_length_adjusted = 0;
883496ad9aaSAl Viro 	struct inode *inode = file_inode(file);
8841da177e4SLinus Torvalds 	unsigned long *cpu_addr;
8851da177e4SLinus Torvalds 	long timeout;
8861da177e4SLinus Torvalds 	TW_New_Ioctl *tw_ioctl;
8871da177e4SLinus Torvalds 	TW_Passthru *passthru;
8881da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = tw_device_extension_list[iminor(inode)];
8891da177e4SLinus Torvalds 	int retval = -EFAULT;
8901da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
8931da177e4SLinus Torvalds 
894c45d15d2SArnd Bergmann 	mutex_lock(&tw_mutex);
8951da177e4SLinus Torvalds 	/* Only let one of these through at a time */
896f4927c45SArnd Bergmann 	if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
897c45d15d2SArnd Bergmann 		mutex_unlock(&tw_mutex);
8981da177e4SLinus Torvalds 		return -EINTR;
899f4927c45SArnd Bergmann 	}
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	/* First copy down the buffer length */
9021da177e4SLinus Torvalds 	if (copy_from_user(&data_buffer_length, argp, sizeof(unsigned int)))
9031da177e4SLinus Torvalds 		goto out;
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	/* Check size */
9061da177e4SLinus Torvalds 	if (data_buffer_length > TW_MAX_IOCTL_SECTORS * 512) {
9071da177e4SLinus Torvalds 		retval = -EINVAL;
9081da177e4SLinus Torvalds 		goto out;
9091da177e4SLinus Torvalds 	}
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds 	/* Hardware can only do multiple of 512 byte transfers */
9121da177e4SLinus Torvalds 	data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	/* Now allocate ioctl buf memory */
9150fb9125eSGustavo A. R. Silva 	cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_New_Ioctl), &dma_handle, GFP_KERNEL);
9161da177e4SLinus Torvalds 	if (cpu_addr == NULL) {
9171da177e4SLinus Torvalds 		retval = -ENOMEM;
9181da177e4SLinus Torvalds 		goto out;
9191da177e4SLinus Torvalds 	}
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	tw_ioctl = (TW_New_Ioctl *)cpu_addr;
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	/* Now copy down the entire ioctl */
9240fb9125eSGustavo A. R. Silva 	if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl)))
9251da177e4SLinus Torvalds 		goto out2;
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	/* See which ioctl we are doing */
9301da177e4SLinus Torvalds 	switch (cmd) {
9311da177e4SLinus Torvalds 		case TW_OP_NOP:
9321da177e4SLinus Torvalds 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n");
9331da177e4SLinus Torvalds 			break;
9341da177e4SLinus Torvalds 		case TW_OP_AEN_LISTEN:
9351da177e4SLinus Torvalds 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
9361da177e4SLinus Torvalds 			memset(tw_ioctl->data_buffer, 0, data_buffer_length);
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
9391da177e4SLinus Torvalds 			if (tw_dev->aen_head == tw_dev->aen_tail) {
9401da177e4SLinus Torvalds 				tw_aen_code = TW_AEN_QUEUE_EMPTY;
9411da177e4SLinus Torvalds 			} else {
9421da177e4SLinus Torvalds 				tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
9431da177e4SLinus Torvalds 				if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
9441da177e4SLinus Torvalds 					tw_dev->aen_head = TW_Q_START;
9451da177e4SLinus Torvalds 				} else {
9461da177e4SLinus Torvalds 					tw_dev->aen_head = tw_dev->aen_head + 1;
9471da177e4SLinus Torvalds 				}
9481da177e4SLinus Torvalds 			}
9491da177e4SLinus Torvalds 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
9501da177e4SLinus Torvalds 			memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
9511da177e4SLinus Torvalds 			break;
9521da177e4SLinus Torvalds 		case TW_CMD_PACKET_WITH_DATA:
9531da177e4SLinus Torvalds 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
9541da177e4SLinus Torvalds 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 			tw_state_request_start(tw_dev, &request_id);
9571da177e4SLinus Torvalds 
9581da177e4SLinus Torvalds 			/* Flag internal command */
9591da177e4SLinus Torvalds 			tw_dev->srb[request_id] = NULL;
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 			/* Flag chrdev ioctl */
9621da177e4SLinus Torvalds 			tw_dev->chrdev_request_id = request_id;
9631da177e4SLinus Torvalds 
9641da177e4SLinus Torvalds 			tw_ioctl->firmware_command.request_id = request_id;
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 			/* Load the sg list */
9671da177e4SLinus Torvalds 			switch (TW_SGL_OUT(tw_ioctl->firmware_command.opcode__sgloffset)) {
9681da177e4SLinus Torvalds 			case 2:
9690fb9125eSGustavo A. R. Silva 				tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl);
9701da177e4SLinus Torvalds 				tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
9711da177e4SLinus Torvalds 				break;
9721da177e4SLinus Torvalds 			case 3:
9730fb9125eSGustavo A. R. Silva 				tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl);
9741da177e4SLinus Torvalds 				tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
9751da177e4SLinus Torvalds 				break;
9761da177e4SLinus Torvalds 			case 5:
9770fb9125eSGustavo A. R. Silva 				passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl);
9781da177e4SLinus Torvalds 				passthru->sg_list[0].length = data_buffer_length_adjusted;
9791da177e4SLinus Torvalds 				break;
9801da177e4SLinus Torvalds 			}
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 			memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 			/* Now post the command packet to the controller */
9851da177e4SLinus Torvalds 			tw_post_command_packet(tw_dev, request_id);
9861da177e4SLinus Torvalds 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds 			timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 			/* Now wait for the command to complete */
9911da177e4SLinus Torvalds 			timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 			/* We timed out, and didn't get an interrupt */
9941da177e4SLinus Torvalds 			if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
9951da177e4SLinus Torvalds 				/* Now we need to reset the board */
9961da177e4SLinus Torvalds 				printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
9971da177e4SLinus Torvalds 				retval = -EIO;
9984fe48187Sadam radford 				if (tw_reset_device_extension(tw_dev)) {
9991da177e4SLinus Torvalds 					printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
10001da177e4SLinus Torvalds 				}
10011da177e4SLinus Torvalds 				goto out2;
10021da177e4SLinus Torvalds 			}
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 			/* Now copy in the command packet response */
10051da177e4SLinus Torvalds 			memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 			/* Now complete the io */
10081da177e4SLinus Torvalds 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
10091da177e4SLinus Torvalds 			tw_dev->posted_request_count--;
10101da177e4SLinus Torvalds 			tw_dev->state[request_id] = TW_S_COMPLETED;
10111da177e4SLinus Torvalds 			tw_state_request_finish(tw_dev, request_id);
10121da177e4SLinus Torvalds 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
10131da177e4SLinus Torvalds 			break;
10141da177e4SLinus Torvalds 		default:
10151da177e4SLinus Torvalds 			retval = -ENOTTY;
10161da177e4SLinus Torvalds 			goto out2;
10171da177e4SLinus Torvalds 	}
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 	/* Now copy the response to userspace */
10200fb9125eSGustavo A. R. Silva 	if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length))
10211da177e4SLinus Torvalds 		goto out2;
10221da177e4SLinus Torvalds 	retval = 0;
10231da177e4SLinus Torvalds out2:
10241da177e4SLinus Torvalds 	/* Now free ioctl buf memory */
10250fb9125eSGustavo A. R. Silva 	dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_New_Ioctl), cpu_addr, dma_handle);
10261da177e4SLinus Torvalds out:
1027a12e25bdSJes Sorensen 	mutex_unlock(&tw_dev->ioctl_lock);
1028c45d15d2SArnd Bergmann 	mutex_unlock(&tw_mutex);
10291da177e4SLinus Torvalds 	return retval;
10301da177e4SLinus Torvalds } /* End tw_chrdev_ioctl() */
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds /* This function handles open for the character device */
1033f2b9857eSJonathan Corbet /* NOTE that this function races with remove. */
tw_chrdev_open(struct inode * inode,struct file * file)10341da177e4SLinus Torvalds static int tw_chrdev_open(struct inode *inode, struct file *file)
10351da177e4SLinus Torvalds {
10361da177e4SLinus Torvalds 	unsigned int minor_number;
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
10391da177e4SLinus Torvalds 
10409899e4d3SWenwen Wang 	if (!capable(CAP_SYS_ADMIN))
10419899e4d3SWenwen Wang 		return -EACCES;
10429899e4d3SWenwen Wang 
10431da177e4SLinus Torvalds 	minor_number = iminor(inode);
10441da177e4SLinus Torvalds 	if (minor_number >= tw_device_extension_count)
10451da177e4SLinus Torvalds 		return -ENODEV;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	return 0;
10481da177e4SLinus Torvalds } /* End tw_chrdev_open() */
10491da177e4SLinus Torvalds 
10501da177e4SLinus Torvalds /* File operations struct for character device */
105100977a59SArjan van de Ven static const struct file_operations tw_fops = {
10521da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
1053f4927c45SArnd Bergmann 	.unlocked_ioctl	= tw_chrdev_ioctl,
10541832f2d8SArnd Bergmann 	.compat_ioctl   = compat_ptr_ioctl,
10551da177e4SLinus Torvalds 	.open		= tw_chrdev_open,
10566038f373SArnd Bergmann 	.release	= NULL,
10576038f373SArnd Bergmann 	.llseek		= noop_llseek,
10581da177e4SLinus Torvalds };
10591da177e4SLinus Torvalds 
10601da177e4SLinus Torvalds /* This function will free up device extension resources */
tw_free_device_extension(TW_Device_Extension * tw_dev)10611da177e4SLinus Torvalds static void tw_free_device_extension(TW_Device_Extension *tw_dev)
10621da177e4SLinus Torvalds {
10631da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
10641da177e4SLinus Torvalds 
10651da177e4SLinus Torvalds 	/* Free command packet and generic buffer memory */
10661da177e4SLinus Torvalds 	if (tw_dev->command_packet_virtual_address[0])
1067bd6cf46bSChristoph Hellwig 		dma_free_coherent(&tw_dev->tw_pci_dev->dev,
1068bd6cf46bSChristoph Hellwig 				sizeof(TW_Command) * TW_Q_LENGTH,
1069bd6cf46bSChristoph Hellwig 				tw_dev->command_packet_virtual_address[0],
1070bd6cf46bSChristoph Hellwig 				tw_dev->command_packet_physical_address[0]);
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[0])
1073bd6cf46bSChristoph Hellwig 		dma_free_coherent(&tw_dev->tw_pci_dev->dev,
1074bd6cf46bSChristoph Hellwig 				sizeof(TW_Sector) * TW_Q_LENGTH,
1075bd6cf46bSChristoph Hellwig 				tw_dev->alignment_virtual_address[0],
1076bd6cf46bSChristoph Hellwig 				tw_dev->alignment_physical_address[0]);
10771da177e4SLinus Torvalds } /* End tw_free_device_extension() */
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds /* This function will send an initconnection command to controller */
tw_initconnection(TW_Device_Extension * tw_dev,int message_credits)10801da177e4SLinus Torvalds static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
10811da177e4SLinus Torvalds {
10821da177e4SLinus Torvalds 	unsigned long command_que_value;
10831da177e4SLinus Torvalds 	TW_Command  *command_packet;
10841da177e4SLinus Torvalds 	TW_Response_Queue response_queue;
10851da177e4SLinus Torvalds 	int request_id = 0;
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n");
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	/* Initialize InitConnection command packet */
10901da177e4SLinus Torvalds 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
10911da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet virtual address.\n");
10921da177e4SLinus Torvalds 		return 1;
10931da177e4SLinus Torvalds 	}
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
10961da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
10971da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_INIT_CONNECTION);
10981da177e4SLinus Torvalds 	command_packet->size = TW_INIT_COMMAND_PACKET_SIZE;
10991da177e4SLinus Torvalds 	command_packet->request_id = request_id;
11001da177e4SLinus Torvalds 	command_packet->status = 0x0;
11011da177e4SLinus Torvalds 	command_packet->flags = 0x0;
11021da177e4SLinus Torvalds 	command_packet->byte6.message_credits = message_credits;
11031da177e4SLinus Torvalds 	command_packet->byte8.init_connection.response_queue_pointer = 0x0;
11041da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
11051da177e4SLinus Torvalds 
11061da177e4SLinus Torvalds 	if (command_que_value == 0) {
11071da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n");
11081da177e4SLinus Torvalds 		return 1;
11091da177e4SLinus Torvalds 	}
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	/* Send command packet to the board */
11121da177e4SLinus Torvalds 	outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
11131da177e4SLinus Torvalds 
11141da177e4SLinus Torvalds 	/* Poll for completion */
11151da177e4SLinus Torvalds 	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
11161da177e4SLinus Torvalds 		response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
11171da177e4SLinus Torvalds 		request_id = TW_RESID_OUT(response_queue.response_id);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 		if (request_id != 0) {
11201da177e4SLinus Torvalds 			/* unexpected request id */
11211da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected request id.\n");
11221da177e4SLinus Torvalds 			return 1;
11231da177e4SLinus Torvalds 		}
11241da177e4SLinus Torvalds 		if (command_packet->status != 0) {
11251da177e4SLinus Torvalds 			/* bad response */
11261da177e4SLinus Torvalds 			tw_decode_sense(tw_dev, request_id, 0);
11271da177e4SLinus Torvalds 			return 1;
11281da177e4SLinus Torvalds 		}
11291da177e4SLinus Torvalds 	}
11301da177e4SLinus Torvalds 	return 0;
11311da177e4SLinus Torvalds } /* End tw_initconnection() */
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds /* Set a value in the features table */
tw_setfeature(TW_Device_Extension * tw_dev,int parm,int param_size,unsigned char * val)11341da177e4SLinus Torvalds static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
11351da177e4SLinus Torvalds 			 unsigned char *val)
11361da177e4SLinus Torvalds {
11371da177e4SLinus Torvalds 	TW_Param *param;
11381da177e4SLinus Torvalds 	TW_Command  *command_packet;
11391da177e4SLinus Torvalds 	TW_Response_Queue response_queue;
11401da177e4SLinus Torvalds 	int request_id = 0;
11411da177e4SLinus Torvalds 	unsigned long command_que_value;
11421da177e4SLinus Torvalds 	unsigned long param_value;
11431da177e4SLinus Torvalds 
11441da177e4SLinus Torvalds 	/* Initialize SetParam command packet */
11451da177e4SLinus Torvalds 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
11461da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
11471da177e4SLinus Torvalds 		return 1;
11481da177e4SLinus Torvalds 	}
11491da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
11501da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
11511da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
11541da177e4SLinus Torvalds 	param->table_id = 0x404;  /* Features table */
11551da177e4SLinus Torvalds 	param->parameter_id = parm;
11561da177e4SLinus Torvalds 	param->parameter_size_bytes = param_size;
11571da177e4SLinus Torvalds 	memcpy(param->data, val, param_size);
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
11601da177e4SLinus Torvalds 	if (param_value == 0) {
11611da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad alignment physical address.\n");
11621da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_COMPLETED;
11631da177e4SLinus Torvalds 		tw_state_request_finish(tw_dev, request_id);
11641da177e4SLinus Torvalds 		tw_dev->srb[request_id]->result = (DID_OK << 16);
11659dd9b96cSBart Van Assche 		scsi_done(tw_dev->srb[request_id]);
11661da177e4SLinus Torvalds 	}
11671da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
11681da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	command_packet->size = 4;
11711da177e4SLinus Torvalds 	command_packet->request_id = request_id;
11721da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
11751da177e4SLinus Torvalds 	if (command_que_value == 0) {
11761da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
11771da177e4SLinus Torvalds 		return 1;
11781da177e4SLinus Torvalds 	}
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 	/* Send command packet to the board */
11811da177e4SLinus Torvalds 	outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 	/* Poll for completion */
11841da177e4SLinus Torvalds 	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
11851da177e4SLinus Torvalds 		response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
11861da177e4SLinus Torvalds 		request_id = TW_RESID_OUT(response_queue.response_id);
11871da177e4SLinus Torvalds 
11881da177e4SLinus Torvalds 		if (request_id != 0) {
11891da177e4SLinus Torvalds 			/* unexpected request id */
11901da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n");
11911da177e4SLinus Torvalds 			return 1;
11921da177e4SLinus Torvalds 		}
11931da177e4SLinus Torvalds 		if (command_packet->status != 0) {
11941da177e4SLinus Torvalds 			/* bad response */
11951da177e4SLinus Torvalds 			tw_decode_sense(tw_dev, request_id, 0);
11961da177e4SLinus Torvalds 			return 1;
11971da177e4SLinus Torvalds 		}
11981da177e4SLinus Torvalds 	}
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds 	return 0;
12011da177e4SLinus Torvalds } /* End tw_setfeature() */
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds /* This function will reset a controller */
tw_reset_sequence(TW_Device_Extension * tw_dev)12041da177e4SLinus Torvalds static int tw_reset_sequence(TW_Device_Extension *tw_dev)
12051da177e4SLinus Torvalds {
12061da177e4SLinus Torvalds 	int error = 0;
12071da177e4SLinus Torvalds 	int tries = 0;
12081da177e4SLinus Torvalds 	unsigned char c = 1;
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds 	/* Reset the board */
12111da177e4SLinus Torvalds 	while (tries < TW_MAX_RESET_TRIES) {
12121da177e4SLinus Torvalds 		TW_SOFT_RESET(tw_dev);
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 		error = tw_aen_drain_queue(tw_dev);
12151da177e4SLinus Torvalds 		if (error) {
12161da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
12171da177e4SLinus Torvalds 			tries++;
12181da177e4SLinus Torvalds 			continue;
12191da177e4SLinus Torvalds 		}
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 		/* Check for controller errors */
12221da177e4SLinus Torvalds 		if (tw_check_errors(tw_dev)) {
12231da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors found, retrying.\n", tw_dev->host->host_no);
12241da177e4SLinus Torvalds 			tries++;
12251da177e4SLinus Torvalds 			continue;
12261da177e4SLinus Torvalds 		}
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds 		/* Now the controller is in a good state */
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds 
12321da177e4SLinus Torvalds 	if (tries >= TW_MAX_RESET_TRIES) {
12331da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors, card not responding, check all cabling.\n", tw_dev->host->host_no);
12341da177e4SLinus Torvalds 		return 1;
12351da177e4SLinus Torvalds 	}
12361da177e4SLinus Torvalds 
12371da177e4SLinus Torvalds 	error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
12381da177e4SLinus Torvalds 	if (error) {
12391da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi%d: Connection initialization failed.\n", tw_dev->host->host_no);
12401da177e4SLinus Torvalds 		return 1;
12411da177e4SLinus Torvalds 	}
12421da177e4SLinus Torvalds 
12431da177e4SLinus Torvalds 	error = tw_setfeature(tw_dev, 2, 1, &c);
12441da177e4SLinus Torvalds 	if (error) {
12451da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Unable to set features for card, probable old firmware or card.\n");
12461da177e4SLinus Torvalds 	}
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 	return 0;
12491da177e4SLinus Torvalds } /* End tw_reset_sequence() */
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds /* This function will initialize the fields of a device extension */
tw_initialize_device_extension(TW_Device_Extension * tw_dev)12521da177e4SLinus Torvalds static int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
12531da177e4SLinus Torvalds {
12541da177e4SLinus Torvalds 	int i, error=0;
12551da177e4SLinus Torvalds 
12561da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 	/* Initialize command packet buffers */
12591da177e4SLinus Torvalds 	error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0);
12601da177e4SLinus Torvalds 	if (error) {
12611da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n");
12621da177e4SLinus Torvalds 		return 1;
12631da177e4SLinus Torvalds 	}
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 	/* Initialize generic buffer */
12661da177e4SLinus Torvalds 	error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1);
12671da177e4SLinus Torvalds 	if (error) {
12681da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n");
12691da177e4SLinus Torvalds 		return 1;
12701da177e4SLinus Torvalds 	}
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	for (i=0;i<TW_Q_LENGTH;i++) {
12731da177e4SLinus Torvalds 		tw_dev->free_queue[i] = i;
12741da177e4SLinus Torvalds 		tw_dev->state[i] = TW_S_INITIAL;
12751da177e4SLinus Torvalds 	}
12761da177e4SLinus Torvalds 
12771da177e4SLinus Torvalds 	tw_dev->pending_head = TW_Q_START;
12781da177e4SLinus Torvalds 	tw_dev->pending_tail = TW_Q_START;
12791da177e4SLinus Torvalds 	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
12801da177e4SLinus Torvalds 
1281a12e25bdSJes Sorensen 	mutex_init(&tw_dev->ioctl_lock);
12821da177e4SLinus Torvalds 	init_waitqueue_head(&tw_dev->ioctl_wqueue);
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	return 0;
12851da177e4SLinus Torvalds } /* End tw_initialize_device_extension() */
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds /* This function will reset a device extension */
tw_reset_device_extension(TW_Device_Extension * tw_dev)12884fe48187Sadam radford static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
12891da177e4SLinus Torvalds {
12901da177e4SLinus Torvalds 	int i = 0;
12911da177e4SLinus Torvalds 	struct scsi_cmnd *srb;
12921da177e4SLinus Torvalds 	unsigned long flags = 0;
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_reset_device_extension()\n");
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	set_bit(TW_IN_RESET, &tw_dev->flags);
12971da177e4SLinus Torvalds 	TW_DISABLE_INTERRUPTS(tw_dev);
12981da177e4SLinus Torvalds 	TW_MASK_COMMAND_INTERRUPT(tw_dev);
12991da177e4SLinus Torvalds 	spin_lock_irqsave(tw_dev->host->host_lock, flags);
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 	/* Abort all requests that are in progress */
13021da177e4SLinus Torvalds 	for (i=0;i<TW_Q_LENGTH;i++) {
13031da177e4SLinus Torvalds 		if ((tw_dev->state[i] != TW_S_FINISHED) &&
13041da177e4SLinus Torvalds 		    (tw_dev->state[i] != TW_S_INITIAL) &&
13051da177e4SLinus Torvalds 		    (tw_dev->state[i] != TW_S_COMPLETED)) {
13061da177e4SLinus Torvalds 			srb = tw_dev->srb[i];
13071da177e4SLinus Torvalds 			if (srb != NULL) {
13081da177e4SLinus Torvalds 				srb->result = (DID_RESET << 16);
13099cd95546SChristoph Hellwig 				scsi_dma_unmap(srb);
13109dd9b96cSBart Van Assche 				scsi_done(srb);
13111da177e4SLinus Torvalds 			}
13121da177e4SLinus Torvalds 		}
13131da177e4SLinus Torvalds 	}
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	/* Reset queues and counts */
13161da177e4SLinus Torvalds 	for (i=0;i<TW_Q_LENGTH;i++) {
13171da177e4SLinus Torvalds 		tw_dev->free_queue[i] = i;
13181da177e4SLinus Torvalds 		tw_dev->state[i] = TW_S_INITIAL;
13191da177e4SLinus Torvalds 	}
13201da177e4SLinus Torvalds 	tw_dev->free_head = TW_Q_START;
13211da177e4SLinus Torvalds 	tw_dev->free_tail = TW_Q_START;
13221da177e4SLinus Torvalds 	tw_dev->posted_request_count = 0;
13231da177e4SLinus Torvalds 	tw_dev->pending_request_count = 0;
13241da177e4SLinus Torvalds 	tw_dev->pending_head = TW_Q_START;
13251da177e4SLinus Torvalds 	tw_dev->pending_tail = TW_Q_START;
13261da177e4SLinus Torvalds 	tw_dev->reset_print = 0;
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds 	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds 	if (tw_reset_sequence(tw_dev)) {
13311da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi%d: Reset sequence failed.\n", tw_dev->host->host_no);
13321da177e4SLinus Torvalds 		return 1;
13331da177e4SLinus Torvalds 	}
13341da177e4SLinus Torvalds 
13354fe48187Sadam radford 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
13361da177e4SLinus Torvalds 	clear_bit(TW_IN_RESET, &tw_dev->flags);
13371da177e4SLinus Torvalds 	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 	return 0;
13401da177e4SLinus Torvalds } /* End tw_reset_device_extension() */
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds /* This funciton returns unit geometry in cylinders/heads/sectors */
tw_scsi_biosparam(struct scsi_device * sdev,struct block_device * bdev,sector_t capacity,int geom[])13431da177e4SLinus Torvalds static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev,
13441da177e4SLinus Torvalds 			     sector_t capacity, int geom[])
13451da177e4SLinus Torvalds {
13461da177e4SLinus Torvalds 	int heads, sectors, cylinders;
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n");
13491da177e4SLinus Torvalds 
13501da177e4SLinus Torvalds 	heads = 64;
13511da177e4SLinus Torvalds 	sectors = 32;
13521da177e4SLinus Torvalds 	cylinders = sector_div(capacity, heads * sectors);
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds 	if (capacity >= 0x200000) {
13551da177e4SLinus Torvalds 		heads = 255;
13561da177e4SLinus Torvalds 		sectors = 63;
13571da177e4SLinus Torvalds 		cylinders = sector_div(capacity, heads * sectors);
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 
13601da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders);
13611da177e4SLinus Torvalds 	geom[0] = heads;
13621da177e4SLinus Torvalds 	geom[1] = sectors;
13631da177e4SLinus Torvalds 	geom[2] = cylinders;
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 	return 0;
13661da177e4SLinus Torvalds } /* End tw_scsi_biosparam() */
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds /* This is the new scsi eh reset function */
tw_scsi_eh_reset(struct scsi_cmnd * SCpnt)13691da177e4SLinus Torvalds static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt)
13701da177e4SLinus Torvalds {
13711da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev=NULL;
13721da177e4SLinus Torvalds 	int retval = FAILED;
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds 	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
13751da177e4SLinus Torvalds 
13761da177e4SLinus Torvalds 	tw_dev->num_resets++;
13771da177e4SLinus Torvalds 
1378017560fcSJeff Garzik 	sdev_printk(KERN_WARNING, SCpnt->device,
1379017560fcSJeff Garzik 		"WARNING: Command (0x%x) timed out, resetting card.\n",
1380017560fcSJeff Garzik 		SCpnt->cmnd[0]);
13811da177e4SLinus Torvalds 
13824fe48187Sadam radford 	/* Make sure we are not issuing an ioctl or resetting from ioctl */
13834fe48187Sadam radford 	mutex_lock(&tw_dev->ioctl_lock);
13844fe48187Sadam radford 
13851da177e4SLinus Torvalds 	/* Now reset the card and some of the device extension data */
13864fe48187Sadam radford 	if (tw_reset_device_extension(tw_dev)) {
13871da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi%d: Reset failed.\n", tw_dev->host->host_no);
13881da177e4SLinus Torvalds 		goto out;
13891da177e4SLinus Torvalds 	}
13901da177e4SLinus Torvalds 
13911da177e4SLinus Torvalds 	retval = SUCCESS;
13921da177e4SLinus Torvalds out:
13934fe48187Sadam radford 	mutex_unlock(&tw_dev->ioctl_lock);
13941da177e4SLinus Torvalds 	return retval;
13951da177e4SLinus Torvalds } /* End tw_scsi_eh_reset() */
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds /* This function handles scsi inquiry commands */
tw_scsiop_inquiry(TW_Device_Extension * tw_dev,int request_id)13981da177e4SLinus Torvalds static int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id)
13991da177e4SLinus Torvalds {
14001da177e4SLinus Torvalds 	TW_Param *param;
14011da177e4SLinus Torvalds 	TW_Command *command_packet;
14021da177e4SLinus Torvalds 	unsigned long command_que_value;
14031da177e4SLinus Torvalds 	unsigned long param_value;
14041da177e4SLinus Torvalds 
14051da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n");
14061da177e4SLinus Torvalds 
14071da177e4SLinus Torvalds 	/* Initialize command packet */
14081da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
14091da177e4SLinus Torvalds 	if (command_packet == NULL) {
14101da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet virtual address.\n");
14111da177e4SLinus Torvalds 		return 1;
14121da177e4SLinus Torvalds 	}
14131da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
14141da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
14151da177e4SLinus Torvalds 	command_packet->size = 4;
14161da177e4SLinus Torvalds 	command_packet->request_id = request_id;
14171da177e4SLinus Torvalds 	command_packet->status = 0;
14181da177e4SLinus Torvalds 	command_packet->flags = 0;
14191da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds 	/* Now setup the param */
14221da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
14231da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment virtual address.\n");
14241da177e4SLinus Torvalds 		return 1;
14251da177e4SLinus Torvalds 	}
14261da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
14271da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
14281da177e4SLinus Torvalds 	param->table_id = 3;	 /* unit summary table */
14291da177e4SLinus Torvalds 	param->parameter_id = 3; /* unitsstatus parameter */
14301da177e4SLinus Torvalds 	param->parameter_size_bytes = TW_MAX_UNITS;
14311da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
14321da177e4SLinus Torvalds 	if (param_value == 0) {
14331da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment physical address.\n");
14341da177e4SLinus Torvalds 		return 1;
14351da177e4SLinus Torvalds 	}
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
14381da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
14391da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
14401da177e4SLinus Torvalds 	if (command_que_value == 0) {
14411da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet physical address.\n");
14421da177e4SLinus Torvalds 		return 1;
14431da177e4SLinus Torvalds 	}
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 	/* Now try to post the command packet */
14461da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds 	return 0;
14491da177e4SLinus Torvalds } /* End tw_scsiop_inquiry() */
14501da177e4SLinus Torvalds 
tw_transfer_internal(TW_Device_Extension * tw_dev,int request_id,void * data,unsigned int len)1451c9d297c5SJames Bottomley static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id,
1452c9d297c5SJames Bottomley 				 void *data, unsigned int len)
1453c9d297c5SJames Bottomley {
14540723d4a8SFUJITA Tomonori 	scsi_sg_copy_from_buffer(tw_dev->srb[request_id], data, len);
1455c9d297c5SJames Bottomley }
1456c9d297c5SJames Bottomley 
14571da177e4SLinus Torvalds /* This function is called by the isr to complete an inquiry command */
tw_scsiop_inquiry_complete(TW_Device_Extension * tw_dev,int request_id)14581da177e4SLinus Torvalds static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
14591da177e4SLinus Torvalds {
14601da177e4SLinus Torvalds 	unsigned char *is_unit_present;
1461c9d297c5SJames Bottomley 	unsigned char request_buffer[36];
14621da177e4SLinus Torvalds 	TW_Param *param;
14631da177e4SLinus Torvalds 
14641da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
14651da177e4SLinus Torvalds 
1466c9d297c5SJames Bottomley 	memset(request_buffer, 0, sizeof(request_buffer));
14671da177e4SLinus Torvalds 	request_buffer[0] = TYPE_DISK; /* Peripheral device type */
14681da177e4SLinus Torvalds 	request_buffer[1] = 0;	       /* Device type modifier */
14691da177e4SLinus Torvalds 	request_buffer[2] = 0;	       /* No ansi/iso compliance */
14701da177e4SLinus Torvalds 	request_buffer[4] = 31;	       /* Additional length */
14711da177e4SLinus Torvalds 	memcpy(&request_buffer[8], "3ware   ", 8);	 /* Vendor ID */
14721da177e4SLinus Torvalds 	sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id);
14731da177e4SLinus Torvalds 	memcpy(&request_buffer[32], TW_DRIVER_VERSION, 3);
1474c9d297c5SJames Bottomley 	tw_transfer_internal(tw_dev, request_id, request_buffer,
1475c9d297c5SJames Bottomley 			     sizeof(request_buffer));
14761da177e4SLinus Torvalds 
14771da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
14781da177e4SLinus Torvalds 	if (param == NULL) {
14791da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Bad alignment virtual address.\n");
14801da177e4SLinus Torvalds 		return 1;
14811da177e4SLinus Torvalds 	}
14821da177e4SLinus Torvalds 	is_unit_present = &(param->data[0]);
14831da177e4SLinus Torvalds 
14841da177e4SLinus Torvalds 	if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
14851da177e4SLinus Torvalds 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
14861da177e4SLinus Torvalds 	} else {
14871da177e4SLinus Torvalds 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
14881da177e4SLinus Torvalds 		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
14891da177e4SLinus Torvalds 		return TW_ISR_DONT_RESULT;
14901da177e4SLinus Torvalds 	}
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 	return 0;
14931da177e4SLinus Torvalds } /* End tw_scsiop_inquiry_complete() */
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds /* This function handles scsi mode_sense commands */
tw_scsiop_mode_sense(TW_Device_Extension * tw_dev,int request_id)14961da177e4SLinus Torvalds static int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
14971da177e4SLinus Torvalds {
14981da177e4SLinus Torvalds 	TW_Param *param;
14991da177e4SLinus Torvalds 	TW_Command *command_packet;
15001da177e4SLinus Torvalds 	unsigned long command_que_value;
15011da177e4SLinus Torvalds 	unsigned long param_value;
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense()\n");
15041da177e4SLinus Torvalds 
15051da177e4SLinus Torvalds 	/* Only page control = 0, page code = 0x8 (cache page) supported */
15061da177e4SLinus Torvalds 	if (tw_dev->srb[request_id]->cmnd[2] != 0x8) {
15071da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_COMPLETED;
15081da177e4SLinus Torvalds 		tw_state_request_finish(tw_dev, request_id);
15091da177e4SLinus Torvalds 		tw_dev->srb[request_id]->result = (DID_OK << 16);
15109dd9b96cSBart Van Assche 		scsi_done(tw_dev->srb[request_id]);
15111da177e4SLinus Torvalds 		return 0;
15121da177e4SLinus Torvalds 	}
15131da177e4SLinus Torvalds 
15141da177e4SLinus Torvalds 	/* Now read firmware cache setting for this unit */
15151da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
15161da177e4SLinus Torvalds 	if (command_packet == NULL) {
15171da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet virtual address.\n");
15181da177e4SLinus Torvalds 		return 1;
15191da177e4SLinus Torvalds 	}
15201da177e4SLinus Torvalds 
15211da177e4SLinus Torvalds 	/* Setup the command packet */
15221da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
15231da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
15241da177e4SLinus Torvalds 	command_packet->size = 4;
15251da177e4SLinus Torvalds 	command_packet->request_id = request_id;
15261da177e4SLinus Torvalds 	command_packet->status = 0;
15271da177e4SLinus Torvalds 	command_packet->flags = 0;
15281da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
15291da177e4SLinus Torvalds 
15301da177e4SLinus Torvalds 	/* Setup the param */
15311da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
15321da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment virtual address.\n");
15331da177e4SLinus Torvalds 		return 1;
15341da177e4SLinus Torvalds 	}
15351da177e4SLinus Torvalds 
15361da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
15371da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
15381da177e4SLinus Torvalds 	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + tw_dev->srb[request_id]->device->id;
15391da177e4SLinus Torvalds 	param->parameter_id = 7; /* unit flags */
15401da177e4SLinus Torvalds 	param->parameter_size_bytes = 1;
15411da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
15421da177e4SLinus Torvalds 	if (param_value == 0) {
15431da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment physical address.\n");
15441da177e4SLinus Torvalds 		return 1;
15451da177e4SLinus Torvalds 	}
15461da177e4SLinus Torvalds 
15471da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
15481da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
15491da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
15501da177e4SLinus Torvalds 	if (command_que_value == 0) {
15511da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet physical address.\n");
15521da177e4SLinus Torvalds 		return 1;
15531da177e4SLinus Torvalds 	}
15541da177e4SLinus Torvalds 
15551da177e4SLinus Torvalds 	/* Now try to post the command packet */
15561da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
15571da177e4SLinus Torvalds 
15581da177e4SLinus Torvalds 	return 0;
15591da177e4SLinus Torvalds } /* End tw_scsiop_mode_sense() */
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds /* This function is called by the isr to complete a mode sense command */
tw_scsiop_mode_sense_complete(TW_Device_Extension * tw_dev,int request_id)15621da177e4SLinus Torvalds static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int request_id)
15631da177e4SLinus Torvalds {
15641da177e4SLinus Torvalds 	TW_Param *param;
15651da177e4SLinus Torvalds 	unsigned char *flags;
1566c9d297c5SJames Bottomley 	unsigned char request_buffer[8];
15671da177e4SLinus Torvalds 
15681da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n");
15691da177e4SLinus Torvalds 
15701da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
15711da177e4SLinus Torvalds 	if (param == NULL) {
15721da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense_complete(): Bad alignment virtual address.\n");
15731da177e4SLinus Torvalds 		return 1;
15741da177e4SLinus Torvalds 	}
15751da177e4SLinus Torvalds 	flags = (char *)&(param->data[0]);
1576c9d297c5SJames Bottomley 	memset(request_buffer, 0, sizeof(request_buffer));
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds 	request_buffer[0] = 0xf;	/* mode data length */
15791da177e4SLinus Torvalds 	request_buffer[1] = 0;		/* default medium type */
15801da177e4SLinus Torvalds 	request_buffer[2] = 0x10;	/* dpo/fua support on */
15811da177e4SLinus Torvalds 	request_buffer[3] = 0;		/* no block descriptors */
15821da177e4SLinus Torvalds 	request_buffer[4] = 0x8;	/* caching page */
15831da177e4SLinus Torvalds 	request_buffer[5] = 0xa;	/* page length */
15841da177e4SLinus Torvalds 	if (*flags & 0x1)
15854fe48187Sadam radford 		request_buffer[6] = 0x5;	/* WCE on, RCD on */
15861da177e4SLinus Torvalds 	else
15874fe48187Sadam radford 		request_buffer[6] = 0x1;	/* WCE off, RCD on */
1588c9d297c5SJames Bottomley 	tw_transfer_internal(tw_dev, request_id, request_buffer,
1589c9d297c5SJames Bottomley 			     sizeof(request_buffer));
15901da177e4SLinus Torvalds 
15911da177e4SLinus Torvalds 	return 0;
15921da177e4SLinus Torvalds } /* End tw_scsiop_mode_sense_complete() */
15931da177e4SLinus Torvalds 
15941da177e4SLinus Torvalds /* This function handles scsi read_capacity commands */
tw_scsiop_read_capacity(TW_Device_Extension * tw_dev,int request_id)15951da177e4SLinus Torvalds static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
15961da177e4SLinus Torvalds {
15971da177e4SLinus Torvalds 	TW_Param *param;
15981da177e4SLinus Torvalds 	TW_Command *command_packet;
15991da177e4SLinus Torvalds 	unsigned long command_que_value;
16001da177e4SLinus Torvalds 	unsigned long param_value;
16011da177e4SLinus Torvalds 
16021da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n");
16031da177e4SLinus Torvalds 
16041da177e4SLinus Torvalds 	/* Initialize command packet */
16051da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
16061da177e4SLinus Torvalds 
16071da177e4SLinus Torvalds 	if (command_packet == NULL) {
16081da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet virtual address.\n");
16091da177e4SLinus Torvalds 		return 1;
16101da177e4SLinus Torvalds 	}
16111da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
16121da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
16131da177e4SLinus Torvalds 	command_packet->size = 4;
16141da177e4SLinus Torvalds 	command_packet->request_id = request_id;
16151da177e4SLinus Torvalds 	command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
16161da177e4SLinus Torvalds 	command_packet->status = 0;
16171da177e4SLinus Torvalds 	command_packet->flags = 0;
16181da177e4SLinus Torvalds 	command_packet->byte6.block_count = 1;
16191da177e4SLinus Torvalds 
16201da177e4SLinus Torvalds 	/* Now setup the param */
16211da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
16221da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment virtual address.\n");
16231da177e4SLinus Torvalds 		return 1;
16241da177e4SLinus Torvalds 	}
16251da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
16261da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
16271da177e4SLinus Torvalds 	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE +
16281da177e4SLinus Torvalds 		tw_dev->srb[request_id]->device->id;
16291da177e4SLinus Torvalds 	param->parameter_id = 4;	/* unitcapacity parameter */
16301da177e4SLinus Torvalds 	param->parameter_size_bytes = 4;
16311da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
16321da177e4SLinus Torvalds 	if (param_value == 0) {
16331da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n");
16341da177e4SLinus Torvalds 		return 1;
16351da177e4SLinus Torvalds 	}
16361da177e4SLinus Torvalds 
16371da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
16381da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
16391da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
16401da177e4SLinus Torvalds 	if (command_que_value == 0) {
16411da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet physical address.\n");
16421da177e4SLinus Torvalds 		return 1;
16431da177e4SLinus Torvalds 	}
16441da177e4SLinus Torvalds 
16451da177e4SLinus Torvalds 	/* Now try to post the command to the board */
16461da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
16471da177e4SLinus Torvalds 
16481da177e4SLinus Torvalds 	return 0;
16491da177e4SLinus Torvalds } /* End tw_scsiop_read_capacity() */
16501da177e4SLinus Torvalds 
16511da177e4SLinus Torvalds /* This function is called by the isr to complete a readcapacity command */
tw_scsiop_read_capacity_complete(TW_Device_Extension * tw_dev,int request_id)16521da177e4SLinus Torvalds static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id)
16531da177e4SLinus Torvalds {
16541da177e4SLinus Torvalds 	unsigned char *param_data;
16551da177e4SLinus Torvalds 	u32 capacity;
1656c9d297c5SJames Bottomley 	char buff[8];
16571da177e4SLinus Torvalds 	TW_Param *param;
16581da177e4SLinus Torvalds 
16591da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n");
16601da177e4SLinus Torvalds 
1661c9d297c5SJames Bottomley 	memset(buff, 0, sizeof(buff));
16621da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
16631da177e4SLinus Torvalds 	if (param == NULL) {
16641da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n");
16651da177e4SLinus Torvalds 		return 1;
16661da177e4SLinus Torvalds 	}
16671da177e4SLinus Torvalds 	param_data = &(param->data[0]);
16681da177e4SLinus Torvalds 
16691da177e4SLinus Torvalds 	capacity = (param_data[3] << 24) | (param_data[2] << 16) |
16701da177e4SLinus Torvalds 		   (param_data[1] << 8) | param_data[0];
16711da177e4SLinus Torvalds 
16721da177e4SLinus Torvalds 	/* Subtract one sector to fix get last sector ioctl */
16731da177e4SLinus Torvalds 	capacity -= 1;
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
16761da177e4SLinus Torvalds 
16771da177e4SLinus Torvalds 	/* Number of LBA's */
16781da177e4SLinus Torvalds 	buff[0] = (capacity >> 24);
16791da177e4SLinus Torvalds 	buff[1] = (capacity >> 16) & 0xff;
16801da177e4SLinus Torvalds 	buff[2] = (capacity >> 8) & 0xff;
16811da177e4SLinus Torvalds 	buff[3] = capacity & 0xff;
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds 	/* Block size in bytes (512) */
16841da177e4SLinus Torvalds 	buff[4] = (TW_BLOCK_SIZE >> 24);
16851da177e4SLinus Torvalds 	buff[5] = (TW_BLOCK_SIZE >> 16) & 0xff;
16861da177e4SLinus Torvalds 	buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff;
16871da177e4SLinus Torvalds 	buff[7] = TW_BLOCK_SIZE & 0xff;
16881da177e4SLinus Torvalds 
1689c9d297c5SJames Bottomley 	tw_transfer_internal(tw_dev, request_id, buff, sizeof(buff));
1690c9d297c5SJames Bottomley 
16911da177e4SLinus Torvalds 	return 0;
16921da177e4SLinus Torvalds } /* End tw_scsiop_read_capacity_complete() */
16931da177e4SLinus Torvalds 
16941da177e4SLinus Torvalds /* This function handles scsi read or write commands */
tw_scsiop_read_write(TW_Device_Extension * tw_dev,int request_id)16951da177e4SLinus Torvalds static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
16961da177e4SLinus Torvalds {
16971da177e4SLinus Torvalds 	TW_Command *command_packet;
16981da177e4SLinus Torvalds 	unsigned long command_que_value;
16996edae708SFUJITA Tomonori 	u32 lba = 0x0, num_sectors = 0x0;
17001da177e4SLinus Torvalds 	int i, use_sg;
17011da177e4SLinus Torvalds 	struct scsi_cmnd *srb;
17026edae708SFUJITA Tomonori 	struct scatterlist *sglist, *sg;
17031da177e4SLinus Torvalds 
17041da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n");
17051da177e4SLinus Torvalds 
17066edae708SFUJITA Tomonori 	srb = tw_dev->srb[request_id];
17076edae708SFUJITA Tomonori 
17086edae708SFUJITA Tomonori 	sglist = scsi_sglist(srb);
17096edae708SFUJITA Tomonori 	if (!sglist) {
17101da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n");
17111da177e4SLinus Torvalds 		return 1;
17121da177e4SLinus Torvalds 	}
17131da177e4SLinus Torvalds 
17141da177e4SLinus Torvalds 	/* Initialize command packet */
17151da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
17161da177e4SLinus Torvalds 	if (command_packet == NULL) {
17171da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): Bad command packet virtual address.\n");
17181da177e4SLinus Torvalds 		return 1;
17191da177e4SLinus Torvalds 	}
17201da177e4SLinus Torvalds 
17211da177e4SLinus Torvalds 	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == READ_10) {
17221da177e4SLinus Torvalds 		command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_READ);
17231da177e4SLinus Torvalds 	} else {
17241da177e4SLinus Torvalds 		command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_WRITE);
17251da177e4SLinus Torvalds 	}
17261da177e4SLinus Torvalds 
17271da177e4SLinus Torvalds 	command_packet->size = 3;
17281da177e4SLinus Torvalds 	command_packet->request_id = request_id;
17291da177e4SLinus Torvalds 	command_packet->unit__hostid = TW_UNITHOST_IN(0, srb->device->id);
17301da177e4SLinus Torvalds 	command_packet->status = 0;
17311da177e4SLinus Torvalds 	command_packet->flags = 0;
17321da177e4SLinus Torvalds 
17331da177e4SLinus Torvalds 	if (srb->cmnd[0] == WRITE_10) {
17341da177e4SLinus Torvalds 		if ((srb->cmnd[1] & 0x8) || (srb->cmnd[1] & 0x10))
17351da177e4SLinus Torvalds 			command_packet->flags = 1;
17361da177e4SLinus Torvalds 	}
17371da177e4SLinus Torvalds 
17381da177e4SLinus Torvalds 	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) {
17391da177e4SLinus Torvalds 		lba = ((u32)srb->cmnd[1] << 16) | ((u32)srb->cmnd[2] << 8) | (u32)srb->cmnd[3];
17401da177e4SLinus Torvalds 		num_sectors = (u32)srb->cmnd[4];
17411da177e4SLinus Torvalds 	} else {
17421da177e4SLinus Torvalds 		lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5];
17431da177e4SLinus Torvalds 		num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
17441da177e4SLinus Torvalds 	}
17451da177e4SLinus Torvalds 
17461da177e4SLinus Torvalds 	/* Update sector statistic */
17471da177e4SLinus Torvalds 	tw_dev->sector_count = num_sectors;
17481da177e4SLinus Torvalds 	if (tw_dev->sector_count > tw_dev->max_sector_count)
17491da177e4SLinus Torvalds 		tw_dev->max_sector_count = tw_dev->sector_count;
17501da177e4SLinus Torvalds 
17511da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors);
17521da177e4SLinus Torvalds 	command_packet->byte8.io.lba = lba;
17531da177e4SLinus Torvalds 	command_packet->byte6.block_count = num_sectors;
17541da177e4SLinus Torvalds 
17559cd95546SChristoph Hellwig 	use_sg = scsi_dma_map(srb);
17569cd95546SChristoph Hellwig 	if (use_sg <= 0)
17571da177e4SLinus Torvalds 		return 1;
17581da177e4SLinus Torvalds 
17596edae708SFUJITA Tomonori 	scsi_for_each_sg(tw_dev->srb[request_id], sg, use_sg, i) {
17606edae708SFUJITA Tomonori 		command_packet->byte8.io.sgl[i].address = sg_dma_address(sg);
17616edae708SFUJITA Tomonori 		command_packet->byte8.io.sgl[i].length = sg_dma_len(sg);
17621da177e4SLinus Torvalds 		command_packet->size+=2;
17631da177e4SLinus Torvalds 	}
17641da177e4SLinus Torvalds 
17651da177e4SLinus Torvalds 	/* Update SG statistics */
17666edae708SFUJITA Tomonori 	tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]);
17671da177e4SLinus Torvalds 	if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
17681da177e4SLinus Torvalds 		tw_dev->max_sgl_entries = tw_dev->sgl_entries;
17691da177e4SLinus Torvalds 
17701da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
17711da177e4SLinus Torvalds 	if (command_que_value == 0) {
17721da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n");
17731da177e4SLinus Torvalds 		return 1;
17741da177e4SLinus Torvalds 	}
17751da177e4SLinus Torvalds 
17761da177e4SLinus Torvalds 	/* Now try to post the command to the board */
17771da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
17781da177e4SLinus Torvalds 
17791da177e4SLinus Torvalds 	return 0;
17801da177e4SLinus Torvalds } /* End tw_scsiop_read_write() */
17811da177e4SLinus Torvalds 
17821da177e4SLinus Torvalds /* This function will handle the request sense scsi command */
tw_scsiop_request_sense(TW_Device_Extension * tw_dev,int request_id)17831da177e4SLinus Torvalds static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
17841da177e4SLinus Torvalds {
17856e3b2bbbSJames Bottomley 	char request_buffer[18];
17866e3b2bbbSJames Bottomley 
17871da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
17881da177e4SLinus Torvalds 
17896e3b2bbbSJames Bottomley 	memset(request_buffer, 0, sizeof(request_buffer));
17906e3b2bbbSJames Bottomley 	request_buffer[0] = 0x70; /* Immediate fixed format */
17916e3b2bbbSJames Bottomley 	request_buffer[7] = 10;	/* minimum size per SPC: 18 bytes */
17926e3b2bbbSJames Bottomley 	/* leave all other fields zero, giving effectively NO_SENSE return */
17936e3b2bbbSJames Bottomley 	tw_transfer_internal(tw_dev, request_id, request_buffer,
17946e3b2bbbSJames Bottomley 			     sizeof(request_buffer));
17956e3b2bbbSJames Bottomley 
17961da177e4SLinus Torvalds 	tw_dev->state[request_id] = TW_S_COMPLETED;
17971da177e4SLinus Torvalds 	tw_state_request_finish(tw_dev, request_id);
17981da177e4SLinus Torvalds 
17991da177e4SLinus Torvalds 	/* If we got a request_sense, we probably want a reset, return error */
18001da177e4SLinus Torvalds 	tw_dev->srb[request_id]->result = (DID_ERROR << 16);
18019dd9b96cSBart Van Assche 	scsi_done(tw_dev->srb[request_id]);
18021da177e4SLinus Torvalds 
18031da177e4SLinus Torvalds 	return 0;
18041da177e4SLinus Torvalds } /* End tw_scsiop_request_sense() */
18051da177e4SLinus Torvalds 
18061da177e4SLinus Torvalds /* This function will handle synchronize cache scsi command */
tw_scsiop_synchronize_cache(TW_Device_Extension * tw_dev,int request_id)18071da177e4SLinus Torvalds static int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
18081da177e4SLinus Torvalds {
18091da177e4SLinus Torvalds 	TW_Command *command_packet;
18101da177e4SLinus Torvalds 	unsigned long command_que_value;
18111da177e4SLinus Torvalds 
18121da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_synchronize_cache()\n");
18131da177e4SLinus Torvalds 
18141da177e4SLinus Torvalds 	/* Send firmware flush command for this unit */
18151da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
18161da177e4SLinus Torvalds 	if (command_packet == NULL) {
18171da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet virtual address.\n");
18181da177e4SLinus Torvalds 		return 1;
18191da177e4SLinus Torvalds 	}
18201da177e4SLinus Torvalds 
18211da177e4SLinus Torvalds 	/* Setup the command packet */
18221da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
18231da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_FLUSH_CACHE);
18241da177e4SLinus Torvalds 	command_packet->size = 2;
18251da177e4SLinus Torvalds 	command_packet->request_id = request_id;
18261da177e4SLinus Torvalds 	command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
18271da177e4SLinus Torvalds 	command_packet->status = 0;
18281da177e4SLinus Torvalds 	command_packet->flags = 0;
18291da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
18301da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
18311da177e4SLinus Torvalds 	if (command_que_value == 0) {
18321da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet physical address.\n");
18331da177e4SLinus Torvalds 		return 1;
18341da177e4SLinus Torvalds 	}
18351da177e4SLinus Torvalds 
18361da177e4SLinus Torvalds 	/* Now try to post the command packet */
18371da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
18381da177e4SLinus Torvalds 
18391da177e4SLinus Torvalds 	return 0;
18401da177e4SLinus Torvalds } /* End tw_scsiop_synchronize_cache() */
18411da177e4SLinus Torvalds 
18421da177e4SLinus Torvalds /* This function will handle test unit ready scsi command */
tw_scsiop_test_unit_ready(TW_Device_Extension * tw_dev,int request_id)18431da177e4SLinus Torvalds static int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
18441da177e4SLinus Torvalds {
18451da177e4SLinus Torvalds 	TW_Param *param;
18461da177e4SLinus Torvalds 	TW_Command *command_packet;
18471da177e4SLinus Torvalds 	unsigned long command_que_value;
18481da177e4SLinus Torvalds 	unsigned long param_value;
18491da177e4SLinus Torvalds 
18501da177e4SLinus Torvalds 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
18511da177e4SLinus Torvalds 
18521da177e4SLinus Torvalds 	/* Initialize command packet */
18531da177e4SLinus Torvalds 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
18541da177e4SLinus Torvalds 	if (command_packet == NULL) {
18551da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet virtual address.\n");
18561da177e4SLinus Torvalds 		return 1;
18571da177e4SLinus Torvalds 	}
18581da177e4SLinus Torvalds 	memset(command_packet, 0, sizeof(TW_Sector));
18591da177e4SLinus Torvalds 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
18601da177e4SLinus Torvalds 	command_packet->size = 4;
18611da177e4SLinus Torvalds 	command_packet->request_id = request_id;
18621da177e4SLinus Torvalds 	command_packet->status = 0;
18631da177e4SLinus Torvalds 	command_packet->flags = 0;
18641da177e4SLinus Torvalds 	command_packet->byte6.parameter_count = 1;
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	/* Now setup the param */
18671da177e4SLinus Torvalds 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
18681da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment virtual address.\n");
18691da177e4SLinus Torvalds 		return 1;
18701da177e4SLinus Torvalds 	}
18711da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
18721da177e4SLinus Torvalds 	memset(param, 0, sizeof(TW_Sector));
18731da177e4SLinus Torvalds 	param->table_id = 3;	 /* unit summary table */
18741da177e4SLinus Torvalds 	param->parameter_id = 3; /* unitsstatus parameter */
18751da177e4SLinus Torvalds 	param->parameter_size_bytes = TW_MAX_UNITS;
18761da177e4SLinus Torvalds 	param_value = tw_dev->alignment_physical_address[request_id];
18771da177e4SLinus Torvalds 	if (param_value == 0) {
18781da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment physical address.\n");
18791da177e4SLinus Torvalds 		return 1;
18801da177e4SLinus Torvalds 	}
18811da177e4SLinus Torvalds 
18821da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].address = param_value;
18831da177e4SLinus Torvalds 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
18841da177e4SLinus Torvalds 	command_que_value = tw_dev->command_packet_physical_address[request_id];
18851da177e4SLinus Torvalds 	if (command_que_value == 0) {
18861da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
18871da177e4SLinus Torvalds 		return 1;
18881da177e4SLinus Torvalds 	}
18891da177e4SLinus Torvalds 
18901da177e4SLinus Torvalds 	/* Now try to post the command packet */
18911da177e4SLinus Torvalds 	tw_post_command_packet(tw_dev, request_id);
18921da177e4SLinus Torvalds 
18931da177e4SLinus Torvalds 	return 0;
18941da177e4SLinus Torvalds } /* End tw_scsiop_test_unit_ready() */
18951da177e4SLinus Torvalds 
18961da177e4SLinus Torvalds /* This function is called by the isr to complete a testunitready command */
tw_scsiop_test_unit_ready_complete(TW_Device_Extension * tw_dev,int request_id)18971da177e4SLinus Torvalds static int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
18981da177e4SLinus Torvalds {
18991da177e4SLinus Torvalds 	unsigned char *is_unit_present;
19001da177e4SLinus Torvalds 	TW_Param *param;
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds 	dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
19031da177e4SLinus Torvalds 
19041da177e4SLinus Torvalds 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
19051da177e4SLinus Torvalds 	if (param == NULL) {
19061da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
19071da177e4SLinus Torvalds 		return 1;
19081da177e4SLinus Torvalds 	}
19091da177e4SLinus Torvalds 	is_unit_present = &(param->data[0]);
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds 	if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
19121da177e4SLinus Torvalds 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
19131da177e4SLinus Torvalds 	} else {
19141da177e4SLinus Torvalds 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
19151da177e4SLinus Torvalds 		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
19161da177e4SLinus Torvalds 		return TW_ISR_DONT_RESULT;
19171da177e4SLinus Torvalds 	}
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds 	return 0;
19201da177e4SLinus Torvalds } /* End tw_scsiop_test_unit_ready_complete() */
19211da177e4SLinus Torvalds 
19221da177e4SLinus Torvalds /* This is the main scsi queue function to handle scsi opcodes */
tw_scsi_queue_lck(struct scsi_cmnd * SCpnt)1923af049dfdSBart Van Assche static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt)
19241da177e4SLinus Torvalds {
1925af049dfdSBart Van Assche 	void (*done)(struct scsi_cmnd *) = scsi_done;
19261da177e4SLinus Torvalds 	unsigned char *command = SCpnt->cmnd;
19271da177e4SLinus Torvalds 	int request_id = 0;
19281da177e4SLinus Torvalds 	int retval = 1;
19291da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
19301da177e4SLinus Torvalds 
19314fe48187Sadam radford 	/* If we are resetting due to timed out ioctl, report as busy */
19324fe48187Sadam radford 	if (test_bit(TW_IN_RESET, &tw_dev->flags))
19334fe48187Sadam radford 		return SCSI_MLQUEUE_HOST_BUSY;
19344fe48187Sadam radford 
19351da177e4SLinus Torvalds 	/* Queue the command and get a request id */
19361da177e4SLinus Torvalds 	tw_state_request_start(tw_dev, &request_id);
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 	/* Save the scsi command for use by the ISR */
19391da177e4SLinus Torvalds 	tw_dev->srb[request_id] = SCpnt;
19401da177e4SLinus Torvalds 
19411da177e4SLinus Torvalds 	switch (*command) {
19421da177e4SLinus Torvalds 	case READ_10:
19431da177e4SLinus Torvalds 	case READ_6:
19441da177e4SLinus Torvalds 	case WRITE_10:
19451da177e4SLinus Torvalds 	case WRITE_6:
19461da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ/WRITE.\n");
19471da177e4SLinus Torvalds 		retval = tw_scsiop_read_write(tw_dev, request_id);
19481da177e4SLinus Torvalds 		break;
19491da177e4SLinus Torvalds 	case TEST_UNIT_READY:
19501da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
19511da177e4SLinus Torvalds 		retval = tw_scsiop_test_unit_ready(tw_dev, request_id);
19521da177e4SLinus Torvalds 		break;
19531da177e4SLinus Torvalds 	case INQUIRY:
19541da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
19551da177e4SLinus Torvalds 		retval = tw_scsiop_inquiry(tw_dev, request_id);
19561da177e4SLinus Torvalds 		break;
19571da177e4SLinus Torvalds 	case READ_CAPACITY:
19581da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
19591da177e4SLinus Torvalds 		retval = tw_scsiop_read_capacity(tw_dev, request_id);
19601da177e4SLinus Torvalds 		break;
19611da177e4SLinus Torvalds 	case REQUEST_SENSE:
19621da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
19631da177e4SLinus Torvalds 		retval = tw_scsiop_request_sense(tw_dev, request_id);
19641da177e4SLinus Torvalds 		break;
19651da177e4SLinus Torvalds 	case MODE_SENSE:
19661da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
19671da177e4SLinus Torvalds 		retval = tw_scsiop_mode_sense(tw_dev, request_id);
19681da177e4SLinus Torvalds 		break;
19691da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
19701da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
19711da177e4SLinus Torvalds 		retval = tw_scsiop_synchronize_cache(tw_dev, request_id);
19721da177e4SLinus Torvalds 		break;
19731da177e4SLinus Torvalds 	case TW_IOCTL:
19741da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: SCSI_IOCTL_SEND_COMMAND deprecated, please update your 3ware tools.\n");
19751da177e4SLinus Torvalds 		break;
19761da177e4SLinus Torvalds 	default:
19771da177e4SLinus Torvalds 		printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command);
19781da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_COMPLETED;
19791da177e4SLinus Torvalds 		tw_state_request_finish(tw_dev, request_id);
1980f2b1e9c6SHannes Reinecke 		scsi_build_sense(SCpnt, 1, ILLEGAL_REQUEST, 0x20, 0);
19811da177e4SLinus Torvalds 		done(SCpnt);
19821da177e4SLinus Torvalds 		retval = 0;
19831da177e4SLinus Torvalds 	}
19841da177e4SLinus Torvalds 	if (retval) {
19851da177e4SLinus Torvalds 		tw_dev->state[request_id] = TW_S_COMPLETED;
19861da177e4SLinus Torvalds 		tw_state_request_finish(tw_dev, request_id);
19871da177e4SLinus Torvalds 		SCpnt->result = (DID_ERROR << 16);
19881da177e4SLinus Torvalds 		done(SCpnt);
19891da177e4SLinus Torvalds 		retval = 0;
19901da177e4SLinus Torvalds 	}
19911da177e4SLinus Torvalds 	return retval;
19921da177e4SLinus Torvalds } /* End tw_scsi_queue() */
19931da177e4SLinus Torvalds 
DEF_SCSI_QCMD(tw_scsi_queue)1994f281233dSJeff Garzik static DEF_SCSI_QCMD(tw_scsi_queue)
1995f281233dSJeff Garzik 
19961da177e4SLinus Torvalds /* This function is the interrupt service routine */
19977d12e780SDavid Howells static irqreturn_t tw_interrupt(int irq, void *dev_instance)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	int request_id;
20001da177e4SLinus Torvalds 	u32 status_reg_value;
20011da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
20021da177e4SLinus Torvalds 	TW_Response_Queue response_que;
20031da177e4SLinus Torvalds 	int error = 0, retval = 0;
20041da177e4SLinus Torvalds 	TW_Command *command_packet;
20051da177e4SLinus Torvalds 	int handled = 0;
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 	/* Get the host lock for io completions */
20081da177e4SLinus Torvalds 	spin_lock(tw_dev->host->host_lock);
20091da177e4SLinus Torvalds 
20101da177e4SLinus Torvalds 	/* Read the registers */
20111da177e4SLinus Torvalds 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 	/* Check if this is our interrupt, otherwise bail */
20141da177e4SLinus Torvalds 	if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
20151da177e4SLinus Torvalds 		goto tw_interrupt_bail;
20161da177e4SLinus Torvalds 
20171da177e4SLinus Torvalds 	handled = 1;
20181da177e4SLinus Torvalds 
20194fe48187Sadam radford 	/* If we are resetting, bail */
20204fe48187Sadam radford 	if (test_bit(TW_IN_RESET, &tw_dev->flags))
20214fe48187Sadam radford 		goto tw_interrupt_bail;
20224fe48187Sadam radford 
20231da177e4SLinus Torvalds 	/* Check controller for errors */
20241da177e4SLinus Torvalds 	if (tw_check_bits(status_reg_value)) {
20251da177e4SLinus Torvalds 		dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
20261da177e4SLinus Torvalds 		if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
20271da177e4SLinus Torvalds 			TW_CLEAR_ALL_INTERRUPTS(tw_dev);
20281da177e4SLinus Torvalds 			goto tw_interrupt_bail;
20291da177e4SLinus Torvalds 		}
20301da177e4SLinus Torvalds 	}
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds 	/* Handle host interrupt */
20331da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_HOST_INTERRUPT) {
20341da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
20351da177e4SLinus Torvalds 		TW_CLEAR_HOST_INTERRUPT(tw_dev);
20361da177e4SLinus Torvalds 	}
20371da177e4SLinus Torvalds 
20381da177e4SLinus Torvalds 	/* Handle attention interrupt */
20391da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
20401da177e4SLinus Torvalds 		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
20411da177e4SLinus Torvalds 		TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
20421da177e4SLinus Torvalds 		tw_state_request_start(tw_dev, &request_id);
20431da177e4SLinus Torvalds 		error = tw_aen_read_queue(tw_dev, request_id);
20441da177e4SLinus Torvalds 		if (error) {
20451da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: scsi%d: Error reading aen queue.\n", tw_dev->host->host_no);
20461da177e4SLinus Torvalds 			tw_dev->state[request_id] = TW_S_COMPLETED;
20471da177e4SLinus Torvalds 			tw_state_request_finish(tw_dev, request_id);
20481da177e4SLinus Torvalds 		}
20491da177e4SLinus Torvalds 	}
20501da177e4SLinus Torvalds 
20511da177e4SLinus Torvalds 	/* Handle command interrupt */
20521da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
20531da177e4SLinus Torvalds 		/* Drain as many pending commands as we can */
20541da177e4SLinus Torvalds 		while (tw_dev->pending_request_count > 0) {
20551da177e4SLinus Torvalds 			request_id = tw_dev->pending_queue[tw_dev->pending_head];
20561da177e4SLinus Torvalds 			if (tw_dev->state[request_id] != TW_S_PENDING) {
20571da177e4SLinus Torvalds 				printk(KERN_WARNING "3w-xxxx: scsi%d: Found request id that wasn't pending.\n", tw_dev->host->host_no);
20581da177e4SLinus Torvalds 				break;
20591da177e4SLinus Torvalds 			}
20601da177e4SLinus Torvalds 			if (tw_post_command_packet(tw_dev, request_id)==0) {
20611da177e4SLinus Torvalds 				if (tw_dev->pending_head == TW_Q_LENGTH-1) {
20621da177e4SLinus Torvalds 					tw_dev->pending_head = TW_Q_START;
20631da177e4SLinus Torvalds 				} else {
20641da177e4SLinus Torvalds 					tw_dev->pending_head = tw_dev->pending_head + 1;
20651da177e4SLinus Torvalds 				}
20661da177e4SLinus Torvalds 				tw_dev->pending_request_count--;
20671da177e4SLinus Torvalds 			} else {
20681da177e4SLinus Torvalds 				/* If we get here, we will continue re-posting on the next command interrupt */
20691da177e4SLinus Torvalds 				break;
20701da177e4SLinus Torvalds 			}
20711da177e4SLinus Torvalds 		}
20721da177e4SLinus Torvalds 		/* If there are no more pending requests, we mask command interrupt */
20731da177e4SLinus Torvalds 		if (tw_dev->pending_request_count == 0)
20741da177e4SLinus Torvalds 			TW_MASK_COMMAND_INTERRUPT(tw_dev);
20751da177e4SLinus Torvalds 	}
20761da177e4SLinus Torvalds 
20771da177e4SLinus Torvalds 	/* Handle response interrupt */
20781da177e4SLinus Torvalds 	if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
20791da177e4SLinus Torvalds 		/* Drain the response queue from the board */
20801da177e4SLinus Torvalds 		while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
20811da177e4SLinus Torvalds 			/* Read response queue register */
20821da177e4SLinus Torvalds 			response_que.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
20831da177e4SLinus Torvalds 			request_id = TW_RESID_OUT(response_que.response_id);
20841da177e4SLinus Torvalds 			command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
20851da177e4SLinus Torvalds 			error = 0;
20861da177e4SLinus Torvalds 
20871da177e4SLinus Torvalds 			/* Check for bad response */
20881da177e4SLinus Torvalds 			if (command_packet->status != 0) {
20891da177e4SLinus Torvalds 				/* If internal command, don't error, don't fill sense */
20901da177e4SLinus Torvalds 				if (tw_dev->srb[request_id] == NULL) {
20911da177e4SLinus Torvalds 					tw_decode_sense(tw_dev, request_id, 0);
20921da177e4SLinus Torvalds 				} else {
20931da177e4SLinus Torvalds 					error = tw_decode_sense(tw_dev, request_id, 1);
20941da177e4SLinus Torvalds 				}
20951da177e4SLinus Torvalds 			}
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds 			/* Check for correct state */
20981da177e4SLinus Torvalds 			if (tw_dev->state[request_id] != TW_S_POSTED) {
20991da177e4SLinus Torvalds 				if (tw_dev->srb[request_id] != NULL) {
21001da177e4SLinus Torvalds 					printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id that wasn't posted.\n", tw_dev->host->host_no);
21011da177e4SLinus Torvalds 					error = 1;
21021da177e4SLinus Torvalds 				}
21031da177e4SLinus Torvalds 			}
21041da177e4SLinus Torvalds 
21051da177e4SLinus Torvalds 			dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
21061da177e4SLinus Torvalds 
21071da177e4SLinus Torvalds 			/* Check for internal command completion */
21081da177e4SLinus Torvalds 			if (tw_dev->srb[request_id] == NULL) {
21091da177e4SLinus Torvalds 				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
21101da177e4SLinus Torvalds 				/* Check for chrdev ioctl completion */
21111da177e4SLinus Torvalds 				if (request_id != tw_dev->chrdev_request_id) {
21121da177e4SLinus Torvalds 					retval = tw_aen_complete(tw_dev, request_id);
21131da177e4SLinus Torvalds 					if (retval) {
21141da177e4SLinus Torvalds 						printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
21151da177e4SLinus Torvalds 					}
21161da177e4SLinus Torvalds 				} else {
21171da177e4SLinus Torvalds 					tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
21181da177e4SLinus Torvalds 					wake_up(&tw_dev->ioctl_wqueue);
21191da177e4SLinus Torvalds 				}
21201da177e4SLinus Torvalds 			} else {
21211da177e4SLinus Torvalds 				switch (tw_dev->srb[request_id]->cmnd[0]) {
21221da177e4SLinus Torvalds 				case READ_10:
21231da177e4SLinus Torvalds 				case READ_6:
21241da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10/READ_6\n");
21251da177e4SLinus Torvalds 					break;
21261da177e4SLinus Torvalds 				case WRITE_10:
21271da177e4SLinus Torvalds 				case WRITE_6:
21281da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n");
21291da177e4SLinus Torvalds 					break;
21301da177e4SLinus Torvalds 				case TEST_UNIT_READY:
21311da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
21321da177e4SLinus Torvalds 					error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id);
21331da177e4SLinus Torvalds 					break;
21341da177e4SLinus Torvalds 				case INQUIRY:
21351da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
21361da177e4SLinus Torvalds 					error = tw_scsiop_inquiry_complete(tw_dev, request_id);
21371da177e4SLinus Torvalds 					break;
21381da177e4SLinus Torvalds 				case READ_CAPACITY:
21391da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
21401da177e4SLinus Torvalds 					error = tw_scsiop_read_capacity_complete(tw_dev, request_id);
21411da177e4SLinus Torvalds 					break;
21421da177e4SLinus Torvalds 				case MODE_SENSE:
21431da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught MODE_SENSE\n");
21441da177e4SLinus Torvalds 					error = tw_scsiop_mode_sense_complete(tw_dev, request_id);
21451da177e4SLinus Torvalds 					break;
21461da177e4SLinus Torvalds 				case SYNCHRONIZE_CACHE:
21471da177e4SLinus Torvalds 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught SYNCHRONIZE_CACHE\n");
21481da177e4SLinus Torvalds 					break;
21491da177e4SLinus Torvalds 				default:
21501da177e4SLinus Torvalds 					printk(KERN_WARNING "3w-xxxx: case slip in tw_interrupt()\n");
21511da177e4SLinus Torvalds 					error = 1;
21521da177e4SLinus Torvalds 				}
21531da177e4SLinus Torvalds 
21541da177e4SLinus Torvalds 				/* If no error command was a success */
21551da177e4SLinus Torvalds 				if (error == 0) {
21561da177e4SLinus Torvalds 					tw_dev->srb[request_id]->result = (DID_OK << 16);
21571da177e4SLinus Torvalds 				}
21581da177e4SLinus Torvalds 
21591da177e4SLinus Torvalds 				/* If error, command failed */
21601da177e4SLinus Torvalds 				if (error == 1) {
21611da177e4SLinus Torvalds 					/* Ask for a host reset */
21623d45cefcSHannes Reinecke 					tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
21631da177e4SLinus Torvalds 				}
21641da177e4SLinus Torvalds 
21651da177e4SLinus Torvalds 				/* Now complete the io */
21661da177e4SLinus Torvalds 				if ((error != TW_ISR_DONT_COMPLETE)) {
21679cd95546SChristoph Hellwig 					scsi_dma_unmap(tw_dev->srb[request_id]);
21689dd9b96cSBart Van Assche 					scsi_done(tw_dev->srb[request_id]);
21691da177e4SLinus Torvalds 					tw_dev->state[request_id] = TW_S_COMPLETED;
21701da177e4SLinus Torvalds 					tw_state_request_finish(tw_dev, request_id);
21711da177e4SLinus Torvalds 					tw_dev->posted_request_count--;
21721da177e4SLinus Torvalds 				}
21731da177e4SLinus Torvalds 			}
21741da177e4SLinus Torvalds 
21751da177e4SLinus Torvalds 			/* Check for valid status after each drain */
21761da177e4SLinus Torvalds 			status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
21771da177e4SLinus Torvalds 			if (tw_check_bits(status_reg_value)) {
21781da177e4SLinus Torvalds 				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
21791da177e4SLinus Torvalds 				if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
21801da177e4SLinus Torvalds 					TW_CLEAR_ALL_INTERRUPTS(tw_dev);
21811da177e4SLinus Torvalds 					goto tw_interrupt_bail;
21821da177e4SLinus Torvalds 				}
21831da177e4SLinus Torvalds 			}
21841da177e4SLinus Torvalds 		}
21851da177e4SLinus Torvalds 	}
21861da177e4SLinus Torvalds 
21871da177e4SLinus Torvalds tw_interrupt_bail:
21881da177e4SLinus Torvalds 	spin_unlock(tw_dev->host->host_lock);
21891da177e4SLinus Torvalds 	return IRQ_RETVAL(handled);
21901da177e4SLinus Torvalds } /* End tw_interrupt() */
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds /* This function tells the controller to shut down */
__tw_shutdown(TW_Device_Extension * tw_dev)21931da177e4SLinus Torvalds static void __tw_shutdown(TW_Device_Extension *tw_dev)
21941da177e4SLinus Torvalds {
21951da177e4SLinus Torvalds 	/* Disable interrupts */
21961da177e4SLinus Torvalds 	TW_DISABLE_INTERRUPTS(tw_dev);
21971da177e4SLinus Torvalds 
21984fe48187Sadam radford 	/* Free up the IRQ */
21994fe48187Sadam radford 	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
22004fe48187Sadam radford 
22011da177e4SLinus Torvalds 	printk(KERN_WARNING "3w-xxxx: Shutting down host %d.\n", tw_dev->host->host_no);
22021da177e4SLinus Torvalds 
22031da177e4SLinus Torvalds 	/* Tell the card we are shutting down */
22041da177e4SLinus Torvalds 	if (tw_initconnection(tw_dev, 1)) {
22051da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Connection shutdown failed.\n");
22061da177e4SLinus Torvalds 	} else {
22071da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Shutdown complete.\n");
22081da177e4SLinus Torvalds 	}
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds 	/* Clear all interrupts just before exit */
22111da177e4SLinus Torvalds 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
22121da177e4SLinus Torvalds } /* End __tw_shutdown() */
22131da177e4SLinus Torvalds 
22141da177e4SLinus Torvalds /* Wrapper for __tw_shutdown */
tw_shutdown(struct pci_dev * pdev)2215d18c3db5SGreg Kroah-Hartman static void tw_shutdown(struct pci_dev *pdev)
22161da177e4SLinus Torvalds {
2217d18c3db5SGreg Kroah-Hartman 	struct Scsi_Host *host = pci_get_drvdata(pdev);
22181da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
22191da177e4SLinus Torvalds 
22201da177e4SLinus Torvalds 	__tw_shutdown(tw_dev);
22211da177e4SLinus Torvalds } /* End tw_shutdown() */
22221da177e4SLinus Torvalds 
22234deedd84Sadam radford /* This function gets called when a disk is coming online */
tw_slave_configure(struct scsi_device * sdev)22244deedd84Sadam radford static int tw_slave_configure(struct scsi_device *sdev)
22254deedd84Sadam radford {
22264deedd84Sadam radford 	/* Force 60 second timeout */
22274deedd84Sadam radford 	blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
22284deedd84Sadam radford 
22294deedd84Sadam radford 	return 0;
22304deedd84Sadam radford } /* End tw_slave_configure() */
22314deedd84Sadam radford 
2232*ca1b0e01SBart Van Assche static const struct scsi_host_template driver_template = {
22331da177e4SLinus Torvalds 	.module			= THIS_MODULE,
22341da177e4SLinus Torvalds 	.name			= "3ware Storage Controller",
22351da177e4SLinus Torvalds 	.queuecommand		= tw_scsi_queue,
22361da177e4SLinus Torvalds 	.eh_host_reset_handler	= tw_scsi_eh_reset,
22371da177e4SLinus Torvalds 	.bios_param		= tw_scsi_biosparam,
2238db5ed4dfSChristoph Hellwig 	.change_queue_depth	= scsi_change_queue_depth,
22391da177e4SLinus Torvalds 	.can_queue		= TW_Q_LENGTH-2,
22404deedd84Sadam radford 	.slave_configure	= tw_slave_configure,
22411da177e4SLinus Torvalds 	.this_id		= -1,
22421da177e4SLinus Torvalds 	.sg_tablesize		= TW_MAX_SGL_LENGTH,
22431da177e4SLinus Torvalds 	.max_sectors		= TW_MAX_SECTORS,
22441da177e4SLinus Torvalds 	.cmd_per_lun		= TW_MAX_CMDS_PER_LUN,
224565bc2a7fSBart Van Assche 	.shost_groups		= tw_host_groups,
224654b2b50cSMartin K. Petersen 	.emulated		= 1,
224754b2b50cSMartin K. Petersen 	.no_write_same		= 1,
22481da177e4SLinus Torvalds };
22491da177e4SLinus Torvalds 
22501da177e4SLinus Torvalds /* This function will probe and initialize a card */
tw_probe(struct pci_dev * pdev,const struct pci_device_id * dev_id)22516f039790SGreg Kroah-Hartman static int tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
22521da177e4SLinus Torvalds {
22531da177e4SLinus Torvalds 	struct Scsi_Host *host = NULL;
22541da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev;
22558ecfb16cSColin Ian King 	int retval;
22561da177e4SLinus Torvalds 
22571da177e4SLinus Torvalds 	retval = pci_enable_device(pdev);
22581da177e4SLinus Torvalds 	if (retval) {
22591da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to enable pci device.");
22601da177e4SLinus Torvalds 		goto out_disable_device;
22611da177e4SLinus Torvalds 	}
22621da177e4SLinus Torvalds 
22631da177e4SLinus Torvalds 	pci_set_master(pdev);
22641da177e4SLinus Torvalds 
2265bd6cf46bSChristoph Hellwig 	retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
22661da177e4SLinus Torvalds 	if (retval) {
22671da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to set dma mask.");
22681da177e4SLinus Torvalds 		goto out_disable_device;
22691da177e4SLinus Torvalds 	}
22701da177e4SLinus Torvalds 
22711da177e4SLinus Torvalds 	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
22721da177e4SLinus Torvalds 	if (!host) {
22731da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to allocate memory for device extension.");
22741da177e4SLinus Torvalds 		retval = -ENOMEM;
22751da177e4SLinus Torvalds 		goto out_disable_device;
22761da177e4SLinus Torvalds 	}
22771da177e4SLinus Torvalds 	tw_dev = (TW_Device_Extension *)host->hostdata;
22781da177e4SLinus Torvalds 
22791da177e4SLinus Torvalds 	/* Save values to device extension */
22801da177e4SLinus Torvalds 	tw_dev->host = host;
22811da177e4SLinus Torvalds 	tw_dev->tw_pci_dev = pdev;
22821da177e4SLinus Torvalds 
22831da177e4SLinus Torvalds 	if (tw_initialize_device_extension(tw_dev)) {
22841da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to initialize device extension.");
22854dc98c19SAnton Vasilyev 		retval = -ENOMEM;
22861da177e4SLinus Torvalds 		goto out_free_device_extension;
22871da177e4SLinus Torvalds 	}
22881da177e4SLinus Torvalds 
22891da177e4SLinus Torvalds 	/* Request IO regions */
22901da177e4SLinus Torvalds 	retval = pci_request_regions(pdev, "3w-xxxx");
22911da177e4SLinus Torvalds 	if (retval) {
22921da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to get mem region.");
22931da177e4SLinus Torvalds 		goto out_free_device_extension;
22941da177e4SLinus Torvalds 	}
22951da177e4SLinus Torvalds 
22961da177e4SLinus Torvalds 	/* Save base address */
22971da177e4SLinus Torvalds 	tw_dev->base_addr = pci_resource_start(pdev, 0);
22981da177e4SLinus Torvalds 	if (!tw_dev->base_addr) {
22991da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Failed to get io address.");
23004dc98c19SAnton Vasilyev 		retval = -ENOMEM;
23011da177e4SLinus Torvalds 		goto out_release_mem_region;
23021da177e4SLinus Torvalds 	}
23031da177e4SLinus Torvalds 
23041da177e4SLinus Torvalds 	/* Disable interrupts on the card */
23051da177e4SLinus Torvalds 	TW_DISABLE_INTERRUPTS(tw_dev);
23061da177e4SLinus Torvalds 
23071da177e4SLinus Torvalds 	/* Initialize the card */
23081da177e4SLinus Torvalds 	if (tw_reset_sequence(tw_dev)) {
23091da177e4SLinus Torvalds 		retval = -EINVAL;
23101da177e4SLinus Torvalds 		goto out_release_mem_region;
23111da177e4SLinus Torvalds 	}
23121da177e4SLinus Torvalds 
23131da177e4SLinus Torvalds 	/* Set host specific parameters */
23141da177e4SLinus Torvalds 	host->max_id = TW_MAX_UNITS;
23151da177e4SLinus Torvalds 	host->max_cmd_len = TW_MAX_CDB_LEN;
23161da177e4SLinus Torvalds 
23171da177e4SLinus Torvalds 	/* Luns and channels aren't supported by adapter */
23181da177e4SLinus Torvalds 	host->max_lun = 0;
23191da177e4SLinus Torvalds 	host->max_channel = 0;
23201da177e4SLinus Torvalds 
23211da177e4SLinus Torvalds 	/* Register the card with the kernel SCSI layer */
23221da177e4SLinus Torvalds 	retval = scsi_add_host(host, &pdev->dev);
23231da177e4SLinus Torvalds 	if (retval) {
23241da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: scsi add host failed");
23251da177e4SLinus Torvalds 		goto out_release_mem_region;
23261da177e4SLinus Torvalds 	}
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 	pci_set_drvdata(pdev, host);
23291da177e4SLinus Torvalds 
23301da177e4SLinus Torvalds 	printk(KERN_WARNING "3w-xxxx: scsi%d: Found a 3ware Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, tw_dev->base_addr, pdev->irq);
23311d6f359aSThomas Gleixner 
23321da177e4SLinus Torvalds 	/* Now setup the interrupt handler */
23331da177e4SLinus Torvalds 	retval = request_irq(pdev->irq, tw_interrupt, IRQF_SHARED, "3w-xxxx", tw_dev);
23341da177e4SLinus Torvalds 	if (retval) {
23351da177e4SLinus Torvalds 		printk(KERN_WARNING "3w-xxxx: Error requesting IRQ.");
23361da177e4SLinus Torvalds 		goto out_remove_host;
23371da177e4SLinus Torvalds 	}
23381da177e4SLinus Torvalds 
23391da177e4SLinus Torvalds 	tw_device_extension_list[tw_device_extension_count] = tw_dev;
23401da177e4SLinus Torvalds 	tw_device_extension_count++;
23411da177e4SLinus Torvalds 
23421da177e4SLinus Torvalds 	/* Re-enable interrupts on the card */
23431da177e4SLinus Torvalds 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds 	/* Finally, scan the host */
23461da177e4SLinus Torvalds 	scsi_scan_host(host);
23471da177e4SLinus Torvalds 
23481da177e4SLinus Torvalds 	if (twe_major == -1) {
23491da177e4SLinus Torvalds 		if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0)
23501da177e4SLinus Torvalds 			printk(KERN_WARNING "3w-xxxx: Failed to register character device.");
23511da177e4SLinus Torvalds 	}
23521da177e4SLinus Torvalds 	return 0;
23531da177e4SLinus Torvalds 
23541da177e4SLinus Torvalds out_remove_host:
23551da177e4SLinus Torvalds 	scsi_remove_host(host);
23561da177e4SLinus Torvalds out_release_mem_region:
23571da177e4SLinus Torvalds 	pci_release_regions(pdev);
23581da177e4SLinus Torvalds out_free_device_extension:
23591da177e4SLinus Torvalds 	tw_free_device_extension(tw_dev);
23601da177e4SLinus Torvalds 	scsi_host_put(host);
23611da177e4SLinus Torvalds out_disable_device:
23621da177e4SLinus Torvalds 	pci_disable_device(pdev);
23631da177e4SLinus Torvalds 
23641da177e4SLinus Torvalds 	return retval;
23651da177e4SLinus Torvalds } /* End tw_probe() */
23661da177e4SLinus Torvalds 
23671da177e4SLinus Torvalds /* This function is called to remove a device */
tw_remove(struct pci_dev * pdev)23681da177e4SLinus Torvalds static void tw_remove(struct pci_dev *pdev)
23691da177e4SLinus Torvalds {
23701da177e4SLinus Torvalds 	struct Scsi_Host *host = pci_get_drvdata(pdev);
23711da177e4SLinus Torvalds 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
23721da177e4SLinus Torvalds 
23731da177e4SLinus Torvalds 	scsi_remove_host(tw_dev->host);
23741da177e4SLinus Torvalds 
23751da177e4SLinus Torvalds 	/* Unregister character device */
23761da177e4SLinus Torvalds 	if (twe_major >= 0) {
23771da177e4SLinus Torvalds 		unregister_chrdev(twe_major, "twe");
23781da177e4SLinus Torvalds 		twe_major = -1;
23791da177e4SLinus Torvalds 	}
23801da177e4SLinus Torvalds 
23811da177e4SLinus Torvalds 	/* Shutdown the card */
23821da177e4SLinus Torvalds 	__tw_shutdown(tw_dev);
23831da177e4SLinus Torvalds 
23841da177e4SLinus Torvalds 	/* Free up the mem region */
23851da177e4SLinus Torvalds 	pci_release_regions(pdev);
23861da177e4SLinus Torvalds 
23871da177e4SLinus Torvalds 	/* Free up device extension resources */
23881da177e4SLinus Torvalds 	tw_free_device_extension(tw_dev);
23891da177e4SLinus Torvalds 
23901da177e4SLinus Torvalds 	scsi_host_put(tw_dev->host);
23911da177e4SLinus Torvalds 	pci_disable_device(pdev);
23921da177e4SLinus Torvalds 	tw_device_extension_count--;
23931da177e4SLinus Torvalds } /* End tw_remove() */
23946f039790SGreg Kroah-Hartman 
23951da177e4SLinus Torvalds /* PCI Devices supported by this driver */
23961da177e4SLinus Torvalds static struct pci_device_id tw_pci_tbl[] = {
23971da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_1000,
23981da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
23991da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_7000,
24001da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
24011da177e4SLinus Torvalds 	{ }
24021da177e4SLinus Torvalds };
24031da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, tw_pci_tbl);
24041da177e4SLinus Torvalds 
24051da177e4SLinus Torvalds /* pci_driver initializer */
24061da177e4SLinus Torvalds static struct pci_driver tw_driver = {
24071da177e4SLinus Torvalds 	.name		= "3w-xxxx",
24081da177e4SLinus Torvalds 	.id_table	= tw_pci_tbl,
2409d18c3db5SGreg Kroah-Hartman 	.probe		= tw_probe,
24101da177e4SLinus Torvalds 	.remove		= tw_remove,
24111da177e4SLinus Torvalds 	.shutdown	= tw_shutdown,
24121da177e4SLinus Torvalds };
24131da177e4SLinus Torvalds 
24141da177e4SLinus Torvalds /* This function is called on driver initialization */
tw_init(void)24151da177e4SLinus Torvalds static int __init tw_init(void)
24161da177e4SLinus Torvalds {
2417dcbccbdeSHenrik Kretzschmar 	printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 	return pci_register_driver(&tw_driver);
24201da177e4SLinus Torvalds } /* End tw_init() */
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds /* This function is called on driver exit */
tw_exit(void)24231da177e4SLinus Torvalds static void __exit tw_exit(void)
24241da177e4SLinus Torvalds {
24251da177e4SLinus Torvalds 	pci_unregister_driver(&tw_driver);
24261da177e4SLinus Torvalds } /* End tw_exit() */
24271da177e4SLinus Torvalds 
24281da177e4SLinus Torvalds module_init(tw_init);
2429 module_exit(tw_exit);
2430 
2431