2023-08-30 17:31:07 +02:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( c ) 2005 - 2014 Brocade Communications Systems , Inc .
* Copyright ( c ) 2014 - QLogic Corporation .
* All rights reserved
* www . qlogic . com
*
* Linux driver for QLogic BR - series Fibre Channel Host Bus Adapter .
*/
/*
* bfad . c Linux driver PCI interface module .
*/
# include <linux/module.h>
# include <linux/kthread.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/pci.h>
# include <linux/firmware.h>
# include <linux/uaccess.h>
# include <asm/fcntl.h>
# include "bfad_drv.h"
# include "bfad_im.h"
# include "bfa_fcs.h"
# include "bfa_defs.h"
# include "bfa.h"
BFA_TRC_FILE ( LDRV , BFAD ) ;
DEFINE_MUTEX ( bfad_mutex ) ;
LIST_HEAD ( bfad_list ) ;
static int bfad_inst ;
static int num_sgpgs_parm ;
int supported_fc4s ;
char * host_name , * os_name , * os_patch ;
int num_rports , num_ios , num_tms ;
int num_fcxps , num_ufbufs ;
int reqq_size , rspq_size , num_sgpgs ;
int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT ;
int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH ;
int bfa_io_max_sge = BFAD_IO_MAX_SGE ;
int bfa_log_level = 3 ; /* WARNING log level */
int ioc_auto_recover = BFA_TRUE ;
int bfa_linkup_delay = - 1 ;
int fdmi_enable = BFA_TRUE ;
int pcie_max_read_reqsz ;
int bfa_debugfs_enable = 1 ;
int msix_disable_cb = 0 , msix_disable_ct = 0 ;
int max_xfer_size = BFAD_MAX_SECTORS > > 1 ;
static int max_rport_logins = BFA_FCS_MAX_RPORT_LOGINS ;
/* Firmware releated */
u32 bfi_image_cb_size , bfi_image_ct_size , bfi_image_ct2_size ;
u32 * bfi_image_cb , * bfi_image_ct , * bfi_image_ct2 ;
# define BFAD_FW_FILE_CB "cbfw-3.2.5.1.bin"
# define BFAD_FW_FILE_CT "ctfw-3.2.5.1.bin"
# define BFAD_FW_FILE_CT2 "ct2fw-3.2.5.1.bin"
static u32 * bfad_load_fwimg ( struct pci_dev * pdev ) ;
static void bfad_free_fwimg ( void ) ;
static void bfad_read_firmware ( struct pci_dev * pdev , u32 * * bfi_image ,
u32 * bfi_image_size , char * fw_name ) ;
static const char * msix_name_ct [ ] = {
" ctrl " ,
" cpe0 " , " cpe1 " , " cpe2 " , " cpe3 " ,
" rme0 " , " rme1 " , " rme2 " , " rme3 " } ;
static const char * msix_name_cb [ ] = {
" cpe0 " , " cpe1 " , " cpe2 " , " cpe3 " ,
" rme0 " , " rme1 " , " rme2 " , " rme3 " ,
" eemc " , " elpu0 " , " elpu1 " , " epss " , " mlpu " } ;
MODULE_FIRMWARE ( BFAD_FW_FILE_CB ) ;
MODULE_FIRMWARE ( BFAD_FW_FILE_CT ) ;
MODULE_FIRMWARE ( BFAD_FW_FILE_CT2 ) ;
module_param ( os_name , charp , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( os_name , " OS name of the hba host machine " ) ;
module_param ( os_patch , charp , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( os_patch , " OS patch level of the hba host machine " ) ;
module_param ( host_name , charp , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( host_name , " Hostname of the hba host machine " ) ;
module_param ( num_rports , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_rports , " Max number of rports supported per port "
" (physical/logical), default=1024 " ) ;
module_param ( num_ios , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_ios , " Max number of ioim requests, default=2000 " ) ;
module_param ( num_tms , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_tms , " Max number of task im requests, default=128 " ) ;
module_param ( num_fcxps , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_fcxps , " Max number of fcxp requests, default=64 " ) ;
module_param ( num_ufbufs , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_ufbufs , " Max number of unsolicited frame "
" buffers, default=64 " ) ;
module_param ( reqq_size , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( reqq_size , " Max number of request queue elements, "
" default=256 " ) ;
module_param ( rspq_size , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( rspq_size , " Max number of response queue elements, "
" default=64 " ) ;
module_param ( num_sgpgs , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( num_sgpgs , " Number of scatter/gather pages, default=2048 " ) ;
module_param ( rport_del_timeout , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( rport_del_timeout , " Rport delete timeout, default=90 secs, "
" Range[>0] " ) ;
module_param ( bfa_lun_queue_depth , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( bfa_lun_queue_depth , " Lun queue depth, default=32, Range[>0] " ) ;
module_param ( bfa_io_max_sge , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( bfa_io_max_sge , " Max io scatter/gather elements, default=255 " ) ;
module_param ( bfa_log_level , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( bfa_log_level , " Driver log level, default=3, "
" Range[Critical:1|Error:2|Warning:3|Info:4] " ) ;
module_param ( ioc_auto_recover , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( ioc_auto_recover , " IOC auto recovery, default=1, "
" Range[off:0|on:1] " ) ;
module_param ( bfa_linkup_delay , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( bfa_linkup_delay , " Link up delay, default=30 secs for "
" boot port. Otherwise 10 secs in RHEL4 & 0 for "
" [RHEL5, SLES10, ESX40] Range[>0] " ) ;
module_param ( msix_disable_cb , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( msix_disable_cb , " Disable Message Signaled Interrupts for QLogic-415/425/815/825 cards, default=0 Range[false:0|true:1] " ) ;
module_param ( msix_disable_ct , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( msix_disable_ct , " Disable Message Signaled Interrupts if possible for QLogic-1010/1020/804/1007/902/1741 cards, default=0, Range[false:0|true:1] " ) ;
module_param ( fdmi_enable , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( fdmi_enable , " Enables fdmi registration, default=1, "
" Range[false:0|true:1] " ) ;
module_param ( pcie_max_read_reqsz , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( pcie_max_read_reqsz , " PCIe max read request size, default=0 "
" (use system setting), Range[128|256|512|1024|2048|4096] " ) ;
module_param ( bfa_debugfs_enable , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( bfa_debugfs_enable , " Enables debugfs feature, default=1, "
" Range[false:0|true:1] " ) ;
module_param ( max_xfer_size , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( max_xfer_size , " default=32MB, "
" Range[64k|128k|256k|512k|1024k|2048k] " ) ;
module_param ( max_rport_logins , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( max_rport_logins , " Max number of logins to initiator and target rports on a port (physical/logical), default=1024 " ) ;
static void
bfad_sm_uninit ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_created ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_initializing ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_operational ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_stopping ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_failed ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
static void
bfad_sm_fcs_exit ( struct bfad_s * bfad , enum bfad_sm_event event ) ;
/*
* Beginning state for the driver instance , awaiting the pci_probe event
*/
static void
bfad_sm_uninit ( struct bfad_s * bfad , enum bfad_sm_event event )
{
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_CREATE :
bfa_sm_set_state ( bfad , bfad_sm_created ) ;
bfad - > bfad_tsk = kthread_create ( bfad_worker , ( void * ) bfad ,
" %s " , " bfad_worker " ) ;
if ( IS_ERR ( bfad - > bfad_tsk ) ) {
printk ( KERN_INFO " bfad[%d]: Kernel thread "
" creation failed! \n " , bfad - > inst_no ) ;
bfa_sm_send_event ( bfad , BFAD_E_KTHREAD_CREATE_FAILED ) ;
}
bfa_sm_send_event ( bfad , BFAD_E_INIT ) ;
break ;
case BFAD_E_STOP :
/* Ignore stop; already in uninit */
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
/*
* Driver Instance is created , awaiting event INIT to initialize the bfad
*/
static void
bfad_sm_created ( struct bfad_s * bfad , enum bfad_sm_event event )
{
unsigned long flags ;
bfa_status_t ret ;
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_INIT :
bfa_sm_set_state ( bfad , bfad_sm_initializing ) ;
init_completion ( & bfad - > comp ) ;
/* Enable Interrupt and wait bfa_init completion */
if ( bfad_setup_intr ( bfad ) ) {
printk ( KERN_WARNING " bfad%d: bfad_setup_intr failed \n " ,
bfad - > inst_no ) ;
bfa_sm_send_event ( bfad , BFAD_E_INIT_FAILED ) ;
break ;
}
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_iocfc_init ( & bfad - > bfa ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
/* Set up interrupt handler for each vectors */
if ( ( bfad - > bfad_flags & BFAD_MSIX_ON ) & &
bfad_install_msix_handler ( bfad ) ) {
printk ( KERN_WARNING " %s: install_msix failed, bfad%d \n " ,
__func__ , bfad - > inst_no ) ;
}
bfad_init_timer ( bfad ) ;
wait_for_completion ( & bfad - > comp ) ;
if ( ( bfad - > bfad_flags & BFAD_HAL_INIT_DONE ) ) {
bfa_sm_send_event ( bfad , BFAD_E_INIT_SUCCESS ) ;
} else {
printk ( KERN_WARNING
" bfa %s: bfa init failed \n " ,
bfad - > pci_name ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_fcs_init ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
ret = bfad_cfg_pport ( bfad , BFA_LPORT_ROLE_FCP_IM ) ;
if ( ret ! = BFA_STATUS_OK ) {
init_completion ( & bfad - > comp ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > pport . flags | = BFAD_PORT_DELETE ;
bfa_fcs_exit ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
bfa_sm_send_event ( bfad , BFAD_E_INIT_FAILED ) ;
break ;
}
bfad - > bfad_flags | = BFAD_HAL_INIT_FAIL ;
bfa_sm_send_event ( bfad , BFAD_E_HAL_INIT_FAILED ) ;
}
break ;
case BFAD_E_KTHREAD_CREATE_FAILED :
bfa_sm_set_state ( bfad , bfad_sm_uninit ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
static void
bfad_sm_initializing ( struct bfad_s * bfad , enum bfad_sm_event event )
{
int retval ;
unsigned long flags ;
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_INIT_SUCCESS :
kthread_stop ( bfad - > bfad_tsk ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_tsk = NULL ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
retval = bfad_start_ops ( bfad ) ;
if ( retval ! = BFA_STATUS_OK ) {
bfa_sm_set_state ( bfad , bfad_sm_failed ) ;
break ;
}
bfa_sm_set_state ( bfad , bfad_sm_operational ) ;
break ;
case BFAD_E_INIT_FAILED :
bfa_sm_set_state ( bfad , bfad_sm_uninit ) ;
kthread_stop ( bfad - > bfad_tsk ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_tsk = NULL ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
break ;
case BFAD_E_HAL_INIT_FAILED :
bfa_sm_set_state ( bfad , bfad_sm_failed ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
static void
bfad_sm_failed ( struct bfad_s * bfad , enum bfad_sm_event event )
{
int retval ;
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_INIT_SUCCESS :
retval = bfad_start_ops ( bfad ) ;
if ( retval ! = BFA_STATUS_OK )
break ;
bfa_sm_set_state ( bfad , bfad_sm_operational ) ;
break ;
case BFAD_E_STOP :
bfa_sm_set_state ( bfad , bfad_sm_fcs_exit ) ;
bfa_sm_send_event ( bfad , BFAD_E_FCS_EXIT_COMP ) ;
break ;
case BFAD_E_EXIT_COMP :
bfa_sm_set_state ( bfad , bfad_sm_uninit ) ;
bfad_remove_intr ( bfad ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
static void
bfad_sm_operational ( struct bfad_s * bfad , enum bfad_sm_event event )
{
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_STOP :
bfa_sm_set_state ( bfad , bfad_sm_fcs_exit ) ;
bfad_fcs_stop ( bfad ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
static void
bfad_sm_fcs_exit ( struct bfad_s * bfad , enum bfad_sm_event event )
{
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_FCS_EXIT_COMP :
bfa_sm_set_state ( bfad , bfad_sm_stopping ) ;
bfad_stop ( bfad ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
}
}
static void
bfad_sm_stopping ( struct bfad_s * bfad , enum bfad_sm_event event )
{
bfa_trc ( bfad , event ) ;
switch ( event ) {
case BFAD_E_EXIT_COMP :
bfa_sm_set_state ( bfad , bfad_sm_uninit ) ;
bfad_remove_intr ( bfad ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
bfad_im_probe_undo ( bfad ) ;
bfad - > bfad_flags & = ~ BFAD_FC4_PROBE_DONE ;
bfad_uncfg_pport ( bfad ) ;
break ;
default :
bfa_sm_fault ( bfad , event ) ;
break ;
}
}
/*
* BFA callbacks
*/
void
bfad_hcb_comp ( void * arg , bfa_status_t status )
{
struct bfad_hal_comp * fcomp = ( struct bfad_hal_comp * ) arg ;
fcomp - > status = status ;
complete ( & fcomp - > comp ) ;
}
/*
* bfa_init callback
*/
void
bfa_cb_init ( void * drv , bfa_status_t init_status )
{
struct bfad_s * bfad = drv ;
if ( init_status = = BFA_STATUS_OK ) {
bfad - > bfad_flags | = BFAD_HAL_INIT_DONE ;
/*
* If BFAD_HAL_INIT_FAIL flag is set :
* Wake up the kernel thread to start
* the bfad operations after HAL init done
*/
if ( ( bfad - > bfad_flags & BFAD_HAL_INIT_FAIL ) ) {
bfad - > bfad_flags & = ~ BFAD_HAL_INIT_FAIL ;
wake_up_process ( bfad - > bfad_tsk ) ;
}
}
complete ( & bfad - > comp ) ;
}
/*
* BFA_FCS callbacks
*/
struct bfad_port_s *
bfa_fcb_lport_new ( struct bfad_s * bfad , struct bfa_fcs_lport_s * port ,
enum bfa_lport_role roles , struct bfad_vf_s * vf_drv ,
struct bfad_vport_s * vp_drv )
{
bfa_status_t rc ;
struct bfad_port_s * port_drv ;
if ( ! vp_drv & & ! vf_drv ) {
port_drv = & bfad - > pport ;
port_drv - > pvb_type = BFAD_PORT_PHYS_BASE ;
} else if ( ! vp_drv & & vf_drv ) {
port_drv = & vf_drv - > base_port ;
port_drv - > pvb_type = BFAD_PORT_VF_BASE ;
} else if ( vp_drv & & ! vf_drv ) {
port_drv = & vp_drv - > drv_port ;
port_drv - > pvb_type = BFAD_PORT_PHYS_VPORT ;
} else {
port_drv = & vp_drv - > drv_port ;
port_drv - > pvb_type = BFAD_PORT_VF_VPORT ;
}
port_drv - > fcs_port = port ;
port_drv - > roles = roles ;
if ( roles & BFA_LPORT_ROLE_FCP_IM ) {
rc = bfad_im_port_new ( bfad , port_drv ) ;
if ( rc ! = BFA_STATUS_OK ) {
bfad_im_port_delete ( bfad , port_drv ) ;
port_drv = NULL ;
}
}
return port_drv ;
}
/*
* FCS RPORT alloc callback , after successful PLOGI by FCS
*/
bfa_status_t
bfa_fcb_rport_alloc ( struct bfad_s * bfad , struct bfa_fcs_rport_s * * rport ,
struct bfad_rport_s * * rport_drv )
{
bfa_status_t rc = BFA_STATUS_OK ;
* rport_drv = kzalloc ( sizeof ( struct bfad_rport_s ) , GFP_ATOMIC ) ;
if ( * rport_drv = = NULL ) {
rc = BFA_STATUS_ENOMEM ;
goto ext ;
}
* rport = & ( * rport_drv ) - > fcs_rport ;
ext :
return rc ;
}
/*
* FCS PBC VPORT Create
*/
void
bfa_fcb_pbc_vport_create ( struct bfad_s * bfad , struct bfi_pbc_vport_s pbc_vport )
{
struct bfa_lport_cfg_s port_cfg = { 0 } ;
struct bfad_vport_s * vport ;
int rc ;
vport = kzalloc ( sizeof ( struct bfad_vport_s ) , GFP_ATOMIC ) ;
if ( ! vport ) {
bfa_trc ( bfad , 0 ) ;
return ;
}
vport - > drv_port . bfad = bfad ;
port_cfg . roles = BFA_LPORT_ROLE_FCP_IM ;
port_cfg . pwwn = pbc_vport . vp_pwwn ;
port_cfg . nwwn = pbc_vport . vp_nwwn ;
port_cfg . preboot_vp = BFA_TRUE ;
rc = bfa_fcs_pbc_vport_create ( & vport - > fcs_vport , & bfad - > bfa_fcs , 0 ,
& port_cfg , vport ) ;
if ( rc ! = BFA_STATUS_OK ) {
bfa_trc ( bfad , 0 ) ;
return ;
}
list_add_tail ( & vport - > list_entry , & bfad - > pbc_vport_list ) ;
}
void
bfad_hal_mem_release ( struct bfad_s * bfad )
{
struct bfa_meminfo_s * hal_meminfo = & bfad - > meminfo ;
struct bfa_mem_dma_s * dma_info , * dma_elem ;
struct bfa_mem_kva_s * kva_info , * kva_elem ;
struct list_head * dm_qe , * km_qe ;
dma_info = & hal_meminfo - > dma_info ;
kva_info = & hal_meminfo - > kva_info ;
/* Iterate through the KVA meminfo queue */
list_for_each ( km_qe , & kva_info - > qe ) {
kva_elem = ( struct bfa_mem_kva_s * ) km_qe ;
vfree ( kva_elem - > kva ) ;
}
/* Iterate through the DMA meminfo queue */
list_for_each ( dm_qe , & dma_info - > qe ) {
dma_elem = ( struct bfa_mem_dma_s * ) dm_qe ;
dma_free_coherent ( & bfad - > pcidev - > dev ,
dma_elem - > mem_len , dma_elem - > kva ,
( dma_addr_t ) dma_elem - > dma ) ;
}
memset ( hal_meminfo , 0 , sizeof ( struct bfa_meminfo_s ) ) ;
}
void
bfad_update_hal_cfg ( struct bfa_iocfc_cfg_s * bfa_cfg )
{
if ( num_rports > 0 )
bfa_cfg - > fwcfg . num_rports = num_rports ;
if ( num_ios > 0 )
bfa_cfg - > fwcfg . num_ioim_reqs = num_ios ;
if ( num_tms > 0 )
bfa_cfg - > fwcfg . num_tskim_reqs = num_tms ;
if ( num_fcxps > 0 & & num_fcxps < = BFA_FCXP_MAX )
bfa_cfg - > fwcfg . num_fcxp_reqs = num_fcxps ;
if ( num_ufbufs > 0 & & num_ufbufs < = BFA_UF_MAX )
bfa_cfg - > fwcfg . num_uf_bufs = num_ufbufs ;
if ( reqq_size > 0 )
bfa_cfg - > drvcfg . num_reqq_elems = reqq_size ;
if ( rspq_size > 0 )
bfa_cfg - > drvcfg . num_rspq_elems = rspq_size ;
if ( num_sgpgs > 0 & & num_sgpgs < = BFA_SGPG_MAX )
bfa_cfg - > drvcfg . num_sgpgs = num_sgpgs ;
/*
* populate the hal values back to the driver for sysfs use .
* otherwise , the default values will be shown as 0 in sysfs
*/
num_rports = bfa_cfg - > fwcfg . num_rports ;
num_ios = bfa_cfg - > fwcfg . num_ioim_reqs ;
num_tms = bfa_cfg - > fwcfg . num_tskim_reqs ;
num_fcxps = bfa_cfg - > fwcfg . num_fcxp_reqs ;
num_ufbufs = bfa_cfg - > fwcfg . num_uf_bufs ;
reqq_size = bfa_cfg - > drvcfg . num_reqq_elems ;
rspq_size = bfa_cfg - > drvcfg . num_rspq_elems ;
num_sgpgs = bfa_cfg - > drvcfg . num_sgpgs ;
}
bfa_status_t
bfad_hal_mem_alloc ( struct bfad_s * bfad )
{
struct bfa_meminfo_s * hal_meminfo = & bfad - > meminfo ;
struct bfa_mem_dma_s * dma_info , * dma_elem ;
struct bfa_mem_kva_s * kva_info , * kva_elem ;
struct list_head * dm_qe , * km_qe ;
bfa_status_t rc = BFA_STATUS_OK ;
dma_addr_t phys_addr ;
bfa_cfg_get_default ( & bfad - > ioc_cfg ) ;
bfad_update_hal_cfg ( & bfad - > ioc_cfg ) ;
bfad - > cfg_data . ioc_queue_depth = bfad - > ioc_cfg . fwcfg . num_ioim_reqs ;
bfa_cfg_get_meminfo ( & bfad - > ioc_cfg , hal_meminfo , & bfad - > bfa ) ;
dma_info = & hal_meminfo - > dma_info ;
kva_info = & hal_meminfo - > kva_info ;
/* Iterate through the KVA meminfo queue */
list_for_each ( km_qe , & kva_info - > qe ) {
kva_elem = ( struct bfa_mem_kva_s * ) km_qe ;
kva_elem - > kva = vzalloc ( kva_elem - > mem_len ) ;
if ( kva_elem - > kva = = NULL ) {
bfad_hal_mem_release ( bfad ) ;
rc = BFA_STATUS_ENOMEM ;
goto ext ;
}
}
/* Iterate through the DMA meminfo queue */
list_for_each ( dm_qe , & dma_info - > qe ) {
dma_elem = ( struct bfa_mem_dma_s * ) dm_qe ;
dma_elem - > kva = dma_alloc_coherent ( & bfad - > pcidev - > dev ,
dma_elem - > mem_len ,
& phys_addr , GFP_KERNEL ) ;
if ( dma_elem - > kva = = NULL ) {
bfad_hal_mem_release ( bfad ) ;
rc = BFA_STATUS_ENOMEM ;
goto ext ;
}
dma_elem - > dma = phys_addr ;
memset ( dma_elem - > kva , 0 , dma_elem - > mem_len ) ;
}
ext :
return rc ;
}
/*
* Create a vport under a vf .
*/
bfa_status_t
bfad_vport_create ( struct bfad_s * bfad , u16 vf_id ,
struct bfa_lport_cfg_s * port_cfg , struct device * dev )
{
struct bfad_vport_s * vport ;
int rc = BFA_STATUS_OK ;
unsigned long flags ;
struct completion fcomp ;
vport = kzalloc ( sizeof ( struct bfad_vport_s ) , GFP_KERNEL ) ;
if ( ! vport ) {
rc = BFA_STATUS_ENOMEM ;
goto ext ;
}
vport - > drv_port . bfad = bfad ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
rc = bfa_fcs_vport_create ( & vport - > fcs_vport , & bfad - > bfa_fcs , vf_id ,
port_cfg , vport ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( rc ! = BFA_STATUS_OK )
goto ext_free_vport ;
if ( port_cfg - > roles & BFA_LPORT_ROLE_FCP_IM ) {
rc = bfad_im_scsi_host_alloc ( bfad , vport - > drv_port . im_port ,
dev ) ;
if ( rc ! = BFA_STATUS_OK )
goto ext_free_fcs_vport ;
}
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_fcs_vport_start ( & vport - > fcs_vport ) ;
list_add_tail ( & vport - > list_entry , & bfad - > vport_list ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
return BFA_STATUS_OK ;
ext_free_fcs_vport :
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
vport - > comp_del = & fcomp ;
init_completion ( vport - > comp_del ) ;
bfa_fcs_vport_delete ( & vport - > fcs_vport ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( vport - > comp_del ) ;
ext_free_vport :
kfree ( vport ) ;
ext :
return rc ;
}
void
bfad_bfa_tmo ( struct timer_list * t )
{
struct bfad_s * bfad = from_timer ( bfad , t , hal_tmo ) ;
unsigned long flags ;
struct list_head doneq ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_timer_beat ( & bfad - > bfa . timer_mod ) ;
bfa_comp_deq ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( ! list_empty ( & doneq ) ) {
bfa_comp_process ( & bfad - > bfa , & doneq ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_comp_free ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
}
mod_timer ( & bfad - > hal_tmo ,
jiffies + msecs_to_jiffies ( BFA_TIMER_FREQ ) ) ;
}
void
bfad_init_timer ( struct bfad_s * bfad )
{
timer_setup ( & bfad - > hal_tmo , bfad_bfa_tmo , 0 ) ;
mod_timer ( & bfad - > hal_tmo ,
jiffies + msecs_to_jiffies ( BFA_TIMER_FREQ ) ) ;
}
int
bfad_pci_init ( struct pci_dev * pdev , struct bfad_s * bfad )
{
int rc = - ENODEV ;
if ( pci_enable_device ( pdev ) ) {
printk ( KERN_ERR " pci_enable_device fail %p \n " , pdev ) ;
goto out ;
}
if ( pci_request_regions ( pdev , BFAD_DRIVER_NAME ) )
goto out_disable_device ;
pci_set_master ( pdev ) ;
rc = dma_set_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 64 ) ) ;
if ( rc ) {
rc = - ENODEV ;
printk ( KERN_ERR " dma_set_mask_and_coherent fail %p \n " , pdev ) ;
goto out_release_region ;
}
bfad - > pci_bar0_kva = pci_iomap ( pdev , 0 , pci_resource_len ( pdev , 0 ) ) ;
bfad - > pci_bar2_kva = pci_iomap ( pdev , 2 , pci_resource_len ( pdev , 2 ) ) ;
if ( bfad - > pci_bar0_kva = = NULL ) {
printk ( KERN_ERR " Fail to map bar0 \n " ) ;
rc = - ENODEV ;
goto out_release_region ;
}
bfad - > hal_pcidev . pci_slot = PCI_SLOT ( pdev - > devfn ) ;
bfad - > hal_pcidev . pci_func = PCI_FUNC ( pdev - > devfn ) ;
bfad - > hal_pcidev . pci_bar_kva = bfad - > pci_bar0_kva ;
bfad - > hal_pcidev . device_id = pdev - > device ;
bfad - > hal_pcidev . ssid = pdev - > subsystem_device ;
bfad - > pci_name = pci_name ( pdev ) ;
bfad - > pci_attr . vendor_id = pdev - > vendor ;
bfad - > pci_attr . device_id = pdev - > device ;
bfad - > pci_attr . ssid = pdev - > subsystem_device ;
bfad - > pci_attr . ssvid = pdev - > subsystem_vendor ;
bfad - > pci_attr . pcifn = PCI_FUNC ( pdev - > devfn ) ;
bfad - > pcidev = pdev ;
/* Adjust PCIe Maximum Read Request Size */
if ( pci_is_pcie ( pdev ) & & pcie_max_read_reqsz ) {
if ( pcie_max_read_reqsz > = 128 & &
pcie_max_read_reqsz < = 4096 & &
is_power_of_2 ( pcie_max_read_reqsz ) ) {
int max_rq = pcie_get_readrq ( pdev ) ;
printk ( KERN_WARNING " BFA[%s]: "
" pcie_max_read_request_size is %d, "
" reset to %d \n " , bfad - > pci_name , max_rq ,
pcie_max_read_reqsz ) ;
pcie_set_readrq ( pdev , pcie_max_read_reqsz ) ;
} else {
printk ( KERN_WARNING " BFA[%s]: invalid "
" pcie_max_read_request_size %d ignored \n " ,
bfad - > pci_name , pcie_max_read_reqsz ) ;
}
}
pci_save_state ( pdev ) ;
return 0 ;
out_release_region :
pci_release_regions ( pdev ) ;
out_disable_device :
pci_disable_device ( pdev ) ;
out :
return rc ;
}
void
bfad_pci_uninit ( struct pci_dev * pdev , struct bfad_s * bfad )
{
pci_iounmap ( pdev , bfad - > pci_bar0_kva ) ;
pci_iounmap ( pdev , bfad - > pci_bar2_kva ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
}
bfa_status_t
bfad_drv_init ( struct bfad_s * bfad )
{
bfa_status_t rc ;
unsigned long flags ;
bfad - > cfg_data . rport_del_timeout = rport_del_timeout ;
bfad - > cfg_data . lun_queue_depth = bfa_lun_queue_depth ;
bfad - > cfg_data . io_max_sge = bfa_io_max_sge ;
bfad - > cfg_data . binding_method = FCP_PWWN_BINDING ;
rc = bfad_hal_mem_alloc ( bfad ) ;
if ( rc ! = BFA_STATUS_OK ) {
printk ( KERN_WARNING " bfad%d bfad_hal_mem_alloc failure \n " ,
bfad - > inst_no ) ;
printk ( KERN_WARNING
" Not enough memory to attach all QLogic BR-series HBA ports. System may need more memory. \n " ) ;
return BFA_STATUS_FAILED ;
}
bfad - > bfa . trcmod = bfad - > trcmod ;
bfad - > bfa . plog = & bfad - > plog_buf ;
bfa_plog_init ( & bfad - > plog_buf ) ;
bfa_plog_str ( & bfad - > plog_buf , BFA_PL_MID_DRVR , BFA_PL_EID_DRIVER_START ,
0 , " Driver Attach " ) ;
bfa_attach ( & bfad - > bfa , bfad , & bfad - > ioc_cfg , & bfad - > meminfo ,
& bfad - > hal_pcidev ) ;
/* FCS INIT */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfa_fcs . trcmod = bfad - > trcmod ;
bfa_fcs_attach ( & bfad - > bfa_fcs , & bfad - > bfa , bfad , BFA_FALSE ) ;
bfad - > bfa_fcs . fdmi_enabled = fdmi_enable ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_flags | = BFAD_DRV_INIT_DONE ;
return BFA_STATUS_OK ;
}
void
bfad_drv_uninit ( struct bfad_s * bfad )
{
unsigned long flags ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
init_completion ( & bfad - > comp ) ;
bfa_iocfc_stop ( & bfad - > bfa ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
bfa_isr_disable ( & bfad - > bfa ) ;
bfa_detach ( & bfad - > bfa ) ;
bfad_remove_intr ( bfad ) ;
bfad_hal_mem_release ( bfad ) ;
bfad - > bfad_flags & = ~ BFAD_DRV_INIT_DONE ;
}
void
bfad_drv_start ( struct bfad_s * bfad )
{
unsigned long flags ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_iocfc_start ( & bfad - > bfa ) ;
bfa_fcs_pbc_vport_init ( & bfad - > bfa_fcs ) ;
bfa_fcs_fabric_modstart ( & bfad - > bfa_fcs ) ;
bfad - > bfad_flags | = BFAD_HAL_START_DONE ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( bfad - > im )
flush_workqueue ( bfad - > im - > drv_workq ) ;
}
void
bfad_fcs_stop ( struct bfad_s * bfad )
{
unsigned long flags ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
init_completion ( & bfad - > comp ) ;
bfad - > pport . flags | = BFAD_PORT_DELETE ;
bfa_fcs_exit ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
bfa_sm_send_event ( bfad , BFAD_E_FCS_EXIT_COMP ) ;
}
void
bfad_stop ( struct bfad_s * bfad )
{
unsigned long flags ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
init_completion ( & bfad - > comp ) ;
bfa_iocfc_stop ( & bfad - > bfa ) ;
bfad - > bfad_flags & = ~ BFAD_HAL_START_DONE ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
bfa_sm_send_event ( bfad , BFAD_E_EXIT_COMP ) ;
}
bfa_status_t
bfad_cfg_pport ( struct bfad_s * bfad , enum bfa_lport_role role )
{
int rc = BFA_STATUS_OK ;
/* Allocate scsi_host for the physical port */
if ( ( supported_fc4s & BFA_LPORT_ROLE_FCP_IM ) & &
( role & BFA_LPORT_ROLE_FCP_IM ) ) {
if ( bfad - > pport . im_port = = NULL ) {
rc = BFA_STATUS_FAILED ;
goto out ;
}
rc = bfad_im_scsi_host_alloc ( bfad , bfad - > pport . im_port ,
& bfad - > pcidev - > dev ) ;
if ( rc ! = BFA_STATUS_OK )
goto out ;
bfad - > pport . roles | = BFA_LPORT_ROLE_FCP_IM ;
}
bfad - > bfad_flags | = BFAD_CFG_PPORT_DONE ;
out :
return rc ;
}
void
bfad_uncfg_pport ( struct bfad_s * bfad )
{
if ( ( supported_fc4s & BFA_LPORT_ROLE_FCP_IM ) & &
( bfad - > pport . roles & BFA_LPORT_ROLE_FCP_IM ) ) {
bfad_im_scsi_host_free ( bfad , bfad - > pport . im_port ) ;
bfad_im_port_clean ( bfad - > pport . im_port ) ;
kfree ( bfad - > pport . im_port ) ;
bfad - > pport . roles & = ~ BFA_LPORT_ROLE_FCP_IM ;
}
bfad - > bfad_flags & = ~ BFAD_CFG_PPORT_DONE ;
}
bfa_status_t
bfad_start_ops ( struct bfad_s * bfad ) {
int retval ;
unsigned long flags ;
struct bfad_vport_s * vport , * vport_new ;
struct bfa_fcs_driver_info_s driver_info ;
/* Limit min/max. xfer size to [64k-32MB] */
if ( max_xfer_size < BFAD_MIN_SECTORS > > 1 )
max_xfer_size = BFAD_MIN_SECTORS > > 1 ;
if ( max_xfer_size > BFAD_MAX_SECTORS > > 1 )
max_xfer_size = BFAD_MAX_SECTORS > > 1 ;
/* Fill the driver_info info to fcs*/
memset ( & driver_info , 0 , sizeof ( driver_info ) ) ;
2023-10-24 12:59:35 +02:00
strscpy ( driver_info . version , BFAD_DRIVER_VERSION ,
2023-08-30 17:31:07 +02:00
sizeof ( driver_info . version ) ) ;
if ( host_name )
2023-10-24 12:59:35 +02:00
strscpy ( driver_info . host_machine_name , host_name ,
2023-08-30 17:31:07 +02:00
sizeof ( driver_info . host_machine_name ) ) ;
if ( os_name )
2023-10-24 12:59:35 +02:00
strscpy ( driver_info . host_os_name , os_name ,
2023-08-30 17:31:07 +02:00
sizeof ( driver_info . host_os_name ) ) ;
if ( os_patch )
2023-10-24 12:59:35 +02:00
strscpy ( driver_info . host_os_patch , os_patch ,
2023-08-30 17:31:07 +02:00
sizeof ( driver_info . host_os_patch ) ) ;
2023-10-24 12:59:35 +02:00
strscpy ( driver_info . os_device_name , bfad - > pci_name ,
2023-08-30 17:31:07 +02:00
sizeof ( driver_info . os_device_name ) ) ;
/* FCS driver info init */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_fcs_driver_info_init ( & bfad - > bfa_fcs , & driver_info ) ;
if ( bfad - > bfad_flags & BFAD_CFG_PPORT_DONE )
bfa_fcs_update_cfg ( & bfad - > bfa_fcs ) ;
else
bfa_fcs_init ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( ! ( bfad - > bfad_flags & BFAD_CFG_PPORT_DONE ) ) {
retval = bfad_cfg_pport ( bfad , BFA_LPORT_ROLE_FCP_IM ) ;
if ( retval ! = BFA_STATUS_OK )
return BFA_STATUS_FAILED ;
}
/* Setup fc host fixed attribute if the lk supports */
bfad_fc_host_init ( bfad - > pport . im_port ) ;
/* BFAD level FC4 IM specific resource allocation */
retval = bfad_im_probe ( bfad ) ;
if ( retval ! = BFA_STATUS_OK ) {
printk ( KERN_WARNING " bfad_im_probe failed \n " ) ;
if ( bfa_sm_cmp_state ( bfad , bfad_sm_initializing ) )
bfa_sm_set_state ( bfad , bfad_sm_failed ) ;
return BFA_STATUS_FAILED ;
} else
bfad - > bfad_flags | = BFAD_FC4_PROBE_DONE ;
bfad_drv_start ( bfad ) ;
/* Complete pbc vport create */
list_for_each_entry_safe ( vport , vport_new , & bfad - > pbc_vport_list ,
list_entry ) {
struct fc_vport_identifiers vid ;
struct fc_vport * fc_vport ;
char pwwn_buf [ BFA_STRING_32 ] ;
memset ( & vid , 0 , sizeof ( vid ) ) ;
vid . roles = FC_PORT_ROLE_FCP_INITIATOR ;
vid . vport_type = FC_PORTTYPE_NPIV ;
vid . disable = false ;
vid . node_name = wwn_to_u64 ( ( u8 * )
( & ( ( vport - > fcs_vport ) . lport . port_cfg . nwwn ) ) ) ;
vid . port_name = wwn_to_u64 ( ( u8 * )
( & ( ( vport - > fcs_vport ) . lport . port_cfg . pwwn ) ) ) ;
fc_vport = fc_vport_create ( bfad - > pport . im_port - > shost , 0 , & vid ) ;
if ( ! fc_vport ) {
wwn2str ( pwwn_buf , vid . port_name ) ;
printk ( KERN_WARNING " bfad%d: failed to create pbc vport "
" %s \n " , bfad - > inst_no , pwwn_buf ) ;
}
list_del ( & vport - > list_entry ) ;
kfree ( vport ) ;
}
/*
* If bfa_linkup_delay is set to - 1 default ; try to retrive the
* value using the bfad_get_linkup_delay ( ) ; else use the
* passed in module param value as the bfa_linkup_delay .
*/
if ( bfa_linkup_delay < 0 ) {
bfa_linkup_delay = bfad_get_linkup_delay ( bfad ) ;
bfad_rport_online_wait ( bfad ) ;
bfa_linkup_delay = - 1 ;
} else
bfad_rport_online_wait ( bfad ) ;
BFA_LOG ( KERN_INFO , bfad , bfa_log_level , " bfa device claimed \n " ) ;
return BFA_STATUS_OK ;
}
int
bfad_worker ( void * ptr )
{
struct bfad_s * bfad = ptr ;
unsigned long flags ;
if ( kthread_should_stop ( ) )
return 0 ;
/* Send event BFAD_E_INIT_SUCCESS */
bfa_sm_send_event ( bfad , BFAD_E_INIT_SUCCESS ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_tsk = NULL ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
return 0 ;
}
/*
* BFA driver interrupt functions
*/
irqreturn_t
bfad_intx ( int irq , void * dev_id )
{
struct bfad_s * bfad = dev_id ;
struct list_head doneq ;
unsigned long flags ;
bfa_boolean_t rc ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
rc = bfa_intx ( & bfad - > bfa ) ;
if ( ! rc ) {
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
return IRQ_NONE ;
}
bfa_comp_deq ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( ! list_empty ( & doneq ) ) {
bfa_comp_process ( & bfad - > bfa , & doneq ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_comp_free ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
}
return IRQ_HANDLED ;
}
static irqreturn_t
bfad_msix ( int irq , void * dev_id )
{
struct bfad_msix_s * vec = dev_id ;
struct bfad_s * bfad = vec - > bfad ;
struct list_head doneq ;
unsigned long flags ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_msix ( & bfad - > bfa , vec - > msix . entry ) ;
bfa_comp_deq ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( ! list_empty ( & doneq ) ) {
bfa_comp_process ( & bfad - > bfa , & doneq ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_comp_free ( & bfad - > bfa , & doneq ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
}
return IRQ_HANDLED ;
}
/*
* Initialize the MSIX entry table .
*/
static void
bfad_init_msix_entry ( struct bfad_s * bfad , struct msix_entry * msix_entries ,
int mask , int max_bit )
{
int i ;
int match = 0x00000001 ;
for ( i = 0 , bfad - > nvec = 0 ; i < MAX_MSIX_ENTRY ; i + + ) {
if ( mask & match ) {
bfad - > msix_tab [ bfad - > nvec ] . msix . entry = i ;
bfad - > msix_tab [ bfad - > nvec ] . bfad = bfad ;
msix_entries [ bfad - > nvec ] . entry = i ;
bfad - > nvec + + ;
}
match < < = 1 ;
}
}
int
bfad_install_msix_handler ( struct bfad_s * bfad )
{
int i , error = 0 ;
for ( i = 0 ; i < bfad - > nvec ; i + + ) {
sprintf ( bfad - > msix_tab [ i ] . name , " bfa-%s-%s " ,
bfad - > pci_name ,
( ( bfa_asic_id_cb ( bfad - > hal_pcidev . device_id ) ) ?
msix_name_cb [ i ] : msix_name_ct [ i ] ) ) ;
error = request_irq ( bfad - > msix_tab [ i ] . msix . vector ,
( irq_handler_t ) bfad_msix , 0 ,
bfad - > msix_tab [ i ] . name , & bfad - > msix_tab [ i ] ) ;
bfa_trc ( bfad , i ) ;
bfa_trc ( bfad , bfad - > msix_tab [ i ] . msix . vector ) ;
if ( error ) {
int j ;
for ( j = 0 ; j < i ; j + + )
free_irq ( bfad - > msix_tab [ j ] . msix . vector ,
& bfad - > msix_tab [ j ] ) ;
bfad - > bfad_flags & = ~ BFAD_MSIX_ON ;
pci_disable_msix ( bfad - > pcidev ) ;
return 1 ;
}
}
return 0 ;
}
/*
* Setup MSIX based interrupt .
*/
int
bfad_setup_intr ( struct bfad_s * bfad )
{
int error ;
u32 mask = 0 , i , num_bit = 0 , max_bit = 0 ;
struct msix_entry msix_entries [ MAX_MSIX_ENTRY ] ;
struct pci_dev * pdev = bfad - > pcidev ;
u16 reg ;
/* Call BFA to get the msix map for this PCI function. */
bfa_msix_getvecs ( & bfad - > bfa , & mask , & num_bit , & max_bit ) ;
/* Set up the msix entry table */
bfad_init_msix_entry ( bfad , msix_entries , mask , max_bit ) ;
if ( ( bfa_asic_id_ctc ( pdev - > device ) & & ! msix_disable_ct ) | |
( bfa_asic_id_cb ( pdev - > device ) & & ! msix_disable_cb ) ) {
error = pci_enable_msix_exact ( bfad - > pcidev ,
msix_entries , bfad - > nvec ) ;
/* In CT1 & CT2, try to allocate just one vector */
if ( error = = - ENOSPC & & bfa_asic_id_ctc ( pdev - > device ) ) {
printk ( KERN_WARNING " bfa %s: trying one msix "
" vector failed to allocate %d[%d] \n " ,
bfad - > pci_name , bfad - > nvec , error ) ;
bfad - > nvec = 1 ;
error = pci_enable_msix_exact ( bfad - > pcidev ,
msix_entries , 1 ) ;
}
if ( error ) {
printk ( KERN_WARNING " bfad%d: "
" pci_enable_msix_exact failed (%d), "
" use line based. \n " ,
bfad - > inst_no , error ) ;
goto line_based ;
}
/* Disable INTX in MSI-X mode */
pci_read_config_word ( pdev , PCI_COMMAND , & reg ) ;
if ( ! ( reg & PCI_COMMAND_INTX_DISABLE ) )
pci_write_config_word ( pdev , PCI_COMMAND ,
reg | PCI_COMMAND_INTX_DISABLE ) ;
/* Save the vectors */
for ( i = 0 ; i < bfad - > nvec ; i + + ) {
bfa_trc ( bfad , msix_entries [ i ] . vector ) ;
bfad - > msix_tab [ i ] . msix . vector = msix_entries [ i ] . vector ;
}
bfa_msix_init ( & bfad - > bfa , bfad - > nvec ) ;
bfad - > bfad_flags | = BFAD_MSIX_ON ;
return 0 ;
}
line_based :
error = request_irq ( bfad - > pcidev - > irq , ( irq_handler_t ) bfad_intx ,
BFAD_IRQ_FLAGS , BFAD_DRIVER_NAME , bfad ) ;
if ( error )
return error ;
bfad - > bfad_flags | = BFAD_INTX_ON ;
return 0 ;
}
void
bfad_remove_intr ( struct bfad_s * bfad )
{
int i ;
if ( bfad - > bfad_flags & BFAD_MSIX_ON ) {
for ( i = 0 ; i < bfad - > nvec ; i + + )
free_irq ( bfad - > msix_tab [ i ] . msix . vector ,
& bfad - > msix_tab [ i ] ) ;
pci_disable_msix ( bfad - > pcidev ) ;
bfad - > bfad_flags & = ~ BFAD_MSIX_ON ;
} else if ( bfad - > bfad_flags & BFAD_INTX_ON ) {
free_irq ( bfad - > pcidev - > irq , bfad ) ;
}
}
/*
* PCI probe entry .
*/
int
bfad_pci_probe ( struct pci_dev * pdev , const struct pci_device_id * pid )
{
struct bfad_s * bfad ;
int error = - ENODEV , retval , i ;
/* For single port cards - only claim function 0 */
if ( ( pdev - > device = = BFA_PCI_DEVICE_ID_FC_8G1P ) & &
( PCI_FUNC ( pdev - > devfn ) ! = 0 ) )
return - ENODEV ;
bfad = kzalloc ( sizeof ( struct bfad_s ) , GFP_KERNEL ) ;
if ( ! bfad ) {
error = - ENOMEM ;
goto out ;
}
bfad - > trcmod = kzalloc ( sizeof ( struct bfa_trc_mod_s ) , GFP_KERNEL ) ;
if ( ! bfad - > trcmod ) {
printk ( KERN_WARNING " Error alloc trace buffer! \n " ) ;
error = - ENOMEM ;
goto out_alloc_trace_failure ;
}
/* TRACE INIT */
bfa_trc_init ( bfad - > trcmod ) ;
bfa_trc ( bfad , bfad_inst ) ;
/* AEN INIT */
INIT_LIST_HEAD ( & bfad - > free_aen_q ) ;
INIT_LIST_HEAD ( & bfad - > active_aen_q ) ;
for ( i = 0 ; i < BFA_AEN_MAX_ENTRY ; i + + )
list_add_tail ( & bfad - > aen_list [ i ] . qe , & bfad - > free_aen_q ) ;
if ( ! ( bfad_load_fwimg ( pdev ) ) ) {
kfree ( bfad - > trcmod ) ;
goto out_alloc_trace_failure ;
}
retval = bfad_pci_init ( pdev , bfad ) ;
if ( retval ) {
printk ( KERN_WARNING " bfad_pci_init failure! \n " ) ;
error = retval ;
goto out_pci_init_failure ;
}
mutex_lock ( & bfad_mutex ) ;
bfad - > inst_no = bfad_inst + + ;
list_add_tail ( & bfad - > list_entry , & bfad_list ) ;
mutex_unlock ( & bfad_mutex ) ;
/* Initializing the state machine: State set to uninit */
bfa_sm_set_state ( bfad , bfad_sm_uninit ) ;
spin_lock_init ( & bfad - > bfad_lock ) ;
spin_lock_init ( & bfad - > bfad_aen_spinlock ) ;
pci_set_drvdata ( pdev , bfad ) ;
bfad - > ref_count = 0 ;
bfad - > pport . bfad = bfad ;
INIT_LIST_HEAD ( & bfad - > pbc_vport_list ) ;
INIT_LIST_HEAD ( & bfad - > vport_list ) ;
/* Setup the debugfs node for this bfad */
if ( bfa_debugfs_enable )
bfad_debugfs_init ( & bfad - > pport ) ;
retval = bfad_drv_init ( bfad ) ;
if ( retval ! = BFA_STATUS_OK )
goto out_drv_init_failure ;
bfa_sm_send_event ( bfad , BFAD_E_CREATE ) ;
if ( bfa_sm_cmp_state ( bfad , bfad_sm_uninit ) )
goto out_bfad_sm_failure ;
return 0 ;
out_bfad_sm_failure :
bfad_hal_mem_release ( bfad ) ;
out_drv_init_failure :
/* Remove the debugfs node for this bfad */
kfree ( bfad - > regdata ) ;
bfad_debugfs_exit ( & bfad - > pport ) ;
mutex_lock ( & bfad_mutex ) ;
bfad_inst - - ;
list_del ( & bfad - > list_entry ) ;
mutex_unlock ( & bfad_mutex ) ;
bfad_pci_uninit ( pdev , bfad ) ;
out_pci_init_failure :
kfree ( bfad - > trcmod ) ;
out_alloc_trace_failure :
kfree ( bfad ) ;
out :
return error ;
}
/*
* PCI remove entry .
*/
void
bfad_pci_remove ( struct pci_dev * pdev )
{
struct bfad_s * bfad = pci_get_drvdata ( pdev ) ;
unsigned long flags ;
bfa_trc ( bfad , bfad - > inst_no ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
if ( bfad - > bfad_tsk ! = NULL ) {
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
kthread_stop ( bfad - > bfad_tsk ) ;
} else {
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
}
/* Send Event BFAD_E_STOP */
bfa_sm_send_event ( bfad , BFAD_E_STOP ) ;
/* Driver detach and dealloc mem */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_detach ( & bfad - > bfa ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
bfad_hal_mem_release ( bfad ) ;
/* Remove the debugfs node for this bfad */
kfree ( bfad - > regdata ) ;
bfad_debugfs_exit ( & bfad - > pport ) ;
/* Cleaning the BFAD instance */
mutex_lock ( & bfad_mutex ) ;
bfad_inst - - ;
list_del ( & bfad - > list_entry ) ;
mutex_unlock ( & bfad_mutex ) ;
bfad_pci_uninit ( pdev , bfad ) ;
kfree ( bfad - > trcmod ) ;
kfree ( bfad ) ;
}
/*
* PCI Error Recovery entry , error detected .
*/
static pci_ers_result_t
bfad_pci_error_detected ( struct pci_dev * pdev , pci_channel_state_t state )
{
struct bfad_s * bfad = pci_get_drvdata ( pdev ) ;
unsigned long flags ;
pci_ers_result_t ret = PCI_ERS_RESULT_NONE ;
dev_printk ( KERN_ERR , & pdev - > dev ,
" error detected state: %d - flags: 0x%x \n " ,
state , bfad - > bfad_flags ) ;
switch ( state ) {
case pci_channel_io_normal : /* non-fatal error */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_flags & = ~ BFAD_EEH_BUSY ;
/* Suspend/fail all bfa operations */
bfa_ioc_suspend ( & bfad - > bfa . ioc ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
ret = PCI_ERS_RESULT_CAN_RECOVER ;
break ;
case pci_channel_io_frozen : /* fatal error */
init_completion ( & bfad - > comp ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_flags | = BFAD_EEH_BUSY ;
/* Suspend/fail all bfa operations */
bfa_ioc_suspend ( & bfad - > bfa . ioc ) ;
bfa_fcs_stop ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
bfad_remove_intr ( bfad ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
pci_disable_device ( pdev ) ;
ret = PCI_ERS_RESULT_NEED_RESET ;
break ;
case pci_channel_io_perm_failure : /* PCI Card is DEAD */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_flags | = BFAD_EEH_BUSY |
BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
/* If the error_detected handler is called with the reason
* pci_channel_io_perm_failure - it will subsequently call
* pci_remove ( ) entry point to remove the pci device from the
* system - So defer the cleanup to pci_remove ( ) ; cleaning up
* here causes inconsistent state during pci_remove ( ) .
*/
ret = PCI_ERS_RESULT_DISCONNECT ;
break ;
default :
WARN_ON ( 1 ) ;
}
return ret ;
}
static int restart_bfa ( struct bfad_s * bfad )
{
unsigned long flags ;
struct pci_dev * pdev = bfad - > pcidev ;
bfa_attach ( & bfad - > bfa , bfad , & bfad - > ioc_cfg ,
& bfad - > meminfo , & bfad - > hal_pcidev ) ;
/* Enable Interrupt and wait bfa_init completion */
if ( bfad_setup_intr ( bfad ) ) {
dev_printk ( KERN_WARNING , & pdev - > dev ,
" %s: bfad_setup_intr failed \n " , bfad - > pci_name ) ;
bfa_sm_send_event ( bfad , BFAD_E_INIT_FAILED ) ;
return - 1 ;
}
init_completion ( & bfad - > comp ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfa_iocfc_init ( & bfad - > bfa ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
/* Set up interrupt handler for each vectors */
if ( ( bfad - > bfad_flags & BFAD_MSIX_ON ) & &
bfad_install_msix_handler ( bfad ) )
dev_printk ( KERN_WARNING , & pdev - > dev ,
" %s: install_msix failed. \n " , bfad - > pci_name ) ;
bfad_init_timer ( bfad ) ;
wait_for_completion ( & bfad - > comp ) ;
bfad_drv_start ( bfad ) ;
return 0 ;
}
/*
* PCI Error Recovery entry , re - initialize the chip .
*/
static pci_ers_result_t
bfad_pci_slot_reset ( struct pci_dev * pdev )
{
struct bfad_s * bfad = pci_get_drvdata ( pdev ) ;
u8 byte ;
int rc ;
dev_printk ( KERN_ERR , & pdev - > dev ,
" bfad_pci_slot_reset flags: 0x%x \n " , bfad - > bfad_flags ) ;
if ( pci_enable_device ( pdev ) ) {
dev_printk ( KERN_ERR , & pdev - > dev , " Cannot re-enable "
" PCI device after reset. \n " ) ;
return PCI_ERS_RESULT_DISCONNECT ;
}
pci_restore_state ( pdev ) ;
/*
* Read some byte ( e . g . DMA max . payload size which can ' t
* be 0xff any time ) to make sure - we did not hit another PCI error
* in the middle of recovery . If we did , then declare permanent failure .
*/
pci_read_config_byte ( pdev , 0x68 , & byte ) ;
if ( byte = = 0xff ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" slot_reset failed ... got another PCI error ! \n " ) ;
goto out_disable_device ;
}
pci_save_state ( pdev ) ;
pci_set_master ( pdev ) ;
rc = dma_set_mask_and_coherent ( & bfad - > pcidev - > dev , DMA_BIT_MASK ( 64 ) ) ;
if ( rc )
goto out_disable_device ;
if ( restart_bfa ( bfad ) = = - 1 )
goto out_disable_device ;
dev_printk ( KERN_WARNING , & pdev - > dev ,
" slot_reset completed flags: 0x%x! \n " , bfad - > bfad_flags ) ;
return PCI_ERS_RESULT_RECOVERED ;
out_disable_device :
pci_disable_device ( pdev ) ;
return PCI_ERS_RESULT_DISCONNECT ;
}
static pci_ers_result_t
bfad_pci_mmio_enabled ( struct pci_dev * pdev )
{
unsigned long flags ;
struct bfad_s * bfad = pci_get_drvdata ( pdev ) ;
dev_printk ( KERN_INFO , & pdev - > dev , " mmio_enabled \n " ) ;
/* Fetch FW diagnostic information */
bfa_ioc_debug_save_ftrc ( & bfad - > bfa . ioc ) ;
/* Cancel all pending IOs */
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
init_completion ( & bfad - > comp ) ;
bfa_fcs_stop ( & bfad - > bfa_fcs ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
wait_for_completion ( & bfad - > comp ) ;
bfad_remove_intr ( bfad ) ;
del_timer_sync ( & bfad - > hal_tmo ) ;
pci_disable_device ( pdev ) ;
return PCI_ERS_RESULT_NEED_RESET ;
}
static void
bfad_pci_resume ( struct pci_dev * pdev )
{
unsigned long flags ;
struct bfad_s * bfad = pci_get_drvdata ( pdev ) ;
dev_printk ( KERN_WARNING , & pdev - > dev , " resume \n " ) ;
/* wait until the link is online */
bfad_rport_online_wait ( bfad ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
bfad - > bfad_flags & = ~ BFAD_EEH_BUSY ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
}
struct pci_device_id bfad_id_table [ ] = {
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_FC_8G2P ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
} ,
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_FC_8G1P ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
} ,
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_CT ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
. class = ( PCI_CLASS_SERIAL_FIBER < < 8 ) ,
. class_mask = ~ 0 ,
} ,
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_CT_FC ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
. class = ( PCI_CLASS_SERIAL_FIBER < < 8 ) ,
. class_mask = ~ 0 ,
} ,
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_CT2 ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
. class = ( PCI_CLASS_SERIAL_FIBER < < 8 ) ,
. class_mask = ~ 0 ,
} ,
{
. vendor = BFA_PCI_VENDOR_ID_BROCADE ,
. device = BFA_PCI_DEVICE_ID_CT2_QUAD ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
. class = ( PCI_CLASS_SERIAL_FIBER < < 8 ) ,
. class_mask = ~ 0 ,
} ,
{ 0 , 0 } ,
} ;
MODULE_DEVICE_TABLE ( pci , bfad_id_table ) ;
/*
* PCI error recovery handlers .
*/
static struct pci_error_handlers bfad_err_handler = {
. error_detected = bfad_pci_error_detected ,
. slot_reset = bfad_pci_slot_reset ,
. mmio_enabled = bfad_pci_mmio_enabled ,
. resume = bfad_pci_resume ,
} ;
static struct pci_driver bfad_pci_driver = {
. name = BFAD_DRIVER_NAME ,
. id_table = bfad_id_table ,
. probe = bfad_pci_probe ,
. remove = bfad_pci_remove ,
. err_handler = & bfad_err_handler ,
} ;
/*
* Driver module init .
*/
static int __init
bfad_init ( void )
{
int error = 0 ;
pr_info ( " QLogic BR-series BFA FC/FCOE SCSI driver - version: %s \n " ,
BFAD_DRIVER_VERSION ) ;
if ( num_sgpgs > 0 )
num_sgpgs_parm = num_sgpgs ;
error = bfad_im_module_init ( ) ;
if ( error ) {
error = - ENOMEM ;
printk ( KERN_WARNING " bfad_im_module_init failure \n " ) ;
goto ext ;
}
if ( strcmp ( FCPI_NAME , " fcpim " ) = = 0 )
supported_fc4s | = BFA_LPORT_ROLE_FCP_IM ;
bfa_auto_recover = ioc_auto_recover ;
bfa_fcs_rport_set_del_timeout ( rport_del_timeout ) ;
bfa_fcs_rport_set_max_logins ( max_rport_logins ) ;
error = pci_register_driver ( & bfad_pci_driver ) ;
if ( error ) {
printk ( KERN_WARNING " pci_register_driver failure \n " ) ;
goto ext ;
}
return 0 ;
ext :
bfad_im_module_exit ( ) ;
return error ;
}
/*
* Driver module exit .
*/
static void __exit
bfad_exit ( void )
{
pci_unregister_driver ( & bfad_pci_driver ) ;
bfad_im_module_exit ( ) ;
bfad_free_fwimg ( ) ;
}
/* Firmware handling */
static void
bfad_read_firmware ( struct pci_dev * pdev , u32 * * bfi_image ,
u32 * bfi_image_size , char * fw_name )
{
const struct firmware * fw ;
if ( request_firmware ( & fw , fw_name , & pdev - > dev ) ) {
printk ( KERN_ALERT " Can't locate firmware %s \n " , fw_name ) ;
* bfi_image = NULL ;
goto out ;
}
* bfi_image = vmalloc ( fw - > size ) ;
if ( NULL = = * bfi_image ) {
printk ( KERN_ALERT " Fail to allocate buffer for fw image "
" size=%x! \n " , ( u32 ) fw - > size ) ;
goto out ;
}
memcpy ( * bfi_image , fw - > data , fw - > size ) ;
* bfi_image_size = fw - > size / sizeof ( u32 ) ;
out :
release_firmware ( fw ) ;
}
static u32 *
bfad_load_fwimg ( struct pci_dev * pdev )
{
if ( bfa_asic_id_ct2 ( pdev - > device ) ) {
if ( bfi_image_ct2_size = = 0 )
bfad_read_firmware ( pdev , & bfi_image_ct2 ,
& bfi_image_ct2_size , BFAD_FW_FILE_CT2 ) ;
return bfi_image_ct2 ;
} else if ( bfa_asic_id_ct ( pdev - > device ) ) {
if ( bfi_image_ct_size = = 0 )
bfad_read_firmware ( pdev , & bfi_image_ct ,
& bfi_image_ct_size , BFAD_FW_FILE_CT ) ;
return bfi_image_ct ;
} else if ( bfa_asic_id_cb ( pdev - > device ) ) {
if ( bfi_image_cb_size = = 0 )
bfad_read_firmware ( pdev , & bfi_image_cb ,
& bfi_image_cb_size , BFAD_FW_FILE_CB ) ;
return bfi_image_cb ;
}
return NULL ;
}
static void
bfad_free_fwimg ( void )
{
if ( bfi_image_ct2_size & & bfi_image_ct2 )
vfree ( bfi_image_ct2 ) ;
if ( bfi_image_ct_size & & bfi_image_ct )
vfree ( bfi_image_ct ) ;
if ( bfi_image_cb_size & & bfi_image_cb )
vfree ( bfi_image_cb ) ;
}
module_init ( bfad_init ) ;
module_exit ( bfad_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " QLogic BR-series Fibre Channel HBA Driver " BFAD_PROTO_NAME ) ;
MODULE_AUTHOR ( " QLogic Corporation " ) ;
MODULE_VERSION ( BFAD_DRIVER_VERSION ) ;