mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-29 23:53:32 +00:00
8f7d3f0f1e
This kthread is not loop at all due to break at the end of the loop. Make that function linear, with no while loop. And remove an unnecessary cast. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Anil Gurumurthy <anil.gurumurthy@qlogic.com> Cc: James Bottomley <James.Bottomley@HansenPartnership.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1822 lines
46 KiB
C
1822 lines
46 KiB
C
/*
|
|
* Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
|
|
* All rights reserved
|
|
* www.brocade.com
|
|
*
|
|
* Linux driver for Brocade Fibre Channel Host Bus Adapter.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (GPL) Version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
|
|
/*
|
|
* 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 <asm/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;
|
|
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.3.0.bin"
|
|
#define BFAD_FW_FILE_CT "ctfw-3.2.3.0.bin"
|
|
#define BFAD_FW_FILE_CT2 "ct2fw-3.2.3.0.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 Brocade-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 Brocade-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 = vmalloc(kva_elem->mem_len);
|
|
if (kva_elem->kva == NULL) {
|
|
bfad_hal_mem_release(bfad);
|
|
rc = BFA_STATUS_ENOMEM;
|
|
goto ext;
|
|
}
|
|
memset(kva_elem->kva, 0, kva_elem->mem_len);
|
|
}
|
|
|
|
/* 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(unsigned long data)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *) data;
|
|
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)
|
|
{
|
|
init_timer(&bfad->hal_tmo);
|
|
bfad->hal_tmo.function = bfad_bfa_tmo;
|
|
bfad->hal_tmo.data = (unsigned long)bfad;
|
|
|
|
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);
|
|
|
|
|
|
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
|
|
(pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
|
|
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
|
|
(pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {
|
|
printk(KERN_ERR "pci_set_dma_mask fail %p\n", pdev);
|
|
goto out_release_region;
|
|
}
|
|
}
|
|
|
|
/* Enable PCIE Advanced Error Recovery (AER) if kernel supports */
|
|
pci_enable_pcie_error_reporting(pdev);
|
|
|
|
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");
|
|
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);
|
|
/* Disable PCIE Advanced Error Recovery (AER) */
|
|
pci_disable_pcie_error_reporting(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 Brocade HBA ports, %s",
|
|
"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));
|
|
strncpy(driver_info.version, BFAD_DRIVER_VERSION,
|
|
sizeof(driver_info.version) - 1);
|
|
if (host_name)
|
|
strncpy(driver_info.host_machine_name, host_name,
|
|
sizeof(driver_info.host_machine_name) - 1);
|
|
if (os_name)
|
|
strncpy(driver_info.host_os_name, os_name,
|
|
sizeof(driver_info.host_os_name) - 1);
|
|
if (os_patch)
|
|
strncpy(driver_info.host_os_patch, os_patch,
|
|
sizeof(driver_info.host_os_patch) - 1);
|
|
|
|
strncpy(driver_info.os_device_name, bfad->pci_name,
|
|
sizeof(driver_info.os_device_name) - 1);
|
|
|
|
/* 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, ®);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(64)) != 0)
|
|
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(32)) != 0)
|
|
goto out_disable_device;
|
|
|
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
|
|
if (restart_bfa(bfad) == -1)
|
|
goto out_disable_device;
|
|
|
|
pci_enable_pcie_error_reporting(pdev);
|
|
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;
|
|
|
|
printk(KERN_INFO "Brocade 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("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME);
|
|
MODULE_AUTHOR("Brocade Communications Systems, Inc.");
|
|
MODULE_VERSION(BFAD_DRIVER_VERSION);
|