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