2005-09-21 01:46:58 +04:00
/*
*
* Linux MegaRAID driver for SAS based RAID controllers
*
* Copyright ( c ) 2003 - 2005 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_sas . c
2006-01-25 22:53:25 +03:00
* Version : v00 .00 .02 .01
2005-09-21 01:46:58 +04:00
*
* Authors :
* Sreenivas Bagalkote < Sreenivas . Bagalkote @ lsil . com >
* Sumant Patro < Sumant . Patro @ lsil . com >
*
* List of supported controllers
*
* OEM Product Name VID DID SSVID SSID
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/list.h>
# include <linux/moduleparam.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/uio.h>
# include <asm/uaccess.h>
2005-10-04 20:36:04 +04:00
# include <linux/fs.h>
2005-09-21 01:46:58 +04:00
# include <linux/compat.h>
2006-01-11 15:16:10 +03:00
# include <linux/mutex.h>
2005-09-21 01:46:58 +04:00
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include "megaraid_sas.h"
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( MEGASAS_VERSION ) ;
MODULE_AUTHOR ( " sreenivas.bagalkote@lsil.com " ) ;
MODULE_DESCRIPTION ( " LSI Logic MegaRAID SAS Driver " ) ;
/*
* PCI ID table for all supported controllers
*/
static struct pci_device_id megasas_pci_table [ ] = {
{
PCI_VENDOR_ID_LSI_LOGIC ,
PCI_DEVICE_ID_LSI_SAS1064R ,
PCI_ANY_ID ,
PCI_ANY_ID ,
} ,
{
PCI_VENDOR_ID_DELL ,
PCI_DEVICE_ID_DELL_PERC5 ,
PCI_ANY_ID ,
PCI_ANY_ID ,
} ,
{ 0 } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , megasas_pci_table ) ;
static int megasas_mgmt_majorno ;
static struct megasas_mgmt_info megasas_mgmt_info ;
static struct fasync_struct * megasas_async_queue ;
2006-01-11 15:16:10 +03:00
static DEFINE_MUTEX ( megasas_async_queue_mutex ) ;
2005-09-21 01:46:58 +04:00
/**
* megasas_get_cmd - Get a command from the free pool
* @ instance : Adapter soft state
*
* Returns a free command from the pool
*/
2006-01-15 00:20:43 +03:00
static struct megasas_cmd * megasas_get_cmd ( struct megasas_instance
2005-09-21 01:46:58 +04:00
* instance )
{
unsigned long flags ;
struct megasas_cmd * cmd = NULL ;
spin_lock_irqsave ( & instance - > cmd_pool_lock , flags ) ;
if ( ! list_empty ( & instance - > cmd_pool ) ) {
cmd = list_entry ( ( & instance - > cmd_pool ) - > next ,
struct megasas_cmd , list ) ;
list_del_init ( & cmd - > list ) ;
} else {
printk ( KERN_ERR " megasas: Command pool empty! \n " ) ;
}
spin_unlock_irqrestore ( & instance - > cmd_pool_lock , flags ) ;
return cmd ;
}
/**
* megasas_return_cmd - Return a cmd to free command pool
* @ instance : Adapter soft state
* @ cmd : Command packet to be returned to free command pool
*/
static inline void
megasas_return_cmd ( struct megasas_instance * instance , struct megasas_cmd * cmd )
{
unsigned long flags ;
spin_lock_irqsave ( & instance - > cmd_pool_lock , flags ) ;
cmd - > scmd = NULL ;
list_add_tail ( & cmd - > list , & instance - > cmd_pool ) ;
spin_unlock_irqrestore ( & instance - > cmd_pool_lock , flags ) ;
}
/**
* megasas_enable_intr - Enables interrupts
* @ regs : MFI register set
*/
static inline void
megasas_enable_intr ( struct megasas_register_set __iomem * regs )
{
writel ( 1 , & ( regs ) - > outbound_intr_mask ) ;
/* Dummy readl to force pci flush */
readl ( & regs - > outbound_intr_mask ) ;
}
/**
* megasas_disable_intr - Disables interrupts
* @ regs : MFI register set
*/
static inline void
megasas_disable_intr ( struct megasas_register_set __iomem * regs )
{
u32 mask = readl ( & regs - > outbound_intr_mask ) & ( ~ 0x00000001 ) ;
writel ( mask , & regs - > outbound_intr_mask ) ;
/* Dummy readl to force pci flush */
readl ( & regs - > outbound_intr_mask ) ;
}
/**
* megasas_issue_polled - Issues a polling command
* @ instance : Adapter soft state
* @ cmd : Command packet to be issued
*
* For polling , MFI requires the cmd_status to be set to 0xFF before posting .
*/
static int
megasas_issue_polled ( struct megasas_instance * instance , struct megasas_cmd * cmd )
{
int i ;
u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000 ;
struct megasas_header * frame_hdr = & cmd - > frame - > hdr ;
frame_hdr - > cmd_status = 0xFF ;
frame_hdr - > flags | = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE ;
/*
* Issue the frame using inbound queue port
*/
writel ( cmd - > frame_phys_addr > > 3 ,
& instance - > reg_set - > inbound_queue_port ) ;
/*
* Wait for cmd_status to change
*/
for ( i = 0 ; ( i < msecs ) & & ( frame_hdr - > cmd_status = = 0xff ) ; i + + ) {
rmb ( ) ;
msleep ( 1 ) ;
}
if ( frame_hdr - > cmd_status = = 0xff )
return - ETIME ;
return 0 ;
}
/**
* megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds
* @ instance : Adapter soft state
* @ cmd : Command to be issued
*
* This function waits on an event for the command to be returned from ISR .
* Used to issue ioctl commands .
*/
static int
megasas_issue_blocked_cmd ( struct megasas_instance * instance ,
struct megasas_cmd * cmd )
{
cmd - > cmd_status = ENODATA ;
writel ( cmd - > frame_phys_addr > > 3 ,
& instance - > reg_set - > inbound_queue_port ) ;
wait_event ( instance - > int_cmd_wait_q , ( cmd - > cmd_status ! = ENODATA ) ) ;
return 0 ;
}
/**
* megasas_issue_blocked_abort_cmd - Aborts previously issued cmd
* @ instance : Adapter soft state
* @ cmd_to_abort : Previously issued cmd to be aborted
*
* MFI firmware can abort previously issued AEN comamnd ( automatic event
* notification ) . The megasas_issue_blocked_abort_cmd ( ) issues such abort
* cmd and blocks till it is completed .
*/
static int
megasas_issue_blocked_abort_cmd ( struct megasas_instance * instance ,
struct megasas_cmd * cmd_to_abort )
{
struct megasas_cmd * cmd ;
struct megasas_abort_frame * abort_fr ;
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd )
return - 1 ;
abort_fr = & cmd - > frame - > abort ;
/*
* Prepare and issue the abort frame
*/
abort_fr - > cmd = MFI_CMD_ABORT ;
abort_fr - > cmd_status = 0xFF ;
abort_fr - > flags = 0 ;
abort_fr - > abort_context = cmd_to_abort - > index ;
abort_fr - > abort_mfi_phys_addr_lo = cmd_to_abort - > frame_phys_addr ;
abort_fr - > abort_mfi_phys_addr_hi = 0 ;
cmd - > sync_cmd = 1 ;
cmd - > cmd_status = 0xFF ;
writel ( cmd - > frame_phys_addr > > 3 ,
& instance - > reg_set - > inbound_queue_port ) ;
/*
* Wait for this cmd to complete
*/
wait_event ( instance - > abort_cmd_wait_q , ( cmd - > cmd_status ! = 0xFF ) ) ;
megasas_return_cmd ( instance , cmd ) ;
return 0 ;
}
/**
* megasas_make_sgl32 - Prepares 32 - bit SGL
* @ instance : Adapter soft state
* @ scp : SCSI command from the mid - layer
* @ mfi_sgl : SGL to be filled in
*
* If successful , this function returns the number of SG elements . Otherwise ,
* it returnes - 1.
*/
2006-01-15 00:20:43 +03:00
static int
2005-09-21 01:46:58 +04:00
megasas_make_sgl32 ( struct megasas_instance * instance , struct scsi_cmnd * scp ,
union megasas_sgl * mfi_sgl )
{
int i ;
int sge_count ;
struct scatterlist * os_sgl ;
/*
* Return 0 if there is no data transfer
*/
if ( ! scp - > request_buffer | | ! scp - > request_bufflen )
return 0 ;
if ( ! scp - > use_sg ) {
mfi_sgl - > sge32 [ 0 ] . phys_addr = pci_map_single ( instance - > pdev ,
scp - >
request_buffer ,
scp - >
request_bufflen ,
scp - >
sc_data_direction ) ;
mfi_sgl - > sge32 [ 0 ] . length = scp - > request_bufflen ;
return 1 ;
}
os_sgl = ( struct scatterlist * ) scp - > request_buffer ;
sge_count = pci_map_sg ( instance - > pdev , os_sgl , scp - > use_sg ,
scp - > sc_data_direction ) ;
for ( i = 0 ; i < sge_count ; i + + , os_sgl + + ) {
mfi_sgl - > sge32 [ i ] . length = sg_dma_len ( os_sgl ) ;
mfi_sgl - > sge32 [ i ] . phys_addr = sg_dma_address ( os_sgl ) ;
}
return sge_count ;
}
/**
* megasas_make_sgl64 - Prepares 64 - bit SGL
* @ instance : Adapter soft state
* @ scp : SCSI command from the mid - layer
* @ mfi_sgl : SGL to be filled in
*
* If successful , this function returns the number of SG elements . Otherwise ,
* it returnes - 1.
*/
2006-01-15 00:20:43 +03:00
static int
2005-09-21 01:46:58 +04:00
megasas_make_sgl64 ( struct megasas_instance * instance , struct scsi_cmnd * scp ,
union megasas_sgl * mfi_sgl )
{
int i ;
int sge_count ;
struct scatterlist * os_sgl ;
/*
* Return 0 if there is no data transfer
*/
if ( ! scp - > request_buffer | | ! scp - > request_bufflen )
return 0 ;
if ( ! scp - > use_sg ) {
mfi_sgl - > sge64 [ 0 ] . phys_addr = pci_map_single ( instance - > pdev ,
scp - >
request_buffer ,
scp - >
request_bufflen ,
scp - >
sc_data_direction ) ;
mfi_sgl - > sge64 [ 0 ] . length = scp - > request_bufflen ;
return 1 ;
}
os_sgl = ( struct scatterlist * ) scp - > request_buffer ;
sge_count = pci_map_sg ( instance - > pdev , os_sgl , scp - > use_sg ,
scp - > sc_data_direction ) ;
for ( i = 0 ; i < sge_count ; i + + , os_sgl + + ) {
mfi_sgl - > sge64 [ i ] . length = sg_dma_len ( os_sgl ) ;
mfi_sgl - > sge64 [ i ] . phys_addr = sg_dma_address ( os_sgl ) ;
}
return sge_count ;
}
/**
* megasas_build_dcdb - Prepares a direct cdb ( DCDB ) command
* @ instance : Adapter soft state
* @ scp : SCSI command
* @ cmd : Command to be prepared in
*
* This function prepares CDB commands . These are typcially pass - through
* commands to the devices .
*/
2006-01-15 00:20:43 +03:00
static int
2005-09-21 01:46:58 +04:00
megasas_build_dcdb ( struct megasas_instance * instance , struct scsi_cmnd * scp ,
struct megasas_cmd * cmd )
{
u32 sge_sz ;
int sge_bytes ;
u32 is_logical ;
u32 device_id ;
u16 flags = 0 ;
struct megasas_pthru_frame * pthru ;
is_logical = MEGASAS_IS_LOGICAL ( scp ) ;
device_id = MEGASAS_DEV_INDEX ( instance , scp ) ;
pthru = ( struct megasas_pthru_frame * ) cmd - > frame ;
if ( scp - > sc_data_direction = = PCI_DMA_TODEVICE )
flags = MFI_FRAME_DIR_WRITE ;
else if ( scp - > sc_data_direction = = PCI_DMA_FROMDEVICE )
flags = MFI_FRAME_DIR_READ ;
else if ( scp - > sc_data_direction = = PCI_DMA_NONE )
flags = MFI_FRAME_DIR_NONE ;
/*
* Prepare the DCDB frame
*/
pthru - > cmd = ( is_logical ) ? MFI_CMD_LD_SCSI_IO : MFI_CMD_PD_SCSI_IO ;
pthru - > cmd_status = 0x0 ;
pthru - > scsi_status = 0x0 ;
pthru - > target_id = device_id ;
pthru - > lun = scp - > device - > lun ;
pthru - > cdb_len = scp - > cmd_len ;
pthru - > timeout = 0 ;
pthru - > flags = flags ;
pthru - > data_xfer_len = scp - > request_bufflen ;
memcpy ( pthru - > cdb , scp - > cmnd , scp - > cmd_len ) ;
/*
* Construct SGL
*/
sge_sz = ( IS_DMA64 ) ? sizeof ( struct megasas_sge64 ) :
sizeof ( struct megasas_sge32 ) ;
if ( IS_DMA64 ) {
pthru - > flags | = MFI_FRAME_SGL64 ;
pthru - > sge_count = megasas_make_sgl64 ( instance , scp ,
& pthru - > sgl ) ;
} else
pthru - > sge_count = megasas_make_sgl32 ( instance , scp ,
& pthru - > sgl ) ;
/*
* Sense info specific
*/
pthru - > sense_len = SCSI_SENSE_BUFFERSIZE ;
pthru - > sense_buf_phys_addr_hi = 0 ;
pthru - > sense_buf_phys_addr_lo = cmd - > sense_phys_addr ;
sge_bytes = sge_sz * pthru - > sge_count ;
/*
* Compute the total number of frames this command consumes . FW uses
* this number to pull sufficient number of frames from host memory .
*/
cmd - > frame_count = ( sge_bytes / MEGAMFI_FRAME_SIZE ) +
( ( sge_bytes % MEGAMFI_FRAME_SIZE ) ? 1 : 0 ) + 1 ;
if ( cmd - > frame_count > 7 )
cmd - > frame_count = 8 ;
return cmd - > frame_count ;
}
/**
* megasas_build_ldio - Prepares IOs to logical devices
* @ instance : Adapter soft state
* @ scp : SCSI command
* @ cmd : Command to to be prepared
*
* Frames ( and accompanying SGLs ) for regular SCSI IOs use this function .
*/
2006-01-15 00:20:43 +03:00
static int
2005-09-21 01:46:58 +04:00
megasas_build_ldio ( struct megasas_instance * instance , struct scsi_cmnd * scp ,
struct megasas_cmd * cmd )
{
u32 sge_sz ;
int sge_bytes ;
u32 device_id ;
u8 sc = scp - > cmnd [ 0 ] ;
u16 flags = 0 ;
struct megasas_io_frame * ldio ;
device_id = MEGASAS_DEV_INDEX ( instance , scp ) ;
ldio = ( struct megasas_io_frame * ) cmd - > frame ;
if ( scp - > sc_data_direction = = PCI_DMA_TODEVICE )
flags = MFI_FRAME_DIR_WRITE ;
else if ( scp - > sc_data_direction = = PCI_DMA_FROMDEVICE )
flags = MFI_FRAME_DIR_READ ;
/*
* Preare the Logical IO frame : 2 nd bit is zero for all read cmds
*/
ldio - > cmd = ( sc & 0x02 ) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ ;
ldio - > cmd_status = 0x0 ;
ldio - > scsi_status = 0x0 ;
ldio - > target_id = device_id ;
ldio - > timeout = 0 ;
ldio - > reserved_0 = 0 ;
ldio - > pad_0 = 0 ;
ldio - > flags = flags ;
ldio - > start_lba_hi = 0 ;
ldio - > access_byte = ( scp - > cmd_len ! = 6 ) ? scp - > cmnd [ 1 ] : 0 ;
/*
* 6 - byte READ ( 0x08 ) or WRITE ( 0x0A ) cdb
*/
if ( scp - > cmd_len = = 6 ) {
ldio - > lba_count = ( u32 ) scp - > cmnd [ 4 ] ;
ldio - > start_lba_lo = ( ( u32 ) scp - > cmnd [ 1 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 2 ] < < 8 ) | ( u32 ) scp - > cmnd [ 3 ] ;
ldio - > start_lba_lo & = 0x1FFFFF ;
}
/*
* 10 - byte READ ( 0x28 ) or WRITE ( 0x2A ) cdb
*/
else if ( scp - > cmd_len = = 10 ) {
ldio - > lba_count = ( u32 ) scp - > cmnd [ 8 ] |
( ( u32 ) scp - > cmnd [ 7 ] < < 8 ) ;
ldio - > start_lba_lo = ( ( u32 ) scp - > cmnd [ 2 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 3 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 4 ] < < 8 ) | ( u32 ) scp - > cmnd [ 5 ] ;
}
/*
* 12 - byte READ ( 0xA8 ) or WRITE ( 0xAA ) cdb
*/
else if ( scp - > cmd_len = = 12 ) {
ldio - > lba_count = ( ( u32 ) scp - > cmnd [ 6 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 7 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 8 ] < < 8 ) | ( u32 ) scp - > cmnd [ 9 ] ;
ldio - > start_lba_lo = ( ( u32 ) scp - > cmnd [ 2 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 3 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 4 ] < < 8 ) | ( u32 ) scp - > cmnd [ 5 ] ;
}
/*
* 16 - byte READ ( 0x88 ) or WRITE ( 0x8A ) cdb
*/
else if ( scp - > cmd_len = = 16 ) {
ldio - > lba_count = ( ( u32 ) scp - > cmnd [ 10 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 11 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 12 ] < < 8 ) | ( u32 ) scp - > cmnd [ 13 ] ;
ldio - > start_lba_lo = ( ( u32 ) scp - > cmnd [ 6 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 7 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 8 ] < < 8 ) | ( u32 ) scp - > cmnd [ 9 ] ;
ldio - > start_lba_hi = ( ( u32 ) scp - > cmnd [ 2 ] < < 24 ) |
( ( u32 ) scp - > cmnd [ 3 ] < < 16 ) |
( ( u32 ) scp - > cmnd [ 4 ] < < 8 ) | ( u32 ) scp - > cmnd [ 5 ] ;
}
/*
* Construct SGL
*/
sge_sz = ( IS_DMA64 ) ? sizeof ( struct megasas_sge64 ) :
sizeof ( struct megasas_sge32 ) ;
if ( IS_DMA64 ) {
ldio - > flags | = MFI_FRAME_SGL64 ;
ldio - > sge_count = megasas_make_sgl64 ( instance , scp , & ldio - > sgl ) ;
} else
ldio - > sge_count = megasas_make_sgl32 ( instance , scp , & ldio - > sgl ) ;
/*
* Sense info specific
*/
ldio - > sense_len = SCSI_SENSE_BUFFERSIZE ;
ldio - > sense_buf_phys_addr_hi = 0 ;
ldio - > sense_buf_phys_addr_lo = cmd - > sense_phys_addr ;
sge_bytes = sge_sz * ldio - > sge_count ;
cmd - > frame_count = ( sge_bytes / MEGAMFI_FRAME_SIZE ) +
( ( sge_bytes % MEGAMFI_FRAME_SIZE ) ? 1 : 0 ) + 1 ;
if ( cmd - > frame_count > 7 )
cmd - > frame_count = 8 ;
return cmd - > frame_count ;
}
/**
2006-01-25 22:53:25 +03:00
* megasas_is_ldio - Checks if the cmd is for logical drive
* @ scmd : SCSI command
*
* Called by megasas_queue_command to find out if the command to be queued
* is a logical drive command
2005-09-21 01:46:58 +04:00
*/
2006-01-25 22:53:25 +03:00
static inline int megasas_is_ldio ( struct scsi_cmnd * cmd )
2005-09-21 01:46:58 +04:00
{
2006-01-25 22:53:25 +03:00
if ( ! MEGASAS_IS_LOGICAL ( cmd ) )
return 0 ;
switch ( cmd - > cmnd [ 0 ] ) {
case READ_10 :
case WRITE_10 :
case READ_12 :
case WRITE_12 :
case READ_6 :
case WRITE_6 :
case READ_16 :
case WRITE_16 :
return 1 ;
default :
return 0 ;
2005-09-21 01:46:58 +04:00
}
}
/**
* megasas_queue_command - Queue entry point
* @ scmd : SCSI command to be queued
* @ done : Callback entry point
*/
static int
megasas_queue_command ( struct scsi_cmnd * scmd , void ( * done ) ( struct scsi_cmnd * ) )
{
u32 frame_count ;
unsigned long flags ;
struct megasas_cmd * cmd ;
struct megasas_instance * instance ;
instance = ( struct megasas_instance * )
scmd - > device - > host - > hostdata ;
scmd - > scsi_done = done ;
scmd - > result = 0 ;
2006-01-25 22:53:25 +03:00
if ( MEGASAS_IS_LOGICAL ( scmd ) & &
( scmd - > device - > id > = MEGASAS_MAX_LD | | scmd - > device - > lun ) ) {
scmd - > result = DID_BAD_TARGET < < 16 ;
goto out_done ;
2005-09-21 01:46:58 +04:00
}
2006-01-25 22:53:25 +03:00
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd )
return SCSI_MLQUEUE_HOST_BUSY ;
/*
* Logical drive command
*/
if ( megasas_is_ldio ( scmd ) )
frame_count = megasas_build_ldio ( instance , scmd , cmd ) ;
else
frame_count = megasas_build_dcdb ( instance , scmd , cmd ) ;
if ( ! frame_count )
goto out_return_cmd ;
2005-09-21 01:46:58 +04:00
cmd - > scmd = scmd ;
scmd - > SCp . ptr = ( char * ) cmd ;
scmd - > SCp . sent_command = jiffies ;
/*
* Issue the command to the FW
*/
spin_lock_irqsave ( & instance - > instance_lock , flags ) ;
instance - > fw_outstanding + + ;
spin_unlock_irqrestore ( & instance - > instance_lock , flags ) ;
writel ( ( ( cmd - > frame_phys_addr > > 3 ) | ( cmd - > frame_count - 1 ) ) ,
& instance - > reg_set - > inbound_queue_port ) ;
return 0 ;
2006-01-25 22:53:25 +03:00
out_return_cmd :
megasas_return_cmd ( instance , cmd ) ;
out_done :
done ( scmd ) ;
return 0 ;
2005-09-21 01:46:58 +04:00
}
/**
* megasas_wait_for_outstanding - Wait for all outstanding cmds
* @ instance : Adapter soft state
*
* This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to
* complete all its outstanding commands . Returns error if one or more IOs
* are pending after this time period . It also marks the controller dead .
*/
static int megasas_wait_for_outstanding ( struct megasas_instance * instance )
{
int i ;
u32 wait_time = MEGASAS_RESET_WAIT_TIME ;
for ( i = 0 ; i < wait_time ; i + + ) {
if ( ! instance - > fw_outstanding )
break ;
if ( ! ( i % MEGASAS_RESET_NOTICE_INTERVAL ) ) {
printk ( KERN_NOTICE " megasas: [%2d]waiting for %d "
" commands to complete \n " , i ,
instance - > fw_outstanding ) ;
}
msleep ( 1000 ) ;
}
if ( instance - > fw_outstanding ) {
instance - > hw_crit_error = 1 ;
return FAILED ;
}
return SUCCESS ;
}
/**
* megasas_generic_reset - Generic reset routine
* @ scmd : Mid - layer SCSI command
*
* This routine implements a generic reset handler for device , bus and host
* reset requests . Device , bus and host specific reset handlers can use this
* function after they do their specific tasks .
*/
static int megasas_generic_reset ( struct scsi_cmnd * scmd )
{
int ret_val ;
struct megasas_instance * instance ;
instance = ( struct megasas_instance * ) scmd - > device - > host - > hostdata ;
2005-10-25 02:04:36 +04:00
scmd_printk ( KERN_NOTICE , scmd , " megasas: RESET -%ld cmd=%x \n " ,
scmd - > serial_number , scmd - > cmnd [ 0 ] ) ;
2005-09-21 01:46:58 +04:00
if ( instance - > hw_crit_error ) {
printk ( KERN_ERR " megasas: cannot recover from previous reset "
" failures \n " ) ;
return FAILED ;
}
ret_val = megasas_wait_for_outstanding ( instance ) ;
if ( ret_val = = SUCCESS )
printk ( KERN_NOTICE " megasas: reset successful \n " ) ;
else
printk ( KERN_ERR " megasas: failed to do reset \n " ) ;
return ret_val ;
}
static enum scsi_eh_timer_return megasas_reset_timer ( struct scsi_cmnd * scmd )
{
unsigned long seconds ;
if ( scmd - > SCp . ptr ) {
seconds = ( jiffies - scmd - > SCp . sent_command ) / HZ ;
if ( seconds < 90 ) {
return EH_RESET_TIMER ;
} else {
return EH_NOT_HANDLED ;
}
}
return EH_HANDLED ;
}
/**
* megasas_reset_device - Device reset handler entry point
*/
static int megasas_reset_device ( struct scsi_cmnd * scmd )
{
int ret ;
/*
* First wait for all commands to complete
*/
ret = megasas_generic_reset ( scmd ) ;
return ret ;
}
/**
* megasas_reset_bus_host - Bus & host reset handler entry point
*/
static int megasas_reset_bus_host ( struct scsi_cmnd * scmd )
{
int ret ;
/*
* Frist wait for all commands to complete
*/
ret = megasas_generic_reset ( scmd ) ;
return ret ;
}
/**
* megasas_service_aen - Processes an event notification
* @ instance : Adapter soft state
* @ cmd : AEN command completed by the ISR
*
* For AEN , driver sends a command down to FW that is held by the FW till an
* event occurs . When an event of interest occurs , FW completes the command
* that it was previously holding .
*
* This routines sends SIGIO signal to processes that have registered with the
* driver for AEN .
*/
static void
megasas_service_aen ( struct megasas_instance * instance , struct megasas_cmd * cmd )
{
/*
* Don ' t signal app if it is just an aborted previously registered aen
*/
if ( ! cmd - > abort_aen )
kill_fasync ( & megasas_async_queue , SIGIO , POLL_IN ) ;
else
cmd - > abort_aen = 0 ;
instance - > aen_cmd = NULL ;
megasas_return_cmd ( instance , cmd ) ;
}
/*
* Scsi host template for megaraid_sas driver
*/
static struct scsi_host_template megasas_template = {
. module = THIS_MODULE ,
. name = " LSI Logic SAS based MegaRAID driver " ,
. proc_name = " megaraid_sas " ,
. queuecommand = megasas_queue_command ,
. eh_device_reset_handler = megasas_reset_device ,
. eh_bus_reset_handler = megasas_reset_bus_host ,
. eh_host_reset_handler = megasas_reset_bus_host ,
. eh_timed_out = megasas_reset_timer ,
. use_clustering = ENABLE_CLUSTERING ,
} ;
/**
* megasas_complete_int_cmd - Completes an internal command
* @ instance : Adapter soft state
* @ cmd : Command to be completed
*
* The megasas_issue_blocked_cmd ( ) function waits for a command to complete
* after it issues a command . This function wakes up that waiting routine by
* calling wake_up ( ) on the wait queue .
*/
static void
megasas_complete_int_cmd ( struct megasas_instance * instance ,
struct megasas_cmd * cmd )
{
cmd - > cmd_status = cmd - > frame - > io . cmd_status ;
if ( cmd - > cmd_status = = ENODATA ) {
cmd - > cmd_status = 0 ;
}
wake_up ( & instance - > int_cmd_wait_q ) ;
}
/**
* megasas_complete_abort - Completes aborting a command
* @ instance : Adapter soft state
* @ cmd : Cmd that was issued to abort another cmd
*
* The megasas_issue_blocked_abort_cmd ( ) function waits on abort_cmd_wait_q
* after it issues an abort on a previously issued command . This function
* wakes up all functions waiting on the same wait queue .
*/
static void
megasas_complete_abort ( struct megasas_instance * instance ,
struct megasas_cmd * cmd )
{
if ( cmd - > sync_cmd ) {
cmd - > sync_cmd = 0 ;
cmd - > cmd_status = 0 ;
wake_up ( & instance - > abort_cmd_wait_q ) ;
}
return ;
}
/**
* megasas_unmap_sgbuf - Unmap SG buffers
* @ instance : Adapter soft state
* @ cmd : Completed command
*/
2006-01-15 00:20:43 +03:00
static void
2005-09-21 01:46:58 +04:00
megasas_unmap_sgbuf ( struct megasas_instance * instance , struct megasas_cmd * cmd )
{
dma_addr_t buf_h ;
u8 opcode ;
if ( cmd - > scmd - > use_sg ) {
pci_unmap_sg ( instance - > pdev , cmd - > scmd - > request_buffer ,
cmd - > scmd - > use_sg , cmd - > scmd - > sc_data_direction ) ;
return ;
}
if ( ! cmd - > scmd - > request_bufflen )
return ;
opcode = cmd - > frame - > hdr . cmd ;
if ( ( opcode = = MFI_CMD_LD_READ ) | | ( opcode = = MFI_CMD_LD_WRITE ) ) {
if ( IS_DMA64 )
buf_h = cmd - > frame - > io . sgl . sge64 [ 0 ] . phys_addr ;
else
buf_h = cmd - > frame - > io . sgl . sge32 [ 0 ] . phys_addr ;
} else {
if ( IS_DMA64 )
buf_h = cmd - > frame - > pthru . sgl . sge64 [ 0 ] . phys_addr ;
else
buf_h = cmd - > frame - > pthru . sgl . sge32 [ 0 ] . phys_addr ;
}
pci_unmap_single ( instance - > pdev , buf_h , cmd - > scmd - > request_bufflen ,
cmd - > scmd - > sc_data_direction ) ;
return ;
}
/**
* megasas_complete_cmd - Completes a command
* @ instance : Adapter soft state
* @ cmd : Command to be completed
* @ alt_status : If non - zero , use this value as status to
* SCSI mid - layer instead of the value returned
* by the FW . This should be used if caller wants
* an alternate status ( as in the case of aborted
* commands )
*/
2006-01-15 00:20:43 +03:00
static void
2005-09-21 01:46:58 +04:00
megasas_complete_cmd ( struct megasas_instance * instance , struct megasas_cmd * cmd ,
u8 alt_status )
{
int exception = 0 ;
struct megasas_header * hdr = & cmd - > frame - > hdr ;
unsigned long flags ;
if ( cmd - > scmd ) {
cmd - > scmd - > SCp . ptr = ( char * ) 0 ;
}
switch ( hdr - > cmd ) {
case MFI_CMD_PD_SCSI_IO :
case MFI_CMD_LD_SCSI_IO :
/*
* MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been
* issued either through an IO path or an IOCTL path . If it
* was via IOCTL , we will send it to internal completion .
*/
if ( cmd - > sync_cmd ) {
cmd - > sync_cmd = 0 ;
megasas_complete_int_cmd ( instance , cmd ) ;
break ;
}
/*
* Don ' t export physical disk devices to mid - layer .
*/
if ( ! MEGASAS_IS_LOGICAL ( cmd - > scmd ) & &
( hdr - > cmd_status = = MFI_STAT_OK ) & &
( cmd - > scmd - > cmnd [ 0 ] = = INQUIRY ) ) {
if ( ( ( * ( u8 * ) cmd - > scmd - > request_buffer ) & 0x1F ) = =
TYPE_DISK ) {
cmd - > scmd - > result = DID_BAD_TARGET < < 16 ;
exception = 1 ;
}
}
case MFI_CMD_LD_READ :
case MFI_CMD_LD_WRITE :
if ( alt_status ) {
cmd - > scmd - > result = alt_status < < 16 ;
exception = 1 ;
}
if ( exception ) {
spin_lock_irqsave ( & instance - > instance_lock , flags ) ;
instance - > fw_outstanding - - ;
spin_unlock_irqrestore ( & instance - > instance_lock , flags ) ;
megasas_unmap_sgbuf ( instance , cmd ) ;
cmd - > scmd - > scsi_done ( cmd - > scmd ) ;
megasas_return_cmd ( instance , cmd ) ;
break ;
}
switch ( hdr - > cmd_status ) {
case MFI_STAT_OK :
cmd - > scmd - > result = DID_OK < < 16 ;
break ;
case MFI_STAT_SCSI_IO_FAILED :
case MFI_STAT_LD_INIT_IN_PROGRESS :
cmd - > scmd - > result =
( DID_ERROR < < 16 ) | hdr - > scsi_status ;
break ;
case MFI_STAT_SCSI_DONE_WITH_ERROR :
cmd - > scmd - > result = ( DID_OK < < 16 ) | hdr - > scsi_status ;
if ( hdr - > scsi_status = = SAM_STAT_CHECK_CONDITION ) {
memset ( cmd - > scmd - > sense_buffer , 0 ,
SCSI_SENSE_BUFFERSIZE ) ;
memcpy ( cmd - > scmd - > sense_buffer , cmd - > sense ,
hdr - > sense_len ) ;
cmd - > scmd - > result | = DRIVER_SENSE < < 24 ;
}
break ;
case MFI_STAT_LD_OFFLINE :
case MFI_STAT_DEVICE_NOT_FOUND :
cmd - > scmd - > result = DID_BAD_TARGET < < 16 ;
break ;
default :
printk ( KERN_DEBUG " megasas: MFI FW status %#x \n " ,
hdr - > cmd_status ) ;
cmd - > scmd - > result = DID_ERROR < < 16 ;
break ;
}
spin_lock_irqsave ( & instance - > instance_lock , flags ) ;
instance - > fw_outstanding - - ;
spin_unlock_irqrestore ( & instance - > instance_lock , flags ) ;
megasas_unmap_sgbuf ( instance , cmd ) ;
cmd - > scmd - > scsi_done ( cmd - > scmd ) ;
megasas_return_cmd ( instance , cmd ) ;
break ;
case MFI_CMD_SMP :
case MFI_CMD_STP :
case MFI_CMD_DCMD :
/*
* See if got an event notification
*/
if ( cmd - > frame - > dcmd . opcode = = MR_DCMD_CTRL_EVENT_WAIT )
megasas_service_aen ( instance , cmd ) ;
else
megasas_complete_int_cmd ( instance , cmd ) ;
break ;
case MFI_CMD_ABORT :
/*
* Cmd issued to abort another cmd returned
*/
megasas_complete_abort ( instance , cmd ) ;
break ;
default :
printk ( " megasas: Unknown command completed! [0x%X] \n " ,
hdr - > cmd ) ;
break ;
}
}
/**
* megasas_deplete_reply_queue - Processes all completed commands
* @ instance : Adapter soft state
* @ alt_status : Alternate status to be returned to
* SCSI mid - layer instead of the status
* returned by the FW
*/
2006-01-15 00:20:43 +03:00
static int
2005-09-21 01:46:58 +04:00
megasas_deplete_reply_queue ( struct megasas_instance * instance , u8 alt_status )
{
u32 status ;
u32 producer ;
u32 consumer ;
u32 context ;
struct megasas_cmd * cmd ;
/*
* Check if it is our interrupt
*/
status = readl ( & instance - > reg_set - > outbound_intr_status ) ;
if ( ! ( status & MFI_OB_INTR_STATUS_MASK ) ) {
return IRQ_NONE ;
}
/*
* Clear the interrupt by writing back the same value
*/
writel ( status , & instance - > reg_set - > outbound_intr_status ) ;
producer = * instance - > producer ;
consumer = * instance - > consumer ;
while ( consumer ! = producer ) {
context = instance - > reply_queue [ consumer ] ;
cmd = instance - > cmd_list [ context ] ;
megasas_complete_cmd ( instance , cmd , alt_status ) ;
consumer + + ;
if ( consumer = = ( instance - > max_fw_cmds + 1 ) ) {
consumer = 0 ;
}
}
* instance - > consumer = producer ;
return IRQ_HANDLED ;
}
/**
* megasas_isr - isr entry point
*/
static irqreturn_t megasas_isr ( int irq , void * devp , struct pt_regs * regs )
{
return megasas_deplete_reply_queue ( ( struct megasas_instance * ) devp ,
DID_OK ) ;
}
/**
* megasas_transition_to_ready - Move the FW to READY state
* @ reg_set : MFI register set
*
* During the initialization , FW passes can potentially be in any one of
* several possible states . If the FW in operational , waiting - for - handshake
* states , driver must take steps to bring it to ready state . Otherwise , it
* has to wait for the ready state .
*/
static int
megasas_transition_to_ready ( struct megasas_register_set __iomem * reg_set )
{
int i ;
u8 max_wait ;
u32 fw_state ;
u32 cur_state ;
fw_state = readl ( & reg_set - > outbound_msg_0 ) & MFI_STATE_MASK ;
while ( fw_state ! = MFI_STATE_READY ) {
printk ( KERN_INFO " megasas: Waiting for FW to come to ready "
" state \n " ) ;
switch ( fw_state ) {
case MFI_STATE_FAULT :
printk ( KERN_DEBUG " megasas: FW in FAULT state!! \n " ) ;
return - ENODEV ;
case MFI_STATE_WAIT_HANDSHAKE :
/*
* Set the CLR bit in inbound doorbell
*/
writel ( MFI_INIT_CLEAR_HANDSHAKE ,
& reg_set - > inbound_doorbell ) ;
max_wait = 2 ;
cur_state = MFI_STATE_WAIT_HANDSHAKE ;
break ;
case MFI_STATE_OPERATIONAL :
/*
* Bring it to READY state ; assuming max wait 2 secs
*/
megasas_disable_intr ( reg_set ) ;
writel ( MFI_INIT_READY , & reg_set - > inbound_doorbell ) ;
max_wait = 10 ;
cur_state = MFI_STATE_OPERATIONAL ;
break ;
case MFI_STATE_UNDEFINED :
/*
* This state should not last for more than 2 seconds
*/
max_wait = 2 ;
cur_state = MFI_STATE_UNDEFINED ;
break ;
case MFI_STATE_BB_INIT :
max_wait = 2 ;
cur_state = MFI_STATE_BB_INIT ;
break ;
case MFI_STATE_FW_INIT :
max_wait = 20 ;
cur_state = MFI_STATE_FW_INIT ;
break ;
case MFI_STATE_FW_INIT_2 :
max_wait = 20 ;
cur_state = MFI_STATE_FW_INIT_2 ;
break ;
case MFI_STATE_DEVICE_SCAN :
max_wait = 20 ;
cur_state = MFI_STATE_DEVICE_SCAN ;
break ;
case MFI_STATE_FLUSH_CACHE :
max_wait = 20 ;
cur_state = MFI_STATE_FLUSH_CACHE ;
break ;
default :
printk ( KERN_DEBUG " megasas: Unknown state 0x%x \n " ,
fw_state ) ;
return - ENODEV ;
}
/*
* The cur_state should not last for more than max_wait secs
*/
for ( i = 0 ; i < ( max_wait * 1000 ) ; i + + ) {
fw_state = MFI_STATE_MASK &
readl ( & reg_set - > outbound_msg_0 ) ;
if ( fw_state = = cur_state ) {
msleep ( 1 ) ;
} else
break ;
}
/*
* Return error if fw_state hasn ' t changed after max_wait
*/
if ( fw_state = = cur_state ) {
printk ( KERN_DEBUG " FW state [%d] hasn't changed "
" in %d secs \n " , fw_state , max_wait ) ;
return - ENODEV ;
}
} ;
return 0 ;
}
/**
* megasas_teardown_frame_pool - Destroy the cmd frame DMA pool
* @ instance : Adapter soft state
*/
static void megasas_teardown_frame_pool ( struct megasas_instance * instance )
{
int i ;
u32 max_cmd = instance - > max_fw_cmds ;
struct megasas_cmd * cmd ;
if ( ! instance - > frame_dma_pool )
return ;
/*
* Return all frames to pool
*/
for ( i = 0 ; i < max_cmd ; i + + ) {
cmd = instance - > cmd_list [ i ] ;
if ( cmd - > frame )
pci_pool_free ( instance - > frame_dma_pool , cmd - > frame ,
cmd - > frame_phys_addr ) ;
if ( cmd - > sense )
pci_pool_free ( instance - > sense_dma_pool , cmd - > frame ,
cmd - > sense_phys_addr ) ;
}
/*
* Now destroy the pool itself
*/
pci_pool_destroy ( instance - > frame_dma_pool ) ;
pci_pool_destroy ( instance - > sense_dma_pool ) ;
instance - > frame_dma_pool = NULL ;
instance - > sense_dma_pool = NULL ;
}
/**
* megasas_create_frame_pool - Creates DMA pool for cmd frames
* @ instance : Adapter soft state
*
* Each command packet has an embedded DMA memory buffer that is used for
* filling MFI frame and the SG list that immediately follows the frame . This
* function creates those DMA memory buffers for each command packet by using
* PCI pool facility .
*/
static int megasas_create_frame_pool ( struct megasas_instance * instance )
{
int i ;
u32 max_cmd ;
u32 sge_sz ;
u32 sgl_sz ;
u32 total_sz ;
u32 frame_count ;
struct megasas_cmd * cmd ;
max_cmd = instance - > max_fw_cmds ;
/*
* Size of our frame is 64 bytes for MFI frame , followed by max SG
* elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer
*/
sge_sz = ( IS_DMA64 ) ? sizeof ( struct megasas_sge64 ) :
sizeof ( struct megasas_sge32 ) ;
/*
* Calculated the number of 64 byte frames required for SGL
*/
sgl_sz = sge_sz * instance - > max_num_sge ;
frame_count = ( sgl_sz + MEGAMFI_FRAME_SIZE - 1 ) / MEGAMFI_FRAME_SIZE ;
/*
* We need one extra frame for the MFI command
*/
frame_count + + ;
total_sz = MEGAMFI_FRAME_SIZE * frame_count ;
/*
* Use DMA pool facility provided by PCI layer
*/
instance - > frame_dma_pool = pci_pool_create ( " megasas frame pool " ,
instance - > pdev , total_sz , 64 ,
0 ) ;
if ( ! instance - > frame_dma_pool ) {
printk ( KERN_DEBUG " megasas: failed to setup frame pool \n " ) ;
return - ENOMEM ;
}
instance - > sense_dma_pool = pci_pool_create ( " megasas sense pool " ,
instance - > pdev , 128 , 4 , 0 ) ;
if ( ! instance - > sense_dma_pool ) {
printk ( KERN_DEBUG " megasas: failed to setup sense pool \n " ) ;
pci_pool_destroy ( instance - > frame_dma_pool ) ;
instance - > frame_dma_pool = NULL ;
return - ENOMEM ;
}
/*
* Allocate and attach a frame to each of the commands in cmd_list .
* By making cmd - > index as the context instead of the & cmd , we can
* always use 32 bit context regardless of the architecture
*/
for ( i = 0 ; i < max_cmd ; i + + ) {
cmd = instance - > cmd_list [ i ] ;
cmd - > frame = pci_pool_alloc ( instance - > frame_dma_pool ,
GFP_KERNEL , & cmd - > frame_phys_addr ) ;
cmd - > sense = pci_pool_alloc ( instance - > sense_dma_pool ,
GFP_KERNEL , & cmd - > sense_phys_addr ) ;
/*
* megasas_teardown_frame_pool ( ) takes care of freeing
* whatever has been allocated
*/
if ( ! cmd - > frame | | ! cmd - > sense ) {
printk ( KERN_DEBUG " megasas: pci_pool_alloc failed \n " ) ;
megasas_teardown_frame_pool ( instance ) ;
return - ENOMEM ;
}
cmd - > frame - > io . context = cmd - > index ;
}
return 0 ;
}
/**
* megasas_free_cmds - Free all the cmds in the free cmd pool
* @ instance : Adapter soft state
*/
static void megasas_free_cmds ( struct megasas_instance * instance )
{
int i ;
/* First free the MFI frame pool */
megasas_teardown_frame_pool ( instance ) ;
/* Free all the commands in the cmd_list */
for ( i = 0 ; i < instance - > max_fw_cmds ; i + + )
kfree ( instance - > cmd_list [ i ] ) ;
/* Free the cmd_list buffer itself */
kfree ( instance - > cmd_list ) ;
instance - > cmd_list = NULL ;
INIT_LIST_HEAD ( & instance - > cmd_pool ) ;
}
/**
* megasas_alloc_cmds - Allocates the command packets
* @ instance : Adapter soft state
*
* Each command that is issued to the FW , whether IO commands from the OS or
* internal commands like IOCTLs , are wrapped in local data structure called
* megasas_cmd . The frame embedded in this megasas_cmd is actually issued to
* the FW .
*
* Each frame has a 32 - bit field called context ( tag ) . This context is used
* to get back the megasas_cmd from the frame when a frame gets completed in
* the ISR . Typically the address of the megasas_cmd itself would be used as
* the context . But we wanted to keep the differences between 32 and 64 bit
* systems to the mininum . We always use 32 bit integers for the context . In
* this driver , the 32 bit values are the indices into an array cmd_list .
* This array is used only to look up the megasas_cmd given the context . The
* free commands themselves are maintained in a linked list called cmd_pool .
*/
static int megasas_alloc_cmds ( struct megasas_instance * instance )
{
int i ;
int j ;
u32 max_cmd ;
struct megasas_cmd * cmd ;
max_cmd = instance - > max_fw_cmds ;
/*
* instance - > cmd_list is an array of struct megasas_cmd pointers .
* Allocate the dynamic array first and then allocate individual
* commands .
*/
instance - > cmd_list = kmalloc ( sizeof ( struct megasas_cmd * ) * max_cmd ,
GFP_KERNEL ) ;
if ( ! instance - > cmd_list ) {
printk ( KERN_DEBUG " megasas: out of memory \n " ) ;
return - ENOMEM ;
}
memset ( instance - > cmd_list , 0 , sizeof ( struct megasas_cmd * ) * max_cmd ) ;
for ( i = 0 ; i < max_cmd ; i + + ) {
instance - > cmd_list [ i ] = kmalloc ( sizeof ( struct megasas_cmd ) ,
GFP_KERNEL ) ;
if ( ! instance - > cmd_list [ i ] ) {
for ( j = 0 ; j < i ; j + + )
kfree ( instance - > cmd_list [ j ] ) ;
kfree ( instance - > cmd_list ) ;
instance - > cmd_list = NULL ;
return - ENOMEM ;
}
}
/*
* Add all the commands to command pool ( instance - > cmd_pool )
*/
for ( i = 0 ; i < max_cmd ; i + + ) {
cmd = instance - > cmd_list [ i ] ;
memset ( cmd , 0 , sizeof ( struct megasas_cmd ) ) ;
cmd - > index = i ;
cmd - > instance = instance ;
list_add_tail ( & cmd - > list , & instance - > cmd_pool ) ;
}
/*
* Create a frame pool and assign one frame to each cmd
*/
if ( megasas_create_frame_pool ( instance ) ) {
printk ( KERN_DEBUG " megasas: Error creating frame DMA pool \n " ) ;
megasas_free_cmds ( instance ) ;
}
return 0 ;
}
/**
* megasas_get_controller_info - Returns FW ' s controller structure
* @ instance : Adapter soft state
* @ ctrl_info : Controller information structure
*
* Issues an internal command ( DCMD ) to get the FW ' s controller structure .
* This information is mainly used to find out the maximum IO transfer per
* command supported by the FW .
*/
static int
megasas_get_ctrl_info ( struct megasas_instance * instance ,
struct megasas_ctrl_info * ctrl_info )
{
int ret = 0 ;
struct megasas_cmd * cmd ;
struct megasas_dcmd_frame * dcmd ;
struct megasas_ctrl_info * ci ;
dma_addr_t ci_h = 0 ;
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd ) {
printk ( KERN_DEBUG " megasas: Failed to get a free cmd \n " ) ;
return - ENOMEM ;
}
dcmd = & cmd - > frame - > dcmd ;
ci = pci_alloc_consistent ( instance - > pdev ,
sizeof ( struct megasas_ctrl_info ) , & ci_h ) ;
if ( ! ci ) {
printk ( KERN_DEBUG " Failed to alloc mem for ctrl info \n " ) ;
megasas_return_cmd ( instance , cmd ) ;
return - ENOMEM ;
}
memset ( ci , 0 , sizeof ( * ci ) ) ;
memset ( dcmd - > mbox . b , 0 , MFI_MBOX_SIZE ) ;
dcmd - > cmd = MFI_CMD_DCMD ;
dcmd - > cmd_status = 0xFF ;
dcmd - > sge_count = 1 ;
dcmd - > flags = MFI_FRAME_DIR_READ ;
dcmd - > timeout = 0 ;
dcmd - > data_xfer_len = sizeof ( struct megasas_ctrl_info ) ;
dcmd - > opcode = MR_DCMD_CTRL_GET_INFO ;
dcmd - > sgl . sge32 [ 0 ] . phys_addr = ci_h ;
dcmd - > sgl . sge32 [ 0 ] . length = sizeof ( struct megasas_ctrl_info ) ;
if ( ! megasas_issue_polled ( instance , cmd ) ) {
ret = 0 ;
memcpy ( ctrl_info , ci , sizeof ( struct megasas_ctrl_info ) ) ;
} else {
ret = - 1 ;
}
pci_free_consistent ( instance - > pdev , sizeof ( struct megasas_ctrl_info ) ,
ci , ci_h ) ;
megasas_return_cmd ( instance , cmd ) ;
return ret ;
}
/**
* megasas_init_mfi - Initializes the FW
* @ instance : Adapter soft state
*
* This is the main function for initializing MFI firmware .
*/
static int megasas_init_mfi ( struct megasas_instance * instance )
{
u32 context_sz ;
u32 reply_q_sz ;
u32 max_sectors_1 ;
u32 max_sectors_2 ;
struct megasas_register_set __iomem * reg_set ;
struct megasas_cmd * cmd ;
struct megasas_ctrl_info * ctrl_info ;
struct megasas_init_frame * init_frame ;
struct megasas_init_queue_info * initq_info ;
dma_addr_t init_frame_h ;
dma_addr_t initq_info_h ;
/*
* Map the message registers
*/
instance - > base_addr = pci_resource_start ( instance - > pdev , 0 ) ;
if ( pci_request_regions ( instance - > pdev , " megasas: LSI Logic " ) ) {
printk ( KERN_DEBUG " megasas: IO memory region busy! \n " ) ;
return - EBUSY ;
}
instance - > reg_set = ioremap_nocache ( instance - > base_addr , 8192 ) ;
if ( ! instance - > reg_set ) {
printk ( KERN_DEBUG " megasas: Failed to map IO mem \n " ) ;
goto fail_ioremap ;
}
reg_set = instance - > reg_set ;
/*
* We expect the FW state to be READY
*/
if ( megasas_transition_to_ready ( instance - > reg_set ) )
goto fail_ready_state ;
/*
* Get various operational parameters from status register
*/
instance - > max_fw_cmds = readl ( & reg_set - > outbound_msg_0 ) & 0x00FFFF ;
instance - > max_num_sge = ( readl ( & reg_set - > outbound_msg_0 ) & 0xFF0000 ) > >
0x10 ;
/*
* Create a pool of commands
*/
if ( megasas_alloc_cmds ( instance ) )
goto fail_alloc_cmds ;
/*
* Allocate memory for reply queue . Length of reply queue should
* be _one_ more than the maximum commands handled by the firmware .
*
* Note : When FW completes commands , it places corresponding contex
* values in this circular reply queue . This circular queue is a fairly
* typical producer - consumer queue . FW is the producer ( of completed
* commands ) and the driver is the consumer .
*/
context_sz = sizeof ( u32 ) ;
reply_q_sz = context_sz * ( instance - > max_fw_cmds + 1 ) ;
instance - > reply_queue = pci_alloc_consistent ( instance - > pdev ,
reply_q_sz ,
& instance - > reply_queue_h ) ;
if ( ! instance - > reply_queue ) {
printk ( KERN_DEBUG " megasas: Out of DMA mem for reply queue \n " ) ;
goto fail_reply_queue ;
}
/*
* Prepare a init frame . Note the init frame points to queue info
* structure . Each frame has SGL allocated after first 64 bytes . For
* this frame - since we don ' t need any SGL - we use SGL ' s space as
* queue info structure
*
* We will not get a NULL command below . We just created the pool .
*/
cmd = megasas_get_cmd ( instance ) ;
init_frame = ( struct megasas_init_frame * ) cmd - > frame ;
initq_info = ( struct megasas_init_queue_info * )
( ( unsigned long ) init_frame + 64 ) ;
init_frame_h = cmd - > frame_phys_addr ;
initq_info_h = init_frame_h + 64 ;
memset ( init_frame , 0 , MEGAMFI_FRAME_SIZE ) ;
memset ( initq_info , 0 , sizeof ( struct megasas_init_queue_info ) ) ;
initq_info - > reply_queue_entries = instance - > max_fw_cmds + 1 ;
initq_info - > reply_queue_start_phys_addr_lo = instance - > reply_queue_h ;
initq_info - > producer_index_phys_addr_lo = instance - > producer_h ;
initq_info - > consumer_index_phys_addr_lo = instance - > consumer_h ;
init_frame - > cmd = MFI_CMD_INIT ;
init_frame - > cmd_status = 0xFF ;
init_frame - > queue_info_new_phys_addr_lo = initq_info_h ;
init_frame - > data_xfer_len = sizeof ( struct megasas_init_queue_info ) ;
/*
* Issue the init frame in polled mode
*/
if ( megasas_issue_polled ( instance , cmd ) ) {
printk ( KERN_DEBUG " megasas: Failed to init firmware \n " ) ;
goto fail_fw_init ;
}
megasas_return_cmd ( instance , cmd ) ;
ctrl_info = kmalloc ( sizeof ( struct megasas_ctrl_info ) , GFP_KERNEL ) ;
/*
* Compute the max allowed sectors per IO : The controller info has two
* limits on max sectors . Driver should use the minimum of these two .
*
* 1 < < stripe_sz_ops . min = max sectors per strip
*
* Note that older firmwares ( < FW ver 30 ) didn ' t report information
* to calculate max_sectors_1 . So the number ended up as zero always .
*/
if ( ctrl_info & & ! megasas_get_ctrl_info ( instance , ctrl_info ) ) {
max_sectors_1 = ( 1 < < ctrl_info - > stripe_sz_ops . min ) *
ctrl_info - > max_strips_per_io ;
max_sectors_2 = ctrl_info - > max_request_size ;
instance - > max_sectors_per_req = ( max_sectors_1 < max_sectors_2 )
? max_sectors_1 : max_sectors_2 ;
} else
instance - > max_sectors_per_req = instance - > max_num_sge *
PAGE_SIZE / 512 ;
kfree ( ctrl_info ) ;
return 0 ;
fail_fw_init :
megasas_return_cmd ( instance , cmd ) ;
pci_free_consistent ( instance - > pdev , reply_q_sz ,
instance - > reply_queue , instance - > reply_queue_h ) ;
fail_reply_queue :
megasas_free_cmds ( instance ) ;
fail_alloc_cmds :
fail_ready_state :
iounmap ( instance - > reg_set ) ;
fail_ioremap :
pci_release_regions ( instance - > pdev ) ;
return - EINVAL ;
}
/**
* megasas_release_mfi - Reverses the FW initialization
* @ intance : Adapter soft state
*/
static void megasas_release_mfi ( struct megasas_instance * instance )
{
u32 reply_q_sz = sizeof ( u32 ) * ( instance - > max_fw_cmds + 1 ) ;
pci_free_consistent ( instance - > pdev , reply_q_sz ,
instance - > reply_queue , instance - > reply_queue_h ) ;
megasas_free_cmds ( instance ) ;
iounmap ( instance - > reg_set ) ;
pci_release_regions ( instance - > pdev ) ;
}
/**
* megasas_get_seq_num - Gets latest event sequence numbers
* @ instance : Adapter soft state
* @ eli : FW event log sequence numbers information
*
* FW maintains a log of all events in a non - volatile area . Upper layers would
* usually find out the latest sequence number of the events , the seq number at
* the boot etc . They would " read " all the events below the latest seq number
* by issuing a direct fw cmd ( DCMD ) . For the future events ( beyond latest seq
* number ) , they would subsribe to AEN ( asynchronous event notification ) and
* wait for the events to happen .
*/
static int
megasas_get_seq_num ( struct megasas_instance * instance ,
struct megasas_evt_log_info * eli )
{
struct megasas_cmd * cmd ;
struct megasas_dcmd_frame * dcmd ;
struct megasas_evt_log_info * el_info ;
dma_addr_t el_info_h = 0 ;
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd ) {
return - ENOMEM ;
}
dcmd = & cmd - > frame - > dcmd ;
el_info = pci_alloc_consistent ( instance - > pdev ,
sizeof ( struct megasas_evt_log_info ) ,
& el_info_h ) ;
if ( ! el_info ) {
megasas_return_cmd ( instance , cmd ) ;
return - ENOMEM ;
}
memset ( el_info , 0 , sizeof ( * el_info ) ) ;
memset ( dcmd - > mbox . b , 0 , MFI_MBOX_SIZE ) ;
dcmd - > cmd = MFI_CMD_DCMD ;
dcmd - > cmd_status = 0x0 ;
dcmd - > sge_count = 1 ;
dcmd - > flags = MFI_FRAME_DIR_READ ;
dcmd - > timeout = 0 ;
dcmd - > data_xfer_len = sizeof ( struct megasas_evt_log_info ) ;
dcmd - > opcode = MR_DCMD_CTRL_EVENT_GET_INFO ;
dcmd - > sgl . sge32 [ 0 ] . phys_addr = el_info_h ;
dcmd - > sgl . sge32 [ 0 ] . length = sizeof ( struct megasas_evt_log_info ) ;
megasas_issue_blocked_cmd ( instance , cmd ) ;
/*
* Copy the data back into callers buffer
*/
memcpy ( eli , el_info , sizeof ( struct megasas_evt_log_info ) ) ;
pci_free_consistent ( instance - > pdev , sizeof ( struct megasas_evt_log_info ) ,
el_info , el_info_h ) ;
megasas_return_cmd ( instance , cmd ) ;
return 0 ;
}
/**
* megasas_register_aen - Registers for asynchronous event notification
* @ instance : Adapter soft state
* @ seq_num : The starting sequence number
* @ class_locale : Class of the event
*
* This function subscribes for AEN for events beyond the @ seq_num . It requests
* to be notified if and only if the event is of type @ class_locale
*/
static int
megasas_register_aen ( struct megasas_instance * instance , u32 seq_num ,
u32 class_locale_word )
{
int ret_val ;
struct megasas_cmd * cmd ;
struct megasas_dcmd_frame * dcmd ;
union megasas_evt_class_locale curr_aen ;
union megasas_evt_class_locale prev_aen ;
/*
* If there an AEN pending already ( aen_cmd ) , check if the
* class_locale of that pending AEN is inclusive of the new
* AEN request we currently have . If it is , then we don ' t have
* to do anything . In other words , whichever events the current
* AEN request is subscribing to , have already been subscribed
* to .
*
* If the old_cmd is _not_ inclusive , then we have to abort
* that command , form a class_locale that is superset of both
* old and current and re - issue to the FW
*/
curr_aen . word = class_locale_word ;
if ( instance - > aen_cmd ) {
prev_aen . word = instance - > aen_cmd - > frame - > dcmd . mbox . w [ 1 ] ;
/*
* A class whose enum value is smaller is inclusive of all
* higher values . If a PROGRESS ( = - 1 ) was previously
* registered , then a new registration requests for higher
* classes need not be sent to FW . They are automatically
* included .
*
* Locale numbers don ' t have such hierarchy . They are bitmap
* values
*/
if ( ( prev_aen . members . class < = curr_aen . members . class ) & &
! ( ( prev_aen . members . locale & curr_aen . members . locale ) ^
curr_aen . members . locale ) ) {
/*
* Previously issued event registration includes
* current request . Nothing to do .
*/
return 0 ;
} else {
curr_aen . members . locale | = prev_aen . members . locale ;
if ( prev_aen . members . class < curr_aen . members . class )
curr_aen . members . class = prev_aen . members . class ;
instance - > aen_cmd - > abort_aen = 1 ;
ret_val = megasas_issue_blocked_abort_cmd ( instance ,
instance - >
aen_cmd ) ;
if ( ret_val ) {
printk ( KERN_DEBUG " megasas: Failed to abort "
" previous AEN command \n " ) ;
return ret_val ;
}
}
}
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd )
return - ENOMEM ;
dcmd = & cmd - > frame - > dcmd ;
memset ( instance - > evt_detail , 0 , sizeof ( struct megasas_evt_detail ) ) ;
/*
* Prepare DCMD for aen registration
*/
memset ( dcmd - > mbox . b , 0 , MFI_MBOX_SIZE ) ;
dcmd - > cmd = MFI_CMD_DCMD ;
dcmd - > cmd_status = 0x0 ;
dcmd - > sge_count = 1 ;
dcmd - > flags = MFI_FRAME_DIR_READ ;
dcmd - > timeout = 0 ;
dcmd - > data_xfer_len = sizeof ( struct megasas_evt_detail ) ;
dcmd - > opcode = MR_DCMD_CTRL_EVENT_WAIT ;
dcmd - > mbox . w [ 0 ] = seq_num ;
dcmd - > mbox . w [ 1 ] = curr_aen . word ;
dcmd - > sgl . sge32 [ 0 ] . phys_addr = ( u32 ) instance - > evt_detail_h ;
dcmd - > sgl . sge32 [ 0 ] . length = sizeof ( struct megasas_evt_detail ) ;
/*
* Store reference to the cmd used to register for AEN . When an
* application wants us to register for AEN , we have to abort this
* cmd and re - register with a new EVENT LOCALE supplied by that app
*/
instance - > aen_cmd = cmd ;
/*
* Issue the aen registration frame
*/
writel ( cmd - > frame_phys_addr > > 3 ,
& instance - > reg_set - > inbound_queue_port ) ;
return 0 ;
}
/**
* megasas_start_aen - Subscribes to AEN during driver load time
* @ instance : Adapter soft state
*/
static int megasas_start_aen ( struct megasas_instance * instance )
{
struct megasas_evt_log_info eli ;
union megasas_evt_class_locale class_locale ;
/*
* Get the latest sequence number from FW
*/
memset ( & eli , 0 , sizeof ( eli ) ) ;
if ( megasas_get_seq_num ( instance , & eli ) )
return - 1 ;
/*
* Register AEN with FW for latest sequence number plus 1
*/
class_locale . members . reserved = 0 ;
class_locale . members . locale = MR_EVT_LOCALE_ALL ;
class_locale . members . class = MR_EVT_CLASS_DEBUG ;
return megasas_register_aen ( instance , eli . newest_seq_num + 1 ,
class_locale . word ) ;
}
/**
* megasas_io_attach - Attaches this driver to SCSI mid - layer
* @ instance : Adapter soft state
*/
static int megasas_io_attach ( struct megasas_instance * instance )
{
struct Scsi_Host * host = instance - > host ;
/*
* Export parameters required by SCSI mid - layer
*/
host - > irq = instance - > pdev - > irq ;
host - > unique_id = instance - > unique_id ;
host - > can_queue = instance - > max_fw_cmds - MEGASAS_INT_CMDS ;
host - > this_id = instance - > init_id ;
host - > sg_tablesize = instance - > max_num_sge ;
host - > max_sectors = instance - > max_sectors_per_req ;
host - > cmd_per_lun = 128 ;
host - > max_channel = MEGASAS_MAX_CHANNELS - 1 ;
host - > max_id = MEGASAS_MAX_DEV_PER_CHANNEL ;
host - > max_lun = MEGASAS_MAX_LUN ;
/*
* Notify the mid - layer about the new controller
*/
if ( scsi_add_host ( host , & instance - > pdev - > dev ) ) {
printk ( KERN_DEBUG " megasas: scsi_add_host failed \n " ) ;
return - ENODEV ;
}
/*
* Trigger SCSI to scan our drives
*/
scsi_scan_host ( host ) ;
return 0 ;
}
/**
* megasas_probe_one - PCI hotplug entry point
* @ pdev : PCI device structure
* @ id : PCI ids of supported hotplugged adapter
*/
static int __devinit
megasas_probe_one ( struct pci_dev * pdev , const struct pci_device_id * id )
{
int rval ;
struct Scsi_Host * host ;
struct megasas_instance * instance ;
/*
* Announce PCI information
*/
printk ( KERN_INFO " megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: " ,
pdev - > vendor , pdev - > device , pdev - > subsystem_vendor ,
pdev - > subsystem_device ) ;
printk ( " bus %d:slot %d:func %d \n " ,
pdev - > bus - > number , PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) ) ;
/*
* PCI prepping : enable device set bus mastering and dma mask
*/
rval = pci_enable_device ( pdev ) ;
if ( rval ) {
return rval ;
}
pci_set_master ( pdev ) ;
/*
* All our contollers are capable of performing 64 - bit DMA
*/
if ( IS_DMA64 ) {
if ( pci_set_dma_mask ( pdev , DMA_64BIT_MASK ) ! = 0 ) {
if ( pci_set_dma_mask ( pdev , DMA_32BIT_MASK ) ! = 0 )
goto fail_set_dma_mask ;
}
} else {
if ( pci_set_dma_mask ( pdev , DMA_32BIT_MASK ) ! = 0 )
goto fail_set_dma_mask ;
}
host = scsi_host_alloc ( & megasas_template ,
sizeof ( struct megasas_instance ) ) ;
if ( ! host ) {
printk ( KERN_DEBUG " megasas: scsi_host_alloc failed \n " ) ;
goto fail_alloc_instance ;
}
instance = ( struct megasas_instance * ) host - > hostdata ;
memset ( instance , 0 , sizeof ( * instance ) ) ;
instance - > producer = pci_alloc_consistent ( pdev , sizeof ( u32 ) ,
& instance - > producer_h ) ;
instance - > consumer = pci_alloc_consistent ( pdev , sizeof ( u32 ) ,
& instance - > consumer_h ) ;
if ( ! instance - > producer | | ! instance - > consumer ) {
printk ( KERN_DEBUG " megasas: Failed to allocate memory for "
" producer, consumer \n " ) ;
goto fail_alloc_dma_buf ;
}
* instance - > producer = 0 ;
* instance - > consumer = 0 ;
instance - > evt_detail = pci_alloc_consistent ( pdev ,
sizeof ( struct
megasas_evt_detail ) ,
& instance - > evt_detail_h ) ;
if ( ! instance - > evt_detail ) {
printk ( KERN_DEBUG " megasas: Failed to allocate memory for "
" event detail structure \n " ) ;
goto fail_alloc_dma_buf ;
}
/*
* Initialize locks and queues
*/
INIT_LIST_HEAD ( & instance - > cmd_pool ) ;
init_waitqueue_head ( & instance - > int_cmd_wait_q ) ;
init_waitqueue_head ( & instance - > abort_cmd_wait_q ) ;
spin_lock_init ( & instance - > cmd_pool_lock ) ;
spin_lock_init ( & instance - > instance_lock ) ;
sema_init ( & instance - > aen_mutex , 1 ) ;
sema_init ( & instance - > ioctl_sem , MEGASAS_INT_CMDS ) ;
/*
* Initialize PCI related and misc parameters
*/
instance - > pdev = pdev ;
instance - > host = host ;
instance - > unique_id = pdev - > bus - > number < < 8 | pdev - > devfn ;
instance - > init_id = MEGASAS_DEFAULT_INIT_ID ;
/*
* Initialize MFI Firmware
*/
if ( megasas_init_mfi ( instance ) )
goto fail_init_mfi ;
/*
* Register IRQ
*/
if ( request_irq ( pdev - > irq , megasas_isr , SA_SHIRQ , " megasas " , instance ) ) {
printk ( KERN_DEBUG " megasas: Failed to register IRQ \n " ) ;
goto fail_irq ;
}
megasas_enable_intr ( instance - > reg_set ) ;
/*
* Store instance in PCI softstate
*/
pci_set_drvdata ( pdev , instance ) ;
/*
* Add this controller to megasas_mgmt_info structure so that it
* can be exported to management applications
*/
megasas_mgmt_info . count + + ;
megasas_mgmt_info . instance [ megasas_mgmt_info . max_index ] = instance ;
megasas_mgmt_info . max_index + + ;
/*
* Initiate AEN ( Asynchronous Event Notification )
*/
if ( megasas_start_aen ( instance ) ) {
printk ( KERN_DEBUG " megasas: start aen failed \n " ) ;
goto fail_start_aen ;
}
/*
* Register with SCSI mid - layer
*/
if ( megasas_io_attach ( instance ) )
goto fail_io_attach ;
return 0 ;
fail_start_aen :
fail_io_attach :
megasas_mgmt_info . count - - ;
megasas_mgmt_info . instance [ megasas_mgmt_info . max_index ] = NULL ;
megasas_mgmt_info . max_index - - ;
pci_set_drvdata ( pdev , NULL ) ;
megasas_disable_intr ( instance - > reg_set ) ;
free_irq ( instance - > pdev - > irq , instance ) ;
megasas_release_mfi ( instance ) ;
fail_irq :
fail_init_mfi :
fail_alloc_dma_buf :
if ( instance - > evt_detail )
pci_free_consistent ( pdev , sizeof ( struct megasas_evt_detail ) ,
instance - > evt_detail ,
instance - > evt_detail_h ) ;
if ( instance - > producer )
pci_free_consistent ( pdev , sizeof ( u32 ) , instance - > producer ,
instance - > producer_h ) ;
if ( instance - > consumer )
pci_free_consistent ( pdev , sizeof ( u32 ) , instance - > consumer ,
instance - > consumer_h ) ;
scsi_host_put ( host ) ;
fail_alloc_instance :
fail_set_dma_mask :
pci_disable_device ( pdev ) ;
return - ENODEV ;
}
/**
* megasas_flush_cache - Requests FW to flush all its caches
* @ instance : Adapter soft state
*/
static void megasas_flush_cache ( struct megasas_instance * instance )
{
struct megasas_cmd * cmd ;
struct megasas_dcmd_frame * dcmd ;
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd )
return ;
dcmd = & cmd - > frame - > dcmd ;
memset ( dcmd - > mbox . b , 0 , MFI_MBOX_SIZE ) ;
dcmd - > cmd = MFI_CMD_DCMD ;
dcmd - > cmd_status = 0x0 ;
dcmd - > sge_count = 0 ;
dcmd - > flags = MFI_FRAME_DIR_NONE ;
dcmd - > timeout = 0 ;
dcmd - > data_xfer_len = 0 ;
dcmd - > opcode = MR_DCMD_CTRL_CACHE_FLUSH ;
dcmd - > mbox . b [ 0 ] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE ;
megasas_issue_blocked_cmd ( instance , cmd ) ;
megasas_return_cmd ( instance , cmd ) ;
return ;
}
/**
* megasas_shutdown_controller - Instructs FW to shutdown the controller
* @ instance : Adapter soft state
*/
static void megasas_shutdown_controller ( struct megasas_instance * instance )
{
struct megasas_cmd * cmd ;
struct megasas_dcmd_frame * dcmd ;
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd )
return ;
if ( instance - > aen_cmd )
megasas_issue_blocked_abort_cmd ( instance , instance - > aen_cmd ) ;
dcmd = & cmd - > frame - > dcmd ;
memset ( dcmd - > mbox . b , 0 , MFI_MBOX_SIZE ) ;
dcmd - > cmd = MFI_CMD_DCMD ;
dcmd - > cmd_status = 0x0 ;
dcmd - > sge_count = 0 ;
dcmd - > flags = MFI_FRAME_DIR_NONE ;
dcmd - > timeout = 0 ;
dcmd - > data_xfer_len = 0 ;
dcmd - > opcode = MR_DCMD_CTRL_SHUTDOWN ;
megasas_issue_blocked_cmd ( instance , cmd ) ;
megasas_return_cmd ( instance , cmd ) ;
return ;
}
/**
* megasas_detach_one - PCI hot " un " plug entry point
* @ pdev : PCI device structure
*/
static void megasas_detach_one ( struct pci_dev * pdev )
{
int i ;
struct Scsi_Host * host ;
struct megasas_instance * instance ;
instance = pci_get_drvdata ( pdev ) ;
host = instance - > host ;
scsi_remove_host ( instance - > host ) ;
megasas_flush_cache ( instance ) ;
megasas_shutdown_controller ( instance ) ;
/*
* Take the instance off the instance array . Note that we will not
* decrement the max_index . We let this array be sparse array
*/
for ( i = 0 ; i < megasas_mgmt_info . max_index ; i + + ) {
if ( megasas_mgmt_info . instance [ i ] = = instance ) {
megasas_mgmt_info . count - - ;
megasas_mgmt_info . instance [ i ] = NULL ;
break ;
}
}
pci_set_drvdata ( instance - > pdev , NULL ) ;
megasas_disable_intr ( instance - > reg_set ) ;
free_irq ( instance - > pdev - > irq , instance ) ;
megasas_release_mfi ( instance ) ;
pci_free_consistent ( pdev , sizeof ( struct megasas_evt_detail ) ,
instance - > evt_detail , instance - > evt_detail_h ) ;
pci_free_consistent ( pdev , sizeof ( u32 ) , instance - > producer ,
instance - > producer_h ) ;
pci_free_consistent ( pdev , sizeof ( u32 ) , instance - > consumer ,
instance - > consumer_h ) ;
scsi_host_put ( host ) ;
pci_set_drvdata ( pdev , NULL ) ;
pci_disable_device ( pdev ) ;
return ;
}
/**
* megasas_shutdown - Shutdown entry point
* @ device : Generic device structure
*/
static void megasas_shutdown ( struct pci_dev * pdev )
{
struct megasas_instance * instance = pci_get_drvdata ( pdev ) ;
megasas_flush_cache ( instance ) ;
}
/**
* megasas_mgmt_open - char node " open " entry point
*/
static int megasas_mgmt_open ( struct inode * inode , struct file * filep )
{
/*
* Allow only those users with admin rights
*/
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
return 0 ;
}
/**
* megasas_mgmt_release - char node " release " entry point
*/
static int megasas_mgmt_release ( struct inode * inode , struct file * filep )
{
filep - > private_data = NULL ;
fasync_helper ( - 1 , filep , 0 , & megasas_async_queue ) ;
return 0 ;
}
/**
* megasas_mgmt_fasync - Async notifier registration from applications
*
* This function adds the calling process to a driver global queue . When an
* event occurs , SIGIO will be sent to all processes in this queue .
*/
static int megasas_mgmt_fasync ( int fd , struct file * filep , int mode )
{
int rc ;
2006-01-11 15:16:10 +03:00
mutex_lock ( & megasas_async_queue_mutex ) ;
2005-09-21 01:46:58 +04:00
rc = fasync_helper ( fd , filep , mode , & megasas_async_queue ) ;
2006-01-11 15:16:10 +03:00
mutex_unlock ( & megasas_async_queue_mutex ) ;
2005-09-21 01:46:58 +04:00
if ( rc > = 0 ) {
/* For sanity check when we get ioctl */
filep - > private_data = filep ;
return 0 ;
}
printk ( KERN_DEBUG " megasas: fasync_helper failed [%d] \n " , rc ) ;
return rc ;
}
/**
* megasas_mgmt_fw_ioctl - Issues management ioctls to FW
* @ instance : Adapter soft state
* @ argp : User ' s ioctl packet
*/
static int
megasas_mgmt_fw_ioctl ( struct megasas_instance * instance ,
struct megasas_iocpacket __user * user_ioc ,
struct megasas_iocpacket * ioc )
{
struct megasas_sge32 * kern_sge32 ;
struct megasas_cmd * cmd ;
void * kbuff_arr [ MAX_IOCTL_SGE ] ;
dma_addr_t buf_handle = 0 ;
int error = 0 , i ;
void * sense = NULL ;
dma_addr_t sense_handle ;
u32 * sense_ptr ;
memset ( kbuff_arr , 0 , sizeof ( kbuff_arr ) ) ;
if ( ioc - > sge_count > MAX_IOCTL_SGE ) {
printk ( KERN_DEBUG " megasas: SGE count [%d] > max limit [%d] \n " ,
ioc - > sge_count , MAX_IOCTL_SGE ) ;
return - EINVAL ;
}
cmd = megasas_get_cmd ( instance ) ;
if ( ! cmd ) {
printk ( KERN_DEBUG " megasas: Failed to get a cmd packet \n " ) ;
return - ENOMEM ;
}
/*
* User ' s IOCTL packet has 2 frames ( maximum ) . Copy those two
* frames into our cmd ' s frames . cmd - > frame ' s context will get
* overwritten when we copy from user ' s frames . So set that value
* alone separately
*/
memcpy ( cmd - > frame , ioc - > frame . raw , 2 * MEGAMFI_FRAME_SIZE ) ;
cmd - > frame - > hdr . context = cmd - > index ;
/*
* The management interface between applications and the fw uses
* MFI frames . E . g , RAID configuration changes , LD property changes
* etc are accomplishes through different kinds of MFI frames . The
* driver needs to care only about substituting user buffers with
* kernel buffers in SGLs . The location of SGL is embedded in the
* struct iocpacket itself .
*/
kern_sge32 = ( struct megasas_sge32 * )
( ( unsigned long ) cmd - > frame + ioc - > sgl_off ) ;
/*
* For each user buffer , create a mirror buffer and copy in
*/
for ( i = 0 ; i < ioc - > sge_count ; i + + ) {
kbuff_arr [ i ] = pci_alloc_consistent ( instance - > pdev ,
ioc - > sgl [ i ] . iov_len ,
& buf_handle ) ;
if ( ! kbuff_arr [ i ] ) {
printk ( KERN_DEBUG " megasas: Failed to alloc "
" kernel SGL buffer for IOCTL \n " ) ;
error = - ENOMEM ;
goto out ;
}
/*
* We don ' t change the dma_coherent_mask , so
* pci_alloc_consistent only returns 32 bit addresses
*/
kern_sge32 [ i ] . phys_addr = ( u32 ) buf_handle ;
kern_sge32 [ i ] . length = ioc - > sgl [ i ] . iov_len ;
/*
* We created a kernel buffer corresponding to the
* user buffer . Now copy in from the user buffer
*/
if ( copy_from_user ( kbuff_arr [ i ] , ioc - > sgl [ i ] . iov_base ,
( u32 ) ( ioc - > sgl [ i ] . iov_len ) ) ) {
error = - EFAULT ;
goto out ;
}
}
if ( ioc - > sense_len ) {
sense = pci_alloc_consistent ( instance - > pdev , ioc - > sense_len ,
& sense_handle ) ;
if ( ! sense ) {
error = - ENOMEM ;
goto out ;
}
sense_ptr =
( u32 * ) ( ( unsigned long ) cmd - > frame + ioc - > sense_off ) ;
* sense_ptr = sense_handle ;
}
/*
* Set the sync_cmd flag so that the ISR knows not to complete this
* cmd to the SCSI mid - layer
*/
cmd - > sync_cmd = 1 ;
megasas_issue_blocked_cmd ( instance , cmd ) ;
cmd - > sync_cmd = 0 ;
/*
* copy out the kernel buffers to user buffers
*/
for ( i = 0 ; i < ioc - > sge_count ; i + + ) {
if ( copy_to_user ( ioc - > sgl [ i ] . iov_base , kbuff_arr [ i ] ,
ioc - > sgl [ i ] . iov_len ) ) {
error = - EFAULT ;
goto out ;
}
}
/*
* copy out the sense
*/
if ( ioc - > sense_len ) {
/*
* sense_ptr points to the location that has the user
* sense buffer address
*/
sense_ptr = ( u32 * ) ( ( unsigned long ) ioc - > frame . raw +
ioc - > sense_off ) ;
if ( copy_to_user ( ( void __user * ) ( ( unsigned long ) ( * sense_ptr ) ) ,
sense , ioc - > sense_len ) ) {
error = - EFAULT ;
goto out ;
}
}
/*
* copy the status codes returned by the fw
*/
if ( copy_to_user ( & user_ioc - > frame . hdr . cmd_status ,
& cmd - > frame - > hdr . cmd_status , sizeof ( u8 ) ) ) {
printk ( KERN_DEBUG " megasas: Error copying out cmd_status \n " ) ;
error = - EFAULT ;
}
out :
if ( sense ) {
pci_free_consistent ( instance - > pdev , ioc - > sense_len ,
sense , sense_handle ) ;
}
for ( i = 0 ; i < ioc - > sge_count & & kbuff_arr [ i ] ; i + + ) {
pci_free_consistent ( instance - > pdev ,
kern_sge32 [ i ] . length ,
kbuff_arr [ i ] , kern_sge32 [ i ] . phys_addr ) ;
}
megasas_return_cmd ( instance , cmd ) ;
return error ;
}
static struct megasas_instance * megasas_lookup_instance ( u16 host_no )
{
int i ;
for ( i = 0 ; i < megasas_mgmt_info . max_index ; i + + ) {
if ( ( megasas_mgmt_info . instance [ i ] ) & &
( megasas_mgmt_info . instance [ i ] - > host - > host_no = = host_no ) )
return megasas_mgmt_info . instance [ i ] ;
}
return NULL ;
}
static int megasas_mgmt_ioctl_fw ( struct file * file , unsigned long arg )
{
struct megasas_iocpacket __user * user_ioc =
( struct megasas_iocpacket __user * ) arg ;
struct megasas_iocpacket * ioc ;
struct megasas_instance * instance ;
int error ;
ioc = kmalloc ( sizeof ( * ioc ) , GFP_KERNEL ) ;
if ( ! ioc )
return - ENOMEM ;
if ( copy_from_user ( ioc , user_ioc , sizeof ( * ioc ) ) ) {
error = - EFAULT ;
goto out_kfree_ioc ;
}
instance = megasas_lookup_instance ( ioc - > host_no ) ;
if ( ! instance ) {
error = - ENODEV ;
goto out_kfree_ioc ;
}
/*
* We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
*/
if ( down_interruptible ( & instance - > ioctl_sem ) ) {
error = - ERESTARTSYS ;
goto out_kfree_ioc ;
}
error = megasas_mgmt_fw_ioctl ( instance , user_ioc , ioc ) ;
up ( & instance - > ioctl_sem ) ;
out_kfree_ioc :
kfree ( ioc ) ;
return error ;
}
static int megasas_mgmt_ioctl_aen ( struct file * file , unsigned long arg )
{
struct megasas_instance * instance ;
struct megasas_aen aen ;
int error ;
if ( file - > private_data ! = file ) {
printk ( KERN_DEBUG " megasas: fasync_helper was not "
" called first \n " ) ;
return - EINVAL ;
}
if ( copy_from_user ( & aen , ( void __user * ) arg , sizeof ( aen ) ) )
return - EFAULT ;
instance = megasas_lookup_instance ( aen . host_no ) ;
if ( ! instance )
return - ENODEV ;
down ( & instance - > aen_mutex ) ;
error = megasas_register_aen ( instance , aen . seq_num ,
aen . class_locale_word ) ;
up ( & instance - > aen_mutex ) ;
return error ;
}
/**
* megasas_mgmt_ioctl - char node ioctl entry point
*/
static long
megasas_mgmt_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
switch ( cmd ) {
case MEGASAS_IOC_FIRMWARE :
return megasas_mgmt_ioctl_fw ( file , arg ) ;
case MEGASAS_IOC_GET_AEN :
return megasas_mgmt_ioctl_aen ( file , arg ) ;
}
return - ENOTTY ;
}
# ifdef CONFIG_COMPAT
static int megasas_mgmt_compat_ioctl_fw ( struct file * file , unsigned long arg )
{
struct compat_megasas_iocpacket __user * cioc =
( struct compat_megasas_iocpacket __user * ) arg ;
struct megasas_iocpacket __user * ioc =
compat_alloc_user_space ( sizeof ( struct megasas_iocpacket ) ) ;
int i ;
int error = 0 ;
clear_user ( ioc , sizeof ( * ioc ) ) ;
if ( copy_in_user ( & ioc - > host_no , & cioc - > host_no , sizeof ( u16 ) ) | |
copy_in_user ( & ioc - > sgl_off , & cioc - > sgl_off , sizeof ( u32 ) ) | |
copy_in_user ( & ioc - > sense_off , & cioc - > sense_off , sizeof ( u32 ) ) | |
copy_in_user ( & ioc - > sense_len , & cioc - > sense_len , sizeof ( u32 ) ) | |
copy_in_user ( ioc - > frame . raw , cioc - > frame . raw , 128 ) | |
copy_in_user ( & ioc - > sge_count , & cioc - > sge_count , sizeof ( u32 ) ) )
return - EFAULT ;
for ( i = 0 ; i < MAX_IOCTL_SGE ; i + + ) {
compat_uptr_t ptr ;
if ( get_user ( ptr , & cioc - > sgl [ i ] . iov_base ) | |
put_user ( compat_ptr ( ptr ) , & ioc - > sgl [ i ] . iov_base ) | |
copy_in_user ( & ioc - > sgl [ i ] . iov_len ,
& cioc - > sgl [ i ] . iov_len , sizeof ( compat_size_t ) ) )
return - EFAULT ;
}
error = megasas_mgmt_ioctl_fw ( file , ( unsigned long ) ioc ) ;
if ( copy_in_user ( & cioc - > frame . hdr . cmd_status ,
& ioc - > frame . hdr . cmd_status , sizeof ( u8 ) ) ) {
printk ( KERN_DEBUG " megasas: error copy_in_user cmd_status \n " ) ;
return - EFAULT ;
}
return error ;
}
static long
megasas_mgmt_compat_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
switch ( cmd ) {
2006-01-25 22:53:25 +03:00
case MEGASAS_IOC_FIRMWARE32 :
return megasas_mgmt_compat_ioctl_fw ( file , arg ) ;
2005-09-21 01:46:58 +04:00
case MEGASAS_IOC_GET_AEN :
return megasas_mgmt_ioctl_aen ( file , arg ) ;
}
return - ENOTTY ;
}
# endif
/*
* File operations structure for management interface
*/
static struct file_operations megasas_mgmt_fops = {
. owner = THIS_MODULE ,
. open = megasas_mgmt_open ,
. release = megasas_mgmt_release ,
. fasync = megasas_mgmt_fasync ,
. unlocked_ioctl = megasas_mgmt_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = megasas_mgmt_compat_ioctl ,
# endif
} ;
/*
* PCI hotplug support registration structure
*/
static struct pci_driver megasas_pci_driver = {
. name = " megaraid_sas " ,
. id_table = megasas_pci_table ,
. probe = megasas_probe_one ,
. remove = __devexit_p ( megasas_detach_one ) ,
. shutdown = megasas_shutdown ,
} ;
/*
* Sysfs driver attributes
*/
static ssize_t megasas_sysfs_show_version ( struct device_driver * dd , char * buf )
{
return snprintf ( buf , strlen ( MEGASAS_VERSION ) + 2 , " %s \n " ,
MEGASAS_VERSION ) ;
}
static DRIVER_ATTR ( version , S_IRUGO , megasas_sysfs_show_version , NULL ) ;
static ssize_t
megasas_sysfs_show_release_date ( struct device_driver * dd , char * buf )
{
return snprintf ( buf , strlen ( MEGASAS_RELDATE ) + 2 , " %s \n " ,
MEGASAS_RELDATE ) ;
}
static DRIVER_ATTR ( release_date , S_IRUGO , megasas_sysfs_show_release_date ,
NULL ) ;
/**
* megasas_init - Driver load entry point
*/
static int __init megasas_init ( void )
{
int rval ;
/*
* Announce driver version and other information
*/
printk ( KERN_INFO " megasas: %s %s \n " , MEGASAS_VERSION ,
MEGASAS_EXT_VERSION ) ;
memset ( & megasas_mgmt_info , 0 , sizeof ( megasas_mgmt_info ) ) ;
/*
* Register character device node
*/
rval = register_chrdev ( 0 , " megaraid_sas_ioctl " , & megasas_mgmt_fops ) ;
if ( rval < 0 ) {
printk ( KERN_DEBUG " megasas: failed to open device node \n " ) ;
return rval ;
}
megasas_mgmt_majorno = rval ;
/*
* Register ourselves as PCI hotplug module
*/
rval = pci_module_init ( & megasas_pci_driver ) ;
if ( rval ) {
printk ( KERN_DEBUG " megasas: PCI hotplug regisration failed \n " ) ;
unregister_chrdev ( megasas_mgmt_majorno , " megaraid_sas_ioctl " ) ;
}
driver_create_file ( & megasas_pci_driver . driver , & driver_attr_version ) ;
driver_create_file ( & megasas_pci_driver . driver ,
& driver_attr_release_date ) ;
return rval ;
}
/**
* megasas_exit - Driver unload entry point
*/
static void __exit megasas_exit ( void )
{
driver_remove_file ( & megasas_pci_driver . driver , & driver_attr_version ) ;
driver_remove_file ( & megasas_pci_driver . driver ,
& driver_attr_release_date ) ;
pci_unregister_driver ( & megasas_pci_driver ) ;
unregister_chrdev ( megasas_mgmt_majorno , " megaraid_sas_ioctl " ) ;
}
module_init ( megasas_init ) ;
module_exit ( megasas_exit ) ;