2005-04-17 02:20:36 +04:00
/*
*
* Linux MegaRAID device driver
*
* Copyright ( c ) 2003 - 2004 LSI Logic Corporation .
*
* 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 ; either version
* 2 of the License , or ( at your option ) any later version .
*
* FILE : megaraid_mbox . c
2006-07-25 18:44:48 +04:00
* Version : v2 .20 .4 .9 ( Jul 16 2006 )
2005-04-17 02:20:36 +04:00
*
* Authors :
* Atul Mukker < Atul . Mukker @ lsil . com >
* Sreenivas Bagalkote < Sreenivas . Bagalkote @ lsil . com >
* Manoj Jose < Manoj . Jose @ lsil . com >
2005-11-17 21:17:25 +03:00
* Seokmann Ju < Seokmann . Ju @ lsil . com >
2005-04-17 02:20:36 +04:00
*
* List of supported controllers
*
* OEM Product Name VID DID SSVID SSID
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Dell PERC3 / QC 101 E 1960 1028 0471
* Dell PERC3 / DC 101 E 1960 1028 04 93
* Dell PERC3 / SC 101 E 1960 1028 0475
* Dell PERC3 / Di 1028 1960 1028 0123
* Dell PERC4 / SC 1000 1960 1028 0520
* Dell PERC4 / DC 1000 1960 1028 051 8
* Dell PERC4 / QC 1000 0407 1028 0531
* Dell PERC4 / Di 1028 000F 1028 014 A
* Dell PERC 4 e / Si 1028 0013 1028 016 c
* Dell PERC 4 e / Di 1028 0013 1028 016 d
* Dell PERC 4 e / Di 1028 0013 1028 016 e
* Dell PERC 4 e / Di 1028 0013 1028 016f
* Dell PERC 4 e / Di 1028 0013 1028 0170
* Dell PERC 4 e / DC 1000 040 8 1028 0002
* Dell PERC 4 e / SC 1000 040 8 1028 0001
*
*
* LSI MegaRAID SCSI 320 - 0 1000 1960 1000 A520
* LSI MegaRAID SCSI 320 - 1 1000 1960 1000 0520
* LSI MegaRAID SCSI 320 - 2 1000 1960 1000 051 8
* LSI MegaRAID SCSI 320 - 0 X 1000 0407 1000 0530
* LSI MegaRAID SCSI 320 - 2 X 1000 0407 1000 0532
* LSI MegaRAID SCSI 320 - 4 X 1000 0407 1000 0531
* LSI MegaRAID SCSI 320 - 1 E 1000 040 8 1000 0001
* LSI MegaRAID SCSI 320 - 2 E 1000 040 8 1000 0002
* LSI MegaRAID SATA 150 - 4 1000 1960 1000 4523
* LSI MegaRAID SATA 150 - 6 1000 1960 1000 0523
* LSI MegaRAID SATA 300 - 4 X 1000 040 9 1000 3004
* LSI MegaRAID SATA 300 - 8 X 1000 040 9 1000 3008
*
* INTEL RAID Controller SRCU42X 1000 0407 8086 0532
* INTEL RAID Controller SRCS16 1000 1960 8086 0523
* INTEL RAID Controller SRCU42E 1000 040 8 8086 0002
* INTEL RAID Controller SRCZCRX 1000 0407 8086 0530
* INTEL RAID Controller SRCS28X 1000 040 9 8086 3008
* INTEL RAID Controller SROMBU42E 1000 040 8 8086 3431
* INTEL RAID Controller SROMBU42E 1000 040 8 8086 3499
* INTEL RAID Controller SRCU51L 1000 1960 8086 0520
*
* FSC MegaRAID PCI Express ROMB 1000 040 8 1734 1065
*
* ACER MegaRAID ROMB - 2 E 1000 040 8 1025 004 D
*
* NEC MegaRAID PCI Express ROMB 1000 040 8 1033 8287
*
* For history of changes , see Documentation / ChangeLog . megaraid
*/
# include "megaraid_mbox.h"
static int megaraid_init ( void ) ;
static void megaraid_exit ( void ) ;
static int megaraid_probe_one ( struct pci_dev * , const struct pci_device_id * ) ;
static void megaraid_detach_one ( struct pci_dev * ) ;
2005-10-17 00:32:46 +04:00
static void megaraid_mbox_shutdown ( struct pci_dev * ) ;
2005-04-17 02:20:36 +04:00
static int megaraid_io_attach ( adapter_t * ) ;
static void megaraid_io_detach ( adapter_t * ) ;
static int megaraid_init_mbox ( adapter_t * ) ;
static void megaraid_fini_mbox ( adapter_t * ) ;
static int megaraid_alloc_cmd_packets ( adapter_t * ) ;
static void megaraid_free_cmd_packets ( adapter_t * ) ;
static int megaraid_mbox_setup_dma_pools ( adapter_t * ) ;
static void megaraid_mbox_teardown_dma_pools ( adapter_t * ) ;
static int megaraid_sysfs_alloc_resources ( adapter_t * ) ;
static void megaraid_sysfs_free_resources ( adapter_t * ) ;
static int megaraid_abort_handler ( struct scsi_cmnd * ) ;
static int megaraid_reset_handler ( struct scsi_cmnd * ) ;
static int mbox_post_sync_cmd ( adapter_t * , uint8_t [ ] ) ;
static int mbox_post_sync_cmd_fast ( adapter_t * , uint8_t [ ] ) ;
static int megaraid_busywait_mbox ( mraid_device_t * ) ;
static int megaraid_mbox_product_info ( adapter_t * ) ;
static int megaraid_mbox_extended_cdb ( adapter_t * ) ;
static int megaraid_mbox_support_ha ( adapter_t * , uint16_t * ) ;
static int megaraid_mbox_support_random_del ( adapter_t * ) ;
static int megaraid_mbox_get_max_sg ( adapter_t * ) ;
static void megaraid_mbox_enum_raid_scsi ( adapter_t * ) ;
static void megaraid_mbox_flush_cache ( adapter_t * ) ;
static void megaraid_mbox_display_scb ( adapter_t * , scb_t * ) ;
static void megaraid_mbox_setup_device_map ( adapter_t * ) ;
static int megaraid_queue_command ( struct scsi_cmnd * ,
void ( * ) ( struct scsi_cmnd * ) ) ;
static scb_t * megaraid_mbox_build_cmd ( adapter_t * , struct scsi_cmnd * , int * ) ;
static void megaraid_mbox_runpendq ( adapter_t * , scb_t * ) ;
static void megaraid_mbox_prepare_pthru ( adapter_t * , scb_t * ,
struct scsi_cmnd * ) ;
static void megaraid_mbox_prepare_epthru ( adapter_t * , scb_t * ,
struct scsi_cmnd * ) ;
static irqreturn_t megaraid_isr ( int , void * , struct pt_regs * ) ;
static void megaraid_mbox_dpc ( unsigned long ) ;
static ssize_t megaraid_sysfs_show_app_hndl ( struct class_device * , char * ) ;
2005-05-17 14:43:37 +04:00
static ssize_t megaraid_sysfs_show_ldnum ( struct device * , struct device_attribute * attr , char * ) ;
2005-04-17 02:20:36 +04:00
static int megaraid_cmm_register ( adapter_t * ) ;
static int megaraid_cmm_unregister ( adapter_t * ) ;
static int megaraid_mbox_mm_handler ( unsigned long , uioc_t * , uint32_t ) ;
static int megaraid_mbox_mm_command ( adapter_t * , uioc_t * ) ;
static void megaraid_mbox_mm_done ( adapter_t * , scb_t * ) ;
static int gather_hbainfo ( adapter_t * , mraid_hba_info_t * ) ;
static int wait_till_fw_empty ( adapter_t * ) ;
2005-11-17 21:17:25 +03:00
MODULE_AUTHOR ( " sju@lsil.com " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " LSI Logic MegaRAID Mailbox Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( MEGARAID_VERSION ) ;
/*
* # # # modules parameters for driver # # #
*/
/**
* Set to enable driver to expose unconfigured disk to kernel
*/
static int megaraid_expose_unconf_disks = 0 ;
module_param_named ( unconf_disks , megaraid_expose_unconf_disks , int , 0 ) ;
MODULE_PARM_DESC ( unconf_disks ,
" Set to expose unconfigured disks to kernel (default=0) " ) ;
/**
* driver wait time if the adapter ' s mailbox is busy
*/
static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT ;
module_param_named ( busy_wait , max_mbox_busy_wait , int , 0 ) ;
MODULE_PARM_DESC ( busy_wait ,
" Max wait for mailbox in microseconds if busy (default=10) " ) ;
/**
* number of sectors per IO command
*/
static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS ;
module_param_named ( max_sectors , megaraid_max_sectors , int , 0 ) ;
MODULE_PARM_DESC ( max_sectors ,
" Maximum number of sectors per IO command (default=128) " ) ;
/**
* number of commands per logical unit
*/
static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN ;
module_param_named ( cmd_per_lun , megaraid_cmd_per_lun , int , 0 ) ;
MODULE_PARM_DESC ( cmd_per_lun ,
" Maximum number of commands per logical unit (default=64) " ) ;
/**
* Fast driver load option , skip scanning for physical devices during load .
* This would result in non - disk devices being skipped during driver load
* time . These can be later added though , using / proc / scsi / scsi
*/
static unsigned int megaraid_fast_load = 0 ;
module_param_named ( fast_load , megaraid_fast_load , int , 0 ) ;
MODULE_PARM_DESC ( fast_load ,
" Faster loading of the driver, skips physical devices! (default=0) " ) ;
/**
* mraid_debug level - threshold for amount of information to be displayed by
* the driver . This level can be changed through modules parameters , ioctl or
* sysfs / proc interface . By default , print the announcement messages only .
*/
int mraid_debug_level = CL_ANN ;
module_param_named ( debug_level , mraid_debug_level , int , 0 ) ;
MODULE_PARM_DESC ( debug_level , " Debug level for driver (default=0) " ) ;
/*
* # # # global data # # #
*/
static uint8_t megaraid_mbox_version [ 8 ] =
2005-05-17 02:32:17 +04:00
{ 0x02 , 0x20 , 0x04 , 0x06 , 3 , 7 , 20 , 5 } ;
2005-04-17 02:20:36 +04:00
/*
* PCI table for all supported controllers .
*/
static struct pci_device_id pci_id_table_g [ ] = {
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4_DI_DISCOVERY ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4_DI_DISCOVERY ,
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
PCI_DEVICE_ID_PERC4_SC ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4_SC ,
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
PCI_DEVICE_ID_PERC4_DC ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4_DC ,
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
2005-05-17 02:32:17 +04:00
PCI_DEVICE_ID_VERDE ,
PCI_ANY_ID ,
PCI_ANY_ID ,
2005-04-17 02:20:36 +04:00
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4_DI_EVERGLADES ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4_DI_EVERGLADES ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4E_SI_BIGBEND ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4E_SI_BIGBEND ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4E_DI_KOBUK ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4E_DI_KOBUK ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4E_DI_CORVETTE ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4E_DI_CORVETTE ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4E_DI_EXPEDITION ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_PERC4E_DI_GUADALUPE ,
PCI_VENDOR_ID_DELL ,
PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE ,
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
2005-05-17 02:32:17 +04:00
PCI_DEVICE_ID_DOBSON ,
PCI_ANY_ID ,
PCI_ANY_ID ,
2005-04-17 02:20:36 +04:00
} ,
{
PCI_VENDOR_ID_AMI ,
PCI_DEVICE_ID_AMI_MEGARAID3 ,
2005-11-17 21:17:25 +03:00
PCI_ANY_ID ,
PCI_ANY_ID ,
2005-04-17 02:20:36 +04:00
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
2005-11-17 21:17:25 +03:00
PCI_DEVICE_ID_AMI_MEGARAID3 ,
PCI_ANY_ID ,
PCI_ANY_ID ,
2005-04-17 02:20:36 +04:00
} ,
{
PCI_VENDOR_ID_LSI_LOGIC ,
2005-05-17 02:32:17 +04:00
PCI_DEVICE_ID_LINDSAY ,
PCI_ANY_ID ,
PCI_ANY_ID ,
2005-04-17 02:20:36 +04:00
} ,
{ 0 } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , pci_id_table_g ) ;
static struct pci_driver megaraid_pci_driver_g = {
. name = " megaraid " ,
. id_table = pci_id_table_g ,
. probe = megaraid_probe_one ,
. remove = __devexit_p ( megaraid_detach_one ) ,
2005-10-17 00:32:46 +04:00
. shutdown = megaraid_mbox_shutdown ,
2005-04-17 02:20:36 +04:00
} ;
// definitions for the device attributes for exporting logical drive number
// for a scsi address (Host, Channel, Id, Lun)
CLASS_DEVICE_ATTR ( megaraid_mbox_app_hndl , S_IRUSR , megaraid_sysfs_show_app_hndl ,
NULL ) ;
// Host template initializer for megaraid mbox sysfs device attributes
static struct class_device_attribute * megaraid_shost_attrs [ ] = {
& class_device_attr_megaraid_mbox_app_hndl ,
NULL ,
} ;
DEVICE_ATTR ( megaraid_mbox_ld , S_IRUSR , megaraid_sysfs_show_ldnum , NULL ) ;
// Host template initializer for megaraid mbox sysfs device attributes
static struct device_attribute * megaraid_sdev_attrs [ ] = {
& dev_attr_megaraid_mbox_ld ,
NULL ,
} ;
2006-07-06 02:00:01 +04:00
/**
* megaraid_change_queue_depth - Change the device ' s queue depth
* @ sdev : scsi device struct
* @ qdepth : depth to set
*
* Return value :
* actual depth set
* */
static int megaraid_change_queue_depth ( struct scsi_device * sdev , int qdepth )
{
if ( qdepth > MBOX_MAX_SCSI_CMDS )
qdepth = MBOX_MAX_SCSI_CMDS ;
scsi_adjust_queue_depth ( sdev , 0 , qdepth ) ;
return sdev - > queue_depth ;
}
2005-04-17 02:20:36 +04:00
/*
* Scsi host template for megaraid unified driver
*/
static struct scsi_host_template megaraid_template_g = {
. module = THIS_MODULE ,
. name = " LSI Logic MegaRAID driver " ,
. proc_name = " megaraid " ,
. queuecommand = megaraid_queue_command ,
. eh_abort_handler = megaraid_abort_handler ,
. eh_device_reset_handler = megaraid_reset_handler ,
. eh_bus_reset_handler = megaraid_reset_handler ,
. eh_host_reset_handler = megaraid_reset_handler ,
2006-07-06 02:00:01 +04:00
. change_queue_depth = megaraid_change_queue_depth ,
2005-04-17 02:20:36 +04:00
. use_clustering = ENABLE_CLUSTERING ,
. sdev_attrs = megaraid_sdev_attrs ,
. shost_attrs = megaraid_shost_attrs ,
} ;
/**
* megaraid_init - module load hook
*
* We register ourselves as hotplug enabled module and let PCI subsystem
* discover our adaters
* */
static int __init
megaraid_init ( void )
{
int rval ;
// Announce the driver version
con_log ( CL_ANN , ( KERN_INFO " megaraid: %s %s \n " , MEGARAID_VERSION ,
MEGARAID_EXT_VERSION ) ) ;
// check validity of module parameters
if ( megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mailbox: max commands per lun reset to %d \n " ,
MBOX_MAX_SCSI_CMDS ) ) ;
megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS ;
}
// register as a PCI hot-plug driver module
2005-05-17 02:32:17 +04:00
rval = pci_register_driver ( & megaraid_pci_driver_g ) ;
if ( rval < 0 ) {
2005-04-17 02:20:36 +04:00
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: could not register hotplug support. \n " ) ) ;
}
return rval ;
}
/**
* megaraid_exit - driver unload entry point
*
* We simply unwrap the megaraid_init routine here
*/
static void __exit
megaraid_exit ( void )
{
con_log ( CL_DLEVEL1 , ( KERN_NOTICE " megaraid: unloading framework \n " ) ) ;
// unregister as PCI hotplug driver
pci_unregister_driver ( & megaraid_pci_driver_g ) ;
return ;
}
/**
* megaraid_probe_one - PCI hotplug entry point
* @ param pdev : handle to this controller ' s PCI configuration space
* @ param id : pci device id of the class of controllers
*
* This routine should be called whenever a new adapter is detected by the
* PCI hotplug susbsytem .
* */
static int __devinit
megaraid_probe_one ( struct pci_dev * pdev , const struct pci_device_id * id )
{
adapter_t * adapter ;
// detected a new controller
con_log ( CL_ANN , ( KERN_INFO
" megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: " ,
pdev - > vendor , pdev - > device , pdev - > subsystem_vendor ,
pdev - > subsystem_device ) ) ;
con_log ( CL_ANN , ( " bus %d:slot %d:func %d \n " , pdev - > bus - > number ,
PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) ) ) ;
if ( pci_enable_device ( pdev ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: pci_enable_device failed \n " ) ) ;
return - ENODEV ;
}
// Enable bus-mastering on this controller
pci_set_master ( pdev ) ;
// Allocate the per driver initialization structure
adapter = kmalloc ( sizeof ( adapter_t ) , GFP_KERNEL ) ;
if ( adapter = = NULL ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d. \n " , __FUNCTION__ , __LINE__ ) ) ;
goto out_probe_one ;
}
memset ( adapter , 0 , sizeof ( adapter_t ) ) ;
// set up PCI related soft state and other pre-known parameters
adapter - > unique_id = pdev - > bus - > number < < 8 | pdev - > devfn ;
adapter - > irq = pdev - > irq ;
adapter - > pdev = pdev ;
atomic_set ( & adapter - > being_detached , 0 ) ;
// Setup the default DMA mask. This would be changed later on
// depending on hardware capabilities
2005-05-17 02:32:17 +04:00
if ( pci_set_dma_mask ( adapter - > pdev , DMA_32BIT_MASK ) ! = 0 ) {
2005-04-17 02:20:36 +04:00
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: pci_set_dma_mask failed:%d \n " , __LINE__ ) ) ;
goto out_free_adapter ;
}
// Initialize the synchronization lock for kernel and LLD
spin_lock_init ( & adapter - > lock ) ;
// Initialize the command queues: the list of free SCBs and the list
// of pending SCBs.
INIT_LIST_HEAD ( & adapter - > kscb_pool ) ;
spin_lock_init ( SCSI_FREE_LIST_LOCK ( adapter ) ) ;
INIT_LIST_HEAD ( & adapter - > pend_list ) ;
spin_lock_init ( PENDING_LIST_LOCK ( adapter ) ) ;
INIT_LIST_HEAD ( & adapter - > completed_list ) ;
spin_lock_init ( COMPLETED_LIST_LOCK ( adapter ) ) ;
// Start the mailbox based controller
if ( megaraid_init_mbox ( adapter ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: maibox adapter did not initialize \n " ) ) ;
goto out_free_adapter ;
}
// Register with LSI Common Management Module
if ( megaraid_cmm_register ( adapter ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: could not register with management module \n " ) ) ;
goto out_fini_mbox ;
}
// setup adapter handle in PCI soft state
pci_set_drvdata ( pdev , adapter ) ;
// attach with scsi mid-layer
if ( megaraid_io_attach ( adapter ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING " megaraid: io attach failed \n " ) ) ;
goto out_cmm_unreg ;
}
return 0 ;
out_cmm_unreg :
pci_set_drvdata ( pdev , NULL ) ;
megaraid_cmm_unregister ( adapter ) ;
out_fini_mbox :
megaraid_fini_mbox ( adapter ) ;
out_free_adapter :
kfree ( adapter ) ;
out_probe_one :
pci_disable_device ( pdev ) ;
return - ENODEV ;
}
/**
* megaraid_detach_one - release the framework resources and call LLD release
* routine
* @ param pdev : handle for our PCI cofiguration space
*
* This routine is called during driver unload . We free all the allocated
* resources and call the corresponding LLD so that it can also release all
* its resources .
*
* This routine is also called from the PCI hotplug system
* */
static void
megaraid_detach_one ( struct pci_dev * pdev )
{
adapter_t * adapter ;
struct Scsi_Host * host ;
// Start a rollback on this adapter
adapter = pci_get_drvdata ( pdev ) ;
if ( ! adapter ) {
con_log ( CL_ANN , ( KERN_CRIT
" megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x \n " ,
pdev - > vendor , pdev - > device , pdev - > subsystem_vendor ,
pdev - > subsystem_device ) ) ;
return ;
}
else {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x \n " ,
pdev - > vendor , pdev - > device , pdev - > subsystem_vendor ,
pdev - > subsystem_device ) ) ;
}
host = adapter - > host ;
// do not allow any more requests from the management module for this
// adapter.
// FIXME: How do we account for the request which might still be
// pending with us?
atomic_set ( & adapter - > being_detached , 1 ) ;
// detach from the IO sub-system
megaraid_io_detach ( adapter ) ;
// reset the device state in the PCI structure. We check this
// condition when we enter here. If the device state is NULL,
// that would mean the device has already been removed
pci_set_drvdata ( pdev , NULL ) ;
// Unregister from common management module
//
// FIXME: this must return success or failure for conditions if there
// is a command pending with LLD or not.
megaraid_cmm_unregister ( adapter ) ;
// finalize the mailbox based controller and release all resources
megaraid_fini_mbox ( adapter ) ;
kfree ( adapter ) ;
scsi_host_put ( host ) ;
pci_disable_device ( pdev ) ;
return ;
}
/**
* megaraid_mbox_shutdown - PCI shutdown for megaraid HBA
* @ param device : generice driver model device
*
* Shutdown notification , perform flush cache
*/
static void
2005-10-17 00:32:46 +04:00
megaraid_mbox_shutdown ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2005-10-17 00:32:46 +04:00
adapter_t * adapter = pci_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
static int counter ;
if ( ! adapter ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: null device in shutdown \n " ) ) ;
return ;
}
// flush caches now
con_log ( CL_ANN , ( KERN_INFO " megaraid: flushing adapter %d... " ,
counter + + ) ) ;
megaraid_mbox_flush_cache ( adapter ) ;
con_log ( CL_ANN , ( " done \n " ) ) ;
}
/**
* megaraid_io_attach - attach a device with the IO subsystem
* @ param adapter : controller ' s soft state
*
* Attach this device with the IO subsystem
* */
static int
megaraid_io_attach ( adapter_t * adapter )
{
struct Scsi_Host * host ;
// Initialize SCSI Host structure
host = scsi_host_alloc ( & megaraid_template_g , 8 ) ;
if ( ! host ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mbox: scsi_register failed \n " ) ) ;
return - 1 ;
}
SCSIHOST2ADAP ( host ) = ( caddr_t ) adapter ;
adapter - > host = host ;
host - > irq = adapter - > irq ;
host - > unique_id = adapter - > unique_id ;
host - > can_queue = adapter - > max_cmds ;
host - > this_id = adapter - > init_id ;
host - > sg_tablesize = adapter - > sglen ;
host - > max_sectors = adapter - > max_sectors ;
host - > cmd_per_lun = adapter - > cmd_per_lun ;
host - > max_channel = adapter - > max_channel ;
host - > max_id = adapter - > max_target ;
host - > max_lun = adapter - > max_lun ;
// notify mid-layer about the new controller
if ( scsi_add_host ( host , & adapter - > pdev - > dev ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mbox: scsi_add_host failed \n " ) ) ;
scsi_host_put ( host ) ;
return - 1 ;
}
scsi_scan_host ( host ) ;
return 0 ;
}
/**
* megaraid_io_detach - detach a device from the IO subsystem
* @ param adapter : controller ' s soft state
*
* Detach this device from the IO subsystem
* */
static void
megaraid_io_detach ( adapter_t * adapter )
{
struct Scsi_Host * host ;
con_log ( CL_DLEVEL1 , ( KERN_INFO " megaraid: io detach \n " ) ) ;
host = adapter - > host ;
scsi_remove_host ( host ) ;
return ;
}
/*
* START : Mailbox Low Level Driver
*
* This is section specific to the single mailbox based controllers
*/
/**
* megaraid_init_mbox - initialize controller
* @ param adapter - our soft state
*
* . Allocate 16 - byte aligned mailbox memory for firmware handshake
* . Allocate controller ' s memory resources
* . Find out all initialization data
* . Allocate memory required for all the commands
* . Use internal library of FW routines , build up complete soft state
*/
2006-06-25 16:48:05 +04:00
static int __devinit
2005-04-17 02:20:36 +04:00
megaraid_init_mbox ( adapter_t * adapter )
{
struct pci_dev * pdev ;
mraid_device_t * raid_dev ;
int i ;
2006-07-25 18:44:48 +04:00
uint32_t magic64 ;
2005-04-17 02:20:36 +04:00
adapter - > ito = MBOX_TIMEOUT ;
pdev = adapter - > pdev ;
/*
* Allocate and initialize the init data structure for mailbox
* controllers
*/
raid_dev = kmalloc ( sizeof ( mraid_device_t ) , GFP_KERNEL ) ;
if ( raid_dev = = NULL ) return - 1 ;
memset ( raid_dev , 0 , sizeof ( mraid_device_t ) ) ;
/*
* Attach the adapter soft state to raid device soft state
*/
adapter - > raid_device = ( caddr_t ) raid_dev ;
raid_dev - > fast_load = megaraid_fast_load ;
// our baseport
raid_dev - > baseport = pci_resource_start ( pdev , 0 ) ;
if ( pci_request_regions ( pdev , " MegaRAID: LSI Logic Corporation " ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: mem region busy \n " ) ) ;
goto out_free_raid_dev ;
}
raid_dev - > baseaddr = ioremap_nocache ( raid_dev - > baseport , 128 ) ;
if ( ! raid_dev - > baseaddr ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: could not map hba memory \n " ) ) ;
goto out_release_regions ;
}
//
// Setup the rest of the soft state using the library of FW routines
//
// request IRQ and register the interrupt service routine
2006-07-02 06:29:42 +04:00
if ( request_irq ( adapter - > irq , megaraid_isr , IRQF_SHARED , " megaraid " ,
2005-04-17 02:20:36 +04:00
adapter ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: Couldn't register IRQ %d! \n " , adapter - > irq ) ) ;
goto out_iounmap ;
}
// initialize the mutual exclusion lock for the mailbox
spin_lock_init ( & raid_dev - > mailbox_lock ) ;
// allocate memory required for commands
if ( megaraid_alloc_cmd_packets ( adapter ) ! = 0 ) {
goto out_free_irq ;
}
// Product info
if ( megaraid_mbox_product_info ( adapter ) ! = 0 ) {
goto out_alloc_cmds ;
}
// Do we support extended CDBs
adapter - > max_cdb_sz = 10 ;
if ( megaraid_mbox_extended_cdb ( adapter ) = = 0 ) {
adapter - > max_cdb_sz = 16 ;
}
/*
* Do we support cluster environment , if we do , what is the initiator
* id .
* NOTE : In a non - cluster aware firmware environment , the LLD should
* return 7 as initiator id .
*/
adapter - > ha = 0 ;
adapter - > init_id = - 1 ;
if ( megaraid_mbox_support_ha ( adapter , & adapter - > init_id ) = = 0 ) {
adapter - > ha = 1 ;
}
/*
* Prepare the device ids array to have the mapping between the kernel
* device address and megaraid device address .
* We export the physical devices on their actual addresses . The
* logical drives are exported on a virtual SCSI channel
*/
megaraid_mbox_setup_device_map ( adapter ) ;
// If the firmware supports random deletion, update the device id map
if ( megaraid_mbox_support_random_del ( adapter ) ) {
// Change the logical drives numbers in device_ids array one
// slot in device_ids is reserved for target id, that's why
// "<=" below
for ( i = 0 ; i < = MAX_LOGICAL_DRIVES_40LD ; i + + ) {
adapter - > device_ids [ adapter - > max_channel ] [ i ] + = 0x80 ;
}
adapter - > device_ids [ adapter - > max_channel ] [ adapter - > init_id ] =
0xFF ;
raid_dev - > random_del_supported = 1 ;
}
/*
* find out the maximum number of scatter - gather elements supported by
* this firmware
*/
adapter - > sglen = megaraid_mbox_get_max_sg ( adapter ) ;
// enumerate RAID and SCSI channels so that all devices on SCSI
// channels can later be exported, including disk devices
megaraid_mbox_enum_raid_scsi ( adapter ) ;
/*
* Other parameters required by upper layer
*
* maximum number of sectors per IO command
*/
adapter - > max_sectors = megaraid_max_sectors ;
/*
* number of queued commands per LUN .
*/
adapter - > cmd_per_lun = megaraid_cmd_per_lun ;
/*
* Allocate resources required to issue FW calls , when sysfs is
* accessed
*/
if ( megaraid_sysfs_alloc_resources ( adapter ) ! = 0 ) {
goto out_alloc_cmds ;
}
// Set the DMA mask to 64-bit. All supported controllers as capable of
// DMA in this range
2006-07-25 18:44:48 +04:00
pci_read_config_dword ( adapter - > pdev , PCI_CONF_AMISIG64 , & magic64 ) ;
if ( ( ( magic64 = = HBA_SIGNATURE_64_BIT ) & &
( ( adapter - > pdev - > subsystem_device ! =
PCI_SUBSYS_ID_MEGARAID_SATA_150_6 ) | |
( adapter - > pdev - > subsystem_device ! =
PCI_SUBSYS_ID_MEGARAID_SATA_150_4 ) ) ) | |
( adapter - > pdev - > vendor = = PCI_VENDOR_ID_LSI_LOGIC & &
adapter - > pdev - > device = = PCI_DEVICE_ID_VERDE ) | |
( adapter - > pdev - > vendor = = PCI_VENDOR_ID_LSI_LOGIC & &
adapter - > pdev - > device = = PCI_DEVICE_ID_DOBSON ) | |
( adapter - > pdev - > vendor = = PCI_VENDOR_ID_LSI_LOGIC & &
adapter - > pdev - > device = = PCI_DEVICE_ID_LINDSAY ) | |
( adapter - > pdev - > vendor = = PCI_VENDOR_ID_DELL & &
adapter - > pdev - > device = = PCI_DEVICE_ID_PERC4_DI_EVERGLADES ) | |
( adapter - > pdev - > vendor = = PCI_VENDOR_ID_DELL & &
adapter - > pdev - > device = = PCI_DEVICE_ID_PERC4E_DI_KOBUK ) ) {
if ( pci_set_dma_mask ( adapter - > pdev , DMA_64BIT_MASK ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: DMA mask for 64-bit failed \n " ) ) ;
if ( pci_set_dma_mask ( adapter - > pdev , DMA_32BIT_MASK ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: 32-bit DMA mask failed \n " ) ) ;
goto out_free_sysfs_res ;
}
}
2005-04-17 02:20:36 +04:00
}
// setup tasklet for DPC
tasklet_init ( & adapter - > dpc_h , megaraid_mbox_dpc ,
( unsigned long ) adapter ) ;
con_log ( CL_DLEVEL1 , ( KERN_INFO
" megaraid mbox hba successfully initialized \n " ) ) ;
return 0 ;
out_free_sysfs_res :
megaraid_sysfs_free_resources ( adapter ) ;
out_alloc_cmds :
megaraid_free_cmd_packets ( adapter ) ;
out_free_irq :
free_irq ( adapter - > irq , adapter ) ;
out_iounmap :
iounmap ( raid_dev - > baseaddr ) ;
out_release_regions :
pci_release_regions ( pdev ) ;
out_free_raid_dev :
kfree ( raid_dev ) ;
return - 1 ;
}
/**
* megaraid_fini_mbox - undo controller initialization
* @ param adapter : our soft state
*/
static void
megaraid_fini_mbox ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
// flush all caches
megaraid_mbox_flush_cache ( adapter ) ;
tasklet_kill ( & adapter - > dpc_h ) ;
megaraid_sysfs_free_resources ( adapter ) ;
megaraid_free_cmd_packets ( adapter ) ;
free_irq ( adapter - > irq , adapter ) ;
iounmap ( raid_dev - > baseaddr ) ;
pci_release_regions ( adapter - > pdev ) ;
kfree ( raid_dev ) ;
return ;
}
/**
* megaraid_alloc_cmd_packets - allocate shared mailbox
* @ param adapter : soft state of the raid controller
*
* Allocate and align the shared mailbox . This maibox is used to issue
* all the commands . For IO based controllers , the mailbox is also regsitered
* with the FW . Allocate memory for all commands as well .
* This is our big allocator
*/
static int
megaraid_alloc_cmd_packets ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
struct pci_dev * pdev ;
unsigned long align ;
scb_t * scb ;
mbox_ccb_t * ccb ;
struct mraid_pci_blk * epthru_pci_blk ;
struct mraid_pci_blk * sg_pci_blk ;
struct mraid_pci_blk * mbox_pci_blk ;
int i ;
pdev = adapter - > pdev ;
/*
* Setup the mailbox
* Allocate the common 16 - byte aligned memory for the handshake
* mailbox .
*/
raid_dev - > una_mbox64 = pci_alloc_consistent ( adapter - > pdev ,
sizeof ( mbox64_t ) , & raid_dev - > una_mbox64_dma ) ;
if ( ! raid_dev - > una_mbox64 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
return - 1 ;
}
memset ( raid_dev - > una_mbox64 , 0 , sizeof ( mbox64_t ) ) ;
/*
* Align the mailbox at 16 - byte boundary
*/
raid_dev - > mbox = & raid_dev - > una_mbox64 - > mbox32 ;
raid_dev - > mbox = ( mbox_t * ) ( ( ( ( unsigned long ) raid_dev - > mbox ) + 15 ) &
( ~ 0UL ^ 0xFUL ) ) ;
raid_dev - > mbox64 = ( mbox64_t * ) ( ( ( unsigned long ) raid_dev - > mbox ) - 8 ) ;
align = ( ( void * ) raid_dev - > mbox -
( ( void * ) & raid_dev - > una_mbox64 - > mbox32 ) ) ;
raid_dev - > mbox_dma = ( unsigned long ) raid_dev - > una_mbox64_dma + 8 +
align ;
// Allocate memory for commands issued internally
adapter - > ibuf = pci_alloc_consistent ( pdev , MBOX_IBUF_SIZE ,
& adapter - > ibuf_dma_h ) ;
if ( ! adapter - > ibuf ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
goto out_free_common_mbox ;
}
memset ( adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
// Allocate memory for our SCSI Command Blocks and their associated
// memory
/*
* Allocate memory for the base list of scb . Later allocate memory for
* CCBs and embedded components of each CCB and point the pointers in
* scb to the allocated components
* NOTE : The code to allocate SCB will be duplicated in all the LLD
* since the calling routine does not yet know the number of available
* commands .
*/
adapter - > kscb_list = kmalloc ( sizeof ( scb_t ) * MBOX_MAX_SCSI_CMDS ,
GFP_KERNEL ) ;
if ( adapter - > kscb_list = = NULL ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
goto out_free_ibuf ;
}
memset ( adapter - > kscb_list , 0 , sizeof ( scb_t ) * MBOX_MAX_SCSI_CMDS ) ;
// memory allocation for our command packets
if ( megaraid_mbox_setup_dma_pools ( adapter ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
goto out_free_scb_list ;
}
// Adjust the scb pointers and link in the free pool
epthru_pci_blk = raid_dev - > epthru_pool ;
sg_pci_blk = raid_dev - > sg_pool ;
mbox_pci_blk = raid_dev - > mbox_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS ; i + + ) {
scb = adapter - > kscb_list + i ;
ccb = raid_dev - > ccb_list + i ;
ccb - > mbox = ( mbox_t * ) ( mbox_pci_blk [ i ] . vaddr + 16 ) ;
ccb - > raw_mbox = ( uint8_t * ) ccb - > mbox ;
ccb - > mbox64 = ( mbox64_t * ) ( mbox_pci_blk [ i ] . vaddr + 8 ) ;
ccb - > mbox_dma_h = ( unsigned long ) mbox_pci_blk [ i ] . dma_addr + 16 ;
// make sure the mailbox is aligned properly
if ( ccb - > mbox_dma_h & 0x0F ) {
con_log ( CL_ANN , ( KERN_CRIT
" megaraid mbox: not aligned on 16-bytes \n " ) ) ;
goto out_teardown_dma_pools ;
}
ccb - > epthru = ( mraid_epassthru_t * )
epthru_pci_blk [ i ] . vaddr ;
ccb - > epthru_dma_h = epthru_pci_blk [ i ] . dma_addr ;
ccb - > pthru = ( mraid_passthru_t * ) ccb - > epthru ;
ccb - > pthru_dma_h = ccb - > epthru_dma_h ;
ccb - > sgl64 = ( mbox_sgl64 * ) sg_pci_blk [ i ] . vaddr ;
ccb - > sgl_dma_h = sg_pci_blk [ i ] . dma_addr ;
ccb - > sgl32 = ( mbox_sgl32 * ) ccb - > sgl64 ;
scb - > ccb = ( caddr_t ) ccb ;
scb - > gp = 0 ;
scb - > sno = i ; // command index
scb - > scp = NULL ;
scb - > state = SCB_FREE ;
scb - > dma_direction = PCI_DMA_NONE ;
scb - > dma_type = MRAID_DMA_NONE ;
scb - > dev_channel = - 1 ;
scb - > dev_target = - 1 ;
// put scb in the free pool
list_add_tail ( & scb - > list , & adapter - > kscb_pool ) ;
}
return 0 ;
out_teardown_dma_pools :
megaraid_mbox_teardown_dma_pools ( adapter ) ;
out_free_scb_list :
kfree ( adapter - > kscb_list ) ;
out_free_ibuf :
pci_free_consistent ( pdev , MBOX_IBUF_SIZE , ( void * ) adapter - > ibuf ,
adapter - > ibuf_dma_h ) ;
out_free_common_mbox :
pci_free_consistent ( adapter - > pdev , sizeof ( mbox64_t ) ,
( caddr_t ) raid_dev - > una_mbox64 , raid_dev - > una_mbox64_dma ) ;
return - 1 ;
}
/**
* megaraid_free_cmd_packets - free memory
* @ param adapter : soft state of the raid controller
*
* Release memory resources allocated for commands
*/
static void
megaraid_free_cmd_packets ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
megaraid_mbox_teardown_dma_pools ( adapter ) ;
kfree ( adapter - > kscb_list ) ;
pci_free_consistent ( adapter - > pdev , MBOX_IBUF_SIZE ,
( void * ) adapter - > ibuf , adapter - > ibuf_dma_h ) ;
pci_free_consistent ( adapter - > pdev , sizeof ( mbox64_t ) ,
( caddr_t ) raid_dev - > una_mbox64 , raid_dev - > una_mbox64_dma ) ;
return ;
}
/**
* megaraid_mbox_setup_dma_pools - setup dma pool for command packets
* @ param adapter : HBA soft state
*
* setup the dma pools for mailbox , passthru and extended passthru structures ,
* and scatter - gather lists
*/
static int
megaraid_mbox_setup_dma_pools ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
struct mraid_pci_blk * epthru_pci_blk ;
struct mraid_pci_blk * sg_pci_blk ;
struct mraid_pci_blk * mbox_pci_blk ;
int i ;
// Allocate memory for 16-bytes aligned mailboxes
raid_dev - > mbox_pool_handle = pci_pool_create ( " megaraid mbox pool " ,
adapter - > pdev ,
sizeof ( mbox64_t ) + 16 ,
16 , 0 ) ;
if ( raid_dev - > mbox_pool_handle = = NULL ) {
goto fail_setup_dma_pool ;
}
mbox_pci_blk = raid_dev - > mbox_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS ; i + + ) {
mbox_pci_blk [ i ] . vaddr = pci_pool_alloc (
raid_dev - > mbox_pool_handle ,
GFP_KERNEL ,
& mbox_pci_blk [ i ] . dma_addr ) ;
if ( ! mbox_pci_blk [ i ] . vaddr ) {
goto fail_setup_dma_pool ;
}
}
/*
* Allocate memory for each embedded passthru strucuture pointer
* Request for a 128 bytes aligned structure for each passthru command
* structure
* Since passthru and extended passthru commands are exclusive , they
* share common memory pool . Passthru structures piggyback on memory
* allocted to extended passthru since passthru is smaller of the two
*/
raid_dev - > epthru_pool_handle = pci_pool_create ( " megaraid mbox pthru " ,
adapter - > pdev , sizeof ( mraid_epassthru_t ) , 128 , 0 ) ;
if ( raid_dev - > epthru_pool_handle = = NULL ) {
goto fail_setup_dma_pool ;
}
epthru_pci_blk = raid_dev - > epthru_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS ; i + + ) {
epthru_pci_blk [ i ] . vaddr = pci_pool_alloc (
raid_dev - > epthru_pool_handle ,
GFP_KERNEL ,
& epthru_pci_blk [ i ] . dma_addr ) ;
if ( ! epthru_pci_blk [ i ] . vaddr ) {
goto fail_setup_dma_pool ;
}
}
// Allocate memory for each scatter-gather list. Request for 512 bytes
// alignment for each sg list
raid_dev - > sg_pool_handle = pci_pool_create ( " megaraid mbox sg " ,
adapter - > pdev ,
sizeof ( mbox_sgl64 ) * MBOX_MAX_SG_SIZE ,
512 , 0 ) ;
if ( raid_dev - > sg_pool_handle = = NULL ) {
goto fail_setup_dma_pool ;
}
sg_pci_blk = raid_dev - > sg_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS ; i + + ) {
sg_pci_blk [ i ] . vaddr = pci_pool_alloc (
raid_dev - > sg_pool_handle ,
GFP_KERNEL ,
& sg_pci_blk [ i ] . dma_addr ) ;
if ( ! sg_pci_blk [ i ] . vaddr ) {
goto fail_setup_dma_pool ;
}
}
return 0 ;
fail_setup_dma_pool :
megaraid_mbox_teardown_dma_pools ( adapter ) ;
return - 1 ;
}
/**
* megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets
* @ param adapter : HBA soft state
*
* teardown the dma pool for mailbox , passthru and extended passthru
* structures , and scatter - gather lists
*/
static void
megaraid_mbox_teardown_dma_pools ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
struct mraid_pci_blk * epthru_pci_blk ;
struct mraid_pci_blk * sg_pci_blk ;
struct mraid_pci_blk * mbox_pci_blk ;
int i ;
sg_pci_blk = raid_dev - > sg_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS & & sg_pci_blk [ i ] . vaddr ; i + + ) {
pci_pool_free ( raid_dev - > sg_pool_handle , sg_pci_blk [ i ] . vaddr ,
sg_pci_blk [ i ] . dma_addr ) ;
}
if ( raid_dev - > sg_pool_handle )
pci_pool_destroy ( raid_dev - > sg_pool_handle ) ;
epthru_pci_blk = raid_dev - > epthru_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS & & epthru_pci_blk [ i ] . vaddr ; i + + ) {
pci_pool_free ( raid_dev - > epthru_pool_handle ,
epthru_pci_blk [ i ] . vaddr , epthru_pci_blk [ i ] . dma_addr ) ;
}
if ( raid_dev - > epthru_pool_handle )
pci_pool_destroy ( raid_dev - > epthru_pool_handle ) ;
mbox_pci_blk = raid_dev - > mbox_pool ;
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS & & mbox_pci_blk [ i ] . vaddr ; i + + ) {
pci_pool_free ( raid_dev - > mbox_pool_handle ,
mbox_pci_blk [ i ] . vaddr , mbox_pci_blk [ i ] . dma_addr ) ;
}
if ( raid_dev - > mbox_pool_handle )
pci_pool_destroy ( raid_dev - > mbox_pool_handle ) ;
return ;
}
/**
* megaraid_alloc_scb - detach and return a scb from the free list
* @ adapter : controller ' s soft state
*
* return the scb from the head of the free list . NULL if there are none
* available
* */
2006-01-15 00:20:43 +03:00
static scb_t *
2005-04-17 02:20:36 +04:00
megaraid_alloc_scb ( adapter_t * adapter , struct scsi_cmnd * scp )
{
struct list_head * head = & adapter - > kscb_pool ;
scb_t * scb = NULL ;
unsigned long flags ;
// detach scb from free pool
spin_lock_irqsave ( SCSI_FREE_LIST_LOCK ( adapter ) , flags ) ;
if ( list_empty ( head ) ) {
spin_unlock_irqrestore ( SCSI_FREE_LIST_LOCK ( adapter ) , flags ) ;
return NULL ;
}
scb = list_entry ( head - > next , scb_t , list ) ;
list_del_init ( & scb - > list ) ;
spin_unlock_irqrestore ( SCSI_FREE_LIST_LOCK ( adapter ) , flags ) ;
scb - > state = SCB_ACTIVE ;
scb - > scp = scp ;
scb - > dma_type = MRAID_DMA_NONE ;
return scb ;
}
/**
* megaraid_dealloc_scb - return the scb to the free pool
* @ adapter : controller ' s soft state
* @ scb : scb to be freed
*
* return the scb back to the free list of scbs . The caller must ' flush ' the
* SCB before calling us . E . g . , performing pci_unamp and / or pci_sync etc .
* NOTE NOTE : Make sure the scb is not on any list before calling this
* routine .
* */
static inline void
megaraid_dealloc_scb ( adapter_t * adapter , scb_t * scb )
{
unsigned long flags ;
// put scb in the free pool
scb - > state = SCB_FREE ;
scb - > scp = NULL ;
spin_lock_irqsave ( SCSI_FREE_LIST_LOCK ( adapter ) , flags ) ;
list_add ( & scb - > list , & adapter - > kscb_pool ) ;
spin_unlock_irqrestore ( SCSI_FREE_LIST_LOCK ( adapter ) , flags ) ;
return ;
}
/**
* megaraid_mbox_mksgl - make the scatter - gather list
* @ adapter - controller ' s soft state
* @ scb - scsi control block
*
* prepare the scatter - gather list
*/
2006-01-15 00:20:43 +03:00
static int
2005-04-17 02:20:36 +04:00
megaraid_mbox_mksgl ( adapter_t * adapter , scb_t * scb )
{
struct scatterlist * sgl ;
mbox_ccb_t * ccb ;
struct page * page ;
unsigned long offset ;
struct scsi_cmnd * scp ;
int sgcnt ;
int i ;
scp = scb - > scp ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
// no mapping required if no data to be transferred
if ( ! scp - > request_buffer | | ! scp - > request_bufflen )
return 0 ;
if ( ! scp - > use_sg ) { /* scatter-gather list not used */
page = virt_to_page ( scp - > request_buffer ) ;
offset = ( ( unsigned long ) scp - > request_buffer & ~ PAGE_MASK ) ;
ccb - > buf_dma_h = pci_map_page ( adapter - > pdev , page , offset ,
scp - > request_bufflen ,
scb - > dma_direction ) ;
scb - > dma_type = MRAID_DMA_WBUF ;
/*
* We need to handle special 64 - bit commands that need a
* minimum of 1 SG
*/
sgcnt = 1 ;
ccb - > sgl64 [ 0 ] . address = ccb - > buf_dma_h ;
ccb - > sgl64 [ 0 ] . length = scp - > request_bufflen ;
return sgcnt ;
}
sgl = ( struct scatterlist * ) scp - > request_buffer ;
// The number of sg elements returned must not exceed our limit
sgcnt = pci_map_sg ( adapter - > pdev , sgl , scp - > use_sg ,
scb - > dma_direction ) ;
if ( sgcnt > adapter - > sglen ) {
con_log ( CL_ANN , ( KERN_CRIT
" megaraid critical: too many sg elements:%d \n " ,
sgcnt ) ) ;
BUG ( ) ;
}
scb - > dma_type = MRAID_DMA_WSG ;
for ( i = 0 ; i < sgcnt ; i + + , sgl + + ) {
ccb - > sgl64 [ i ] . address = sg_dma_address ( sgl ) ;
ccb - > sgl64 [ i ] . length = sg_dma_len ( sgl ) ;
}
// Return count of SG nodes
return sgcnt ;
}
/**
* mbox_post_cmd - issue a mailbox command
* @ adapter - controller ' s soft state
* @ scb - command to be issued
*
* post the command to the controller if mailbox is availble .
*/
2006-01-15 00:20:43 +03:00
static int
2005-04-17 02:20:36 +04:00
mbox_post_cmd ( adapter_t * adapter , scb_t * scb )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox64_t * mbox64 ;
mbox_t * mbox ;
mbox_ccb_t * ccb ;
unsigned long flags ;
unsigned int i = 0 ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
mbox = raid_dev - > mbox ;
mbox64 = raid_dev - > mbox64 ;
/*
* Check for busy mailbox . If it is , return failure - the caller
* should retry later .
*/
spin_lock_irqsave ( MAILBOX_LOCK ( raid_dev ) , flags ) ;
if ( unlikely ( mbox - > busy ) ) {
do {
udelay ( 1 ) ;
i + + ;
rmb ( ) ;
} while ( mbox - > busy & & ( i < max_mbox_busy_wait ) ) ;
if ( mbox - > busy ) {
spin_unlock_irqrestore ( MAILBOX_LOCK ( raid_dev ) , flags ) ;
return - 1 ;
}
}
// Copy this command's mailbox data into "adapter's" mailbox
memcpy ( ( caddr_t ) mbox64 , ( caddr_t ) ccb - > mbox64 , 22 ) ;
mbox - > cmdid = scb - > sno ;
adapter - > outstanding_cmds + + ;
if ( scb - > dma_direction = = PCI_DMA_TODEVICE ) {
if ( ! scb - > scp - > use_sg ) { // sg list not used
pci_dma_sync_single_for_device ( adapter - > pdev ,
ccb - > buf_dma_h ,
scb - > scp - > request_bufflen ,
PCI_DMA_TODEVICE ) ;
}
else {
pci_dma_sync_sg_for_device ( adapter - > pdev ,
scb - > scp - > request_buffer ,
scb - > scp - > use_sg , PCI_DMA_TODEVICE ) ;
}
}
mbox - > busy = 1 ; // Set busy
mbox - > poll = 0 ;
mbox - > ack = 0 ;
wmb ( ) ;
WRINDOOR ( raid_dev , raid_dev - > mbox_dma | 0x1 ) ;
spin_unlock_irqrestore ( MAILBOX_LOCK ( raid_dev ) , flags ) ;
return 0 ;
}
/**
* megaraid_queue_command - generic queue entry point for all LLDs
* @ scp : pointer to the scsi command to be executed
* @ done : callback routine to be called after the cmd has be completed
*
* Queue entry point for mailbox based controllers .
*/
static int
megaraid_queue_command ( struct scsi_cmnd * scp , void ( * done ) ( struct scsi_cmnd * ) )
{
adapter_t * adapter ;
scb_t * scb ;
int if_busy ;
adapter = SCP2ADAPTER ( scp ) ;
scp - > scsi_done = done ;
scp - > result = 0 ;
/*
* Allocate and build a SCB request
* if_busy flag will be set if megaraid_mbox_build_cmd ( ) command could
* not allocate scb . We will return non - zero status in that case .
* NOTE : scb can be null even though certain commands completed
* successfully , e . g . , MODE_SENSE and TEST_UNIT_READY , it would
* return 0 in that case , and we would do the callback right away .
*/
if_busy = 0 ;
2005-10-31 23:06:02 +03:00
scb = megaraid_mbox_build_cmd ( adapter , scp , & if_busy ) ;
2005-04-17 02:20:36 +04:00
if ( ! scb ) { // command already completed
done ( scp ) ;
return 0 ;
}
2005-10-31 23:06:02 +03:00
megaraid_mbox_runpendq ( adapter , scb ) ;
2005-04-17 02:20:36 +04:00
return if_busy ;
}
/**
* megaraid_mbox_build_cmd - transform the mid - layer scsi command to megaraid
* firmware lingua
* @ adapter - controller ' s soft state
* @ scp - mid - layer scsi command pointer
* @ busy - set if request could not be completed because of lack of
* resources
*
* convert the command issued by mid - layer to format understood by megaraid
* firmware . We also complete certain command without sending them to firmware
*/
static scb_t *
megaraid_mbox_build_cmd ( adapter_t * adapter , struct scsi_cmnd * scp , int * busy )
{
mraid_device_t * rdev = ADAP2RAIDDEV ( adapter ) ;
int channel ;
int target ;
int islogical ;
mbox_ccb_t * ccb ;
mraid_passthru_t * pthru ;
mbox64_t * mbox64 ;
mbox_t * mbox ;
scb_t * scb ;
char skip [ ] = " skipping " ;
char scan [ ] = " scanning " ;
char * ss ;
/*
* Get the appropriate device map for the device this command is
* intended for
*/
MRAID_GET_DEVICE_MAP ( adapter , scp , channel , target , islogical ) ;
/*
* Logical drive commands
*/
if ( islogical ) {
switch ( scp - > cmnd [ 0 ] ) {
case TEST_UNIT_READY :
/*
* Do we support clustering and is the support enabled
* If no , return success always
*/
if ( ! adapter - > ha ) {
scp - > result = ( DID_OK < < 16 ) ;
return NULL ;
}
if ( ! ( scb = megaraid_alloc_scb ( adapter , scp ) ) ) {
scp - > result = ( DID_ERROR < < 16 ) ;
* busy = 1 ;
return NULL ;
}
scb - > dma_direction = scp - > sc_data_direction ;
scb - > dev_channel = 0xFF ;
scb - > dev_target = target ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
/*
* The command id will be provided by the command
* issuance routine
*/
ccb - > raw_mbox [ 0 ] = CLUSTER_CMD ;
ccb - > raw_mbox [ 2 ] = RESERVATION_STATUS ;
ccb - > raw_mbox [ 3 ] = target ;
return scb ;
case MODE_SENSE :
if ( scp - > use_sg ) {
struct scatterlist * sgl ;
caddr_t vaddr ;
sgl = ( struct scatterlist * ) scp - > request_buffer ;
if ( sgl - > page ) {
vaddr = ( caddr_t )
( page_address ( ( & sgl [ 0 ] ) - > page )
+ ( & sgl [ 0 ] ) - > offset ) ;
memset ( vaddr , 0 , scp - > cmnd [ 4 ] ) ;
}
else {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mailbox: invalid sg:%d \n " ,
__LINE__ ) ) ;
}
}
else {
memset ( scp - > request_buffer , 0 , scp - > cmnd [ 4 ] ) ;
}
scp - > result = ( DID_OK < < 16 ) ;
return NULL ;
case INQUIRY :
/*
* Display the channel scan for logical drives
* Do not display scan for a channel if already done .
*/
if ( ! ( rdev - > last_disp & ( 1L < < SCP2CHANNEL ( scp ) ) ) ) {
con_log ( CL_ANN , ( KERN_INFO
" scsi[%d]: scanning scsi channel %d " ,
adapter - > host - > host_no ,
SCP2CHANNEL ( scp ) ) ) ;
con_log ( CL_ANN , (
" [virtual] for logical drives \n " ) ) ;
rdev - > last_disp | = ( 1L < < SCP2CHANNEL ( scp ) ) ;
}
2006-07-25 18:44:58 +04:00
if ( scp - > cmnd [ 1 ] & MEGA_SCSI_INQ_EVPD ) {
scp - > sense_buffer [ 0 ] = 0x70 ;
scp - > sense_buffer [ 2 ] = ILLEGAL_REQUEST ;
scp - > sense_buffer [ 12 ] = MEGA_INVALID_FIELD_IN_CDB ;
scp - > result = CHECK_CONDITION < < 1 ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
/* Fall through */
case READ_CAPACITY :
/*
* Do not allow LUN > 0 for logical drives and
* requests for more than 40 logical drives
*/
if ( SCP2LUN ( scp ) ) {
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
if ( ( target % 0x80 ) > = MAX_LOGICAL_DRIVES_40LD ) {
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
/* Allocate a SCB and initialize passthru */
if ( ! ( scb = megaraid_alloc_scb ( adapter , scp ) ) ) {
scp - > result = ( DID_ERROR < < 16 ) ;
* busy = 1 ;
return NULL ;
}
ccb = ( mbox_ccb_t * ) scb - > ccb ;
scb - > dev_channel = 0xFF ;
scb - > dev_target = target ;
pthru = ccb - > pthru ;
mbox = ccb - > mbox ;
mbox64 = ccb - > mbox64 ;
pthru - > timeout = 0 ;
pthru - > ars = 1 ;
pthru - > reqsenselen = 14 ;
pthru - > islogical = 1 ;
pthru - > logdrv = target ;
pthru - > cdblen = scp - > cmd_len ;
memcpy ( pthru - > cdb , scp - > cmnd , scp - > cmd_len ) ;
mbox - > cmd = MBOXCMD_PASSTHRU64 ;
scb - > dma_direction = scp - > sc_data_direction ;
pthru - > dataxferlen = scp - > request_bufflen ;
pthru - > dataxferaddr = ccb - > sgl_dma_h ;
pthru - > numsge = megaraid_mbox_mksgl ( adapter ,
scb ) ;
mbox - > xferaddr = 0xFFFFFFFF ;
mbox64 - > xferaddr_lo = ( uint32_t ) ccb - > pthru_dma_h ;
mbox64 - > xferaddr_hi = 0 ;
return scb ;
case READ_6 :
case WRITE_6 :
case READ_10 :
case WRITE_10 :
case READ_12 :
case WRITE_12 :
/*
* Allocate a SCB and initialize mailbox
*/
if ( ! ( scb = megaraid_alloc_scb ( adapter , scp ) ) ) {
scp - > result = ( DID_ERROR < < 16 ) ;
* busy = 1 ;
return NULL ;
}
ccb = ( mbox_ccb_t * ) scb - > ccb ;
scb - > dev_channel = 0xFF ;
scb - > dev_target = target ;
mbox = ccb - > mbox ;
mbox64 = ccb - > mbox64 ;
mbox - > logdrv = target ;
/*
* A little HACK : 2 nd bit is zero for all scsi read
* commands and is set for all scsi write commands
*/
mbox - > cmd = ( scp - > cmnd [ 0 ] & 0x02 ) ? MBOXCMD_LWRITE64 :
MBOXCMD_LREAD64 ;
/*
* 6 - byte READ ( 0x08 ) or WRITE ( 0x0A ) cdb
*/
if ( scp - > cmd_len = = 6 ) {
mbox - > numsectors = ( uint32_t ) scp - > cmnd [ 4 ] ;
mbox - > lba =
( ( uint32_t ) scp - > cmnd [ 1 ] < < 16 ) |
( ( uint32_t ) scp - > cmnd [ 2 ] < < 8 ) |
( uint32_t ) scp - > cmnd [ 3 ] ;
mbox - > lba & = 0x1FFFFF ;
}
/*
* 10 - byte READ ( 0x28 ) or WRITE ( 0x2A ) cdb
*/
else if ( scp - > cmd_len = = 10 ) {
mbox - > numsectors =
( uint32_t ) scp - > cmnd [ 8 ] |
( ( uint32_t ) scp - > cmnd [ 7 ] < < 8 ) ;
mbox - > lba =
( ( uint32_t ) scp - > cmnd [ 2 ] < < 24 ) |
( ( uint32_t ) scp - > cmnd [ 3 ] < < 16 ) |
( ( uint32_t ) scp - > cmnd [ 4 ] < < 8 ) |
( uint32_t ) scp - > cmnd [ 5 ] ;
}
/*
* 12 - byte READ ( 0xA8 ) or WRITE ( 0xAA ) cdb
*/
else if ( scp - > cmd_len = = 12 ) {
mbox - > lba =
( ( uint32_t ) scp - > cmnd [ 2 ] < < 24 ) |
( ( uint32_t ) scp - > cmnd [ 3 ] < < 16 ) |
( ( uint32_t ) scp - > cmnd [ 4 ] < < 8 ) |
( uint32_t ) scp - > cmnd [ 5 ] ;
mbox - > numsectors =
( ( uint32_t ) scp - > cmnd [ 6 ] < < 24 ) |
( ( uint32_t ) scp - > cmnd [ 7 ] < < 16 ) |
( ( uint32_t ) scp - > cmnd [ 8 ] < < 8 ) |
( uint32_t ) scp - > cmnd [ 9 ] ;
}
else {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: unsupported CDB length \n " ) ) ;
megaraid_dealloc_scb ( adapter , scb ) ;
scp - > result = ( DID_ERROR < < 16 ) ;
return NULL ;
}
scb - > dma_direction = scp - > sc_data_direction ;
// Calculate Scatter-Gather info
mbox64 - > xferaddr_lo = ( uint32_t ) ccb - > sgl_dma_h ;
mbox - > numsge = megaraid_mbox_mksgl ( adapter ,
scb ) ;
mbox - > xferaddr = 0xFFFFFFFF ;
mbox64 - > xferaddr_hi = 0 ;
return scb ;
case RESERVE :
case RELEASE :
/*
* Do we support clustering and is the support enabled
*/
if ( ! adapter - > ha ) {
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
/*
* Allocate a SCB and initialize mailbox
*/
if ( ! ( scb = megaraid_alloc_scb ( adapter , scp ) ) ) {
scp - > result = ( DID_ERROR < < 16 ) ;
* busy = 1 ;
return NULL ;
}
ccb = ( mbox_ccb_t * ) scb - > ccb ;
scb - > dev_channel = 0xFF ;
scb - > dev_target = target ;
ccb - > raw_mbox [ 0 ] = CLUSTER_CMD ;
ccb - > raw_mbox [ 2 ] = ( scp - > cmnd [ 0 ] = = RESERVE ) ?
RESERVE_LD : RELEASE_LD ;
ccb - > raw_mbox [ 3 ] = target ;
scb - > dma_direction = scp - > sc_data_direction ;
return scb ;
default :
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
}
else { // Passthru device commands
// Do not allow access to target id > 15 or LUN > 7
if ( target > 15 | | SCP2LUN ( scp ) > 7 ) {
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
// if fast load option was set and scan for last device is
// over, reset the fast_load flag so that during a possible
// next scan, devices can be made available
if ( rdev - > fast_load & & ( target = = 15 ) & &
( SCP2CHANNEL ( scp ) = = adapter - > max_channel - 1 ) ) {
con_log ( CL_ANN , ( KERN_INFO
" megaraid[%d]: physical device scan re-enabled \n " ,
adapter - > host - > host_no ) ) ;
rdev - > fast_load = 0 ;
}
/*
* Display the channel scan for physical devices
*/
if ( ! ( rdev - > last_disp & ( 1L < < SCP2CHANNEL ( scp ) ) ) ) {
ss = rdev - > fast_load ? skip : scan ;
con_log ( CL_ANN , ( KERN_INFO
" scsi[%d]: %s scsi channel %d [Phy %d] " ,
adapter - > host - > host_no , ss , SCP2CHANNEL ( scp ) ,
channel ) ) ;
con_log ( CL_ANN , (
" for non-raid devices \n " ) ) ;
rdev - > last_disp | = ( 1L < < SCP2CHANNEL ( scp ) ) ;
}
// disable channel sweep if fast load option given
if ( rdev - > fast_load ) {
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
return NULL ;
}
// Allocate a SCB and initialize passthru
if ( ! ( scb = megaraid_alloc_scb ( adapter , scp ) ) ) {
scp - > result = ( DID_ERROR < < 16 ) ;
* busy = 1 ;
return NULL ;
}
ccb = ( mbox_ccb_t * ) scb - > ccb ;
scb - > dev_channel = channel ;
scb - > dev_target = target ;
scb - > dma_direction = scp - > sc_data_direction ;
mbox = ccb - > mbox ;
mbox64 = ccb - > mbox64 ;
// Does this firmware support extended CDBs
if ( adapter - > max_cdb_sz = = 16 ) {
mbox - > cmd = MBOXCMD_EXTPTHRU ;
megaraid_mbox_prepare_epthru ( adapter , scb , scp ) ;
mbox64 - > xferaddr_lo = ( uint32_t ) ccb - > epthru_dma_h ;
mbox64 - > xferaddr_hi = 0 ;
mbox - > xferaddr = 0xFFFFFFFF ;
}
else {
mbox - > cmd = MBOXCMD_PASSTHRU64 ;
megaraid_mbox_prepare_pthru ( adapter , scb , scp ) ;
mbox64 - > xferaddr_lo = ( uint32_t ) ccb - > pthru_dma_h ;
mbox64 - > xferaddr_hi = 0 ;
mbox - > xferaddr = 0xFFFFFFFF ;
}
return scb ;
}
// NOT REACHED
}
/**
* megaraid_mbox_runpendq - execute commands queued in the pending queue
* @ adapter : controller ' s soft state
* @ scb : SCB to be queued in the pending list
*
* scan the pending list for commands which are not yet issued and try to
* post to the controller . The SCB can be a null pointer , which would indicate
* no SCB to be queue , just try to execute the ones in the pending list .
*
* NOTE : We do not actually traverse the pending list . The SCBs are plucked
* out from the head of the pending list . If it is successfully issued , the
* next SCB is at the head now .
*/
static void
megaraid_mbox_runpendq ( adapter_t * adapter , scb_t * scb_q )
{
scb_t * scb ;
unsigned long flags ;
spin_lock_irqsave ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
if ( scb_q ) {
scb_q - > state = SCB_PENDQ ;
list_add_tail ( & scb_q - > list , & adapter - > pend_list ) ;
}
// if the adapter in not in quiescent mode, post the commands to FW
if ( adapter - > quiescent ) {
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
return ;
}
while ( ! list_empty ( & adapter - > pend_list ) ) {
assert_spin_locked ( PENDING_LIST_LOCK ( adapter ) ) ;
scb = list_entry ( adapter - > pend_list . next , scb_t , list ) ;
// remove the scb from the pending list and try to
// issue. If we are unable to issue it, put back in
// the pending list and return
list_del_init ( & scb - > list ) ;
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
// if mailbox was busy, return SCB back to pending
// list. Make sure to add at the head, since that's
// where it would have been removed from
scb - > state = SCB_ISSUED ;
if ( mbox_post_cmd ( adapter , scb ) ! = 0 ) {
spin_lock_irqsave ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
scb - > state = SCB_PENDQ ;
list_add ( & scb - > list , & adapter - > pend_list ) ;
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) ,
flags ) ;
return ;
}
spin_lock_irqsave ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
}
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
return ;
}
/**
* megaraid_mbox_prepare_pthru - prepare a command for physical devices
* @ adapter - pointer to controller ' s soft state
* @ scb - scsi control block
* @ scp - scsi command from the mid - layer
*
* prepare a command for the scsi physical devices
*/
static void
megaraid_mbox_prepare_pthru ( adapter_t * adapter , scb_t * scb ,
struct scsi_cmnd * scp )
{
mbox_ccb_t * ccb ;
mraid_passthru_t * pthru ;
uint8_t channel ;
uint8_t target ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
pthru = ccb - > pthru ;
channel = scb - > dev_channel ;
target = scb - > dev_target ;
// 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
pthru - > timeout = 4 ;
pthru - > ars = 1 ;
pthru - > islogical = 0 ;
pthru - > channel = 0 ;
pthru - > target = ( channel < < 4 ) | target ;
pthru - > logdrv = SCP2LUN ( scp ) ;
pthru - > reqsenselen = 14 ;
pthru - > cdblen = scp - > cmd_len ;
memcpy ( pthru - > cdb , scp - > cmnd , scp - > cmd_len ) ;
if ( scp - > request_bufflen ) {
pthru - > dataxferlen = scp - > request_bufflen ;
pthru - > dataxferaddr = ccb - > sgl_dma_h ;
pthru - > numsge = megaraid_mbox_mksgl ( adapter , scb ) ;
}
else {
pthru - > dataxferaddr = 0 ;
pthru - > dataxferlen = 0 ;
pthru - > numsge = 0 ;
}
return ;
}
/**
* megaraid_mbox_prepare_epthru - prepare a command for physical devices
* @ adapter - pointer to controller ' s soft state
* @ scb - scsi control block
* @ scp - scsi command from the mid - layer
*
* prepare a command for the scsi physical devices . This rountine prepares
* commands for devices which can take extended CDBs ( > 10 bytes )
*/
static void
megaraid_mbox_prepare_epthru ( adapter_t * adapter , scb_t * scb ,
struct scsi_cmnd * scp )
{
mbox_ccb_t * ccb ;
mraid_epassthru_t * epthru ;
uint8_t channel ;
uint8_t target ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
epthru = ccb - > epthru ;
channel = scb - > dev_channel ;
target = scb - > dev_target ;
// 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
epthru - > timeout = 4 ;
epthru - > ars = 1 ;
epthru - > islogical = 0 ;
epthru - > channel = 0 ;
epthru - > target = ( channel < < 4 ) | target ;
epthru - > logdrv = SCP2LUN ( scp ) ;
epthru - > reqsenselen = 14 ;
epthru - > cdblen = scp - > cmd_len ;
memcpy ( epthru - > cdb , scp - > cmnd , scp - > cmd_len ) ;
if ( scp - > request_bufflen ) {
epthru - > dataxferlen = scp - > request_bufflen ;
epthru - > dataxferaddr = ccb - > sgl_dma_h ;
epthru - > numsge = megaraid_mbox_mksgl ( adapter , scb ) ;
}
else {
epthru - > dataxferaddr = 0 ;
epthru - > dataxferlen = 0 ;
epthru - > numsge = 0 ;
}
return ;
}
/**
* megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs
* @ adapter - controller ' s soft state
*
* Interrupt ackrowledgement sequence for memory mapped HBAs . Find out the
* completed command and put them on the completed list for later processing .
*
* Returns : 1 if the interrupt is valid , 0 otherwise
*/
2006-01-15 00:20:43 +03:00
static int
2005-04-17 02:20:36 +04:00
megaraid_ack_sequence ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox_t * mbox ;
scb_t * scb ;
uint8_t nstatus ;
uint8_t completed [ MBOX_MAX_FIRMWARE_STATUS ] ;
struct list_head clist ;
int handled ;
uint32_t dword ;
unsigned long flags ;
int i , j ;
mbox = raid_dev - > mbox ;
// move the SCBs from the firmware completed array to our local list
INIT_LIST_HEAD ( & clist ) ;
// loop till F/W has more commands for us to complete
handled = 0 ;
spin_lock_irqsave ( MAILBOX_LOCK ( raid_dev ) , flags ) ;
do {
/*
* Check if a valid interrupt is pending . If found , force the
* interrupt line low .
*/
dword = RDOUTDOOR ( raid_dev ) ;
if ( dword ! = 0x10001234 ) break ;
handled = 1 ;
WROUTDOOR ( raid_dev , 0x10001234 ) ;
nstatus = 0 ;
// wait for valid numstatus to post
for ( i = 0 ; i < 0xFFFFF ; i + + ) {
if ( mbox - > numstatus ! = 0xFF ) {
nstatus = mbox - > numstatus ;
break ;
}
rmb ( ) ;
}
mbox - > numstatus = 0xFF ;
adapter - > outstanding_cmds - = nstatus ;
for ( i = 0 ; i < nstatus ; i + + ) {
// wait for valid command index to post
for ( j = 0 ; j < 0xFFFFF ; j + + ) {
if ( mbox - > completed [ i ] ! = 0xFF ) break ;
rmb ( ) ;
}
completed [ i ] = mbox - > completed [ i ] ;
mbox - > completed [ i ] = 0xFF ;
if ( completed [ i ] = = 0xFF ) {
con_log ( CL_ANN , ( KERN_CRIT
" megaraid: command posting timed out \n " ) ) ;
BUG ( ) ;
continue ;
}
// Get SCB associated with this command id
if ( completed [ i ] > = MBOX_MAX_SCSI_CMDS ) {
// a cmm command
scb = adapter - > uscb_list + ( completed [ i ] -
MBOX_MAX_SCSI_CMDS ) ;
}
else {
// an os command
scb = adapter - > kscb_list + completed [ i ] ;
}
scb - > status = mbox - > status ;
list_add_tail ( & scb - > list , & clist ) ;
}
// Acknowledge interrupt
WRINDOOR ( raid_dev , 0x02 ) ;
} while ( 1 ) ;
spin_unlock_irqrestore ( MAILBOX_LOCK ( raid_dev ) , flags ) ;
// put the completed commands in the completed list. DPC would
// complete these commands later
spin_lock_irqsave ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
list_splice ( & clist , & adapter - > completed_list ) ;
spin_unlock_irqrestore ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
// schedule the DPC if there is some work for it
if ( handled )
tasklet_schedule ( & adapter - > dpc_h ) ;
return handled ;
}
/**
* megaraid_isr - isr for memory based mailbox based controllers
* @ irq - irq
* @ devp - pointer to our soft state
* @ regs - unused
*
* Interrupt service routine for memory - mapped mailbox controllers .
*/
static irqreturn_t
megaraid_isr ( int irq , void * devp , struct pt_regs * regs )
{
adapter_t * adapter = devp ;
int handled ;
handled = megaraid_ack_sequence ( adapter ) ;
/* Loop through any pending requests */
if ( ! adapter - > quiescent ) {
megaraid_mbox_runpendq ( adapter , NULL ) ;
}
return IRQ_RETVAL ( handled ) ;
}
/**
* megaraid_mbox_sync_scb - sync kernel buffers
* @ adapter : controller ' s soft state
* @ scb : pointer to the resource packet
*
* DMA sync if required .
*/
2006-01-15 00:20:43 +03:00
static void
2005-04-17 02:20:36 +04:00
megaraid_mbox_sync_scb ( adapter_t * adapter , scb_t * scb )
{
mbox_ccb_t * ccb ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
switch ( scb - > dma_type ) {
case MRAID_DMA_WBUF :
if ( scb - > dma_direction = = PCI_DMA_FROMDEVICE ) {
pci_dma_sync_single_for_cpu ( adapter - > pdev ,
ccb - > buf_dma_h ,
scb - > scp - > request_bufflen ,
PCI_DMA_FROMDEVICE ) ;
}
pci_unmap_page ( adapter - > pdev , ccb - > buf_dma_h ,
scb - > scp - > request_bufflen , scb - > dma_direction ) ;
break ;
case MRAID_DMA_WSG :
if ( scb - > dma_direction = = PCI_DMA_FROMDEVICE ) {
pci_dma_sync_sg_for_cpu ( adapter - > pdev ,
scb - > scp - > request_buffer ,
scb - > scp - > use_sg , PCI_DMA_FROMDEVICE ) ;
}
pci_unmap_sg ( adapter - > pdev , scb - > scp - > request_buffer ,
scb - > scp - > use_sg , scb - > dma_direction ) ;
break ;
default :
break ;
}
return ;
}
/**
* megaraid_mbox_dpc - the tasklet to complete the commands from completed list
* @ devp : pointer to HBA soft state
*
* Pick up the commands from the completed list and send back to the owners .
* This is a reentrant function and does not assume any locks are held while
* it is being called .
*/
static void
megaraid_mbox_dpc ( unsigned long devp )
{
adapter_t * adapter = ( adapter_t * ) devp ;
mraid_device_t * raid_dev ;
struct list_head clist ;
struct scatterlist * sgl ;
scb_t * scb ;
scb_t * tmp ;
struct scsi_cmnd * scp ;
mraid_passthru_t * pthru ;
mraid_epassthru_t * epthru ;
mbox_ccb_t * ccb ;
int islogical ;
int pdev_index ;
int pdev_state ;
mbox_t * mbox ;
unsigned long flags ;
uint8_t c ;
int status ;
2006-04-27 13:33:06 +04:00
uioc_t * kioc ;
2005-04-17 02:20:36 +04:00
if ( ! adapter ) return ;
raid_dev = ADAP2RAIDDEV ( adapter ) ;
// move the SCBs from the completed list to our local list
INIT_LIST_HEAD ( & clist ) ;
spin_lock_irqsave ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
list_splice_init ( & adapter - > completed_list , & clist ) ;
spin_unlock_irqrestore ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
list_for_each_entry_safe ( scb , tmp , & clist , list ) {
status = scb - > status ;
scp = scb - > scp ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
pthru = ccb - > pthru ;
epthru = ccb - > epthru ;
mbox = ccb - > mbox ;
// Make sure f/w has completed a valid command
if ( scb - > state ! = SCB_ISSUED ) {
con_log ( CL_ANN , ( KERN_CRIT
" megaraid critical err: invalid command %d:%d:%p \n " ,
scb - > sno , scb - > state , scp ) ) ;
BUG ( ) ;
continue ; // Must never happen!
}
// check for the management command and complete it right away
if ( scb - > sno > = MBOX_MAX_SCSI_CMDS ) {
scb - > state = SCB_FREE ;
scb - > status = status ;
// remove from local clist
list_del_init ( & scb - > list ) ;
2006-04-27 13:33:06 +04:00
kioc = ( uioc_t * ) scb - > gp ;
kioc - > status = 0 ;
2005-04-17 02:20:36 +04:00
megaraid_mbox_mm_done ( adapter , scb ) ;
continue ;
}
// Was an abort issued for this command earlier
if ( scb - > state & SCB_ABORT ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: aborted cmd %lx[%x] completed \n " ,
scp - > serial_number , scb - > sno ) ) ;
}
/*
* If the inquiry came of a disk drive which is not part of
* any RAID array , expose it to the kernel . For this to be
* enabled , user must set the " megaraid_expose_unconf_disks "
* flag to 1 by specifying it on module parameter list .
* This would enable data migration off drives from other
* configurations .
*/
islogical = MRAID_IS_LOGICAL ( adapter , scp ) ;
if ( scp - > cmnd [ 0 ] = = INQUIRY & & status = = 0 & & islogical = = 0
& & IS_RAID_CH ( raid_dev , scb - > dev_channel ) ) {
if ( scp - > use_sg ) {
sgl = ( struct scatterlist * )
scp - > request_buffer ;
if ( sgl - > page ) {
c = * ( unsigned char * )
( page_address ( ( & sgl [ 0 ] ) - > page ) +
( & sgl [ 0 ] ) - > offset ) ;
}
else {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mailbox: invalid sg:%d \n " ,
__LINE__ ) ) ;
c = 0 ;
}
}
else {
c = * ( uint8_t * ) scp - > request_buffer ;
}
if ( ( c & 0x1F ) = = TYPE_DISK ) {
pdev_index = ( scb - > dev_channel * 16 ) +
scb - > dev_target ;
pdev_state =
raid_dev - > pdrv_state [ pdev_index ] & 0x0F ;
if ( pdev_state = = PDRV_ONLINE | |
pdev_state = = PDRV_FAILED | |
pdev_state = = PDRV_RBLD | |
pdev_state = = PDRV_HOTSPARE | |
megaraid_expose_unconf_disks = = 0 ) {
status = 0xF0 ;
}
}
}
// Convert MegaRAID status to Linux error code
switch ( status ) {
case 0x00 :
scp - > result = ( DID_OK < < 16 ) ;
break ;
case 0x02 :
/* set sense_buffer and result fields */
if ( mbox - > cmd = = MBOXCMD_PASSTHRU | |
mbox - > cmd = = MBOXCMD_PASSTHRU64 ) {
memcpy ( scp - > sense_buffer , pthru - > reqsensearea ,
14 ) ;
scp - > result = DRIVER_SENSE < < 24 |
DID_OK < < 16 | CHECK_CONDITION < < 1 ;
}
else {
if ( mbox - > cmd = = MBOXCMD_EXTPTHRU ) {
memcpy ( scp - > sense_buffer ,
epthru - > reqsensearea , 14 ) ;
scp - > result = DRIVER_SENSE < < 24 |
DID_OK < < 16 |
CHECK_CONDITION < < 1 ;
} else {
scp - > sense_buffer [ 0 ] = 0x70 ;
scp - > sense_buffer [ 2 ] = ABORTED_COMMAND ;
scp - > result = CHECK_CONDITION < < 1 ;
}
}
break ;
case 0x08 :
scp - > result = DID_BUS_BUSY < < 16 | status ;
break ;
default :
/*
* If TEST_UNIT_READY fails , we know RESERVATION_STATUS
* failed
*/
if ( scp - > cmnd [ 0 ] = = TEST_UNIT_READY ) {
scp - > result = DID_ERROR < < 16 |
RESERVATION_CONFLICT < < 1 ;
}
else
/*
* Error code returned is 1 if Reserve or Release
* failed or the input parameter is invalid
*/
if ( status = = 1 & & ( scp - > cmnd [ 0 ] = = RESERVE | |
scp - > cmnd [ 0 ] = = RELEASE ) ) {
scp - > result = DID_ERROR < < 16 |
RESERVATION_CONFLICT < < 1 ;
}
else {
scp - > result = DID_BAD_TARGET < < 16 | status ;
}
}
// print a debug message for all failed commands
if ( status ) {
megaraid_mbox_display_scb ( adapter , scb ) ;
}
// Free our internal resources and call the mid-layer callback
// routine
megaraid_mbox_sync_scb ( adapter , scb ) ;
// remove from local clist
list_del_init ( & scb - > list ) ;
// put back in free list
megaraid_dealloc_scb ( adapter , scb ) ;
// send the scsi packet back to kernel
scp - > scsi_done ( scp ) ;
}
return ;
}
/**
* megaraid_abort_handler - abort the scsi command
* @ scp : command to be aborted
*
* Abort a previous SCSI request . Only commands on the pending list can be
* aborted . All the commands issued to the F / W must complete .
* */
static int
2005-10-31 23:06:02 +03:00
megaraid_abort_handler ( struct scsi_cmnd * scp )
2005-04-17 02:20:36 +04:00
{
adapter_t * adapter ;
mraid_device_t * raid_dev ;
scb_t * scb ;
scb_t * tmp ;
int found ;
unsigned long flags ;
int i ;
adapter = SCP2ADAPTER ( scp ) ;
raid_dev = ADAP2RAIDDEV ( adapter ) ;
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: aborting-%ld cmd=%x <c=%d t=%d l=%d> \n " ,
scp - > serial_number , scp - > cmnd [ 0 ] , SCP2CHANNEL ( scp ) ,
SCP2TARGET ( scp ) , SCP2LUN ( scp ) ) ) ;
// If FW has stopped responding, simply return failure
if ( raid_dev - > hw_error ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: hw error, not aborting \n " ) ) ;
return FAILED ;
}
// There might a race here, where the command was completed by the
// firmware and now it is on the completed list. Before we could
// complete the command to the kernel in dpc, the abort came.
// Find out if this is the case to avoid the race.
scb = NULL ;
spin_lock_irqsave ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
list_for_each_entry_safe ( scb , tmp , & adapter - > completed_list , list ) {
if ( scb - > scp = = scp ) { // Found command
list_del_init ( & scb - > list ) ; // from completed list
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: %ld:%d[%d:%d], abort from completed list \n " ,
scp - > serial_number , scb - > sno ,
scb - > dev_channel , scb - > dev_target ) ) ;
scp - > result = ( DID_ABORT < < 16 ) ;
scp - > scsi_done ( scp ) ;
megaraid_dealloc_scb ( adapter , scb ) ;
spin_unlock_irqrestore ( COMPLETED_LIST_LOCK ( adapter ) ,
flags ) ;
return SUCCESS ;
}
}
spin_unlock_irqrestore ( COMPLETED_LIST_LOCK ( adapter ) , flags ) ;
// Find out if this command is still on the pending list. If it is and
// was never issued, abort and return success. If the command is owned
// by the firmware, we must wait for it to complete by the FW.
spin_lock_irqsave ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
list_for_each_entry_safe ( scb , tmp , & adapter - > pend_list , list ) {
if ( scb - > scp = = scp ) { // Found command
list_del_init ( & scb - > list ) ; // from pending list
ASSERT ( ! ( scb - > state & SCB_ISSUED ) ) ;
con_log ( CL_ANN , ( KERN_WARNING
" megaraid abort: %ld[%d:%d], driver owner \n " ,
scp - > serial_number , scb - > dev_channel ,
scb - > dev_target ) ) ;
scp - > result = ( DID_ABORT < < 16 ) ;
scp - > scsi_done ( scp ) ;
megaraid_dealloc_scb ( adapter , scb ) ;
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) ,
flags ) ;
return SUCCESS ;
}
}
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
// Check do we even own this command, in which case this would be
// owned by the firmware. The only way to locate the FW scb is to
// traverse through the list of all SCB, since driver does not
// maintain these SCBs on any list
found = 0 ;
2005-10-31 23:06:02 +03:00
spin_lock_irq ( & adapter - > lock ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < MBOX_MAX_SCSI_CMDS ; i + + ) {
scb = adapter - > kscb_list + i ;
if ( scb - > scp = = scp ) {
found = 1 ;
if ( ! ( scb - > state & SCB_ISSUED ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid abort: %ld%d[%d:%d], invalid state \n " ,
scp - > serial_number , scb - > sno , scb - > dev_channel ,
scb - > dev_target ) ) ;
BUG ( ) ;
}
else {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid abort: %ld:%d[%d:%d], fw owner \n " ,
scp - > serial_number , scb - > sno , scb - > dev_channel ,
scb - > dev_target ) ) ;
}
}
}
2005-10-31 23:06:02 +03:00
spin_unlock_irq ( & adapter - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( ! found ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid abort: scsi cmd:%ld, do now own \n " ,
scp - > serial_number ) ) ;
// FIXME: Should there be a callback for this command?
return SUCCESS ;
}
// We cannot actually abort a command owned by firmware, return
// failure and wait for reset. In host reset handler, we will find out
// if the HBA is still live
return FAILED ;
}
/**
* megaraid_reset_handler - device reset hadler for mailbox based driver
* @ scp : reference command
*
* Reset handler for the mailbox based controller . First try to find out if
* the FW is still live , in which case the outstanding commands counter mut go
* down to 0. If that happens , also issue the reservation reset command to
* relinquish ( possible ) reservations on the logical drives connected to this
* host
* */
static int
2005-10-31 23:06:02 +03:00
megaraid_reset_handler ( struct scsi_cmnd * scp )
2005-04-17 02:20:36 +04:00
{
adapter_t * adapter ;
scb_t * scb ;
scb_t * tmp ;
mraid_device_t * raid_dev ;
unsigned long flags ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
int rval ;
int recovery_window ;
int recovering ;
int i ;
2006-04-27 13:33:06 +04:00
uioc_t * kioc ;
2005-04-17 02:20:36 +04:00
adapter = SCP2ADAPTER ( scp ) ;
raid_dev = ADAP2RAIDDEV ( adapter ) ;
// return failure if adapter is not responding
if ( raid_dev - > hw_error ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: hw error, cannot reset \n " ) ) ;
return FAILED ;
}
// Under exceptional conditions, FW can take up to 3 minutes to
// complete command processing. Wait for additional 2 minutes for the
// pending commands counter to go down to 0. If it doesn't, let the
// controller be marked offline
// Also, reset all the commands currently owned by the driver
spin_lock_irqsave ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
list_for_each_entry_safe ( scb , tmp , & adapter - > pend_list , list ) {
list_del_init ( & scb - > list ) ; // from pending list
2006-04-27 13:33:06 +04:00
if ( scb - > sno > = MBOX_MAX_SCSI_CMDS ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: IOCTL packet with %d[%d:%d] being reset \n " ,
scb - > sno , scb - > dev_channel , scb - > dev_target ) ) ;
2005-04-17 02:20:36 +04:00
2006-04-27 13:33:06 +04:00
scb - > status = - 1 ;
2005-04-17 02:20:36 +04:00
2006-04-27 13:33:06 +04:00
kioc = ( uioc_t * ) scb - > gp ;
kioc - > status = - EFAULT ;
megaraid_mbox_mm_done ( adapter , scb ) ;
} else {
if ( scb - > scp = = scp ) { // Found command
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: %ld:%d[%d:%d], reset from pending list \n " ,
scp - > serial_number , scb - > sno ,
scb - > dev_channel , scb - > dev_target ) ) ;
} else {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: IO packet with %d[%d:%d] being reset \n " ,
scb - > sno , scb - > dev_channel , scb - > dev_target ) ) ;
}
scb - > scp - > result = ( DID_RESET < < 16 ) ;
scb - > scp - > scsi_done ( scb - > scp ) ;
megaraid_dealloc_scb ( adapter , scb ) ;
}
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( PENDING_LIST_LOCK ( adapter ) , flags ) ;
if ( adapter - > outstanding_cmds ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: %d outstanding commands. Max wait %d sec \n " ,
2006-04-27 13:33:06 +04:00
adapter - > outstanding_cmds ,
( MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT ) ) ) ;
2005-04-17 02:20:36 +04:00
}
recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT ;
recovering = adapter - > outstanding_cmds ;
2006-04-27 13:33:06 +04:00
for ( i = 0 ; i < recovery_window ; i + + ) {
2005-04-17 02:20:36 +04:00
megaraid_ack_sequence ( adapter ) ;
// print a message once every 5 seconds only
if ( ! ( i % 5 ) ) {
con_log ( CL_ANN , (
" megaraid mbox: Wait for %d commands to complete:%d \n " ,
adapter - > outstanding_cmds ,
2006-04-27 13:33:06 +04:00
( MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT ) - i ) ) ;
2005-04-17 02:20:36 +04:00
}
// bailout if no recovery happended in reset time
2006-04-27 13:33:06 +04:00
if ( adapter - > outstanding_cmds = = 0 ) {
2005-04-17 02:20:36 +04:00
break ;
}
msleep ( 1000 ) ;
}
2005-10-31 23:06:02 +03:00
spin_lock ( & adapter - > lock ) ;
2005-04-17 02:20:36 +04:00
// If still outstanding commands, bail out
if ( adapter - > outstanding_cmds ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mbox: critical hardware error! \n " ) ) ;
raid_dev - > hw_error = 1 ;
2005-10-31 23:06:02 +03:00
rval = FAILED ;
goto out ;
2005-04-17 02:20:36 +04:00
}
else {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid mbox: reset sequence completed sucessfully \n " ) ) ;
}
// If the controller supports clustering, reset reservations
2005-10-31 23:06:02 +03:00
if ( ! adapter - > ha ) {
rval = SUCCESS ;
goto out ;
}
2005-04-17 02:20:36 +04:00
// clear reservations if any
raw_mbox [ 0 ] = CLUSTER_CMD ;
raw_mbox [ 2 ] = RESET_RESERVATIONS ;
rval = SUCCESS ;
if ( mbox_post_sync_cmd_fast ( adapter , raw_mbox ) = = 0 ) {
con_log ( CL_ANN ,
( KERN_INFO " megaraid: reservation reset \n " ) ) ;
}
else {
rval = FAILED ;
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: reservation reset failed \n " ) ) ;
}
2005-10-31 23:06:02 +03:00
out :
spin_unlock_irq ( & adapter - > lock ) ;
2005-04-17 02:20:36 +04:00
return rval ;
}
/*
* START : internal commands library
*
* This section of the driver has the common routine used by the driver and
* also has all the FW routines
*/
/**
* mbox_post_sync_cmd ( ) - blocking command to the mailbox based controllers
* @ adapter - controller ' s soft state
* @ raw_mbox - the mailbox
*
* Issue a scb in synchronous and non - interrupt mode for mailbox based
* controllers
*/
static int
mbox_post_sync_cmd ( adapter_t * adapter , uint8_t raw_mbox [ ] )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox64_t * mbox64 ;
mbox_t * mbox ;
uint8_t status ;
int i ;
mbox64 = raid_dev - > mbox64 ;
mbox = raid_dev - > mbox ;
/*
* Wait until mailbox is free
*/
if ( megaraid_busywait_mbox ( raid_dev ) ! = 0 )
goto blocked_mailbox ;
/*
* Copy mailbox data into host structure
*/
memcpy ( ( caddr_t ) mbox , ( caddr_t ) raw_mbox , 16 ) ;
mbox - > cmdid = 0xFE ;
mbox - > busy = 1 ;
mbox - > poll = 0 ;
mbox - > ack = 0 ;
mbox - > numstatus = 0xFF ;
mbox - > status = 0xFF ;
wmb ( ) ;
WRINDOOR ( raid_dev , raid_dev - > mbox_dma | 0x1 ) ;
// wait for maximum 1 second for status to post. If the status is not
// available within 1 second, assume FW is initializing and wait
// for an extended amount of time
if ( mbox - > numstatus = = 0xFF ) { // status not yet available
2006-03-24 14:16:13 +03:00
udelay ( 25 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; mbox - > numstatus = = 0xFF & & i < 1000 ; i + + ) {
rmb ( ) ;
msleep ( 1 ) ;
}
if ( i = = 1000 ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid mailbox: wait for FW to boot " ) ) ;
for ( i = 0 ; ( mbox - > numstatus = = 0xFF ) & &
( i < MBOX_RESET_WAIT ) ; i + + ) {
rmb ( ) ;
con_log ( CL_ANN , ( " \b \b \b \b \b [%03d] " ,
MBOX_RESET_WAIT - i ) ) ;
msleep ( 1000 ) ;
}
if ( i = = MBOX_RESET_WAIT ) {
con_log ( CL_ANN , (
" \n megaraid mailbox: status not available \n " ) ) ;
return - 1 ;
}
con_log ( CL_ANN , ( " \b \b \b \b \b [ok] \n " ) ) ;
}
}
// wait for maximum 1 second for poll semaphore
if ( mbox - > poll ! = 0x77 ) {
udelay ( 25 ) ;
for ( i = 0 ; ( mbox - > poll ! = 0x77 ) & & ( i < 1000 ) ; i + + ) {
rmb ( ) ;
msleep ( 1 ) ;
}
if ( i = = 1000 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mailbox: could not get poll semaphore \n " ) ) ;
return - 1 ;
}
}
WRINDOOR ( raid_dev , raid_dev - > mbox_dma | 0x2 ) ;
wmb ( ) ;
// wait for maximum 1 second for acknowledgement
if ( RDINDOOR ( raid_dev ) & 0x2 ) {
udelay ( 25 ) ;
for ( i = 0 ; ( RDINDOOR ( raid_dev ) & 0x2 ) & & ( i < 1000 ) ; i + + ) {
rmb ( ) ;
msleep ( 1 ) ;
}
if ( i = = 1000 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mailbox: could not acknowledge \n " ) ) ;
return - 1 ;
}
}
mbox - > poll = 0 ;
mbox - > ack = 0x77 ;
status = mbox - > status ;
// invalidate the completed command id array. After command
// completion, firmware would write the valid id.
mbox - > numstatus = 0xFF ;
mbox - > status = 0xFF ;
for ( i = 0 ; i < MBOX_MAX_FIRMWARE_STATUS ; i + + ) {
mbox - > completed [ i ] = 0xFF ;
}
return status ;
blocked_mailbox :
con_log ( CL_ANN , ( KERN_WARNING " megaraid: blocked mailbox \n " ) ) ;
return - 1 ;
}
/**
* mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers
* @ adapter - controller ' s soft state
* @ raw_mbox - the mailbox
*
* Issue a scb in synchronous and non - interrupt mode for mailbox based
* controllers . This is a faster version of the synchronous command and
* therefore can be called in interrupt - context as well
*/
static int
mbox_post_sync_cmd_fast ( adapter_t * adapter , uint8_t raw_mbox [ ] )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox_t * mbox ;
long i ;
mbox = raid_dev - > mbox ;
// return immediately if the mailbox is busy
if ( mbox - > busy ) return - 1 ;
// Copy mailbox data into host structure
memcpy ( ( caddr_t ) mbox , ( caddr_t ) raw_mbox , 14 ) ;
mbox - > cmdid = 0xFE ;
mbox - > busy = 1 ;
mbox - > poll = 0 ;
mbox - > ack = 0 ;
mbox - > numstatus = 0xFF ;
mbox - > status = 0xFF ;
wmb ( ) ;
WRINDOOR ( raid_dev , raid_dev - > mbox_dma | 0x1 ) ;
2006-04-27 13:33:06 +04:00
for ( i = 0 ; i < MBOX_SYNC_WAIT_CNT ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( mbox - > numstatus ! = 0xFF ) break ;
2005-11-17 21:17:25 +03:00
rmb ( ) ;
2006-04-27 13:33:06 +04:00
udelay ( MBOX_SYNC_DELAY_200 ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-27 13:33:06 +04:00
if ( i = = MBOX_SYNC_WAIT_CNT ) {
2005-04-17 02:20:36 +04:00
// We may need to re-calibrate the counter
con_log ( CL_ANN , ( KERN_CRIT
" megaraid: fast sync command timed out \n " ) ) ;
}
WRINDOOR ( raid_dev , raid_dev - > mbox_dma | 0x2 ) ;
wmb ( ) ;
return mbox - > status ;
}
/**
* megaraid_busywait_mbox ( ) - Wait until the controller ' s mailbox is available
* @ raid_dev - RAID device ( HBA ) soft state
*
* wait until the controller ' s mailbox is available to accept more commands .
* wait for at most 1 second
*/
static int
megaraid_busywait_mbox ( mraid_device_t * raid_dev )
{
mbox_t * mbox = raid_dev - > mbox ;
int i = 0 ;
if ( mbox - > busy ) {
udelay ( 25 ) ;
for ( i = 0 ; mbox - > busy & & i < 1000 ; i + + )
msleep ( 1 ) ;
}
if ( i < 1000 ) return 0 ;
else return - 1 ;
}
/**
* megaraid_mbox_product_info - some static information about the controller
* @ adapter - our soft state
*
* issue commands to the controller to grab some parameters required by our
* caller .
*/
static int
megaraid_mbox_product_info ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
mraid_pinfo_t * pinfo ;
dma_addr_t pinfo_dma_h ;
mraid_inquiry3_t * mraid_inq3 ;
int i ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( raw_mbox ) ) ;
mbox = ( mbox_t * ) raw_mbox ;
/*
* Issue an ENQUIRY3 command to find out certain adapter parameters ,
* e . g . , max channels , max commands etc .
*/
pinfo = pci_alloc_consistent ( adapter - > pdev , sizeof ( mraid_pinfo_t ) ,
& pinfo_dma_h ) ;
if ( pinfo = = NULL ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
return - 1 ;
}
memset ( pinfo , 0 , sizeof ( mraid_pinfo_t ) ) ;
mbox - > xferaddr = ( uint32_t ) adapter - > ibuf_dma_h ;
memset ( ( void * ) adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
raw_mbox [ 0 ] = FC_NEW_CONFIG ;
raw_mbox [ 2 ] = NC_SUBOP_ENQUIRY3 ;
raw_mbox [ 3 ] = ENQ3_GET_SOLICITED_FULL ;
// Issue the command
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING " megaraid: Inquiry3 failed \n " ) ) ;
pci_free_consistent ( adapter - > pdev , sizeof ( mraid_pinfo_t ) ,
pinfo , pinfo_dma_h ) ;
return - 1 ;
}
/*
* Collect information about state of each physical drive
* attached to the controller . We will expose all the disks
* which are not part of RAID
*/
mraid_inq3 = ( mraid_inquiry3_t * ) adapter - > ibuf ;
for ( i = 0 ; i < MBOX_MAX_PHYSICAL_DRIVES ; i + + ) {
raid_dev - > pdrv_state [ i ] = mraid_inq3 - > pdrv_state [ i ] ;
}
/*
* Get product info for information like number of channels ,
* maximum commands supported .
*/
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( raw_mbox ) ) ;
mbox - > xferaddr = ( uint32_t ) pinfo_dma_h ;
raw_mbox [ 0 ] = FC_NEW_CONFIG ;
raw_mbox [ 2 ] = NC_SUBOP_PRODUCT_INFO ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: product info failed \n " ) ) ;
pci_free_consistent ( adapter - > pdev , sizeof ( mraid_pinfo_t ) ,
pinfo , pinfo_dma_h ) ;
return - 1 ;
}
/*
* Setup some parameters for host , as required by our caller
*/
adapter - > max_channel = pinfo - > nchannels ;
/*
* we will export all the logical drives on a single channel .
* Add 1 since inquires do not come for inititor ID
*/
adapter - > max_target = MAX_LOGICAL_DRIVES_40LD + 1 ;
adapter - > max_lun = 8 ; // up to 8 LUNs for non-disk devices
/*
* These are the maximum outstanding commands for the scsi - layer
*/
adapter - > max_cmds = MBOX_MAX_SCSI_CMDS ;
memset ( adapter - > fw_version , 0 , VERSION_SIZE ) ;
memset ( adapter - > bios_version , 0 , VERSION_SIZE ) ;
memcpy ( adapter - > fw_version , pinfo - > fw_version , 4 ) ;
adapter - > fw_version [ 4 ] = 0 ;
memcpy ( adapter - > bios_version , pinfo - > bios_version , 4 ) ;
adapter - > bios_version [ 4 ] = 0 ;
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: fw version:[%s] bios version:[%s] \n " ,
adapter - > fw_version , adapter - > bios_version ) ) ;
pci_free_consistent ( adapter - > pdev , sizeof ( mraid_pinfo_t ) , pinfo ,
pinfo_dma_h ) ;
return 0 ;
}
/**
* megaraid_mbox_extended_cdb - check for support for extended CDBs
* @ adapter - soft state for the controller
*
* this routine check whether the controller in question supports extended
* ( > 10 bytes ) CDBs
*/
static int
megaraid_mbox_extended_cdb ( adapter_t * adapter )
{
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
int rval ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( raw_mbox ) ) ;
mbox - > xferaddr = ( uint32_t ) adapter - > ibuf_dma_h ;
memset ( ( void * ) adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
raw_mbox [ 0 ] = MAIN_MISC_OPCODE ;
raw_mbox [ 2 ] = SUPPORT_EXT_CDB ;
/*
* Issue the command
*/
rval = 0 ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) ! = 0 ) {
rval = - 1 ;
}
return rval ;
}
/**
* megaraid_mbox_support_ha - Do we support clustering
* @ adapter - soft state for the controller
* @ init_id - ID of the initiator
*
* Determine if the firmware supports clustering and the ID of the initiator .
*/
static int
megaraid_mbox_support_ha ( adapter_t * adapter , uint16_t * init_id )
{
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
int rval ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( raw_mbox ) ) ;
mbox - > xferaddr = ( uint32_t ) adapter - > ibuf_dma_h ;
memset ( ( void * ) adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
raw_mbox [ 0 ] = GET_TARGET_ID ;
// Issue the command
* init_id = 7 ;
rval = - 1 ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) = = 0 ) {
* init_id = * ( uint8_t * ) adapter - > ibuf ;
con_log ( CL_ANN , ( KERN_INFO
" megaraid: cluster firmware, initiator ID: %d \n " ,
* init_id ) ) ;
rval = 0 ;
}
return rval ;
}
/**
* megaraid_mbox_support_random_del - Do we support random deletion
* @ adapter - soft state for the controller
*
* Determine if the firmware supports random deletion
* Return : 1 is operation supported , 0 otherwise
*/
static int
megaraid_mbox_support_random_del ( adapter_t * adapter )
{
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
int rval ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( mbox_t ) ) ;
raw_mbox [ 0 ] = FC_DEL_LOGDRV ;
raw_mbox [ 2 ] = OP_SUP_DEL_LOGDRV ;
// Issue the command
rval = 0 ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) = = 0 ) {
con_log ( CL_DLEVEL1 , ( " megaraid: supports random deletion \n " ) ) ;
rval = 1 ;
}
return rval ;
}
/**
* megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware
* @ adapter - soft state for the controller
*
* Find out the maximum number of scatter - gather elements supported by the
* firmware
*/
static int
megaraid_mbox_get_max_sg ( adapter_t * adapter )
{
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
int nsg ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( mbox_t ) ) ;
mbox - > xferaddr = ( uint32_t ) adapter - > ibuf_dma_h ;
memset ( ( void * ) adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
raw_mbox [ 0 ] = MAIN_MISC_OPCODE ;
raw_mbox [ 2 ] = GET_MAX_SG_SUPPORT ;
// Issue the command
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) = = 0 ) {
nsg = * ( uint8_t * ) adapter - > ibuf ;
}
else {
nsg = MBOX_DEFAULT_SG_SIZE ;
}
if ( nsg > MBOX_MAX_SG_SIZE ) nsg = MBOX_MAX_SG_SIZE ;
return nsg ;
}
/**
* megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels
* @ adapter - soft state for the controller
*
* Enumerate the RAID and SCSI channels for ROMB platoforms so that channels
* can be exported as regular SCSI channels
*/
static void
megaraid_mbox_enum_raid_scsi ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( mbox_t ) ) ;
mbox - > xferaddr = ( uint32_t ) adapter - > ibuf_dma_h ;
memset ( ( void * ) adapter - > ibuf , 0 , MBOX_IBUF_SIZE ) ;
raw_mbox [ 0 ] = CHNL_CLASS ;
raw_mbox [ 2 ] = GET_CHNL_CLASS ;
// Issue the command. If the command fails, all channels are RAID
// channels
raid_dev - > channel_class = 0xFF ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) = = 0 ) {
raid_dev - > channel_class = * ( uint8_t * ) adapter - > ibuf ;
}
return ;
}
/**
* megaraid_mbox_flush_cache - flush adapter and disks cache
* @ param adapter : soft state for the controller
*
* Flush adapter cache followed by disks cache
*/
static void
megaraid_mbox_flush_cache ( adapter_t * adapter )
{
mbox_t * mbox ;
uint8_t raw_mbox [ sizeof ( mbox_t ) ] ;
mbox = ( mbox_t * ) raw_mbox ;
memset ( ( caddr_t ) raw_mbox , 0 , sizeof ( mbox_t ) ) ;
raw_mbox [ 0 ] = FLUSH_ADAPTER ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) ! = 0 ) {
con_log ( CL_ANN , ( " megaraid: flush adapter failed \n " ) ) ;
}
raw_mbox [ 0 ] = FLUSH_SYSTEM ;
if ( mbox_post_sync_cmd ( adapter , raw_mbox ) ! = 0 ) {
con_log ( CL_ANN , ( " megaraid: flush disks cache failed \n " ) ) ;
}
return ;
}
/**
* megaraid_mbox_display_scb - display SCB information , mostly debug purposes
* @ param adapter : controllers ' soft state
* @ param scb : SCB to be displayed
* @ param level : debug level for console print
*
* Diplay information about the given SCB iff the current debug level is
* verbose
*/
static void
megaraid_mbox_display_scb ( adapter_t * adapter , scb_t * scb )
{
mbox_ccb_t * ccb ;
struct scsi_cmnd * scp ;
mbox_t * mbox ;
int level ;
int i ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
scp = scb - > scp ;
mbox = ccb - > mbox ;
level = CL_DLEVEL3 ;
con_log ( level , ( KERN_NOTICE
" megaraid mailbox: status:%#x cmd:%#x id:%#x " , scb - > status ,
mbox - > cmd , scb - > sno ) ) ;
con_log ( level , ( " sec:%#x lba:%#x addr:%#x ld:%d sg:%d \n " ,
mbox - > numsectors , mbox - > lba , mbox - > xferaddr , mbox - > logdrv ,
mbox - > numsge ) ) ;
if ( ! scp ) return ;
con_log ( level , ( KERN_NOTICE " scsi cmnd: " ) ) ;
for ( i = 0 ; i < scp - > cmd_len ; i + + ) {
con_log ( level , ( " %#2.02x " , scp - > cmnd [ i ] ) ) ;
}
con_log ( level , ( " \n " ) ) ;
return ;
}
/**
* megaraid_mbox_setup_device_map - manage device ids
* @ adapter : Driver ' s soft state
*
* Manange the device ids to have an appropraite mapping between the kernel
* scsi addresses and megaraid scsi and logical drive addresses . We export
* scsi devices on their actual addresses , whereas the logical drives are
* exported on a virtual scsi channel .
* */
static void
megaraid_mbox_setup_device_map ( adapter_t * adapter )
{
uint8_t c ;
uint8_t t ;
/*
* First fill the values on the logical drive channel
*/
for ( t = 0 ; t < LSI_MAX_LOGICAL_DRIVES_64LD ; t + + )
adapter - > device_ids [ adapter - > max_channel ] [ t ] =
( t < adapter - > init_id ) ? t : t - 1 ;
adapter - > device_ids [ adapter - > max_channel ] [ adapter - > init_id ] = 0xFF ;
/*
* Fill the values on the physical devices channels
*/
for ( c = 0 ; c < adapter - > max_channel ; c + + )
for ( t = 0 ; t < LSI_MAX_LOGICAL_DRIVES_64LD ; t + + )
adapter - > device_ids [ c ] [ t ] = ( c < < 8 ) | t ;
}
/*
* END : internal commands library
*/
/*
* START : Interface for the common management module
*
* This is the module , which interfaces with the common mangement module to
* provide support for ioctl and sysfs
*/
/**
* megaraid_cmm_register - register with the mangement module
* @ param adapter : HBA soft state
*
* Register with the management module , which allows applications to issue
* ioctl calls to the drivers . This interface is used by the management module
* to setup sysfs support as well .
*/
static int
megaraid_cmm_register ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
mraid_mmadp_t adp ;
scb_t * scb ;
mbox_ccb_t * ccb ;
int rval ;
int i ;
// Allocate memory for the base list of scb for management module.
adapter - > uscb_list = kmalloc ( sizeof ( scb_t ) * MBOX_MAX_USER_CMDS ,
GFP_KERNEL ) ;
if ( adapter - > uscb_list = = NULL ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
return - 1 ;
}
memset ( adapter - > uscb_list , 0 , sizeof ( scb_t ) * MBOX_MAX_USER_CMDS ) ;
// Initialize the synchronization parameters for resources for
// commands for management module
INIT_LIST_HEAD ( & adapter - > uscb_pool ) ;
spin_lock_init ( USER_FREE_LIST_LOCK ( adapter ) ) ;
// link all the packets. Note, CCB for commands, coming from the
// commom management module, mailbox physical address are already
// setup by it. We just need placeholder for that in our local command
// control blocks
for ( i = 0 ; i < MBOX_MAX_USER_CMDS ; i + + ) {
scb = adapter - > uscb_list + i ;
ccb = raid_dev - > uccb_list + i ;
scb - > ccb = ( caddr_t ) ccb ;
ccb - > mbox64 = raid_dev - > umbox64 + i ;
ccb - > mbox = & ccb - > mbox64 - > mbox32 ;
ccb - > raw_mbox = ( uint8_t * ) ccb - > mbox ;
scb - > gp = 0 ;
// COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR
// COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER)
scb - > sno = i + MBOX_MAX_SCSI_CMDS ;
scb - > scp = NULL ;
scb - > state = SCB_FREE ;
scb - > dma_direction = PCI_DMA_NONE ;
scb - > dma_type = MRAID_DMA_NONE ;
scb - > dev_channel = - 1 ;
scb - > dev_target = - 1 ;
// put scb in the free pool
list_add_tail ( & scb - > list , & adapter - > uscb_pool ) ;
}
adp . unique_id = adapter - > unique_id ;
adp . drvr_type = DRVRTYPE_MBOX ;
adp . drvr_data = ( unsigned long ) adapter ;
adp . pdev = adapter - > pdev ;
adp . issue_uioc = megaraid_mbox_mm_handler ;
2006-04-27 13:33:06 +04:00
adp . timeout = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT ;
2005-04-17 02:20:36 +04:00
adp . max_kioc = MBOX_MAX_USER_CMDS ;
if ( ( rval = mraid_mm_register_adp ( & adp ) ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mbox: did not register with CMM \n " ) ) ;
kfree ( adapter - > uscb_list ) ;
}
return rval ;
}
/**
* megaraid_cmm_unregister - un - register with the mangement module
* @ param adapter : HBA soft state
*
* Un - register with the management module .
* FIXME : mgmt module must return failure for unregister if it has pending
* commands in LLD
*/
static int
megaraid_cmm_unregister ( adapter_t * adapter )
{
kfree ( adapter - > uscb_list ) ;
mraid_mm_unregister_adp ( adapter - > unique_id ) ;
return 0 ;
}
/**
* megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD
* @ param drvr_data : LLD specific data
* @ param kioc : CMM interface packet
* @ param action : command action
*
* This routine is invoked whenever the Common Mangement Module ( CMM ) has a
* command for us . The ' action ' parameter specifies if this is a new command
* or otherwise .
*/
static int
megaraid_mbox_mm_handler ( unsigned long drvr_data , uioc_t * kioc , uint32_t action )
{
adapter_t * adapter ;
if ( action ! = IOCTL_ISSUE ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: unsupported management action:%#2x \n " ,
action ) ) ;
return ( - ENOTSUPP ) ;
}
adapter = ( adapter_t * ) drvr_data ;
// make sure this adapter is not being detached right now.
if ( atomic_read ( & adapter - > being_detached ) ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: reject management request, detaching \n " ) ) ;
return ( - ENODEV ) ;
}
switch ( kioc - > opcode ) {
case GET_ADAP_INFO :
kioc - > status = gather_hbainfo ( adapter , ( mraid_hba_info_t * )
( unsigned long ) kioc - > buf_vaddr ) ;
kioc - > done ( kioc ) ;
return kioc - > status ;
case MBOX_CMD :
return megaraid_mbox_mm_command ( adapter , kioc ) ;
default :
kioc - > status = ( - EINVAL ) ;
kioc - > done ( kioc ) ;
return ( - EINVAL ) ;
}
return 0 ; // not reached
}
/**
* megaraid_mbox_mm_command - issues commands routed through CMM
* @ param adapter : HBA soft state
* @ param kioc : management command packet
*
* Issues commands , which are routed through the management module .
*/
static int
megaraid_mbox_mm_command ( adapter_t * adapter , uioc_t * kioc )
{
struct list_head * head = & adapter - > uscb_pool ;
mbox64_t * mbox64 ;
uint8_t * raw_mbox ;
scb_t * scb ;
mbox_ccb_t * ccb ;
unsigned long flags ;
// detach one scb from free pool
spin_lock_irqsave ( USER_FREE_LIST_LOCK ( adapter ) , flags ) ;
if ( list_empty ( head ) ) { // should never happen because of CMM
con_log ( CL_ANN , ( KERN_WARNING
" megaraid mbox: bug in cmm handler, lost resources \n " ) ) ;
spin_unlock_irqrestore ( USER_FREE_LIST_LOCK ( adapter ) , flags ) ;
return ( - EINVAL ) ;
}
scb = list_entry ( head - > next , scb_t , list ) ;
list_del_init ( & scb - > list ) ;
spin_unlock_irqrestore ( USER_FREE_LIST_LOCK ( adapter ) , flags ) ;
scb - > state = SCB_ACTIVE ;
scb - > dma_type = MRAID_DMA_NONE ;
scb - > dma_direction = PCI_DMA_NONE ;
ccb = ( mbox_ccb_t * ) scb - > ccb ;
mbox64 = ( mbox64_t * ) ( unsigned long ) kioc - > cmdbuf ;
raw_mbox = ( uint8_t * ) & mbox64 - > mbox32 ;
memcpy ( ccb - > mbox64 , mbox64 , sizeof ( mbox64_t ) ) ;
scb - > gp = ( unsigned long ) kioc ;
/*
* If it is a logdrv random delete operation , we have to wait till
* there are no outstanding cmds at the fw and then issue it directly
*/
if ( raw_mbox [ 0 ] = = FC_DEL_LOGDRV & & raw_mbox [ 2 ] = = OP_DEL_LOGDRV ) {
if ( wait_till_fw_empty ( adapter ) ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid mbox: LD delete, timed out \n " ) ) ;
kioc - > status = - ETIME ;
scb - > status = - 1 ;
megaraid_mbox_mm_done ( adapter , scb ) ;
return ( - ETIME ) ;
}
INIT_LIST_HEAD ( & scb - > list ) ;
scb - > state = SCB_ISSUED ;
if ( mbox_post_cmd ( adapter , scb ) ! = 0 ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid mbox: LD delete, mailbox busy \n " ) ) ;
kioc - > status = - EBUSY ;
scb - > status = - 1 ;
megaraid_mbox_mm_done ( adapter , scb ) ;
return ( - EBUSY ) ;
}
return 0 ;
}
// put the command on the pending list and execute
megaraid_mbox_runpendq ( adapter , scb ) ;
return 0 ;
}
static int
wait_till_fw_empty ( adapter_t * adapter )
{
unsigned long flags = 0 ;
int i ;
/*
* Set the quiescent flag to stop issuing cmds to FW .
*/
2005-10-31 23:06:02 +03:00
spin_lock_irqsave ( & adapter - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
adapter - > quiescent + + ;
2005-10-31 23:06:02 +03:00
spin_unlock_irqrestore ( & adapter - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
/*
* Wait till there are no more cmds outstanding at FW . Try for at most
* 60 seconds
*/
for ( i = 0 ; i < 60 & & adapter - > outstanding_cmds ; i + + ) {
con_log ( CL_DLEVEL1 , ( KERN_INFO
" megaraid: FW has %d pending commands \n " ,
adapter - > outstanding_cmds ) ) ;
msleep ( 1000 ) ;
}
return adapter - > outstanding_cmds ;
}
/**
* megaraid_mbox_mm_done - callback for CMM commands
* @ adapter : HBA soft state
* @ scb : completed command
*
* Callback routine for internal commands originated from the management
* module .
*/
static void
megaraid_mbox_mm_done ( adapter_t * adapter , scb_t * scb )
{
uioc_t * kioc ;
mbox64_t * mbox64 ;
uint8_t * raw_mbox ;
unsigned long flags ;
kioc = ( uioc_t * ) scb - > gp ;
mbox64 = ( mbox64_t * ) ( unsigned long ) kioc - > cmdbuf ;
mbox64 - > mbox32 . status = scb - > status ;
raw_mbox = ( uint8_t * ) & mbox64 - > mbox32 ;
// put scb in the free pool
scb - > state = SCB_FREE ;
scb - > scp = NULL ;
spin_lock_irqsave ( USER_FREE_LIST_LOCK ( adapter ) , flags ) ;
list_add ( & scb - > list , & adapter - > uscb_pool ) ;
spin_unlock_irqrestore ( USER_FREE_LIST_LOCK ( adapter ) , flags ) ;
// if a delete logical drive operation succeeded, restart the
// controller
if ( raw_mbox [ 0 ] = = FC_DEL_LOGDRV & & raw_mbox [ 2 ] = = OP_DEL_LOGDRV ) {
adapter - > quiescent - - ;
megaraid_mbox_runpendq ( adapter , NULL ) ;
}
kioc - > done ( kioc ) ;
return ;
}
/**
* gather_hbainfo - HBA characteristics for the applications
* @ param adapter : HBA soft state
* @ param hinfo : pointer to the caller ' s host info strucuture
*/
static int
gather_hbainfo ( adapter_t * adapter , mraid_hba_info_t * hinfo )
{
uint8_t dmajor ;
dmajor = megaraid_mbox_version [ 0 ] ;
hinfo - > pci_vendor_id = adapter - > pdev - > vendor ;
hinfo - > pci_device_id = adapter - > pdev - > device ;
hinfo - > subsys_vendor_id = adapter - > pdev - > subsystem_vendor ;
hinfo - > subsys_device_id = adapter - > pdev - > subsystem_device ;
hinfo - > pci_bus = adapter - > pdev - > bus - > number ;
hinfo - > pci_dev_fn = adapter - > pdev - > devfn ;
hinfo - > pci_slot = PCI_SLOT ( adapter - > pdev - > devfn ) ;
hinfo - > irq = adapter - > host - > irq ;
hinfo - > baseport = ADAP2RAIDDEV ( adapter ) - > baseport ;
hinfo - > unique_id = ( hinfo - > pci_bus < < 8 ) | adapter - > pdev - > devfn ;
hinfo - > host_no = adapter - > host - > host_no ;
return 0 ;
}
/*
* END : Interface for the common management module
*/
/**
* megaraid_sysfs_alloc_resources - allocate sysfs related resources
*
* Allocate packets required to issue FW calls whenever the sysfs attributes
* are read . These attributes would require up - to - date information from the
* FW . Also set up resources for mutual exclusion to share these resources and
* the wait queue .
*
* @ param adapter : controller ' s soft state
*
* @ return 0 on success
* @ return - ERROR_CODE on failure
*/
static int
megaraid_sysfs_alloc_resources ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
int rval = 0 ;
raid_dev - > sysfs_uioc = kmalloc ( sizeof ( uioc_t ) , GFP_KERNEL ) ;
raid_dev - > sysfs_mbox64 = kmalloc ( sizeof ( mbox64_t ) , GFP_KERNEL ) ;
raid_dev - > sysfs_buffer = pci_alloc_consistent ( adapter - > pdev ,
PAGE_SIZE , & raid_dev - > sysfs_buffer_dma ) ;
if ( ! raid_dev - > sysfs_uioc | | ! raid_dev - > sysfs_mbox64 | |
! raid_dev - > sysfs_buffer ) {
con_log ( CL_ANN , ( KERN_WARNING
" megaraid: out of memory, %s %d \n " , __FUNCTION__ ,
__LINE__ ) ) ;
rval = - ENOMEM ;
megaraid_sysfs_free_resources ( adapter ) ;
}
sema_init ( & raid_dev - > sysfs_sem , 1 ) ;
init_waitqueue_head ( & raid_dev - > sysfs_wait_q ) ;
return rval ;
}
/**
* megaraid_sysfs_free_resources - free sysfs related resources
*
* Free packets allocated for sysfs FW commands
*
* @ param adapter : controller ' s soft state
*/
static void
megaraid_sysfs_free_resources ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
2005-11-07 12:01:26 +03:00
kfree ( raid_dev - > sysfs_uioc ) ;
kfree ( raid_dev - > sysfs_mbox64 ) ;
2005-04-17 02:20:36 +04:00
if ( raid_dev - > sysfs_buffer ) {
pci_free_consistent ( adapter - > pdev , PAGE_SIZE ,
raid_dev - > sysfs_buffer , raid_dev - > sysfs_buffer_dma ) ;
}
}
/**
* megaraid_sysfs_get_ldmap_done - callback for get ldmap
*
* Callback routine called in the ISR / tasklet context for get ldmap call
*
* @ param uioc : completed packet
*/
static void
megaraid_sysfs_get_ldmap_done ( uioc_t * uioc )
{
adapter_t * adapter = ( adapter_t * ) uioc - > buf_vaddr ;
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
uioc - > status = 0 ;
wake_up ( & raid_dev - > sysfs_wait_q ) ;
}
/**
* megaraid_sysfs_get_ldmap_timeout - timeout handling for get ldmap
*
* Timeout routine to recover and return to application , in case the adapter
* has stopped responding . A timeout of 60 seconds for this command seem like
* a good value
*
* @ param uioc : timed out packet
*/
static void
megaraid_sysfs_get_ldmap_timeout ( unsigned long data )
{
uioc_t * uioc = ( uioc_t * ) data ;
adapter_t * adapter = ( adapter_t * ) uioc - > buf_vaddr ;
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
uioc - > status = - ETIME ;
wake_up ( & raid_dev - > sysfs_wait_q ) ;
}
/**
* megaraid_sysfs_get_ldmap - get update logical drive map
*
* This routine will be called whenever user reads the logical drive
* attributes , go get the current logical drive mapping table from the
* firmware . We use the managment API ' s to issue commands to the controller .
*
* NOTE : The commands issuance functionality is not generalized and
* implemented in context of " get ld map " command only . If required , the
* command issuance logical can be trivially pulled out and implemented as a
* standalone libary . For now , this should suffice since there is no other
* user of this interface .
*
* @ param adapter : controller ' s soft state
*
* @ return 0 on success
* @ return - 1 on failure
*/
static int
megaraid_sysfs_get_ldmap ( adapter_t * adapter )
{
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
uioc_t * uioc ;
mbox64_t * mbox64 ;
mbox_t * mbox ;
char * raw_mbox ;
struct timer_list sysfs_timer ;
struct timer_list * timerp ;
caddr_t ldmap ;
int rval = 0 ;
/*
* Allow only one read at a time to go through the sysfs attributes
*/
down ( & raid_dev - > sysfs_sem ) ;
uioc = raid_dev - > sysfs_uioc ;
mbox64 = raid_dev - > sysfs_mbox64 ;
ldmap = raid_dev - > sysfs_buffer ;
memset ( uioc , 0 , sizeof ( uioc_t ) ) ;
memset ( mbox64 , 0 , sizeof ( mbox64_t ) ) ;
memset ( ldmap , 0 , sizeof ( raid_dev - > curr_ldmap ) ) ;
mbox = & mbox64 - > mbox32 ;
raw_mbox = ( char * ) mbox ;
uioc - > cmdbuf = ( uint64_t ) ( unsigned long ) mbox64 ;
uioc - > buf_vaddr = ( caddr_t ) adapter ;
uioc - > status = - ENODATA ;
uioc - > done = megaraid_sysfs_get_ldmap_done ;
/*
* Prepare the mailbox packet to get the current logical drive mapping
* table
*/
mbox - > xferaddr = ( uint32_t ) raid_dev - > sysfs_buffer_dma ;
raw_mbox [ 0 ] = FC_DEL_LOGDRV ;
raw_mbox [ 2 ] = OP_GET_LDID_MAP ;
/*
* Setup a timer to recover from a non - responding controller
*/
timerp = & sysfs_timer ;
init_timer ( timerp ) ;
timerp - > function = megaraid_sysfs_get_ldmap_timeout ;
timerp - > data = ( unsigned long ) uioc ;
timerp - > expires = jiffies + 60 * HZ ;
add_timer ( timerp ) ;
/*
* Send the command to the firmware
*/
rval = megaraid_mbox_mm_command ( adapter , uioc ) ;
if ( rval = = 0 ) { // command successfully issued
wait_event ( raid_dev - > sysfs_wait_q , ( uioc - > status ! = - ENODATA ) ) ;
/*
* Check if the command timed out
*/
if ( uioc - > status = = - ETIME ) {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: sysfs get ld map timed out \n " ) ) ;
rval = - ETIME ;
}
else {
rval = mbox - > status ;
}
if ( rval = = 0 ) {
memcpy ( raid_dev - > curr_ldmap , ldmap ,
sizeof ( raid_dev - > curr_ldmap ) ) ;
}
else {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: get ld map failed with %x \n " , rval ) ) ;
}
}
else {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: could not issue ldmap command:%x \n " , rval ) ) ;
}
del_timer_sync ( timerp ) ;
up ( & raid_dev - > sysfs_sem ) ;
return rval ;
}
/**
* megaraid_sysfs_show_app_hndl - display application handle for this adapter
*
* Display the handle used by the applications while executing management
* tasks on the adapter . We invoke a management module API to get the adapter
* handle , since we do not interface with applications directly .
*
* @ param cdev : class device object representation for the host
* @ param buf : buffer to send data to
*/
static ssize_t
megaraid_sysfs_show_app_hndl ( struct class_device * cdev , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( cdev ) ;
adapter_t * adapter = ( adapter_t * ) SCSIHOST2ADAP ( shost ) ;
uint32_t app_hndl ;
app_hndl = mraid_mm_adapter_app_handle ( adapter - > unique_id ) ;
return snprintf ( buf , 8 , " %u \n " , app_hndl ) ;
}
/**
* megaraid_sysfs_show_ldnum - display the logical drive number for this device
*
* Display the logical drive number for the device in question , if it a valid
* logical drive . For physical devices , " -1 " is returned
* The logical drive number is displayed in following format
*
* < SCSI ID > < LD NUM > < LD STICKY ID > < APP ADAPTER HANDLE >
* < int > < int > < int > < int >
*
* @ param dev : device object representation for the scsi device
* @ param buf : buffer to send data to
*/
static ssize_t
2005-05-17 14:43:37 +04:00
megaraid_sysfs_show_ldnum ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct scsi_device * sdev = to_scsi_device ( dev ) ;
adapter_t * adapter = ( adapter_t * ) SCSIHOST2ADAP ( sdev - > host ) ;
mraid_device_t * raid_dev = ADAP2RAIDDEV ( adapter ) ;
int scsi_id = - 1 ;
int logical_drv = - 1 ;
int ldid_map = - 1 ;
uint32_t app_hndl = 0 ;
int mapped_sdev_id ;
int rval ;
int i ;
if ( raid_dev - > random_del_supported & &
MRAID_IS_LOGICAL_SDEV ( adapter , sdev ) ) {
rval = megaraid_sysfs_get_ldmap ( adapter ) ;
if ( rval = = 0 ) {
for ( i = 0 ; i < MAX_LOGICAL_DRIVES_40LD ; i + + ) {
mapped_sdev_id = sdev - > id ;
if ( sdev - > id > adapter - > init_id ) {
mapped_sdev_id - = 1 ;
}
if ( raid_dev - > curr_ldmap [ i ] = = mapped_sdev_id ) {
scsi_id = sdev - > id ;
logical_drv = i ;
ldid_map = raid_dev - > curr_ldmap [ i ] ;
app_hndl = mraid_mm_adapter_app_handle (
adapter - > unique_id ) ;
break ;
}
}
}
else {
con_log ( CL_ANN , ( KERN_NOTICE
" megaraid: sysfs get ld map failed: %x \n " ,
rval ) ) ;
}
}
return snprintf ( buf , 36 , " %d %d %d %d \n " , scsi_id , logical_drv ,
ldid_map , app_hndl ) ;
}
/*
* END : Mailbox Low Level Driver
*/
module_init ( megaraid_init ) ;
module_exit ( megaraid_exit ) ;
/* vim: set ts=8 sw=8 tw=78 ai si: */