2005-04-22 18:02:25 -04:00
/*
* linux / drivers / message / fusion / mptspi . c
* For use with LSI Logic PCI chip / adapter ( s )
* running LSI Logic Fusion MPT ( Message Passing Technology ) firmware .
*
* Copyright ( c ) 1999 - 2005 LSI Logic Corporation
* ( mailto : mpt_linux_developer @ lsil . com )
*
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
NO WARRANTY
THE PROGRAM IS PROVIDED ON AN " AS IS " BASIS , WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND , EITHER EXPRESS OR IMPLIED INCLUDING , WITHOUT
LIMITATION , ANY WARRANTIES OR CONDITIONS OF TITLE , NON - INFRINGEMENT ,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE . Each Recipient is
solely responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors , damage to or loss of data ,
programs or equipment , and unavailability or interruption of operations .
DISCLAIMER OF LIABILITY
NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
DAMAGES ( INCLUDING WITHOUT LIMITATION LOST PROFITS ) , HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
# include "linux_compat.h" /* linux-2.6 tweaks */
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/kdev_t.h>
# include <linux/blkdev.h>
# include <linux/delay.h> /* for mdelay */
# include <linux/interrupt.h> /* needed for in_interrupt() proto */
# include <linux/reboot.h> /* notifier code */
# include <linux/sched.h>
# include <linux/workqueue.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_tcq.h>
# include "mptbase.h"
# include "mptscsih.h"
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
# define my_NAME "Fusion MPT SPI Host driver"
# define my_VERSION MPT_LINUX_VERSION_COMMON
# define MYNAM "mptspi"
MODULE_AUTHOR ( MODULEAUTHOR ) ;
MODULE_DESCRIPTION ( my_NAME ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Command line args */
# ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
static int mpt_dv = MPTSCSIH_DOMAIN_VALIDATION ;
module_param ( mpt_dv , int , 0 ) ;
MODULE_PARM_DESC ( mpt_dv , " DV Algorithm: enhanced=1, basic=0 (default=MPTSCSIH_DOMAIN_VALIDATION=1) " ) ;
static int mpt_width = MPTSCSIH_MAX_WIDTH ;
module_param ( mpt_width , int , 0 ) ;
MODULE_PARM_DESC ( mpt_width , " Max Bus Width: wide=1, narrow=0 (default=MPTSCSIH_MAX_WIDTH=1) " ) ;
static ushort mpt_factor = MPTSCSIH_MIN_SYNC ;
module_param ( mpt_factor , ushort , 0 ) ;
MODULE_PARM_DESC ( mpt_factor , " Min Sync Factor (default=MPTSCSIH_MIN_SYNC=0x08) " ) ;
# endif
static int mpt_saf_te = MPTSCSIH_SAF_TE ;
module_param ( mpt_saf_te , int , 0 ) ;
MODULE_PARM_DESC ( mpt_saf_te , " Force enabling SEP Processor: enable=1 (default=MPTSCSIH_SAF_TE=0) " ) ;
static int mpt_pq_filter = 0 ;
module_param ( mpt_pq_filter , int , 0 ) ;
MODULE_PARM_DESC ( mpt_pq_filter , " Enable peripheral qualifier filter: enable=1 (default=0) " ) ;
static int mptspiDoneCtx = - 1 ;
static int mptspiTaskCtx = - 1 ;
static int mptspiInternalCtx = - 1 ; /* Used only for internal commands */
static struct scsi_host_template mptspi_driver_template = {
. proc_name = " mptspi " ,
. proc_info = mptscsih_proc_info ,
. name = " MPT SPI Host " ,
. info = mptscsih_info ,
. queuecommand = mptscsih_qcmd ,
. slave_alloc = mptscsih_slave_alloc ,
. slave_configure = mptscsih_slave_configure ,
. slave_destroy = mptscsih_slave_destroy ,
2005-06-24 12:18:57 -06:00
. change_queue_depth = mptscsih_change_queue_depth ,
2005-04-22 18:02:25 -04:00
. eh_abort_handler = mptscsih_abort ,
. eh_device_reset_handler = mptscsih_dev_reset ,
. eh_bus_reset_handler = mptscsih_bus_reset ,
. eh_host_reset_handler = mptscsih_host_reset ,
. bios_param = mptscsih_bios_param ,
. can_queue = MPT_SCSI_CAN_QUEUE ,
. this_id = - 1 ,
. sg_tablesize = MPT_SCSI_SG_DEPTH ,
. max_sectors = 8192 ,
. cmd_per_lun = 7 ,
. use_clustering = ENABLE_CLUSTERING ,
} ;
/****************************************************************************
* Supported hardware
*/
static struct pci_device_id mptspi_pci_table [ ] = {
{ PCI_VENDOR_ID_LSI_LOGIC , PCI_DEVICE_ID_LSI_53C1030 ,
PCI_ANY_ID , PCI_ANY_ID } ,
{ PCI_VENDOR_ID_LSI_LOGIC , PCI_DEVICE_ID_LSI_1030_53C1035 ,
PCI_ANY_ID , PCI_ANY_ID } ,
{ 0 } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , mptspi_pci_table ) ;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* mptspi_probe - Installs scsi devices per bus .
* @ pdev : Pointer to pci_dev structure
*
* Returns 0 for success , non - zero for failure .
*
*/
static int
mptspi_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
struct Scsi_Host * sh ;
MPT_SCSI_HOST * hd ;
MPT_ADAPTER * ioc ;
unsigned long flags ;
int sz , ii ;
int numSGE = 0 ;
int scale ;
int ioc_cap ;
u8 * mem ;
int error = 0 ;
int r ;
if ( ( r = mpt_attach ( pdev , id ) ) ! = 0 )
return r ;
ioc = pci_get_drvdata ( pdev ) ;
2005-04-30 17:09:38 -05:00
ioc - > DoneCtx = mptspiDoneCtx ;
ioc - > TaskCtx = mptspiTaskCtx ;
ioc - > InternalCtx = mptspiInternalCtx ;
2005-04-22 18:02:25 -04:00
/* Added sanity check on readiness of the MPT adapter.
*/
if ( ioc - > last_state ! = MPI_IOC_STATE_OPERATIONAL ) {
printk ( MYIOC_s_WARN_FMT
" Skipping because it's not operational! \n " ,
ioc - > name ) ;
return - ENODEV ;
}
if ( ! ioc - > active ) {
printk ( MYIOC_s_WARN_FMT " Skipping because it's disabled! \n " ,
ioc - > name ) ;
return - ENODEV ;
}
/* Sanity check - ensure at least 1 port is INITIATOR capable
*/
ioc_cap = 0 ;
for ( ii = 0 ; ii < ioc - > facts . NumberOfPorts ; ii + + ) {
if ( ioc - > pfacts [ ii ] . ProtocolFlags &
MPI_PORTFACTS_PROTOCOL_INITIATOR )
ioc_cap + + ;
}
if ( ! ioc_cap ) {
printk ( MYIOC_s_WARN_FMT
" Skipping ioc=%p because SCSI Initiator mode is NOT enabled! \n " ,
ioc - > name , ioc ) ;
return - ENODEV ;
}
sh = scsi_host_alloc ( & mptspi_driver_template , sizeof ( MPT_SCSI_HOST ) ) ;
if ( ! sh ) {
printk ( MYIOC_s_WARN_FMT
" Unable to register controller with SCSI subsystem \n " ,
ioc - > name ) ;
return - 1 ;
}
spin_lock_irqsave ( & ioc - > FreeQlock , flags ) ;
/* Attach the SCSI Host to the IOC structure
*/
ioc - > sh = sh ;
sh - > io_port = 0 ;
sh - > n_io_port = 0 ;
sh - > irq = 0 ;
/* set 16 byte cdb's */
sh - > max_cmd_len = 16 ;
/* Yikes! This is important!
* Otherwise , by default , linux
* only scans target IDs 0 - 7 !
* pfactsN - > MaxDevices unreliable
* ( not supported in early
* versions of the FW ) .
* max_id = 1 + actual max id ,
* max_lun = 1 + actual last lun ,
* see hosts . h : o (
*/
sh - > max_id = MPT_MAX_SCSI_DEVICES ;
sh - > max_lun = MPT_LAST_LUN + 1 ;
sh - > max_channel = 0 ;
sh - > this_id = ioc - > pfacts [ 0 ] . PortSCSIID ;
/* Required entry.
*/
sh - > unique_id = ioc - > id ;
/* Verify that we won't exceed the maximum
* number of chain buffers
* We can optimize : ZZ = req_sz / sizeof ( SGE )
* For 32 bit SGE ' s :
* numSGE = 1 + ( ZZ - 1 ) * ( maxChain - 1 ) + ZZ
* + ( req_sz - 64 ) / sizeof ( SGE )
* A slightly different algorithm is required for
* 64 bit SGEs .
*/
scale = ioc - > req_sz / ( sizeof ( dma_addr_t ) + sizeof ( u32 ) ) ;
if ( sizeof ( dma_addr_t ) = = sizeof ( u64 ) ) {
numSGE = ( scale - 1 ) *
( ioc - > facts . MaxChainDepth - 1 ) + scale +
( ioc - > req_sz - 60 ) / ( sizeof ( dma_addr_t ) +
sizeof ( u32 ) ) ;
} else {
numSGE = 1 + ( scale - 1 ) *
( ioc - > facts . MaxChainDepth - 1 ) + scale +
( ioc - > req_sz - 64 ) / ( sizeof ( dma_addr_t ) +
sizeof ( u32 ) ) ;
}
if ( numSGE < sh - > sg_tablesize ) {
/* Reset this value */
dprintk ( ( MYIOC_s_INFO_FMT
" Resetting sg_tablesize to %d from %d \n " ,
ioc - > name , numSGE , sh - > sg_tablesize ) ) ;
sh - > sg_tablesize = numSGE ;
}
spin_unlock_irqrestore ( & ioc - > FreeQlock , flags ) ;
hd = ( MPT_SCSI_HOST * ) sh - > hostdata ;
hd - > ioc = ioc ;
/* SCSI needs scsi_cmnd lookup table!
* ( with size equal to req_depth * PtrSz ! )
*/
sz = ioc - > req_depth * sizeof ( void * ) ;
mem = kmalloc ( sz , GFP_ATOMIC ) ;
if ( mem = = NULL ) {
error = - ENOMEM ;
goto mptspi_probe_failed ;
}
memset ( mem , 0 , sz ) ;
hd - > ScsiLookup = ( struct scsi_cmnd * * ) mem ;
dprintk ( ( MYIOC_s_INFO_FMT " ScsiLookup @ %p, sz=%d \n " ,
ioc - > name , hd - > ScsiLookup , sz ) ) ;
/* Allocate memory for the device structures.
* A non - Null pointer at an offset
* indicates a device exists .
* max_id = 1 + maximum id ( hosts . h )
*/
sz = sh - > max_id * sizeof ( void * ) ;
mem = kmalloc ( sz , GFP_ATOMIC ) ;
if ( mem = = NULL ) {
error = - ENOMEM ;
goto mptspi_probe_failed ;
}
memset ( mem , 0 , sz ) ;
hd - > Targets = ( VirtDevice * * ) mem ;
dprintk ( ( KERN_INFO
" Targets @ %p, sz=%d \n " , hd - > Targets , sz ) ) ;
/* Clear the TM flags
*/
hd - > tmPending = 0 ;
hd - > tmState = TM_STATE_NONE ;
hd - > resetPending = 0 ;
hd - > abortSCpnt = NULL ;
/* Clear the pointer used to store
* single - threaded commands , i . e . , those
* issued during a bus scan , dv and
* configuration pages .
*/
hd - > cmdPtr = NULL ;
/* Initialize this SCSI Hosts' timers
* To use , set the timer expires field
* and add_timer
*/
init_timer ( & hd - > timer ) ;
hd - > timer . data = ( unsigned long ) hd ;
hd - > timer . function = mptscsih_timer_expired ;
ioc - > spi_data . Saf_Te = mpt_saf_te ;
hd - > mpt_pq_filter = mpt_pq_filter ;
# ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
if ( ioc - > spi_data . maxBusWidth > mpt_width )
ioc - > spi_data . maxBusWidth = mpt_width ;
if ( ioc - > spi_data . minSyncFactor < mpt_factor )
ioc - > spi_data . minSyncFactor = mpt_factor ;
if ( ioc - > spi_data . minSyncFactor = = MPT_ASYNC ) {
ioc - > spi_data . maxSyncOffset = 0 ;
}
ioc - > spi_data . mpt_dv = mpt_dv ;
hd - > negoNvram = 0 ;
ddvprintk ( ( MYIOC_s_INFO_FMT
" dv %x width %x factor %x saf_te %x mpt_pq_filter %x \n " ,
ioc - > name ,
mpt_dv ,
mpt_width ,
mpt_factor ,
mpt_saf_te ,
mpt_pq_filter ) ) ;
# else
hd - > negoNvram = MPT_SCSICFG_USE_NVRAM ;
ddvprintk ( ( MYIOC_s_INFO_FMT
" saf_te %x mpt_pq_filter %x \n " ,
ioc - > name ,
mpt_saf_te ,
mpt_pq_filter ) ) ;
# endif
ioc - > spi_data . forceDv = 0 ;
ioc - > spi_data . noQas = 0 ;
for ( ii = 0 ; ii < MPT_MAX_SCSI_DEVICES ; ii + + )
ioc - > spi_data . dvStatus [ ii ] =
MPT_SCSICFG_NEGOTIATE ;
for ( ii = 0 ; ii < MPT_MAX_SCSI_DEVICES ; ii + + )
ioc - > spi_data . dvStatus [ ii ] | =
MPT_SCSICFG_DV_NOT_DONE ;
init_waitqueue_head ( & hd - > scandv_waitq ) ;
hd - > scandv_wait_done = 0 ;
hd - > last_queue_full = 0 ;
error = scsi_add_host ( sh , & ioc - > pcidev - > dev ) ;
if ( error ) {
dprintk ( ( KERN_ERR MYNAM
" scsi_add_host failed \n " ) ) ;
goto mptspi_probe_failed ;
}
scsi_scan_host ( sh ) ;
return 0 ;
mptspi_probe_failed :
mptscsih_remove ( pdev ) ;
return error ;
}
static struct pci_driver mptspi_driver = {
. name = " mptspi " ,
. id_table = mptspi_pci_table ,
. probe = mptspi_probe ,
. remove = __devexit_p ( mptscsih_remove ) ,
2005-06-23 17:35:56 -07:00
. shutdown = mptscsih_shutdown ,
2005-04-22 18:02:25 -04:00
# ifdef CONFIG_PM
. suspend = mptscsih_suspend ,
. resume = mptscsih_resume ,
# endif
} ;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mptspi_init - Register MPT adapter ( s ) as SCSI host ( s ) with
* linux scsi mid - layer .
*
* Returns 0 for success , non - zero for failure .
*/
static int __init
mptspi_init ( void )
{
show_mptmod_ver ( my_NAME , my_VERSION ) ;
mptspiDoneCtx = mpt_register ( mptscsih_io_done , MPTSPI_DRIVER ) ;
mptspiTaskCtx = mpt_register ( mptscsih_taskmgmt_complete , MPTSPI_DRIVER ) ;
mptspiInternalCtx = mpt_register ( mptscsih_scandv_complete , MPTSPI_DRIVER ) ;
if ( mpt_event_register ( mptspiDoneCtx , mptscsih_event_process ) = = 0 ) {
devtprintk ( ( KERN_INFO MYNAM
" : Registered for IOC event notifications \n " ) ) ;
}
if ( mpt_reset_register ( mptspiDoneCtx , mptscsih_ioc_reset ) = = 0 ) {
dprintk ( ( KERN_INFO MYNAM
" : Registered for IOC reset notifications \n " ) ) ;
}
return pci_register_driver ( & mptspi_driver ) ;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mptspi_exit - Unregisters MPT adapter ( s )
*
*/
static void __exit
mptspi_exit ( void )
{
pci_unregister_driver ( & mptspi_driver ) ;
mpt_reset_deregister ( mptspiDoneCtx ) ;
dprintk ( ( KERN_INFO MYNAM
" : Deregistered for IOC reset notifications \n " ) ) ;
mpt_event_deregister ( mptspiDoneCtx ) ;
dprintk ( ( KERN_INFO MYNAM
" : Deregistered for IOC event notifications \n " ) ) ;
mpt_deregister ( mptspiInternalCtx ) ;
mpt_deregister ( mptspiTaskCtx ) ;
mpt_deregister ( mptspiDoneCtx ) ;
}
module_init ( mptspi_init ) ;
module_exit ( mptspi_exit ) ;