summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
author <jejb@titanic.il.steeleye.com>2005-04-17 16:05:31 -0500
committerJames Bottomley <jejb@titanic>2005-04-18 13:50:53 -0500
commitdea3101e0a5c897d2c9351a7444e139db9f40247 (patch)
tree61de19e98eed08bb760703b362eab2038c34f261 /drivers/scsi
parent8e8790415e91964096f862a58cacb55d2bc9a817 (diff)
downloadblackbird-op-linux-dea3101e0a5c897d2c9351a7444e139db9f40247.tar.gz
blackbird-op-linux-dea3101e0a5c897d2c9351a7444e139db9f40247.zip
lpfc: add Emulex FC driver version 8.0.28
From: James.Smart@Emulex.Com Modified for kernel import and Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/Kconfig8
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/lpfc/Makefile32
-rw-r--r--drivers/scsi/lpfc/lpfc.h384
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c1291
-rw-r--r--drivers/scsi/lpfc/lpfc_compat.h97
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h216
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c1237
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h206
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c3258
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c2537
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h2687
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c1739
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h41
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c646
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c179
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c1842
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1246
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h157
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c2885
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h216
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h32
22 files changed, 20937 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 718df4c6c3b2..750b11cefd93 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1314,6 +1314,14 @@ config SCSI_QLOGICPTI
source "drivers/scsi/qla2xxx/Kconfig"
+config SCSI_LPFC
+ tristate "Emulex LightPulse Fibre Channel Support"
+ depends on PCI && SCSI
+ select SCSI_FC_ATTRS
+ help
+ This lpfc driver supports the Emulex LightPulse
+ Family of Fibre Channel PCI host adapters.
+
config SCSI_SEAGATE
tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support"
depends on X86 && ISA && SCSI && BROKEN
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 29fcee35ec01..9cb9fe7d623a 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o
obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/
+obj-$(CONFIG_SCSI_LPFC) += lpfc/
obj-$(CONFIG_SCSI_PAS16) += pas16.o
obj-$(CONFIG_SCSI_SEAGATE) += seagate.o
obj-$(CONFIG_SCSI_FD_8xx) += seagate.o
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
new file mode 100644
index 000000000000..2b3098591c41
--- /dev/null
+++ b/drivers/scsi/lpfc/Makefile
@@ -0,0 +1,32 @@
+#/*******************************************************************
+# * This file is part of the Emulex Linux Device Driver for *
+# * Enterprise Fibre Channel Host Bus Adapters. *
+# * Refer to the README file included with this package for *
+# * driver version and adapter support. *
+# * Copyright (C) 2004 Emulex Corporation. *
+# * www.emulex.com *
+# * *
+# * This program is free software; you can redistribute it and/or *
+# * modify it under the terms of the GNU General Public License *
+# * as published by the Free Software Foundation; either version 2 *
+# * of the License, or (at your option) any later version. *
+# * *
+# * 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, a copy of which *
+# * can be found in the file COPYING included with this package. *
+# *******************************************************************/
+######################################################################
+
+#$Id: Makefile 1.58 2005/01/23 19:00:32EST sf_support Exp $
+
+ifneq ($(GCOV),)
+ EXTRA_CFLAGS += -fprofile-arcs -ftest-coverage
+ EXTRA_CFLAGS += -O0
+endif
+
+obj-$(CONFIG_SCSI_LPFC) := lpfc.o
+
+lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \
+ lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsi.o lpfc_attr.o
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
new file mode 100644
index 000000000000..d78247c63d04
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -0,0 +1,384 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc.h 1.167 2005/04/07 08:47:05EDT sf_support Exp $
+ */
+
+struct lpfc_sli2_slim;
+
+#define LPFC_MAX_TARGET 256 /* max targets supported */
+#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els req */
+#define LPFC_MAX_NS_RETRY 3 /* max NameServer retries */
+
+#define LPFC_DFT_HBA_Q_DEPTH 2048 /* max cmds per hba */
+#define LPFC_LC_HBA_Q_DEPTH 1024 /* max cmds per low cost hba */
+#define LPFC_LP101_HBA_Q_DEPTH 128 /* max cmds per low cost hba */
+
+#define LPFC_CMD_PER_LUN 30 /* max outstanding cmds per lun */
+#define LPFC_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
+#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
+
+/* Define macros for 64 bit support */
+#define putPaddrLow(addr) ((uint32_t) (0xffffffff & (u64)(addr)))
+#define putPaddrHigh(addr) ((uint32_t) (0xffffffff & (((u64)(addr))>>32)))
+#define getPaddr(high, low) ((dma_addr_t)( \
+ (( (u64)(high)<<16 ) << 16)|( (u64)(low))))
+/* Provide maximum configuration definitions. */
+#define LPFC_DRVR_TIMEOUT 16 /* driver iocb timeout value in sec */
+#define MAX_FCP_TARGET 256 /* max num of FCP targets supported */
+#define FC_MAX_ADPTMSG 64
+
+#define MAX_HBAEVT 32
+
+/* Provide DMA memory definitions the driver uses per port instance. */
+struct lpfc_dmabuf {
+ struct list_head list;
+ void *virt; /* virtual address ptr */
+ dma_addr_t phys; /* mapped address */
+};
+
+struct lpfc_dma_pool {
+ struct lpfc_dmabuf *elements;
+ uint32_t max_count;
+ uint32_t current_count;
+};
+
+/* Priority bit. Set value to exceed low water mark in lpfc_mem. */
+#define MEM_PRI 0x100
+
+
+/****************************************************************************/
+/* Device VPD save area */
+/****************************************************************************/
+typedef struct lpfc_vpd {
+ uint32_t status; /* vpd status value */
+ uint32_t length; /* number of bytes actually returned */
+ struct {
+ uint32_t rsvd1; /* Revision numbers */
+ uint32_t biuRev;
+ uint32_t smRev;
+ uint32_t smFwRev;
+ uint32_t endecRev;
+ uint16_t rBit;
+ uint8_t fcphHigh;
+ uint8_t fcphLow;
+ uint8_t feaLevelHigh;
+ uint8_t feaLevelLow;
+ uint32_t postKernRev;
+ uint32_t opFwRev;
+ uint8_t opFwName[16];
+ uint32_t sli1FwRev;
+ uint8_t sli1FwName[16];
+ uint32_t sli2FwRev;
+ uint8_t sli2FwName[16];
+ } rev;
+} lpfc_vpd_t;
+
+struct lpfc_scsi_buf;
+
+
+/*
+ * lpfc stat counters
+ */
+struct lpfc_stats {
+ /* Statistics for ELS commands */
+ uint32_t elsLogiCol;
+ uint32_t elsRetryExceeded;
+ uint32_t elsXmitRetry;
+ uint32_t elsDelayRetry;
+ uint32_t elsRcvDrop;
+ uint32_t elsRcvFrame;
+ uint32_t elsRcvRSCN;
+ uint32_t elsRcvRNID;
+ uint32_t elsRcvFARP;
+ uint32_t elsRcvFARPR;
+ uint32_t elsRcvFLOGI;
+ uint32_t elsRcvPLOGI;
+ uint32_t elsRcvADISC;
+ uint32_t elsRcvPDISC;
+ uint32_t elsRcvFAN;
+ uint32_t elsRcvLOGO;
+ uint32_t elsRcvPRLO;
+ uint32_t elsRcvPRLI;
+ uint32_t elsRcvRRQ;
+ uint32_t elsXmitFLOGI;
+ uint32_t elsXmitPLOGI;
+ uint32_t elsXmitPRLI;
+ uint32_t elsXmitADISC;
+ uint32_t elsXmitLOGO;
+ uint32_t elsXmitSCR;
+ uint32_t elsXmitRNID;
+ uint32_t elsXmitFARP;
+ uint32_t elsXmitFARPR;
+ uint32_t elsXmitACC;
+ uint32_t elsXmitLSRJT;
+
+ uint32_t frameRcvBcast;
+ uint32_t frameRcvMulti;
+ uint32_t strayXmitCmpl;
+ uint32_t frameXmitDelay;
+ uint32_t xriCmdCmpl;
+ uint32_t xriStatErr;
+ uint32_t LinkUp;
+ uint32_t LinkDown;
+ uint32_t LinkMultiEvent;
+ uint32_t NoRcvBuf;
+ uint32_t fcpCmd;
+ uint32_t fcpCmpl;
+ uint32_t fcpRspErr;
+ uint32_t fcpRemoteStop;
+ uint32_t fcpPortRjt;
+ uint32_t fcpPortBusy;
+ uint32_t fcpError;
+ uint32_t fcpLocalErr;
+};
+
+enum sysfs_mbox_state {
+ SMBOX_IDLE,
+ SMBOX_WRITING,
+ SMBOX_READING
+};
+
+struct lpfc_sysfs_mbox {
+ enum sysfs_mbox_state state;
+ size_t offset;
+ struct lpfcMboxq * mbox;
+};
+
+struct lpfc_hba {
+ struct list_head hba_list; /* List of hbas/ports */
+ struct lpfc_sli sli;
+ struct lpfc_sli2_slim *slim2p;
+ dma_addr_t slim2p_mapping;
+ uint16_t pci_cfg_value;
+
+ uint32_t hba_state;
+
+#define LPFC_INIT_START 1 /* Initial state after board reset */
+#define LPFC_INIT_MBX_CMDS 2 /* Initialize HBA with mbox commands */
+#define LPFC_LINK_DOWN 3 /* HBA initialized, link is down */
+#define LPFC_LINK_UP 4 /* Link is up - issue READ_LA */
+#define LPFC_LOCAL_CFG_LINK 5 /* local NPORT Id configured */
+#define LPFC_FLOGI 6 /* FLOGI sent to Fabric */
+#define LPFC_FABRIC_CFG_LINK 7 /* Fabric assigned NPORT Id
+ configured */
+#define LPFC_NS_REG 8 /* Register with NameServer */
+#define LPFC_NS_QRY 9 /* Query NameServer for NPort ID list */
+#define LPFC_BUILD_DISC_LIST 10 /* Build ADISC and PLOGI lists for
+ * device authentication / discovery */
+#define LPFC_DISC_AUTH 11 /* Processing ADISC list */
+#define LPFC_CLEAR_LA 12 /* authentication cmplt - issue
+ CLEAR_LA */
+#define LPFC_HBA_READY 32
+#define LPFC_HBA_ERROR 0xff
+
+ uint8_t fc_linkspeed; /* Link speed after last READ_LA */
+
+ uint32_t fc_eventTag; /* event tag for link attention */
+ uint32_t fc_prli_sent; /* cntr for outstanding PRLIs */
+
+ uint32_t num_disc_nodes; /*in addition to hba_state */
+
+ struct timer_list fc_estabtmo; /* link establishment timer */
+ struct timer_list fc_disctmo; /* Discovery rescue timer */
+ struct timer_list fc_fdmitmo; /* fdmi timer */
+ /* These fields used to be binfo */
+ struct lpfc_name fc_nodename; /* fc nodename */
+ struct lpfc_name fc_portname; /* fc portname */
+ uint32_t fc_pref_DID; /* preferred D_ID */
+ uint8_t fc_pref_ALPA; /* preferred AL_PA */
+ uint32_t fc_edtov; /* E_D_TOV timer value */
+ uint32_t fc_arbtov; /* ARB_TOV timer value */
+ uint32_t fc_ratov; /* R_A_TOV timer value */
+ uint32_t fc_rttov; /* R_T_TOV timer value */
+ uint32_t fc_altov; /* AL_TOV timer value */
+ uint32_t fc_crtov; /* C_R_TOV timer value */
+ uint32_t fc_citov; /* C_I_TOV timer value */
+ uint32_t fc_myDID; /* fibre channel S_ID */
+ uint32_t fc_prevDID; /* previous fibre channel S_ID */
+
+ struct serv_parm fc_sparam; /* buffer for our service parameters */
+ struct serv_parm fc_fabparam; /* fabric service parameters buffer */
+ uint8_t alpa_map[128]; /* AL_PA map from READ_LA */
+
+ uint8_t fc_ns_retry; /* retries for fabric nameserver */
+ uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */
+ uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */
+ struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN];
+ uint32_t lmt;
+ uint32_t fc_flag; /* FC flags */
+#define FC_PT2PT 0x1 /* pt2pt with no fabric */
+#define FC_PT2PT_PLOGI 0x2 /* pt2pt initiate PLOGI */
+#define FC_DISC_TMO 0x4 /* Discovery timer running */
+#define FC_PUBLIC_LOOP 0x8 /* Public loop */
+#define FC_LBIT 0x10 /* LOGIN bit in loopinit set */
+#define FC_RSCN_MODE 0x20 /* RSCN cmd rcv'ed */
+#define FC_NLP_MORE 0x40 /* More node to process in node tbl */
+#define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */
+#define FC_FABRIC 0x100 /* We are fabric attached */
+#define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */
+#define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/
+#define FC_LOADING 0x1000 /* HBA in process of loading drvr */
+#define FC_UNLOADING 0x2000 /* HBA in process of unloading drvr */
+#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */
+#define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */
+#define FC_NDISC_ACTIVE 0x10000 /* NPort discovery active */
+
+ uint32_t fc_topology; /* link topology, from LINK INIT */
+
+ struct lpfc_stats fc_stat;
+
+ /* These are the head/tail pointers for the bind, plogi, adisc, unmap,
+ * and map lists. Their counters are immediately following.
+ */
+ struct list_head fc_plogi_list;
+ struct list_head fc_adisc_list;
+ struct list_head fc_reglogin_list;
+ struct list_head fc_prli_list;
+ struct list_head fc_nlpunmap_list;
+ struct list_head fc_nlpmap_list;
+ struct list_head fc_npr_list;
+ struct list_head fc_unused_list;
+
+ /* Keep counters for the number of entries in each list. */
+ uint16_t fc_plogi_cnt;
+ uint16_t fc_adisc_cnt;
+ uint16_t fc_reglogin_cnt;
+ uint16_t fc_prli_cnt;
+ uint16_t fc_unmap_cnt;
+ uint16_t fc_map_cnt;
+ uint16_t fc_npr_cnt;
+ uint16_t fc_unused_cnt;
+ struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */
+ uint32_t nport_event_cnt; /* timestamp for nlplist entry */
+
+#define LPFC_RPI_HASH_SIZE 64
+#define LPFC_RPI_HASH_FUNC(x) ((x) & (0x3f))
+ /* ptr to active D_ID / RPIs */
+ struct lpfc_nodelist *fc_nlplookup[LPFC_RPI_HASH_SIZE];
+ uint32_t wwnn[2];
+ uint32_t RandomData[7];
+
+ uint32_t cfg_log_verbose;
+ uint32_t cfg_lun_queue_depth;
+ uint32_t cfg_nodev_tmo;
+ uint32_t cfg_hba_queue_depth;
+ uint32_t cfg_fcp_class;
+ uint32_t cfg_use_adisc;
+ uint32_t cfg_ack0;
+ uint32_t cfg_topology;
+ uint32_t cfg_scan_down;
+ uint32_t cfg_link_speed;
+ uint32_t cfg_cr_delay;
+ uint32_t cfg_cr_count;
+ uint32_t cfg_fdmi_on;
+ uint32_t cfg_fcp_bind_method;
+ uint32_t cfg_discovery_threads;
+ uint32_t cfg_max_luns;
+ uint32_t cfg_sg_seg_cnt;
+ uint32_t cfg_sg_dma_buf_size;
+
+ lpfc_vpd_t vpd; /* vital product data */
+
+ struct Scsi_Host *host;
+ struct pci_dev *pcidev;
+ struct list_head work_list;
+ uint32_t work_ha; /* Host Attention Bits for WT */
+ uint32_t work_ha_mask; /* HA Bits owned by WT */
+ uint32_t work_hs; /* HS stored in case of ERRAT */
+ uint32_t work_status[2]; /* Extra status from SLIM */
+ uint32_t work_hba_events; /* Timeout to be handled */
+#define WORKER_DISC_TMO 0x1 /* Discovery timeout */
+#define WORKER_ELS_TMO 0x2 /* ELS timeout */
+#define WORKER_MBOX_TMO 0x4 /* MBOX timeout */
+#define WORKER_FDMI_TMO 0x8 /* FDMI timeout */
+
+ wait_queue_head_t *work_wait;
+ struct task_struct *worker_thread;
+
+ unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */
+ unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */
+ void __iomem *slim_memmap_p; /* Kernel memory mapped address for
+ PCI BAR0 */
+ void __iomem *ctrl_regs_memmap_p;/* Kernel memory mapped address for
+ PCI BAR2 */
+
+ void __iomem *MBslimaddr; /* virtual address for mbox cmds */
+ void __iomem *HAregaddr; /* virtual address for host attn reg */
+ void __iomem *CAregaddr; /* virtual address for chip attn reg */
+ void __iomem *HSregaddr; /* virtual address for host status
+ reg */
+ void __iomem *HCregaddr; /* virtual address for host ctl reg */
+
+ int brd_no; /* FC board number */
+
+ char SerialNumber[32]; /* adapter Serial Number */
+ char OptionROMVersion[32]; /* adapter BIOS / Fcode version */
+ char ModelDesc[256]; /* Model Description */
+ char ModelName[80]; /* Model Name */
+ char ProgramType[256]; /* Program Type */
+ char Port[20]; /* Port No */
+ uint8_t vpd_flag; /* VPD data flag */
+
+#define VPD_MODEL_DESC 0x1 /* valid vpd model description */
+#define VPD_MODEL_NAME 0x2 /* valid vpd model name */
+#define VPD_PROGRAM_TYPE 0x4 /* valid vpd program type */
+#define VPD_PORT 0x8 /* valid vpd port data */
+#define VPD_MASK 0xf /* mask for any vpd data */
+
+ struct timer_list els_tmofunc;
+
+ void *link_stats;
+
+ /*
+ * stat counters
+ */
+ uint64_t fc4InputRequests;
+ uint64_t fc4OutputRequests;
+ uint64_t fc4ControlRequests;
+
+ struct lpfc_sysfs_mbox sysfs_mbox;
+
+ /* fastpath list. */
+ struct list_head lpfc_scsi_buf_list;
+ uint32_t total_scsi_bufs;
+ struct list_head lpfc_iocb_list;
+ uint32_t total_iocbq_bufs;
+
+ /* pci_mem_pools */
+ struct pci_pool *lpfc_scsi_dma_buf_pool;
+ struct pci_pool *lpfc_mbuf_pool;
+ struct lpfc_dma_pool lpfc_mbuf_safety_pool;
+
+ mempool_t *mbox_mem_pool;
+ mempool_t *nlp_mem_pool;
+ struct list_head freebufList;
+ struct list_head ctrspbuflist;
+ struct list_head rnidrspbuflist;
+};
+
+
+struct rnidrsp {
+ void *buf;
+ uint32_t uniqueid;
+ struct list_head list;
+ uint32_t data;
+};
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
new file mode 100644
index 000000000000..1276bd77b995
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -0,0 +1,1291 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_attr.c 1.24 2005/04/13 11:58:55EDT sf_support Exp $
+ */
+
+#include <linux/ctype.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_version.h"
+#include "lpfc_compat.h"
+#include "lpfc_crtn.h"
+
+
+static void
+lpfc_jedec_to_ascii(int incr, char hdw[])
+{
+ int i, j;
+ for (i = 0; i < 8; i++) {
+ j = (incr & 0xf);
+ if (j <= 9)
+ hdw[7 - i] = 0x30 + j;
+ else
+ hdw[7 - i] = 0x61 + j - 10;
+ incr = (incr >> 4);
+ }
+ hdw[8] = 0;
+ return;
+}
+
+static ssize_t
+lpfc_drvr_version_show(struct class_device *cdev, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
+}
+
+static ssize_t
+management_version_show(struct class_device *cdev, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, DFC_API_VERSION "\n");
+}
+
+static ssize_t
+lpfc_info_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host));
+}
+
+static ssize_t
+lpfc_serialnum_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n",phba->SerialNumber);
+}
+
+static ssize_t
+lpfc_modeldesc_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelDesc);
+}
+
+static ssize_t
+lpfc_modelname_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelName);
+}
+
+static ssize_t
+lpfc_programtype_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n",phba->ProgramType);
+}
+
+static ssize_t
+lpfc_portnum_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n",phba->Port);
+}
+
+static ssize_t
+lpfc_fwrev_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ char fwrev[32];
+ lpfc_decode_firmware_rev(phba, fwrev, 1);
+ return snprintf(buf, PAGE_SIZE, "%s\n",fwrev);
+}
+
+static ssize_t
+lpfc_hdw_show(struct class_device *cdev, char *buf)
+{
+ char hdw[9];
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ lpfc_vpd_t *vp = &phba->vpd;
+ lpfc_jedec_to_ascii(vp->rev.biuRev, hdw);
+ return snprintf(buf, PAGE_SIZE, "%s\n", hdw);
+}
+static ssize_t
+lpfc_option_rom_version_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion);
+}
+static ssize_t
+lpfc_state_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ int len = 0;
+ switch (phba->hba_state) {
+ case LPFC_INIT_START:
+ case LPFC_INIT_MBX_CMDS:
+ case LPFC_LINK_DOWN:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n");
+ break;
+ case LPFC_LINK_UP:
+ case LPFC_LOCAL_CFG_LINK:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Link Up\n");
+ break;
+ case LPFC_FLOGI:
+ case LPFC_FABRIC_CFG_LINK:
+ case LPFC_NS_REG:
+ case LPFC_NS_QRY:
+ case LPFC_BUILD_DISC_LIST:
+ case LPFC_DISC_AUTH:
+ case LPFC_CLEAR_LA:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "Link Up - Discovery\n");
+ break;
+ case LPFC_HBA_READY:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "Link Up - Ready:\n");
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ if (phba->fc_flag & FC_PUBLIC_LOOP)
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ " Public Loop\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ " Private Loop\n");
+ } else {
+ if (phba->fc_flag & FC_FABRIC)
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ " Fabric\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ " Point-2-Point\n");
+ }
+ }
+ return len;
+}
+
+static ssize_t
+lpfc_num_discovered_ports_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%d\n", phba->fc_map_cnt +
+ phba->fc_unmap_cnt);
+}
+
+
+static ssize_t
+lpfc_issue_lip (struct class_device *cdev, const char *buf, size_t count)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0];
+ int val = 0;
+ LPFC_MBOXQ_t *pmboxq;
+ int mbxstatus = MBXERR_ERROR;
+
+ if ((sscanf(buf, "%d", &val) != 1) ||
+ (val != 1))
+ return -EINVAL;
+
+ if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+ (phba->hba_state != LPFC_HBA_READY))
+ return -EPERM;
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL);
+
+ if (!pmboxq)
+ return -ENOMEM;
+
+ memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+ lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed);
+ mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+ if (mbxstatus == MBX_TIMEOUT)
+ pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ else
+ mempool_free( pmboxq, phba->mbox_mem_pool);
+
+ if (mbxstatus == MBXERR_ERROR)
+ return -EIO;
+
+ return strlen(buf);
+}
+
+static ssize_t
+lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
+}
+
+static ssize_t
+lpfc_board_online_show(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+ if (!phba) return 0;
+
+ if (phba->fc_flag & FC_OFFLINE_MODE)
+ return snprintf(buf, PAGE_SIZE, "0\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static ssize_t
+lpfc_board_online_store(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ struct Scsi_Host *host = class_to_shost(cdev);
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ struct completion online_compl;
+ int val=0, status=0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return 0;
+
+ init_completion(&online_compl);
+
+ if (val)
+ lpfc_workq_post_event(phba, &status, &online_compl,
+ LPFC_EVT_ONLINE);
+ else
+ lpfc_workq_post_event(phba, &status, &online_compl,
+ LPFC_EVT_OFFLINE);
+ wait_for_completion(&online_compl);
+ if (!status)
+ return strlen(buf);
+ else
+ return 0;
+}
+
+
+#define lpfc_param_show(attr) \
+static ssize_t \
+lpfc_##attr##_show(struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *host = class_to_shost(cdev);\
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\
+ int val = 0;\
+ if (phba){\
+ val = phba->cfg_##attr;\
+ return snprintf(buf, PAGE_SIZE, "%d\n",\
+ phba->cfg_##attr);\
+ }\
+ return 0;\
+}
+
+#define lpfc_param_store(attr, minval, maxval) \
+static ssize_t \
+lpfc_##attr##_store(struct class_device *cdev, const char *buf, size_t count) \
+{ \
+ struct Scsi_Host *host = class_to_shost(cdev);\
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\
+ int val = 0;\
+ if (!isdigit(buf[0]))\
+ return -EINVAL;\
+ if (sscanf(buf, "0x%x", &val) != 1)\
+ if (sscanf(buf, "%d", &val) != 1)\
+ return -EINVAL;\
+ if (phba){\
+ if (val >= minval && val <= maxval) {\
+ phba->cfg_##attr = val;\
+ return strlen(buf);\
+ }\
+ }\
+ return 0;\
+}
+
+#define LPFC_ATTR_R_NOINIT(name, desc) \
+extern int lpfc_##name;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
+static int lpfc_##name = defval;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
+static int lpfc_##name = defval;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+lpfc_param_store(name, minval, maxval)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+ lpfc_##name##_show, lpfc_##name##_store)
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL);
+static CLASS_DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL);
+static CLASS_DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL);
+static CLASS_DEVICE_ATTR(modelname, S_IRUGO, lpfc_modelname_show, NULL);
+static CLASS_DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL);
+static CLASS_DEVICE_ATTR(portnum, S_IRUGO, lpfc_portnum_show, NULL);
+static CLASS_DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL);
+static CLASS_DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL);
+static CLASS_DEVICE_ATTR(state, S_IRUGO, lpfc_state_show, NULL);
+static CLASS_DEVICE_ATTR(option_rom_version, S_IRUGO,
+ lpfc_option_rom_version_show, NULL);
+static CLASS_DEVICE_ATTR(num_discovered_ports, S_IRUGO,
+ lpfc_num_discovered_ports_show, NULL);
+static CLASS_DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
+static CLASS_DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show,
+ NULL);
+static CLASS_DEVICE_ATTR(management_version, S_IRUGO, management_version_show,
+ NULL);
+static CLASS_DEVICE_ATTR(issue_lip, S_IWUSR, NULL, lpfc_issue_lip);
+static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR,
+ lpfc_board_online_show, lpfc_board_online_store);
+
+
+/*
+# lpfc_log_verbose: Only turn this flag on if you are willing to risk being
+# deluged with LOTS of information.
+# You can set a bit mask to record specific types of verbose messages:
+#
+# LOG_ELS 0x1 ELS events
+# LOG_DISCOVERY 0x2 Link discovery events
+# LOG_MBOX 0x4 Mailbox events
+# LOG_INIT 0x8 Initialization events
+# LOG_LINK_EVENT 0x10 Link events
+# LOG_IP 0x20 IP traffic history
+# LOG_FCP 0x40 FCP traffic history
+# LOG_NODE 0x80 Node table events
+# LOG_MISC 0x400 Miscellaneous events
+# LOG_SLI 0x800 SLI events
+# LOG_CHK_COND 0x1000 FCP Check condition flag
+# LOG_LIBDFC 0x2000 LIBDFC events
+# LOG_ALL_MSG 0xffff LOG all messages
+*/
+LPFC_ATTR_RW(log_verbose, 0x0, 0x0, 0xffff, "Verbose logging bit-mask");
+
+/*
+# lun_queue_depth: This parameter is used to limit the number of outstanding
+# commands per FCP LUN. Value range is [1,128]. Default value is 30.
+*/
+LPFC_ATTR_R(lun_queue_depth, 30, 1, 128,
+ "Max number of FCP commands we can queue to a specific LUN");
+
+/*
+# Some disk devices have a "select ID" or "select Target" capability.
+# From a protocol standpoint "select ID" usually means select the
+# Fibre channel "ALPA". In the FC-AL Profile there is an "informative
+# annex" which contains a table that maps a "select ID" (a number
+# between 0 and 7F) to an ALPA. By default, for compatibility with
+# older drivers, the lpfc driver scans this table from low ALPA to high
+# ALPA.
+#
+# Turning on the scan-down variable (on = 1, off = 0) will
+# cause the lpfc driver to use an inverted table, effectively
+# scanning ALPAs from high to low. Value range is [0,1]. Default value is 1.
+#
+# (Note: This "select ID" functionality is a LOOP ONLY characteristic
+# and will not work across a fabric. Also this parameter will take
+# effect only in the case when ALPA map is not available.)
+*/
+LPFC_ATTR_R(scan_down, 1, 0, 1,
+ "Start scanning for devices from highest ALPA to lowest");
+
+/*
+# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
+# until the timer expires. Value range is [0,255]. Default value is 20.
+# NOTE: this MUST be less then the SCSI Layer command timeout - 1.
+*/
+LPFC_ATTR_RW(nodev_tmo, 30, 0, 255,
+ "Seconds driver will hold I/O waiting for a device to come back");
+
+/*
+# lpfc_topology: link topology for init link
+# 0x0 = attempt loop mode then point-to-point
+# 0x02 = attempt point-to-point mode only
+# 0x04 = attempt loop mode only
+# 0x06 = attempt point-to-point mode then loop
+# Set point-to-point mode if you want to run as an N_Port.
+# Set loop mode if you want to run as an NL_Port. Value range is [0,0x6].
+# Default value is 0.
+*/
+LPFC_ATTR_R(topology, 0, 0, 6, "Select Fibre Channel topology");
+
+/*
+# lpfc_link_speed: Link speed selection for initializing the Fibre Channel
+# connection.
+# 0 = auto select (default)
+# 1 = 1 Gigabaud
+# 2 = 2 Gigabaud
+# 4 = 4 Gigabaud
+# Value range is [0,4]. Default value is 0.
+*/
+LPFC_ATTR_R(link_speed, 0, 0, 4, "Select link speed");
+
+/*
+# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
+# Value range is [2,3]. Default value is 3.
+*/
+LPFC_ATTR_R(fcp_class, 3, 2, 3,
+ "Select Fibre Channel class of service for FCP sequences");
+
+/*
+# lpfc_use_adisc: Use ADISC for FCP rediscovery instead of PLOGI. Value range
+# is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_RW(use_adisc, 0, 0, 1,
+ "Use ADISC on rediscovery to authenticate FCP devices");
+
+/*
+# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value
+# range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support");
+
+/*
+# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing
+# cr_delay (msec) or cr_count outstanding commands. cr_delay can take
+# value [0,63]. cr_count can take value [0,255]. Default value of cr_delay
+# is 0. Default value of cr_count is 1. The cr_count feature is disabled if
+# cr_delay is set to 0.
+*/
+static int lpfc_cr_delay = 0;
+module_param(lpfc_cr_delay, int , 0);
+MODULE_PARM_DESC(lpfc_cr_delay, "A count of milliseconds after which an "
+ "interrupt response is generated");
+
+static int lpfc_cr_count = 1;
+module_param(lpfc_cr_count, int, 0);
+MODULE_PARM_DESC(lpfc_cr_count, "A count of I/O completions after which an "
+ "interrupt response is generated");
+
+/*
+# lpfc_fdmi_on: controls FDMI support.
+# 0 = no FDMI support
+# 1 = support FDMI without attribute of hostname
+# 2 = support FDMI with attribute of hostname
+# Value range [0,2]. Default value is 0.
+*/
+LPFC_ATTR_RW(fdmi_on, 0, 0, 2, "Enable FDMI support");
+
+/*
+# Specifies the maximum number of ELS cmds we can have outstanding (for
+# discovery). Value range is [1,64]. Default value = 32.
+*/
+static int lpfc_discovery_threads = 32;
+module_param(lpfc_discovery_threads, int, 0);
+MODULE_PARM_DESC(lpfc_discovery_threads, "Maximum number of ELS commands "
+ "during discovery");
+
+/*
+# lpfc_max_luns: maximum number of LUNs per target driver will support
+# Value range is [1,32768]. Default value is 256.
+# NOTE: The SCSI layer will scan each target for this many luns
+*/
+LPFC_ATTR_R(max_luns, 256, 1, 32768,
+ "Maximum number of LUNs per target driver will support");
+
+struct class_device_attribute *lpfc_host_attrs[] = {
+ &class_device_attr_info,
+ &class_device_attr_serialnum,
+ &class_device_attr_modeldesc,
+ &class_device_attr_modelname,
+ &class_device_attr_programtype,
+ &class_device_attr_portnum,
+ &class_device_attr_fwrev,
+ &class_device_attr_hdw,
+ &class_device_attr_option_rom_version,
+ &class_device_attr_state,
+ &class_device_attr_num_discovered_ports,
+ &class_device_attr_lpfc_drvr_version,
+ &class_device_attr_lpfc_log_verbose,
+ &class_device_attr_lpfc_lun_queue_depth,
+ &class_device_attr_lpfc_nodev_tmo,
+ &class_device_attr_lpfc_fcp_class,
+ &class_device_attr_lpfc_use_adisc,
+ &class_device_attr_lpfc_ack0,
+ &class_device_attr_lpfc_topology,
+ &class_device_attr_lpfc_scan_down,
+ &class_device_attr_lpfc_link_speed,
+ &class_device_attr_lpfc_fdmi_on,
+ &class_device_attr_lpfc_max_luns,
+ &class_device_attr_nport_evt_cnt,
+ &class_device_attr_management_version,
+ &class_device_attr_issue_lip,
+ &class_device_attr_board_online,
+ NULL,
+};
+
+static ssize_t
+sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ size_t buf_off;
+ struct Scsi_Host *host = class_to_shost(container_of(kobj,
+ struct class_device, kobj));
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+ if ((off + count) > FF_REG_AREA_SIZE)
+ return -ERANGE;
+
+ if (count == 0) return 0;
+
+ if (off % 4 || count % 4 || (unsigned long)buf % 4)
+ return -EINVAL;
+
+ spin_lock_irq(phba->host->host_lock);
+
+ if (!(phba->fc_flag & FC_OFFLINE_MODE)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return -EPERM;
+ }
+
+ for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t))
+ writel(*((uint32_t *)(buf + buf_off)),
+ phba->ctrl_regs_memmap_p + off + buf_off);
+
+ spin_unlock_irq(phba->host->host_lock);
+
+ return count;
+}
+
+static ssize_t
+sysfs_ctlreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ size_t buf_off;
+ uint32_t * tmp_ptr;
+ struct Scsi_Host *host = class_to_shost(container_of(kobj,
+ struct class_device, kobj));
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+ if (off > FF_REG_AREA_SIZE)
+ return -ERANGE;
+
+ if ((off + count) > FF_REG_AREA_SIZE)
+ count = FF_REG_AREA_SIZE - off;
+
+ if (count == 0) return 0;
+
+ if (off % 4 || count % 4 || (unsigned long)buf % 4)
+ return -EINVAL;
+
+ spin_lock_irq(phba->host->host_lock);
+
+ for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) {
+ tmp_ptr = (uint32_t *)(buf + buf_off);
+ *tmp_ptr = readl(phba->ctrl_regs_memmap_p + off + buf_off);
+ }
+
+ spin_unlock_irq(phba->host->host_lock);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_ctlreg_attr = {
+ .attr = {
+ .name = "ctlreg",
+ .mode = S_IRUSR | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = 256,
+ .read = sysfs_ctlreg_read,
+ .write = sysfs_ctlreg_write,
+};
+
+
+static void
+sysfs_mbox_idle (struct lpfc_hba * phba)
+{
+ phba->sysfs_mbox.state = SMBOX_IDLE;
+ phba->sysfs_mbox.offset = 0;
+
+ if (phba->sysfs_mbox.mbox) {
+ mempool_free(phba->sysfs_mbox.mbox,
+ phba->mbox_mem_pool);
+ phba->sysfs_mbox.mbox = NULL;
+ }
+}
+
+static ssize_t
+sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct Scsi_Host * host =
+ class_to_shost(container_of(kobj, struct class_device, kobj));
+ struct lpfc_hba * phba = (struct lpfc_hba*)host->hostdata[0];
+ struct lpfcMboxq * mbox = NULL;
+
+ if ((count + off) > MAILBOX_CMD_SIZE)
+ return -ERANGE;
+
+ if (off % 4 || count % 4 || (unsigned long)buf % 4)
+ return -EINVAL;
+
+ if (count == 0)
+ return 0;
+
+ if (off == 0) {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ }
+
+ spin_lock_irq(host->host_lock);
+
+ if (off == 0) {
+ if (phba->sysfs_mbox.mbox)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ else
+ phba->sysfs_mbox.mbox = mbox;
+ phba->sysfs_mbox.state = SMBOX_WRITING;
+ } else {
+ if (phba->sysfs_mbox.state != SMBOX_WRITING ||
+ phba->sysfs_mbox.offset != off ||
+ phba->sysfs_mbox.mbox == NULL ) {
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(host->host_lock);
+ return -EINVAL;
+ }
+ }
+
+ memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off,
+ buf, count);
+
+ phba->sysfs_mbox.offset = off + count;
+
+ spin_unlock_irq(host->host_lock);
+
+ return count;
+}
+
+static ssize_t
+sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct Scsi_Host *host =
+ class_to_shost(container_of(kobj, struct class_device,
+ kobj));
+ struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+ int rc;
+
+ if (off > sizeof(MAILBOX_t))
+ return -ERANGE;
+
+ if ((count + off) > sizeof(MAILBOX_t))
+ count = sizeof(MAILBOX_t) - off;
+
+ if (off % 4 || count % 4 || (unsigned long)buf % 4)
+ return -EINVAL;
+
+ if (off && count == 0)
+ return 0;
+
+ spin_lock_irq(phba->host->host_lock);
+
+ if (off == 0 &&
+ phba->sysfs_mbox.state == SMBOX_WRITING &&
+ phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) {
+
+ switch (phba->sysfs_mbox.mbox->mb.mbxCommand) {
+ /* Offline only */
+ case MBX_WRITE_NV:
+ case MBX_INIT_LINK:
+ case MBX_DOWN_LINK:
+ case MBX_CONFIG_LINK:
+ case MBX_CONFIG_RING:
+ case MBX_RESET_RING:
+ case MBX_UNREG_LOGIN:
+ case MBX_CLEAR_LA:
+ case MBX_DUMP_CONTEXT:
+ case MBX_RUN_DIAGS:
+ case MBX_RESTART:
+ case MBX_FLASH_WR_ULA:
+ case MBX_SET_MASK:
+ case MBX_SET_SLIM:
+ case MBX_SET_DEBUG:
+ if (!(phba->fc_flag & FC_OFFLINE_MODE)) {
+ printk(KERN_WARNING "mbox_read:Command 0x%x "
+ "is illegal in on-line state\n",
+ phba->sysfs_mbox.mbox->mb.mbxCommand);
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(phba->host->host_lock);
+ return -EPERM;
+ }
+ case MBX_LOAD_SM:
+ case MBX_READ_NV:
+ case MBX_READ_CONFIG:
+ case MBX_READ_RCONFIG:
+ case MBX_READ_STATUS:
+ case MBX_READ_XRI:
+ case MBX_READ_REV:
+ case MBX_READ_LNK_STAT:
+ case MBX_DUMP_MEMORY:
+ case MBX_DOWN_LOAD:
+ case MBX_UPDATE_CFG:
+ case MBX_LOAD_AREA:
+ case MBX_LOAD_EXP_ROM:
+ break;
+ case MBX_READ_SPARM64:
+ case MBX_READ_LA:
+ case MBX_READ_LA64:
+ case MBX_REG_LOGIN:
+ case MBX_REG_LOGIN64:
+ case MBX_CONFIG_PORT:
+ case MBX_RUN_BIU_DIAG:
+ printk(KERN_WARNING "mbox_read: Illegal Command 0x%x\n",
+ phba->sysfs_mbox.mbox->mb.mbxCommand);
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(phba->host->host_lock);
+ return -EPERM;
+ default:
+ printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n",
+ phba->sysfs_mbox.mbox->mb.mbxCommand);
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(phba->host->host_lock);
+ return -EPERM;
+ }
+
+ if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+ (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){
+
+ spin_unlock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_mbox (phba,
+ phba->sysfs_mbox.mbox,
+ MBX_POLL);
+ spin_lock_irq(phba->host->host_lock);
+
+ } else {
+ spin_unlock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_mbox_wait (phba,
+ phba->sysfs_mbox.mbox,
+ phba->fc_ratov * 2);
+ spin_lock_irq(phba->host->host_lock);
+ }
+
+ if (rc != MBX_SUCCESS) {
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(host->host_lock);
+ return -ENODEV;
+ }
+ phba->sysfs_mbox.state = SMBOX_READING;
+ }
+ else if (phba->sysfs_mbox.offset != off ||
+ phba->sysfs_mbox.state != SMBOX_READING) {
+ printk(KERN_WARNING "mbox_read: Bad State\n");
+ sysfs_mbox_idle(phba);
+ spin_unlock_irq(host->host_lock);
+ return -EINVAL;
+ }
+
+ memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count);
+
+ phba->sysfs_mbox.offset = off + count;
+
+ if (phba->sysfs_mbox.offset == sizeof(MAILBOX_t))
+ sysfs_mbox_idle(phba);
+
+ spin_unlock_irq(phba->host->host_lock);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_mbox_attr = {
+ .attr = {
+ .name = "mbox",
+ .mode = S_IRUSR | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(MAILBOX_t),
+ .read = sysfs_mbox_read,
+ .write = sysfs_mbox_write,
+};
+
+int
+lpfc_alloc_sysfs_attr(struct lpfc_hba *phba)
+{
+ struct Scsi_Host *host = phba->host;
+ int error;
+
+ error = sysfs_create_bin_file(&host->shost_classdev.kobj,
+ &sysfs_ctlreg_attr);
+ if (error)
+ goto out;
+
+ error = sysfs_create_bin_file(&host->shost_classdev.kobj,
+ &sysfs_mbox_attr);
+ if (error)
+ goto out_remove_ctlreg_attr;
+
+ return 0;
+out_remove_ctlreg_attr:
+ sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr);
+out:
+ return error;
+}
+
+void
+lpfc_free_sysfs_attr(struct lpfc_hba *phba)
+{
+ struct Scsi_Host *host = phba->host;
+
+ sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr);
+ sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr);
+}
+
+
+/*
+ * Dynamic FC Host Attributes Support
+ */
+
+static void
+lpfc_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+ /* note: fc_myDID already in cpu endianness */
+ fc_host_port_id(shost) = phba->fc_myDID;
+}
+
+static void
+lpfc_get_host_port_type(struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+ spin_lock_irq(shost->host_lock);
+
+ if (phba->hba_state == LPFC_HBA_READY) {
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ if (phba->fc_flag & FC_PUBLIC_LOOP)
+ fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_LPORT;
+ } else {
+ if (phba->fc_flag & FC_FABRIC)
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_PTP;
+ }
+ } else
+ fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+
+ spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+ spin_lock_irq(shost->host_lock);
+
+ if (phba->fc_flag & FC_OFFLINE_MODE)
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ else {
+ switch (phba->hba_state) {
+ case LPFC_INIT_START:
+ case LPFC_INIT_MBX_CMDS:
+ case LPFC_LINK_DOWN:
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ break;
+ case LPFC_LINK_UP:
+ case LPFC_LOCAL_CFG_LINK:
+ case LPFC_FLOGI:
+ case LPFC_FABRIC_CFG_LINK:
+ case LPFC_NS_REG:
+ case LPFC_NS_QRY:
+ case LPFC_BUILD_DISC_LIST:
+ case LPFC_DISC_AUTH:
+ case LPFC_CLEAR_LA:
+ case LPFC_HBA_READY:
+ /* Links up, beyond this port_type reports state */
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ break;
+ case LPFC_HBA_ERROR:
+ fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
+ break;
+ default:
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+ break;
+ }
+ }
+
+ spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_speed(struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+ spin_lock_irq(shost->host_lock);
+
+ if (phba->hba_state == LPFC_HBA_READY) {
+ switch(phba->fc_linkspeed) {
+ case LA_1GHZ_LINK:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ case LA_2GHZ_LINK:
+ fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
+ break;
+ case LA_4GHZ_LINK:
+ fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_fabric_name (struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+ u64 nodename;
+
+ spin_lock_irq(shost->host_lock);
+
+ if ((phba->fc_flag & FC_FABRIC) ||
+ ((phba->fc_topology == TOPOLOGY_LOOP) &&
+ (phba->fc_flag & FC_PUBLIC_LOOP)))
+ memcpy(&nodename, &phba->fc_fabparam.nodeName, sizeof(u64));
+ else
+ /* fabric is local port if there is no F/FL_Port */
+ memcpy(&nodename, &phba->fc_nodename, sizeof(u64));
+
+ spin_unlock_irq(shost->host_lock);
+
+ fc_host_fabric_name(shost) = be64_to_cpu(nodename);
+}
+
+
+static struct fc_host_statistics *
+lpfc_get_stats(struct Scsi_Host *shost)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+ struct lpfc_sli *psli = &phba->sli;
+ struct fc_host_statistics *hs =
+ (struct fc_host_statistics *)phba->link_stats;
+ LPFC_MBOXQ_t *pmboxq;
+ MAILBOX_t *pmb;
+ int rc=0;
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmboxq)
+ return NULL;
+ memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+
+ pmb = &pmboxq->mb;
+ pmb->mbxCommand = MBX_READ_STATUS;
+ pmb->mbxOwner = OWN_HOST;
+ pmboxq->context1 = NULL;
+
+ if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+ (!(psli->sli_flag & LPFC_SLI2_ACTIVE))){
+ rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+ } else
+ rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+ if (rc != MBX_SUCCESS) {
+ if (pmboxq) {
+ if (rc == MBX_TIMEOUT)
+ pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ else
+ mempool_free( pmboxq, phba->mbox_mem_pool);
+ }
+ return NULL;
+ }
+
+ hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt;
+ hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256);
+ hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt;
+ hs->rx_words = (pmb->un.varRdStatus.rcvByteCnt * 256);
+
+ memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+ pmb->mbxCommand = MBX_READ_LNK_STAT;
+ pmb->mbxOwner = OWN_HOST;
+ pmboxq->context1 = NULL;
+
+ if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+ (!(psli->sli_flag & LPFC_SLI2_ACTIVE))) {
+ rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+ } else
+ rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+ if (rc != MBX_SUCCESS) {
+ if (pmboxq) {
+ if (rc == MBX_TIMEOUT)
+ pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ else
+ mempool_free( pmboxq, phba->mbox_mem_pool);
+ }
+ return NULL;
+ }
+
+ hs->link_failure_count = pmb->un.varRdLnk.linkFailureCnt;
+ hs->loss_of_sync_count = pmb->un.varRdLnk.lossSyncCnt;
+ hs->loss_of_signal_count = pmb->un.varRdLnk.lossSignalCnt;
+ hs->prim_seq_protocol_err_count = pmb->un.varRdLnk.primSeqErrCnt;
+ hs->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
+ hs->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
+ hs->error_frames = pmb->un.varRdLnk.crcCnt;
+
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ hs->lip_count = (phba->fc_eventTag >> 1);
+ hs->nos_count = -1;
+ } else {
+ hs->lip_count = -1;
+ hs->nos_count = (phba->fc_eventTag >> 1);
+ }
+
+ hs->dumped_frames = -1;
+
+/* FIX ME */
+ /*hs->SecondsSinceLastReset = (jiffies - lpfc_loadtime) / HZ;*/
+
+ return hs;
+}
+
+
+/*
+ * The LPFC driver treats linkdown handling as target loss events so there
+ * are no sysfs handlers for link_down_tmo.
+ */
+static void
+lpfc_get_starget_port_id(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+ uint32_t did = -1;
+ struct lpfc_nodelist *ndlp = NULL;
+
+ spin_lock_irq(shost->host_lock);
+ /* Search the mapped list for this target ID */
+ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+ if (starget->id == ndlp->nlp_sid) {
+ did = ndlp->nlp_DID;
+ break;
+ }
+ }
+ spin_unlock_irq(shost->host_lock);
+
+ fc_starget_port_id(starget) = did;
+}
+
+static void
+lpfc_get_starget_node_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+ uint64_t node_name = 0;
+ struct lpfc_nodelist *ndlp = NULL;
+
+ spin_lock_irq(shost->host_lock);
+ /* Search the mapped list for this target ID */
+ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+ if (starget->id == ndlp->nlp_sid) {
+ memcpy(&node_name, &ndlp->nlp_nodename,
+ sizeof(struct lpfc_name));
+ break;
+ }
+ }
+ spin_unlock_irq(shost->host_lock);
+
+ fc_starget_node_name(starget) = be64_to_cpu(node_name);
+}
+
+static void
+lpfc_get_starget_port_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+ uint64_t port_name = 0;
+ struct lpfc_nodelist *ndlp = NULL;
+
+ spin_lock_irq(shost->host_lock);
+ /* Search the mapped list for this target ID */
+ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+ if (starget->id == ndlp->nlp_sid) {
+ memcpy(&port_name, &ndlp->nlp_portname,
+ sizeof(struct lpfc_name));
+ break;
+ }
+ }
+ spin_unlock_irq(shost->host_lock);
+
+ fc_starget_port_name(starget) = be64_to_cpu(port_name);
+}
+
+static void
+lpfc_get_rport_loss_tmo(struct fc_rport *rport)
+{
+ /*
+ * Return the driver's global value for device loss timeout plus
+ * five seconds to allow the driver's nodev timer to run.
+ */
+ rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
+}
+
+static void
+lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+ /*
+ * The driver doesn't have a per-target timeout setting. Set
+ * this value globally. lpfc_nodev_tmo should be greater then 0.
+ */
+ if (timeout)
+ lpfc_nodev_tmo = timeout;
+ else
+ lpfc_nodev_tmo = 1;
+ rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
+}
+
+
+#define lpfc_rport_show_function(field, format_string, sz, cast) \
+static ssize_t \
+lpfc_show_rport_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct fc_rport *rport = transport_class_to_rport(cdev); \
+ struct lpfc_rport_data *rdata = rport->hostdata; \
+ return snprintf(buf, sz, format_string, \
+ (rdata->target) ? cast rdata->target->field : 0); \
+}
+
+#define lpfc_rport_rd_attr(field, format_string, sz) \
+ lpfc_rport_show_function(field, format_string, sz, ) \
+static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
+
+
+struct fc_function_template lpfc_transport_functions = {
+ /* fixed attributes the driver supports */
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_symbolic_name = 1,
+ .show_host_supported_speeds = 1,
+ .show_host_maxframe_size = 1,
+
+ /* dynamic attributes the driver supports */
+ .get_host_port_id = lpfc_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .get_host_port_type = lpfc_get_host_port_type,
+ .show_host_port_type = 1,
+
+ .get_host_port_state = lpfc_get_host_port_state,
+ .show_host_port_state = 1,
+
+ /* active_fc4s is shown but doesn't change (thus no get function) */
+ .show_host_active_fc4s = 1,
+
+ .get_host_speed = lpfc_get_host_speed,
+ .show_host_speed = 1,
+
+ .get_host_fabric_name = lpfc_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+
+ /*
+ * The LPFC driver treats linkdown handling as target loss events
+ * so there are no sysfs handlers for link_down_tmo.
+ */
+
+ .get_fc_host_stats = lpfc_get_stats,
+
+ /* the LPFC driver doesn't support resetting stats yet */
+
+ .dd_fcrport_size = sizeof(struct lpfc_rport_data),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .get_rport_dev_loss_tmo = lpfc_get_rport_loss_tmo,
+ .set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .get_starget_port_id = lpfc_get_starget_port_id,
+ .show_starget_port_id = 1,
+
+ .get_starget_node_name = lpfc_get_starget_node_name,
+ .show_starget_node_name = 1,
+
+ .get_starget_port_name = lpfc_get_starget_port_name,
+ .show_starget_port_name = 1,
+};
+
+void
+lpfc_get_cfgparam(struct lpfc_hba *phba)
+{
+ phba->cfg_log_verbose = lpfc_log_verbose;
+ phba->cfg_cr_delay = lpfc_cr_delay;
+ phba->cfg_cr_count = lpfc_cr_count;
+ phba->cfg_lun_queue_depth = lpfc_lun_queue_depth;
+ phba->cfg_fcp_class = lpfc_fcp_class;
+ phba->cfg_use_adisc = lpfc_use_adisc;
+ phba->cfg_ack0 = lpfc_ack0;
+ phba->cfg_topology = lpfc_topology;
+ phba->cfg_scan_down = lpfc_scan_down;
+ phba->cfg_nodev_tmo = lpfc_nodev_tmo;
+ phba->cfg_link_speed = lpfc_link_speed;
+ phba->cfg_fdmi_on = lpfc_fdmi_on;
+ phba->cfg_discovery_threads = lpfc_discovery_threads;
+ phba->cfg_max_luns = lpfc_max_luns;
+
+ /*
+ * The total number of segments is the configuration value plus 2
+ * since the IOCB need a command and response bde.
+ */
+ phba->cfg_sg_seg_cnt = LPFC_SG_SEG_CNT + 2;
+
+ /*
+ * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+ * used to create the sg_dma_buf_pool must be dynamically calculated
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp) +
+ (phba->cfg_sg_seg_cnt * sizeof(struct ulp_bde64));
+
+ switch (phba->pcidev->device) {
+ case PCI_DEVICE_ID_LP101:
+ case PCI_DEVICE_ID_BSMB:
+ case PCI_DEVICE_ID_ZSMB:
+ phba->cfg_hba_queue_depth = LPFC_LP101_HBA_Q_DEPTH;
+ break;
+ case PCI_DEVICE_ID_RFLY:
+ case PCI_DEVICE_ID_PFLY:
+ case PCI_DEVICE_ID_BMID:
+ case PCI_DEVICE_ID_ZMID:
+ case PCI_DEVICE_ID_TFLY:
+ phba->cfg_hba_queue_depth = LPFC_LC_HBA_Q_DEPTH;
+ break;
+ default:
+ phba->cfg_hba_queue_depth = LPFC_DFT_HBA_Q_DEPTH;
+ }
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h
new file mode 100644
index 000000000000..646649fe962a
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_compat.h
@@ -0,0 +1,97 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_compat.h 1.32 2005/01/25 17:51:45EST sf_support Exp $
+ *
+ * This file provides macros to aid compilation in the Linux 2.4 kernel
+ * over various platform architectures.
+ */
+
+/*******************************************************************
+Note: HBA's SLI memory contains little-endian LW.
+Thus to access it from a little-endian host,
+memcpy_toio() and memcpy_fromio() can be used.
+However on a big-endian host, copy 4 bytes at a time,
+using writel() and readl().
+ *******************************************************************/
+
+#if __BIG_ENDIAN
+
+static inline void
+lpfc_memcpy_to_slim(void __iomem *dest, void *src, unsigned int bytes)
+{
+ uint32_t __iomem *dest32;
+ uint32_t *src32;
+ unsigned int four_bytes;
+
+
+ dest32 = (uint32_t __iomem *) dest;
+ src32 = (uint32_t *) src;
+
+ /* write input bytes, 4 bytes at a time */
+ for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) {
+ writel( *src32, dest32);
+ readl(dest32); /* flush */
+ dest32++;
+ src32++;
+ }
+
+ return;
+}
+
+static inline void
+lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes)
+{
+ uint32_t *dest32;
+ uint32_t __iomem *src32;
+ unsigned int four_bytes;
+
+
+ dest32 = (uint32_t *) dest;
+ src32 = (uint32_t __iomem *) src;
+
+ /* read input bytes, 4 bytes at a time */
+ for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) {
+ *dest32 = readl( src32);
+ dest32++;
+ src32++;
+ }
+
+ return;
+}
+
+#else
+
+static inline void
+lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes)
+{
+ /* actually returns 1 byte past dest */
+ memcpy_toio( dest, src, bytes);
+}
+
+static inline void
+lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes)
+{
+ /* actually returns 1 byte past dest */
+ memcpy_fromio( dest, src, bytes);
+}
+
+#endif /* __BIG_ENDIAN */
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
new file mode 100644
index 000000000000..c504477a6a5d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -0,0 +1,216 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_crtn.h 1.166 2005/04/07 08:46:47EDT sf_support Exp $
+ */
+
+void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb,
+ struct lpfc_dmabuf *mp);
+void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *,
+ uint32_t);
+void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_unreg_did(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+
+
+int lpfc_linkdown(struct lpfc_hba *);
+void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+
+void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_nlp_plogi(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_adisc(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_unmapped(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_list(struct lpfc_hba *, struct lpfc_nodelist *, int);
+void lpfc_set_disctmo(struct lpfc_hba *);
+int lpfc_can_disctmo(struct lpfc_hba *);
+int lpfc_unreg_rpi(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *, struct lpfc_nodelist *);
+int lpfc_nlp_remove(struct lpfc_hba *, struct lpfc_nodelist *);
+void lpfc_nlp_init(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t);
+struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_hba *, uint32_t);
+struct lpfc_nodelist *lpfc_setup_rscn_node(struct lpfc_hba *, uint32_t);
+void lpfc_disc_list_loopmap(struct lpfc_hba *);
+void lpfc_disc_start(struct lpfc_hba *);
+void lpfc_disc_flush_list(struct lpfc_hba *);
+void lpfc_disc_timeout(unsigned long);
+void lpfc_scan_timeout(unsigned long);
+
+struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi);
+struct lpfc_nodelist *lpfc_findnode_remove_rpi(struct lpfc_hba * phba,
+ uint16_t rpi);
+void lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint16_t rpi);
+
+int lpfc_workq_post_event(struct lpfc_hba *, void *, void *, uint32_t);
+int lpfc_do_work(void *);
+int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *,
+ uint32_t);
+
+uint32_t lpfc_cmpl_prli_reglogin_issue(struct lpfc_hba *,
+ struct lpfc_nodelist *, void *,
+ uint32_t);
+uint32_t lpfc_cmpl_plogi_prli_issue(struct lpfc_hba *, struct lpfc_nodelist *,
+ void *, uint32_t);
+
+int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *,
+ struct serv_parm *, uint32_t);
+int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist * ndlp,
+ int);
+int lpfc_els_abort_flogi(struct lpfc_hba *);
+int lpfc_initial_flogi(struct lpfc_hba *);
+int lpfc_issue_els_plogi(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_prli(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_adisc(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_logo(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_scr(struct lpfc_hba *, uint32_t, uint8_t);
+int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
+int lpfc_els_rsp_acc(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *,
+ struct lpfc_nodelist *, LPFC_MBOXQ_t *, uint8_t);
+int lpfc_els_rsp_reject(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *,
+ struct lpfc_nodelist *);
+int lpfc_els_rsp_adisc_acc(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_nodelist *);
+int lpfc_els_rsp_prli_acc(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_nodelist *);
+void lpfc_els_retry_delay(unsigned long);
+void lpfc_els_retry_delay_handler(struct lpfc_nodelist *);
+void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
+int lpfc_els_handle_rscn(struct lpfc_hba *);
+int lpfc_els_flush_rscn(struct lpfc_hba *);
+int lpfc_rscn_payload_check(struct lpfc_hba *, uint32_t);
+void lpfc_els_flush_cmd(struct lpfc_hba *);
+int lpfc_els_disc_adisc(struct lpfc_hba *);
+int lpfc_els_disc_plogi(struct lpfc_hba *);
+void lpfc_els_timeout(unsigned long);
+void lpfc_els_timeout_handler(struct lpfc_hba *);
+
+void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
+int lpfc_ns_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int);
+int lpfc_fdmi_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int);
+void lpfc_fdmi_tmo(unsigned long);
+void lpfc_fdmi_tmo_handler(struct lpfc_hba *);
+
+int lpfc_config_port_prep(struct lpfc_hba *);
+int lpfc_config_port_post(struct lpfc_hba *);
+int lpfc_hba_down_prep(struct lpfc_hba *);
+void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
+int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int);
+void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
+uint8_t *lpfc_get_lpfchba_info(struct lpfc_hba *, uint8_t *);
+int lpfc_fcp_abort(struct lpfc_hba *, int, int, int);
+int lpfc_online(struct lpfc_hba *);
+int lpfc_offline(struct lpfc_hba *);
+
+
+int lpfc_sli_setup(struct lpfc_hba *);
+int lpfc_sli_queue_setup(struct lpfc_hba *);
+void lpfc_slim_access(struct lpfc_hba *);
+
+void lpfc_handle_eratt(struct lpfc_hba *);
+void lpfc_handle_latt(struct lpfc_hba *);
+irqreturn_t lpfc_intr_handler(int, void *, struct pt_regs *);
+
+void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *);
+void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
+LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *);
+
+int lpfc_mem_alloc(struct lpfc_hba *);
+void lpfc_mem_free(struct lpfc_hba *);
+
+int lpfc_sli_hba_setup(struct lpfc_hba *);
+int lpfc_sli_hba_down(struct lpfc_hba *);
+int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+int lpfc_sli_handle_mb_event(struct lpfc_hba *);
+int lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
+ struct lpfc_sli_ring *, uint32_t);
+void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *, uint32_t);
+void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
+int lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
+int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_dmabuf *);
+struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *,
+ struct lpfc_sli_ring *,
+ dma_addr_t);
+int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
+int lpfc_sli_sum_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,
+ uint64_t, lpfc_ctx_cmd);
+int lpfc_sli_abort_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,
+ uint64_t, uint32_t, lpfc_ctx_cmd);
+
+void lpfc_mbox_timeout(unsigned long);
+void lpfc_mbox_timeout_handler(struct lpfc_hba *);
+void lpfc_map_fcp_cmnd_to_bpl(struct lpfc_hba *, struct lpfc_scsi_buf *);
+void lpfc_free_scsi_cmd(struct lpfc_scsi_buf *);
+uint32_t lpfc_os_timeout_transform(struct lpfc_hba *, uint32_t);
+
+struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order,
+ uint32_t did);
+
+int lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq,
+ uint32_t timeout);
+
+int lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * piocb,
+ uint32_t flag,
+ struct lpfc_iocbq * prspiocbq,
+ uint32_t timeout);
+void lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba,
+ struct lpfc_iocbq * queue1,
+ struct lpfc_iocbq * queue2);
+
+void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *);
+void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t);
+
+/* Function prototypes. */
+const char* lpfc_info(struct Scsi_Host *);
+void lpfc_get_cfgparam(struct lpfc_hba *);
+int lpfc_alloc_sysfs_attr(struct lpfc_hba *);
+void lpfc_free_sysfs_attr(struct lpfc_hba *);
+extern struct class_device_attribute *lpfc_host_attrs[];
+extern struct scsi_host_template lpfc_template;
+extern struct fc_function_template lpfc_transport_functions;
+
+void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp);
+
+#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
+#define HBA_EVENT_RSCN 5
+#define HBA_EVENT_LINK_UP 2
+#define HBA_EVENT_LINK_DOWN 3
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
new file mode 100644
index 000000000000..c40cb239c16d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -0,0 +1,1237 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_ct.c 1.161 2005/04/13 11:59:01EDT sf_support Exp $
+ *
+ * Fibre Channel SCSI LAN Device Driver CT support
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_version.h"
+
+#define HBA_PORTSPEED_UNKNOWN 0 /* Unknown - transceiver
+ * incapable of reporting */
+#define HBA_PORTSPEED_1GBIT 1 /* 1 GBit/sec */
+#define HBA_PORTSPEED_2GBIT 2 /* 2 GBit/sec */
+#define HBA_PORTSPEED_4GBIT 8 /* 4 GBit/sec */
+#define HBA_PORTSPEED_8GBIT 16 /* 8 GBit/sec */
+#define HBA_PORTSPEED_10GBIT 4 /* 10 GBit/sec */
+#define HBA_PORTSPEED_NOT_NEGOTIATED 5 /* Speed not established */
+
+#define FOURBYTES 4
+
+
+static char *lpfc_release_version = LPFC_DRIVER_VERSION;
+
+/*
+ * lpfc_ct_unsol_event
+ */
+void
+lpfc_ct_unsol_event(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocbq)
+{
+
+ struct lpfc_iocbq *next_piocbq;
+ struct lpfc_dmabuf *pmbuf = NULL;
+ struct lpfc_dmabuf *matp, *next_matp;
+ uint32_t ctx = 0, size = 0, cnt = 0;
+ IOCB_t *icmd = &piocbq->iocb;
+ IOCB_t *save_icmd = icmd;
+ int i, go_exit = 0;
+ struct list_head head;
+
+ if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {
+ /* Not enough posted buffers; Try posting more buffers */
+ phba->fc_stat.NoRcvBuf++;
+ lpfc_post_buffer(phba, pring, 0, 1);
+ return;
+ }
+
+ /* If there are no BDEs associated with this IOCB,
+ * there is nothing to do.
+ */
+ if (icmd->ulpBdeCount == 0)
+ return;
+
+ INIT_LIST_HEAD(&head);
+ list_add_tail(&head, &piocbq->list);
+
+ list_for_each_entry_safe(piocbq, next_piocbq, &head, list) {
+ icmd = &piocbq->iocb;
+ if (ctx == 0)
+ ctx = (uint32_t) (icmd->ulpContext);
+ if (icmd->ulpBdeCount == 0)
+ continue;
+
+ for (i = 0; i < icmd->ulpBdeCount; i++) {
+ matp = lpfc_sli_ringpostbuf_get(phba, pring,
+ getPaddr(icmd->un.
+ cont64[i].
+ addrHigh,
+ icmd->un.
+ cont64[i].
+ addrLow));
+ if (!matp) {
+ /* Insert lpfc log message here */
+ lpfc_post_buffer(phba, pring, cnt, 1);
+ go_exit = 1;
+ goto ct_unsol_event_exit_piocbq;
+ }
+
+ /* Typically for Unsolicited CT requests */
+ if (!pmbuf) {
+ pmbuf = matp;
+ INIT_LIST_HEAD(&pmbuf->list);
+ } else
+ list_add_tail(&matp->list, &pmbuf->list);
+
+ size += icmd->un.cont64[i].tus.f.bdeSize;
+ cnt++;
+ }
+
+ icmd->ulpBdeCount = 0;
+ }
+
+ lpfc_post_buffer(phba, pring, cnt, 1);
+ if (save_icmd->ulpStatus) {
+ go_exit = 1;
+ }
+
+ct_unsol_event_exit_piocbq:
+ if (pmbuf) {
+ list_for_each_entry_safe(matp, next_matp, &pmbuf->list, list) {
+ lpfc_mbuf_free(phba, matp->virt, matp->phys);
+ list_del(&matp->list);
+ kfree(matp);
+ }
+ lpfc_mbuf_free(phba, pmbuf->virt, pmbuf->phys);
+ kfree(pmbuf);
+ }
+ return;
+}
+
+static void
+lpfc_free_ct_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mlist)
+{
+ struct lpfc_dmabuf *mlast, *next_mlast;
+
+ list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) {
+ lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
+ list_del(&mlast->list);
+ kfree(mlast);
+ }
+ lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
+ kfree(mlist);
+ return;
+}
+
+static struct lpfc_dmabuf *
+lpfc_alloc_ct_rsp(struct lpfc_hba * phba, int cmdcode, struct ulp_bde64 * bpl,
+ uint32_t size, int *entries)
+{
+ struct lpfc_dmabuf *mlist = NULL;
+ struct lpfc_dmabuf *mp;
+ int cnt, i = 0;
+
+ /* We get chucks of FCELSSIZE */
+ cnt = size > FCELSSIZE ? FCELSSIZE: size;
+
+ while (size) {
+ /* Allocate buffer for rsp payload */
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp) {
+ if (mlist)
+ lpfc_free_ct_rsp(phba, mlist);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&mp->list);
+
+ if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT))
+ mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
+ else
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
+
+ if (!mp->virt) {
+ kfree(mp);
+ lpfc_free_ct_rsp(phba, mlist);
+ return NULL;
+ }
+
+ /* Queue it to a linked list */
+ if (!mlist)
+ mlist = mp;
+ else
+ list_add_tail(&mp->list, &mlist->list);
+
+ bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+ /* build buffer ptr list for IOCB */
+ bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+ bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+ bpl->tus.f.bdeSize = (uint16_t) cnt;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+
+ i++;
+ size -= cnt;
+ }
+
+ *entries = i;
+ return mlist;
+}
+
+static int
+lpfc_gen_req(struct lpfc_hba *phba, struct lpfc_dmabuf *bmp,
+ struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp,
+ void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *),
+ struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry,
+ uint32_t tmo)
+{
+
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *geniocb = NULL;
+
+ /* Allocate buffer for command iocb */
+ spin_lock_irq(phba->host->host_lock);
+ list_remove_head(lpfc_iocb_list, geniocb, struct lpfc_iocbq, list);
+ spin_unlock_irq(phba->host->host_lock);
+
+ if (geniocb == NULL)
+ return 1;
+ memset(geniocb, 0, sizeof (struct lpfc_iocbq));
+
+ icmd = &geniocb->iocb;
+ icmd->un.genreq64.bdl.ulpIoTag32 = 0;
+ icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL;
+ icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64));
+
+ if (usr_flg)
+ geniocb->context3 = NULL;
+ else
+ geniocb->context3 = (uint8_t *) bmp;
+
+ /* Save for completion so we can release these resources */
+ geniocb->context1 = (uint8_t *) inp;
+ geniocb->context2 = (uint8_t *) outp;
+
+ /* Fill in payload, bp points to frame payload */
+ icmd->ulpCommand = CMD_GEN_REQUEST64_CR;
+
+ /* Fill in rest of iocb */
+ icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
+ icmd->un.genreq64.w5.hcsw.Dfctl = 0;
+ icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
+ icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+
+ if (!tmo)
+ tmo = (2 * phba->fc_ratov) + 1;
+ icmd->ulpTimeout = tmo;
+ icmd->ulpBdeCount = 1;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+ icmd->ulpContext = ndlp->nlp_rpi;
+
+ /* Issue GEN REQ IOCB for NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0119 Issue GEN REQ IOCB for NPORT x%x "
+ "Data: x%x x%x\n", phba->brd_no, icmd->un.ulpWord[5],
+ icmd->ulpIoTag, phba->hba_state);
+ geniocb->iocb_cmpl = cmpl;
+ geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;
+ spin_lock_irq(phba->host->host_lock);
+ if (lpfc_sli_issue_iocb(phba, pring, geniocb, 0) == IOCB_ERROR) {
+ list_add_tail(&geniocb->list, lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ return 1;
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ return 0;
+}
+
+static int
+lpfc_ct_cmd(struct lpfc_hba *phba, struct lpfc_dmabuf *inmp,
+ struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp,
+ void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *),
+ uint32_t rsp_size)
+{
+ struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt;
+ struct lpfc_dmabuf *outmp;
+ int cnt = 0, status;
+ int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)->
+ CommandResponse.bits.CmdRsp;
+
+ bpl++; /* Skip past ct request */
+
+ /* Put buffer(s) for ct rsp in bpl */
+ outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);
+ if (!outmp)
+ return -ENOMEM;
+
+ status = lpfc_gen_req(phba, bmp, inmp, outmp, cmpl, ndlp, 0,
+ cnt+1, 0);
+ if (status) {
+ lpfc_free_ct_rsp(phba, outmp);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int
+lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size)
+{
+ struct lpfc_sli_ct_request *Response =
+ (struct lpfc_sli_ct_request *) mp->virt;
+ struct lpfc_nodelist *ndlp = NULL;
+ struct lpfc_dmabuf *mlast, *next_mp;
+ uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType;
+ uint32_t Did;
+ uint32_t CTentry;
+ int Cnt;
+ struct list_head head;
+
+ lpfc_set_disctmo(phba);
+
+ Cnt = Size > FCELSSIZE ? FCELSSIZE : Size;
+
+ list_add_tail(&head, &mp->list);
+ list_for_each_entry_safe(mp, next_mp, &head, list) {
+ mlast = mp;
+
+ Size -= Cnt;
+
+ if (!ctptr)
+ ctptr = (uint32_t *) mlast->virt;
+ else
+ Cnt -= 16; /* subtract length of CT header */
+
+ /* Loop through entire NameServer list of DIDs */
+ while (Cnt) {
+
+ /* Get next DID from NameServer List */
+ CTentry = *ctptr++;
+ Did = ((be32_to_cpu(CTentry)) & Mask_DID);
+
+ ndlp = NULL;
+ if (Did != phba->fc_myDID) {
+ /* Check for rscn processing or not */
+ ndlp = lpfc_setup_disc_node(phba, Did);
+ }
+ /* Mark all node table entries that are in the
+ Nameserver */
+ if (ndlp) {
+ /* NameServer Rsp */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0238 Process x%x NameServer"
+ " Rsp Data: x%x x%x x%x\n",
+ phba->brd_no,
+ Did, ndlp->nlp_flag,
+ phba->fc_flag,
+ phba->fc_rscn_id_cnt);
+ } else {
+ /* NameServer Rsp */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0239 Skip x%x NameServer "
+ "Rsp Data: x%x x%x x%x\n",
+ phba->brd_no,
+ Did, Size, phba->fc_flag,
+ phba->fc_rscn_id_cnt);
+ }
+
+ if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY)))
+ goto nsout1;
+ Cnt -= sizeof (uint32_t);
+ }
+ ctptr = NULL;
+
+ }
+
+nsout1:
+ list_del(&head);
+
+ /* Here we are finished in the case RSCN */
+ if (phba->hba_state == LPFC_HBA_READY) {
+ lpfc_els_flush_rscn(phba);
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ return 0;
+}
+
+
+
+
+static void
+lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_sli *psli;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_dmabuf *inp;
+ struct lpfc_dmabuf *outp;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_sli_ct_request *CTrsp;
+
+ psli = &phba->sli;
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ inp = (struct lpfc_dmabuf *) cmdiocb->context1;
+ outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+ bmp = (struct lpfc_dmabuf *) cmdiocb->context3;
+
+ irsp = &rspiocb->iocb;
+ if (irsp->ulpStatus) {
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) ||
+ (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
+ goto out;
+ }
+
+ /* Check for retry */
+ if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) {
+ phba->fc_ns_retry++;
+ /* CT command is being retried */
+ ndlp =
+ lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+ NameServer_DID);
+ if (ndlp) {
+ if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) ==
+ 0) {
+ goto out;
+ }
+ }
+ }
+ } else {
+ /* Good status, continue checking */
+ CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
+ if (CTrsp->CommandResponse.bits.CmdRsp ==
+ be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) {
+ lpfc_ns_rsp(phba, outp,
+ (uint32_t) (irsp->un.genreq64.bdl.bdeSize));
+ } else if (CTrsp->CommandResponse.bits.CmdRsp ==
+ be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
+ /* NameServer Rsp Error */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0240 NameServer Rsp Error "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ CTrsp->CommandResponse.bits.CmdRsp,
+ (uint32_t) CTrsp->ReasonCode,
+ (uint32_t) CTrsp->Explanation,
+ phba->fc_flag);
+ } else {
+ /* NameServer Rsp Error */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0241 NameServer Rsp Error "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ CTrsp->CommandResponse.bits.CmdRsp,
+ (uint32_t) CTrsp->ReasonCode,
+ (uint32_t) CTrsp->Explanation,
+ phba->fc_flag);
+ }
+ }
+ /* Link up / RSCN discovery */
+ lpfc_disc_start(phba);
+out:
+ lpfc_free_ct_rsp(phba, outp);
+ lpfc_mbuf_free(phba, inp->virt, inp->phys);
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ kfree(inp);
+ kfree(bmp);
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&cmdiocb->list, &phba->lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_dmabuf *inp;
+ struct lpfc_dmabuf *outp;
+ IOCB_t *irsp;
+ struct lpfc_sli_ct_request *CTrsp;
+
+ psli = &phba->sli;
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ inp = (struct lpfc_dmabuf *) cmdiocb->context1;
+ outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+ bmp = (struct lpfc_dmabuf *) cmdiocb->context3;
+ irsp = &rspiocb->iocb;
+
+ CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
+
+ /* RFT request completes status <ulpStatus> CmdRsp <CmdRsp> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0209 RFT request completes ulpStatus x%x "
+ "CmdRsp x%x\n", phba->brd_no, irsp->ulpStatus,
+ CTrsp->CommandResponse.bits.CmdRsp);
+
+ lpfc_free_ct_rsp(phba, outp);
+ lpfc_mbuf_free(phba, inp->virt, inp->phys);
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ kfree(inp);
+ kfree(bmp);
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&cmdiocb->list, &phba->lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb);
+ return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb);
+ return;
+}
+
+void
+lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp)
+{
+ char fwrev[16];
+
+ lpfc_decode_firmware_rev(phba, fwrev, 0);
+
+ if (phba->Port[0]) {
+ sprintf(symbp, "Emulex %s Port %s FV%s DV%s", phba->ModelName,
+ phba->Port, fwrev, lpfc_release_version);
+ } else {
+ sprintf(symbp, "Emulex %s FV%s DV%s", phba->ModelName,
+ fwrev, lpfc_release_version);
+ }
+}
+
+/*
+ * lpfc_ns_cmd
+ * Description:
+ * Issue Cmd to NameServer
+ * SLI_CTNS_GID_FT
+ * LI_CTNS_RFT_ID
+ */
+int
+lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
+{
+ struct lpfc_dmabuf *mp, *bmp;
+ struct lpfc_sli_ct_request *CtReq;
+ struct ulp_bde64 *bpl;
+ void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *) = NULL;
+ uint32_t rsp_size = 1024;
+
+ /* fill in BDEs for command */
+ /* Allocate buffer for command payload */
+ mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp)
+ goto ns_cmd_exit;
+
+ INIT_LIST_HEAD(&mp->list);
+ mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
+ if (!mp->virt)
+ goto ns_cmd_free_mp;
+
+ /* Allocate buffer for Buffer ptr list */
+ bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp)
+ goto ns_cmd_free_mpvirt;
+
+ INIT_LIST_HEAD(&bmp->list);
+ bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(bmp->phys));
+ if (!bmp->virt)
+ goto ns_cmd_free_bmp;
+
+ /* NameServer Req */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0236 NameServer Req Data: x%x x%x x%x\n",
+ phba->brd_no, cmdcode, phba->fc_flag,
+ phba->fc_rscn_id_cnt);
+
+ bpl = (struct ulp_bde64 *) bmp->virt;
+ memset(bpl, 0, sizeof(struct ulp_bde64));
+ bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+ bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+ bpl->tus.f.bdeFlags = 0;
+ if (cmdcode == SLI_CTNS_GID_FT)
+ bpl->tus.f.bdeSize = GID_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_RFT_ID)
+ bpl->tus.f.bdeSize = RFT_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_RNN_ID)
+ bpl->tus.f.bdeSize = RNN_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_RSNN_NN)
+ bpl->tus.f.bdeSize = RSNN_REQUEST_SZ;
+ else
+ bpl->tus.f.bdeSize = 0;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ CtReq = (struct lpfc_sli_ct_request *) mp->virt;
+ memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request));
+ CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
+ CtReq->RevisionId.bits.InId = 0;
+ CtReq->FsType = SLI_CT_DIRECTORY_SERVICE;
+ CtReq->FsSubType = SLI_CT_DIRECTORY_NAME_SERVER;
+ CtReq->CommandResponse.bits.Size = 0;
+ switch (cmdcode) {
+ case SLI_CTNS_GID_FT:
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_CTNS_GID_FT);
+ CtReq->un.gid.Fc4Type = SLI_CTPT_FCP;
+ if (phba->hba_state < LPFC_HBA_READY)
+ phba->hba_state = LPFC_NS_QRY;
+ lpfc_set_disctmo(phba);
+ cmpl = lpfc_cmpl_ct_cmd_gid_ft;
+ rsp_size = FC_MAX_NS_RSP;
+ break;
+
+ case SLI_CTNS_RFT_ID:
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_CTNS_RFT_ID);
+ CtReq->un.rft.PortId = be32_to_cpu(phba->fc_myDID);
+ CtReq->un.rft.fcpReg = 1;
+ cmpl = lpfc_cmpl_ct_cmd_rft_id;
+ break;
+
+ case SLI_CTNS_RNN_ID:
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_CTNS_RNN_ID);
+ CtReq->un.rnn.PortId = be32_to_cpu(phba->fc_myDID);
+ memcpy(CtReq->un.rnn.wwnn, &phba->fc_nodename,
+ sizeof (struct lpfc_name));
+ cmpl = lpfc_cmpl_ct_cmd_rnn_id;
+ break;
+
+ case SLI_CTNS_RSNN_NN:
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_CTNS_RSNN_NN);
+ memcpy(CtReq->un.rsnn.wwnn, &phba->fc_nodename,
+ sizeof (struct lpfc_name));
+ lpfc_get_hba_sym_node_name(phba, CtReq->un.rsnn.symbname);
+ CtReq->un.rsnn.len = strlen(CtReq->un.rsnn.symbname);
+ cmpl = lpfc_cmpl_ct_cmd_rsnn_nn;
+ break;
+ }
+
+ if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, rsp_size))
+ /* On success, The cmpl function will free the buffers */
+ return 0;
+
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ns_cmd_free_bmp:
+ kfree(bmp);
+ns_cmd_free_mpvirt:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ns_cmd_free_mp:
+ kfree(mp);
+ns_cmd_exit:
+ return 1;
+}
+
+static void
+lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb)
+{
+ struct lpfc_dmabuf *bmp = cmdiocb->context3;
+ struct lpfc_dmabuf *inp = cmdiocb->context1;
+ struct lpfc_dmabuf *outp = cmdiocb->context2;
+ struct lpfc_sli_ct_request *CTrsp = outp->virt;
+ struct lpfc_sli_ct_request *CTcmd = inp->virt;
+ struct lpfc_nodelist *ndlp;
+ uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
+ uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp;
+
+ ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID);
+ if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
+ /* FDMI rsp failed */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0220 FDMI rsp failed Data: x%x\n",
+ phba->brd_no,
+ be16_to_cpu(fdmi_cmd));
+ }
+
+ switch (be16_to_cpu(fdmi_cmd)) {
+ case SLI_MGMT_RHBA:
+ lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RPA);
+ break;
+
+ case SLI_MGMT_RPA:
+ break;
+
+ case SLI_MGMT_DHBA:
+ lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DPRT);
+ break;
+
+ case SLI_MGMT_DPRT:
+ lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RHBA);
+ break;
+ }
+
+ lpfc_free_ct_rsp(phba, outp);
+ lpfc_mbuf_free(phba, inp->virt, inp->phys);
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ kfree(inp);
+ kfree(bmp);
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&cmdiocb->list, &phba->lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+int
+lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
+{
+ struct lpfc_dmabuf *mp, *bmp;
+ struct lpfc_sli_ct_request *CtReq;
+ struct ulp_bde64 *bpl;
+ uint32_t size;
+ REG_HBA *rh;
+ PORT_ENTRY *pe;
+ REG_PORT_ATTRIBUTE *pab;
+ ATTRIBUTE_BLOCK *ab;
+ ATTRIBUTE_ENTRY *ae;
+ void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *);
+
+
+ /* fill in BDEs for command */
+ /* Allocate buffer for command payload */
+ mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp)
+ goto fdmi_cmd_exit;
+
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
+ if (!mp->virt)
+ goto fdmi_cmd_free_mp;
+
+ /* Allocate buffer for Buffer ptr list */
+ bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp)
+ goto fdmi_cmd_free_mpvirt;
+
+ bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys));
+ if (!bmp->virt)
+ goto fdmi_cmd_free_bmp;
+
+ INIT_LIST_HEAD(&mp->list);
+ INIT_LIST_HEAD(&bmp->list);
+
+ /* FDMI request */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0218 FDMI Request Data: x%x x%x x%x\n",
+ phba->brd_no,
+ phba->fc_flag, phba->hba_state, cmdcode);
+
+ CtReq = (struct lpfc_sli_ct_request *) mp->virt;
+
+ memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request));
+ CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
+ CtReq->RevisionId.bits.InId = 0;
+
+ CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE;
+ CtReq->FsSubType = SLI_CT_FDMI_Subtypes;
+ size = 0;
+
+ switch (cmdcode) {
+ case SLI_MGMT_RHBA:
+ {
+ lpfc_vpd_t *vp = &phba->vpd;
+ uint32_t i, j, incr;
+ int len;
+
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_MGMT_RHBA);
+ CtReq->CommandResponse.bits.Size = 0;
+ rh = (REG_HBA *) & CtReq->un.PortID;
+ memcpy(&rh->hi.PortName, &phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ /* One entry (port) per adapter */
+ rh->rpl.EntryCnt = be32_to_cpu(1);
+ memcpy(&rh->rpl.pe, &phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+
+ /* point to the HBA attribute block */
+ size = 2 * sizeof (struct lpfc_name) + FOURBYTES;
+ ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size);
+ ab->EntryCnt = 0;
+
+ /* Point to the beginning of the first HBA attribute
+ entry */
+ /* #1 HBA attribute entry */
+ size += FOURBYTES;
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES
+ + sizeof (struct lpfc_name));
+ memcpy(&ae->un.NodeName, &phba->fc_sparam.nodeName,
+ sizeof (struct lpfc_name));
+ ab->EntryCnt++;
+ size += FOURBYTES + sizeof (struct lpfc_name);
+
+ /* #2 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER);
+ strcpy(ae->un.Manufacturer, "Emulex Corporation");
+ len = strlen(ae->un.Manufacturer);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #3 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER);
+ strcpy(ae->un.SerialNumber, phba->SerialNumber);
+ len = strlen(ae->un.SerialNumber);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #4 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(MODEL);
+ strcpy(ae->un.Model, phba->ModelName);
+ len = strlen(ae->un.Model);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #5 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION);
+ strcpy(ae->un.ModelDescription, phba->ModelDesc);
+ len = strlen(ae->un.ModelDescription);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #6 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8);
+ /* Convert JEDEC ID to ascii for hardware version */
+ incr = vp->rev.biuRev;
+ for (i = 0; i < 8; i++) {
+ j = (incr & 0xf);
+ if (j <= 9)
+ ae->un.HardwareVersion[7 - i] =
+ (char)((uint8_t) 0x30 +
+ (uint8_t) j);
+ else
+ ae->un.HardwareVersion[7 - i] =
+ (char)((uint8_t) 0x61 +
+ (uint8_t) (j - 10));
+ incr = (incr >> 4);
+ }
+ ab->EntryCnt++;
+ size += FOURBYTES + 8;
+
+ /* #7 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION);
+ strcpy(ae->un.DriverVersion, lpfc_release_version);
+ len = strlen(ae->un.DriverVersion);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #8 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION);
+ strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion);
+ len = strlen(ae->un.OptionROMVersion);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #9 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION);
+ lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion,
+ 1);
+ len = strlen(ae->un.FirmwareVersion);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #10 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION);
+ sprintf(ae->un.OsNameVersion, "%s %s %s",
+ system_utsname.sysname, system_utsname.release,
+ system_utsname.version);
+ len = strlen(ae->un.OsNameVersion);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ ab->EntryCnt++;
+ size += FOURBYTES + len;
+
+ /* #11 HBA attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+ ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+ ae->un.MaxCTPayloadLen = (65 * 4096);
+ ab->EntryCnt++;
+ size += FOURBYTES + 4;
+
+ ab->EntryCnt = be32_to_cpu(ab->EntryCnt);
+ /* Total size */
+ size = GID_REQUEST_SZ - 4 + size;
+ }
+ break;
+
+ case SLI_MGMT_RPA:
+ {
+ lpfc_vpd_t *vp;
+ struct serv_parm *hsp;
+ int len;
+
+ vp = &phba->vpd;
+
+ CtReq->CommandResponse.bits.CmdRsp =
+ be16_to_cpu(SLI_MGMT_RPA);
+ CtReq->CommandResponse.bits.Size = 0;
+ pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID;
+ size = sizeof (struct lpfc_name) + FOURBYTES;
+ memcpy((uint8_t *) & pab->PortName,
+ (uint8_t *) & phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ pab->ab.EntryCnt = 0;
+
+ /* #1 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+ ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32);
+ ae->un.SupportFC4Types[2] = 1;
+ ae->un.SupportFC4Types[7] = 1;
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + 32;
+
+ /* #2 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+ ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+ if (FC_JEDEC_ID(vp->rev.biuRev) == VIPER_JEDEC_ID)
+ ae->un.SupportSpeed = HBA_PORTSPEED_10GBIT;
+ else if (FC_JEDEC_ID(vp->rev.biuRev) == HELIOS_JEDEC_ID)
+ ae->un.SupportSpeed = HBA_PORTSPEED_4GBIT;
+ else if ((FC_JEDEC_ID(vp->rev.biuRev) ==
+ CENTAUR_2G_JEDEC_ID)
+ || (FC_JEDEC_ID(vp->rev.biuRev) ==
+ PEGASUS_JEDEC_ID)
+ || (FC_JEDEC_ID(vp->rev.biuRev) ==
+ THOR_JEDEC_ID))
+ ae->un.SupportSpeed = HBA_PORTSPEED_2GBIT;
+ else
+ ae->un.SupportSpeed = HBA_PORTSPEED_1GBIT;
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + 4;
+
+ /* #3 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+ ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+ switch(phba->fc_linkspeed) {
+ case LA_1GHZ_LINK:
+ ae->un.PortSpeed = HBA_PORTSPEED_1GBIT;
+ break;
+ case LA_2GHZ_LINK:
+ ae->un.PortSpeed = HBA_PORTSPEED_2GBIT;
+ break;
+ case LA_4GHZ_LINK:
+ ae->un.PortSpeed = HBA_PORTSPEED_4GBIT;
+ break;
+ default:
+ ae->un.PortSpeed =
+ HBA_PORTSPEED_UNKNOWN;
+ break;
+ }
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + 4;
+
+ /* #4 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+ ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE);
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+ hsp = (struct serv_parm *) & phba->fc_sparam;
+ ae->un.MaxFrameSize =
+ (((uint32_t) hsp->cmn.
+ bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn.
+ bbRcvSizeLsb;
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + 4;
+
+ /* #5 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+ ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME);
+ strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME);
+ len = strlen((char *)ae->un.OsDeviceName);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + len;
+
+ if (phba->cfg_fdmi_on == 2) {
+ /* #6 Port attribute entry */
+ ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab +
+ size);
+ ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME);
+ sprintf(ae->un.HostName, "%s",
+ system_utsname.nodename);
+ len = strlen(ae->un.HostName);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ ae->ad.bits.AttrLen =
+ be16_to_cpu(FOURBYTES + len);
+ pab->ab.EntryCnt++;
+ size += FOURBYTES + len;
+ }
+
+ pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt);
+ /* Total size */
+ size = GID_REQUEST_SZ - 4 + size;
+ }
+ break;
+
+ case SLI_MGMT_DHBA:
+ CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA);
+ CtReq->CommandResponse.bits.Size = 0;
+ pe = (PORT_ENTRY *) & CtReq->un.PortID;
+ memcpy((uint8_t *) & pe->PortName,
+ (uint8_t *) & phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+ break;
+
+ case SLI_MGMT_DPRT:
+ CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT);
+ CtReq->CommandResponse.bits.Size = 0;
+ pe = (PORT_ENTRY *) & CtReq->un.PortID;
+ memcpy((uint8_t *) & pe->PortName,
+ (uint8_t *) & phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+ break;
+ }
+
+ bpl = (struct ulp_bde64 *) bmp->virt;
+ bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+ bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.f.bdeSize = size;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ cmpl = lpfc_cmpl_ct_cmd_fdmi;
+
+ if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP))
+ return 0;
+
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+fdmi_cmd_free_bmp:
+ kfree(bmp);
+fdmi_cmd_free_mpvirt:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+fdmi_cmd_free_mp:
+ kfree(mp);
+fdmi_cmd_exit:
+ /* Issue FDMI request failed */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0244 Issue FDMI request failed Data: x%x\n",
+ phba->brd_no,
+ cmdcode);
+ return 1;
+}
+
+void
+lpfc_fdmi_tmo(unsigned long ptr)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+ unsigned long iflag;
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ if (!(phba->work_hba_events & WORKER_FDMI_TMO)) {
+ phba->work_hba_events |= WORKER_FDMI_TMO;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ }
+ spin_unlock_irqrestore(phba->host->host_lock,iflag);
+}
+
+void
+lpfc_fdmi_tmo_handler(struct lpfc_hba *phba)
+{
+ struct lpfc_nodelist *ndlp;
+
+ spin_lock_irq(phba->host->host_lock);
+ if (!(phba->work_hba_events & WORKER_FDMI_TMO)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+ }
+ ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID);
+ if (ndlp) {
+ if (system_utsname.nodename[0] != '\0') {
+ lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA);
+ } else {
+ mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60);
+ }
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+
+
+void
+lpfc_decode_firmware_rev(struct lpfc_hba * phba, char *fwrevision, int flag)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ lpfc_vpd_t *vp = &phba->vpd;
+ uint32_t b1, b2, b3, b4, i, rev;
+ char c;
+ uint32_t *ptr, str[4];
+ uint8_t *fwname;
+
+ if (vp->rev.rBit) {
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+ rev = vp->rev.sli2FwRev;
+ else
+ rev = vp->rev.sli1FwRev;
+
+ b1 = (rev & 0x0000f000) >> 12;
+ b2 = (rev & 0x00000f00) >> 8;
+ b3 = (rev & 0x000000c0) >> 6;
+ b4 = (rev & 0x00000030) >> 4;
+
+ switch (b4) {
+ case 0:
+ c = 'N';
+ break;
+ case 1:
+ c = 'A';
+ break;
+ case 2:
+ c = 'B';
+ break;
+ default:
+ c = 0;
+ break;
+ }
+ b4 = (rev & 0x0000000f);
+
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+ fwname = vp->rev.sli2FwName;
+ else
+ fwname = vp->rev.sli1FwName;
+
+ for (i = 0; i < 16; i++)
+ if (fwname[i] == 0x20)
+ fwname[i] = 0;
+
+ ptr = (uint32_t*)fwname;
+
+ for (i = 0; i < 3; i++)
+ str[i] = be32_to_cpu(*ptr++);
+
+ if (c == 0) {
+ if (flag)
+ sprintf(fwrevision, "%d.%d%d (%s)",
+ b1, b2, b3, (char *)str);
+ else
+ sprintf(fwrevision, "%d.%d%d", b1,
+ b2, b3);
+ } else {
+ if (flag)
+ sprintf(fwrevision, "%d.%d%d%c%d (%s)",
+ b1, b2, b3, c,
+ b4, (char *)str);
+ else
+ sprintf(fwrevision, "%d.%d%d%c%d",
+ b1, b2, b3, c, b4);
+ }
+ } else {
+ rev = vp->rev.smFwRev;
+
+ b1 = (rev & 0xff000000) >> 24;
+ b2 = (rev & 0x00f00000) >> 20;
+ b3 = (rev & 0x000f0000) >> 16;
+ c = (rev & 0x0000ff00) >> 8;
+ b4 = (rev & 0x000000ff);
+
+ if (flag)
+ sprintf(fwrevision, "%d.%d%d%c%d ", b1,
+ b2, b3, c, b4);
+ else
+ sprintf(fwrevision, "%d.%d%d%c%d ", b1,
+ b2, b3, c, b4);
+ }
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
new file mode 100644
index 000000000000..adccc99510d5
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -0,0 +1,206 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_disc.h 1.61 2005/04/07 08:46:52EDT sf_support Exp $
+ */
+
+#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */
+#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */
+#define FC_MAXLOOP 126 /* max devices supported on a fc loop */
+#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */
+
+
+/* This is the protocol dependent definition for a Node List Entry.
+ * This is used by Fibre Channel protocol to support FCP.
+ */
+
+/* structure used to queue event to the discovery tasklet */
+struct lpfc_work_evt {
+ struct list_head evt_listp;
+ void * evt_arg1;
+ void * evt_arg2;
+ uint32_t evt;
+};
+
+#define LPFC_EVT_NODEV_TMO 0x1
+#define LPFC_EVT_ONLINE 0x2
+#define LPFC_EVT_OFFLINE 0x3
+#define LPFC_EVT_ELS_RETRY 0x4
+
+struct lpfc_nodelist {
+ struct list_head nlp_listp;
+ struct lpfc_name nlp_portname; /* port name */
+ struct lpfc_name nlp_nodename; /* node name */
+ uint32_t nlp_flag; /* entry flags */
+ uint32_t nlp_DID; /* FC D_ID of entry */
+ uint32_t nlp_last_elscmd; /* Last ELS cmd sent */
+ uint16_t nlp_type;
+#define NLP_FC_NODE 0x1 /* entry is an FC node */
+#define NLP_FABRIC 0x4 /* entry rep a Fabric entity */
+#define NLP_FCP_TARGET 0x8 /* entry is an FCP target */
+#define NLP_FCP_INITIATOR 0x10 /* entry is an FCP Initiator */
+
+ uint16_t nlp_rpi;
+ uint16_t nlp_state; /* state transition indicator */
+ uint16_t nlp_xri; /* output exchange id for RPI */
+ uint16_t nlp_sid; /* scsi id */
+#define NLP_NO_SID 0xffff
+ uint16_t nlp_maxframe; /* Max RCV frame size */
+ uint8_t nlp_class_sup; /* Supported Classes */
+ uint8_t nlp_retry; /* used for ELS retries */
+ uint8_t nlp_disc_refcnt; /* used for DSM */
+ uint8_t nlp_fcp_info; /* class info, bits 0-3 */
+#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */
+
+ struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */
+ struct timer_list nlp_tmofunc; /* Used for nodev tmo */
+ struct fc_rport *rport; /* Corresponding FC transport
+ port structure */
+ struct lpfc_nodelist *nlp_rpi_hash_next;
+ struct lpfc_hba *nlp_phba;
+ struct lpfc_work_evt nodev_timeout_evt;
+ struct lpfc_work_evt els_retry_evt;
+};
+
+/* Defines for nlp_flag (uint32) */
+#define NLP_NO_LIST 0x0 /* Indicates immediately free node */
+#define NLP_UNUSED_LIST 0x1 /* Flg to indicate node will be freed */
+#define NLP_PLOGI_LIST 0x2 /* Flg to indicate sent PLOGI */
+#define NLP_ADISC_LIST 0x3 /* Flg to indicate sent ADISC */
+#define NLP_REGLOGIN_LIST 0x4 /* Flg to indicate sent REG_LOGIN */
+#define NLP_PRLI_LIST 0x5 /* Flg to indicate sent PRLI */
+#define NLP_UNMAPPED_LIST 0x6 /* Node is now unmapped */
+#define NLP_MAPPED_LIST 0x7 /* Node is now mapped */
+#define NLP_NPR_LIST 0x8 /* Node is in NPort Recovery state */
+#define NLP_JUST_DQ 0x9 /* just deque ndlp in lpfc_nlp_list */
+#define NLP_LIST_MASK 0xf /* mask to see what list node is on */
+#define NLP_PLOGI_SND 0x20 /* sent PLOGI request for this entry */
+#define NLP_PRLI_SND 0x40 /* sent PRLI request for this entry */
+#define NLP_ADISC_SND 0x80 /* sent ADISC request for this entry */
+#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */
+#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */
+#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */
+#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */
+#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */
+#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */
+#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */
+#define NLP_LOGO_ACC 0x100000 /* Process LOGO after ACC completes */
+#define NLP_TGT_NO_SCSIID 0x200000 /* good PRLI but no binding for scsid */
+#define NLP_ACC_REGLOGIN 0x1000000 /* Issue Reg Login after successful
+ ACC */
+#define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from
+ NPR list */
+#define NLP_DELAY_REMOVE 0x4000000 /* Defer removal till end of DSM */
+
+/* Defines for list searchs */
+#define NLP_SEARCH_MAPPED 0x1 /* search mapped */
+#define NLP_SEARCH_UNMAPPED 0x2 /* search unmapped */
+#define NLP_SEARCH_PLOGI 0x4 /* search plogi */
+#define NLP_SEARCH_ADISC 0x8 /* search adisc */
+#define NLP_SEARCH_REGLOGIN 0x10 /* search reglogin */
+#define NLP_SEARCH_PRLI 0x20 /* search prli */
+#define NLP_SEARCH_NPR 0x40 /* search npr */
+#define NLP_SEARCH_UNUSED 0x80 /* search mapped */
+#define NLP_SEARCH_ALL 0xff /* search all lists */
+
+/* There are 4 different double linked lists nodelist entries can reside on.
+ * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used
+ * when Link Up discovery or Registered State Change Notification (RSCN)
+ * processing is needed. Each list holds the nodes that require a PLOGI or
+ * ADISC Extended Link Service (ELS) request. These lists keep track of the
+ * nodes affected by an RSCN, or a Link Up (Typically, all nodes are effected
+ * by Link Up) event. The unmapped_list contains all nodes that have
+ * successfully logged into at the Fibre Channel level. The
+ * mapped_list will contain all nodes that are mapped FCP targets.
+ *
+ * The bind list is a list of undiscovered (potentially non-existent) nodes
+ * that we have saved binding information on. This information is used when
+ * nodes transition from the unmapped to the mapped list.
+ */
+
+/* Defines for nlp_state */
+#define NLP_STE_UNUSED_NODE 0x0 /* node is just allocated */
+#define NLP_STE_PLOGI_ISSUE 0x1 /* PLOGI was sent to NL_PORT */
+#define NLP_STE_ADISC_ISSUE 0x2 /* ADISC was sent to NL_PORT */
+#define NLP_STE_REG_LOGIN_ISSUE 0x3 /* REG_LOGIN was issued for NL_PORT */
+#define NLP_STE_PRLI_ISSUE 0x4 /* PRLI was sent to NL_PORT */
+#define NLP_STE_UNMAPPED_NODE 0x5 /* PRLI completed from NL_PORT */
+#define NLP_STE_MAPPED_NODE 0x6 /* Identified as a FCP Target */
+#define NLP_STE_NPR_NODE 0x7 /* NPort disappeared */
+#define NLP_STE_MAX_STATE 0x8
+#define NLP_STE_FREED_NODE 0xff /* node entry was freed to MEM_NLP */
+
+/* For UNUSED_NODE state, the node has just been allocated.
+ * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on
+ * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list
+ * and put on the unmapped list. For ADISC processing, the node is taken off
+ * the ADISC list and placed on either the mapped or unmapped list (depending
+ * on its previous state). Once on the unmapped list, a PRLI is issued and the
+ * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is
+ * changed to PRLI_COMPL. If the completion indicates a mapped
+ * node, the node is taken off the unmapped list. The binding list is checked
+ * for a valid binding, or a binding is automatically assigned. If binding
+ * assignment is unsuccessful, the node is left on the unmapped list. If
+ * binding assignment is successful, the associated binding list entry (if
+ * any) is removed, and the node is placed on the mapped list.
+ */
+/*
+ * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
+ * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
+ * expire, all effected nodes will receive a DEVICE_RM event.
+ */
+/*
+ * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists
+ * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap
+ * check, additional nodes may be added (DEVICE_ADD) or removed (DEVICE_RM) to /
+ * from the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated,
+ * we will first process the ADISC list. 32 entries are processed initially and
+ * ADISC is initited for each one. Completions / Events for each node are
+ * funnelled thru the state machine. As each node finishes ADISC processing, it
+ * starts ADISC for any nodes waiting for ADISC processing. If no nodes are
+ * waiting, and the ADISC list count is identically 0, then we are done. For
+ * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we
+ * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI
+ * list. 32 entries are processed initially and PLOGI is initited for each one.
+ * Completions / Events for each node are funnelled thru the state machine. As
+ * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting
+ * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is
+ * identically 0, then we are done. We have now completed discovery / RSCN
+ * handling. Upon completion, ALL nodes should be on either the mapped or
+ * unmapped lists.
+ */
+
+/* Defines for Node List Entry Events that could happen */
+#define NLP_EVT_RCV_PLOGI 0x0 /* Rcv'd an ELS PLOGI command */
+#define NLP_EVT_RCV_PRLI 0x1 /* Rcv'd an ELS PRLI command */
+#define NLP_EVT_RCV_LOGO 0x2 /* Rcv'd an ELS LOGO command */
+#define NLP_EVT_RCV_ADISC 0x3 /* Rcv'd an ELS ADISC command */
+#define NLP_EVT_RCV_PDISC 0x4 /* Rcv'd an ELS PDISC command */
+#define NLP_EVT_RCV_PRLO 0x5 /* Rcv'd an ELS PRLO command */
+#define NLP_EVT_CMPL_PLOGI 0x6 /* Sent an ELS PLOGI command */
+#define NLP_EVT_CMPL_PRLI 0x7 /* Sent an ELS PRLI command */
+#define NLP_EVT_CMPL_LOGO 0x8 /* Sent an ELS LOGO command */
+#define NLP_EVT_CMPL_ADISC 0x9 /* Sent an ELS ADISC command */
+#define NLP_EVT_CMPL_REG_LOGIN 0xa /* REG_LOGIN mbox cmd completed */
+#define NLP_EVT_DEVICE_RM 0xb /* Device not found in NS / ALPAmap */
+#define NLP_EVT_DEVICE_RECOVERY 0xc /* Device existence unknown */
+#define NLP_EVT_MAX_EVENT 0xd
+
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
new file mode 100644
index 000000000000..68d1b77e0256
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -0,0 +1,3258 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_els.c 1.186 2005/04/13 14:26:55EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+
+static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *);
+static int lpfc_max_els_tries = 3;
+
+static int
+lpfc_els_chk_latt(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli;
+ LPFC_MBOXQ_t *mbox;
+ uint32_t ha_copy;
+ int rc;
+
+ psli = &phba->sli;
+
+ if ((phba->hba_state >= LPFC_HBA_READY) ||
+ (phba->hba_state == LPFC_LINK_DOWN))
+ return 0;
+
+ /* Read the HBA Host Attention Register */
+ spin_lock_irq(phba->host->host_lock);
+ ha_copy = readl(phba->HAregaddr);
+ spin_unlock_irq(phba->host->host_lock);
+
+ if (!(ha_copy & HA_LATT))
+ return 0;
+
+ /* Pending Link Event during Discovery */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_DISCOVERY,
+ "%d:0237 Pending Link Event during "
+ "Discovery: State x%x\n",
+ phba->brd_no, phba->hba_state);
+
+ /* CLEAR_LA should re-enable link attention events and
+ * we should then imediately take a LATT event. The
+ * LATT processing should call lpfc_linkdown() which
+ * will cleanup any left over in-progress discovery
+ * events.
+ */
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_ABORT_DISCOVERY;
+ spin_unlock_irq(phba->host->host_lock);
+
+ if (phba->hba_state != LPFC_CLEAR_LA) {
+ if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
+ phba->hba_state = LPFC_CLEAR_LA;
+ lpfc_clear_la(phba, mbox);
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ rc = lpfc_sli_issue_mbox (phba, mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ phba->hba_state = LPFC_HBA_ERROR;
+ }
+ }
+ }
+
+ return (1);
+
+}
+
+static struct lpfc_iocbq *
+lpfc_prep_els_iocb(struct lpfc_hba * phba,
+ uint8_t expectRsp,
+ uint16_t cmdSize,
+ uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd)
+{
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *elsiocb = NULL;
+ struct lpfc_dmabuf *pcmd, *prsp, *pbuflist;
+ struct ulp_bde64 *bpl;
+ IOCB_t *icmd;
+
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+
+ if (phba->hba_state < LPFC_LINK_UP)
+ return NULL;
+
+
+ /* Allocate buffer for command iocb */
+ spin_lock_irq(phba->host->host_lock);
+ list_remove_head(lpfc_iocb_list, elsiocb, struct lpfc_iocbq, list);
+ spin_unlock_irq(phba->host->host_lock);
+
+ if (elsiocb == NULL)
+ return NULL;
+ memset(elsiocb, 0, sizeof (struct lpfc_iocbq));
+ icmd = &elsiocb->iocb;
+
+ /* fill in BDEs for command */
+ /* Allocate buffer for command payload */
+ if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL)) == 0) ||
+ ((pcmd->virt = lpfc_mbuf_alloc(phba,
+ MEM_PRI, &(pcmd->phys))) == 0)) {
+ if (pcmd)
+ kfree(pcmd);
+
+ list_add_tail(&elsiocb->list, lpfc_iocb_list);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&pcmd->list);
+
+ /* Allocate buffer for response payload */
+ if (expectRsp) {
+ prsp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (prsp)
+ prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+ &prsp->phys);
+ if (prsp == 0 || prsp->virt == 0) {
+ if (prsp)
+ kfree(prsp);
+ lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+ kfree(pcmd);
+ list_add_tail(&elsiocb->list, lpfc_iocb_list);
+ return NULL;
+ }
+ INIT_LIST_HEAD(&prsp->list);
+ } else {
+ prsp = NULL;
+ }
+
+ /* Allocate buffer for Buffer ptr list */
+ pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (pbuflist)
+ pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+ &pbuflist->phys);
+ if (pbuflist == 0 || pbuflist->virt == 0) {
+ list_add_tail(&elsiocb->list, lpfc_iocb_list);
+ lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+ lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+ kfree(pcmd);
+ kfree(prsp);
+ if (pbuflist)
+ kfree(pbuflist);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&pbuflist->list);
+
+ icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys);
+ icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys);
+ icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL;
+ if (expectRsp) {
+ icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64));
+ icmd->un.elsreq64.remoteID = ndlp->nlp_DID; /* DID */
+ icmd->ulpCommand = CMD_ELS_REQUEST64_CR;
+ } else {
+ icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64);
+ icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX;
+ }
+
+ icmd->ulpBdeCount = 1;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+
+ bpl = (struct ulp_bde64 *) pbuflist->virt;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys));
+ bpl->tus.f.bdeSize = cmdSize;
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ if (expectRsp) {
+ bpl++;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys));
+ bpl->tus.f.bdeSize = FCELSSIZE;
+ bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ }
+
+ /* Save for completion so we can release these resources */
+ elsiocb->context1 = (uint8_t *) ndlp;
+ elsiocb->context2 = (uint8_t *) pcmd;
+ elsiocb->context3 = (uint8_t *) pbuflist;
+ elsiocb->retry = retry;
+ elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT;
+
+ if (prsp) {
+ list_add(&prsp->list, &pcmd->list);
+ }
+
+ if (expectRsp) {
+ /* Xmit ELS command <elsCmd> to remote NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0116 Xmit ELS command x%x to remote "
+ "NPORT x%x Data: x%x x%x\n",
+ phba->brd_no, elscmd,
+ ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state);
+ } else {
+ /* Xmit ELS response <elsCmd> to remote NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0117 Xmit ELS response x%x to remote "
+ "NPORT x%x Data: x%x x%x\n",
+ phba->brd_no, elscmd,
+ ndlp->nlp_DID, icmd->ulpIoTag, cmdSize);
+ }
+
+ return (elsiocb);
+}
+
+
+static int
+lpfc_cmpl_els_flogi_fabric(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct serv_parm *sp, IOCB_t *irsp)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_FABRIC;
+ spin_unlock_irq(phba->host->host_lock);
+
+ phba->fc_edtov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) /* E_D_TOV ticks are in nanoseconds */
+ phba->fc_edtov = (phba->fc_edtov + 999999) / 1000000;
+
+ phba->fc_ratov = (be32_to_cpu(sp->cmn.w2.r_a_tov) + 999) / 1000;
+
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_PUBLIC_LOOP;
+ spin_unlock_irq(phba->host->host_lock);
+ } else {
+ /*
+ * If we are a N-port connected to a Fabric, fixup sparam's so
+ * logins to devices on remote loops work.
+ */
+ phba->fc_sparam.cmn.altBbCredit = 1;
+ }
+
+ phba->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
+ memcpy(&ndlp->nlp_portname, &sp->portName, sizeof(struct lpfc_name));
+ memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name));
+ ndlp->nlp_class_sup = 0;
+ if (sp->cls1.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS1;
+ if (sp->cls2.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS2;
+ if (sp->cls3.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS3;
+ if (sp->cls4.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS4;
+ ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
+ sp->cmn.bbRcvSizeLsb;
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ goto fail;
+
+ phba->hba_state = LPFC_FABRIC_CFG_LINK;
+ lpfc_config_link(phba, mbox);
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT | MBX_STOP_IOCB);
+ if (rc == MBX_NOT_FINISHED)
+ goto fail_free_mbox;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ goto fail;
+
+ if (lpfc_reg_login(phba, Fabric_DID, (uint8_t *) sp, mbox, 0))
+ goto fail_free_mbox;
+
+ /*
+ * set_slim mailbox command needs to execute first,
+ * queue this command to be processed later.
+ */
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_fabric_reg_login;
+ mbox->context2 = ndlp;
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT | MBX_STOP_IOCB);
+ if (rc == MBX_NOT_FINISHED)
+ goto fail_free_mbox;
+
+ return 0;
+
+ fail_free_mbox:
+ mempool_free(mbox, phba->mbox_mem_pool);
+ fail:
+ return -ENXIO;
+}
+
+/*
+ * We FLOGIed into an NPort, initiate pt2pt protocol
+ */
+static int
+lpfc_cmpl_els_flogi_nport(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct serv_parm *sp)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ spin_unlock_irq(phba->host->host_lock);
+
+ phba->fc_edtov = FF_DEF_EDTOV;
+ phba->fc_ratov = FF_DEF_RATOV;
+ rc = memcmp(&phba->fc_portname, &sp->portName,
+ sizeof(struct lpfc_name));
+ if (rc >= 0) {
+ /* This side will initiate the PLOGI */
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_PT2PT_PLOGI;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /*
+ * N_Port ID cannot be 0, set our to LocalID the other
+ * side will be RemoteID.
+ */
+
+ /* not equal */
+ if (rc)
+ phba->fc_myDID = PT2PT_LocalID;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ goto fail;
+
+ lpfc_config_link(phba, mbox);
+
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, mbox,
+ MBX_NOWAIT | MBX_STOP_IOCB);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto fail;
+ }
+ mempool_free(ndlp, phba->nlp_mem_pool);
+
+ ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, PT2PT_RemoteID);
+ if (!ndlp) {
+ /*
+ * Cannot find existing Fabric ndlp, so allocate a
+ * new one
+ */
+ ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ if (!ndlp)
+ goto fail;
+
+ lpfc_nlp_init(phba, ndlp, PT2PT_RemoteID);
+ }
+
+ memcpy(&ndlp->nlp_portname, &sp->portName,
+ sizeof(struct lpfc_name));
+ memcpy(&ndlp->nlp_nodename, &sp->nodeName,
+ sizeof(struct lpfc_name));
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ } else {
+ /* This side will wait for the PLOGI */
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_PT2PT;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Start discovery - this should just do CLEAR_LA */
+ lpfc_disc_start(phba);
+ return 0;
+ fail:
+ return -ENXIO;
+}
+
+static void
+lpfc_cmpl_els_flogi(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp = &rspiocb->iocb;
+ struct lpfc_nodelist *ndlp = cmdiocb->context1;
+ struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp;
+ struct serv_parm *sp;
+ int rc;
+
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba)) {
+ lpfc_nlp_remove(phba, ndlp);
+ goto out;
+ }
+
+ if (irsp->ulpStatus) {
+ /* Check for retry */
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* ELS command is being retried */
+ goto out;
+ }
+ /* FLOGI failed, so there is no fabric */
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* If private loop, then allow max outstandting els to be
+ * LPFC_MAX_DISC_THREADS (32). Scanning in the case of no
+ * alpa map would take too long otherwise.
+ */
+ if (phba->alpa_map[0] == 0) {
+ phba->cfg_discovery_threads =
+ LPFC_MAX_DISC_THREADS;
+ }
+
+ /* FLOGI failure */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_ELS,
+ "%d:0100 FLOGI failure Data: x%x x%x\n",
+ phba->brd_no,
+ irsp->ulpStatus, irsp->un.ulpWord[4]);
+ goto flogifail;
+ }
+
+ /*
+ * The FLogI succeeded. Sync the data for the CPU before
+ * accessing it.
+ */
+ prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+
+ sp = prsp->virt + sizeof(uint32_t);
+
+ /* FLOGI completes successfully */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0101 FLOGI completes sucessfully "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ irsp->un.ulpWord[4], sp->cmn.e_d_tov,
+ sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
+
+ if (phba->hba_state == LPFC_FLOGI) {
+ /*
+ * If Common Service Parameters indicate Nport
+ * we are point to point, if Fport we are Fabric.
+ */
+ if (sp->cmn.fPort)
+ rc = lpfc_cmpl_els_flogi_fabric(phba, ndlp, sp, irsp);
+ else
+ rc = lpfc_cmpl_els_flogi_nport(phba, ndlp, sp);
+
+ if (!rc)
+ goto out;
+ }
+
+flogifail:
+ lpfc_nlp_remove(phba, ndlp);
+
+ if (irsp->ulpStatus != IOSTAT_LOCAL_REJECT ||
+ (irsp->un.ulpWord[4] != IOERR_SLI_ABORTED &&
+ irsp->un.ulpWord[4] != IOERR_SLI_DOWN)) {
+ /* FLOGI failed, so just use loop map to make discovery list */
+ lpfc_disc_list_loopmap(phba);
+
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ }
+
+out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+}
+
+static int
+lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint8_t retry)
+{
+ struct serv_parm *sp;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ uint32_t tmo;
+ int rc;
+
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+
+ cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm));
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_FLOGI)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ /* For FLOGI request, remainder of payload is service parameters */
+ *((uint32_t *) (pcmd)) = ELS_CMD_FLOGI;
+ pcmd += sizeof (uint32_t);
+ memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+ sp = (struct serv_parm *) pcmd;
+
+ /* Setup CSPs accordingly for Fabric */
+ sp->cmn.e_d_tov = 0;
+ sp->cmn.w2.r_a_tov = 0;
+ sp->cls1.classValid = 0;
+ sp->cls2.seqDelivery = 1;
+ sp->cls3.seqDelivery = 1;
+ if (sp->cmn.fcphLow < FC_PH3)
+ sp->cmn.fcphLow = FC_PH3;
+ if (sp->cmn.fcphHigh < FC_PH3)
+ sp->cmn.fcphHigh = FC_PH3;
+
+ tmo = phba->fc_ratov;
+ phba->fc_ratov = LPFC_DISC_FLOGI_TMO;
+ lpfc_set_disctmo(phba);
+ phba->fc_ratov = tmo;
+
+ phba->fc_stat.elsXmitFLOGI++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi;
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+int
+lpfc_els_abort_flogi(struct lpfc_hba * phba)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ struct lpfc_nodelist *ndlp;
+ IOCB_t *icmd;
+
+ /* Abort outstanding I/O on NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0201 Abort outstanding I/O on NPort x%x\n",
+ phba->brd_no, Fabric_DID);
+
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+
+ /*
+ * Check the txcmplq for an iocb that matches the nport the driver is
+ * searching for.
+ */
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ icmd = &iocb->iocb;
+ if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) {
+ ndlp = (struct lpfc_nodelist *)(iocb->context1);
+ if (ndlp && (ndlp->nlp_DID == Fabric_DID)) {
+ list_del(&iocb->list);
+ pring->txcmplq_cnt--;
+
+ if ((icmd->un.elsreq64.bdl.ulpIoTag32)) {
+ lpfc_sli_issue_abort_iotag32
+ (phba, pring, iocb);
+ }
+ if (iocb->iocb_cmpl) {
+ icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] =
+ IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&iocb->list,
+ &phba->lpfc_iocb_list);
+ }
+ }
+ }
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ return 0;
+}
+
+int
+lpfc_initial_flogi(struct lpfc_hba * phba)
+{
+ struct lpfc_nodelist *ndlp;
+
+ /* First look for Fabric ndlp on the unmapped list */
+
+ if ((ndlp =
+ lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+ Fabric_DID)) == 0) {
+ /* Cannot find existing Fabric ndlp, so allocate a new one */
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL))
+ == 0) {
+ return (0);
+ }
+ lpfc_nlp_init(phba, ndlp, Fabric_DID);
+ }
+ else {
+ phba->fc_unmap_cnt--;
+ list_del(&ndlp->nlp_listp);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_LIST_MASK;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ if (lpfc_issue_els_flogi(phba, ndlp, 0)) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+ return (1);
+}
+
+static void
+lpfc_more_plogi(struct lpfc_hba * phba)
+{
+ int sentplogi;
+
+ if (phba->num_disc_nodes)
+ phba->num_disc_nodes--;
+
+ /* Continue discovery with <num_disc_nodes> PLOGIs to go */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0232 Continue discovery with %d PLOGIs to go "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->num_disc_nodes, phba->fc_plogi_cnt,
+ phba->fc_flag, phba->hba_state);
+
+ /* Check to see if there are more PLOGIs to be sent */
+ if (phba->fc_flag & FC_NLP_MORE) {
+ /* go thru NPR list and issue any remaining ELS PLOGIs */
+ sentplogi = lpfc_els_disc_plogi(phba);
+ }
+ return;
+}
+
+static void
+lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+ int disc, rc, did, type;
+
+ psli = &phba->sli;
+
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ irsp = &rspiocb->iocb;
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_PLOGI_SND;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Since ndlp can be freed in the disc state machine, note if this node
+ * is being used during discovery.
+ */
+ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+ rc = 0;
+
+ /* PLOGI completes to NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0102 PLOGI completes to NPort x%x "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+ irsp->un.ulpWord[4], disc, phba->num_disc_nodes);
+
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba)) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ goto out;
+ }
+
+ /* ndlp could be freed in DSM, save these values now */
+ type = ndlp->nlp_type;
+ did = ndlp->nlp_DID;
+
+ if (irsp->ulpStatus) {
+ /* Check for retry */
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* ELS command is being retried */
+ if (disc) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ goto out;
+ }
+
+ /* PLOGI failed */
+ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+ (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+ }
+ else {
+ rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_PLOGI);
+ }
+ } else {
+ /* Good status, call state machine */
+ rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_PLOGI);
+ }
+
+ if (type & NLP_FABRIC) {
+ /* If we cannot login to Nameserver, kick off discovery now */
+ if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) {
+ lpfc_disc_start(phba);
+ }
+ goto out;
+ }
+
+ if (disc && phba->num_disc_nodes) {
+ /* Check to see if there are more PLOGIs to be sent */
+ lpfc_more_plogi(phba);
+ }
+
+ if (rc != NLP_STE_FREED_NODE) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+
+ if (phba->num_disc_nodes == 0) {
+ if(disc) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_NDISC_ACTIVE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ lpfc_can_disctmo(phba);
+ if (phba->fc_flag & FC_RSCN_MODE) {
+ /* Check to see if more RSCNs came in while we were
+ * processing this one.
+ */
+ if ((phba->fc_rscn_id_cnt == 0) &&
+ (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_RSCN_MODE;
+ spin_unlock_irq(phba->host->host_lock);
+ } else {
+ lpfc_els_handle_rscn(phba);
+ }
+ }
+ }
+
+out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint8_t retry)
+{
+ struct serv_parm *sp;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm));
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_PLOGI)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ /* For PLOGI request, remainder of payload is service parameters */
+ *((uint32_t *) (pcmd)) = ELS_CMD_PLOGI;
+ pcmd += sizeof (uint32_t);
+ memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+ sp = (struct serv_parm *) pcmd;
+
+ if (sp->cmn.fcphLow < FC_PH_4_3)
+ sp->cmn.fcphLow = FC_PH_4_3;
+
+ if (sp->cmn.fcphHigh < FC_PH3)
+ sp->cmn.fcphHigh = FC_PH3;
+
+ phba->fc_stat.elsXmitPLOGI++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_PLOGI_SND;
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ ndlp->nlp_flag &= ~NLP_PLOGI_SND;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return (0);
+}
+
+static void
+lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ irsp = &(rspiocb->iocb);
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_PRLI_SND;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* PRLI completes to NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0103 PRLI completes to NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+ irsp->un.ulpWord[4], phba->num_disc_nodes);
+
+ phba->fc_prli_sent--;
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba))
+ goto out;
+
+ if (irsp->ulpStatus) {
+ /* Check for retry */
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* ELS command is being retried */
+ goto out;
+ }
+ /* PRLI failed */
+ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+ (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+ goto out;
+ }
+ else {
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_PRLI);
+ }
+ } else {
+ /* Good status, call state machine */
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI);
+ }
+
+out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint8_t retry)
+{
+ PRLI *npr;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = (sizeof (uint32_t) + sizeof (PRLI));
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_PRLI)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ /* For PRLI request, remainder of payload is service parameters */
+ memset(pcmd, 0, (sizeof (PRLI) + sizeof (uint32_t)));
+ *((uint32_t *) (pcmd)) = ELS_CMD_PRLI;
+ pcmd += sizeof (uint32_t);
+
+ /* For PRLI, remainder of payload is PRLI parameter page */
+ npr = (PRLI *) pcmd;
+ /*
+ * If our firmware version is 3.20 or later,
+ * set the following bits for FC-TAPE support.
+ */
+ if (phba->vpd.rev.feaLevelHigh >= 0x02) {
+ npr->ConfmComplAllowed = 1;
+ npr->Retry = 1;
+ npr->TaskRetryIdReq = 1;
+ }
+ npr->estabImagePair = 1;
+ npr->readXferRdyDis = 1;
+
+ /* For FCP support */
+ npr->prliType = PRLI_FCP_TYPE;
+ npr->initiatorFunc = 1;
+
+ phba->fc_stat.elsXmitPRLI++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_prli;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_PRLI_SND;
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ ndlp->nlp_flag &= ~NLP_PRLI_SND;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ phba->fc_prli_sent++;
+ return (0);
+}
+
+static void
+lpfc_more_adisc(struct lpfc_hba * phba)
+{
+ int sentadisc;
+
+ if (phba->num_disc_nodes)
+ phba->num_disc_nodes--;
+
+ /* Continue discovery with <num_disc_nodes> ADISCs to go */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0210 Continue discovery with %d ADISCs to go "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->num_disc_nodes, phba->fc_adisc_cnt,
+ phba->fc_flag, phba->hba_state);
+
+ /* Check to see if there are more ADISCs to be sent */
+ if (phba->fc_flag & FC_NLP_MORE) {
+ lpfc_set_disctmo(phba);
+
+ /* go thru NPR list and issue any remaining ELS ADISCs */
+ sentadisc = lpfc_els_disc_adisc(phba);
+ }
+ return;
+}
+
+static void
+lpfc_rscn_disc(struct lpfc_hba * phba)
+{
+ /* RSCN discovery */
+ /* go thru NPR list and issue ELS PLOGIs */
+ if (phba->fc_npr_cnt) {
+ if (lpfc_els_disc_plogi(phba))
+ return;
+ }
+ if (phba->fc_flag & FC_RSCN_MODE) {
+ /* Check to see if more RSCNs came in while we were
+ * processing this one.
+ */
+ if ((phba->fc_rscn_id_cnt == 0) &&
+ (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_RSCN_MODE;
+ spin_unlock_irq(phba->host->host_lock);
+ } else {
+ lpfc_els_handle_rscn(phba);
+ }
+ }
+}
+
+static void
+lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+ LPFC_MBOXQ_t *mbox;
+ int disc, rc;
+
+ psli = &phba->sli;
+
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ irsp = &(rspiocb->iocb);
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_ADISC_SND;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Since ndlp can be freed in the disc state machine, note if this node
+ * is being used during discovery.
+ */
+ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+
+ /* ADISC completes to NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0104 ADISC completes to NPort x%x "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+ irsp->un.ulpWord[4], disc, phba->num_disc_nodes);
+
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba)) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ goto out;
+ }
+
+ if (irsp->ulpStatus) {
+ /* Check for retry */
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* ELS command is being retried */
+ if (disc) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_set_disctmo(phba);
+ }
+ goto out;
+ }
+ /* ADISC failed */
+ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+ (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+ }
+ else {
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_ADISC);
+ }
+ } else {
+ /* Good status, call state machine */
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_ADISC);
+ }
+
+ if (disc && phba->num_disc_nodes) {
+ /* Check to see if there are more ADISCs to be sent */
+ lpfc_more_adisc(phba);
+
+ /* Check to see if we are done with ADISC authentication */
+ if (phba->num_disc_nodes == 0) {
+ lpfc_can_disctmo(phba);
+ /* If we get here, there is nothing left to wait for */
+ if ((phba->hba_state < LPFC_HBA_READY) &&
+ (phba->hba_state != LPFC_CLEAR_LA)) {
+ /* Link up discovery */
+ if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL))) {
+ phba->hba_state = LPFC_CLEAR_LA;
+ lpfc_clear_la(phba, mbox);
+ mbox->mbox_cmpl =
+ lpfc_mbx_cmpl_clear_la;
+ rc = lpfc_sli_issue_mbox
+ (phba, mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox,
+ phba->mbox_mem_pool);
+ lpfc_disc_flush_list(phba);
+ psli->ring[(psli->ip_ring)].
+ flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->fcp_ring)].
+ flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->next_ring)].
+ flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ phba->hba_state =
+ LPFC_HBA_READY;
+ }
+ }
+ } else {
+ lpfc_rscn_disc(phba);
+ }
+ }
+ }
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint8_t retry)
+{
+ ADISC *ap;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = (sizeof (uint32_t) + sizeof (ADISC));
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_ADISC)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ /* For ADISC request, remainder of payload is service parameters */
+ *((uint32_t *) (pcmd)) = ELS_CMD_ADISC;
+ pcmd += sizeof (uint32_t);
+
+ /* Fill in ADISC payload */
+ ap = (ADISC *) pcmd;
+ ap->hardAL_PA = phba->fc_pref_ALPA;
+ memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+ memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+ ap->DID = be32_to_cpu(phba->fc_myDID);
+
+ phba->fc_stat.elsXmitADISC++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_ADISC_SND;
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ ndlp->nlp_flag &= ~NLP_ADISC_SND;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return (0);
+}
+
+static void
+lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->context_un.rsp_iocb = rspiocb;
+
+ irsp = &(rspiocb->iocb);
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_LOGO_SND;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* LOGO completes to NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0105 LOGO completes to NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+ irsp->un.ulpWord[4], phba->num_disc_nodes);
+
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba))
+ goto out;
+
+ if (irsp->ulpStatus) {
+ /* Check for retry */
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* ELS command is being retried */
+ goto out;
+ }
+ /* LOGO failed */
+ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+ (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+ goto out;
+ }
+ else {
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+ NLP_EVT_CMPL_LOGO);
+ }
+ } else {
+ /* Good status, call state machine */
+ lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
+
+ if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+ lpfc_unreg_rpi(phba, ndlp);
+ }
+ }
+
+out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint8_t retry)
+{
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING];
+
+ cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name));
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_LOGO)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ *((uint32_t *) (pcmd)) = ELS_CMD_LOGO;
+ pcmd += sizeof (uint32_t);
+
+ /* Fill in LOGO payload */
+ *((uint32_t *) (pcmd)) = be32_to_cpu(phba->fc_myDID);
+ pcmd += sizeof (uint32_t);
+ memcpy(pcmd, &phba->fc_portname, sizeof (struct lpfc_name));
+
+ phba->fc_stat.elsXmitLOGO++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_logo;
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_LOGO_SND;
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ ndlp->nlp_flag &= ~NLP_LOGO_SND;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return (0);
+}
+
+static void
+lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+
+ irsp = &rspiocb->iocb;
+
+ /* ELS cmd tag <ulpIoTag> completes */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_ELS,
+ "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n",
+ phba->brd_no,
+ irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]);
+
+ /* Check to see if link went down during discovery */
+ lpfc_els_chk_latt(phba);
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry)
+{
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+ cmdsize = (sizeof (uint32_t) + sizeof (SCR));
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) == 0) {
+ return (1);
+ }
+
+ lpfc_nlp_init(phba, ndlp, nportid);
+
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_SCR)) == 0) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_SCR;
+ pcmd += sizeof (uint32_t);
+
+ /* For SCR, remainder of payload is SCR parameter page */
+ memset(pcmd, 0, sizeof (SCR));
+ ((SCR *) pcmd)->Function = SCR_FUNC_FULL;
+
+ phba->fc_stat.elsXmitSCR++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+ spin_lock_irq(phba->host->host_lock);
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ spin_unlock_irq(phba->host->host_lock);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ return (0);
+}
+
+static int
+lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry)
+{
+ IOCB_t *icmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ FARP *fp;
+ uint8_t *pcmd;
+ uint32_t *lp;
+ uint16_t cmdsize;
+ struct lpfc_nodelist *ondlp;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+ cmdsize = (sizeof (uint32_t) + sizeof (FARP));
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) == 0) {
+ return (1);
+ }
+ lpfc_nlp_init(phba, ndlp, nportid);
+
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+ ndlp, ELS_CMD_RNID)) == 0) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_FARPR;
+ pcmd += sizeof (uint32_t);
+
+ /* Fill in FARPR payload */
+ fp = (FARP *) (pcmd);
+ memset(fp, 0, sizeof (FARP));
+ lp = (uint32_t *) pcmd;
+ *lp++ = be32_to_cpu(nportid);
+ *lp++ = be32_to_cpu(phba->fc_myDID);
+ fp->Rflags = 0;
+ fp->Mflags = (FARP_MATCH_PORT | FARP_MATCH_NODE);
+
+ memcpy(&fp->RportName, &phba->fc_portname, sizeof (struct lpfc_name));
+ memcpy(&fp->RnodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+ if ((ondlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, nportid))) {
+ memcpy(&fp->OportName, &ondlp->nlp_portname,
+ sizeof (struct lpfc_name));
+ memcpy(&fp->OnodeName, &ondlp->nlp_nodename,
+ sizeof (struct lpfc_name));
+ }
+
+ phba->fc_stat.elsXmitFARPR++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+ spin_lock_irq(phba->host->host_lock);
+ if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ spin_unlock_irq(phba->host->host_lock);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ return (0);
+}
+
+void
+lpfc_els_retry_delay(unsigned long ptr)
+{
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_hba *phba;
+ unsigned long iflag;
+ struct lpfc_work_evt *evtp;
+
+ ndlp = (struct lpfc_nodelist *)ptr;
+ phba = ndlp->nlp_phba;
+ evtp = &ndlp->els_retry_evt;
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ if (!list_empty(&evtp->evt_listp)) {
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return;
+ }
+
+ evtp->evt_arg1 = ndlp;
+ evtp->evt = LPFC_EVT_ELS_RETRY;
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return;
+}
+
+void
+lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba;
+ uint32_t cmd;
+ uint32_t did;
+ uint8_t retry;
+
+ phba = ndlp->nlp_phba;
+ spin_lock_irq(phba->host->host_lock);
+ did = (uint32_t) (ndlp->nlp_DID);
+ cmd = (uint32_t) (ndlp->nlp_last_elscmd);
+
+ if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+ }
+
+ ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ retry = ndlp->nlp_retry;
+
+ switch (cmd) {
+ case ELS_CMD_FLOGI:
+ lpfc_issue_els_flogi(phba, ndlp, retry);
+ break;
+ case ELS_CMD_PLOGI:
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, retry);
+ break;
+ case ELS_CMD_ADISC:
+ ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+ lpfc_issue_els_adisc(phba, ndlp, retry);
+ break;
+ case ELS_CMD_PRLI:
+ ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+ lpfc_issue_els_prli(phba, ndlp, retry);
+ break;
+ case ELS_CMD_LOGO:
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ lpfc_issue_els_logo(phba, ndlp, retry);
+ break;
+ }
+ return;
+}
+
+static int
+lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ IOCB_t *irsp;
+ struct lpfc_dmabuf *pcmd;
+ struct lpfc_nodelist *ndlp;
+ uint32_t *elscmd;
+ struct ls_rjt stat;
+ int retry, maxretry;
+ int delay;
+ uint32_t cmd;
+
+ retry = 0;
+ delay = 0;
+ maxretry = lpfc_max_els_tries;
+ irsp = &rspiocb->iocb;
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ cmd = 0;
+ /* Note: context2 may be 0 for internal driver abort
+ * of delays ELS command.
+ */
+
+ if (pcmd && pcmd->virt) {
+ elscmd = (uint32_t *) (pcmd->virt);
+ cmd = *elscmd++;
+ }
+
+ switch (irsp->ulpStatus) {
+ case IOSTAT_FCP_RSP_ERROR:
+ case IOSTAT_REMOTE_STOP:
+ break;
+
+ case IOSTAT_LOCAL_REJECT:
+ switch ((irsp->un.ulpWord[4] & 0xff)) {
+ case IOERR_LOOP_OPEN_FAILURE:
+ if (cmd == ELS_CMD_PLOGI) {
+ if (cmdiocb->retry == 0) {
+ delay = 1;
+ }
+ }
+ retry = 1;
+ break;
+
+ case IOERR_SEQUENCE_TIMEOUT:
+ retry = 1;
+ if ((cmd == ELS_CMD_FLOGI)
+ && (phba->fc_topology != TOPOLOGY_LOOP)) {
+ delay = 1;
+ maxretry = 48;
+ }
+ break;
+
+ case IOERR_NO_RESOURCES:
+ if (cmd == ELS_CMD_PLOGI) {
+ delay = 1;
+ }
+ retry = 1;
+ break;
+
+ case IOERR_INVALID_RPI:
+ retry = 1;
+ break;
+ }
+ break;
+
+ case IOSTAT_NPORT_RJT:
+ case IOSTAT_FABRIC_RJT:
+ if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) {
+ retry = 1;
+ break;
+ }
+ break;
+
+ case IOSTAT_NPORT_BSY:
+ case IOSTAT_FABRIC_BSY:
+ retry = 1;
+ break;
+
+ case IOSTAT_LS_RJT:
+ stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]);
+ /* Added for Vendor specifc support
+ * Just keep retrying for these Rsn / Exp codes
+ */
+ switch (stat.un.b.lsRjtRsnCode) {
+ case LSRJT_UNABLE_TPC:
+ if (stat.un.b.lsRjtRsnCodeExp ==
+ LSEXP_CMD_IN_PROGRESS) {
+ if (cmd == ELS_CMD_PLOGI) {
+ delay = 1;
+ maxretry = 48;
+ }
+ retry = 1;
+ break;
+ }
+ if (cmd == ELS_CMD_PLOGI) {
+ delay = 1;
+ maxretry = lpfc_max_els_tries + 1;
+ retry = 1;
+ break;
+ }
+ break;
+
+ case LSRJT_LOGICAL_BSY:
+ if (cmd == ELS_CMD_PLOGI) {
+ delay = 1;
+ maxretry = 48;
+ }
+ retry = 1;
+ break;
+ }
+ break;
+
+ case IOSTAT_INTERMED_RSP:
+ case IOSTAT_BA_RJT:
+ break;
+
+ default:
+ break;
+ }
+
+ if (ndlp->nlp_DID == FDMI_DID) {
+ retry = 1;
+ }
+
+ if ((++cmdiocb->retry) >= maxretry) {
+ phba->fc_stat.elsRetryExceeded++;
+ retry = 0;
+ }
+
+ if (retry) {
+
+ /* Retry ELS command <elsCmd> to remote NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0107 Retry ELS command x%x to remote "
+ "NPORT x%x Data: x%x x%x\n",
+ phba->brd_no,
+ cmd, ndlp->nlp_DID, cmdiocb->retry, delay);
+
+ if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) {
+ /* If discovery / RSCN timer is running, reset it */
+ if (timer_pending(&phba->fc_disctmo) ||
+ (phba->fc_flag & FC_RSCN_MODE)) {
+ lpfc_set_disctmo(phba);
+ }
+ }
+
+ phba->fc_stat.elsXmitRetry++;
+ if (delay) {
+ phba->fc_stat.elsDelayRetry++;
+ ndlp->nlp_retry = cmdiocb->retry;
+
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ ndlp->nlp_last_elscmd = cmd;
+
+ return (1);
+ }
+ switch (cmd) {
+ case ELS_CMD_FLOGI:
+ lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry);
+ return (1);
+ case ELS_CMD_PLOGI:
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry);
+ return (1);
+ case ELS_CMD_ADISC:
+ ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+ lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry);
+ return (1);
+ case ELS_CMD_PRLI:
+ ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+ lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry);
+ return (1);
+ case ELS_CMD_LOGO:
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry);
+ return (1);
+ }
+ }
+
+ /* No retry ELS command <elsCmd> to remote NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0108 No retry ELS command x%x to remote NPORT x%x "
+ "Data: x%x x%x\n",
+ phba->brd_no,
+ cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag);
+
+ return (0);
+}
+
+int
+lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb)
+{
+ struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
+
+ /* context2 = cmd, context2->next = rsp, context3 = bpl */
+ if (elsiocb->context2) {
+ buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
+ /* Free the response before processing the command. */
+ if (!list_empty(&buf_ptr1->list)) {
+ list_remove_head(&buf_ptr1->list, buf_ptr,
+ struct lpfc_dmabuf,
+ list);
+ lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+ kfree(buf_ptr);
+ }
+ lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
+ kfree(buf_ptr1);
+ }
+
+ if (elsiocb->context3) {
+ buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3;
+ lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+ kfree(buf_ptr);
+ }
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&elsiocb->list, &phba->lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ return 0;
+}
+
+static void
+lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ struct lpfc_nodelist *ndlp;
+
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+
+ /* ACC to LOGO completes to NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0109 ACC to LOGO completes to NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, ndlp->nlp_rpi);
+
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_LOGO_ACC;
+ spin_unlock_irq(phba->host->host_lock);
+
+ switch (ndlp->nlp_state) {
+ case NLP_STE_UNUSED_NODE: /* node is just allocated */
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ break;
+ case NLP_STE_NPR_NODE: /* NPort Recovery mode */
+ lpfc_unreg_rpi(phba, ndlp);
+ break;
+ default:
+ break;
+ }
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+static void
+lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ struct lpfc_nodelist *ndlp;
+ LPFC_MBOXQ_t *mbox = NULL;
+
+ ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ if (cmdiocb->context_un.mbox)
+ mbox = cmdiocb->context_un.mbox;
+
+
+ /* Check to see if link went down during discovery */
+ if ((lpfc_els_chk_latt(phba)) || !ndlp) {
+ if (mbox) {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ }
+ goto out;
+ }
+
+ /* ELS response tag <ulpIoTag> completes */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0110 ELS response tag x%x completes "
+ "Data: x%x x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus,
+ rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+ if (mbox) {
+ if ((rspiocb->iocb.ulpStatus == 0)
+ && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) {
+ /* set_slim mailbox command needs to execute first,
+ * queue this command to be processed later.
+ */
+ lpfc_unreg_rpi(phba, ndlp);
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
+ mbox->context2 = ndlp;
+ ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST);
+ if (lpfc_sli_issue_mbox(phba, mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB))
+ != MBX_NOT_FINISHED) {
+ goto out;
+ }
+ /* NOTE: we should have messages for unsuccessful
+ reglogin */
+ mempool_free( mbox, phba->mbox_mem_pool);
+ } else {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ }
+ }
+ }
+out:
+ if (ndlp) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ lpfc_els_free_iocb(phba, cmdiocb);
+ return;
+}
+
+int
+lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag,
+ struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp,
+ LPFC_MBOXQ_t * mbox, uint8_t newnode)
+{
+ IOCB_t *icmd;
+ IOCB_t *oldcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ int rc;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+ oldcmd = &oldiocb->iocb;
+
+ switch (flag) {
+ case ELS_CMD_ACC:
+ cmdsize = sizeof (uint32_t);
+ if ((elsiocb =
+ lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp, ELS_CMD_ACC)) == 0) {
+ return (1);
+ }
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+ pcmd += sizeof (uint32_t);
+ break;
+ case ELS_CMD_PLOGI:
+ cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t));
+ if ((elsiocb =
+ lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp, ELS_CMD_ACC)) == 0) {
+ return (1);
+ }
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ if (mbox)
+ elsiocb->context_un.mbox = mbox;
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+ pcmd += sizeof (uint32_t);
+ memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+ break;
+ default:
+ return (1);
+ }
+
+ if (newnode)
+ elsiocb->context1 = NULL;
+
+ /* Xmit ELS ACC response tag <ulpIoTag> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0128 Xmit ELS ACC response tag x%x "
+ "Data: x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ elsiocb->iocb.ulpIoTag,
+ elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+ if (ndlp->nlp_flag & NLP_LOGO_ACC) {
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc;
+ } else {
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+ }
+
+ phba->fc_stat.elsXmitACC++;
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+int
+lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError,
+ struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+ IOCB_t *icmd;
+ IOCB_t *oldcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ int rc;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = 2 * sizeof (uint32_t);
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp, ELS_CMD_LS_RJT)) == 0) {
+ return (1);
+ }
+
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+ pcmd += sizeof (uint32_t);
+ *((uint32_t *) (pcmd)) = rejectError;
+
+ /* Xmit ELS RJT <err> response tag <ulpIoTag> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0129 Xmit ELS RJT x%x response tag x%x "
+ "Data: x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ rejectError, elsiocb->iocb.ulpIoTag,
+ elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+ phba->fc_stat.elsXmitLSRJT++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+int
+lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba,
+ struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+ ADISC *ap;
+ IOCB_t *icmd;
+ IOCB_t *oldcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ int rc;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = sizeof (uint32_t) + sizeof (ADISC);
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp, ELS_CMD_ACC)) == 0) {
+ return (1);
+ }
+
+ /* Xmit ADISC ACC response tag <ulpIoTag> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0130 Xmit ADISC ACC response tag x%x "
+ "Data: x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ elsiocb->iocb.ulpIoTag,
+ elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+ pcmd += sizeof (uint32_t);
+
+ ap = (ADISC *) (pcmd);
+ ap->hardAL_PA = phba->fc_pref_ALPA;
+ memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+ memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+ ap->DID = be32_to_cpu(phba->fc_myDID);
+
+ phba->fc_stat.elsXmitACC++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+int
+lpfc_els_rsp_prli_acc(struct lpfc_hba * phba,
+ struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+ PRLI *npr;
+ lpfc_vpd_t *vpd;
+ IOCB_t *icmd;
+ IOCB_t *oldcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ int rc;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
+
+ cmdsize = sizeof (uint32_t) + sizeof (PRLI);
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp,
+ (ELS_CMD_ACC |
+ (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) ==
+ 0) {
+ return (1);
+ }
+
+ /* Xmit PRLI ACC response tag <ulpIoTag> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0131 Xmit PRLI ACC response tag x%x "
+ "Data: x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ elsiocb->iocb.ulpIoTag,
+ elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK));
+ pcmd += sizeof (uint32_t);
+
+ /* For PRLI, remainder of payload is PRLI parameter page */
+ memset(pcmd, 0, sizeof (PRLI));
+
+ npr = (PRLI *) pcmd;
+ vpd = &phba->vpd;
+ /*
+ * If our firmware version is 3.20 or later,
+ * set the following bits for FC-TAPE support.
+ */
+ if (vpd->rev.feaLevelHigh >= 0x02) {
+ npr->ConfmComplAllowed = 1;
+ npr->Retry = 1;
+ npr->TaskRetryIdReq = 1;
+ }
+
+ npr->acceptRspCode = PRLI_REQ_EXECUTED;
+ npr->estabImagePair = 1;
+ npr->readXferRdyDis = 1;
+ npr->ConfmComplAllowed = 1;
+
+ npr->prliType = PRLI_FCP_TYPE;
+ npr->initiatorFunc = 1;
+
+ phba->fc_stat.elsXmitACC++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba,
+ uint8_t format,
+ struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+ RNID *rn;
+ IOCB_t *icmd;
+ IOCB_t *oldcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ uint8_t *pcmd;
+ uint16_t cmdsize;
+ int rc;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING];
+
+ cmdsize = sizeof (uint32_t) + sizeof (uint32_t)
+ + (2 * sizeof (struct lpfc_name));
+ if (format)
+ cmdsize += sizeof (RNID_TOP_DISC);
+
+ if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+ ndlp, ELS_CMD_ACC)) == 0) {
+ return (1);
+ }
+
+ /* Xmit RNID ACC response tag <ulpIoTag> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0132 Xmit RNID ACC response tag x%x "
+ "Data: x%x\n",
+ phba->brd_no,
+ elsiocb->iocb.ulpIoTag,
+ elsiocb->iocb.ulpContext);
+
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+ pcmd += sizeof (uint32_t);
+
+ memset(pcmd, 0, sizeof (RNID));
+ rn = (RNID *) (pcmd);
+ rn->Format = format;
+ rn->CommonLen = (2 * sizeof (struct lpfc_name));
+ memcpy(&rn->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+ memcpy(&rn->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+ switch (format) {
+ case 0:
+ rn->SpecificLen = 0;
+ break;
+ case RNID_TOPOLOGY_DISC:
+ rn->SpecificLen = sizeof (RNID_TOP_DISC);
+ memcpy(&rn->un.topologyDisc.portName,
+ &phba->fc_portname, sizeof (struct lpfc_name));
+ rn->un.topologyDisc.unitType = RNID_HBA;
+ rn->un.topologyDisc.physPort = 0;
+ rn->un.topologyDisc.attachedNodes = 0;
+ break;
+ default:
+ rn->CommonLen = 0;
+ rn->SpecificLen = 0;
+ break;
+ }
+
+ phba->fc_stat.elsXmitACC++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+ elsiocb->context1 = NULL; /* Don't need ndlp for cmpl,
+ * it could be freed */
+
+ spin_lock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ spin_unlock_irq(phba->host->host_lock);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return (1);
+ }
+ return (0);
+}
+
+int
+lpfc_els_disc_adisc(struct lpfc_hba * phba)
+{
+ int sentadisc;
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ sentadisc = 0;
+ /* go thru NPR list and issue any remaining ELS ADISCs */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+ nlp_listp) {
+ if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
+ if (ndlp->nlp_flag & NLP_NPR_ADISC) {
+ ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+ lpfc_nlp_list(phba, ndlp,
+ NLP_ADISC_LIST);
+ lpfc_issue_els_adisc(phba, ndlp, 0);
+ sentadisc++;
+ phba->num_disc_nodes++;
+ if (phba->num_disc_nodes >=
+ phba->cfg_discovery_threads) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_NLP_MORE;
+ spin_unlock_irq(phba->host->host_lock);
+ break;
+ }
+ }
+ }
+ }
+ if (sentadisc == 0) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_NLP_MORE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ return(sentadisc);
+}
+
+int
+lpfc_els_disc_plogi(struct lpfc_hba * phba)
+{
+ int sentplogi;
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ sentplogi = 0;
+ /* go thru NPR list and issue any remaining ELS PLOGIs */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+ nlp_listp) {
+ if ((ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
+ (!(ndlp->nlp_flag & NLP_DELAY_TMO))) {
+ if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ sentplogi++;
+ phba->num_disc_nodes++;
+ if (phba->num_disc_nodes >=
+ phba->cfg_discovery_threads) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_NLP_MORE;
+ spin_unlock_irq(phba->host->host_lock);
+ break;
+ }
+ }
+ }
+ }
+ if (sentplogi == 0) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_NLP_MORE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ return(sentplogi);
+}
+
+int
+lpfc_els_flush_rscn(struct lpfc_hba * phba)
+{
+ struct lpfc_dmabuf *mp;
+ int i;
+
+ for (i = 0; i < phba->fc_rscn_id_cnt; i++) {
+ mp = phba->fc_rscn_id_list[i];
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ phba->fc_rscn_id_list[i] = NULL;
+ }
+ phba->fc_rscn_id_cnt = 0;
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY);
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_can_disctmo(phba);
+ return (0);
+}
+
+int
+lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did)
+{
+ D_ID ns_did;
+ D_ID rscn_did;
+ struct lpfc_dmabuf *mp;
+ uint32_t *lp;
+ uint32_t payload_len, cmd, i, match;
+
+ ns_did.un.word = did;
+ match = 0;
+
+ /* Never match fabric nodes for RSCNs */
+ if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
+ return(0);
+
+ /* If we are doing a FULL RSCN rediscovery, match everything */
+ if (phba->fc_flag & FC_RSCN_DISCOVERY) {
+ return (did);
+ }
+
+ for (i = 0; i < phba->fc_rscn_id_cnt; i++) {
+ mp = phba->fc_rscn_id_list[i];
+ lp = (uint32_t *) mp->virt;
+ cmd = *lp++;
+ payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */
+ payload_len -= sizeof (uint32_t); /* take off word 0 */
+ while (payload_len) {
+ rscn_did.un.word = *lp++;
+ rscn_did.un.word = be32_to_cpu(rscn_did.un.word);
+ payload_len -= sizeof (uint32_t);
+ switch (rscn_did.un.b.resv) {
+ case 0: /* Single N_Port ID effected */
+ if (ns_did.un.word == rscn_did.un.word) {
+ match = did;
+ }
+ break;
+ case 1: /* Whole N_Port Area effected */
+ if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+ && (ns_did.un.b.area == rscn_did.un.b.area))
+ {
+ match = did;
+ }
+ break;
+ case 2: /* Whole N_Port Domain effected */
+ if (ns_did.un.b.domain == rscn_did.un.b.domain)
+ {
+ match = did;
+ }
+ break;
+ case 3: /* Whole Fabric effected */
+ match = did;
+ break;
+ default:
+ /* Unknown Identifier in RSCN list */
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0217 Unknown Identifier in "
+ "RSCN payload Data: x%x\n",
+ phba->brd_no, rscn_did.un.word);
+ break;
+ }
+ if (match) {
+ break;
+ }
+ }
+ }
+ return (match);
+}
+
+static int
+lpfc_rscn_recovery_check(struct lpfc_hba * phba)
+{
+ struct lpfc_nodelist *ndlp = NULL, *next_ndlp;
+ struct list_head *listp;
+ struct list_head *node_list[7];
+ int i;
+
+ /* Look at all nodes effected by pending RSCNs and move
+ * them to NPR list.
+ */
+ node_list[0] = &phba->fc_npr_list; /* MUST do this list first */
+ node_list[1] = &phba->fc_nlpmap_list;
+ node_list[2] = &phba->fc_nlpunmap_list;
+ node_list[3] = &phba->fc_prli_list;
+ node_list[4] = &phba->fc_reglogin_list;
+ node_list[5] = &phba->fc_adisc_list;
+ node_list[6] = &phba->fc_plogi_list;
+ for (i = 0; i < 7; i++) {
+ listp = node_list[i];
+ if (list_empty(listp))
+ continue;
+
+ list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
+ if (!(lpfc_rscn_payload_check(phba, ndlp->nlp_DID)))
+ continue;
+
+ lpfc_disc_state_machine(phba, ndlp, NULL,
+ NLP_EVT_DEVICE_RECOVERY);
+ if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+ ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+ del_timer_sync(&ndlp->nlp_delayfunc);
+ if (!list_empty(&ndlp->
+ els_retry_evt.evt_listp))
+ list_del_init(&ndlp->
+ els_retry_evt.evt_listp);
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+lpfc_els_rcv_rscn(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb,
+ struct lpfc_nodelist * ndlp, uint8_t newnode)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ uint32_t payload_len, cmd;
+
+ icmd = &cmdiocb->iocb;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */
+ payload_len -= sizeof (uint32_t); /* take off word 0 */
+ cmd &= ELS_CMD_MASK;
+
+ /* RSCN received */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0214 RSCN received Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt);
+
+ /* If we are about to begin discovery, just ACC the RSCN.
+ * Discovery processing will satisfy it.
+ */
+ if (phba->hba_state < LPFC_NS_QRY) {
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL,
+ newnode);
+ return (0);
+ }
+
+ /* If we are already processing an RSCN, save the received
+ * RSCN payload buffer, cmdiocb->context2 to process later.
+ */
+ if (phba->fc_flag & (FC_RSCN_MODE | FC_NDISC_ACTIVE)) {
+ if ((phba->fc_rscn_id_cnt < FC_MAX_HOLD_RSCN) &&
+ !(phba->fc_flag & FC_RSCN_DISCOVERY)) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_RSCN_MODE;
+ spin_unlock_irq(phba->host->host_lock);
+ phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd;
+
+ /* If we zero, cmdiocb->context2, the calling
+ * routine will not try to free it.
+ */
+ cmdiocb->context2 = NULL;
+
+ /* Deferred RSCN */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0235 Deferred RSCN "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->fc_rscn_id_cnt,
+ phba->fc_flag, phba->hba_state);
+ } else {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_RSCN_DISCOVERY;
+ spin_unlock_irq(phba->host->host_lock);
+ /* ReDiscovery RSCN */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0234 ReDiscovery RSCN "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->fc_rscn_id_cnt,
+ phba->fc_flag, phba->hba_state);
+ }
+ /* Send back ACC */
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL,
+ newnode);
+
+ /* send RECOVERY event for ALL nodes that match RSCN payload */
+ lpfc_rscn_recovery_check(phba);
+ return (0);
+ }
+
+ phba->fc_flag |= FC_RSCN_MODE;
+ phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd;
+ /*
+ * If we zero, cmdiocb->context2, the calling routine will
+ * not try to free it.
+ */
+ cmdiocb->context2 = NULL;
+
+ lpfc_set_disctmo(phba);
+
+ /* Send back ACC */
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode);
+
+ /* send RECOVERY event for ALL nodes that match RSCN payload */
+ lpfc_rscn_recovery_check(phba);
+
+ return (lpfc_els_handle_rscn(phba));
+}
+
+int
+lpfc_els_handle_rscn(struct lpfc_hba * phba)
+{
+ struct lpfc_nodelist *ndlp;
+
+ /* Start timer for RSCN processing */
+ lpfc_set_disctmo(phba);
+
+ /* RSCN processed */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0215 RSCN processed Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ phba->fc_flag, 0, phba->fc_rscn_id_cnt,
+ phba->hba_state);
+
+ /* To process RSCN, first compare RSCN data with NameServer */
+ phba->fc_ns_retry = 0;
+ if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+ NameServer_DID))) {
+ /* Good ndlp, issue CT Request to NameServer */
+ if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) {
+ /* Wait for NameServer query cmpl before we can
+ continue */
+ return (1);
+ }
+ } else {
+ /* If login to NameServer does not exist, issue one */
+ /* Good status, issue PLOGI to NameServer */
+ if ((ndlp =
+ lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) {
+ /* Wait for NameServer login cmpl before we can
+ continue */
+ return (1);
+ }
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL))
+ == 0) {
+ lpfc_els_flush_rscn(phba);
+ return (0);
+ } else {
+ lpfc_nlp_init(phba, ndlp, NameServer_DID);
+ ndlp->nlp_type |= NLP_FABRIC;
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ /* Wait for NameServer login cmpl before we can
+ continue */
+ return (1);
+ }
+ }
+
+ lpfc_els_flush_rscn(phba);
+ return (0);
+}
+
+static int
+lpfc_els_rcv_flogi(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb,
+ struct lpfc_nodelist * ndlp, uint8_t newnode)
+{
+ struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ uint32_t *lp = (uint32_t *) pcmd->virt;
+ IOCB_t *icmd = &cmdiocb->iocb;
+ struct serv_parm *sp;
+ LPFC_MBOXQ_t *mbox;
+ struct ls_rjt stat;
+ uint32_t cmd, did;
+ int rc;
+
+ cmd = *lp++;
+ sp = (struct serv_parm *) lp;
+
+ /* FLOGI received */
+
+ lpfc_set_disctmo(phba);
+
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ /* We should never receive a FLOGI in loop mode, ignore it */
+ did = icmd->un.elsreq64.remoteID;
+
+ /* An FLOGI ELS command <elsCmd> was received from DID <did> in
+ Loop Mode */
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "%d:0113 An FLOGI ELS command x%x was received "
+ "from DID x%x in Loop Mode\n",
+ phba->brd_no, cmd, did);
+ return (1);
+ }
+
+ did = Fabric_DID;
+
+ if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) {
+ /* For a FLOGI we accept, then if our portname is greater
+ * then the remote portname we initiate Nport login.
+ */
+
+ rc = memcmp(&phba->fc_portname, &sp->portName,
+ sizeof (struct lpfc_name));
+
+ if (!rc) {
+ if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL)) == 0) {
+ return (1);
+ }
+ lpfc_linkdown(phba);
+ lpfc_init_link(phba, mbox,
+ phba->cfg_topology,
+ phba->cfg_link_speed);
+ mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox
+ (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ }
+ return (1);
+ }
+ else if (rc > 0) { /* greater than */
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_PT2PT_PLOGI;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ phba->fc_flag |= FC_PT2PT;
+ phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ } else {
+ /* Reject this request because invalid parameters */
+ stat.un.b.lsRjtRsvd0 = 0;
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+ stat.un.b.vendorUnique = 0;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+ return (1);
+ }
+
+ /* Send back ACC */
+ lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode);
+
+ return (0);
+}
+
+static int
+lpfc_els_rcv_rnid(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ RNID *rn;
+ struct ls_rjt stat;
+ uint32_t cmd, did;
+
+ icmd = &cmdiocb->iocb;
+ did = icmd->un.elsreq64.remoteID;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ rn = (RNID *) lp;
+
+ /* RNID received */
+
+ switch (rn->Format) {
+ case 0:
+ case RNID_TOPOLOGY_DISC:
+ /* Send back ACC */
+ lpfc_els_rsp_rnid_acc(phba, rn->Format, cmdiocb, ndlp);
+ break;
+ default:
+ /* Reject this request because format not supported */
+ stat.un.b.lsRjtRsvd0 = 0;
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
+ stat.un.b.vendorUnique = 0;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+ }
+ return (0);
+}
+
+static int
+lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ RRQ *rrq;
+ uint32_t cmd, did;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_FCP_RING];
+ icmd = &cmdiocb->iocb;
+ did = icmd->un.elsreq64.remoteID;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ rrq = (RRQ *) lp;
+
+ /* RRQ received */
+ /* Get oxid / rxid from payload and abort it */
+ spin_lock_irq(phba->host->host_lock);
+ if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) {
+ lpfc_sli_abort_iocb(phba, pring, 0, 0, rrq->Oxid,
+ LPFC_CTX_CTX);
+ } else {
+ lpfc_sli_abort_iocb(phba, pring, 0, 0, rrq->Rxid,
+ LPFC_CTX_CTX);
+ }
+
+ spin_unlock_irq(phba->host->host_lock);
+ /* ACCEPT the rrq request */
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+ return 0;
+}
+
+static int
+lpfc_els_rcv_farp(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ FARP *fp;
+ uint32_t cmd, cnt, did;
+
+ icmd = &cmdiocb->iocb;
+ did = icmd->un.elsreq64.remoteID;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ fp = (FARP *) lp;
+
+ /* FARP-REQ received from DID <did> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_IP,
+ "%d:0601 FARP-REQ received from DID x%x\n",
+ phba->brd_no, did);
+
+ /* We will only support match on WWPN or WWNN */
+ if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) {
+ return (0);
+ }
+
+ cnt = 0;
+ /* If this FARP command is searching for my portname */
+ if (fp->Mflags & FARP_MATCH_PORT) {
+ if (memcmp(&fp->RportName, &phba->fc_portname,
+ sizeof (struct lpfc_name)) == 0)
+ cnt = 1;
+ }
+
+ /* If this FARP command is searching for my nodename */
+ if (fp->Mflags & FARP_MATCH_NODE) {
+ if (memcmp(&fp->RnodeName, &phba->fc_nodename,
+ sizeof (struct lpfc_name)) == 0)
+ cnt = 1;
+ }
+
+ if (cnt) {
+ if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+ (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) {
+ /* Log back into the node before sending the FARP. */
+ if (fp->Rflags & FARP_REQUEST_PLOGI) {
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ }
+
+ /* Send a FARP response to that node */
+ if (fp->Rflags & FARP_REQUEST_FARPR) {
+ lpfc_issue_els_farpr(phba, did, 0);
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+lpfc_els_rcv_farpr(struct lpfc_hba * phba,
+ struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ uint32_t cmd, did;
+
+ icmd = &cmdiocb->iocb;
+ did = icmd->un.elsreq64.remoteID;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ /* FARP-RSP received from DID <did> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_IP,
+ "%d:0600 FARP-RSP received from DID x%x\n",
+ phba->brd_no, did);
+
+ /* ACCEPT the Farp resp request */
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+ return 0;
+}
+
+static int
+lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ FAN *fp;
+ uint32_t cmd, did;
+
+ icmd = &cmdiocb->iocb;
+ did = icmd->un.elsreq64.remoteID;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ fp = (FAN *) lp;
+
+ /* FAN received */
+
+ /* ACCEPT the FAN request */
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+ if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+ /* The discovery state machine needs to take a different
+ * action if this node has switched fabrics
+ */
+ if ((memcmp(&fp->FportName, &phba->fc_fabparam.portName,
+ sizeof (struct lpfc_name)) != 0)
+ ||
+ (memcmp(&fp->FnodeName, &phba->fc_fabparam.nodeName,
+ sizeof (struct lpfc_name)) != 0)) {
+ /* This node has switched fabrics. An FLOGI is required
+ * after the timeout
+ */
+ return (0);
+ }
+
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ }
+
+ return (0);
+}
+
+void
+lpfc_els_timeout(unsigned long ptr)
+{
+ struct lpfc_hba *phba;
+ unsigned long iflag;
+
+ phba = (struct lpfc_hba *)ptr;
+ if (phba == 0)
+ return;
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ if (!(phba->work_hba_events & WORKER_ELS_TMO)) {
+ phba->work_hba_events |= WORKER_ELS_TMO;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return;
+}
+
+void
+lpfc_els_timeout_handler(struct lpfc_hba *phba)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *tmp_iocb, *piocb;
+ IOCB_t *cmd = NULL;
+ struct lpfc_dmabuf *pcmd;
+ struct list_head *dlp;
+ uint32_t *elscmd;
+ uint32_t els_command;
+ uint32_t timeout;
+ uint32_t remote_ID;
+
+ if (phba == 0)
+ return;
+ spin_lock_irq(phba->host->host_lock);
+ /* If the timer is already canceled do nothing */
+ if (!(phba->work_hba_events & WORKER_ELS_TMO)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+ }
+ timeout = (uint32_t)(phba->fc_ratov << 1);
+
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+ dlp = &pring->txcmplq;
+
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+ cmd = &piocb->iocb;
+
+ if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+ continue;
+ }
+ pcmd = (struct lpfc_dmabuf *) piocb->context2;
+ elscmd = (uint32_t *) (pcmd->virt);
+ els_command = *elscmd;
+
+ if ((els_command == ELS_CMD_FARP)
+ || (els_command == ELS_CMD_FARPR)) {
+ continue;
+ }
+
+ if (piocb->drvrTimeout > 0) {
+ if (piocb->drvrTimeout >= timeout) {
+ piocb->drvrTimeout -= timeout;
+ } else {
+ piocb->drvrTimeout = 0;
+ }
+ continue;
+ }
+
+ list_del(&piocb->list);
+ pring->txcmplq_cnt--;
+
+ if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+ struct lpfc_nodelist *ndlp;
+
+ ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+ remote_ID = ndlp->nlp_DID;
+ if (cmd->un.elsreq64.bdl.ulpIoTag32) {
+ lpfc_sli_issue_abort_iotag32(phba,
+ pring, piocb);
+ }
+ } else {
+ remote_ID = cmd->un.elsreq64.remoteID;
+ }
+
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_ELS,
+ "%d:0127 ELS timeout Data: x%x x%x x%x x%x\n",
+ phba->brd_no, els_command,
+ remote_ID, cmd->ulpCommand, cmd->ulpIoTag);
+
+ /*
+ * The iocb has timed out; abort it.
+ */
+ if (piocb->iocb_cmpl) {
+ cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (piocb->iocb_cmpl) (phba, piocb, piocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&piocb->list, &phba->lpfc_iocb_list);
+ }
+ }
+ if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt) {
+ phba->els_tmofunc.expires = jiffies + HZ * timeout;
+ add_timer(&phba->els_tmofunc);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+}
+
+void
+lpfc_els_flush_cmd(struct lpfc_hba * phba)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *tmp_iocb, *piocb;
+ IOCB_t *cmd = NULL;
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *elscmd;
+ uint32_t els_command;
+ uint32_t remote_ID;
+
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) {
+ cmd = &piocb->iocb;
+
+ if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+ continue;
+ }
+
+ /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */
+ if ((cmd->ulpCommand == CMD_QUE_RING_BUF_CN) ||
+ (cmd->ulpCommand == CMD_QUE_RING_BUF64_CN) ||
+ (cmd->ulpCommand == CMD_CLOSE_XRI_CN) ||
+ (cmd->ulpCommand == CMD_ABORT_XRI_CN)) {
+ continue;
+ }
+
+ pcmd = (struct lpfc_dmabuf *) piocb->context2;
+ elscmd = (uint32_t *) (pcmd->virt);
+ els_command = *elscmd;
+
+ if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+ struct lpfc_nodelist *ndlp;
+
+ ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+ remote_ID = ndlp->nlp_DID;
+ if (phba->hba_state == LPFC_HBA_READY) {
+ continue;
+ }
+ } else {
+ remote_ID = cmd->un.elsreq64.remoteID;
+ }
+
+ list_del(&piocb->list);
+ pring->txcmplq_cnt--;
+
+ cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+
+ if (piocb->iocb_cmpl) {
+ spin_unlock_irq(phba->host->host_lock);
+ (piocb->iocb_cmpl) (phba, piocb, piocb);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ else
+ list_add_tail(&piocb->list, &phba->lpfc_iocb_list);
+ }
+
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+ cmd = &piocb->iocb;
+
+ if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+ continue;
+ }
+ pcmd = (struct lpfc_dmabuf *) piocb->context2;
+ elscmd = (uint32_t *) (pcmd->virt);
+ els_command = *elscmd;
+
+ if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+ struct lpfc_nodelist *ndlp;
+
+ ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+ remote_ID = ndlp->nlp_DID;
+ if (phba->hba_state == LPFC_HBA_READY) {
+ continue;
+ }
+ } else {
+ remote_ID = cmd->un.elsreq64.remoteID;
+ }
+
+ list_del(&piocb->list);
+ pring->txcmplq_cnt--;
+
+ cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+
+ if (piocb->iocb_cmpl) {
+ spin_unlock_irq(phba->host->host_lock);
+ (piocb->iocb_cmpl) (phba, piocb, piocb);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ else
+ list_add_tail(&piocb->list, &phba->lpfc_iocb_list);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+
+void
+lpfc_els_unsol_event(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring, struct lpfc_iocbq * elsiocb)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_dmabuf *mp;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ struct ls_rjt stat;
+ uint32_t cmd;
+ uint32_t did;
+ uint32_t newnode;
+ uint32_t drop_cmd = 0; /* by default do NOT drop received cmd */
+ uint32_t rjt_err = 0;
+
+ psli = &phba->sli;
+ icmd = &elsiocb->iocb;
+
+ if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {
+ /* Not enough posted buffers; Try posting more buffers */
+ phba->fc_stat.NoRcvBuf++;
+ lpfc_post_buffer(phba, pring, 0, 1);
+ return;
+ }
+
+ /* If there are no BDEs associated with this IOCB,
+ * there is nothing to do.
+ */
+ if (icmd->ulpBdeCount == 0)
+ return;
+
+ /* type of ELS cmd is first 32bit word in packet */
+ mp = lpfc_sli_ringpostbuf_get(phba, pring, getPaddr(icmd->un.
+ cont64[0].
+ addrHigh,
+ icmd->un.
+ cont64[0].addrLow));
+ if (mp == 0) {
+ drop_cmd = 1;
+ goto dropit;
+ }
+
+ newnode = 0;
+ lp = (uint32_t *) mp->virt;
+ cmd = *lp++;
+ lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1);
+
+ if (icmd->ulpStatus) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ drop_cmd = 1;
+ goto dropit;
+ }
+
+ /* Check to see if link went down during discovery */
+ if (lpfc_els_chk_latt(phba)) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ drop_cmd = 1;
+ goto dropit;
+ }
+
+ did = icmd->un.rcvels.remoteID;
+ if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) {
+ /* Cannot find existing Fabric ndlp, so allocate a new one */
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL))
+ == 0) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ drop_cmd = 1;
+ goto dropit;
+ }
+
+ lpfc_nlp_init(phba, ndlp, did);
+ newnode = 1;
+ if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) {
+ ndlp->nlp_type |= NLP_FABRIC;
+ }
+ }
+
+ phba->fc_stat.elsRcvFrame++;
+ elsiocb->context1 = ndlp;
+ elsiocb->context2 = mp;
+
+ if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) {
+ cmd &= ELS_CMD_MASK;
+ }
+ /* ELS command <elsCmd> received from NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "%d:0112 ELS command x%x received from NPORT x%x "
+ "Data: x%x\n", phba->brd_no, cmd, did, phba->hba_state);
+
+ switch (cmd) {
+ case ELS_CMD_PLOGI:
+ phba->fc_stat.elsRcvPLOGI++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI);
+ break;
+ case ELS_CMD_FLOGI:
+ phba->fc_stat.elsRcvFLOGI++;
+ lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode);
+ if (newnode) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+ break;
+ case ELS_CMD_LOGO:
+ phba->fc_stat.elsRcvLOGO++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO);
+ break;
+ case ELS_CMD_PRLO:
+ phba->fc_stat.elsRcvPRLO++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO);
+ break;
+ case ELS_CMD_RSCN:
+ phba->fc_stat.elsRcvRSCN++;
+ lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode);
+ if (newnode) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+ break;
+ case ELS_CMD_ADISC:
+ phba->fc_stat.elsRcvADISC++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC);
+ break;
+ case ELS_CMD_PDISC:
+ phba->fc_stat.elsRcvPDISC++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC);
+ break;
+ case ELS_CMD_FARPR:
+ phba->fc_stat.elsRcvFARPR++;
+ lpfc_els_rcv_farpr(phba, elsiocb, ndlp);
+ break;
+ case ELS_CMD_FARP:
+ phba->fc_stat.elsRcvFARP++;
+ lpfc_els_rcv_farp(phba, elsiocb, ndlp);
+ break;
+ case ELS_CMD_FAN:
+ phba->fc_stat.elsRcvFAN++;
+ lpfc_els_rcv_fan(phba, elsiocb, ndlp);
+ break;
+ case ELS_CMD_RRQ:
+ phba->fc_stat.elsRcvRRQ++;
+ lpfc_els_rcv_rrq(phba, elsiocb, ndlp);
+ break;
+ case ELS_CMD_PRLI:
+ phba->fc_stat.elsRcvPRLI++;
+ if (phba->hba_state < LPFC_DISC_AUTH) {
+ rjt_err = LSEXP_NOTHING_MORE;
+ break;
+ }
+ lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI);
+ break;
+ case ELS_CMD_RNID:
+ phba->fc_stat.elsRcvRNID++;
+ lpfc_els_rcv_rnid(phba, elsiocb, ndlp);
+ break;
+ default:
+ /* Unsupported ELS command, reject */
+ rjt_err = LSEXP_NOTHING_MORE;
+
+ /* Unknown ELS command <elsCmd> received from NPORT <did> */
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "%d:0115 Unknown ELS command x%x received from "
+ "NPORT x%x\n", phba->brd_no, cmd, did);
+ if (newnode) {
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+ break;
+ }
+
+ /* check if need to LS_RJT received ELS cmd */
+ if (rjt_err) {
+ stat.un.b.lsRjtRsvd0 = 0;
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = rjt_err;
+ stat.un.b.vendorUnique = 0;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp);
+ }
+
+ if (elsiocb->context2) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+dropit:
+ /* check if need to drop received ELS cmd */
+ if (drop_cmd == 1) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "%d:0111 Dropping received ELS cmd "
+ "Data: x%x x%x\n", phba->brd_no,
+ icmd->ulpStatus, icmd->un.ulpWord[4]);
+ phba->fc_stat.elsRcvDrop++;
+ }
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
new file mode 100644
index 000000000000..d546206038bf
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -0,0 +1,2537 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_hbadisc.c 1.266 2005/04/13 11:59:06EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_disc.h"
+#include "lpfc_sli.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+
+/* AlpaArray for assignment of scsid for scan-down and bind_method */
+static uint8_t lpfcAlpaArray[] = {
+ 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6,
+ 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA,
+ 0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5,
+ 0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9,
+ 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97,
+ 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79,
+ 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B,
+ 0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56,
+ 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A,
+ 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35,
+ 0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29,
+ 0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17,
+ 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01
+};
+
+static void lpfc_disc_timeout_handler(struct lpfc_hba *);
+
+static void
+lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+{
+ if (!(ndlp->nlp_type & NLP_FABRIC)) {
+ /* Nodev timeout on NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0203 Nodev timeout on NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, ndlp->nlp_rpi);
+ }
+
+ spin_lock_irq(phba->host->host_lock);
+ if (!(ndlp->nlp_flag & NLP_NODEV_TMO)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+ }
+
+ ndlp->nlp_flag &= ~NLP_NODEV_TMO;
+
+ if (ndlp->nlp_sid != NLP_NO_SID) {
+ /* flush the target */
+ lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+ ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM);
+ return;
+}
+
+static void
+lpfc_work_list_done(struct lpfc_hba * phba)
+{
+ struct lpfc_work_evt *evtp = NULL;
+ struct lpfc_nodelist *ndlp;
+ int free_evt;
+
+ spin_lock_irq(phba->host->host_lock);
+ while(!list_empty(&phba->work_list)) {
+ list_remove_head((&phba->work_list), evtp, typeof(*evtp),
+ evt_listp);
+ spin_unlock_irq(phba->host->host_lock);
+ free_evt = 1;
+ switch(evtp->evt) {
+ case LPFC_EVT_NODEV_TMO:
+ ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
+ lpfc_process_nodev_timeout(phba, ndlp);
+ free_evt = 0;
+ break;
+ case LPFC_EVT_ELS_RETRY:
+ ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
+ lpfc_els_retry_delay_handler(ndlp);
+ free_evt = 0;
+ break;
+ case LPFC_EVT_ONLINE:
+ *(int *)(evtp->evt_arg1) = lpfc_online(phba);
+ complete((struct completion *)(evtp->evt_arg2));
+ break;
+ case LPFC_EVT_OFFLINE:
+ *(int *)(evtp->evt_arg1) = lpfc_offline(phba);
+ complete((struct completion *)(evtp->evt_arg2));
+ break;
+ }
+ if (free_evt)
+ kfree(evtp);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+}
+
+static void
+lpfc_work_done(struct lpfc_hba * phba)
+{
+ struct lpfc_sli_ring *pring;
+ int i;
+ uint32_t ha_copy;
+ uint32_t control;
+ uint32_t work_hba_events;
+
+ spin_lock_irq(phba->host->host_lock);
+ ha_copy = phba->work_ha;
+ phba->work_ha = 0;
+ work_hba_events=phba->work_hba_events;
+ spin_unlock_irq(phba->host->host_lock);
+
+ if(ha_copy & HA_ERATT)
+ lpfc_handle_eratt(phba);
+
+ if(ha_copy & HA_MBATT)
+ lpfc_sli_handle_mb_event(phba);
+
+ if(ha_copy & HA_LATT)
+ lpfc_handle_latt(phba);
+
+ if (work_hba_events & WORKER_DISC_TMO)
+ lpfc_disc_timeout_handler(phba);
+
+ if (work_hba_events & WORKER_ELS_TMO)
+ lpfc_els_timeout_handler(phba);
+
+ if (work_hba_events & WORKER_MBOX_TMO)
+ lpfc_mbox_timeout_handler(phba);
+
+ if (work_hba_events & WORKER_FDMI_TMO)
+ lpfc_fdmi_tmo_handler(phba);
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->work_hba_events &= ~work_hba_events;
+ spin_unlock_irq(phba->host->host_lock);
+
+ for (i = 0; i < phba->sli.num_rings; i++, ha_copy >>= 4) {
+ pring = &phba->sli.ring[i];
+ if ((ha_copy & HA_RXATT)
+ || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+ if (pring->flag & LPFC_STOP_IOCB_MASK) {
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+ } else {
+ lpfc_sli_handle_slow_ring_event(phba, pring,
+ (ha_copy &
+ HA_RXMASK));
+ pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
+ }
+ /*
+ * Turn on Ring interrupts
+ */
+ spin_lock_irq(phba->host->host_lock);
+ control = readl(phba->HCregaddr);
+ control |= (HC_R0INT_ENA << i);
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ }
+
+ lpfc_work_list_done (phba);
+
+}
+
+static int
+check_work_wait_done(struct lpfc_hba *phba) {
+
+ spin_lock_irq(phba->host->host_lock);
+ if (phba->work_ha ||
+ phba->work_hba_events ||
+ (!list_empty(&phba->work_list)) ||
+ kthread_should_stop()) {
+ spin_unlock_irq(phba->host->host_lock);
+ return 1;
+ } else {
+ spin_unlock_irq(phba->host->host_lock);
+ return 0;
+ }
+}
+
+int
+lpfc_do_work(void *p)
+{
+ struct lpfc_hba *phba = p;
+ int rc;
+ DECLARE_WAIT_QUEUE_HEAD(work_waitq);
+
+ set_user_nice(current, -20);
+ phba->work_wait = &work_waitq;
+
+ while (1) {
+
+ rc = wait_event_interruptible(work_waitq,
+ check_work_wait_done(phba));
+ BUG_ON(rc);
+
+ if (kthread_should_stop())
+ break;
+
+ lpfc_work_done(phba);
+
+ }
+ phba->work_wait = NULL;
+ return 0;
+}
+
+/*
+ * This is only called to handle FC worker events. Since this a rare
+ * occurance, we allocate a struct lpfc_work_evt structure here instead of
+ * embedding it in the IOCB.
+ */
+int
+lpfc_workq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2,
+ uint32_t evt)
+{
+ struct lpfc_work_evt *evtp;
+
+ /*
+ * All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events will
+ * be queued to worker thread for processing
+ */
+ evtp = kmalloc(sizeof(struct lpfc_work_evt), GFP_KERNEL);
+ if (!evtp)
+ return 0;
+
+ evtp->evt_arg1 = arg1;
+ evtp->evt_arg2 = arg2;
+ evtp->evt = evt;
+
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
+ spin_lock_irq(phba->host->host_lock);
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ spin_unlock_irq(phba->host->host_lock);
+
+ return 1;
+}
+
+int
+lpfc_linkdown(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ struct list_head *listp;
+ struct list_head *node_list[7];
+ LPFC_MBOXQ_t *mb;
+ int rc, i;
+
+ psli = &phba->sli;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->hba_state = LPFC_LINK_DOWN;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Clean up any firmware default rpi's */
+ if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
+ lpfc_unreg_did(phba, 0xffffffff, mb);
+ mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl;
+ if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))
+ == MBX_NOT_FINISHED) {
+ mempool_free( mb, phba->mbox_mem_pool);
+ }
+ }
+
+ /* Cleanup any outstanding RSCN activity */
+ lpfc_els_flush_rscn(phba);
+
+ /* Cleanup any outstanding ELS commands */
+ lpfc_els_flush_cmd(phba);
+
+ /* Issue a LINK DOWN event to all nodes */
+ node_list[0] = &phba->fc_npr_list; /* MUST do this list first */
+ node_list[1] = &phba->fc_nlpmap_list;
+ node_list[2] = &phba->fc_nlpunmap_list;
+ node_list[3] = &phba->fc_prli_list;
+ node_list[4] = &phba->fc_reglogin_list;
+ node_list[5] = &phba->fc_adisc_list;
+ node_list[6] = &phba->fc_plogi_list;
+ for (i = 0; i < 7; i++) {
+ listp = node_list[i];
+ if (list_empty(listp))
+ continue;
+
+ list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
+ /* Fabric nodes are not handled thru state machine for
+ link down */
+ if (ndlp->nlp_type & NLP_FABRIC) {
+ /* Remove ALL Fabric nodes except Fabric_DID */
+ if (ndlp->nlp_DID != Fabric_DID) {
+ /* Take it off current list and free */
+ lpfc_nlp_list(phba, ndlp,
+ NLP_NO_LIST);
+ }
+ }
+ else {
+
+ rc = lpfc_disc_state_machine(phba, ndlp, NULL,
+ NLP_EVT_DEVICE_RECOVERY);
+
+ /* Check config parameter use-adisc or FCP-2 */
+ if ((rc != NLP_STE_FREED_NODE) &&
+ (phba->cfg_use_adisc == 0) &&
+ !(ndlp->nlp_fcp_info &
+ NLP_FCP_2_DEVICE)) {
+ /* We know we will have to relogin, so
+ * unreglogin the rpi right now to fail
+ * any outstanding I/Os quickly.
+ */
+ lpfc_unreg_rpi(phba, ndlp);
+ }
+ }
+ }
+ }
+
+ /* free any ndlp's on unused list */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+ nlp_listp) {
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ }
+
+ /* Setup myDID for link up if we are in pt2pt mode */
+ if (phba->fc_flag & FC_PT2PT) {
+ phba->fc_myDID = 0;
+ if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
+ lpfc_config_link(phba, mb);
+ mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl;
+ if (lpfc_sli_issue_mbox
+ (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))
+ == MBX_NOT_FINISHED) {
+ mempool_free( mb, phba->mbox_mem_pool);
+ }
+ }
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI);
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_LBIT;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Turn off discovery timer if its running */
+ lpfc_can_disctmo(phba);
+
+ /* Must process IOCBs on all rings to handle ABORTed I/Os */
+ return (0);
+}
+
+static int
+lpfc_linkup(struct lpfc_hba * phba)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->hba_state = LPFC_LINK_UP;
+ phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY |
+ FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY);
+ phba->fc_flag |= FC_NDISC_ACTIVE;
+ phba->fc_ns_retry = 0;
+ spin_unlock_irq(phba->host->host_lock);
+
+
+ /*
+ * Clean up old Fabric NLP_FABRIC logins.
+ */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list,
+ nlp_listp) {
+ if (ndlp->nlp_DID == Fabric_DID) {
+ /* Take it off current list and free */
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ }
+ }
+
+ /* free any ndlp's on unused list */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+ nlp_listp) {
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ }
+
+ return 0;
+}
+
+/*
+ * This routine handles processing a CLEAR_LA mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+ uint32_t control;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+ /* Since we don't do discovery right now, turn these off here */
+ psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+
+ /* Check for error */
+ if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) {
+ /* CLEAR_LA mbox error <mbxStatus> state <hba_state> */
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "%d:0320 CLEAR_LA mbxStatus error x%x hba "
+ "state x%x\n",
+ phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+ phba->hba_state = LPFC_HBA_ERROR;
+ goto out;
+ }
+
+ if (phba->fc_flag & FC_ABORT_DISCOVERY)
+ goto out;
+
+ phba->num_disc_nodes = 0;
+ /* go thru NPR list and issue ELS PLOGIs */
+ if (phba->fc_npr_cnt) {
+ lpfc_els_disc_plogi(phba);
+ }
+
+ if(!phba->num_disc_nodes) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_NDISC_ACTIVE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+
+ phba->hba_state = LPFC_HBA_READY;
+
+out:
+ /* Device Discovery completes */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0225 Device Discovery completes\n",
+ phba->brd_no);
+
+ mempool_free( pmb, phba->mbox_mem_pool);
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_ABORT_DISCOVERY;
+ if (phba->fc_flag & FC_ESTABLISH_LINK) {
+ phba->fc_flag &= ~FC_ESTABLISH_LINK;
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ del_timer_sync(&phba->fc_estabtmo);
+
+ lpfc_can_disctmo(phba);
+
+ /* turn on Link Attention interrupts */
+ spin_lock_irq(phba->host->host_lock);
+ psli->sli_flag |= LPFC_PROCESS_LA;
+ control = readl(phba->HCregaddr);
+ control |= HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+
+ return;
+}
+
+static void
+lpfc_mbx_cmpl_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+ /* Check for error */
+ if (mb->mbxStatus) {
+ /* CONFIG_LINK mbox error <mbxStatus> state <hba_state> */
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "%d:0306 CONFIG_LINK mbxStatus error x%x "
+ "HBA state x%x\n",
+ phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+ lpfc_linkdown(phba);
+ phba->hba_state = LPFC_HBA_ERROR;
+ goto out;
+ }
+
+ if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ /* If we are public loop and L bit was set */
+ if ((phba->fc_flag & FC_PUBLIC_LOOP) &&
+ !(phba->fc_flag & FC_LBIT)) {
+ /* Need to wait for FAN - use discovery timer
+ * for timeout. hba_state is identically
+ * LPFC_LOCAL_CFG_LINK while waiting for FAN
+ */
+ lpfc_set_disctmo(phba);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return;
+ }
+ }
+
+ /* Start discovery by sending a FLOGI hba_state is identically
+ * LPFC_FLOGI while waiting for FLOGI cmpl
+ */
+ phba->hba_state = LPFC_FLOGI;
+ lpfc_set_disctmo(phba);
+ lpfc_initial_flogi(phba);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return;
+ }
+ if (phba->hba_state == LPFC_FABRIC_CFG_LINK) {
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return;
+ }
+
+out:
+ /* CONFIG_LINK bad hba state <hba_state> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0200 CONFIG_LINK bad hba state x%x\n",
+ phba->brd_no, phba->hba_state);
+
+ if (phba->hba_state != LPFC_CLEAR_LA) {
+ lpfc_clear_la(phba, pmb);
+ pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB))
+ == MBX_NOT_FINISHED) {
+ mempool_free( pmb, phba->mbox_mem_pool);
+ lpfc_disc_flush_list(phba);
+ psli->ring[(psli->ip_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->fcp_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->next_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ phba->hba_state = LPFC_HBA_READY;
+ }
+ } else {
+ mempool_free( pmb, phba->mbox_mem_pool);
+ }
+ return;
+}
+
+static void
+lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ MAILBOX_t *mb = &pmb->mb;
+ struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1;
+
+
+ /* Check for error */
+ if (mb->mbxStatus) {
+ /* READ_SPARAM mbox error <mbxStatus> state <hba_state> */
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "%d:0319 READ_SPARAM mbxStatus error x%x "
+ "hba state x%x>\n",
+ phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+ lpfc_linkdown(phba);
+ phba->hba_state = LPFC_HBA_ERROR;
+ goto out;
+ }
+
+ memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt,
+ sizeof (struct serv_parm));
+ memcpy((uint8_t *) & phba->fc_nodename,
+ (uint8_t *) & phba->fc_sparam.nodeName,
+ sizeof (struct lpfc_name));
+ memcpy((uint8_t *) & phba->fc_portname,
+ (uint8_t *) & phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return;
+
+out:
+ pmb->context1 = NULL;
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ if (phba->hba_state != LPFC_CLEAR_LA) {
+ lpfc_clear_la(phba, pmb);
+ pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB))
+ == MBX_NOT_FINISHED) {
+ mempool_free( pmb, phba->mbox_mem_pool);
+ lpfc_disc_flush_list(phba);
+ psli->ring[(psli->ip_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->fcp_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->next_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ phba->hba_state = LPFC_HBA_READY;
+ }
+ } else {
+ mempool_free( pmb, phba->mbox_mem_pool);
+ }
+ return;
+}
+
+static void
+lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
+{
+ int i;
+ LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox;
+ sparam_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+ spin_lock_irq(phba->host->host_lock);
+ switch(la->UlnkSpeed) {
+ case LA_1GHZ_LINK:
+ phba->fc_linkspeed = LA_1GHZ_LINK;
+ break;
+ case LA_2GHZ_LINK:
+ phba->fc_linkspeed = LA_2GHZ_LINK;
+ break;
+ case LA_4GHZ_LINK:
+ phba->fc_linkspeed = LA_4GHZ_LINK;
+ break;
+ default:
+ phba->fc_linkspeed = LA_UNKNW_LINK;
+ break;
+ }
+
+ phba->fc_topology = la->topology;
+
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ /* Get Loop Map information */
+
+ if (la->il)
+ phba->fc_flag |= FC_LBIT;
+
+ phba->fc_myDID = la->granted_AL_PA;
+ i = la->un.lilpBde64.tus.f.bdeSize;
+
+ if (i == 0) {
+ phba->alpa_map[0] = 0;
+ } else {
+ if (phba->cfg_log_verbose & LOG_LINK_EVENT) {
+ int numalpa, j, k;
+ union {
+ uint8_t pamap[16];
+ struct {
+ uint32_t wd1;
+ uint32_t wd2;
+ uint32_t wd3;
+ uint32_t wd4;
+ } pa;
+ } un;
+ numalpa = phba->alpa_map[0];
+ j = 0;
+ while (j < numalpa) {
+ memset(un.pamap, 0, 16);
+ for (k = 1; j < numalpa; k++) {
+ un.pamap[k - 1] =
+ phba->alpa_map[j + 1];
+ j++;
+ if (k == 16)
+ break;
+ }
+ /* Link Up Event ALPA map */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_LINK_EVENT,
+ "%d:1304 Link Up Event "
+ "ALPA map Data: x%x "
+ "x%x x%x x%x\n",
+ phba->brd_no,
+ un.pa.wd1, un.pa.wd2,
+ un.pa.wd3, un.pa.wd4);
+ }
+ }
+ }
+ } else {
+ phba->fc_myDID = phba->fc_pref_DID;
+ phba->fc_flag |= FC_LBIT;
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ lpfc_linkup(phba);
+ if (sparam_mbox) {
+ lpfc_read_sparam(phba, sparam_mbox);
+ sparam_mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam;
+ lpfc_sli_issue_mbox(phba, sparam_mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ }
+
+ if (cfglink_mbox) {
+ phba->hba_state = LPFC_LOCAL_CFG_LINK;
+ lpfc_config_link(phba, cfglink_mbox);
+ cfglink_mbox->mbox_cmpl = lpfc_mbx_cmpl_config_link;
+ lpfc_sli_issue_mbox(phba, cfglink_mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ }
+}
+
+static void
+lpfc_mbx_issue_link_down(struct lpfc_hba *phba) {
+ uint32_t control;
+ struct lpfc_sli *psli = &phba->sli;
+
+ lpfc_linkdown(phba);
+
+ /* turn on Link Attention interrupts - no CLEAR_LA needed */
+ spin_lock_irq(phba->host->host_lock);
+ psli->sli_flag |= LPFC_PROCESS_LA;
+ control = readl(phba->HCregaddr);
+ control |= HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+}
+
+/*
+ * This routine handles processing a READ_LA mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ READ_LA_VAR *la;
+ MAILBOX_t *mb = &pmb->mb;
+ struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+ /* Check for error */
+ if (mb->mbxStatus) {
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_LINK_EVENT,
+ "%d:1307 READ_LA mbox error x%x state x%x\n",
+ phba->brd_no,
+ mb->mbxStatus, phba->hba_state);
+ lpfc_mbx_issue_link_down(phba);
+ phba->hba_state = LPFC_HBA_ERROR;
+ goto lpfc_mbx_cmpl_read_la_free_mbuf;
+ }
+
+ la = (READ_LA_VAR *) & pmb->mb.un.varReadLA;
+
+ memcpy(&phba->alpa_map[0], mp->virt, 128);
+
+ if (((phba->fc_eventTag + 1) < la->eventTag) ||
+ (phba->fc_eventTag == la->eventTag)) {
+ phba->fc_stat.LinkMultiEvent++;
+ if (la->attType == AT_LINK_UP) {
+ if (phba->fc_eventTag != 0)
+ lpfc_linkdown(phba);
+ }
+ }
+
+ phba->fc_eventTag = la->eventTag;
+
+ if (la->attType == AT_LINK_UP) {
+ phba->fc_stat.LinkUp++;
+ lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+ "%d:1303 Link Up Event x%x received "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no, la->eventTag, phba->fc_eventTag,
+ la->granted_AL_PA, la->UlnkSpeed,
+ phba->alpa_map[0]);
+ lpfc_mbx_process_link_up(phba, la);
+ } else {
+ phba->fc_stat.LinkDown++;
+ lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+ "%d:1305 Link Down Event x%x received "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, la->eventTag, phba->fc_eventTag,
+ phba->hba_state, phba->fc_flag);
+ lpfc_mbx_issue_link_down(phba);
+ }
+
+lpfc_mbx_cmpl_read_la_free_mbuf:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return;
+}
+
+/*
+ * This routine handles processing a REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+
+ ndlp = (struct lpfc_nodelist *) pmb->context2;
+ mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+ pmb->context1 = NULL;
+
+ /* Good status, call state machine */
+ lpfc_disc_state_machine(phba, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN);
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+
+ return;
+}
+
+/*
+ * This routine handles processing a Fabric REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *ndlp_fdmi;
+
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+
+ ndlp = (struct lpfc_nodelist *) pmb->context2;
+ mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+ if (mb->mbxStatus) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+
+ /* FLOGI failed, so just use loop map to make discovery list */
+ lpfc_disc_list_loopmap(phba);
+
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ return;
+ }
+
+ pmb->context1 = NULL;
+
+ if (ndlp->nlp_rpi != 0)
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+ ndlp->nlp_rpi = mb->un.varWords[0];
+ lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+ ndlp->nlp_type |= NLP_FABRIC;
+ ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+ if (phba->hba_state == LPFC_FABRIC_CFG_LINK) {
+ /* This NPort has been assigned an NPort_ID by the fabric as a
+ * result of the completed fabric login. Issue a State Change
+ * Registration (SCR) ELS request to the fabric controller
+ * (SCR_DID) so that this NPort gets RSCN events from the
+ * fabric.
+ */
+ lpfc_issue_els_scr(phba, SCR_DID, 0);
+
+ /* Allocate a new node instance. If the pool is empty, just
+ * start the discovery process and skip the Nameserver login
+ * process. This is attempted again later on. Otherwise, issue
+ * a Port Login (PLOGI) to the NameServer
+ */
+ if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL))
+ == 0) {
+ lpfc_disc_start(phba);
+ } else {
+ lpfc_nlp_init(phba, ndlp, NameServer_DID);
+ ndlp->nlp_type |= NLP_FABRIC;
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ if (phba->cfg_fdmi_on) {
+ if ((ndlp_fdmi = mempool_alloc(
+ phba->nlp_mem_pool,
+ GFP_KERNEL))) {
+ lpfc_nlp_init(phba, ndlp_fdmi,
+ FDMI_DID);
+ ndlp_fdmi->nlp_type |= NLP_FABRIC;
+ ndlp_fdmi->nlp_state =
+ NLP_STE_PLOGI_ISSUE;
+ lpfc_issue_els_plogi(phba, ndlp_fdmi,
+ 0);
+ }
+ }
+ }
+ }
+
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+
+ return;
+}
+
+/*
+ * This routine handles processing a NameServer REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+
+ ndlp = (struct lpfc_nodelist *) pmb->context2;
+ mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+ if (mb->mbxStatus) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+
+ /* RegLogin failed, so just use loop map to make discovery
+ list */
+ lpfc_disc_list_loopmap(phba);
+
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ return;
+ }
+
+ pmb->context1 = NULL;
+
+ if (ndlp->nlp_rpi != 0)
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+ ndlp->nlp_rpi = mb->un.varWords[0];
+ lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+ ndlp->nlp_type |= NLP_FABRIC;
+ ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+ if (phba->hba_state < LPFC_HBA_READY) {
+ /* Link up discovery requires Fabrib registration. */
+ lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID);
+ lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN);
+ lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID);
+ }
+
+ phba->fc_ns_retry = 0;
+ /* Good status, issue CT Request to NameServer */
+ if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT)) {
+ /* Cannot issue NameServer Query, so finish up discovery */
+ lpfc_disc_start(phba);
+ }
+
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+
+ return;
+}
+
+static void
+lpfc_register_remote_port(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp)
+{
+ struct fc_rport *rport;
+ struct lpfc_rport_data *rdata;
+ struct fc_rport_identifiers rport_ids;
+ uint64_t wwn;
+
+ /* Remote port has reappeared. Re-register w/ FC transport */
+ memcpy(&wwn, &ndlp->nlp_nodename, sizeof(uint64_t));
+ rport_ids.node_name = be64_to_cpu(wwn);
+ memcpy(&wwn, &ndlp->nlp_portname, sizeof(uint64_t));
+ rport_ids.port_name = be64_to_cpu(wwn);
+ rport_ids.port_id = ndlp->nlp_DID;
+ rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ if (ndlp->nlp_type & NLP_FCP_TARGET)
+ rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ if (ndlp->nlp_type & NLP_FCP_INITIATOR)
+ rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+
+ ndlp->rport = rport = fc_remote_port_add(phba->host, 0, &rport_ids);
+ if (!rport) {
+ dev_printk(KERN_WARNING, &phba->pcidev->dev,
+ "Warning: fc_remote_port_add failed\n");
+ return;
+ }
+
+ /* initialize static port data */
+ rport->maxframe_size = ndlp->nlp_maxframe;
+ rport->supported_classes = ndlp->nlp_class_sup;
+ if ((rport->scsi_target_id != -1) &&
+ (rport->scsi_target_id < MAX_FCP_TARGET)) {
+ ndlp->nlp_sid = rport->scsi_target_id;
+ }
+ rdata = rport->dd_data;
+ rdata->pnode = ndlp;
+
+ return;
+}
+
+int
+lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
+{
+ enum { none, unmapped, mapped } rport_add = none, rport_del = none;
+ struct lpfc_sli *psli;
+
+ psli = &phba->sli;
+ /* Sanity check to ensure we are not moving to / from the same list */
+ if ((nlp->nlp_flag & NLP_LIST_MASK) == list) {
+ if (list != NLP_NO_LIST)
+ return(0);
+ }
+
+ switch(nlp->nlp_flag & NLP_LIST_MASK) {
+ case NLP_NO_LIST: /* Not on any list */
+ break;
+ case NLP_UNUSED_LIST:
+ phba->fc_unused_cnt--;
+ list_del(&nlp->nlp_listp);
+ break;
+ case NLP_PLOGI_LIST:
+ phba->fc_plogi_cnt--;
+ list_del(&nlp->nlp_listp);
+ break;
+ case NLP_ADISC_LIST:
+ phba->fc_adisc_cnt--;
+ list_del(&nlp->nlp_listp);
+ break;
+ case NLP_REGLOGIN_LIST:
+ phba->fc_reglogin_cnt--;
+ list_del(&nlp->nlp_listp);
+ break;
+ case NLP_PRLI_LIST:
+ phba->fc_prli_cnt--;
+ list_del(&nlp->nlp_listp);
+ break;
+ case NLP_UNMAPPED_LIST:
+ phba->fc_unmap_cnt--;
+ list_del(&nlp->nlp_listp);
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID;
+ nlp->nlp_type &= ~NLP_FC_NODE;
+ spin_unlock_irq(phba->host->host_lock);
+ phba->nport_event_cnt++;
+ if (nlp->rport)
+ rport_del = unmapped;
+ break;
+ case NLP_MAPPED_LIST:
+ phba->fc_map_cnt--;
+ list_del(&nlp->nlp_listp);
+ phba->nport_event_cnt++;
+ if (nlp->rport)
+ rport_del = mapped;
+ break;
+ case NLP_NPR_LIST:
+ phba->fc_npr_cnt--;
+ list_del(&nlp->nlp_listp);
+ /* Stop delay tmo if taking node off NPR list */
+ if ((nlp->nlp_flag & NLP_DELAY_TMO) &&
+ (list != NLP_NPR_LIST)) {
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag &= ~NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&nlp->nlp_delayfunc);
+ if (!list_empty(&nlp->els_retry_evt.evt_listp))
+ list_del_init(&nlp->els_retry_evt.evt_listp);
+ }
+ break;
+ }
+
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag &= ~NLP_LIST_MASK;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Add NPort <did> to <num> list */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_NODE,
+ "%d:0904 Add NPort x%x to %d list Data: x%x\n",
+ phba->brd_no,
+ nlp->nlp_DID, list, nlp->nlp_flag);
+
+ switch(list) {
+ case NLP_NO_LIST: /* No list, just remove it */
+ lpfc_nlp_remove(phba, nlp);
+ break;
+ case NLP_UNUSED_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the unused list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list);
+ phba->fc_unused_cnt++;
+ break;
+ case NLP_PLOGI_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the plogi list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list);
+ phba->fc_plogi_cnt++;
+ break;
+ case NLP_ADISC_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the adisc list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list);
+ phba->fc_adisc_cnt++;
+ break;
+ case NLP_REGLOGIN_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the reglogin list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list);
+ phba->fc_reglogin_cnt++;
+ break;
+ case NLP_PRLI_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the prli list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list);
+ phba->fc_prli_cnt++;
+ break;
+ case NLP_UNMAPPED_LIST:
+ rport_add = unmapped;
+ /* ensure all vestiges of "mapped" significance are gone */
+ nlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the unmap list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list);
+ phba->fc_unmap_cnt++;
+ phba->nport_event_cnt++;
+ /* stop nodev tmo if running */
+ if (nlp->nlp_flag & NLP_NODEV_TMO) {
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag &= ~NLP_NODEV_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&nlp->nlp_tmofunc);
+ if (!list_empty(&nlp->nodev_timeout_evt.evt_listp))
+ list_del_init(&nlp->nodev_timeout_evt.
+ evt_listp);
+
+ }
+ nlp->nlp_type |= NLP_FC_NODE;
+ break;
+ case NLP_MAPPED_LIST:
+ rport_add = mapped;
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the map list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list);
+ phba->fc_map_cnt++;
+ phba->nport_event_cnt++;
+ /* stop nodev tmo if running */
+ if (nlp->nlp_flag & NLP_NODEV_TMO) {
+ nlp->nlp_flag &= ~NLP_NODEV_TMO;
+ del_timer_sync(&nlp->nlp_tmofunc);
+ if (!list_empty(&nlp->nodev_timeout_evt.evt_listp))
+ list_del_init(&nlp->nodev_timeout_evt.
+ evt_listp);
+
+ }
+ break;
+ case NLP_NPR_LIST:
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= list;
+ spin_unlock_irq(phba->host->host_lock);
+ /* Put it at the end of the npr list */
+ list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list);
+ phba->fc_npr_cnt++;
+
+ /*
+ * Sanity check for Fabric entity.
+ * Set nodev_tmo for NPR state, for Fabric use 1 sec.
+ */
+ if (nlp->nlp_type & NLP_FABRIC) {
+ mod_timer(&nlp->nlp_tmofunc, jiffies + HZ);
+ }
+ else {
+ mod_timer(&nlp->nlp_tmofunc,
+ jiffies + HZ * phba->cfg_nodev_tmo);
+ }
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= NLP_NODEV_TMO;
+ nlp->nlp_flag &= ~NLP_RCV_PLOGI;
+ spin_unlock_irq(phba->host->host_lock);
+ break;
+ case NLP_JUST_DQ:
+ break;
+ }
+
+ /*
+ * We make all the calls into the transport after we have
+ * moved the node between lists. This so that we don't
+ * release the lock while in-between lists.
+ */
+
+ /* Don't upcall midlayer if we're unloading */
+ if (!(phba->fc_flag & FC_UNLOADING)) {
+ /*
+ * We revalidate the rport pointer as the "add" function
+ * may have removed the remote port.
+ */
+ if ((rport_del != none) && nlp->rport)
+ fc_remote_port_block(nlp->rport);
+
+ if (rport_add != none) {
+ /*
+ * Tell the fc transport about the port, if we haven't
+ * already. If we have, and it's a scsi entity, be
+ * sure to unblock any attached scsi devices
+ */
+ if (!nlp->rport)
+ lpfc_register_remote_port(phba, nlp);
+ else
+ fc_remote_port_unblock(nlp->rport);
+
+ /*
+ * if we added to Mapped list, but the remote port
+ * registration failed or assigned a target id outside
+ * our presentable range - move the node to the
+ * Unmapped List
+ */
+ if ((rport_add == mapped) &&
+ ((!nlp->rport) ||
+ (nlp->rport->scsi_target_id == -1) ||
+ (nlp->rport->scsi_target_id >= MAX_FCP_TARGET))) {
+ nlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ spin_lock_irq(phba->host->host_lock);
+ nlp->nlp_flag |= NLP_TGT_NO_SCSIID;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_nlp_list(phba, nlp, NLP_UNMAPPED_LIST);
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Start / ReStart rescue timer for Discovery / RSCN handling
+ */
+void
+lpfc_set_disctmo(struct lpfc_hba * phba)
+{
+ uint32_t tmo;
+
+ tmo = ((phba->fc_ratov * 2) + 1);
+
+ mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo);
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_DISC_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Start Discovery Timer state <hba_state> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0247 Start Discovery Timer state x%x "
+ "Data: x%x x%lx x%x x%x\n",
+ phba->brd_no,
+ phba->hba_state, tmo, (unsigned long)&phba->fc_disctmo,
+ phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+ return;
+}
+
+/*
+ * Cancel rescue timer for Discovery / RSCN handling
+ */
+int
+lpfc_can_disctmo(struct lpfc_hba * phba)
+{
+ /* Turn off discovery timer if its running */
+ if (phba->fc_flag & FC_DISC_TMO) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_DISC_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&phba->fc_disctmo);
+ phba->work_hba_events &= ~WORKER_DISC_TMO;
+ }
+
+ /* Cancel Discovery Timer state <hba_state> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0248 Cancel Discovery Timer state x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->hba_state, phba->fc_flag,
+ phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+ return (0);
+}
+
+/*
+ * Check specified ring for outstanding IOCB on the SLI queue
+ * Return true if iocb matches the specified nport
+ */
+int
+lpfc_check_sli_ndlp(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * iocb, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_sli *psli;
+ IOCB_t *icmd;
+
+ psli = &phba->sli;
+ icmd = &iocb->iocb;
+ if (pring->ringno == LPFC_ELS_RING) {
+ switch (icmd->ulpCommand) {
+ case CMD_GEN_REQUEST64_CR:
+ if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi)
+ return (1);
+ case CMD_ELS_REQUEST64_CR:
+ case CMD_XMIT_ELS_RSP64_CX:
+ if (iocb->context1 == (uint8_t *) ndlp)
+ return (1);
+ }
+ } else if (pring->ringno == psli->ip_ring) {
+
+ } else if (pring->ringno == psli->fcp_ring) {
+ /* Skip match check if waiting to relogin to FCP target */
+ if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
+ (ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ return (0);
+ }
+ if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) {
+ return (1);
+ }
+ } else if (pring->ringno == psli->next_ring) {
+
+ }
+ return (0);
+}
+
+/*
+ * Free resources / clean up outstanding I/Os
+ * associated with nlp_rpi in the LPFC_NODELIST entry.
+ */
+static int
+lpfc_no_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ IOCB_t *icmd;
+ uint32_t rpi, i;
+
+ /*
+ * Everything that matches on txcmplq will be returned
+ * by firmware with a no rpi error.
+ */
+ psli = &phba->sli;
+ rpi = ndlp->nlp_rpi;
+ if (rpi) {
+ /* Now process each ring */
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->ring[i];
+
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq,
+ list) {
+ /*
+ * Check to see if iocb matches the nport we are
+ * looking for
+ */
+ if ((lpfc_check_sli_ndlp
+ (phba, pring, iocb, ndlp))) {
+ /* It matches, so deque and call compl
+ with an error */
+ list_del(&iocb->list);
+ pring->txq_cnt--;
+ if (iocb->iocb_cmpl) {
+ icmd = &iocb->iocb;
+ icmd->ulpStatus =
+ IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] =
+ IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->
+ host_lock);
+ (iocb->iocb_cmpl) (phba,
+ iocb, iocb);
+ spin_lock_irq(phba->host->
+ host_lock);
+ } else {
+ list_add_tail(&iocb->list,
+ &phba->lpfc_iocb_list);
+ }
+ }
+ }
+ spin_unlock_irq(phba->host->host_lock);
+
+ }
+ }
+ return (0);
+}
+
+/*
+ * Free rpi associated with LPFC_NODELIST entry.
+ * This routine is called from lpfc_freenode(), when we are removing
+ * a LPFC_NODELIST entry. It is also called if the driver initiates a
+ * LOGO that completes successfully, and we are waiting to PLOGI back
+ * to the remote NPort. In addition, it is called after we receive
+ * and unsolicated ELS cmd, send back a rsp, the rsp completes and
+ * we are waiting to PLOGI back to the remote NPort.
+ */
+int
+lpfc_unreg_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+
+ if (ndlp->nlp_rpi) {
+ if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
+ lpfc_unreg_login(phba, ndlp->nlp_rpi, mbox);
+ mbox->mbox_cmpl=lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox
+ (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED)
+ mempool_free( mbox, phba->mbox_mem_pool);
+ }
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+ lpfc_no_rpi(phba, ndlp);
+ ndlp->nlp_rpi = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Free resources associated with LPFC_NODELIST entry
+ * so it can be freed.
+ */
+static int
+lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+ LPFC_MBOXQ_t *mb;
+ LPFC_MBOXQ_t *nextmb;
+ struct lpfc_dmabuf *mp;
+ struct fc_rport *rport;
+
+ /* Cleanup node for NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0900 Cleanup node for NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, ndlp->nlp_rpi);
+
+ lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ);
+
+ /*
+ * if unloading the driver - just leave the remote port in place.
+ * The driver unload will force the attached devices to detach
+ * and flush cache's w/o generating flush errors.
+ */
+ if ((ndlp->rport) && !(phba->fc_flag & FC_UNLOADING)) {
+ rport = ndlp->rport;
+ ndlp->rport = NULL;
+ fc_remote_port_unblock(rport);
+ fc_remote_port_delete(rport);
+ ndlp->nlp_sid = NLP_NO_SID;
+ }
+
+ /* cleanup any ndlp on mbox q waiting for reglogin cmpl */
+ if ((mb = phba->sli.mbox_active)) {
+ if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ (ndlp == (struct lpfc_nodelist *) mb->context2)) {
+ mb->context2 = NULL;
+ mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ }
+ }
+ list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
+ if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ (ndlp == (struct lpfc_nodelist *) mb->context2)) {
+ mp = (struct lpfc_dmabuf *) (mb->context1);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ list_del(&mb->list);
+ mempool_free(mb, phba->mbox_mem_pool);
+ }
+ }
+
+ lpfc_els_abort(phba,ndlp,0);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~(NLP_NODEV_TMO|NLP_DELAY_TMO);
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&ndlp->nlp_tmofunc);
+
+ del_timer_sync(&ndlp->nlp_delayfunc);
+
+ if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp))
+ list_del_init(&ndlp->nodev_timeout_evt.evt_listp);
+ if (!list_empty(&ndlp->els_retry_evt.evt_listp))
+ list_del_init(&ndlp->els_retry_evt.evt_listp);
+
+ lpfc_unreg_rpi(phba, ndlp);
+
+ return (0);
+}
+
+/*
+ * Check to see if we can free the nlp back to the freelist.
+ * If we are in the middle of using the nlp in the discovery state
+ * machine, defer the free till we reach the end of the state machine.
+ */
+int
+lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+ if (ndlp->nlp_flag & NLP_NODEV_TMO) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NODEV_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&ndlp->nlp_tmofunc);
+ if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp))
+ list_del_init(&ndlp->nodev_timeout_evt.evt_listp);
+
+ }
+
+
+ if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&ndlp->nlp_delayfunc);
+ if (!list_empty(&ndlp->els_retry_evt.evt_listp))
+ list_del_init(&ndlp->els_retry_evt.evt_listp);
+ }
+
+ if (ndlp->nlp_disc_refcnt) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_REMOVE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ else {
+ lpfc_freenode(phba, ndlp);
+ mempool_free( ndlp, phba->nlp_mem_pool);
+ }
+ return(0);
+}
+
+static int
+lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did)
+{
+ D_ID mydid;
+ D_ID ndlpdid;
+ D_ID matchdid;
+
+ if (did == Bcast_DID)
+ return (0);
+
+ if (ndlp->nlp_DID == 0) {
+ return (0);
+ }
+
+ /* First check for Direct match */
+ if (ndlp->nlp_DID == did)
+ return (1);
+
+ /* Next check for area/domain identically equals 0 match */
+ mydid.un.word = phba->fc_myDID;
+ if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) {
+ return (0);
+ }
+
+ matchdid.un.word = did;
+ ndlpdid.un.word = ndlp->nlp_DID;
+ if (matchdid.un.b.id == ndlpdid.un.b.id) {
+ if ((mydid.un.b.domain == matchdid.un.b.domain) &&
+ (mydid.un.b.area == matchdid.un.b.area)) {
+ if ((ndlpdid.un.b.domain == 0) &&
+ (ndlpdid.un.b.area == 0)) {
+ if (ndlpdid.un.b.id)
+ return (1);
+ }
+ return (0);
+ }
+
+ matchdid.un.word = ndlp->nlp_DID;
+ if ((mydid.un.b.domain == ndlpdid.un.b.domain) &&
+ (mydid.un.b.area == ndlpdid.un.b.area)) {
+ if ((matchdid.un.b.domain == 0) &&
+ (matchdid.un.b.area == 0)) {
+ if (matchdid.un.b.id)
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+/* Search for a nodelist entry on a specific list */
+struct lpfc_nodelist *
+lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ uint32_t data1;
+
+ if (order & NLP_SEARCH_UNMAPPED) {
+ list_for_each_entry_safe(ndlp, next_ndlp,
+ &phba->fc_nlpunmap_list, nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* FIND node DID unmapped */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0929 FIND node DID unmapped"
+ " Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_MAPPED) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* FIND node DID mapped */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0930 FIND node DID mapped "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_PLOGI) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to PLOGI */
+ /* FIND node DID plogi */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0908 FIND node DID plogi "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_ADISC) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to ADISC */
+ /* FIND node DID adisc */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0931 FIND node DID adisc "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_REGLOGIN) {
+ list_for_each_entry_safe(ndlp, next_ndlp,
+ &phba->fc_reglogin_list, nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to REGLOGIN */
+ /* FIND node DID reglogin */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0931 FIND node DID reglogin"
+ " Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_PRLI) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to PRLI */
+ /* FIND node DID prli */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0931 FIND node DID prli "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_NPR) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to NPR */
+ /* FIND node DID npr */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0931 FIND node DID npr "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ if (order & NLP_SEARCH_UNUSED) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+ nlp_listp) {
+ if (lpfc_matchdid(phba, ndlp, did)) {
+
+ data1 = (((uint32_t) ndlp->nlp_state << 24) |
+ ((uint32_t) ndlp->nlp_xri << 16) |
+ ((uint32_t) ndlp->nlp_type << 8) |
+ ((uint32_t) ndlp->nlp_rpi & 0xff));
+ /* LOG change to UNUSED */
+ /* FIND node DID unused */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+ "%d:0931 FIND node DID unused "
+ "Data: x%p x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1);
+ return (ndlp);
+ }
+ }
+ }
+
+ /* FIND node did <did> NOT FOUND */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_NODE,
+ "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n",
+ phba->brd_no, did, order);
+
+ /* no match found */
+ return NULL;
+}
+
+struct lpfc_nodelist *
+lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did)
+{
+ struct lpfc_nodelist *ndlp;
+ uint32_t flg;
+
+ if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) {
+ if ((phba->hba_state == LPFC_HBA_READY) &&
+ ((lpfc_rscn_payload_check(phba, did) == 0)))
+ return NULL;
+ ndlp = (struct lpfc_nodelist *)
+ mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ if (!ndlp)
+ return NULL;
+ lpfc_nlp_init(phba, ndlp, did);
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ return ndlp;
+ }
+ if ((phba->hba_state == LPFC_HBA_READY) &&
+ (phba->fc_flag & FC_RSCN_MODE)) {
+ if (lpfc_rscn_payload_check(phba, did)) {
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ }
+ else {
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ ndlp = NULL;
+ }
+ }
+ else {
+ flg = ndlp->nlp_flag & NLP_LIST_MASK;
+ if ((flg == NLP_ADISC_LIST) ||
+ (flg == NLP_PLOGI_LIST)) {
+ return NULL;
+ }
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ }
+ return ndlp;
+}
+
+/* Build a list of nodes to discover based on the loopmap */
+void
+lpfc_disc_list_loopmap(struct lpfc_hba * phba)
+{
+ int j;
+ uint32_t alpa, index;
+
+ if (phba->hba_state <= LPFC_LINK_DOWN) {
+ return;
+ }
+ if (phba->fc_topology != TOPOLOGY_LOOP) {
+ return;
+ }
+
+ /* Check for loop map present or not */
+ if (phba->alpa_map[0]) {
+ for (j = 1; j <= phba->alpa_map[0]; j++) {
+ alpa = phba->alpa_map[j];
+
+ if (((phba->fc_myDID & 0xff) == alpa) || (alpa == 0)) {
+ continue;
+ }
+ lpfc_setup_disc_node(phba, alpa);
+ }
+ } else {
+ /* No alpamap, so try all alpa's */
+ for (j = 0; j < FC_MAXLOOP; j++) {
+ /* If cfg_scan_down is set, start from highest
+ * ALPA (0xef) to lowest (0x1).
+ */
+ if (phba->cfg_scan_down)
+ index = j;
+ else
+ index = FC_MAXLOOP - j - 1;
+ alpa = lpfcAlpaArray[index];
+ if ((phba->fc_myDID & 0xff) == alpa) {
+ continue;
+ }
+
+ lpfc_setup_disc_node(phba, alpa);
+ }
+ }
+ return;
+}
+
+/* Start Link up / RSCN discovery on NPR list */
+void
+lpfc_disc_start(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli;
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ uint32_t did_changed, num_sent;
+ uint32_t clear_la_pending;
+ int rc;
+
+ psli = &phba->sli;
+
+ if (phba->hba_state <= LPFC_LINK_DOWN) {
+ return;
+ }
+ if (phba->hba_state == LPFC_CLEAR_LA)
+ clear_la_pending = 1;
+ else
+ clear_la_pending = 0;
+
+ if (phba->hba_state < LPFC_HBA_READY) {
+ phba->hba_state = LPFC_DISC_AUTH;
+ }
+ lpfc_set_disctmo(phba);
+
+ if (phba->fc_prevDID == phba->fc_myDID) {
+ did_changed = 0;
+ } else {
+ did_changed = 1;
+ }
+ phba->fc_prevDID = phba->fc_myDID;
+ phba->num_disc_nodes = 0;
+
+ /* Start Discovery state <hba_state> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0202 Start Discovery hba state x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->hba_state, phba->fc_flag,
+ phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+ /* If our did changed, we MUST do PLOGI */
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+ nlp_listp) {
+ if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
+ if (did_changed) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ }
+ }
+
+ /* First do ADISCs - if any */
+ num_sent = lpfc_els_disc_adisc(phba);
+
+ if (num_sent)
+ return;
+
+ if ((phba->hba_state < LPFC_HBA_READY) && (!clear_la_pending)) {
+ /* If we get here, there is nothing to ADISC */
+ if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
+ phba->hba_state = LPFC_CLEAR_LA;
+ lpfc_clear_la(phba, mbox);
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ rc = lpfc_sli_issue_mbox(phba, mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ lpfc_disc_flush_list(phba);
+ psli->ring[(psli->ip_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->fcp_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->next_ring)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ phba->hba_state = LPFC_HBA_READY;
+ }
+ }
+ } else {
+ /* Next do PLOGIs - if any */
+ num_sent = lpfc_els_disc_plogi(phba);
+
+ if (num_sent)
+ return;
+
+ if (phba->fc_flag & FC_RSCN_MODE) {
+ /* Check to see if more RSCNs came in while we
+ * were processing this one.
+ */
+ if ((phba->fc_rscn_id_cnt == 0) &&
+ (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_RSCN_MODE;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ else
+ lpfc_els_handle_rscn(phba);
+ }
+ }
+ return;
+}
+
+/*
+ * Ignore completion for all IOCBs on tx and txcmpl queue for ELS
+ * ring the match the sppecified nodelist.
+ */
+static void
+lpfc_free_tx(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+ struct lpfc_sli *psli;
+ IOCB_t *icmd;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_dmabuf *mp;
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING];
+
+ /* Error matching iocb on txq or txcmplq
+ * First check the txq.
+ */
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ if (iocb->context1 != ndlp) {
+ continue;
+ }
+ icmd = &iocb->iocb;
+ if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) ||
+ (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) {
+
+ list_del(&iocb->list);
+ pring->txq_cnt--;
+ lpfc_els_free_iocb(phba, iocb);
+ }
+ }
+
+ /* Next check the txcmplq */
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ if (iocb->context1 != ndlp) {
+ continue;
+ }
+ icmd = &iocb->iocb;
+ if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) ||
+ (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) {
+
+ iocb->iocb_cmpl = NULL;
+ /* context2 = cmd, context2->next = rsp, context3 =
+ bpl */
+ if (iocb->context2) {
+ /* Free the response IOCB before handling the
+ command. */
+
+ mp = (struct lpfc_dmabuf *) (iocb->context2);
+ mp = list_get_first(&mp->list,
+ struct lpfc_dmabuf,
+ list);
+ if (mp) {
+ /* Delay before releasing rsp buffer to
+ * give UNREG mbox a chance to take
+ * effect.
+ */
+ list_add(&mp->list,
+ &phba->freebufList);
+ }
+ lpfc_mbuf_free(phba,
+ ((struct lpfc_dmabuf *)
+ iocb->context2)->virt,
+ ((struct lpfc_dmabuf *)
+ iocb->context2)->phys);
+ kfree(iocb->context2);
+ }
+
+ if (iocb->context3) {
+ lpfc_mbuf_free(phba,
+ ((struct lpfc_dmabuf *)
+ iocb->context3)->virt,
+ ((struct lpfc_dmabuf *)
+ iocb->context3)->phys);
+ kfree(iocb->context3);
+ }
+ }
+ }
+
+ return;
+}
+
+void
+lpfc_disc_flush_list(struct lpfc_hba * phba)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ if (phba->fc_plogi_cnt) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+ nlp_listp) {
+ lpfc_free_tx(phba, ndlp);
+ lpfc_nlp_remove(phba, ndlp);
+ }
+ }
+ if (phba->fc_adisc_cnt) {
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+ nlp_listp) {
+ lpfc_free_tx(phba, ndlp);
+ lpfc_nlp_remove(phba, ndlp);
+ }
+ }
+ return;
+}
+
+/*****************************************************************************/
+/*
+ * NAME: lpfc_disc_timeout
+ *
+ * FUNCTION: Fibre Channel driver discovery timeout routine.
+ *
+ * EXECUTION ENVIRONMENT: interrupt only
+ *
+ * CALLED FROM:
+ * Timer function
+ *
+ * RETURNS:
+ * none
+ */
+/*****************************************************************************/
+void
+lpfc_disc_timeout(unsigned long ptr)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+ unsigned long flags = 0;
+
+ if (unlikely(!phba))
+ return;
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ if (!(phba->work_hba_events & WORKER_DISC_TMO)) {
+ phba->work_hba_events |= WORKER_DISC_TMO;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+ return;
+}
+
+static void
+lpfc_disc_timeout_handler(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_nodelist *ndlp;
+ LPFC_MBOXQ_t *clearlambox, *initlinkmbox;
+ int rc, clrlaerr = 0;
+
+ if (unlikely(!phba))
+ return;
+
+ if (!(phba->fc_flag & FC_DISC_TMO))
+ return;
+
+ psli = &phba->sli;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_DISC_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+
+ switch (phba->hba_state) {
+
+ case LPFC_LOCAL_CFG_LINK:
+ /* hba_state is identically LPFC_LOCAL_CFG_LINK while waiting for FAN */
+ /* FAN timeout */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_DISCOVERY,
+ "%d:0221 FAN timeout\n",
+ phba->brd_no);
+
+ /* Forget about FAN, Start discovery by sending a FLOGI
+ * hba_state is identically LPFC_FLOGI while waiting for FLOGI
+ * cmpl
+ */
+ phba->hba_state = LPFC_FLOGI;
+ lpfc_set_disctmo(phba);
+ lpfc_initial_flogi(phba);
+ break;
+
+ case LPFC_FLOGI:
+ /* hba_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */
+ /* Initial FLOGI timeout */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0222 Initial FLOGI timeout\n",
+ phba->brd_no);
+
+ /* Assume no Fabric and go on with discovery.
+ * Check for outstanding ELS FLOGI to abort.
+ */
+
+ /* FLOGI failed, so just use loop map to make discovery list */
+ lpfc_disc_list_loopmap(phba);
+
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ break;
+
+ case LPFC_FABRIC_CFG_LINK:
+ /* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for
+ NameServer login */
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0223 Timeout while waiting for NameServer "
+ "login\n", phba->brd_no);
+
+ /* Next look for NameServer ndlp */
+ ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID);
+ if (ndlp)
+ lpfc_nlp_remove(phba, ndlp);
+ /* Start discovery */
+ lpfc_disc_start(phba);
+ break;
+
+ case LPFC_NS_QRY:
+ /* Check for wait for NameServer Rsp timeout */
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0224 NameServer Query timeout "
+ "Data: x%x x%x\n",
+ phba->brd_no,
+ phba->fc_ns_retry, LPFC_MAX_NS_RETRY);
+
+ ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+ NameServer_DID);
+ if (ndlp) {
+ if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) {
+ /* Try it one more time */
+ rc = lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT);
+ if (rc == 0)
+ break;
+ }
+ phba->fc_ns_retry = 0;
+ }
+
+ /* Nothing to authenticate, so CLEAR_LA right now */
+ clearlambox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!clearlambox) {
+ clrlaerr = 1;
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0226 Device Discovery "
+ "completion error\n",
+ phba->brd_no);
+ phba->hba_state = LPFC_HBA_ERROR;
+ break;
+ }
+
+ phba->hba_state = LPFC_CLEAR_LA;
+ lpfc_clear_la(phba, clearlambox);
+ clearlambox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ rc = lpfc_sli_issue_mbox(phba, clearlambox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(clearlambox, phba->mbox_mem_pool);
+ clrlaerr = 1;
+ break;
+ }
+
+ /* Setup and issue mailbox INITIALIZE LINK command */
+ initlinkmbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!initlinkmbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0226 Device Discovery "
+ "completion error\n",
+ phba->brd_no);
+ phba->hba_state = LPFC_HBA_ERROR;
+ break;
+ }
+
+ lpfc_linkdown(phba);
+ lpfc_init_link(phba, initlinkmbox, phba->cfg_topology,
+ phba->cfg_link_speed);
+ initlinkmbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+ rc = lpfc_sli_issue_mbox(phba, initlinkmbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED)
+ mempool_free(initlinkmbox, phba->mbox_mem_pool);
+
+ break;
+
+ case LPFC_DISC_AUTH:
+ /* Node Authentication timeout */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0227 Node Authentication timeout\n",
+ phba->brd_no);
+ lpfc_disc_flush_list(phba);
+ clearlambox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!clearlambox) {
+ clrlaerr = 1;
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "%d:0226 Device Discovery "
+ "completion error\n",
+ phba->brd_no);
+ phba->hba_state = LPFC_HBA_ERROR;
+ break;
+ }
+ phba->hba_state = LPFC_CLEAR_LA;
+ lpfc_clear_la(phba, clearlambox);
+ clearlambox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+ rc = lpfc_sli_issue_mbox(phba, clearlambox,
+ (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(clearlambox, phba->mbox_mem_pool);
+ clrlaerr = 1;
+ }
+ break;
+
+ case LPFC_CLEAR_LA:
+ /* CLEAR LA timeout */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0228 CLEAR LA timeout\n",
+ phba->brd_no);
+ clrlaerr = 1;
+ break;
+
+ case LPFC_HBA_READY:
+ if (phba->fc_flag & FC_RSCN_MODE) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0231 RSCN timeout Data: x%x x%x\n",
+ phba->brd_no,
+ phba->fc_ns_retry, LPFC_MAX_NS_RETRY);
+
+ /* Cleanup any outstanding ELS commands */
+ lpfc_els_flush_cmd(phba);
+
+ lpfc_els_flush_rscn(phba);
+ lpfc_disc_flush_list(phba);
+ }
+ break;
+ }
+
+ if (clrlaerr) {
+ lpfc_disc_flush_list(phba);
+ psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+ phba->hba_state = LPFC_HBA_READY;
+ }
+
+ return;
+}
+
+static void
+lpfc_nodev_timeout(unsigned long ptr)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+ unsigned long iflag;
+ struct lpfc_work_evt *evtp;
+
+ ndlp = (struct lpfc_nodelist *)ptr;
+ phba = ndlp->nlp_phba;
+ evtp = &ndlp->nodev_timeout_evt;
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+
+ if (!list_empty(&evtp->evt_listp)) {
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return;
+ }
+ evtp->evt_arg1 = ndlp;
+ evtp->evt = LPFC_EVT_NODEV_TMO;
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return;
+}
+
+
+/*
+ * This routine handles processing a NameServer REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_nodelist *ndlp;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+
+ ndlp = (struct lpfc_nodelist *) pmb->context2;
+ mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+ pmb->context1 = NULL;
+
+ if (ndlp->nlp_rpi != 0)
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+ ndlp->nlp_rpi = mb->un.varWords[0];
+ lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+ ndlp->nlp_type |= NLP_FABRIC;
+ ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+ /* Start issuing Fabric-Device Management Interface (FDMI)
+ * command to 0xfffffa (FDMI well known port)
+ */
+ if (phba->cfg_fdmi_on == 1) {
+ lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA);
+ } else {
+ /*
+ * Delay issuing FDMI command if fdmi-on=2
+ * (supporting RPA/hostnmae)
+ */
+ mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60);
+ }
+
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mempool_free( pmb, phba->mbox_mem_pool);
+
+ return;
+}
+
+/*
+ * This routine looks up the ndlp hash
+ * table for the given RPI. If rpi found
+ * it return the node list pointer
+ * else return 0.
+ */
+struct lpfc_nodelist *
+lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi)
+{
+ struct lpfc_nodelist *ret;
+
+ ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)];
+ while ((ret != 0) && (ret->nlp_rpi != rpi)) {
+ ret = ret->nlp_rpi_hash_next;
+ }
+ return ret;
+}
+
+/*
+ * This routine looks up the ndlp hash table for the
+ * given RPI. If rpi found it return the node list
+ * pointer else return 0 after deleting the entry
+ * from hash table.
+ */
+struct lpfc_nodelist *
+lpfc_findnode_remove_rpi(struct lpfc_hba * phba, uint16_t rpi)
+{
+ struct lpfc_nodelist *ret, *temp;;
+
+ ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)];
+ if (ret == 0)
+ return NULL;
+
+ if (ret->nlp_rpi == rpi) {
+ phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)] =
+ ret->nlp_rpi_hash_next;
+ ret->nlp_rpi_hash_next = NULL;
+ return ret;
+ }
+
+ while ((ret->nlp_rpi_hash_next != 0) &&
+ (ret->nlp_rpi_hash_next->nlp_rpi != rpi)) {
+ ret = ret->nlp_rpi_hash_next;
+ }
+
+ if (ret->nlp_rpi_hash_next != 0) {
+ temp = ret->nlp_rpi_hash_next;
+ ret->nlp_rpi_hash_next = temp->nlp_rpi_hash_next;
+ temp->nlp_rpi_hash_next = NULL;
+ return temp;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * This routine adds the node list entry to the
+ * ndlp hash table.
+ */
+void
+lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint16_t rpi)
+{
+
+ uint32_t index;
+
+ index = LPFC_RPI_HASH_FUNC(rpi);
+ ndlp->nlp_rpi_hash_next = phba->fc_nlplookup[index];
+ phba->fc_nlplookup[index] = ndlp;
+ return;
+}
+
+void
+lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ uint32_t did)
+{
+ memset(ndlp, 0, sizeof (struct lpfc_nodelist));
+ INIT_LIST_HEAD(&ndlp->nodev_timeout_evt.evt_listp);
+ INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+ init_timer(&ndlp->nlp_tmofunc);
+ ndlp->nlp_tmofunc.function = lpfc_nodev_timeout;
+ ndlp->nlp_tmofunc.data = (unsigned long)ndlp;
+ init_timer(&ndlp->nlp_delayfunc);
+ ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+ ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+ ndlp->nlp_DID = did;
+ ndlp->nlp_phba = phba;
+ ndlp->nlp_sid = NLP_NO_SID;
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
new file mode 100644
index 000000000000..fc958a99dadb
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -0,0 +1,2687 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_hw.h 1.37 2005/03/29 19:51:45EST sf_support Exp $
+ */
+
+#define FDMI_DID 0xfffffaU
+#define NameServer_DID 0xfffffcU
+#define SCR_DID 0xfffffdU
+#define Fabric_DID 0xfffffeU
+#define Bcast_DID 0xffffffU
+#define Mask_DID 0xffffffU
+#define CT_DID_MASK 0xffff00U
+#define Fabric_DID_MASK 0xfff000U
+#define WELL_KNOWN_DID_MASK 0xfffff0U
+
+#define PT2PT_LocalID 1
+#define PT2PT_RemoteID 2
+
+#define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */
+#define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */
+#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */
+#define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */
+
+#define LPFC_BUF_RING0 64 /* Number of buffers to post to RING
+ 0 */
+
+#define FCELSSIZE 1024 /* maximum ELS transfer size */
+
+#define LPFC_FCP_RING 0 /* ring 0 for FCP initiator commands */
+#define LPFC_IP_RING 1 /* ring 1 for IP commands */
+#define LPFC_ELS_RING 2 /* ring 2 for ELS commands */
+#define LPFC_FCP_NEXT_RING 3
+
+#define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */
+#define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */
+#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 IP command ring entries */
+#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 IP response ring entries */
+#define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36 /* SLI-2 extra FCP cmd ring entries */
+#define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52 /* SLI-2 extra FCP rsp ring entries */
+#define SLI2_IOCB_CMD_R2_ENTRIES 20 /* SLI-2 ELS command ring entries */
+#define SLI2_IOCB_RSP_R2_ENTRIES 20 /* SLI-2 ELS response ring entries */
+#define SLI2_IOCB_CMD_R3_ENTRIES 0
+#define SLI2_IOCB_RSP_R3_ENTRIES 0
+#define SLI2_IOCB_CMD_R3XTRA_ENTRIES 24
+#define SLI2_IOCB_RSP_R3XTRA_ENTRIES 32
+
+/* Common Transport structures and definitions */
+
+union CtRevisionId {
+ /* Structure is in Big Endian format */
+ struct {
+ uint32_t Revision:8;
+ uint32_t InId:24;
+ } bits;
+ uint32_t word;
+};
+
+union CtCommandResponse {
+ /* Structure is in Big Endian format */
+ struct {
+ uint32_t CmdRsp:16;
+ uint32_t Size:16;
+ } bits;
+ uint32_t word;
+};
+
+struct lpfc_sli_ct_request {
+ /* Structure is in Big Endian format */
+ union CtRevisionId RevisionId;
+ uint8_t FsType;
+ uint8_t FsSubType;
+ uint8_t Options;
+ uint8_t Rsrvd1;
+ union CtCommandResponse CommandResponse;
+ uint8_t Rsrvd2;
+ uint8_t ReasonCode;
+ uint8_t Explanation;
+ uint8_t VendorUnique;
+
+ union {
+ uint32_t PortID;
+ struct gid {
+ uint8_t PortType; /* for GID_PT requests */
+ uint8_t DomainScope;
+ uint8_t AreaScope;
+ uint8_t Fc4Type; /* for GID_FT requests */
+ } gid;
+ struct rft {
+ uint32_t PortId; /* For RFT_ID requests */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd0:16;
+ uint32_t rsvd1:7;
+ uint32_t fcpReg:1; /* Type 8 */
+ uint32_t rsvd2:2;
+ uint32_t ipReg:1; /* Type 5 */
+ uint32_t rsvd3:5;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t rsvd0:16;
+ uint32_t fcpReg:1; /* Type 8 */
+ uint32_t rsvd1:7;
+ uint32_t rsvd3:5;
+ uint32_t ipReg:1; /* Type 5 */
+ uint32_t rsvd2:2;
+#endif
+
+ uint32_t rsvd[7];
+ } rft;
+ struct rnn {
+ uint32_t PortId; /* For RNN_ID requests */
+ uint8_t wwnn[8];
+ } rnn;
+ struct rsnn { /* For RSNN_ID requests */
+ uint8_t wwnn[8];
+ uint8_t len;
+ uint8_t symbname[255];
+ } rsnn;
+ } un;
+};
+
+#define SLI_CT_REVISION 1
+#define GID_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 260)
+#define RFT_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 228)
+#define RNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 252)
+#define RSNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request))
+
+/*
+ * FsType Definitions
+ */
+
+#define SLI_CT_MANAGEMENT_SERVICE 0xFA
+#define SLI_CT_TIME_SERVICE 0xFB
+#define SLI_CT_DIRECTORY_SERVICE 0xFC
+#define SLI_CT_FABRIC_CONTROLLER_SERVICE 0xFD
+
+/*
+ * Directory Service Subtypes
+ */
+
+#define SLI_CT_DIRECTORY_NAME_SERVER 0x02
+
+/*
+ * Response Codes
+ */
+
+#define SLI_CT_RESPONSE_FS_RJT 0x8001
+#define SLI_CT_RESPONSE_FS_ACC 0x8002
+
+/*
+ * Reason Codes
+ */
+
+#define SLI_CT_NO_ADDITIONAL_EXPL 0x0
+#define SLI_CT_INVALID_COMMAND 0x01
+#define SLI_CT_INVALID_VERSION 0x02
+#define SLI_CT_LOGICAL_ERROR 0x03
+#define SLI_CT_INVALID_IU_SIZE 0x04
+#define SLI_CT_LOGICAL_BUSY 0x05
+#define SLI_CT_PROTOCOL_ERROR 0x07
+#define SLI_CT_UNABLE_TO_PERFORM_REQ 0x09
+#define SLI_CT_REQ_NOT_SUPPORTED 0x0b
+#define SLI_CT_HBA_INFO_NOT_REGISTERED 0x10
+#define SLI_CT_MULTIPLE_HBA_ATTR_OF_SAME_TYPE 0x11
+#define SLI_CT_INVALID_HBA_ATTR_BLOCK_LEN 0x12
+#define SLI_CT_HBA_ATTR_NOT_PRESENT 0x13
+#define SLI_CT_PORT_INFO_NOT_REGISTERED 0x20
+#define SLI_CT_MULTIPLE_PORT_ATTR_OF_SAME_TYPE 0x21
+#define SLI_CT_INVALID_PORT_ATTR_BLOCK_LEN 0x22
+#define SLI_CT_VENDOR_UNIQUE 0xff
+
+/*
+ * Name Server SLI_CT_UNABLE_TO_PERFORM_REQ Explanations
+ */
+
+#define SLI_CT_NO_PORT_ID 0x01
+#define SLI_CT_NO_PORT_NAME 0x02
+#define SLI_CT_NO_NODE_NAME 0x03
+#define SLI_CT_NO_CLASS_OF_SERVICE 0x04
+#define SLI_CT_NO_IP_ADDRESS 0x05
+#define SLI_CT_NO_IPA 0x06
+#define SLI_CT_NO_FC4_TYPES 0x07
+#define SLI_CT_NO_SYMBOLIC_PORT_NAME 0x08
+#define SLI_CT_NO_SYMBOLIC_NODE_NAME 0x09
+#define SLI_CT_NO_PORT_TYPE 0x0A
+#define SLI_CT_ACCESS_DENIED 0x10
+#define SLI_CT_INVALID_PORT_ID 0x11
+#define SLI_CT_DATABASE_EMPTY 0x12
+
+/*
+ * Name Server Command Codes
+ */
+
+#define SLI_CTNS_GA_NXT 0x0100
+#define SLI_CTNS_GPN_ID 0x0112
+#define SLI_CTNS_GNN_ID 0x0113
+#define SLI_CTNS_GCS_ID 0x0114
+#define SLI_CTNS_GFT_ID 0x0117
+#define SLI_CTNS_GSPN_ID 0x0118
+#define SLI_CTNS_GPT_ID 0x011A
+#define SLI_CTNS_GID_PN 0x0121
+#define SLI_CTNS_GID_NN 0x0131
+#define SLI_CTNS_GIP_NN 0x0135
+#define SLI_CTNS_GIPA_NN 0x0136
+#define SLI_CTNS_GSNN_NN 0x0139
+#define SLI_CTNS_GNN_IP 0x0153
+#define SLI_CTNS_GIPA_IP 0x0156
+#define SLI_CTNS_GID_FT 0x0171
+#define SLI_CTNS_GID_PT 0x01A1
+#define SLI_CTNS_RPN_ID 0x0212
+#define SLI_CTNS_RNN_ID 0x0213
+#define SLI_CTNS_RCS_ID 0x0214
+#define SLI_CTNS_RFT_ID 0x0217
+#define SLI_CTNS_RSPN_ID 0x0218
+#define SLI_CTNS_RPT_ID 0x021A
+#define SLI_CTNS_RIP_NN 0x0235
+#define SLI_CTNS_RIPA_NN 0x0236
+#define SLI_CTNS_RSNN_NN 0x0239
+#define SLI_CTNS_DA_ID 0x0300
+
+/*
+ * Port Types
+ */
+
+#define SLI_CTPT_N_PORT 0x01
+#define SLI_CTPT_NL_PORT 0x02
+#define SLI_CTPT_FNL_PORT 0x03
+#define SLI_CTPT_IP 0x04
+#define SLI_CTPT_FCP 0x08
+#define SLI_CTPT_NX_PORT 0x7F
+#define SLI_CTPT_F_PORT 0x81
+#define SLI_CTPT_FL_PORT 0x82
+#define SLI_CTPT_E_PORT 0x84
+
+#define SLI_CT_LAST_ENTRY 0x80000000
+
+/* Fibre Channel Service Parameter definitions */
+
+#define FC_PH_4_0 6 /* FC-PH version 4.0 */
+#define FC_PH_4_1 7 /* FC-PH version 4.1 */
+#define FC_PH_4_2 8 /* FC-PH version 4.2 */
+#define FC_PH_4_3 9 /* FC-PH version 4.3 */
+
+#define FC_PH_LOW 8 /* Lowest supported FC-PH version */
+#define FC_PH_HIGH 9 /* Highest supported FC-PH version */
+#define FC_PH3 0x20 /* FC-PH-3 version */
+
+#define FF_FRAME_SIZE 2048
+
+struct lpfc_name {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t nameType:4; /* FC Word 0, bit 28:31 */
+ uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */
+ uint8_t nameType:4; /* FC Word 0, bit 28:31 */
+#endif
+
+#define NAME_IEEE 0x1 /* IEEE name - nameType */
+#define NAME_IEEE_EXT 0x2 /* IEEE extended name */
+#define NAME_FC_TYPE 0x3 /* FC native name type */
+#define NAME_IP_TYPE 0x4 /* IP address */
+#define NAME_CCITT_TYPE 0xC
+#define NAME_CCITT_GR_TYPE 0xE
+ uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */
+ uint8_t IEEE[6]; /* FC IEEE address */
+};
+
+struct csp {
+ uint8_t fcphHigh; /* FC Word 0, byte 0 */
+ uint8_t fcphLow;
+ uint8_t bbCreditMsb;
+ uint8_t bbCreditlsb; /* FC Word 0, byte 3 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t increasingOffset:1; /* FC Word 1, bit 31 */
+ uint16_t randomOffset:1; /* FC Word 1, bit 30 */
+ uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */
+ uint16_t fPort:1; /* FC Word 1, bit 28 */
+ uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
+ uint16_t edtovResolution:1; /* FC Word 1, bit 26 */
+ uint16_t multicast:1; /* FC Word 1, bit 25 */
+ uint16_t broadcast:1; /* FC Word 1, bit 24 */
+
+ uint16_t huntgroup:1; /* FC Word 1, bit 23 */
+ uint16_t simplex:1; /* FC Word 1, bit 22 */
+ uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */
+ uint16_t dhd:1; /* FC Word 1, bit 18 */
+ uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */
+ uint16_t payloadlength:1; /* FC Word 1, bit 16 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t broadcast:1; /* FC Word 1, bit 24 */
+ uint16_t multicast:1; /* FC Word 1, bit 25 */
+ uint16_t edtovResolution:1; /* FC Word 1, bit 26 */
+ uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
+ uint16_t fPort:1; /* FC Word 1, bit 28 */
+ uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */
+ uint16_t randomOffset:1; /* FC Word 1, bit 30 */
+ uint16_t increasingOffset:1; /* FC Word 1, bit 31 */
+
+ uint16_t payloadlength:1; /* FC Word 1, bit 16 */
+ uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */
+ uint16_t dhd:1; /* FC Word 1, bit 18 */
+ uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */
+ uint16_t simplex:1; /* FC Word 1, bit 22 */
+ uint16_t huntgroup:1; /* FC Word 1, bit 23 */
+#endif
+
+ uint8_t bbRcvSizeMsb; /* Upper nibble is reserved */
+ uint8_t bbRcvSizeLsb; /* FC Word 1, byte 3 */
+ union {
+ struct {
+ uint8_t word2Reserved1; /* FC Word 2 byte 0 */
+
+ uint8_t totalConcurrSeq; /* FC Word 2 byte 1 */
+ uint8_t roByCategoryMsb; /* FC Word 2 byte 2 */
+
+ uint8_t roByCategoryLsb; /* FC Word 2 byte 3 */
+ } nPort;
+ uint32_t r_a_tov; /* R_A_TOV must be in B.E. format */
+ } w2;
+
+ uint32_t e_d_tov; /* E_D_TOV must be in B.E. format */
+};
+
+struct class_parms {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t classValid:1; /* FC Word 0, bit 31 */
+ uint8_t intermix:1; /* FC Word 0, bit 30 */
+ uint8_t stackedXparent:1; /* FC Word 0, bit 29 */
+ uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */
+ uint8_t seqDelivery:1; /* FC Word 0, bit 27 */
+ uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */
+ uint8_t seqDelivery:1; /* FC Word 0, bit 27 */
+ uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */
+ uint8_t stackedXparent:1; /* FC Word 0, bit 29 */
+ uint8_t intermix:1; /* FC Word 0, bit 30 */
+ uint8_t classValid:1; /* FC Word 0, bit 31 */
+
+#endif
+
+ uint8_t word0Reserved2; /* FC Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */
+ uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */
+ uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */
+ uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */
+ uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */
+ uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */
+ uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */
+ uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */
+ uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */
+#endif
+
+ uint8_t word0Reserved4; /* FC Word 0, bit 0: 7 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */
+ uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */
+ uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */
+ uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */
+ uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */
+ uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */
+ uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */
+ uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */
+ uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */
+ uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */
+ uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */
+#endif
+
+ uint8_t word1Reserved2; /* FC Word 1, bit 16:23 */
+ uint8_t rcvDataSizeMsb; /* FC Word 1, bit 8:15 */
+ uint8_t rcvDataSizeLsb; /* FC Word 1, bit 0: 7 */
+
+ uint8_t concurrentSeqMsb; /* FC Word 2, bit 24:31 */
+ uint8_t concurrentSeqLsb; /* FC Word 2, bit 16:23 */
+ uint8_t EeCreditSeqMsb; /* FC Word 2, bit 8:15 */
+ uint8_t EeCreditSeqLsb; /* FC Word 2, bit 0: 7 */
+
+ uint8_t openSeqPerXchgMsb; /* FC Word 3, bit 24:31 */
+ uint8_t openSeqPerXchgLsb; /* FC Word 3, bit 16:23 */
+ uint8_t word3Reserved1; /* Fc Word 3, bit 8:15 */
+ uint8_t word3Reserved2; /* Fc Word 3, bit 0: 7 */
+};
+
+struct serv_parm { /* Structure is in Big Endian format */
+ struct csp cmn;
+ struct lpfc_name portName;
+ struct lpfc_name nodeName;
+ struct class_parms cls1;
+ struct class_parms cls2;
+ struct class_parms cls3;
+ struct class_parms cls4;
+ uint8_t vendorVersion[16];
+};
+
+/*
+ * Extended Link Service LS_COMMAND codes (Payload Word 0)
+ */
+#ifdef __BIG_ENDIAN_BITFIELD
+#define ELS_CMD_MASK 0xffff0000
+#define ELS_RSP_MASK 0xff000000
+#define ELS_CMD_LS_RJT 0x01000000
+#define ELS_CMD_ACC 0x02000000
+#define ELS_CMD_PLOGI 0x03000000
+#define ELS_CMD_FLOGI 0x04000000
+#define ELS_CMD_LOGO 0x05000000
+#define ELS_CMD_ABTX 0x06000000
+#define ELS_CMD_RCS 0x07000000
+#define ELS_CMD_RES 0x08000000
+#define ELS_CMD_RSS 0x09000000
+#define ELS_CMD_RSI 0x0A000000
+#define ELS_CMD_ESTS 0x0B000000
+#define ELS_CMD_ESTC 0x0C000000
+#define ELS_CMD_ADVC 0x0D000000
+#define ELS_CMD_RTV 0x0E000000
+#define ELS_CMD_RLS 0x0F000000
+#define ELS_CMD_ECHO 0x10000000
+#define ELS_CMD_TEST 0x11000000
+#define ELS_CMD_RRQ 0x12000000
+#define ELS_CMD_PRLI 0x20100014
+#define ELS_CMD_PRLO 0x21100014
+#define ELS_CMD_PDISC 0x50000000
+#define ELS_CMD_FDISC 0x51000000
+#define ELS_CMD_ADISC 0x52000000
+#define ELS_CMD_FARP 0x54000000
+#define ELS_CMD_FARPR 0x55000000
+#define ELS_CMD_FAN 0x60000000
+#define ELS_CMD_RSCN 0x61040000
+#define ELS_CMD_SCR 0x62000000
+#define ELS_CMD_RNID 0x78000000
+#else /* __LITTLE_ENDIAN_BITFIELD */
+#define ELS_CMD_MASK 0xffff
+#define ELS_RSP_MASK 0xff
+#define ELS_CMD_LS_RJT 0x01
+#define ELS_CMD_ACC 0x02
+#define ELS_CMD_PLOGI 0x03
+#define ELS_CMD_FLOGI 0x04
+#define ELS_CMD_LOGO 0x05
+#define ELS_CMD_ABTX 0x06
+#define ELS_CMD_RCS 0x07
+#define ELS_CMD_RES 0x08
+#define ELS_CMD_RSS 0x09
+#define ELS_CMD_RSI 0x0A
+#define ELS_CMD_ESTS 0x0B
+#define ELS_CMD_ESTC 0x0C
+#define ELS_CMD_ADVC 0x0D
+#define ELS_CMD_RTV 0x0E
+#define ELS_CMD_RLS 0x0F
+#define ELS_CMD_ECHO 0x10
+#define ELS_CMD_TEST 0x11
+#define ELS_CMD_RRQ 0x12
+#define ELS_CMD_PRLI 0x14001020
+#define ELS_CMD_PRLO 0x14001021
+#define ELS_CMD_PDISC 0x50
+#define ELS_CMD_FDISC 0x51
+#define ELS_CMD_ADISC 0x52
+#define ELS_CMD_FARP 0x54
+#define ELS_CMD_FARPR 0x55
+#define ELS_CMD_FAN 0x60
+#define ELS_CMD_RSCN 0x0461
+#define ELS_CMD_SCR 0x62
+#define ELS_CMD_RNID 0x78
+#endif
+
+/*
+ * LS_RJT Payload Definition
+ */
+
+struct ls_rjt { /* Structure is in Big Endian format */
+ union {
+ uint32_t lsRjtError;
+ struct {
+ uint8_t lsRjtRsvd0; /* FC Word 0, bit 24:31 */
+
+ uint8_t lsRjtRsnCode; /* FC Word 0, bit 16:23 */
+ /* LS_RJT reason codes */
+#define LSRJT_INVALID_CMD 0x01
+#define LSRJT_LOGICAL_ERR 0x03
+#define LSRJT_LOGICAL_BSY 0x05
+#define LSRJT_PROTOCOL_ERR 0x07
+#define LSRJT_UNABLE_TPC 0x09 /* Unable to perform command */
+#define LSRJT_CMD_UNSUPPORTED 0x0B
+#define LSRJT_VENDOR_UNIQUE 0xFF /* See Byte 3 */
+
+ uint8_t lsRjtRsnCodeExp; /* FC Word 0, bit 8:15 */
+ /* LS_RJT reason explanation */
+#define LSEXP_NOTHING_MORE 0x00
+#define LSEXP_SPARM_OPTIONS 0x01
+#define LSEXP_SPARM_ICTL 0x03
+#define LSEXP_SPARM_RCTL 0x05
+#define LSEXP_SPARM_RCV_SIZE 0x07
+#define LSEXP_SPARM_CONCUR_SEQ 0x09
+#define LSEXP_SPARM_CREDIT 0x0B
+#define LSEXP_INVALID_PNAME 0x0D
+#define LSEXP_INVALID_NNAME 0x0E
+#define LSEXP_INVALID_CSP 0x0F
+#define LSEXP_INVALID_ASSOC_HDR 0x11
+#define LSEXP_ASSOC_HDR_REQ 0x13
+#define LSEXP_INVALID_O_SID 0x15
+#define LSEXP_INVALID_OX_RX 0x17
+#define LSEXP_CMD_IN_PROGRESS 0x19
+#define LSEXP_INVALID_NPORT_ID 0x1F
+#define LSEXP_INVALID_SEQ_ID 0x21
+#define LSEXP_INVALID_XCHG 0x23
+#define LSEXP_INACTIVE_XCHG 0x25
+#define LSEXP_RQ_REQUIRED 0x27
+#define LSEXP_OUT_OF_RESOURCE 0x29
+#define LSEXP_CANT_GIVE_DATA 0x2A
+#define LSEXP_REQ_UNSUPPORTED 0x2C
+ uint8_t vendorUnique; /* FC Word 0, bit 0: 7 */
+ } b;
+ } un;
+};
+
+/*
+ * N_Port Login (FLOGO/PLOGO Request) Payload Definition
+ */
+
+typedef struct _LOGO { /* Structure is in Big Endian format */
+ union {
+ uint32_t nPortId32; /* Access nPortId as a word */
+ struct {
+ uint8_t word1Reserved1; /* FC Word 1, bit 31:24 */
+ uint8_t nPortIdByte0; /* N_port ID bit 16:23 */
+ uint8_t nPortIdByte1; /* N_port ID bit 8:15 */
+ uint8_t nPortIdByte2; /* N_port ID bit 0: 7 */
+ } b;
+ } un;
+ struct lpfc_name portName; /* N_port name field */
+} LOGO;
+
+/*
+ * FCP Login (PRLI Request / ACC) Payload Definition
+ */
+
+#define PRLX_PAGE_LEN 0x10
+#define TPRLO_PAGE_LEN 0x14
+
+typedef struct _PRLI { /* Structure is in Big Endian format */
+ uint8_t prliType; /* FC Parm Word 0, bit 24:31 */
+
+#define PRLI_FCP_TYPE 0x08
+ uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */
+ uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */
+ uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */
+
+ /* ACC = imagePairEstablished */
+ uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */
+ uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */
+ uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */
+ uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */
+ uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */
+ uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */
+ /* ACC = imagePairEstablished */
+#endif
+
+#define PRLI_REQ_EXECUTED 0x1 /* acceptRspCode */
+#define PRLI_NO_RESOURCES 0x2
+#define PRLI_INIT_INCOMPLETE 0x3
+#define PRLI_NO_SUCH_PA 0x4
+#define PRLI_PREDEF_CONFIG 0x5
+#define PRLI_PARTIAL_SUCCESS 0x6
+#define PRLI_INVALID_PAGE_CNT 0x7
+ uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */
+
+ uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */
+
+ uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */
+
+ uint8_t word3Reserved1; /* FC Parm Word 3, bit 24:31 */
+ uint8_t word3Reserved2; /* FC Parm Word 3, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */
+ uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */
+ uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */
+ uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */
+ uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */
+ uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */
+ uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */
+ uint16_t Retry:1; /* FC Parm Word 3, bit 8 */
+ uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */
+ uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */
+ uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */
+ uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */
+ uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */
+ uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */
+ uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */
+ uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t Retry:1; /* FC Parm Word 3, bit 8 */
+ uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */
+ uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */
+ uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */
+ uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */
+ uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */
+ uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */
+ uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */
+ uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */
+ uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */
+ uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */
+ uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */
+ uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */
+ uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */
+ uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */
+ uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */
+#endif
+} PRLI;
+
+/*
+ * FCP Logout (PRLO Request / ACC) Payload Definition
+ */
+
+typedef struct _PRLO { /* Structure is in Big Endian format */
+ uint8_t prloType; /* FC Parm Word 0, bit 24:31 */
+
+#define PRLO_FCP_TYPE 0x08
+ uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */
+ uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */
+ uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */
+ uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */
+ uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */
+ uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */
+ uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */
+#endif
+
+#define PRLO_REQ_EXECUTED 0x1 /* acceptRspCode */
+#define PRLO_NO_SUCH_IMAGE 0x4
+#define PRLO_INVALID_PAGE_CNT 0x7
+
+ uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */
+
+ uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */
+
+ uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */
+
+ uint32_t word3Reserved1; /* FC Parm Word 3, bit 0:31 */
+} PRLO;
+
+typedef struct _ADISC { /* Structure is in Big Endian format */
+ uint32_t hardAL_PA;
+ struct lpfc_name portName;
+ struct lpfc_name nodeName;
+ uint32_t DID;
+} ADISC;
+
+typedef struct _FARP { /* Structure is in Big Endian format */
+ uint32_t Mflags:8;
+ uint32_t Odid:24;
+#define FARP_NO_ACTION 0 /* FARP information enclosed, no
+ action */
+#define FARP_MATCH_PORT 0x1 /* Match on Responder Port Name */
+#define FARP_MATCH_NODE 0x2 /* Match on Responder Node Name */
+#define FARP_MATCH_IP 0x4 /* Match on IP address, not supported */
+#define FARP_MATCH_IPV4 0x5 /* Match on IPV4 address, not
+ supported */
+#define FARP_MATCH_IPV6 0x6 /* Match on IPV6 address, not
+ supported */
+ uint32_t Rflags:8;
+ uint32_t Rdid:24;
+#define FARP_REQUEST_PLOGI 0x1 /* Request for PLOGI */
+#define FARP_REQUEST_FARPR 0x2 /* Request for FARP Response */
+ struct lpfc_name OportName;
+ struct lpfc_name OnodeName;
+ struct lpfc_name RportName;
+ struct lpfc_name RnodeName;
+ uint8_t Oipaddr[16];
+ uint8_t Ripaddr[16];
+} FARP;
+
+typedef struct _FAN { /* Structure is in Big Endian format */
+ uint32_t Fdid;
+ struct lpfc_name FportName;
+ struct lpfc_name FnodeName;
+} FAN;
+
+typedef struct _SCR { /* Structure is in Big Endian format */
+ uint8_t resvd1;
+ uint8_t resvd2;
+ uint8_t resvd3;
+ uint8_t Function;
+#define SCR_FUNC_FABRIC 0x01
+#define SCR_FUNC_NPORT 0x02
+#define SCR_FUNC_FULL 0x03
+#define SCR_CLEAR 0xff
+} SCR;
+
+typedef struct _RNID_TOP_DISC {
+ struct lpfc_name portName;
+ uint8_t resvd[8];
+ uint32_t unitType;
+#define RNID_HBA 0x7
+#define RNID_HOST 0xa
+#define RNID_DRIVER 0xd
+ uint32_t physPort;
+ uint32_t attachedNodes;
+ uint16_t ipVersion;
+#define RNID_IPV4 0x1
+#define RNID_IPV6 0x2
+ uint16_t UDPport;
+ uint8_t ipAddr[16];
+ uint16_t resvd1;
+ uint16_t flags;
+#define RNID_TD_SUPPORT 0x1
+#define RNID_LP_VALID 0x2
+} RNID_TOP_DISC;
+
+typedef struct _RNID { /* Structure is in Big Endian format */
+ uint8_t Format;
+#define RNID_TOPOLOGY_DISC 0xdf
+ uint8_t CommonLen;
+ uint8_t resvd1;
+ uint8_t SpecificLen;
+ struct lpfc_name portName;
+ struct lpfc_name nodeName;
+ union {
+ RNID_TOP_DISC topologyDisc; /* topology disc (0xdf) */
+ } un;
+} RNID;
+
+typedef struct _RRQ { /* Structure is in Big Endian format */
+ uint32_t SID;
+ uint16_t Oxid;
+ uint16_t Rxid;
+ uint8_t resv[32]; /* optional association hdr */
+} RRQ;
+
+/* This is used for RSCN command */
+typedef struct _D_ID { /* Structure is in Big Endian format */
+ union {
+ uint32_t word;
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t resv;
+ uint8_t domain;
+ uint8_t area;
+ uint8_t id;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t id;
+ uint8_t area;
+ uint8_t domain;
+ uint8_t resv;
+#endif
+ } b;
+ } un;
+} D_ID;
+
+/*
+ * Structure to define all ELS Payload types
+ */
+
+typedef struct _ELS_PKT { /* Structure is in Big Endian format */
+ uint8_t elsCode; /* FC Word 0, bit 24:31 */
+ uint8_t elsByte1;
+ uint8_t elsByte2;
+ uint8_t elsByte3;
+ union {
+ struct ls_rjt lsRjt; /* Payload for LS_RJT ELS response */
+ struct serv_parm logi; /* Payload for PLOGI/FLOGI/PDISC/ACC */
+ LOGO logo; /* Payload for PLOGO/FLOGO/ACC */
+ PRLI prli; /* Payload for PRLI/ACC */
+ PRLO prlo; /* Payload for PRLO/ACC */
+ ADISC adisc; /* Payload for ADISC/ACC */
+ FARP farp; /* Payload for FARP/ACC */
+ FAN fan; /* Payload for FAN */
+ SCR scr; /* Payload for SCR/ACC */
+ RRQ rrq; /* Payload for RRQ */
+ RNID rnid; /* Payload for RNID */
+ uint8_t pad[128 - 4]; /* Pad out to payload of 128 bytes */
+ } un;
+} ELS_PKT;
+
+/*
+ * FDMI
+ * HBA MAnagement Operations Command Codes
+ */
+#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */
+#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */
+#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */
+#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */
+#define SLI_MGMT_RHBA 0x200 /* Register HBA */
+#define SLI_MGMT_RHAT 0x201 /* Register HBA atttributes */
+#define SLI_MGMT_RPRT 0x210 /* Register Port */
+#define SLI_MGMT_RPA 0x211 /* Register Port attributes */
+#define SLI_MGMT_DHBA 0x300 /* De-register HBA */
+#define SLI_MGMT_DPRT 0x310 /* De-register Port */
+
+/*
+ * Management Service Subtypes
+ */
+#define SLI_CT_FDMI_Subtypes 0x10
+
+/*
+ * HBA Management Service Reject Code
+ */
+#define REJECT_CODE 0x9 /* Unable to perform command request */
+
+/*
+ * HBA Management Service Reject Reason Code
+ * Please refer to the Reason Codes above
+ */
+
+/*
+ * HBA Attribute Types
+ */
+#define NODE_NAME 0x1
+#define MANUFACTURER 0x2
+#define SERIAL_NUMBER 0x3
+#define MODEL 0x4
+#define MODEL_DESCRIPTION 0x5
+#define HARDWARE_VERSION 0x6
+#define DRIVER_VERSION 0x7
+#define OPTION_ROM_VERSION 0x8
+#define FIRMWARE_VERSION 0x9
+#define OS_NAME_VERSION 0xa
+#define MAX_CT_PAYLOAD_LEN 0xb
+
+/*
+ * Port Attrubute Types
+ */
+#define SUPPORTED_FC4_TYPES 0x1
+#define SUPPORTED_SPEED 0x2
+#define PORT_SPEED 0x3
+#define MAX_FRAME_SIZE 0x4
+#define OS_DEVICE_NAME 0x5
+#define HOST_NAME 0x6
+
+union AttributesDef {
+ /* Structure is in Big Endian format */
+ struct {
+ uint32_t AttrType:16;
+ uint32_t AttrLen:16;
+ } bits;
+ uint32_t word;
+};
+
+
+/*
+ * HBA Attribute Entry (8 - 260 bytes)
+ */
+typedef struct {
+ union AttributesDef ad;
+ union {
+ uint32_t VendorSpecific;
+ uint8_t Manufacturer[64];
+ uint8_t SerialNumber[64];
+ uint8_t Model[256];
+ uint8_t ModelDescription[256];
+ uint8_t HardwareVersion[256];
+ uint8_t DriverVersion[256];
+ uint8_t OptionROMVersion[256];
+ uint8_t FirmwareVersion[256];
+ struct lpfc_name NodeName;
+ uint8_t SupportFC4Types[32];
+ uint32_t SupportSpeed;
+ uint32_t PortSpeed;
+ uint32_t MaxFrameSize;
+ uint8_t OsDeviceName[256];
+ uint8_t OsNameVersion[256];
+ uint32_t MaxCTPayloadLen;
+ uint8_t HostName[256];
+ } un;
+} ATTRIBUTE_ENTRY;
+
+/*
+ * HBA Attribute Block
+ */
+typedef struct {
+ uint32_t EntryCnt; /* Number of HBA attribute entries */
+ ATTRIBUTE_ENTRY Entry; /* Variable-length array */
+} ATTRIBUTE_BLOCK;
+
+/*
+ * Port Entry
+ */
+typedef struct {
+ struct lpfc_name PortName;
+} PORT_ENTRY;
+
+/*
+ * HBA Identifier
+ */
+typedef struct {
+ struct lpfc_name PortName;
+} HBA_IDENTIFIER;
+
+/*
+ * Registered Port List Format
+ */
+typedef struct {
+ uint32_t EntryCnt;
+ PORT_ENTRY pe; /* Variable-length array */
+} REG_PORT_LIST;
+
+/*
+ * Register HBA(RHBA)
+ */
+typedef struct {
+ HBA_IDENTIFIER hi;
+ REG_PORT_LIST rpl; /* variable-length array */
+/* ATTRIBUTE_BLOCK ab; */
+} REG_HBA;
+
+/*
+ * Register HBA Attributes (RHAT)
+ */
+typedef struct {
+ struct lpfc_name HBA_PortName;
+ ATTRIBUTE_BLOCK ab;
+} REG_HBA_ATTRIBUTE;
+
+/*
+ * Register Port Attributes (RPA)
+ */
+typedef struct {
+ struct lpfc_name PortName;
+ ATTRIBUTE_BLOCK ab;
+} REG_PORT_ATTRIBUTE;
+
+/*
+ * Get Registered HBA List (GRHL) Accept Payload Format
+ */
+typedef struct {
+ uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */
+ struct lpfc_name HBA_PortName; /* Variable-length array */
+} GRHL_ACC_PAYLOAD;
+
+/*
+ * Get Registered Port List (GRPL) Accept Payload Format
+ */
+typedef struct {
+ uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */
+ PORT_ENTRY Reg_Port_Entry[1]; /* Variable-length array */
+} GRPL_ACC_PAYLOAD;
+
+/*
+ * Get Port Attributes (GPAT) Accept Payload Format
+ */
+
+typedef struct {
+ ATTRIBUTE_BLOCK pab;
+} GPAT_ACC_PAYLOAD;
+
+
+/*
+ * Begin HBA configuration parameters.
+ * The PCI configuration register BAR assignments are:
+ * BAR0, offset 0x10 - SLIM base memory address
+ * BAR1, offset 0x14 - SLIM base memory high address
+ * BAR2, offset 0x18 - REGISTER base memory address
+ * BAR3, offset 0x1c - REGISTER base memory high address
+ * BAR4, offset 0x20 - BIU I/O registers
+ * BAR5, offset 0x24 - REGISTER base io high address
+ */
+
+/* Number of rings currently used and available. */
+#define MAX_CONFIGURED_RINGS 3
+#define MAX_RINGS 4
+
+/* IOCB / Mailbox is owned by FireFly */
+#define OWN_CHIP 1
+
+/* IOCB / Mailbox is owned by Host */
+#define OWN_HOST 0
+
+/* Number of 4-byte words in an IOCB. */
+#define IOCB_WORD_SZ 8
+
+/* defines for type field in fc header */
+#define FC_ELS_DATA 0x1
+#define FC_LLC_SNAP 0x5
+#define FC_FCP_DATA 0x8
+#define FC_COMMON_TRANSPORT_ULP 0x20
+
+/* defines for rctl field in fc header */
+#define FC_DEV_DATA 0x0
+#define FC_UNSOL_CTL 0x2
+#define FC_SOL_CTL 0x3
+#define FC_UNSOL_DATA 0x4
+#define FC_FCP_CMND 0x6
+#define FC_ELS_REQ 0x22
+#define FC_ELS_RSP 0x23
+
+/* network headers for Dfctl field */
+#define FC_NET_HDR 0x20
+
+/* Start FireFly Register definitions */
+#define PCI_VENDOR_ID_EMULEX 0x10df
+#define PCI_DEVICE_ID_FIREFLY 0x1ae5
+#define PCI_DEVICE_ID_SUPERFLY 0xf700
+#define PCI_DEVICE_ID_DRAGONFLY 0xf800
+#define PCI_DEVICE_ID_RFLY 0xf095
+#define PCI_DEVICE_ID_PFLY 0xf098
+#define PCI_DEVICE_ID_TFLY 0xf0a5
+#define PCI_DEVICE_ID_CENTAUR 0xf900
+#define PCI_DEVICE_ID_PEGASUS 0xf980
+#define PCI_DEVICE_ID_THOR 0xfa00
+#define PCI_DEVICE_ID_VIPER 0xfb00
+#define PCI_DEVICE_ID_HELIOS 0xfd00
+#define PCI_DEVICE_ID_BMID 0xf0d5
+#define PCI_DEVICE_ID_BSMB 0xf0d1
+#define PCI_DEVICE_ID_ZEPHYR 0xfe00
+#define PCI_DEVICE_ID_ZMID 0xf0e5
+#define PCI_DEVICE_ID_ZSMB 0xf0e1
+#define PCI_DEVICE_ID_LP101 0xf0a1
+#define PCI_DEVICE_ID_LP10000S 0xfc00
+
+#define JEDEC_ID_ADDRESS 0x0080001c
+#define FIREFLY_JEDEC_ID 0x1ACC
+#define SUPERFLY_JEDEC_ID 0x0020
+#define DRAGONFLY_JEDEC_ID 0x0021
+#define DRAGONFLY_V2_JEDEC_ID 0x0025
+#define CENTAUR_2G_JEDEC_ID 0x0026
+#define CENTAUR_1G_JEDEC_ID 0x0028
+#define PEGASUS_ORION_JEDEC_ID 0x0036
+#define PEGASUS_JEDEC_ID 0x0038
+#define THOR_JEDEC_ID 0x0012
+#define HELIOS_JEDEC_ID 0x0364
+#define ZEPHYR_JEDEC_ID 0x0577
+#define VIPER_JEDEC_ID 0x4838
+
+#define JEDEC_ID_MASK 0x0FFFF000
+#define JEDEC_ID_SHIFT 12
+#define FC_JEDEC_ID(id) ((id & JEDEC_ID_MASK) >> JEDEC_ID_SHIFT)
+
+typedef struct { /* FireFly BIU registers */
+ uint32_t hostAtt; /* See definitions for Host Attention
+ register */
+ uint32_t chipAtt; /* See definitions for Chip Attention
+ register */
+ uint32_t hostStatus; /* See definitions for Host Status register */
+ uint32_t hostControl; /* See definitions for Host Control register */
+ uint32_t buiConfig; /* See definitions for BIU configuration
+ register */
+} FF_REGS;
+
+/* IO Register size in bytes */
+#define FF_REG_AREA_SIZE 256
+
+/* Host Attention Register */
+
+#define HA_REG_OFFSET 0 /* Byte offset from register base address */
+
+#define HA_R0RE_REQ 0x00000001 /* Bit 0 */
+#define HA_R0CE_RSP 0x00000002 /* Bit 1 */
+#define HA_R0ATT 0x00000008 /* Bit 3 */
+#define HA_R1RE_REQ 0x00000010 /* Bit 4 */
+#define HA_R1CE_RSP 0x00000020 /* Bit 5 */
+#define HA_R1ATT 0x00000080 /* Bit 7 */
+#define HA_R2RE_REQ 0x00000100 /* Bit 8 */
+#define HA_R2CE_RSP 0x00000200 /* Bit 9 */
+#define HA_R2ATT 0x00000800 /* Bit 11 */
+#define HA_R3RE_REQ 0x00001000 /* Bit 12 */
+#define HA_R3CE_RSP 0x00002000 /* Bit 13 */
+#define HA_R3ATT 0x00008000 /* Bit 15 */
+#define HA_LATT 0x20000000 /* Bit 29 */
+#define HA_MBATT 0x40000000 /* Bit 30 */
+#define HA_ERATT 0x80000000 /* Bit 31 */
+
+#define HA_RXRE_REQ 0x00000001 /* Bit 0 */
+#define HA_RXCE_RSP 0x00000002 /* Bit 1 */
+#define HA_RXATT 0x00000008 /* Bit 3 */
+#define HA_RXMASK 0x0000000f
+
+/* Chip Attention Register */
+
+#define CA_REG_OFFSET 4 /* Byte offset from register base address */
+
+#define CA_R0CE_REQ 0x00000001 /* Bit 0 */
+#define CA_R0RE_RSP 0x00000002 /* Bit 1 */
+#define CA_R0ATT 0x00000008 /* Bit 3 */
+#define CA_R1CE_REQ 0x00000010 /* Bit 4 */
+#define CA_R1RE_RSP 0x00000020 /* Bit 5 */
+#define CA_R1ATT 0x00000080 /* Bit 7 */
+#define CA_R2CE_REQ 0x00000100 /* Bit 8 */
+#define CA_R2RE_RSP 0x00000200 /* Bit 9 */
+#define CA_R2ATT 0x00000800 /* Bit 11 */
+#define CA_R3CE_REQ 0x00001000 /* Bit 12 */
+#define CA_R3RE_RSP 0x00002000 /* Bit 13 */
+#define CA_R3ATT 0x00008000 /* Bit 15 */
+#define CA_MBATT 0x40000000 /* Bit 30 */
+
+/* Host Status Register */
+
+#define HS_REG_OFFSET 8 /* Byte offset from register base address */
+
+#define HS_MBRDY 0x00400000 /* Bit 22 */
+#define HS_FFRDY 0x00800000 /* Bit 23 */
+#define HS_FFER8 0x01000000 /* Bit 24 */
+#define HS_FFER7 0x02000000 /* Bit 25 */
+#define HS_FFER6 0x04000000 /* Bit 26 */
+#define HS_FFER5 0x08000000 /* Bit 27 */
+#define HS_FFER4 0x10000000 /* Bit 28 */
+#define HS_FFER3 0x20000000 /* Bit 29 */
+#define HS_FFER2 0x40000000 /* Bit 30 */
+#define HS_FFER1 0x80000000 /* Bit 31 */
+#define HS_FFERM 0xFF000000 /* Mask for error bits 31:24 */
+
+/* Host Control Register */
+
+#define HC_REG_OFFSET 12 /* Word offset from register base address */
+
+#define HC_MBINT_ENA 0x00000001 /* Bit 0 */
+#define HC_R0INT_ENA 0x00000002 /* Bit 1 */
+#define HC_R1INT_ENA 0x00000004 /* Bit 2 */
+#define HC_R2INT_ENA 0x00000008 /* Bit 3 */
+#define HC_R3INT_ENA 0x00000010 /* Bit 4 */
+#define HC_INITHBI 0x02000000 /* Bit 25 */
+#define HC_INITMB 0x04000000 /* Bit 26 */
+#define HC_INITFF 0x08000000 /* Bit 27 */
+#define HC_LAINT_ENA 0x20000000 /* Bit 29 */
+#define HC_ERINT_ENA 0x80000000 /* Bit 31 */
+
+/* Mailbox Commands */
+#define MBX_SHUTDOWN 0x00 /* terminate testing */
+#define MBX_LOAD_SM 0x01
+#define MBX_READ_NV 0x02
+#define MBX_WRITE_NV 0x03
+#define MBX_RUN_BIU_DIAG 0x04
+#define MBX_INIT_LINK 0x05
+#define MBX_DOWN_LINK 0x06
+#define MBX_CONFIG_LINK 0x07
+#define MBX_CONFIG_RING 0x09
+#define MBX_RESET_RING 0x0A
+#define MBX_READ_CONFIG 0x0B
+#define MBX_READ_RCONFIG 0x0C
+#define MBX_READ_SPARM 0x0D
+#define MBX_READ_STATUS 0x0E
+#define MBX_READ_RPI 0x0F
+#define MBX_READ_XRI 0x10
+#define MBX_READ_REV 0x11
+#define MBX_READ_LNK_STAT 0x12
+#define MBX_REG_LOGIN 0x13
+#define MBX_UNREG_LOGIN 0x14
+#define MBX_READ_LA 0x15
+#define MBX_CLEAR_LA 0x16
+#define MBX_DUMP_MEMORY 0x17
+#define MBX_DUMP_CONTEXT 0x18
+#define MBX_RUN_DIAGS 0x19
+#define MBX_RESTART 0x1A
+#define MBX_UPDATE_CFG 0x1B
+#define MBX_DOWN_LOAD 0x1C
+#define MBX_DEL_LD_ENTRY 0x1D
+#define MBX_RUN_PROGRAM 0x1E
+#define MBX_SET_MASK 0x20
+#define MBX_SET_SLIM 0x21
+#define MBX_UNREG_D_ID 0x23
+#define MBX_CONFIG_FARP 0x25
+
+#define MBX_LOAD_AREA 0x81
+#define MBX_RUN_BIU_DIAG64 0x84
+#define MBX_CONFIG_PORT 0x88
+#define MBX_READ_SPARM64 0x8D
+#define MBX_READ_RPI64 0x8F
+#define MBX_REG_LOGIN64 0x93
+#define MBX_READ_LA64 0x95
+
+#define MBX_FLASH_WR_ULA 0x98
+#define MBX_SET_DEBUG 0x99
+#define MBX_LOAD_EXP_ROM 0x9C
+
+#define MBX_MAX_CMDS 0x9D
+#define MBX_SLI2_CMD_MASK 0x80
+
+/* IOCB Commands */
+
+#define CMD_RCV_SEQUENCE_CX 0x01
+#define CMD_XMIT_SEQUENCE_CR 0x02
+#define CMD_XMIT_SEQUENCE_CX 0x03
+#define CMD_XMIT_BCAST_CN 0x04
+#define CMD_XMIT_BCAST_CX 0x05
+#define CMD_QUE_RING_BUF_CN 0x06
+#define CMD_QUE_XRI_BUF_CX 0x07
+#define CMD_IOCB_CONTINUE_CN 0x08
+#define CMD_RET_XRI_BUF_CX 0x09
+#define CMD_ELS_REQUEST_CR 0x0A
+#define CMD_ELS_REQUEST_CX 0x0B
+#define CMD_RCV_ELS_REQ_CX 0x0D
+#define CMD_ABORT_XRI_CN 0x0E
+#define CMD_ABORT_XRI_CX 0x0F
+#define CMD_CLOSE_XRI_CN 0x10
+#define CMD_CLOSE_XRI_CX 0x11
+#define CMD_CREATE_XRI_CR 0x12
+#define CMD_CREATE_XRI_CX 0x13
+#define CMD_GET_RPI_CN 0x14
+#define CMD_XMIT_ELS_RSP_CX 0x15
+#define CMD_GET_RPI_CR 0x16
+#define CMD_XRI_ABORTED_CX 0x17
+#define CMD_FCP_IWRITE_CR 0x18
+#define CMD_FCP_IWRITE_CX 0x19
+#define CMD_FCP_IREAD_CR 0x1A
+#define CMD_FCP_IREAD_CX 0x1B
+#define CMD_FCP_ICMND_CR 0x1C
+#define CMD_FCP_ICMND_CX 0x1D
+
+#define CMD_ADAPTER_MSG 0x20
+#define CMD_ADAPTER_DUMP 0x22
+
+/* SLI_2 IOCB Command Set */
+
+#define CMD_RCV_SEQUENCE64_CX 0x81
+#define CMD_XMIT_SEQUENCE64_CR 0x82
+#define CMD_XMIT_SEQUENCE64_CX 0x83
+#define CMD_XMIT_BCAST64_CN 0x84
+#define CMD_XMIT_BCAST64_CX 0x85
+#define CMD_QUE_RING_BUF64_CN 0x86
+#define CMD_QUE_XRI_BUF64_CX 0x87
+#define CMD_IOCB_CONTINUE64_CN 0x88
+#define CMD_RET_XRI_BUF64_CX 0x89
+#define CMD_ELS_REQUEST64_CR 0x8A
+#define CMD_ELS_REQUEST64_CX 0x8B
+#define CMD_ABORT_MXRI64_CN 0x8C
+#define CMD_RCV_ELS_REQ64_CX 0x8D
+#define CMD_XMIT_ELS_RSP64_CX 0x95
+#define CMD_FCP_IWRITE64_CR 0x98
+#define CMD_FCP_IWRITE64_CX 0x99
+#define CMD_FCP_IREAD64_CR 0x9A
+#define CMD_FCP_IREAD64_CX 0x9B
+#define CMD_FCP_ICMND64_CR 0x9C
+#define CMD_FCP_ICMND64_CX 0x9D
+
+#define CMD_GEN_REQUEST64_CR 0xC2
+#define CMD_GEN_REQUEST64_CX 0xC3
+
+#define CMD_MAX_IOCB_CMD 0xE6
+#define CMD_IOCB_MASK 0xff
+
+#define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG
+ iocb */
+#define LPFC_MAX_ADPTMSG 32 /* max msg data */
+/*
+ * Define Status
+ */
+#define MBX_SUCCESS 0
+#define MBXERR_NUM_RINGS 1
+#define MBXERR_NUM_IOCBS 2
+#define MBXERR_IOCBS_EXCEEDED 3
+#define MBXERR_BAD_RING_NUMBER 4
+#define MBXERR_MASK_ENTRIES_RANGE 5
+#define MBXERR_MASKS_EXCEEDED 6
+#define MBXERR_BAD_PROFILE 7
+#define MBXERR_BAD_DEF_CLASS 8
+#define MBXERR_BAD_MAX_RESPONDER 9
+#define MBXERR_BAD_MAX_ORIGINATOR 10
+#define MBXERR_RPI_REGISTERED 11
+#define MBXERR_RPI_FULL 12
+#define MBXERR_NO_RESOURCES 13
+#define MBXERR_BAD_RCV_LENGTH 14
+#define MBXERR_DMA_ERROR 15
+#define MBXERR_ERROR 16
+#define MBX_NOT_FINISHED 255
+
+#define MBX_BUSY 0xffffff /* Attempted cmd to busy Mailbox */
+#define MBX_TIMEOUT 0xfffffe /* time-out expired waiting for */
+
+/*
+ * Begin Structure Definitions for Mailbox Commands
+ */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t tval;
+ uint8_t tmask;
+ uint8_t rval;
+ uint8_t rmask;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t rmask;
+ uint8_t rval;
+ uint8_t tmask;
+ uint8_t tval;
+#endif
+} RR_REG;
+
+struct ulp_bde {
+ uint32_t bdeAddress;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t bdeReserved:4;
+ uint32_t bdeAddrHigh:4;
+ uint32_t bdeSize:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t bdeSize:24;
+ uint32_t bdeAddrHigh:4;
+ uint32_t bdeReserved:4;
+#endif
+};
+
+struct ulp_bde64 { /* SLI-2 */
+ union ULP_BDE_TUS {
+ uint32_t w;
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED
+ VALUE !! */
+ uint32_t bdeSize:24; /* Size of buffer (in bytes) */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t bdeSize:24; /* Size of buffer (in bytes) */
+ uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED
+ VALUE !! */
+#endif
+
+#define BUFF_USE_RSVD 0x01 /* bdeFlags */
+#define BUFF_USE_INTRPT 0x02 /* Not Implemented with LP6000 */
+#define BUFF_USE_CMND 0x04 /* Optional, 1=cmd/rsp 0=data buffer */
+#define BUFF_USE_RCV 0x08 /* "" "", 1=rcv buffer, 0=xmit
+ buffer */
+#define BUFF_TYPE_32BIT 0x10 /* "" "", 1=32 bit addr 0=64 bit
+ addr */
+#define BUFF_TYPE_SPECIAL 0x20 /* Not Implemented with LP6000 */
+#define BUFF_TYPE_BDL 0x40 /* Optional, may be set in BDL */
+#define BUFF_TYPE_INVALID 0x80 /* "" "" */
+ } f;
+ } tus;
+ uint32_t addrLow;
+ uint32_t addrHigh;
+};
+#define BDE64_SIZE_WORD 0
+#define BPL64_SIZE_WORD 0x40
+
+typedef struct ULP_BDL { /* SLI-2 */
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t bdeFlags:8; /* BDL Flags */
+ uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */
+ uint32_t bdeFlags:8; /* BDL Flags */
+#endif
+
+ uint32_t addrLow; /* Address 0:31 */
+ uint32_t addrHigh; /* Address 32:63 */
+ uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */
+} ULP_BDL;
+
+/* Structure for MB Command LOAD_SM and DOWN_LOAD */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd2:25;
+ uint32_t acknowledgment:1;
+ uint32_t version:1;
+ uint32_t erase_or_prog:1;
+ uint32_t update_flash:1;
+ uint32_t update_ram:1;
+ uint32_t method:1;
+ uint32_t load_cmplt:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t load_cmplt:1;
+ uint32_t method:1;
+ uint32_t update_ram:1;
+ uint32_t update_flash:1;
+ uint32_t erase_or_prog:1;
+ uint32_t version:1;
+ uint32_t acknowledgment:1;
+ uint32_t rsvd2:25;
+#endif
+
+ uint32_t dl_to_adr_low;
+ uint32_t dl_to_adr_high;
+ uint32_t dl_len;
+ union {
+ uint32_t dl_from_mbx_offset;
+ struct ulp_bde dl_from_bde;
+ struct ulp_bde64 dl_from_bde64;
+ } un;
+
+} LOAD_SM_VAR;
+
+/* Structure for MB Command READ_NVPARM (02) */
+
+typedef struct {
+ uint32_t rsvd1[3]; /* Read as all one's */
+ uint32_t rsvd2; /* Read as all zero's */
+ uint32_t portname[2]; /* N_PORT name */
+ uint32_t nodename[2]; /* NODE name */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t pref_DID:24;
+ uint32_t hardAL_PA:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t hardAL_PA:8;
+ uint32_t pref_DID:24;
+#endif
+
+ uint32_t rsvd3[21]; /* Read as all one's */
+} READ_NV_VAR;
+
+/* Structure for MB Command WRITE_NVPARMS (03) */
+
+typedef struct {
+ uint32_t rsvd1[3]; /* Must be all one's */
+ uint32_t rsvd2; /* Must be all zero's */
+ uint32_t portname[2]; /* N_PORT name */
+ uint32_t nodename[2]; /* NODE name */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t pref_DID:24;
+ uint32_t hardAL_PA:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t hardAL_PA:8;
+ uint32_t pref_DID:24;
+#endif
+
+ uint32_t rsvd3[21]; /* Must be all one's */
+} WRITE_NV_VAR;
+
+/* Structure for MB Command RUN_BIU_DIAG (04) */
+/* Structure for MB Command RUN_BIU_DIAG64 (0x84) */
+
+typedef struct {
+ uint32_t rsvd1;
+ union {
+ struct {
+ struct ulp_bde xmit_bde;
+ struct ulp_bde rcv_bde;
+ } s1;
+ struct {
+ struct ulp_bde64 xmit_bde64;
+ struct ulp_bde64 rcv_bde64;
+ } s2;
+ } un;
+} BIU_DIAG_VAR;
+
+/* Structure for MB Command INIT_LINK (05) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd1:24;
+ uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */
+ uint32_t rsvd1:24;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */
+ uint8_t rsvd2;
+ uint16_t link_flags;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t link_flags;
+ uint8_t rsvd2;
+ uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */
+#endif
+
+#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */
+#define FLAGS_TOPOLOGY_MODE_LOOP_PT 0x00 /* Attempt loop then pt-pt */
+#define FLAGS_TOPOLOGY_MODE_PT_PT 0x02 /* Attempt pt-pt only */
+#define FLAGS_TOPOLOGY_MODE_LOOP 0x04 /* Attempt loop only */
+#define FLAGS_TOPOLOGY_MODE_PT_LOOP 0x06 /* Attempt pt-pt then loop */
+#define FLAGS_LIRP_LILP 0x80 /* LIRP / LILP is disabled */
+
+#define FLAGS_TOPOLOGY_FAILOVER 0x0400 /* Bit 10 */
+#define FLAGS_LINK_SPEED 0x0800 /* Bit 11 */
+
+ uint32_t link_speed;
+#define LINK_SPEED_AUTO 0 /* Auto selection */
+#define LINK_SPEED_1G 1 /* 1 Gigabaud */
+#define LINK_SPEED_2G 2 /* 2 Gigabaud */
+#define LINK_SPEED_4G 4 /* 4 Gigabaud */
+#define LINK_SPEED_8G 8 /* 4 Gigabaud */
+#define LINK_SPEED_10G 16 /* 10 Gigabaud */
+
+} INIT_LINK_VAR;
+
+/* Structure for MB Command DOWN_LINK (06) */
+
+typedef struct {
+ uint32_t rsvd1;
+} DOWN_LINK_VAR;
+
+/* Structure for MB Command CONFIG_LINK (07) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t cr:1;
+ uint32_t ci:1;
+ uint32_t cr_delay:6;
+ uint32_t cr_count:8;
+ uint32_t rsvd1:8;
+ uint32_t MaxBBC:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t MaxBBC:8;
+ uint32_t rsvd1:8;
+ uint32_t cr_count:8;
+ uint32_t cr_delay:6;
+ uint32_t ci:1;
+ uint32_t cr:1;
+#endif
+
+ uint32_t myId;
+ uint32_t rsvd2;
+ uint32_t edtov;
+ uint32_t arbtov;
+ uint32_t ratov;
+ uint32_t rttov;
+ uint32_t altov;
+ uint32_t crtov;
+ uint32_t citov;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rrq_enable:1;
+ uint32_t rrq_immed:1;
+ uint32_t rsvd4:29;
+ uint32_t ack0_enable:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t ack0_enable:1;
+ uint32_t rsvd4:29;
+ uint32_t rrq_immed:1;
+ uint32_t rrq_enable:1;
+#endif
+} CONFIG_LINK;
+
+/* Structure for MB Command PART_SLIM (08)
+ * will be removed since SLI1 is no longer supported!
+ */
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t offCiocb;
+ uint16_t numCiocb;
+ uint16_t offRiocb;
+ uint16_t numRiocb;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t numCiocb;
+ uint16_t offCiocb;
+ uint16_t numRiocb;
+ uint16_t offRiocb;
+#endif
+} RING_DEF;
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t unused1:24;
+ uint32_t numRing:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t numRing:8;
+ uint32_t unused1:24;
+#endif
+
+ RING_DEF ringdef[4];
+ uint32_t hbainit;
+} PART_SLIM_VAR;
+
+/* Structure for MB Command CONFIG_RING (09) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t unused2:6;
+ uint32_t recvSeq:1;
+ uint32_t recvNotify:1;
+ uint32_t numMask:8;
+ uint32_t profile:8;
+ uint32_t unused1:4;
+ uint32_t ring:4;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t ring:4;
+ uint32_t unused1:4;
+ uint32_t profile:8;
+ uint32_t numMask:8;
+ uint32_t recvNotify:1;
+ uint32_t recvSeq:1;
+ uint32_t unused2:6;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t maxRespXchg;
+ uint16_t maxOrigXchg;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t maxOrigXchg;
+ uint16_t maxRespXchg;
+#endif
+
+ RR_REG rrRegs[6];
+} CONFIG_RING_VAR;
+
+/* Structure for MB Command RESET_RING (10) */
+
+typedef struct {
+ uint32_t ring_no;
+} RESET_RING_VAR;
+
+/* Structure for MB Command READ_CONFIG (11) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t cr:1;
+ uint32_t ci:1;
+ uint32_t cr_delay:6;
+ uint32_t cr_count:8;
+ uint32_t InitBBC:8;
+ uint32_t MaxBBC:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t MaxBBC:8;
+ uint32_t InitBBC:8;
+ uint32_t cr_count:8;
+ uint32_t cr_delay:6;
+ uint32_t ci:1;
+ uint32_t cr:1;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t topology:8;
+ uint32_t myDid:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t myDid:24;
+ uint32_t topology:8;
+#endif
+
+ /* Defines for topology (defined previously) */
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t AR:1;
+ uint32_t IR:1;
+ uint32_t rsvd1:29;
+ uint32_t ack0:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t ack0:1;
+ uint32_t rsvd1:29;
+ uint32_t IR:1;
+ uint32_t AR:1;
+#endif
+
+ uint32_t edtov;
+ uint32_t arbtov;
+ uint32_t ratov;
+ uint32_t rttov;
+ uint32_t altov;
+ uint32_t lmt;
+#define LMT_RESERVED 0x0 /* Not used */
+#define LMT_266_10bit 0x1 /* 265.625 Mbaud 10 bit iface */
+#define LMT_532_10bit 0x2 /* 531.25 Mbaud 10 bit iface */
+#define LMT_1063_20bit 0x3 /* 1062.5 Mbaud 20 bit iface */
+#define LMT_1063_10bit 0x4 /* 1062.5 Mbaud 10 bit iface */
+#define LMT_2125_10bit 0x8 /* 2125 Mbaud 10 bit iface */
+#define LMT_4250_10bit 0x40 /* 4250 Mbaud 10 bit iface */
+
+ uint32_t rsvd2;
+ uint32_t rsvd3;
+ uint32_t max_xri;
+ uint32_t max_iocb;
+ uint32_t max_rpi;
+ uint32_t avail_xri;
+ uint32_t avail_iocb;
+ uint32_t avail_rpi;
+ uint32_t default_rpi;
+} READ_CONFIG_VAR;
+
+/* Structure for MB Command READ_RCONFIG (12) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd2:7;
+ uint32_t recvNotify:1;
+ uint32_t numMask:8;
+ uint32_t profile:8;
+ uint32_t rsvd1:4;
+ uint32_t ring:4;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t ring:4;
+ uint32_t rsvd1:4;
+ uint32_t profile:8;
+ uint32_t numMask:8;
+ uint32_t recvNotify:1;
+ uint32_t rsvd2:7;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t maxResp;
+ uint16_t maxOrig;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t maxOrig;
+ uint16_t maxResp;
+#endif
+
+ RR_REG rrRegs[6];
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t cmdRingOffset;
+ uint16_t cmdEntryCnt;
+ uint16_t rspRingOffset;
+ uint16_t rspEntryCnt;
+ uint16_t nextCmdOffset;
+ uint16_t rsvd3;
+ uint16_t nextRspOffset;
+ uint16_t rsvd4;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t cmdEntryCnt;
+ uint16_t cmdRingOffset;
+ uint16_t rspEntryCnt;
+ uint16_t rspRingOffset;
+ uint16_t rsvd3;
+ uint16_t nextCmdOffset;
+ uint16_t rsvd4;
+ uint16_t nextRspOffset;
+#endif
+} READ_RCONF_VAR;
+
+/* Structure for MB Command READ_SPARM (13) */
+/* Structure for MB Command READ_SPARM64 (0x8D) */
+
+typedef struct {
+ uint32_t rsvd1;
+ uint32_t rsvd2;
+ union {
+ struct ulp_bde sp; /* This BDE points to struct serv_parm
+ structure */
+ struct ulp_bde64 sp64;
+ } un;
+} READ_SPARM_VAR;
+
+/* Structure for MB Command READ_STATUS (14) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd1:31;
+ uint32_t clrCounters:1;
+ uint16_t activeXriCnt;
+ uint16_t activeRpiCnt;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t clrCounters:1;
+ uint32_t rsvd1:31;
+ uint16_t activeRpiCnt;
+ uint16_t activeXriCnt;
+#endif
+
+ uint32_t xmitByteCnt;
+ uint32_t rcvByteCnt;
+ uint32_t xmitFrameCnt;
+ uint32_t rcvFrameCnt;
+ uint32_t xmitSeqCnt;
+ uint32_t rcvSeqCnt;
+ uint32_t totalOrigExchanges;
+ uint32_t totalRespExchanges;
+ uint32_t rcvPbsyCnt;
+ uint32_t rcvFbsyCnt;
+} READ_STATUS_VAR;
+
+/* Structure for MB Command READ_RPI (15) */
+/* Structure for MB Command READ_RPI64 (0x8F) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t nextRpi;
+ uint16_t reqRpi;
+ uint32_t rsvd2:8;
+ uint32_t DID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t reqRpi;
+ uint16_t nextRpi;
+ uint32_t DID:24;
+ uint32_t rsvd2:8;
+#endif
+
+ union {
+ struct ulp_bde sp;
+ struct ulp_bde64 sp64;
+ } un;
+
+} READ_RPI_VAR;
+
+/* Structure for MB Command READ_XRI (16) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t nextXri;
+ uint16_t reqXri;
+ uint16_t rsvd1;
+ uint16_t rpi;
+ uint32_t rsvd2:8;
+ uint32_t DID:24;
+ uint32_t rsvd3:8;
+ uint32_t SID:24;
+ uint32_t rsvd4;
+ uint8_t seqId;
+ uint8_t rsvd5;
+ uint16_t seqCount;
+ uint16_t oxId;
+ uint16_t rxId;
+ uint32_t rsvd6:30;
+ uint32_t si:1;
+ uint32_t exchOrig:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t reqXri;
+ uint16_t nextXri;
+ uint16_t rpi;
+ uint16_t rsvd1;
+ uint32_t DID:24;
+ uint32_t rsvd2:8;
+ uint32_t SID:24;
+ uint32_t rsvd3:8;
+ uint32_t rsvd4;
+ uint16_t seqCount;
+ uint8_t rsvd5;
+ uint8_t seqId;
+ uint16_t rxId;
+ uint16_t oxId;
+ uint32_t exchOrig:1;
+ uint32_t si:1;
+ uint32_t rsvd6:30;
+#endif
+} READ_XRI_VAR;
+
+/* Structure for MB Command READ_REV (17) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t cv:1;
+ uint32_t rr:1;
+ uint32_t rsvd1:29;
+ uint32_t rv:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t rv:1;
+ uint32_t rsvd1:29;
+ uint32_t rr:1;
+ uint32_t cv:1;
+#endif
+
+ uint32_t biuRev;
+ uint32_t smRev;
+ union {
+ uint32_t smFwRev;
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t ProgType;
+ uint8_t ProgId;
+ uint16_t ProgVer:4;
+ uint16_t ProgRev:4;
+ uint16_t ProgFixLvl:2;
+ uint16_t ProgDistType:2;
+ uint16_t DistCnt:4;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t DistCnt:4;
+ uint16_t ProgDistType:2;
+ uint16_t ProgFixLvl:2;
+ uint16_t ProgRev:4;
+ uint16_t ProgVer:4;
+ uint8_t ProgId;
+ uint8_t ProgType;
+#endif
+
+ } b;
+ } un;
+ uint32_t endecRev;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t feaLevelHigh;
+ uint8_t feaLevelLow;
+ uint8_t fcphHigh;
+ uint8_t fcphLow;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t fcphLow;
+ uint8_t fcphHigh;
+ uint8_t feaLevelLow;
+ uint8_t feaLevelHigh;
+#endif
+
+ uint32_t postKernRev;
+ uint32_t opFwRev;
+ uint8_t opFwName[16];
+ uint32_t sli1FwRev;
+ uint8_t sli1FwName[16];
+ uint32_t sli2FwRev;
+ uint8_t sli2FwName[16];
+ uint32_t rsvd2;
+ uint32_t RandomData[7];
+} READ_REV_VAR;
+
+/* Structure for MB Command READ_LINK_STAT (18) */
+
+typedef struct {
+ uint32_t rsvd1;
+ uint32_t linkFailureCnt;
+ uint32_t lossSyncCnt;
+
+ uint32_t lossSignalCnt;
+ uint32_t primSeqErrCnt;
+ uint32_t invalidXmitWord;
+ uint32_t crcCnt;
+ uint32_t primSeqTimeout;
+ uint32_t elasticOverrun;
+ uint32_t arbTimeout;
+} READ_LNK_VAR;
+
+/* Structure for MB Command REG_LOGIN (19) */
+/* Structure for MB Command REG_LOGIN64 (0x93) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t rsvd1;
+ uint16_t rpi;
+ uint32_t rsvd2:8;
+ uint32_t did:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t rpi;
+ uint16_t rsvd1;
+ uint32_t did:24;
+ uint32_t rsvd2:8;
+#endif
+
+ union {
+ struct ulp_bde sp;
+ struct ulp_bde64 sp64;
+ } un;
+
+} REG_LOGIN_VAR;
+
+/* Word 30 contents for REG_LOGIN */
+typedef union {
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t rsvd1:12;
+ uint16_t wd30_class:4;
+ uint16_t xri;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t xri;
+ uint16_t wd30_class:4;
+ uint16_t rsvd1:12;
+#endif
+ } f;
+ uint32_t word;
+} REG_WD30;
+
+/* Structure for MB Command UNREG_LOGIN (20) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t rsvd1;
+ uint16_t rpi;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t rpi;
+ uint16_t rsvd1;
+#endif
+} UNREG_LOGIN_VAR;
+
+/* Structure for MB Command UNREG_D_ID (0x23) */
+
+typedef struct {
+ uint32_t did;
+} UNREG_D_ID_VAR;
+
+/* Structure for MB Command READ_LA (21) */
+/* Structure for MB Command READ_LA64 (0x95) */
+
+typedef struct {
+ uint32_t eventTag; /* Event tag */
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd1:22;
+ uint32_t pb:1;
+ uint32_t il:1;
+ uint32_t attType:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t attType:8;
+ uint32_t il:1;
+ uint32_t pb:1;
+ uint32_t rsvd1:22;
+#endif
+
+#define AT_RESERVED 0x00 /* Reserved - attType */
+#define AT_LINK_UP 0x01 /* Link is up */
+#define AT_LINK_DOWN 0x02 /* Link is down */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t granted_AL_PA;
+ uint8_t lipAlPs;
+ uint8_t lipType;
+ uint8_t topology;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t topology;
+ uint8_t lipType;
+ uint8_t lipAlPs;
+ uint8_t granted_AL_PA;
+#endif
+
+#define TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */
+#define TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */
+
+ union {
+ struct ulp_bde lilpBde; /* This BDE points to a 128 byte buffer
+ to */
+ /* store the LILP AL_PA position map into */
+ struct ulp_bde64 lilpBde64;
+ } un;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t Dlu:1;
+ uint32_t Dtf:1;
+ uint32_t Drsvd2:14;
+ uint32_t DlnkSpeed:8;
+ uint32_t DnlPort:4;
+ uint32_t Dtx:2;
+ uint32_t Drx:2;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t Drx:2;
+ uint32_t Dtx:2;
+ uint32_t DnlPort:4;
+ uint32_t DlnkSpeed:8;
+ uint32_t Drsvd2:14;
+ uint32_t Dtf:1;
+ uint32_t Dlu:1;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t Ulu:1;
+ uint32_t Utf:1;
+ uint32_t Ursvd2:14;
+ uint32_t UlnkSpeed:8;
+ uint32_t UnlPort:4;
+ uint32_t Utx:2;
+ uint32_t Urx:2;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t Urx:2;
+ uint32_t Utx:2;
+ uint32_t UnlPort:4;
+ uint32_t UlnkSpeed:8;
+ uint32_t Ursvd2:14;
+ uint32_t Utf:1;
+ uint32_t Ulu:1;
+#endif
+
+#define LA_UNKNW_LINK 0x0 /* lnkSpeed */
+#define LA_1GHZ_LINK 0x04 /* lnkSpeed */
+#define LA_2GHZ_LINK 0x08 /* lnkSpeed */
+#define LA_4GHZ_LINK 0x10 /* lnkSpeed */
+#define LA_8GHZ_LINK 0x20 /* lnkSpeed */
+#define LA_10GHZ_LINK 0x40 /* lnkSpeed */
+
+} READ_LA_VAR;
+
+/* Structure for MB Command CLEAR_LA (22) */
+
+typedef struct {
+ uint32_t eventTag; /* Event tag */
+ uint32_t rsvd1;
+} CLEAR_LA_VAR;
+
+/* Structure for MB Command DUMP */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd:25;
+ uint32_t ra:1;
+ uint32_t co:1;
+ uint32_t cv:1;
+ uint32_t type:4;
+ uint32_t entry_index:16;
+ uint32_t region_id:16;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t type:4;
+ uint32_t cv:1;
+ uint32_t co:1;
+ uint32_t ra:1;
+ uint32_t rsvd:25;
+ uint32_t region_id:16;
+ uint32_t entry_index:16;
+#endif
+
+ uint32_t rsvd1;
+ uint32_t word_cnt;
+ uint32_t resp_offset;
+} DUMP_VAR;
+
+#define DMP_MEM_REG 0x1
+#define DMP_NV_PARAMS 0x2
+
+#define DMP_REGION_VPD 0xe
+#define DMP_VPD_SIZE 0x400 /* maximum amount of VPD */
+#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */
+#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */
+
+/* Structure for MB Command CONFIG_PORT (0x88) */
+
+typedef struct {
+ uint32_t pcbLen;
+ uint32_t pcbLow; /* bit 31:0 of memory based port config block */
+ uint32_t pcbHigh; /* bit 63:32 of memory based port config block */
+ uint32_t hbainit[5];
+} CONFIG_PORT_VAR;
+
+/* SLI-2 Port Control Block */
+
+/* SLIM POINTER */
+#define SLIMOFF 0x30 /* WORD */
+
+typedef struct _SLI2_RDSC {
+ uint32_t cmdEntries;
+ uint32_t cmdAddrLow;
+ uint32_t cmdAddrHigh;
+
+ uint32_t rspEntries;
+ uint32_t rspAddrLow;
+ uint32_t rspAddrHigh;
+} SLI2_RDSC;
+
+typedef struct _PCB {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t type:8;
+#define TYPE_NATIVE_SLI2 0x01;
+ uint32_t feature:8;
+#define FEATURE_INITIAL_SLI2 0x01;
+ uint32_t rsvd:12;
+ uint32_t maxRing:4;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t maxRing:4;
+ uint32_t rsvd:12;
+ uint32_t feature:8;
+#define FEATURE_INITIAL_SLI2 0x01;
+ uint32_t type:8;
+#define TYPE_NATIVE_SLI2 0x01;
+#endif
+
+ uint32_t mailBoxSize;
+ uint32_t mbAddrLow;
+ uint32_t mbAddrHigh;
+
+ uint32_t hgpAddrLow;
+ uint32_t hgpAddrHigh;
+
+ uint32_t pgpAddrLow;
+ uint32_t pgpAddrHigh;
+ SLI2_RDSC rdsc[MAX_RINGS];
+} PCB_t;
+
+/* NEW_FEATURE */
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd0:27;
+ uint32_t discardFarp:1;
+ uint32_t IPEnable:1;
+ uint32_t nodeName:1;
+ uint32_t portName:1;
+ uint32_t filterEnable:1;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t filterEnable:1;
+ uint32_t portName:1;
+ uint32_t nodeName:1;
+ uint32_t IPEnable:1;
+ uint32_t discardFarp:1;
+ uint32_t rsvd:27;
+#endif
+
+ uint8_t portname[8]; /* Used to be struct lpfc_name */
+ uint8_t nodename[8];
+ uint32_t rsvd1;
+ uint32_t rsvd2;
+ uint32_t rsvd3;
+ uint32_t IPAddress;
+} CONFIG_FARP_VAR;
+
+/* Union of all Mailbox Command types */
+#define MAILBOX_CMD_WSIZE 32
+#define MAILBOX_CMD_SIZE (MAILBOX_CMD_WSIZE * sizeof(uint32_t))
+
+typedef union {
+ uint32_t varWords[MAILBOX_CMD_WSIZE - 1];
+ LOAD_SM_VAR varLdSM; /* cmd = 1 (LOAD_SM) */
+ READ_NV_VAR varRDnvp; /* cmd = 2 (READ_NVPARMS) */
+ WRITE_NV_VAR varWTnvp; /* cmd = 3 (WRITE_NVPARMS) */
+ BIU_DIAG_VAR varBIUdiag; /* cmd = 4 (RUN_BIU_DIAG) */
+ INIT_LINK_VAR varInitLnk; /* cmd = 5 (INIT_LINK) */
+ DOWN_LINK_VAR varDwnLnk; /* cmd = 6 (DOWN_LINK) */
+ CONFIG_LINK varCfgLnk; /* cmd = 7 (CONFIG_LINK) */
+ PART_SLIM_VAR varSlim; /* cmd = 8 (PART_SLIM) */
+ CONFIG_RING_VAR varCfgRing; /* cmd = 9 (CONFIG_RING) */
+ RESET_RING_VAR varRstRing; /* cmd = 10 (RESET_RING) */
+ READ_CONFIG_VAR varRdConfig; /* cmd = 11 (READ_CONFIG) */
+ READ_RCONF_VAR varRdRConfig; /* cmd = 12 (READ_RCONFIG) */
+ READ_SPARM_VAR varRdSparm; /* cmd = 13 (READ_SPARM(64)) */
+ READ_STATUS_VAR varRdStatus; /* cmd = 14 (READ_STATUS) */
+ READ_RPI_VAR varRdRPI; /* cmd = 15 (READ_RPI(64)) */
+ READ_XRI_VAR varRdXRI; /* cmd = 16 (READ_XRI) */
+ READ_REV_VAR varRdRev; /* cmd = 17 (READ_REV) */
+ READ_LNK_VAR varRdLnk; /* cmd = 18 (READ_LNK_STAT) */
+ REG_LOGIN_VAR varRegLogin; /* cmd = 19 (REG_LOGIN(64)) */
+ UNREG_LOGIN_VAR varUnregLogin; /* cmd = 20 (UNREG_LOGIN) */
+ READ_LA_VAR varReadLA; /* cmd = 21 (READ_LA(64)) */
+ CLEAR_LA_VAR varClearLA; /* cmd = 22 (CLEAR_LA) */
+ DUMP_VAR varDmp; /* Warm Start DUMP mbx cmd */
+ UNREG_D_ID_VAR varUnregDID; /* cmd = 0x23 (UNREG_D_ID) */
+ CONFIG_FARP_VAR varCfgFarp; /* cmd = 0x25 (CONFIG_FARP) NEW_FEATURE */
+ CONFIG_PORT_VAR varCfgPort; /* cmd = 0x88 (CONFIG_PORT) */
+} MAILVARIANTS;
+
+/*
+ * SLI-2 specific structures
+ */
+
+typedef struct {
+ uint32_t cmdPutInx;
+ uint32_t rspGetInx;
+} HGP;
+
+typedef struct {
+ uint32_t cmdGetInx;
+ uint32_t rspPutInx;
+} PGP;
+
+typedef struct _SLI2_DESC {
+ HGP host[MAX_RINGS];
+ uint32_t unused1[16];
+ PGP port[MAX_RINGS];
+} SLI2_DESC;
+
+typedef union {
+ SLI2_DESC s2;
+} SLI_VAR;
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t mbxStatus;
+ uint8_t mbxCommand;
+ uint8_t mbxReserved:6;
+ uint8_t mbxHc:1;
+ uint8_t mbxOwner:1; /* Low order bit first word */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t mbxOwner:1; /* Low order bit first word */
+ uint8_t mbxHc:1;
+ uint8_t mbxReserved:6;
+ uint8_t mbxCommand;
+ uint16_t mbxStatus;
+#endif
+
+ MAILVARIANTS un;
+ SLI_VAR us;
+} MAILBOX_t;
+
+/*
+ * Begin Structure Definitions for IOCB Commands
+ */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t statAction;
+ uint8_t statRsn;
+ uint8_t statBaExp;
+ uint8_t statLocalError;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t statLocalError;
+ uint8_t statBaExp;
+ uint8_t statRsn;
+ uint8_t statAction;
+#endif
+ /* statRsn P/F_RJT reason codes */
+#define RJT_BAD_D_ID 0x01 /* Invalid D_ID field */
+#define RJT_BAD_S_ID 0x02 /* Invalid S_ID field */
+#define RJT_UNAVAIL_TEMP 0x03 /* N_Port unavailable temp. */
+#define RJT_UNAVAIL_PERM 0x04 /* N_Port unavailable perm. */
+#define RJT_UNSUP_CLASS 0x05 /* Class not supported */
+#define RJT_DELIM_ERR 0x06 /* Delimiter usage error */
+#define RJT_UNSUP_TYPE 0x07 /* Type not supported */
+#define RJT_BAD_CONTROL 0x08 /* Invalid link conrtol */
+#define RJT_BAD_RCTL 0x09 /* R_CTL invalid */
+#define RJT_BAD_FCTL 0x0A /* F_CTL invalid */
+#define RJT_BAD_OXID 0x0B /* OX_ID invalid */
+#define RJT_BAD_RXID 0x0C /* RX_ID invalid */
+#define RJT_BAD_SEQID 0x0D /* SEQ_ID invalid */
+#define RJT_BAD_DFCTL 0x0E /* DF_CTL invalid */
+#define RJT_BAD_SEQCNT 0x0F /* SEQ_CNT invalid */
+#define RJT_BAD_PARM 0x10 /* Param. field invalid */
+#define RJT_XCHG_ERR 0x11 /* Exchange error */
+#define RJT_PROT_ERR 0x12 /* Protocol error */
+#define RJT_BAD_LENGTH 0x13 /* Invalid Length */
+#define RJT_UNEXPECTED_ACK 0x14 /* Unexpected ACK */
+#define RJT_LOGIN_REQUIRED 0x16 /* Login required */
+#define RJT_TOO_MANY_SEQ 0x17 /* Excessive sequences */
+#define RJT_XCHG_NOT_STRT 0x18 /* Exchange not started */
+#define RJT_UNSUP_SEC_HDR 0x19 /* Security hdr not supported */
+#define RJT_UNAVAIL_PATH 0x1A /* Fabric Path not available */
+#define RJT_VENDOR_UNIQUE 0xFF /* Vendor unique error */
+
+#define IOERR_SUCCESS 0x00 /* statLocalError */
+#define IOERR_MISSING_CONTINUE 0x01
+#define IOERR_SEQUENCE_TIMEOUT 0x02
+#define IOERR_INTERNAL_ERROR 0x03
+#define IOERR_INVALID_RPI 0x04
+#define IOERR_NO_XRI 0x05
+#define IOERR_ILLEGAL_COMMAND 0x06
+#define IOERR_XCHG_DROPPED 0x07
+#define IOERR_ILLEGAL_FIELD 0x08
+#define IOERR_BAD_CONTINUE 0x09
+#define IOERR_TOO_MANY_BUFFERS 0x0A
+#define IOERR_RCV_BUFFER_WAITING 0x0B
+#define IOERR_NO_CONNECTION 0x0C
+#define IOERR_TX_DMA_FAILED 0x0D
+#define IOERR_RX_DMA_FAILED 0x0E
+#define IOERR_ILLEGAL_FRAME 0x0F
+#define IOERR_EXTRA_DATA 0x10
+#define IOERR_NO_RESOURCES 0x11
+#define IOERR_RESERVED 0x12
+#define IOERR_ILLEGAL_LENGTH 0x13
+#define IOERR_UNSUPPORTED_FEATURE 0x14
+#define IOERR_ABORT_IN_PROGRESS 0x15
+#define IOERR_ABORT_REQUESTED 0x16
+#define IOERR_RECEIVE_BUFFER_TIMEOUT 0x17
+#define IOERR_LOOP_OPEN_FAILURE 0x18
+#define IOERR_RING_RESET 0x19
+#define IOERR_LINK_DOWN 0x1A
+#define IOERR_CORRUPTED_DATA 0x1B
+#define IOERR_CORRUPTED_RPI 0x1C
+#define IOERR_OUT_OF_ORDER_DATA 0x1D
+#define IOERR_OUT_OF_ORDER_ACK 0x1E
+#define IOERR_DUP_FRAME 0x1F
+#define IOERR_LINK_CONTROL_FRAME 0x20 /* ACK_N received */
+#define IOERR_BAD_HOST_ADDRESS 0x21
+#define IOERR_RCV_HDRBUF_WAITING 0x22
+#define IOERR_MISSING_HDR_BUFFER 0x23
+#define IOERR_MSEQ_CHAIN_CORRUPTED 0x24
+#define IOERR_ABORTMULT_REQUESTED 0x25
+#define IOERR_BUFFER_SHORTAGE 0x28
+#define IOERR_DEFAULT 0x29
+#define IOERR_CNT 0x2A
+
+#define IOERR_DRVR_MASK 0x100
+#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */
+#define IOERR_SLI_BRESET 0x102
+#define IOERR_SLI_ABORTED 0x103
+} PARM_ERR;
+
+typedef union {
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t Rctl; /* R_CTL field */
+ uint8_t Type; /* TYPE field */
+ uint8_t Dfctl; /* DF_CTL field */
+ uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */
+ uint8_t Dfctl; /* DF_CTL field */
+ uint8_t Type; /* TYPE field */
+ uint8_t Rctl; /* R_CTL field */
+#endif
+
+#define BC 0x02 /* Broadcast Received - Fctl */
+#define SI 0x04 /* Sequence Initiative */
+#define LA 0x08 /* Ignore Link Attention state */
+#define LS 0x80 /* Last Sequence */
+ } hcsw;
+ uint32_t reserved;
+} WORD5;
+
+/* IOCB Command template for a generic response */
+typedef struct {
+ uint32_t reserved[4];
+ PARM_ERR perr;
+} GENERIC_RSP;
+
+/* IOCB Command template for XMIT / XMIT_BCAST / RCV_SEQUENCE / XMIT_ELS */
+typedef struct {
+ struct ulp_bde xrsqbde[2];
+ uint32_t xrsqRo; /* Starting Relative Offset */
+ WORD5 w5; /* Header control/status word */
+} XR_SEQ_FIELDS;
+
+/* IOCB Command template for ELS_REQUEST */
+typedef struct {
+ struct ulp_bde elsReq;
+ struct ulp_bde elsRsp;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t word4Rsvd:7;
+ uint32_t fl:1;
+ uint32_t myID:24;
+ uint32_t word5Rsvd:8;
+ uint32_t remoteID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t myID:24;
+ uint32_t fl:1;
+ uint32_t word4Rsvd:7;
+ uint32_t remoteID:24;
+ uint32_t word5Rsvd:8;
+#endif
+} ELS_REQUEST;
+
+/* IOCB Command template for RCV_ELS_REQ */
+typedef struct {
+ struct ulp_bde elsReq[2];
+ uint32_t parmRo;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t word5Rsvd:8;
+ uint32_t remoteID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t remoteID:24;
+ uint32_t word5Rsvd:8;
+#endif
+} RCV_ELS_REQ;
+
+/* IOCB Command template for ABORT / CLOSE_XRI */
+typedef struct {
+ uint32_t rsvd[3];
+ uint32_t abortType;
+#define ABORT_TYPE_ABTX 0x00000000
+#define ABORT_TYPE_ABTS 0x00000001
+ uint32_t parm;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t abortContextTag; /* ulpContext from command to abort/close */
+ uint16_t abortIoTag; /* ulpIoTag from command to abort/close */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t abortIoTag; /* ulpIoTag from command to abort/close */
+ uint16_t abortContextTag; /* ulpContext from command to abort/close */
+#endif
+} AC_XRI;
+
+/* IOCB Command template for ABORT_MXRI64 */
+typedef struct {
+ uint32_t rsvd[3];
+ uint32_t abortType;
+ uint32_t parm;
+ uint32_t iotag32;
+} A_MXRI64;
+
+/* IOCB Command template for GET_RPI */
+typedef struct {
+ uint32_t rsvd[4];
+ uint32_t parmRo;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t word5Rsvd:8;
+ uint32_t remoteID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t remoteID:24;
+ uint32_t word5Rsvd:8;
+#endif
+} GET_RPI;
+
+/* IOCB Command template for all FCP Initiator commands */
+typedef struct {
+ struct ulp_bde fcpi_cmnd; /* FCP_CMND payload descriptor */
+ struct ulp_bde fcpi_rsp; /* Rcv buffer */
+ uint32_t fcpi_parm;
+ uint32_t fcpi_XRdy; /* transfer ready for IWRITE */
+} FCPI_FIELDS;
+
+/* IOCB Command template for all FCP Target commands */
+typedef struct {
+ struct ulp_bde fcpt_Buffer[2]; /* FCP_CMND payload descriptor */
+ uint32_t fcpt_Offset;
+ uint32_t fcpt_Length; /* transfer ready for IWRITE */
+} FCPT_FIELDS;
+
+/* SLI-2 IOCB structure definitions */
+
+/* IOCB Command template for 64 bit XMIT / XMIT_BCAST / XMIT_ELS */
+typedef struct {
+ ULP_BDL bdl;
+ uint32_t xrsqRo; /* Starting Relative Offset */
+ WORD5 w5; /* Header control/status word */
+} XMT_SEQ_FIELDS64;
+
+/* IOCB Command template for 64 bit RCV_SEQUENCE64 */
+typedef struct {
+ struct ulp_bde64 rcvBde;
+ uint32_t rsvd1;
+ uint32_t xrsqRo; /* Starting Relative Offset */
+ WORD5 w5; /* Header control/status word */
+} RCV_SEQ_FIELDS64;
+
+/* IOCB Command template for ELS_REQUEST64 */
+typedef struct {
+ ULP_BDL bdl;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t word4Rsvd:7;
+ uint32_t fl:1;
+ uint32_t myID:24;
+ uint32_t word5Rsvd:8;
+ uint32_t remoteID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t myID:24;
+ uint32_t fl:1;
+ uint32_t word4Rsvd:7;
+ uint32_t remoteID:24;
+ uint32_t word5Rsvd:8;
+#endif
+} ELS_REQUEST64;
+
+/* IOCB Command template for GEN_REQUEST64 */
+typedef struct {
+ ULP_BDL bdl;
+ uint32_t xrsqRo; /* Starting Relative Offset */
+ WORD5 w5; /* Header control/status word */
+} GEN_REQUEST64;
+
+/* IOCB Command template for RCV_ELS_REQ64 */
+typedef struct {
+ struct ulp_bde64 elsReq;
+ uint32_t rcvd1;
+ uint32_t parmRo;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t word5Rsvd:8;
+ uint32_t remoteID:24;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t remoteID:24;
+ uint32_t word5Rsvd:8;
+#endif
+} RCV_ELS_REQ64;
+
+/* IOCB Command template for all 64 bit FCP Initiator commands */
+typedef struct {
+ ULP_BDL bdl;
+ uint32_t fcpi_parm;
+ uint32_t fcpi_XRdy; /* transfer ready for IWRITE */
+} FCPI_FIELDS64;
+
+/* IOCB Command template for all 64 bit FCP Target commands */
+typedef struct {
+ ULP_BDL bdl;
+ uint32_t fcpt_Offset;
+ uint32_t fcpt_Length; /* transfer ready for IWRITE */
+} FCPT_FIELDS64;
+
+typedef struct _IOCB { /* IOCB structure */
+ union {
+ GENERIC_RSP grsp; /* Generic response */
+ XR_SEQ_FIELDS xrseq; /* XMIT / BCAST / RCV_SEQUENCE cmd */
+ struct ulp_bde cont[3]; /* up to 3 continuation bdes */
+ RCV_ELS_REQ rcvels; /* RCV_ELS_REQ template */
+ AC_XRI acxri; /* ABORT / CLOSE_XRI template */
+ A_MXRI64 amxri; /* abort multiple xri command overlay */
+ GET_RPI getrpi; /* GET_RPI template */
+ FCPI_FIELDS fcpi; /* FCP Initiator template */
+ FCPT_FIELDS fcpt; /* FCP target template */
+
+ /* SLI-2 structures */
+
+ struct ulp_bde64 cont64[2]; /* up to 2 64 bit continuation
+ bde_64s */
+ ELS_REQUEST64 elsreq64; /* ELS_REQUEST template */
+ GEN_REQUEST64 genreq64; /* GEN_REQUEST template */
+ RCV_ELS_REQ64 rcvels64; /* RCV_ELS_REQ template */
+ XMT_SEQ_FIELDS64 xseq64; /* XMIT / BCAST cmd */
+ FCPI_FIELDS64 fcpi64; /* FCP 64 bit Initiator template */
+ FCPT_FIELDS64 fcpt64; /* FCP 64 bit target template */
+
+ uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */
+ } un;
+ union {
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t ulpContext; /* High order bits word 6 */
+ uint16_t ulpIoTag; /* Low order bits word 6 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t ulpIoTag; /* Low order bits word 6 */
+ uint16_t ulpContext; /* High order bits word 6 */
+#endif
+ } t1;
+ struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t ulpContext; /* High order bits word 6 */
+ uint16_t ulpIoTag1:2; /* Low order bits word 6 */
+ uint16_t ulpIoTag0:14; /* Low order bits word 6 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint16_t ulpIoTag0:14; /* Low order bits word 6 */
+ uint16_t ulpIoTag1:2; /* Low order bits word 6 */
+ uint16_t ulpContext; /* High order bits word 6 */
+#endif
+ } t2;
+ } un1;
+#define ulpContext un1.t1.ulpContext
+#define ulpIoTag un1.t1.ulpIoTag
+#define ulpIoTag0 un1.t2.ulpIoTag0
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t ulpTimeout:8;
+ uint32_t ulpXS:1;
+ uint32_t ulpFCP2Rcvy:1;
+ uint32_t ulpPU:2;
+ uint32_t ulpIr:1;
+ uint32_t ulpClass:3;
+ uint32_t ulpCommand:8;
+ uint32_t ulpStatus:4;
+ uint32_t ulpBdeCount:2;
+ uint32_t ulpLe:1;
+ uint32_t ulpOwner:1; /* Low order bit word 7 */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t ulpOwner:1; /* Low order bit word 7 */
+ uint32_t ulpLe:1;
+ uint32_t ulpBdeCount:2;
+ uint32_t ulpStatus:4;
+ uint32_t ulpCommand:8;
+ uint32_t ulpClass:3;
+ uint32_t ulpIr:1;
+ uint32_t ulpPU:2;
+ uint32_t ulpFCP2Rcvy:1;
+ uint32_t ulpXS:1;
+ uint32_t ulpTimeout:8;
+#endif
+
+#define PARM_UNUSED 0 /* PU field (Word 4) not used */
+#define PARM_REL_OFF 1 /* PU field (Word 4) = R. O. */
+#define PARM_READ_CHECK 2 /* PU field (Word 4) = Data Transfer Length */
+#define CLASS1 0 /* Class 1 */
+#define CLASS2 1 /* Class 2 */
+#define CLASS3 2 /* Class 3 */
+#define CLASS_FCP_INTERMIX 7 /* FCP Data->Cls 1, all else->Cls 2 */
+
+#define IOSTAT_SUCCESS 0x0 /* ulpStatus - HBA defined */
+#define IOSTAT_FCP_RSP_ERROR 0x1
+#define IOSTAT_REMOTE_STOP 0x2
+#define IOSTAT_LOCAL_REJECT 0x3
+#define IOSTAT_NPORT_RJT 0x4
+#define IOSTAT_FABRIC_RJT 0x5
+#define IOSTAT_NPORT_BSY 0x6
+#define IOSTAT_FABRIC_BSY 0x7
+#define IOSTAT_INTERMED_RSP 0x8
+#define IOSTAT_LS_RJT 0x9
+#define IOSTAT_BA_RJT 0xA
+#define IOSTAT_RSVD1 0xB
+#define IOSTAT_RSVD2 0xC
+#define IOSTAT_RSVD3 0xD
+#define IOSTAT_RSVD4 0xE
+#define IOSTAT_RSVD5 0xF
+#define IOSTAT_DRIVER_REJECT 0x10 /* ulpStatus - Driver defined */
+#define IOSTAT_DEFAULT 0xF /* Same as rsvd5 for now */
+#define IOSTAT_CNT 0x11
+
+} IOCB_t;
+
+
+#define SLI1_SLIM_SIZE (4 * 1024)
+
+/* Up to 498 IOCBs will fit into 16k
+ * 256 (MAILBOX_t) + 140 (PCB_t) + ( 32 (IOCB_t) * 498 ) = < 16384
+ */
+#define SLI2_SLIM_SIZE (16 * 1024)
+
+/* Maximum IOCBs that will fit in SLI2 slim */
+#define MAX_SLI2_IOCB 498
+
+struct lpfc_sli2_slim {
+ MAILBOX_t mbx;
+ PCB_t pcb;
+ IOCB_t IOCBs[MAX_SLI2_IOCB];
+};
+
+/*******************************************************************
+This macro check PCI device to allow special handling for LC HBAs.
+
+Parameters:
+device : struct pci_dev 's device field
+
+return 1 => TRUE
+ 0 => FALSE
+ *******************************************************************/
+static inline int
+lpfc_is_LC_HBA(unsigned short device)
+{
+ if ((device == PCI_DEVICE_ID_TFLY) ||
+ (device == PCI_DEVICE_ID_PFLY) ||
+ (device == PCI_DEVICE_ID_LP101) ||
+ (device == PCI_DEVICE_ID_BMID) ||
+ (device == PCI_DEVICE_ID_BSMB) ||
+ (device == PCI_DEVICE_ID_ZMID) ||
+ (device == PCI_DEVICE_ID_ZSMB) ||
+ (device == PCI_DEVICE_ID_RFLY))
+ return 1;
+ else
+ return 0;
+}
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
new file mode 100644
index 000000000000..233c912b63ce
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -0,0 +1,1739 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_init.c 1.233 2005/04/13 11:59:09EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_version.h"
+
+static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *);
+static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
+static int lpfc_post_rcv_buf(struct lpfc_hba *);
+
+static struct scsi_transport_template *lpfc_transport_template = NULL;
+static DEFINE_IDR(lpfc_hba_index);
+
+/************************************************************************/
+/* */
+/* lpfc_config_port_prep */
+/* This routine will do LPFC initialization prior to the */
+/* CONFIG_PORT mailbox command. This will be initialized */
+/* as a SLI layer callback routine. */
+/* This routine returns 0 on success or -ERESTART if it wants */
+/* the SLI layer to reset the HBA and try again. Any */
+/* other return value indicates an error. */
+/* */
+/************************************************************************/
+int
+lpfc_config_port_prep(struct lpfc_hba * phba)
+{
+ lpfc_vpd_t *vp = &phba->vpd;
+ int i = 0, rc;
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ char *lpfc_vpd_data = NULL;
+ uint16_t offset = 0;
+ static char licensed[56] =
+ "key unlock for use with gnu public licensed code only\0";
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -ENOMEM;
+ }
+
+ mb = &pmb->mb;
+ phba->hba_state = LPFC_INIT_MBX_CMDS;
+
+ if (lpfc_is_LC_HBA(phba->pcidev->device)) {
+ uint32_t *ptext = (uint32_t *) licensed;
+
+ for (i = 0; i < 56; i += sizeof (uint32_t), ptext++)
+ *ptext = cpu_to_be32(*ptext);
+
+ lpfc_read_nv(phba, pmb);
+ memset((char*)mb->un.varRDnvp.rsvd3, 0,
+ sizeof (mb->un.varRDnvp.rsvd3));
+ memcpy((char*)mb->un.varRDnvp.rsvd3, licensed,
+ sizeof (licensed));
+
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_MBOX,
+ "%d:0324 Config Port initialization "
+ "error, mbxCmd x%x READ_NVPARM, "
+ "mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -ERESTART;
+ }
+ memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename,
+ sizeof (mb->un.varRDnvp.nodename));
+ }
+
+ /* Setup and issue mailbox READ REV command */
+ lpfc_read_rev(phba, pmb);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0439 Adapter failed to init, mbxCmd x%x "
+ "READ_REV, mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return -ERESTART;
+ }
+
+ /* The HBA's current state is provided by the ProgType and rr fields.
+ * Read and check the value of these fields before continuing to config
+ * this port.
+ */
+ if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) {
+ /* Old firmware */
+ vp->rev.rBit = 0;
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0440 Adapter failed to init, mbxCmd x%x "
+ "READ_REV detected outdated firmware"
+ "Data: x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, 0);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -ERESTART;
+ } else {
+ vp->rev.rBit = 1;
+ vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev;
+ memcpy(vp->rev.sli1FwName,
+ (char*)mb->un.varRdRev.sli1FwName, 16);
+ vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev;
+ memcpy(vp->rev.sli2FwName,
+ (char *)mb->un.varRdRev.sli2FwName, 16);
+ }
+
+ /* Save information as VPD data */
+ vp->rev.biuRev = mb->un.varRdRev.biuRev;
+ vp->rev.smRev = mb->un.varRdRev.smRev;
+ vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev;
+ vp->rev.endecRev = mb->un.varRdRev.endecRev;
+ vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh;
+ vp->rev.fcphLow = mb->un.varRdRev.fcphLow;
+ vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh;
+ vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow;
+ vp->rev.postKernRev = mb->un.varRdRev.postKernRev;
+ vp->rev.opFwRev = mb->un.varRdRev.opFwRev;
+
+ if (lpfc_is_LC_HBA(phba->pcidev->device))
+ memcpy(phba->RandomData, (char *)&mb->un.varWords[24],
+ sizeof (phba->RandomData));
+
+ /* Get the default values for Model Name and Description */
+ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
+
+ /* Get adapter VPD information */
+ pmb->context2 = kmalloc(DMP_RSP_SIZE, GFP_KERNEL);
+ if (!pmb->context2)
+ goto out_free_mbox;
+ lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_KERNEL);
+ if (!lpfc_vpd_data)
+ goto out_free_context2;
+
+ do {
+ lpfc_dump_mem(phba, pmb, offset);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "%d:0441 VPD not present on adapter, "
+ "mbxCmd x%x DUMP VPD, mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ kfree(lpfc_vpd_data);
+ lpfc_vpd_data = NULL;
+ break;
+ }
+
+ lpfc_sli_pcimem_bcopy(pmb->context2, lpfc_vpd_data + offset,
+ mb->un.varDmp.word_cnt);
+ offset += mb->un.varDmp.word_cnt;
+ } while (mb->un.varDmp.word_cnt);
+ lpfc_parse_vpd(phba, lpfc_vpd_data);
+
+ kfree(lpfc_vpd_data);
+out_free_context2:
+ kfree(pmb->context2);
+out_free_mbox:
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return 0;
+}
+
+/************************************************************************/
+/* */
+/* lpfc_config_port_post */
+/* This routine will do LPFC initialization after the */
+/* CONFIG_PORT mailbox command. This will be initialized */
+/* as a SLI layer callback routine. */
+/* This routine returns 0 on success. Any other return value */
+/* indicates an error. */
+/* */
+/************************************************************************/
+int
+lpfc_config_port_post(struct lpfc_hba * phba)
+{
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_sli *psli = &phba->sli;
+ uint32_t status, timeout;
+ int i, j, rc;
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -ENOMEM;
+ }
+ mb = &pmb->mb;
+
+ lpfc_config_link(phba, pmb);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0447 Adapter failed init, mbxCmd x%x "
+ "CONFIG_LINK mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ phba->hba_state = LPFC_HBA_ERROR;
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
+ /* Get login parameters for NID. */
+ lpfc_read_sparam(phba, pmb);
+ if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0448 Adapter failed init, mbxCmd x%x "
+ "READ_SPARM mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ phba->hba_state = LPFC_HBA_ERROR;
+ mp = (struct lpfc_dmabuf *) pmb->context1;
+ mempool_free( pmb, phba->mbox_mem_pool);
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ return -EIO;
+ }
+
+ mp = (struct lpfc_dmabuf *) pmb->context1;
+
+ memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm));
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ pmb->context1 = NULL;
+
+ memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName,
+ sizeof (struct lpfc_name));
+ memcpy(&phba->fc_portname, &phba->fc_sparam.portName,
+ sizeof (struct lpfc_name));
+ /* If no serial number in VPD data, use low 6 bytes of WWNN */
+ /* This should be consolidated into parse_vpd ? - mr */
+ if (phba->SerialNumber[0] == 0) {
+ uint8_t *outptr;
+
+ outptr = (uint8_t *) & phba->fc_nodename.IEEE[0];
+ for (i = 0; i < 12; i++) {
+ status = *outptr++;
+ j = ((status & 0xf0) >> 4);
+ if (j <= 9)
+ phba->SerialNumber[i] =
+ (char)((uint8_t) 0x30 + (uint8_t) j);
+ else
+ phba->SerialNumber[i] =
+ (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
+ i++;
+ j = (status & 0xf);
+ if (j <= 9)
+ phba->SerialNumber[i] =
+ (char)((uint8_t) 0x30 + (uint8_t) j);
+ else
+ phba->SerialNumber[i] =
+ (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
+ }
+ }
+
+ /* This should turn on DELAYED ABTS for ELS timeouts */
+ lpfc_set_slim(phba, pmb, 0x052198, 0x1);
+ if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+ phba->hba_state = LPFC_HBA_ERROR;
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
+
+ lpfc_read_config(phba, pmb);
+ if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0453 Adapter failed to init, mbxCmd x%x "
+ "READ_CONFIG, mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+ phba->hba_state = LPFC_HBA_ERROR;
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
+ /* Reset the DFT_HBA_Q_DEPTH to the max xri */
+ if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1))
+ phba->cfg_hba_queue_depth =
+ mb->un.varRdConfig.max_xri + 1;
+
+ phba->lmt = mb->un.varRdConfig.lmt;
+ /* HBA is not 4GB capable, or HBA is not 2GB capable,
+ don't let link speed ask for it */
+ if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) &&
+ (phba->cfg_link_speed > LINK_SPEED_2G)) ||
+ (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) &&
+ (phba->cfg_link_speed > LINK_SPEED_1G))) {
+ /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_LINK_EVENT,
+ "%d:1302 Invalid speed for this board: "
+ "Reset link speed to auto: x%x\n",
+ phba->brd_no,
+ phba->cfg_link_speed);
+ phba->cfg_link_speed = LINK_SPEED_AUTO;
+ }
+
+ phba->hba_state = LPFC_LINK_DOWN;
+
+ /* Only process IOCBs on ring 0 till hba_state is READY */
+ if (psli->ring[psli->ip_ring].cmdringaddr)
+ psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT;
+ if (psli->ring[psli->fcp_ring].cmdringaddr)
+ psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT;
+ if (psli->ring[psli->next_ring].cmdringaddr)
+ psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT;
+
+ /* Post receive buffers for desired rings */
+ lpfc_post_rcv_buf(phba);
+
+ /* Enable appropriate host interrupts */
+ spin_lock_irq(phba->host->host_lock);
+ status = readl(phba->HCregaddr);
+ status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA;
+ if (psli->num_rings > 0)
+ status |= HC_R0INT_ENA;
+ if (psli->num_rings > 1)
+ status |= HC_R1INT_ENA;
+ if (psli->num_rings > 2)
+ status |= HC_R2INT_ENA;
+ if (psli->num_rings > 3)
+ status |= HC_R3INT_ENA;
+
+ writel(status, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+
+ /*
+ * Setup the ring 0 (els) timeout handler
+ */
+ timeout = phba->fc_ratov << 1;
+ phba->els_tmofunc.expires = jiffies + HZ * timeout;
+ add_timer(&phba->els_tmofunc);
+
+ lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0454 Adapter failed to init, mbxCmd x%x "
+ "INIT_LINK, mbxStatus x%x\n",
+ phba->brd_no,
+ mb->mbxCommand, mb->mbxStatus);
+
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ phba->hba_state = LPFC_HBA_ERROR;
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ /* MBOX buffer will be freed in mbox compl */
+
+ i = 0;
+ while ((phba->hba_state != LPFC_HBA_READY) ||
+ (phba->num_disc_nodes) || (phba->fc_prli_sent) ||
+ ((phba->fc_map_cnt == 0) && (i<2)) ||
+ (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) {
+ /* Check every second for 30 retries. */
+ i++;
+ if (i > 30) {
+ break;
+ }
+ if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) {
+ /* The link is down. Set linkdown timeout */
+ break;
+ }
+
+ /* Delay for 1 second to give discovery time to complete. */
+ msleep(1000);
+
+ }
+
+ /* Since num_disc_nodes keys off of PLOGI, delay a bit to let
+ * any potential PRLIs to flush thru the SLI sub-system.
+ */
+ msleep(50);
+
+ return (0);
+}
+
+/************************************************************************/
+/* */
+/* lpfc_hba_down_prep */
+/* This routine will do LPFC uninitialization before the */
+/* HBA is reset when bringing down the SLI Layer. This will be */
+/* initialized as a SLI layer callback routine. */
+/* This routine returns 0 on success. Any other return value */
+/* indicates an error. */
+/* */
+/************************************************************************/
+int
+lpfc_hba_down_prep(struct lpfc_hba * phba)
+{
+ /* Disable interrupts */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+
+ /* Cleanup potential discovery resources */
+ lpfc_els_flush_rscn(phba);
+ lpfc_els_flush_cmd(phba);
+ lpfc_disc_flush_list(phba);
+
+ return (0);
+}
+
+/************************************************************************/
+/* */
+/* lpfc_handle_eratt */
+/* This routine will handle processing a Host Attention */
+/* Error Status event. This will be initialized */
+/* as a SLI layer callback routine. */
+/* */
+/************************************************************************/
+void
+lpfc_handle_eratt(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+
+ /*
+ * If a reset is sent to the HBA restore PCI configuration registers.
+ */
+ if ( phba->hba_state == LPFC_INIT_START ) {
+ mdelay(1);
+ readl(phba->HCregaddr); /* flush */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+
+ /* Restore PCI cmd register */
+ pci_write_config_word(phba->pcidev,
+ PCI_COMMAND, phba->pci_cfg_value);
+ }
+
+ if (phba->work_hs & HS_FFER6) {
+ /* Re-establishing Link */
+ lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
+ "%d:1301 Re-establishing Link "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->work_hs,
+ phba->work_status[0], phba->work_status[1]);
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag |= FC_ESTABLISH_LINK;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /*
+ * Firmware stops when it triggled erratt with HS_FFER6.
+ * That could cause the I/Os dropped by the firmware.
+ * Error iocb (I/O) on txcmplq and let the SCSI layer
+ * retry it after re-establishing link.
+ */
+ pring = &psli->ring[psli->fcp_ring];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+
+
+ /*
+ * There was a firmware error. Take the hba offline and then
+ * attempt to restart it.
+ */
+ lpfc_offline(phba);
+ if (lpfc_online(phba) == 0) { /* Initialize the HBA */
+ mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60);
+ return;
+ }
+ } else {
+ /* The if clause above forces this code path when the status
+ * failure is a value other than FFER6. Do not call the offline
+ * twice. This is the adapter hardware error path.
+ */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "%d:0457 Adapter Hardware Error "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, phba->work_hs,
+ phba->work_status[0], phba->work_status[1]);
+
+ lpfc_offline(phba);
+
+ /*
+ * Restart all traffic to this host. Since the fc_transport
+ * block functions (future) were not called in lpfc_offline,
+ * don't call them here.
+ */
+ scsi_unblock_requests(phba->host);
+ }
+}
+
+/************************************************************************/
+/* */
+/* lpfc_handle_latt */
+/* This routine will handle processing a Host Attention */
+/* Link Status event. This will be initialized */
+/* as a SLI layer callback routine. */
+/* */
+/************************************************************************/
+void
+lpfc_handle_latt(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ LPFC_MBOXQ_t *pmb;
+ volatile uint32_t control;
+ struct lpfc_dmabuf *mp;
+ int rc = -ENOMEM;
+
+ pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb)
+ goto lpfc_handle_latt_err_exit;
+
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp)
+ goto lpfc_handle_latt_free_pmb;
+
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+ if (!mp->virt)
+ goto lpfc_handle_latt_free_mp;
+
+ rc = -EIO;
+
+
+ psli->slistat.link_event++;
+ lpfc_read_la(phba, pmb, mp);
+ pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
+ rc = lpfc_sli_issue_mbox (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED)
+ goto lpfc_handle_latt_free_mp;
+
+ /* Clear Link Attention in HA REG */
+ spin_lock_irq(phba->host->host_lock);
+ writel(HA_LATT, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+
+ return;
+
+lpfc_handle_latt_free_mp:
+ kfree(mp);
+lpfc_handle_latt_free_pmb:
+ kfree(pmb);
+lpfc_handle_latt_err_exit:
+ /* Enable Link attention interrupts */
+ spin_lock_irq(phba->host->host_lock);
+ psli->sli_flag |= LPFC_PROCESS_LA;
+ control = readl(phba->HCregaddr);
+ control |= HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+
+ /* Clear Link Attention in HA REG */
+ writel(HA_LATT, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_linkdown(phba);
+ phba->hba_state = LPFC_HBA_ERROR;
+
+ /* The other case is an error from issue_mbox */
+ if (rc == -ENOMEM)
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_MBOX,
+ "%d:0300 READ_LA: no buffers\n",
+ phba->brd_no);
+
+ return;
+}
+
+/************************************************************************/
+/* */
+/* lpfc_parse_vpd */
+/* This routine will parse the VPD data */
+/* */
+/************************************************************************/
+static int
+lpfc_parse_vpd(struct lpfc_hba * phba, uint8_t * vpd)
+{
+ uint8_t lenlo, lenhi;
+ uint32_t Length;
+ int i, j;
+ int finished = 0;
+ int index = 0;
+
+ if (!vpd)
+ return 0;
+
+ /* Vital Product */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_INIT,
+ "%d:0455 Vital Product Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ (uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2],
+ (uint32_t) vpd[3]);
+ do {
+ switch (vpd[index]) {
+ case 0x82:
+ index += 1;
+ lenlo = vpd[index];
+ index += 1;
+ lenhi = vpd[index];
+ index += 1;
+ i = ((((unsigned short)lenhi) << 8) + lenlo);
+ index += i;
+ break;
+ case 0x90:
+ index += 1;
+ lenlo = vpd[index];
+ index += 1;
+ lenhi = vpd[index];
+ index += 1;
+ Length = ((((unsigned short)lenhi) << 8) + lenlo);
+
+ while (Length > 0) {
+ /* Look for Serial Number */
+ if ((vpd[index] == 'S') && (vpd[index+1] == 'N')) {
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ j = 0;
+ Length -= (3+i);
+ while(i--) {
+ phba->SerialNumber[j++] = vpd[index++];
+ if (j == 31)
+ break;
+ }
+ phba->SerialNumber[j] = 0;
+ continue;
+ }
+ else if ((vpd[index] == 'V') && (vpd[index+1] == '1')) {
+ phba->vpd_flag |= VPD_MODEL_DESC;
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ j = 0;
+ Length -= (3+i);
+ while(i--) {
+ phba->ModelDesc[j++] = vpd[index++];
+ if (j == 255)
+ break;
+ }
+ phba->ModelDesc[j] = 0;
+ continue;
+ }
+ else if ((vpd[index] == 'V') && (vpd[index+1] == '2')) {
+ phba->vpd_flag |= VPD_MODEL_NAME;
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ j = 0;
+ Length -= (3+i);
+ while(i--) {
+ phba->ModelName[j++] = vpd[index++];
+ if (j == 79)
+ break;
+ }
+ phba->ModelName[j] = 0;
+ continue;
+ }
+ else if ((vpd[index] == 'V') && (vpd[index+1] == '3')) {
+ phba->vpd_flag |= VPD_PROGRAM_TYPE;
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ j = 0;
+ Length -= (3+i);
+ while(i--) {
+ phba->ProgramType[j++] = vpd[index++];
+ if (j == 255)
+ break;
+ }
+ phba->ProgramType[j] = 0;
+ continue;
+ }
+ else if ((vpd[index] == 'V') && (vpd[index+1] == '4')) {
+ phba->vpd_flag |= VPD_PORT;
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ j = 0;
+ Length -= (3+i);
+ while(i--) {
+ phba->Port[j++] = vpd[index++];
+ if (j == 19)
+ break;
+ }
+ phba->Port[j] = 0;
+ continue;
+ }
+ else {
+ index += 2;
+ i = vpd[index];
+ index += 1;
+ index += i;
+ Length -= (3 + i);
+ }
+ }
+ finished = 0;
+ break;
+ case 0x78:
+ finished = 1;
+ break;
+ default:
+ index ++;
+ break;
+ }
+ } while (!finished && (index < 108));
+
+ return(1);
+}
+
+static void
+lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp)
+{
+ lpfc_vpd_t *vp;
+ uint32_t id;
+ char str[16];
+
+ vp = &phba->vpd;
+ pci_read_config_dword(phba->pcidev, PCI_VENDOR_ID, &id);
+
+ switch ((id >> 16) & 0xffff) {
+ case PCI_DEVICE_ID_SUPERFLY:
+ if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
+ strcpy(str, "LP7000 1");
+ else
+ strcpy(str, "LP7000E 1");
+ break;
+ case PCI_DEVICE_ID_DRAGONFLY:
+ strcpy(str, "LP8000 1");
+ break;
+ case PCI_DEVICE_ID_CENTAUR:
+ if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
+ strcpy(str, "LP9002 2");
+ else
+ strcpy(str, "LP9000 1");
+ break;
+ case PCI_DEVICE_ID_RFLY:
+ strcpy(str, "LP952 2");
+ break;
+ case PCI_DEVICE_ID_PEGASUS:
+ strcpy(str, "LP9802 2");
+ break;
+ case PCI_DEVICE_ID_THOR:
+ strcpy(str, "LP10000 2");
+ break;
+ case PCI_DEVICE_ID_VIPER:
+ strcpy(str, "LPX1000 10");
+ break;
+ case PCI_DEVICE_ID_PFLY:
+ strcpy(str, "LP982 2");
+ break;
+ case PCI_DEVICE_ID_TFLY:
+ strcpy(str, "LP1050 2");
+ break;
+ case PCI_DEVICE_ID_HELIOS:
+ strcpy(str, "LP11000 4");
+ break;
+ case PCI_DEVICE_ID_BMID:
+ strcpy(str, "LP1150 4");
+ break;
+ case PCI_DEVICE_ID_BSMB:
+ strcpy(str, "LP111 4");
+ break;
+ case PCI_DEVICE_ID_ZEPHYR:
+ strcpy(str, "LP11000e 4");
+ break;
+ case PCI_DEVICE_ID_ZMID:
+ strcpy(str, "LP1150e 4");
+ break;
+ case PCI_DEVICE_ID_ZSMB:
+ strcpy(str, "LP111e 4");
+ break;
+ case PCI_DEVICE_ID_LP101:
+ strcpy(str, "LP101 2");
+ break;
+ case PCI_DEVICE_ID_LP10000S:
+ strcpy(str, "LP10000-S 2");
+ break;
+ }
+ if (mdp)
+ sscanf(str, "%s", mdp);
+ if (descp)
+ sprintf(descp, "Emulex LightPulse %s Gigabit PCI Fibre "
+ "Channel Adapter", str);
+}
+
+/**************************************************/
+/* lpfc_post_buffer */
+/* */
+/* This routine will post count buffers to the */
+/* ring with the QUE_RING_BUF_CN command. This */
+/* allows 3 buffers / command to be posted. */
+/* Returns the number of buffers NOT posted. */
+/**************************************************/
+int
+lpfc_post_buffer(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, int cnt,
+ int type)
+{
+ IOCB_t *icmd;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ struct lpfc_iocbq *iocb = NULL;
+ struct lpfc_dmabuf *mp1, *mp2;
+
+ cnt += pring->missbufcnt;
+
+ /* While there are buffers to post */
+ while (cnt > 0) {
+ /* Allocate buffer for command iocb */
+ spin_lock_irq(phba->host->host_lock);
+ list_remove_head(lpfc_iocb_list, iocb, struct lpfc_iocbq, list);
+ spin_unlock_irq(phba->host->host_lock);
+ if (iocb == NULL) {
+ pring->missbufcnt = cnt;
+ return cnt;
+ }
+ memset(iocb, 0, sizeof (struct lpfc_iocbq));
+ icmd = &iocb->iocb;
+
+ /* 2 buffers can be posted per command */
+ /* Allocate buffer to post */
+ mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (mp1)
+ mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+ &mp1->phys);
+ if (mp1 == 0 || mp1->virt == 0) {
+ if (mp1)
+ kfree(mp1);
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&iocb->list, lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ pring->missbufcnt = cnt;
+ return cnt;
+ }
+
+ INIT_LIST_HEAD(&mp1->list);
+ /* Allocate buffer to post */
+ if (cnt > 1) {
+ mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ if (mp2)
+ mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+ &mp2->phys);
+ if (mp2 == 0 || mp2->virt == 0) {
+ if (mp2)
+ kfree(mp2);
+ lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
+ kfree(mp1);
+ spin_lock_irq(phba->host->host_lock);
+ list_add_tail(&iocb->list, lpfc_iocb_list);
+ spin_unlock_irq(phba->host->host_lock);
+ pring->missbufcnt = cnt;
+ return cnt;
+ }
+
+ INIT_LIST_HEAD(&mp2->list);
+ } else {
+ mp2 = NULL;
+ }
+
+ icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys);
+ icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys);
+ icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE;
+ icmd->ulpBdeCount = 1;
+ cnt--;
+ if (mp2) {
+ icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys);
+ icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys);
+ icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE;
+ cnt--;
+ icmd->ulpBdeCount = 2;
+ }
+
+ icmd->ulpCommand = CMD_QUE_RING_BUF64_CN;
+ icmd->ulpLe = 1;
+
+ spin_lock_irq(phba->host->host_lock);
+ if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) {
+ lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
+ kfree(mp1);
+ cnt++;
+ if (mp2) {
+ lpfc_mbuf_free(phba, mp2->virt, mp2->phys);
+ kfree(mp2);
+ cnt++;
+ }
+ list_add_tail(&iocb->list, lpfc_iocb_list);
+ pring->missbufcnt = cnt;
+ spin_unlock_irq(phba->host->host_lock);
+ return cnt;
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_sli_ringpostbuf_put(phba, pring, mp1);
+ if (mp2) {
+ lpfc_sli_ringpostbuf_put(phba, pring, mp2);
+ }
+ }
+ pring->missbufcnt = 0;
+ return 0;
+}
+
+/************************************************************************/
+/* */
+/* lpfc_post_rcv_buf */
+/* This routine post initial rcv buffers to the configured rings */
+/* */
+/************************************************************************/
+static int
+lpfc_post_rcv_buf(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+
+ /* Ring 0, ELS / CT buffers */
+ lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1);
+ /* Ring 2 - FCP no buffers needed */
+
+ return 0;
+}
+
+#define S(N,V) (((V)<<(N))|((V)>>(32-(N))))
+
+/************************************************************************/
+/* */
+/* lpfc_sha_init */
+/* */
+/************************************************************************/
+static void
+lpfc_sha_init(uint32_t * HashResultPointer)
+{
+ HashResultPointer[0] = 0x67452301;
+ HashResultPointer[1] = 0xEFCDAB89;
+ HashResultPointer[2] = 0x98BADCFE;
+ HashResultPointer[3] = 0x10325476;
+ HashResultPointer[4] = 0xC3D2E1F0;
+}
+
+/************************************************************************/
+/* */
+/* lpfc_sha_iterate */
+/* */
+/************************************************************************/
+static void
+lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
+{
+ int t;
+ uint32_t TEMP;
+ uint32_t A, B, C, D, E;
+ t = 16;
+ do {
+ HashWorkingPointer[t] =
+ S(1,
+ HashWorkingPointer[t - 3] ^ HashWorkingPointer[t -
+ 8] ^
+ HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]);
+ } while (++t <= 79);
+ t = 0;
+ A = HashResultPointer[0];
+ B = HashResultPointer[1];
+ C = HashResultPointer[2];
+ D = HashResultPointer[3];
+ E = HashResultPointer[4];
+
+ do {
+ if (t < 20) {
+ TEMP = ((B & C) | ((~B) & D)) + 0x5A827999;
+ } else if (t < 40) {
+ TEMP = (B ^ C ^ D) + 0x6ED9EBA1;
+ } else if (t < 60) {
+ TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC;
+ } else {
+ TEMP = (B ^ C ^ D) + 0xCA62C1D6;
+ }
+ TEMP += S(5, A) + E + HashWorkingPointer[t];
+ E = D;
+ D = C;
+ C = S(30, B);
+ B = A;
+ A = TEMP;
+ } while (++t <= 79);
+
+ HashResultPointer[0] += A;
+ HashResultPointer[1] += B;
+ HashResultPointer[2] += C;
+ HashResultPointer[3] += D;
+ HashResultPointer[4] += E;
+
+}
+
+/************************************************************************/
+/* */
+/* lpfc_challenge_key */
+/* */
+/************************************************************************/
+static void
+lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking)
+{
+ *HashWorking = (*RandomChallenge ^ *HashWorking);
+}
+
+/************************************************************************/
+/* */
+/* lpfc_hba_init */
+/* */
+/************************************************************************/
+void
+lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
+{
+ int t;
+ uint32_t *HashWorking;
+ uint32_t *pwwnn = phba->wwnn;
+
+ HashWorking = kmalloc(80 * sizeof(uint32_t), GFP_KERNEL);
+ if (!HashWorking)
+ return;
+
+ memset(HashWorking, 0, (80 * sizeof(uint32_t)));
+ HashWorking[0] = HashWorking[78] = *pwwnn++;
+ HashWorking[1] = HashWorking[79] = *pwwnn;
+
+ for (t = 0; t < 7; t++)
+ lpfc_challenge_key(phba->RandomData + t, HashWorking + t);
+
+ lpfc_sha_init(hbainit);
+ lpfc_sha_iterate(hbainit, HashWorking);
+ kfree(HashWorking);
+}
+
+static void
+lpfc_cleanup(struct lpfc_hba * phba, uint32_t save_bind)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ /* clean up phba - lpfc specific */
+ lpfc_can_disctmo(phba);
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+ nlp_listp) {
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_reglogin_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+ nlp_listp) {
+ lpfc_nlp_remove(phba, ndlp);
+ }
+
+ INIT_LIST_HEAD(&phba->fc_nlpmap_list);
+ INIT_LIST_HEAD(&phba->fc_nlpunmap_list);
+ INIT_LIST_HEAD(&phba->fc_unused_list);
+ INIT_LIST_HEAD(&phba->fc_plogi_list);
+ INIT_LIST_HEAD(&phba->fc_adisc_list);
+ INIT_LIST_HEAD(&phba->fc_reglogin_list);
+ INIT_LIST_HEAD(&phba->fc_prli_list);
+ INIT_LIST_HEAD(&phba->fc_npr_list);
+
+ phba->fc_map_cnt = 0;
+ phba->fc_unmap_cnt = 0;
+ phba->fc_plogi_cnt = 0;
+ phba->fc_adisc_cnt = 0;
+ phba->fc_reglogin_cnt = 0;
+ phba->fc_prli_cnt = 0;
+ phba->fc_npr_cnt = 0;
+ phba->fc_unused_cnt= 0;
+ return;
+}
+
+static void
+lpfc_establish_link_tmo(unsigned long ptr)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+ unsigned long iflag;
+
+
+ /* Re-establishing Link, timer expired */
+ lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+ "%d:1300 Re-establishing Link, timer expired "
+ "Data: x%x x%x\n",
+ phba->brd_no, phba->fc_flag, phba->hba_state);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ phba->fc_flag &= ~FC_ESTABLISH_LINK;
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+static int
+lpfc_stop_timer(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+
+ /* Instead of a timer, this has been converted to a
+ * deferred procedding list.
+ */
+ while (!list_empty(&phba->freebufList)) {
+
+ struct lpfc_dmabuf *mp = NULL;
+
+ list_remove_head((&phba->freebufList), mp,
+ struct lpfc_dmabuf, list);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ }
+
+ del_timer_sync(&phba->fc_estabtmo);
+ del_timer_sync(&phba->fc_disctmo);
+ del_timer_sync(&phba->fc_fdmitmo);
+ del_timer_sync(&phba->els_tmofunc);
+ psli = &phba->sli;
+ del_timer_sync(&psli->mbox_tmo);
+ return(1);
+}
+
+int
+lpfc_online(struct lpfc_hba * phba)
+{
+ if (!phba)
+ return 0;
+
+ if (!(phba->fc_flag & FC_OFFLINE_MODE))
+ return 0;
+
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_INIT,
+ "%d:0458 Bring Adapter online\n",
+ phba->brd_no);
+
+ if (!lpfc_sli_queue_setup(phba))
+ return 1;
+
+ if (lpfc_sli_hba_setup(phba)) /* Initialize the HBA */
+ return 1;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_OFFLINE_MODE;
+ spin_unlock_irq(phba->host->host_lock);
+
+ /*
+ * Restart all traffic to this host. Since the fc_transport block
+ * functions (future) were not called in lpfc_offline, don't call them
+ * here.
+ */
+ scsi_unblock_requests(phba->host);
+ return 0;
+}
+
+int
+lpfc_offline(struct lpfc_hba * phba)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli;
+ unsigned long iflag;
+ int i = 0;
+
+ if (!phba)
+ return 0;
+
+ if (phba->fc_flag & FC_OFFLINE_MODE)
+ return 0;
+
+ /*
+ * Don't call the fc_transport block api (future). The device is
+ * going offline and causing a timer to fire in the midlayer is
+ * unproductive. Just block all new requests until the driver
+ * comes back online.
+ */
+ scsi_block_requests(phba->host);
+ psli = &phba->sli;
+ pring = &psli->ring[psli->fcp_ring];
+
+ lpfc_linkdown(phba);
+
+ /* The linkdown event takes 30 seconds to timeout. */
+ while (pring->txcmplq_cnt) {
+ mdelay(10);
+ if (i++ > 3000)
+ break;
+ }
+
+ /* stop all timers associated with this hba */
+ lpfc_stop_timer(phba);
+ phba->work_hba_events = 0;
+
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_INIT,
+ "%d:0460 Bring Adapter offline\n",
+ phba->brd_no);
+
+ /* Bring down the SLI Layer and cleanup. The HBA is offline
+ now. */
+ lpfc_sli_hba_down(phba);
+ lpfc_cleanup(phba, 1);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ phba->fc_flag |= FC_OFFLINE_MODE;
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return 0;
+}
+
+/******************************************************************************
+* Function name: lpfc_scsi_free
+*
+* Description: Called from lpfc_pci_remove_one free internal driver resources
+*
+******************************************************************************/
+static int
+lpfc_scsi_free(struct lpfc_hba * phba)
+{
+ struct lpfc_scsi_buf *sb, *sb_next;
+ struct lpfc_iocbq *io, *io_next;
+
+ spin_lock_irq(phba->host->host_lock);
+ /* Release all the lpfc_scsi_bufs maintained by this host. */
+ list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
+ list_del(&sb->list);
+ pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
+ sb->dma_handle);
+ kfree(sb);
+ phba->total_scsi_bufs--;
+ }
+
+ /* Release all the lpfc_iocbq entries maintained by this host. */
+ list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
+ list_del(&io->list);
+ kfree(io);
+ phba->total_iocbq_bufs--;
+ }
+
+ spin_unlock_irq(phba->host->host_lock);
+
+ return 0;
+}
+
+
+static int __devinit
+lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+ struct Scsi_Host *host;
+ struct lpfc_hba *phba;
+ struct lpfc_sli *psli;
+ struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
+ unsigned long bar0map_len, bar2map_len;
+ int error = -ENODEV, retval;
+ int i;
+ u64 wwname;
+
+ if (pci_enable_device(pdev))
+ goto out;
+ if (pci_request_regions(pdev, LPFC_DRIVER_NAME))
+ goto out_disable_device;
+
+ host = scsi_host_alloc(&lpfc_template,
+ sizeof (struct lpfc_hba) + sizeof (unsigned long));
+ if (!host)
+ goto out_release_regions;
+
+ phba = (struct lpfc_hba*)host->hostdata;
+ memset(phba, 0, sizeof (struct lpfc_hba));
+ phba->link_stats = (void *)&phba[1];
+ phba->host = host;
+
+ phba->fc_flag |= FC_LOADING;
+ phba->pcidev = pdev;
+
+ /* Assign an unused board number */
+ if (!idr_pre_get(&lpfc_hba_index, GFP_KERNEL))
+ goto out_put_host;
+
+ error = idr_get_new(&lpfc_hba_index, NULL, &phba->brd_no);
+ if (error)
+ goto out_put_host;
+
+ host->unique_id = phba->brd_no;
+
+ INIT_LIST_HEAD(&phba->ctrspbuflist);
+ INIT_LIST_HEAD(&phba->rnidrspbuflist);
+ INIT_LIST_HEAD(&phba->freebufList);
+
+ /* Initialize timers used by driver */
+ init_timer(&phba->fc_estabtmo);
+ phba->fc_estabtmo.function = lpfc_establish_link_tmo;
+ phba->fc_estabtmo.data = (unsigned long)phba;
+ init_timer(&phba->fc_disctmo);
+ phba->fc_disctmo.function = lpfc_disc_timeout;
+ phba->fc_disctmo.data = (unsigned long)phba;
+
+ init_timer(&phba->fc_fdmitmo);
+ phba->fc_fdmitmo.function = lpfc_fdmi_tmo;
+ phba->fc_fdmitmo.data = (unsigned long)phba;
+ init_timer(&phba->els_tmofunc);
+ phba->els_tmofunc.function = lpfc_els_timeout;
+ phba->els_tmofunc.data = (unsigned long)phba;
+ psli = &phba->sli;
+ init_timer(&psli->mbox_tmo);
+ psli->mbox_tmo.function = lpfc_mbox_timeout;
+ psli->mbox_tmo.data = (unsigned long)phba;
+
+ /*
+ * Get all the module params for configuring this host and then
+ * establish the host parameters.
+ */
+ lpfc_get_cfgparam(phba);
+
+ host->max_id = LPFC_MAX_TARGET;
+ host->max_lun = phba->cfg_max_luns;
+ host->this_id = -1;
+
+ /* Initialize all internally managed lists. */
+ INIT_LIST_HEAD(&phba->fc_nlpmap_list);
+ INIT_LIST_HEAD(&phba->fc_nlpunmap_list);
+ INIT_LIST_HEAD(&phba->fc_unused_list);
+ INIT_LIST_HEAD(&phba->fc_plogi_list);
+ INIT_LIST_HEAD(&phba->fc_adisc_list);
+ INIT_LIST_HEAD(&phba->fc_reglogin_list);
+ INIT_LIST_HEAD(&phba->fc_prli_list);
+ INIT_LIST_HEAD(&phba->fc_npr_list);
+
+
+ pci_set_master(pdev);
+ retval = pci_set_mwi(pdev);
+ if (retval)
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "Warning: pci_set_mwi returned %d\n", retval);
+
+ if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
+ if (pci_set_dma_mask(phba->pcidev, DMA_32BIT_MASK) != 0)
+ goto out_idr_remove;
+
+ /*
+ * Get the bus address of Bar0 and Bar2 and the number of bytes
+ * required by each mapping.
+ */
+ phba->pci_bar0_map = pci_resource_start(phba->pcidev, 0);
+ bar0map_len = pci_resource_len(phba->pcidev, 0);
+
+ phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2);
+ bar2map_len = pci_resource_len(phba->pcidev, 2);
+
+ /* Map HBA SLIM and Control Registers to a kernel virtual address. */
+ phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
+ phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
+
+ /* Allocate memory for SLI-2 structures */
+ phba->slim2p = dma_alloc_coherent(&phba->pcidev->dev, SLI2_SLIM_SIZE,
+ &phba->slim2p_mapping, GFP_KERNEL);
+ if (!phba->slim2p)
+ goto out_iounmap;
+
+
+ /* Initialize the SLI Layer to run with lpfc HBAs. */
+ lpfc_sli_setup(phba);
+ lpfc_sli_queue_setup(phba);
+
+ error = lpfc_mem_alloc(phba);
+ if (error)
+ goto out_free_slim;
+
+ /* Initialize and populate the iocb list per host. */
+ INIT_LIST_HEAD(&phba->lpfc_iocb_list);
+ for (i = 0; i < LPFC_IOCB_LIST_CNT; i++) {
+ iocbq_entry = kmalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
+ if (iocbq_entry == NULL) {
+ printk(KERN_ERR "%s: only allocated %d iocbs of "
+ "expected %d count. Unloading driver.\n",
+ __FUNCTION__, i, LPFC_IOCB_LIST_CNT);
+ error = -ENOMEM;
+ goto out_free_iocbq;
+ }
+
+ memset(iocbq_entry, 0, sizeof(struct lpfc_iocbq));
+ spin_lock_irq(phba->host->host_lock);
+ list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
+ phba->total_iocbq_bufs++;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+
+ /* Initialize HBA structure */
+ phba->fc_edtov = FF_DEF_EDTOV;
+ phba->fc_ratov = FF_DEF_RATOV;
+ phba->fc_altov = FF_DEF_ALTOV;
+ phba->fc_arbtov = FF_DEF_ARBTOV;
+
+ INIT_LIST_HEAD(&phba->work_list);
+ phba->work_ha_mask = (HA_ERATT|HA_MBATT|HA_LATT);
+ phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ goto out_free_iocbq;
+ }
+
+ /* We can rely on a queue depth attribute only after SLI HBA setup */
+ host->can_queue = phba->cfg_hba_queue_depth - 10;
+
+ /* Tell the midlayer we support 16 byte commands */
+ host->max_cmd_len = 16;
+
+ /* Initialize the list of scsi buffers used by driver for scsi IO. */
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
+
+ host->transportt = lpfc_transport_template;
+ host->hostdata[0] = (unsigned long)phba;
+ pci_set_drvdata(pdev, host);
+ error = scsi_add_host(host, &pdev->dev);
+ if (error)
+ goto out_kthread_stop;
+
+ error = lpfc_alloc_sysfs_attr(phba);
+ if (error)
+ goto out_kthread_stop;
+
+ error = request_irq(phba->pcidev->irq, lpfc_intr_handler, SA_SHIRQ,
+ LPFC_DRIVER_NAME, phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "%d:0451 Enable interrupt handler failed\n",
+ phba->brd_no);
+ goto out_free_sysfs_attr;
+ }
+ phba->MBslimaddr = phba->slim_memmap_p;
+ phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
+ phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
+ phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
+ phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+
+ error = lpfc_sli_hba_setup(phba);
+ if (error)
+ goto out_free_irq;
+
+ /*
+ * set fixed host attributes
+ * Must done after lpfc_sli_hba_setup()
+ */
+
+ memcpy(&wwname, &phba->fc_nodename, sizeof(u64));
+ fc_host_node_name(host) = be64_to_cpu(wwname);
+ memcpy(&wwname, &phba->fc_portname, sizeof(u64));
+ fc_host_port_name(host) = be64_to_cpu(wwname);
+ fc_host_supported_classes(host) = FC_COS_CLASS3;
+
+ memset(fc_host_supported_fc4s(host), 0,
+ sizeof(fc_host_supported_fc4s(host)));
+ fc_host_supported_fc4s(host)[2] = 1;
+ fc_host_supported_fc4s(host)[7] = 1;
+
+ lpfc_get_hba_sym_node_name(phba, fc_host_symbolic_name(host));
+
+ fc_host_supported_speeds(host) = 0;
+ switch (FC_JEDEC_ID(phba->vpd.rev.biuRev)) {
+ case VIPER_JEDEC_ID:
+ fc_host_supported_speeds(host) |= FC_PORTSPEED_10GBIT;
+ break;
+ case HELIOS_JEDEC_ID:
+ fc_host_supported_speeds(host) |= FC_PORTSPEED_4GBIT;
+ /* Fall through */
+ case CENTAUR_2G_JEDEC_ID:
+ case PEGASUS_JEDEC_ID:
+ case THOR_JEDEC_ID:
+ fc_host_supported_speeds(host) |= FC_PORTSPEED_2GBIT;
+ /* Fall through */
+ default:
+ fc_host_supported_speeds(host) = FC_PORTSPEED_1GBIT;
+ }
+
+ fc_host_maxframe_size(host) =
+ ((((uint32_t) phba->fc_sparam.cmn.bbRcvSizeMsb & 0x0F) << 8) |
+ (uint32_t) phba->fc_sparam.cmn.bbRcvSizeLsb);
+
+ /* This value is also unchanging */
+ memset(fc_host_active_fc4s(host), 0,
+ sizeof(fc_host_active_fc4s(host)));
+ fc_host_active_fc4s(host)[2] = 1;
+ fc_host_active_fc4s(host)[7] = 1;
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->fc_flag &= ~FC_LOADING;
+ spin_unlock_irq(phba->host->host_lock);
+ return 0;
+
+out_free_irq:
+ lpfc_stop_timer(phba);
+ phba->work_hba_events = 0;
+ free_irq(phba->pcidev->irq, phba);
+out_free_sysfs_attr:
+ lpfc_free_sysfs_attr(phba);
+out_kthread_stop:
+ kthread_stop(phba->worker_thread);
+out_free_iocbq:
+ list_for_each_entry_safe(iocbq_entry, iocbq_next,
+ &phba->lpfc_iocb_list, list) {
+ spin_lock_irq(phba->host->host_lock);
+ kfree(iocbq_entry);
+ phba->total_iocbq_bufs--;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ lpfc_mem_free(phba);
+out_free_slim:
+ dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, phba->slim2p,
+ phba->slim2p_mapping);
+out_iounmap:
+ iounmap(phba->ctrl_regs_memmap_p);
+ iounmap(phba->slim_memmap_p);
+out_idr_remove:
+ idr_remove(&lpfc_hba_index, phba->brd_no);
+out_put_host:
+ scsi_host_put(host);
+out_release_regions:
+ pci_release_regions(pdev);
+out_disable_device:
+ pci_disable_device(pdev);
+out:
+ return error;
+}
+
+static void __devexit
+lpfc_pci_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata[0];
+ unsigned long iflag;
+
+ lpfc_free_sysfs_attr(phba);
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ phba->fc_flag |= FC_UNLOADING;
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+
+ fc_remove_host(phba->host);
+ scsi_remove_host(phba->host);
+
+ kthread_stop(phba->worker_thread);
+
+ /*
+ * Bring down the SLI Layer. This step disable all interrupts,
+ * clears the rings, discards all mailbox commands, and resets
+ * the HBA.
+ */
+ lpfc_sli_hba_down(phba);
+
+ /* Release the irq reservation */
+ free_irq(phba->pcidev->irq, phba);
+
+ lpfc_cleanup(phba, 0);
+ lpfc_stop_timer(phba);
+ phba->work_hba_events = 0;
+
+ /*
+ * Call scsi_free before mem_free since scsi bufs are released to their
+ * corresponding pools here.
+ */
+ lpfc_scsi_free(phba);
+ lpfc_mem_free(phba);
+
+ /* Free resources associated with SLI2 interface */
+ dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+ phba->slim2p, phba->slim2p_mapping);
+
+ /* unmap adapter SLIM and Control Registers */
+ iounmap(phba->ctrl_regs_memmap_p);
+ iounmap(phba->slim_memmap_p);
+
+ pci_release_regions(phba->pcidev);
+ pci_disable_device(phba->pcidev);
+
+ idr_remove(&lpfc_hba_index, phba->brd_no);
+ scsi_host_put(phba->host);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_device_id lpfc_id_table[] = {
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP10000S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, lpfc_id_table);
+
+static struct pci_driver lpfc_driver = {
+ .name = LPFC_DRIVER_NAME,
+ .id_table = lpfc_id_table,
+ .probe = lpfc_pci_probe_one,
+ .remove = __devexit_p(lpfc_pci_remove_one),
+};
+
+static int __init
+lpfc_init(void)
+{
+ int error = 0;
+
+ printk(LPFC_MODULE_DESC "\n");
+
+ lpfc_transport_template =
+ fc_attach_transport(&lpfc_transport_functions);
+ if (!lpfc_transport_template)
+ return -ENOMEM;
+ error = pci_register_driver(&lpfc_driver);
+ if (error)
+ fc_release_transport(lpfc_transport_template);
+
+ return error;
+}
+
+static void __exit
+lpfc_exit(void)
+{
+ pci_unregister_driver(&lpfc_driver);
+ fc_release_transport(lpfc_transport_template);
+}
+
+module_init(lpfc_init);
+module_exit(lpfc_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(LPFC_MODULE_DESC);
+MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com");
+MODULE_VERSION("0:" LPFC_DRIVER_VERSION);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
new file mode 100644
index 000000000000..a85268880fae
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -0,0 +1,41 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_logmsg.h 1.32 2005/01/25 17:52:01EST sf_support Exp $
+ */
+
+#define LOG_ELS 0x1 /* ELS events */
+#define LOG_DISCOVERY 0x2 /* Link discovery events */
+#define LOG_MBOX 0x4 /* Mailbox events */
+#define LOG_INIT 0x8 /* Initialization events */
+#define LOG_LINK_EVENT 0x10 /* Link events */
+#define LOG_IP 0x20 /* IP traffic history */
+#define LOG_FCP 0x40 /* FCP traffic history */
+#define LOG_NODE 0x80 /* Node table events */
+#define LOG_MISC 0x400 /* Miscellaneous events */
+#define LOG_SLI 0x800 /* SLI events */
+#define LOG_CHK_COND 0x1000 /* FCP Check condition flag */
+#define LOG_LIBDFC 0x2000 /* Libdfc events */
+#define LOG_ALL_MSG 0xffff /* LOG all messages */
+
+#define lpfc_printf_log(phba, level, mask, fmt, arg...) \
+ { if (((mask) &(phba)->cfg_log_verbose) || (level[1] <= '3')) \
+ dev_printk(level, &((phba)->pcidev)->dev, fmt, ##arg); }
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
new file mode 100644
index 000000000000..8712a80fe747
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -0,0 +1,646 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_mbox.c 1.85 2005/04/13 11:59:11EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_compat.h"
+
+/**********************************************/
+
+/* mailbox command */
+/**********************************************/
+void
+lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
+{
+ MAILBOX_t *mb;
+ void *ctx;
+
+ mb = &pmb->mb;
+ ctx = pmb->context2;
+
+ /* Setup to dump VPD region */
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_DUMP_MEMORY;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.type = DMP_NV_PARAMS;
+ mb->un.varDmp.entry_index = offset;
+ mb->un.varDmp.region_id = DMP_REGION_VPD;
+ mb->un.varDmp.word_cnt = (DMP_RSP_SIZE / sizeof (uint32_t));
+ mb->un.varDmp.co = 0;
+ mb->un.varDmp.resp_offset = 0;
+ pmb->context2 = ctx;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/**********************************************/
+/* lpfc_read_nv Issue a READ NVPARAM */
+/* mailbox command */
+/**********************************************/
+void
+lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_READ_NV;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/**********************************************/
+/* lpfc_read_la Issue a READ LA */
+/* mailbox command */
+/**********************************************/
+int
+lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, struct lpfc_dmabuf *mp)
+{
+ MAILBOX_t *mb;
+ struct lpfc_sli *psli;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ INIT_LIST_HEAD(&mp->list);
+ mb->mbxCommand = MBX_READ_LA64;
+ mb->un.varReadLA.un.lilpBde64.tus.f.bdeSize = 128;
+ mb->un.varReadLA.un.lilpBde64.addrHigh = putPaddrHigh(mp->phys);
+ mb->un.varReadLA.un.lilpBde64.addrLow = putPaddrLow(mp->phys);
+
+ /* Save address for later completion and set the owner to host so that
+ * the FW knows this mailbox is available for processing.
+ */
+ pmb->context1 = (uint8_t *) mp;
+ mb->mbxOwner = OWN_HOST;
+ return (0);
+}
+
+/**********************************************/
+/* lpfc_clear_la Issue a CLEAR LA */
+/* mailbox command */
+/**********************************************/
+void
+lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->un.varClearLA.eventTag = phba->fc_eventTag;
+ mb->mbxCommand = MBX_CLEAR_LA;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/**************************************************/
+/* lpfc_config_link Issue a CONFIG LINK */
+/* mailbox command */
+/**************************************************/
+void
+lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ /* NEW_FEATURE
+ * SLI-2, Coalescing Response Feature.
+ */
+ if (phba->cfg_cr_delay) {
+ mb->un.varCfgLnk.cr = 1;
+ mb->un.varCfgLnk.ci = 1;
+ mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay;
+ mb->un.varCfgLnk.cr_count = phba->cfg_cr_count;
+ }
+
+ mb->un.varCfgLnk.myId = phba->fc_myDID;
+ mb->un.varCfgLnk.edtov = phba->fc_edtov;
+ mb->un.varCfgLnk.arbtov = phba->fc_arbtov;
+ mb->un.varCfgLnk.ratov = phba->fc_ratov;
+ mb->un.varCfgLnk.rttov = phba->fc_rttov;
+ mb->un.varCfgLnk.altov = phba->fc_altov;
+ mb->un.varCfgLnk.crtov = phba->fc_crtov;
+ mb->un.varCfgLnk.citov = phba->fc_citov;
+
+ if (phba->cfg_ack0)
+ mb->un.varCfgLnk.ack0_enable = 1;
+
+ mb->mbxCommand = MBX_CONFIG_LINK;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/**********************************************/
+/* lpfc_init_link Issue an INIT LINK */
+/* mailbox command */
+/**********************************************/
+void
+lpfc_init_link(struct lpfc_hba * phba,
+ LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed)
+{
+ lpfc_vpd_t *vpd;
+ struct lpfc_sli *psli;
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ psli = &phba->sli;
+ switch (topology) {
+ case FLAGS_TOPOLOGY_MODE_LOOP_PT:
+ mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP;
+ mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER;
+ break;
+ case FLAGS_TOPOLOGY_MODE_PT_PT:
+ mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
+ break;
+ case FLAGS_TOPOLOGY_MODE_LOOP:
+ mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP;
+ break;
+ case FLAGS_TOPOLOGY_MODE_PT_LOOP:
+ mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
+ mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER;
+ break;
+ }
+
+ /* NEW_FEATURE
+ * Setting up the link speed
+ */
+ vpd = &phba->vpd;
+ if (vpd->rev.feaLevelHigh >= 0x02){
+ switch(linkspeed){
+ case LINK_SPEED_1G:
+ case LINK_SPEED_2G:
+ case LINK_SPEED_4G:
+ mb->un.varInitLnk.link_flags |=
+ FLAGS_LINK_SPEED;
+ mb->un.varInitLnk.link_speed = linkspeed;
+ break;
+ case LINK_SPEED_AUTO:
+ default:
+ mb->un.varInitLnk.link_speed =
+ LINK_SPEED_AUTO;
+ break;
+ }
+
+ }
+ else
+ mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO;
+
+ mb->mbxCommand = (volatile uint8_t)MBX_INIT_LINK;
+ mb->mbxOwner = OWN_HOST;
+ mb->un.varInitLnk.fabric_AL_PA = phba->fc_pref_ALPA;
+ return;
+}
+
+/**********************************************/
+/* lpfc_read_sparam Issue a READ SPARAM */
+/* mailbox command */
+/**********************************************/
+int
+lpfc_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_dmabuf *mp;
+ MAILBOX_t *mb;
+ struct lpfc_sli *psli;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->mbxOwner = OWN_HOST;
+
+ /* Get a buffer to hold the HBAs Service Parameters */
+
+ if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL)) == 0) ||
+ ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) {
+ if (mp)
+ kfree(mp);
+ mb->mbxCommand = MBX_READ_SPARM64;
+ /* READ_SPARAM: no buffers */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_MBOX,
+ "%d:0301 READ_SPARAM: no buffers\n",
+ phba->brd_no);
+ return (1);
+ }
+ INIT_LIST_HEAD(&mp->list);
+ mb->mbxCommand = MBX_READ_SPARM64;
+ mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
+ mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys);
+ mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys);
+
+ /* save address for completion */
+ pmb->context1 = mp;
+
+ return (0);
+}
+
+/********************************************/
+/* lpfc_unreg_did Issue a UNREG_DID */
+/* mailbox command */
+/********************************************/
+void
+lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->un.varUnregDID.did = did;
+
+ mb->mbxCommand = MBX_UNREG_D_ID;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/***********************************************/
+
+/* command to write slim */
+/***********************************************/
+void
+lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr,
+ uint32_t value)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ /* addr = 0x090597 is AUTO ABTS disable for ELS commands */
+ /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */
+
+ /*
+ * Always turn on DELAYED ABTS for ELS timeouts
+ */
+ if ((addr == 0x052198) && (value == 0))
+ value = 1;
+
+ mb->un.varWords[0] = addr;
+ mb->un.varWords[1] = value;
+
+ mb->mbxCommand = MBX_SET_SLIM;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/**********************************************/
+/* lpfc_read_nv Issue a READ CONFIG */
+/* mailbox command */
+/**********************************************/
+void
+lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->mbxCommand = MBX_READ_CONFIG;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+/********************************************/
+/* lpfc_reg_login Issue a REG_LOGIN */
+/* mailbox command */
+/********************************************/
+int
+lpfc_reg_login(struct lpfc_hba * phba,
+ uint32_t did, uint8_t * param, LPFC_MBOXQ_t * pmb, uint32_t flag)
+{
+ uint8_t *sparam;
+ struct lpfc_dmabuf *mp;
+ MAILBOX_t *mb;
+ struct lpfc_sli *psli;
+
+ psli = &phba->sli;
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->un.varRegLogin.rpi = 0;
+ mb->un.varRegLogin.did = did;
+ mb->un.varWords[30] = flag; /* Set flag to issue action on cmpl */
+
+ mb->mbxOwner = OWN_HOST;
+
+ /* Get a buffer to hold NPorts Service Parameters */
+ if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL)) == NULL) ||
+ ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) {
+ if (mp)
+ kfree(mp);
+
+ mb->mbxCommand = MBX_REG_LOGIN64;
+ /* REG_LOGIN: no buffers */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_MBOX,
+ "%d:0302 REG_LOGIN: no buffers Data x%x x%x\n",
+ phba->brd_no,
+ (uint32_t) did, (uint32_t) flag);
+ return (1);
+ }
+ INIT_LIST_HEAD(&mp->list);
+ sparam = mp->virt;
+
+ /* Copy param's into a new buffer */
+ memcpy(sparam, param, sizeof (struct serv_parm));
+
+ /* save address for completion */
+ pmb->context1 = (uint8_t *) mp;
+
+ mb->mbxCommand = MBX_REG_LOGIN64;
+ mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
+ mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys);
+ mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys);
+
+ return (0);
+}
+
+/**********************************************/
+/* lpfc_unreg_login Issue a UNREG_LOGIN */
+/* mailbox command */
+/**********************************************/
+void
+lpfc_unreg_login(struct lpfc_hba * phba, uint32_t rpi, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->un.varUnregLogin.rpi = (uint16_t) rpi;
+ mb->un.varUnregLogin.rsvd1 = 0;
+
+ mb->mbxCommand = MBX_UNREG_LOGIN;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+static void
+lpfc_config_pcb_setup(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+ PCB_t *pcbp = &phba->slim2p->pcb;
+ dma_addr_t pdma_addr;
+ uint32_t offset;
+ uint32_t iocbCnt;
+ int i;
+
+ psli->MBhostaddr = (uint32_t *)&phba->slim2p->mbx;
+ pcbp->maxRing = (psli->num_rings - 1);
+
+ iocbCnt = 0;
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->ring[i];
+ /* A ring MUST have both cmd and rsp entries defined to be
+ valid */
+ if ((pring->numCiocb == 0) || (pring->numRiocb == 0)) {
+ pcbp->rdsc[i].cmdEntries = 0;
+ pcbp->rdsc[i].rspEntries = 0;
+ pcbp->rdsc[i].cmdAddrHigh = 0;
+ pcbp->rdsc[i].rspAddrHigh = 0;
+ pcbp->rdsc[i].cmdAddrLow = 0;
+ pcbp->rdsc[i].rspAddrLow = 0;
+ pring->cmdringaddr = NULL;
+ pring->rspringaddr = NULL;
+ continue;
+ }
+ /* Command ring setup for ring */
+ pring->cmdringaddr =
+ (void *)&phba->slim2p->IOCBs[iocbCnt];
+ pcbp->rdsc[i].cmdEntries = pring->numCiocb;
+
+ offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] -
+ (uint8_t *)phba->slim2p;
+ pdma_addr = phba->slim2p_mapping + offset;
+ pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr);
+ pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr);
+ iocbCnt += pring->numCiocb;
+
+ /* Response ring setup for ring */
+ pring->rspringaddr =
+ (void *)&phba->slim2p->IOCBs[iocbCnt];
+
+ pcbp->rdsc[i].rspEntries = pring->numRiocb;
+ offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] -
+ (uint8_t *)phba->slim2p;
+ pdma_addr = phba->slim2p_mapping + offset;
+ pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr);
+ pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr);
+ iocbCnt += pring->numRiocb;
+ }
+}
+
+void
+lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb;
+
+ mb = &pmb->mb;
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+ mb->un.varRdRev.cv = 1;
+ mb->mbxCommand = MBX_READ_REV;
+ mb->mbxOwner = OWN_HOST;
+ return;
+}
+
+void
+lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
+{
+ int i;
+ MAILBOX_t *mb = &pmb->mb;
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+
+ memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+ mb->un.varCfgRing.ring = ring;
+ mb->un.varCfgRing.maxOrigXchg = 0;
+ mb->un.varCfgRing.maxRespXchg = 0;
+ mb->un.varCfgRing.recvNotify = 1;
+
+ psli = &phba->sli;
+ pring = &psli->ring[ring];
+ mb->un.varCfgRing.numMask = pring->num_mask;
+ mb->mbxCommand = MBX_CONFIG_RING;
+ mb->mbxOwner = OWN_HOST;
+
+ /* Is this ring configured for a specific profile */
+ if (pring->prt[0].profile) {
+ mb->un.varCfgRing.profile = pring->prt[0].profile;
+ return;
+ }
+
+ /* Otherwise we setup specific rctl / type masks for this ring */
+ for (i = 0; i < pring->num_mask; i++) {
+ mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl;
+ if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ)
+ mb->un.varCfgRing.rrRegs[i].rmask = 0xff;
+ else
+ mb->un.varCfgRing.rrRegs[i].rmask = 0xfe;
+ mb->un.varCfgRing.rrRegs[i].tval = pring->prt[i].type;
+ mb->un.varCfgRing.rrRegs[i].tmask = 0xff;
+ }
+
+ return;
+}
+
+void
+lpfc_config_port(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ MAILBOX_t *mb = &pmb->mb;
+ dma_addr_t pdma_addr;
+ uint32_t bar_low, bar_high;
+ size_t offset;
+ HGP hgp;
+ void __iomem *to_slim;
+
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_CONFIG_PORT;
+ mb->mbxOwner = OWN_HOST;
+
+ mb->un.varCfgPort.pcbLen = sizeof(PCB_t);
+
+ offset = (uint8_t *)&phba->slim2p->pcb - (uint8_t *)phba->slim2p;
+ pdma_addr = phba->slim2p_mapping + offset;
+ mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
+ mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
+
+ /* Now setup pcb */
+ phba->slim2p->pcb.type = TYPE_NATIVE_SLI2;
+ phba->slim2p->pcb.feature = FEATURE_INITIAL_SLI2;
+
+ /* Setup Mailbox pointers */
+ phba->slim2p->pcb.mailBoxSize = sizeof(MAILBOX_t);
+ offset = (uint8_t *)&phba->slim2p->mbx - (uint8_t *)phba->slim2p;
+ pdma_addr = phba->slim2p_mapping + offset;
+ phba->slim2p->pcb.mbAddrHigh = putPaddrHigh(pdma_addr);
+ phba->slim2p->pcb.mbAddrLow = putPaddrLow(pdma_addr);
+
+ /*
+ * Setup Host Group ring pointer.
+ *
+ * For efficiency reasons, the ring get/put pointers can be
+ * placed in adapter memory (SLIM) rather than in host memory.
+ * This allows firmware to avoid PCI reads/writes when updating
+ * and checking pointers.
+ *
+ * The firmware recognizes the use of SLIM memory by comparing
+ * the address of the get/put pointers structure with that of
+ * the SLIM BAR (BAR0).
+ *
+ * Caution: be sure to use the PCI config space value of BAR0/BAR1
+ * (the hardware's view of the base address), not the OS's
+ * value of pci_resource_start() as the OS value may be a cookie
+ * for ioremap/iomap.
+ */
+
+
+ pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_0, &bar_low);
+ pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_1, &bar_high);
+
+
+ /* mask off BAR0's flag bits 0 - 3 */
+ phba->slim2p->pcb.hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) +
+ (SLIMOFF*sizeof(uint32_t));
+ if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64)
+ phba->slim2p->pcb.hgpAddrHigh = bar_high;
+ else
+ phba->slim2p->pcb.hgpAddrHigh = 0;
+ /* write HGP data to SLIM at the required longword offset */
+ memset(&hgp, 0, sizeof(HGP));
+ to_slim = phba->MBslimaddr + (SLIMOFF*sizeof (uint32_t));
+ lpfc_memcpy_to_slim(to_slim, &hgp, sizeof (HGP));
+
+ /* Setup Port Group ring pointer */
+ offset = (uint8_t *)&phba->slim2p->mbx.us.s2.port -
+ (uint8_t *)phba->slim2p;
+ pdma_addr = phba->slim2p_mapping + offset;
+ phba->slim2p->pcb.pgpAddrHigh = putPaddrHigh(pdma_addr);
+ phba->slim2p->pcb.pgpAddrLow = putPaddrLow(pdma_addr);
+
+ /* Use callback routine to setp rings in the pcb */
+ lpfc_config_pcb_setup(phba);
+
+ /* special handling for LC HBAs */
+ if (lpfc_is_LC_HBA(phba->pcidev->device)) {
+ uint32_t hbainit[5];
+
+ lpfc_hba_init(phba, hbainit);
+
+ memcpy(&mb->un.varCfgPort.hbainit, hbainit, 20);
+ }
+
+ /* Swap PCB if needed */
+ lpfc_sli_pcimem_bcopy(&phba->slim2p->pcb, &phba->slim2p->pcb,
+ sizeof (PCB_t));
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "%d:0405 Service Level Interface (SLI) 2 selected\n",
+ phba->brd_no);
+}
+
+void
+lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
+{
+ struct lpfc_sli *psli;
+
+ psli = &phba->sli;
+
+ list_add_tail(&mbq->list, &psli->mboxq);
+
+ psli->mboxq_cnt++;
+
+ return;
+}
+
+LPFC_MBOXQ_t *
+lpfc_mbox_get(struct lpfc_hba * phba)
+{
+ LPFC_MBOXQ_t *mbq = NULL;
+ struct lpfc_sli *psli = &phba->sli;
+
+ list_remove_head((&psli->mboxq), mbq, LPFC_MBOXQ_t,
+ list);
+ if (mbq) {
+ psli->mboxq_cnt--;
+ }
+
+ return mbq;
+}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
new file mode 100644
index 000000000000..4397e1160712
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -0,0 +1,179 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_mem.c 1.79 2005/04/13 14:25:50EDT sf_support Exp $
+ */
+
+#include <linux/mempool.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+
+#define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */
+#define LPFC_MEM_POOL_SIZE 64 /* max elem in non-DMA safety pool */
+
+static void *
+lpfc_pool_kmalloc(unsigned int gfp_flags, void *data)
+{
+ return kmalloc((unsigned long)data, gfp_flags);
+}
+
+static void
+lpfc_pool_kfree(void *obj, void *data)
+{
+ kfree(obj);
+}
+
+int
+lpfc_mem_alloc(struct lpfc_hba * phba)
+{
+ struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+ int i;
+
+ phba->lpfc_scsi_dma_buf_pool = pci_pool_create("lpfc_scsi_dma_buf_pool",
+ phba->pcidev, phba->cfg_sg_dma_buf_size, 8, 0);
+ if (!phba->lpfc_scsi_dma_buf_pool)
+ goto fail;
+
+ phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev,
+ LPFC_BPL_SIZE, 8,0);
+ if (!phba->lpfc_mbuf_pool)
+ goto fail_free_dma_buf_pool;
+
+ pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) *
+ LPFC_MBUF_POOL_SIZE, GFP_KERNEL);
+ pool->max_count = 0;
+ pool->current_count = 0;
+ for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) {
+ pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool,
+ GFP_KERNEL, &pool->elements[i].phys);
+ if (!pool->elements[i].virt)
+ goto fail_free_mbuf_pool;
+ pool->max_count++;
+ pool->current_count++;
+ }
+
+ phba->mbox_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+ lpfc_pool_kmalloc, lpfc_pool_kfree,
+ (void *)(unsigned long)sizeof(LPFC_MBOXQ_t));
+ if (!phba->mbox_mem_pool)
+ goto fail_free_mbuf_pool;
+
+ phba->nlp_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+ lpfc_pool_kmalloc, lpfc_pool_kfree,
+ (void *)(unsigned long)sizeof(struct lpfc_nodelist));
+ if (!phba->nlp_mem_pool)
+ goto fail_free_mbox_pool;
+
+ return 0;
+
+ fail_free_mbox_pool:
+ mempool_destroy(phba->mbox_mem_pool);
+ fail_free_mbuf_pool:
+ while (--i)
+ pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+ pool->elements[i].phys);
+ kfree(pool->elements);
+ pci_pool_destroy(phba->lpfc_mbuf_pool);
+ fail_free_dma_buf_pool:
+ pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
+ fail:
+ return -ENOMEM;
+}
+
+void
+lpfc_mem_free(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+ LPFC_MBOXQ_t *mbox, *next_mbox;
+ struct lpfc_dmabuf *mp;
+ int i;
+
+ list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) {
+ mp = (struct lpfc_dmabuf *) (mbox->context1);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ list_del(&mbox->list);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ }
+
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ if (psli->mbox_active) {
+ mbox = psli->mbox_active;
+ mp = (struct lpfc_dmabuf *) (mbox->context1);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ mempool_free(mbox, phba->mbox_mem_pool);
+ psli->mbox_active = NULL;
+ }
+
+ for (i = 0; i < pool->current_count; i++)
+ pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+ pool->elements[i].phys);
+ kfree(pool->elements);
+ mempool_destroy(phba->nlp_mem_pool);
+ mempool_destroy(phba->mbox_mem_pool);
+
+ pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
+ pci_pool_destroy(phba->lpfc_mbuf_pool);
+}
+
+void *
+lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle)
+{
+ struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+ void *ret;
+
+ ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_KERNEL, handle);
+
+ if (!ret && ( mem_flags & MEM_PRI) && pool->current_count) {
+ pool->current_count--;
+ ret = pool->elements[pool->current_count].virt;
+ *handle = pool->elements[pool->current_count].phys;
+ }
+ return ret;
+}
+
+void
+lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
+{
+ struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+
+ if (pool->current_count < pool->max_count) {
+ pool->elements[pool->current_count].virt = virt;
+ pool->elements[pool->current_count].phys = dma;
+ pool->current_count++;
+ } else {
+ pci_pool_free(phba->lpfc_mbuf_pool, virt, dma);
+ }
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
new file mode 100644
index 000000000000..e7470a4738c5
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -0,0 +1,1842 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_nportdisc.c 1.179 2005/04/13 11:59:13EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+
+
+/* Called to verify a rcv'ed ADISC was intended for us. */
+static int
+lpfc_check_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ struct lpfc_name * nn, struct lpfc_name * pn)
+{
+ /* Compare the ADISC rsp WWNN / WWPN matches our internal node
+ * table entry for that node.
+ */
+ if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)) != 0)
+ return (0);
+
+ if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0)
+ return (0);
+
+ /* we match, return success */
+ return (1);
+}
+
+
+int
+lpfc_check_sparm(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, struct serv_parm * sp,
+ uint32_t class)
+{
+ volatile struct serv_parm *hsp = &phba->fc_sparam;
+ /* First check for supported version */
+
+ /* Next check for class validity */
+ if (sp->cls1.classValid) {
+
+ if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb)
+ sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb;
+ if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb)
+ sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb;
+ } else if (class == CLASS1) {
+ return (0);
+ }
+
+ if (sp->cls2.classValid) {
+
+ if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb)
+ sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb;
+ if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb)
+ sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb;
+ } else if (class == CLASS2) {
+ return (0);
+ }
+
+ if (sp->cls3.classValid) {
+
+ if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb)
+ sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb;
+ if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb)
+ sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb;
+ } else if (class == CLASS3) {
+ return (0);
+ }
+
+ if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb)
+ sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb;
+ if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb)
+ sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb;
+
+ /* If check is good, copy wwpn wwnn into ndlp */
+ memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name));
+ memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name));
+ return (1);
+}
+
+static void *
+lpfc_check_elscmpl_iocb(struct lpfc_hba * phba,
+ struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_dmabuf *pcmd, *prsp;
+ uint32_t *lp;
+ void *ptr = NULL;
+ IOCB_t *irsp;
+
+ irsp = &rspiocb->iocb;
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+ /* For lpfc_els_abort, context2 could be zero'ed to delay
+ * freeing associated memory till after ABTS completes.
+ */
+ if (pcmd) {
+ prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf,
+ list);
+ if (prsp) {
+ lp = (uint32_t *) prsp->virt;
+ ptr = (void *)((uint8_t *)lp + sizeof(uint32_t));
+ }
+ }
+ else {
+ /* Force ulpStatus error since we are returning NULL ptr */
+ if (!(irsp->ulpStatus)) {
+ irsp->ulpStatus = IOSTAT_LOCAL_REJECT;
+ irsp->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ }
+ ptr = NULL;
+ }
+ return (ptr);
+}
+
+
+/*
+ * Free resources / clean up outstanding I/Os
+ * associated with a LPFC_NODELIST entry. This
+ * routine effectively results in a "software abort".
+ */
+int
+lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ int send_abts)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ IOCB_t *icmd;
+ int found = 0;
+
+ /* Abort outstanding I/O on NPort <nlp_DID> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+ "%d:0201 Abort outstanding I/O on NPort x%x "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, ndlp->nlp_rpi);
+
+ psli = &phba->sli;
+ pring = &psli->ring[LPFC_ELS_RING];
+
+ /* First check the txq */
+ do {
+ found = 0;
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ /* Check to see if iocb matches the nport we are looking
+ for */
+ if ((lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))) {
+ found = 1;
+ /* It matches, so deque and call compl with an
+ error */
+ list_del(&iocb->list);
+ pring->txq_cnt--;
+ if (iocb->iocb_cmpl) {
+ icmd = &iocb->iocb;
+ icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&iocb->list,
+ &phba->lpfc_iocb_list);
+ }
+ break;
+ }
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ } while (found);
+
+ /* Everything on txcmplq will be returned by firmware
+ * with a no rpi / linkdown / abort error. For ring 0,
+ * ELS discovery, we want to get rid of it right here.
+ */
+ /* Next check the txcmplq */
+ do {
+ found = 0;
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
+ list) {
+ /* Check to see if iocb matches the nport we are looking
+ for */
+ if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) {
+ found = 1;
+ /* It matches, so deque and call compl with an
+ error */
+ list_del(&iocb->list);
+ pring->txcmplq_cnt--;
+
+ icmd = &iocb->iocb;
+ /* If the driver is completing an ELS
+ * command early, flush it out of the firmware.
+ */
+ if (send_abts &&
+ (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) &&
+ (icmd->un.elsreq64.bdl.ulpIoTag32)) {
+ lpfc_sli_issue_abort_iotag32(phba,
+ pring, iocb);
+ }
+ if (iocb->iocb_cmpl) {
+ icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&iocb->list,
+ &phba->lpfc_iocb_list);
+ }
+ break;
+ }
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ } while(found);
+
+ /* If we are delaying issuing an ELS command, cancel it */
+ if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+ ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+ del_timer_sync(&ndlp->nlp_delayfunc);
+ if (!list_empty(&ndlp->els_retry_evt.evt_listp))
+ list_del_init(&ndlp->els_retry_evt.evt_listp);
+ }
+ return (0);
+}
+
+static int
+lpfc_rcv_plogi(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ IOCB_t *icmd;
+ struct serv_parm *sp;
+ LPFC_MBOXQ_t *mbox;
+ struct ls_rjt stat;
+ int rc;
+
+ memset(&stat, 0, sizeof (struct ls_rjt));
+ if (phba->hba_state <= LPFC_FLOGI) {
+ /* Before responding to PLOGI, check for pt2pt mode.
+ * If we are pt2pt, with an outstanding FLOGI, abort
+ * the FLOGI and resend it first.
+ */
+ if (phba->fc_flag & FC_PT2PT) {
+ lpfc_els_abort_flogi(phba);
+ if (!(phba->fc_flag & FC_PT2PT_PLOGI)) {
+ /* If the other side is supposed to initiate
+ * the PLOGI anyway, just ACC it now and
+ * move on with discovery.
+ */
+ phba->fc_edtov = FF_DEF_EDTOV;
+ phba->fc_ratov = FF_DEF_RATOV;
+ /* Start discovery - this should just do
+ CLEAR_LA */
+ lpfc_disc_start(phba);
+ }
+ else {
+ lpfc_initial_flogi(phba);
+ }
+ }
+ else {
+ stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb,
+ ndlp);
+ return 0;
+ }
+ }
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+ sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+ if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3) == 0)) {
+ /* Reject this request because invalid parameters */
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+ return (0);
+ }
+ icmd = &cmdiocb->iocb;
+
+ /* PLOGI chkparm OK */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_ELS,
+ "%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag,
+ ndlp->nlp_rpi);
+
+ if ((phba->cfg_fcp_class == 2) &&
+ (sp->cls2.classValid)) {
+ ndlp->nlp_fcp_info |= CLASS2;
+ } else {
+ ndlp->nlp_fcp_info |= CLASS3;
+ }
+ ndlp->nlp_class_sup = 0;
+ if (sp->cls1.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS1;
+ if (sp->cls2.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS2;
+ if (sp->cls3.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS3;
+ if (sp->cls4.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS4;
+ ndlp->nlp_maxframe =
+ ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;
+
+ /* no need to reg_login if we are already in one of these states */
+ switch(ndlp->nlp_state) {
+ case NLP_STE_NPR_NODE:
+ if (!(ndlp->nlp_flag & NLP_NPR_ADISC))
+ break;
+ case NLP_STE_REG_LOGIN_ISSUE:
+ case NLP_STE_PRLI_ISSUE:
+ case NLP_STE_UNMAPPED_NODE:
+ case NLP_STE_MAPPED_NODE:
+ lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0);
+ return (1);
+ }
+
+ if ((phba->fc_flag & FC_PT2PT)
+ && !(phba->fc_flag & FC_PT2PT_PLOGI)) {
+ /* rcv'ed PLOGI decides what our NPortId will be */
+ phba->fc_myDID = icmd->un.rcvels.parmRo;
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (mbox == NULL)
+ goto out;
+ lpfc_config_link(phba, mbox);
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox
+ (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+
+ lpfc_can_disctmo(phba);
+ }
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (mbox == NULL)
+ goto out;
+
+ if (lpfc_reg_login(phba, icmd->un.rcvels.remoteID,
+ (uint8_t *) sp, mbox, 0)) {
+ mempool_free( mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+
+ /* ACC PLOGI rsp command needs to execute first,
+ * queue this mbox command to be processed later.
+ */
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
+ mbox->context2 = ndlp;
+ ndlp->nlp_flag |= NLP_ACC_REGLOGIN;
+
+ /* If there is an outstanding PLOGI issued, abort it before
+ * sending ACC rsp to PLOGI recieved.
+ */
+ if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(phba, ndlp, 1);
+ }
+ ndlp->nlp_flag |= NLP_RCV_PLOGI;
+ lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0);
+ return (1);
+
+out:
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+ return (0);
+}
+
+static int
+lpfc_rcv_padisc(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ struct serv_parm *sp;
+ struct lpfc_name *pnn, *ppn;
+ struct ls_rjt stat;
+ ADISC *ap;
+ IOCB_t *icmd;
+ uint32_t *lp;
+ uint32_t cmd;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+
+ cmd = *lp++;
+ if (cmd == ELS_CMD_ADISC) {
+ ap = (ADISC *) lp;
+ pnn = (struct lpfc_name *) & ap->nodeName;
+ ppn = (struct lpfc_name *) & ap->portName;
+ } else {
+ sp = (struct serv_parm *) lp;
+ pnn = (struct lpfc_name *) & sp->nodeName;
+ ppn = (struct lpfc_name *) & sp->portName;
+ }
+
+ icmd = &cmdiocb->iocb;
+ if ((icmd->ulpStatus == 0) &&
+ (lpfc_check_adisc(phba, ndlp, pnn, ppn))) {
+ if (cmd == ELS_CMD_ADISC) {
+ lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp);
+ }
+ else {
+ lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp,
+ NULL, 0);
+ }
+ return (1);
+ }
+ /* Reject this request because invalid parameters */
+ stat.un.b.lsRjtRsvd0 = 0;
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+ stat.un.b.vendorUnique = 0;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+
+ ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+ /* 1 sec timeout */
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ return (0);
+}
+
+static int
+lpfc_rcv_logo(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp,
+ struct lpfc_iocbq *cmdiocb)
+{
+ /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */
+ /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary
+ * PLOGIs during LOGO storms from a device.
+ */
+ ndlp->nlp_flag |= NLP_LOGO_ACC;
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+ if (!(ndlp->nlp_type & NLP_FABRIC)) {
+ /* Only try to re-login if this is NOT a Fabric Node */
+ ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ }
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+
+ ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ /* The driver has to wait until the ACC completes before it continues
+ * processing the LOGO. The action will resume in
+ * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an
+ * unreg_login, the driver waits so the ACC does not get aborted.
+ */
+ return (0);
+}
+
+static void
+lpfc_rcv_prli(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ uint32_t *lp;
+ PRLI *npr;
+ struct fc_rport *rport = ndlp->rport;
+ u32 roles;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+ npr = (PRLI *) ((uint8_t *) lp + sizeof (uint32_t));
+
+ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) &&
+ (npr->prliType == PRLI_FCP_TYPE)) {
+ if (npr->initiatorFunc)
+ ndlp->nlp_type |= NLP_FCP_INITIATOR;
+ if (npr->targetFunc)
+ ndlp->nlp_type |= NLP_FCP_TARGET;
+ if (npr->Retry)
+ ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+ }
+ if (rport) {
+ /* We need to update the rport role values */
+ roles = FC_RPORT_ROLE_UNKNOWN;
+ if (ndlp->nlp_type & NLP_FCP_INITIATOR)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (ndlp->nlp_type & NLP_FCP_TARGET)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+ fc_remote_port_rolechg(rport, roles);
+ }
+}
+
+static uint32_t
+lpfc_disc_set_adisc(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp)
+{
+ /* Check config parameter use-adisc or FCP-2 */
+ if ((phba->cfg_use_adisc == 0) &&
+ !(phba->fc_flag & FC_RSCN_MODE)) {
+ if (!(ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE))
+ return (0);
+ }
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_ADISC;
+ spin_unlock_irq(phba->host->host_lock);
+ return (1);
+}
+
+static uint32_t
+lpfc_disc_noop(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ /* This routine does nothing, just return the current state */
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_disc_illegal(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0253 Illegal State Transition: node x%x event x%x, "
+ "state x%x Data: x%x x%x\n",
+ phba->brd_no,
+ ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi,
+ ndlp->nlp_flag);
+ return (ndlp->nlp_state);
+}
+
+/* Start of Discovery State Machine routines */
+
+static uint32_t
+lpfc_rcv_plogi_unused_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ if (lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+ ndlp->nlp_state = NLP_STE_UNUSED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+ return (ndlp->nlp_state);
+ }
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_rcv_els_unused_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ lpfc_issue_els_logo(phba, ndlp, 0);
+ lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_unused_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_LOGO_ACC;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+ lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_logo_unused_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_rm_unused_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_rcv_plogi_plogi_issue(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = arg;
+ struct lpfc_dmabuf *pcmd;
+ struct serv_parm *sp;
+ uint32_t *lp;
+ struct ls_rjt stat;
+ int port_cmp;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ lp = (uint32_t *) pcmd->virt;
+ sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+
+ memset(&stat, 0, sizeof (struct ls_rjt));
+
+ /* For a PLOGI, we only accept if our portname is less
+ * than the remote portname.
+ */
+ phba->fc_stat.elsLogiCol++;
+ port_cmp = memcmp(&phba->fc_portname, &sp->portName,
+ sizeof (struct lpfc_name));
+
+ if (port_cmp >= 0) {
+ /* Reject this request because the remote node will accept
+ ours */
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+ }
+ else {
+ lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+ } /* if our portname was less */
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(phba, ndlp, 1);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+
+ if (evt == NLP_EVT_RCV_LOGO) {
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+ }
+ else {
+ lpfc_issue_els_logo(phba, ndlp, 0);
+ }
+
+ /* Put ndlp in npr list set plogi timer for 1 sec */
+ ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb, *rspiocb;
+ struct lpfc_dmabuf *pcmd, *prsp;
+ uint32_t *lp;
+ IOCB_t *irsp;
+ struct serv_parm *sp;
+ LPFC_MBOXQ_t *mbox;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+ rspiocb = cmdiocb->context_un.rsp_iocb;
+
+ if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
+ return (ndlp->nlp_state);
+ }
+
+ irsp = &rspiocb->iocb;
+
+ if (irsp->ulpStatus)
+ goto out;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+ prsp = list_get_first(&pcmd->list,
+ struct lpfc_dmabuf,
+ list);
+ lp = (uint32_t *) prsp->virt;
+
+ sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+ if (!lpfc_check_sparm(phba, ndlp, sp, CLASS3))
+ goto out;
+
+ /* PLOGI chkparm OK */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_ELS,
+ "%d:0121 PLOGI chkparm OK "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
+
+ if ((phba->cfg_fcp_class == 2) &&
+ (sp->cls2.classValid)) {
+ ndlp->nlp_fcp_info |= CLASS2;
+ } else {
+ ndlp->nlp_fcp_info |= CLASS3;
+ }
+ ndlp->nlp_class_sup = 0;
+ if (sp->cls1.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS1;
+ if (sp->cls2.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS2;
+ if (sp->cls3.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS3;
+ if (sp->cls4.classValid)
+ ndlp->nlp_class_sup |= FC_COS_CLASS4;
+ ndlp->nlp_maxframe =
+ ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
+ sp->cmn.bbRcvSizeLsb;
+
+ if (!(mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL)))
+ goto out;
+
+ lpfc_unreg_rpi(phba, ndlp);
+ if (lpfc_reg_login
+ (phba, irsp->un.elsreq64.remoteID,
+ (uint8_t *) sp, mbox, 0) == 0) {
+ /* set_slim mailbox command needs to
+ * execute first, queue this command to
+ * be processed later.
+ */
+ switch(ndlp->nlp_DID) {
+ case NameServer_DID:
+ mbox->mbox_cmpl =
+ lpfc_mbx_cmpl_ns_reg_login;
+ break;
+ case FDMI_DID:
+ mbox->mbox_cmpl =
+ lpfc_mbx_cmpl_fdmi_reg_login;
+ break;
+ default:
+ mbox->mbox_cmpl =
+ lpfc_mbx_cmpl_reg_login;
+ }
+ mbox->context2 = ndlp;
+ if (lpfc_sli_issue_mbox(phba, mbox,
+ (MBX_NOWAIT | MBX_STOP_IOCB))
+ != MBX_NOT_FINISHED) {
+ ndlp->nlp_state =
+ NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_list(phba, ndlp,
+ NLP_REGLOGIN_LIST);
+ return (ndlp->nlp_state);
+ }
+ mempool_free(mbox, phba->mbox_mem_pool);
+ } else {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ }
+
+
+ out:
+ /* Free this node since the driver cannot login or has the wrong
+ sparm */
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_rm_plogi_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_plogi_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ /* software abort outstanding ADISC */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ if (lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+ return (ndlp->nlp_state);
+ }
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* software abort outstanding ADISC */
+ lpfc_els_abort(phba, ndlp, 0);
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* Treat like rcv logo */
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_adisc_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb, *rspiocb;
+ IOCB_t *irsp;
+ ADISC *ap;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+ rspiocb = cmdiocb->context_un.rsp_iocb;
+
+ ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+ irsp = &rspiocb->iocb;
+
+ if ((irsp->ulpStatus) ||
+ (!lpfc_check_adisc(phba, ndlp, &ap->nodeName, &ap->portName))) {
+ ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+ /* 1 sec timeout */
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+
+ memset(&ndlp->nlp_nodename, 0, sizeof (struct lpfc_name));
+ memset(&ndlp->nlp_portname, 0, sizeof (struct lpfc_name));
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ lpfc_unreg_rpi(phba, ndlp);
+ return (ndlp->nlp_state);
+ }
+ ndlp->nlp_state = NLP_STE_MAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ /* software abort outstanding ADISC */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_adisc_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ /* software abort outstanding ADISC */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+
+ lpfc_disc_set_adisc(phba, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp,
+ void *arg, uint32_t evt)
+{
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ uint32_t did;
+
+ pmb = (LPFC_MBOXQ_t *) arg;
+ mb = &pmb->mb;
+ did = mb->un.varWords[1];
+ if (mb->mbxStatus) {
+ /* RegLogin failed */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_DISCOVERY,
+ "%d:0246 RegLogin failed Data: x%x x%x x%x\n",
+ phba->brd_no,
+ did, mb->mbxStatus, phba->hba_state);
+
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+
+ lpfc_issue_els_logo(phba, ndlp, 0);
+ /* Put ndlp in npr list set plogi timer for 1 sec */
+ ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ return (ndlp->nlp_state);
+ }
+
+ if (ndlp->nlp_rpi != 0)
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+
+ ndlp->nlp_rpi = mb->un.varWords[0];
+ lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+
+ /* Only if we are not a fabric nport do we issue PRLI */
+ if (!(ndlp->nlp_type & NLP_FABRIC)) {
+ ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+ lpfc_issue_els_prli(phba, ndlp, 0);
+ } else {
+ ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+ }
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* Software abort outstanding PRLI before sending acc */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+/* This routine is envoked when we rcv a PRLO request from a nport
+ * we are logged into. We should send back a PRLO rsp setting the
+ * appropriate bits.
+ * NEXT STATE = PRLI_ISSUE
+ */
+static uint32_t
+lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_prli_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb, *rspiocb;
+ IOCB_t *irsp;
+ PRLI *npr;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+ rspiocb = cmdiocb->context_un.rsp_iocb;
+ npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+
+ irsp = &rspiocb->iocb;
+ if (irsp->ulpStatus) {
+ ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+ return (ndlp->nlp_state);
+ }
+
+ /* Check out PRLI rsp */
+ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) &&
+ (npr->prliType == PRLI_FCP_TYPE)) {
+ if (npr->initiatorFunc)
+ ndlp->nlp_type |= NLP_FCP_INITIATOR;
+ if (npr->targetFunc)
+ ndlp->nlp_type |= NLP_FCP_TARGET;
+ if (npr->Retry)
+ ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+ }
+
+ ndlp->nlp_state = NLP_STE_MAPPED_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST);
+ return (ndlp->nlp_state);
+}
+
+/*! lpfc_device_rm_prli_issue
+ *
+ * \pre
+ * \post
+ * \param phba
+ * \param ndlp
+ * \param arg
+ * \param evt
+ * \return uint32_t
+ *
+ * \b Description:
+ * This routine is envoked when we a request to remove a nport we are in the
+ * process of PRLIing. We should software abort outstanding prli, unreg
+ * login, send a logout. We will change node state to UNUSED_NODE, put it
+ * on plogi list so it can be freed when LOGO completes.
+ *
+ */
+static uint32_t
+lpfc_device_rm_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ /* software abort outstanding PRLI */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+
+/*! lpfc_device_recov_prli_issue
+ *
+ * \pre
+ * \post
+ * \param phba
+ * \param ndlp
+ * \param arg
+ * \param evt
+ * \return uint32_t
+ *
+ * \b Description:
+ * The routine is envoked when the state of a device is unknown, like
+ * during a link down. We should remove the nodelist entry from the
+ * unmapped list, issue a UNREG_LOGIN, do a software abort of the
+ * outstanding PRLI command, then free the node entry.
+ */
+static uint32_t
+lpfc_device_recov_prli_issue(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ /* software abort outstanding PRLI */
+ lpfc_els_abort(phba, ndlp, 1);
+
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_prli(phba, ndlp, cmdiocb);
+ lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* Treat like rcv logo */
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_recov_unmap_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ lpfc_disc_set_adisc(phba, ndlp);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* flush the target */
+ spin_lock_irq(phba->host->host_lock);
+ lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+ ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);
+ spin_unlock_irq(phba->host->host_lock);
+
+ /* Treat like rcv logo */
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_recov_mapped_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ ndlp->nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_disc_set_adisc(phba, ndlp);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ /* Ignore PLOGI if we have an outstanding LOGO */
+ if (ndlp->nlp_flag & NLP_LOGO_SND) {
+ return (ndlp->nlp_state);
+ }
+
+ if (lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC);
+ spin_unlock_irq(phba->host->host_lock);
+ return (ndlp->nlp_state);
+ }
+
+ /* send PLOGI immediately, move to PLOGI issue state */
+ if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ }
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+ struct ls_rjt stat;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ memset(&stat, 0, sizeof (struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+
+ if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ if (ndlp->nlp_flag & NLP_NPR_ADISC) {
+ ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+ lpfc_issue_els_adisc(phba, ndlp, 0);
+ } else {
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ }
+ }
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_logo(phba, ndlp, cmdiocb);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+
+ if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ if (ndlp->nlp_flag & NLP_NPR_ADISC) {
+ ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+ lpfc_issue_els_adisc(phba, ndlp, 0);
+ } else {
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ }
+ }
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb;
+
+ cmdiocb = (struct lpfc_iocbq *) arg;
+
+ lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+ if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+ if (ndlp->nlp_last_elscmd == (unsigned long)ELS_CMD_PLOGI) {
+ return (ndlp->nlp_state);
+ } else {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+ spin_unlock_irq(phba->host->host_lock);
+ del_timer_sync(&ndlp->nlp_delayfunc);
+ if (!list_empty(&ndlp->els_retry_evt.evt_listp))
+ list_del_init(&ndlp->els_retry_evt.evt_listp);
+ }
+ }
+
+ ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+ lpfc_issue_els_plogi(phba, ndlp, 0);
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_logo_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ lpfc_unreg_rpi(phba, ndlp);
+ /* This routine does nothing, just return the current state */
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+
+ pmb = (LPFC_MBOXQ_t *) arg;
+ mb = &pmb->mb;
+
+ /* save rpi */
+ if (ndlp->nlp_rpi != 0)
+ lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+
+ ndlp->nlp_rpi = mb->un.varWords[0];
+ lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+
+ return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+ return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_npr_node(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg,
+ uint32_t evt)
+{
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(phba->host->host_lock);
+ return (ndlp->nlp_state);
+}
+
+
+/* This next section defines the NPort Discovery State Machine */
+
+/* There are 4 different double linked lists nodelist entries can reside on.
+ * The plogi list and adisc list are used when Link Up discovery or RSCN
+ * processing is needed. Each list holds the nodes that we will send PLOGI
+ * or ADISC on. These lists will keep track of what nodes will be effected
+ * by an RSCN, or a Link Up (Typically, all nodes are effected on Link Up).
+ * The unmapped_list will contain all nodes that we have successfully logged
+ * into at the Fibre Channel level. The mapped_list will contain all nodes
+ * that are mapped FCP targets.
+ */
+/*
+ * The bind list is a list of undiscovered (potentially non-existent) nodes
+ * that we have saved binding information on. This information is used when
+ * nodes transition from the unmapped to the mapped list.
+ */
+/* For UNUSED_NODE state, the node has just been allocated .
+ * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on
+ * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list
+ * and put on the unmapped list. For ADISC processing, the node is taken off
+ * the ADISC list and placed on either the mapped or unmapped list (depending
+ * on its previous state). Once on the unmapped list, a PRLI is issued and the
+ * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is
+ * changed to UNMAPPED_NODE. If the completion indicates a mapped
+ * node, the node is taken off the unmapped list. The binding list is checked
+ * for a valid binding, or a binding is automatically assigned. If binding
+ * assignment is unsuccessful, the node is left on the unmapped list. If
+ * binding assignment is successful, the associated binding list entry (if
+ * any) is removed, and the node is placed on the mapped list.
+ */
+/*
+ * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
+ * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
+ * expire, all effected nodes will receive a DEVICE_RM event.
+ */
+/*
+ * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists
+ * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap
+ * check, additional nodes may be added or removed (via DEVICE_RM) to / from
+ * the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated,
+ * we will first process the ADISC list. 32 entries are processed initially and
+ * ADISC is initited for each one. Completions / Events for each node are
+ * funnelled thru the state machine. As each node finishes ADISC processing, it
+ * starts ADISC for any nodes waiting for ADISC processing. If no nodes are
+ * waiting, and the ADISC list count is identically 0, then we are done. For
+ * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we
+ * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI
+ * list. 32 entries are processed initially and PLOGI is initited for each one.
+ * Completions / Events for each node are funnelled thru the state machine. As
+ * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting
+ * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is
+ * indentically 0, then we are done. We have now completed discovery / RSCN
+ * handling. Upon completion, ALL nodes should be on either the mapped or
+ * unmapped lists.
+ */
+
+static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
+ (struct lpfc_hba *, struct lpfc_nodelist *, void *, uint32_t) = {
+ /* Action routine Event Current State */
+ lpfc_rcv_plogi_unused_node, /* RCV_PLOGI UNUSED_NODE */
+ lpfc_rcv_els_unused_node, /* RCV_PRLI */
+ lpfc_rcv_logo_unused_node, /* RCV_LOGO */
+ lpfc_rcv_els_unused_node, /* RCV_ADISC */
+ lpfc_rcv_els_unused_node, /* RCV_PDISC */
+ lpfc_rcv_els_unused_node, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_cmpl_logo_unused_node, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_unused_node, /* DEVICE_RM */
+ lpfc_disc_illegal, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_plogi_issue, /* RCV_PLOGI PLOGI_ISSUE */
+ lpfc_rcv_els_plogi_issue, /* RCV_PRLI */
+ lpfc_rcv_els_plogi_issue, /* RCV_LOGO */
+ lpfc_rcv_els_plogi_issue, /* RCV_ADISC */
+ lpfc_rcv_els_plogi_issue, /* RCV_PDISC */
+ lpfc_rcv_els_plogi_issue, /* RCV_PRLO */
+ lpfc_cmpl_plogi_plogi_issue, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_plogi_issue, /* DEVICE_RM */
+ lpfc_device_recov_plogi_issue, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_adisc_issue, /* RCV_PLOGI ADISC_ISSUE */
+ lpfc_rcv_prli_adisc_issue, /* RCV_PRLI */
+ lpfc_rcv_logo_adisc_issue, /* RCV_LOGO */
+ lpfc_rcv_padisc_adisc_issue, /* RCV_ADISC */
+ lpfc_rcv_padisc_adisc_issue, /* RCV_PDISC */
+ lpfc_rcv_prlo_adisc_issue, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_cmpl_adisc_adisc_issue, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_adisc_issue, /* DEVICE_RM */
+ lpfc_device_recov_adisc_issue, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_reglogin_issue, /* RCV_PLOGI REG_LOGIN_ISSUE */
+ lpfc_rcv_prli_reglogin_issue, /* RCV_PLOGI */
+ lpfc_rcv_logo_reglogin_issue, /* RCV_LOGO */
+ lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC */
+ lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC */
+ lpfc_rcv_prlo_reglogin_issue, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_cmpl_reglogin_reglogin_issue,/* CMPL_REG_LOGIN */
+ lpfc_device_rm_reglogin_issue, /* DEVICE_RM */
+ lpfc_device_recov_reglogin_issue,/* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_prli_issue, /* RCV_PLOGI PRLI_ISSUE */
+ lpfc_rcv_prli_prli_issue, /* RCV_PRLI */
+ lpfc_rcv_logo_prli_issue, /* RCV_LOGO */
+ lpfc_rcv_padisc_prli_issue, /* RCV_ADISC */
+ lpfc_rcv_padisc_prli_issue, /* RCV_PDISC */
+ lpfc_rcv_prlo_prli_issue, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_cmpl_prli_prli_issue, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_prli_issue, /* DEVICE_RM */
+ lpfc_device_recov_prli_issue, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_unmap_node, /* RCV_PLOGI UNMAPPED_NODE */
+ lpfc_rcv_prli_unmap_node, /* RCV_PRLI */
+ lpfc_rcv_logo_unmap_node, /* RCV_LOGO */
+ lpfc_rcv_padisc_unmap_node, /* RCV_ADISC */
+ lpfc_rcv_padisc_unmap_node, /* RCV_PDISC */
+ lpfc_rcv_prlo_unmap_node, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_disc_illegal, /* DEVICE_RM */
+ lpfc_device_recov_unmap_node, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_mapped_node, /* RCV_PLOGI MAPPED_NODE */
+ lpfc_rcv_prli_mapped_node, /* RCV_PRLI */
+ lpfc_rcv_logo_mapped_node, /* RCV_LOGO */
+ lpfc_rcv_padisc_mapped_node, /* RCV_ADISC */
+ lpfc_rcv_padisc_mapped_node, /* RCV_PDISC */
+ lpfc_rcv_prlo_mapped_node, /* RCV_PRLO */
+ lpfc_disc_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_disc_illegal, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_disc_illegal, /* DEVICE_RM */
+ lpfc_device_recov_mapped_node, /* DEVICE_RECOVERY */
+
+ lpfc_rcv_plogi_npr_node, /* RCV_PLOGI NPR_NODE */
+ lpfc_rcv_prli_npr_node, /* RCV_PRLI */
+ lpfc_rcv_logo_npr_node, /* RCV_LOGO */
+ lpfc_rcv_padisc_npr_node, /* RCV_ADISC */
+ lpfc_rcv_padisc_npr_node, /* RCV_PDISC */
+ lpfc_rcv_prlo_npr_node, /* RCV_PRLO */
+ lpfc_disc_noop, /* CMPL_PLOGI */
+ lpfc_disc_noop, /* CMPL_PRLI */
+ lpfc_cmpl_logo_npr_node, /* CMPL_LOGO */
+ lpfc_disc_noop, /* CMPL_ADISC */
+ lpfc_cmpl_reglogin_npr_node, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_npr_node, /* DEVICE_RM */
+ lpfc_device_recov_npr_node, /* DEVICE_RECOVERY */
+};
+
+int
+lpfc_disc_state_machine(struct lpfc_hba * phba,
+ struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+ uint32_t cur_state, rc;
+ uint32_t(*func) (struct lpfc_hba *, struct lpfc_nodelist *, void *,
+ uint32_t);
+
+ ndlp->nlp_disc_refcnt++;
+ cur_state = ndlp->nlp_state;
+
+ /* DSM in event <evt> on NPort <nlp_DID> in state <cur_state> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0211 DSM in event x%x on NPort x%x in state %d "
+ "Data: x%x\n",
+ phba->brd_no,
+ evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag);
+
+ func = lpfc_disc_action[(cur_state * NLP_EVT_MAX_EVENT) + evt];
+ rc = (func) (phba, ndlp, arg, evt);
+
+ /* DSM out state <rc> on NPort <nlp_DID> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_DISCOVERY,
+ "%d:0212 DSM out state %d on NPort x%x Data: x%x\n",
+ phba->brd_no,
+ rc, ndlp->nlp_DID, ndlp->nlp_flag);
+
+ ndlp->nlp_disc_refcnt--;
+
+ /* Check to see if ndlp removal is deferred */
+ if ((ndlp->nlp_disc_refcnt == 0)
+ && (ndlp->nlp_flag & NLP_DELAY_REMOVE)) {
+ spin_lock_irq(phba->host->host_lock);
+ ndlp->nlp_flag &= ~NLP_DELAY_REMOVE;
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_nlp_remove(phba, ndlp);
+ return (NLP_STE_FREED_NODE);
+ }
+ if (rc == NLP_STE_FREED_NODE)
+ return (NLP_STE_FREED_NODE);
+ ndlp->nlp_state = rc;
+ return (rc);
+}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
new file mode 100644
index 000000000000..42fab03ad2ba
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -0,0 +1,1246 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_scsi.c 1.37 2005/04/13 14:27:09EDT sf_support Exp $
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_version.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+
+#define LPFC_RESET_WAIT 2
+#define LPFC_ABORT_WAIT 2
+
+static inline void lpfc_put_lun(struct fcp_cmnd *fcmd, unsigned int lun)
+{
+ fcmd->fcpLunLsl = 0;
+ fcmd->fcpLunMsl = swab16((uint16_t)lun);
+}
+
+/*
+ * This routine allocates a scsi buffer, which contains all the necessary
+ * information needed to initiate a SCSI I/O. The non-DMAable buffer region
+ * contains information to build the IOCB. The DMAable region contains
+ * memory for the FCP CMND, FCP RSP, and the inital BPL. In addition to
+ * allocating memeory, the FCP CMND and FCP RSP BDEs are setup in the BPL
+ * and the BPL BDE is setup in the IOCB.
+ */
+static struct lpfc_scsi_buf *
+lpfc_get_scsi_buf(struct lpfc_hba * phba)
+{
+ struct lpfc_scsi_buf *psb;
+ struct ulp_bde64 *bpl;
+ IOCB_t *iocb;
+ dma_addr_t pdma_phys;
+
+ psb = kmalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+ if (!psb)
+ return NULL;
+ memset(psb, 0, sizeof (struct lpfc_scsi_buf));
+ psb->scsi_hba = phba;
+
+ /*
+ * Get memory from the pci pool to map the virt space to pci bus space
+ * for an I/O. The DMA buffer includes space for the struct fcp_cmnd,
+ * struct fcp_rsp and the number of bde's necessary to support the
+ * sg_tablesize.
+ */
+ psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool, GFP_KERNEL,
+ &psb->dma_handle);
+ if (!psb->data) {
+ kfree(psb);
+ return NULL;
+ }
+
+ /* Initialize virtual ptrs to dma_buf region. */
+ memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+
+ psb->fcp_cmnd = psb->data;
+ psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
+ psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp);
+
+ /* Initialize local short-hand pointers. */
+ bpl = psb->fcp_bpl;
+ pdma_phys = psb->dma_handle;
+
+ /*
+ * The first two bdes are the FCP_CMD and FCP_RSP. The balance are sg
+ * list bdes. Initialize the first two and leave the rest for
+ * queuecommand.
+ */
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys));
+ bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys));
+ bpl->tus.f.bdeSize = sizeof (struct fcp_cmnd);
+ bpl->tus.f.bdeFlags = BUFF_USE_CMND;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+
+ /* Setup the physical region for the FCP RSP */
+ pdma_phys += sizeof (struct fcp_cmnd);
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys));
+ bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys));
+ bpl->tus.f.bdeSize = sizeof (struct fcp_rsp);
+ bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV);
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ /*
+ * Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
+ * initialize it with all known data now.
+ */
+ pdma_phys += (sizeof (struct fcp_rsp));
+ iocb = &psb->cur_iocbq.iocb;
+ iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+ iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys);
+ iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys);
+ iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64));
+ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL;
+ iocb->ulpBdeCount = 1;
+ iocb->ulpClass = CLASS3;
+
+ return psb;
+}
+
+static void
+lpfc_free_scsi_buf(struct lpfc_scsi_buf * psb)
+{
+ struct lpfc_hba *phba = psb->scsi_hba;
+
+ /*
+ * There are only two special cases to consider. (1) the scsi command
+ * requested scatter-gather usage or (2) the scsi command allocated
+ * a request buffer, but did not request use_sg. There is a third
+ * case, but it does not require resource deallocation.
+ */
+ if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) {
+ dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer,
+ psb->seg_cnt, psb->pCmd->sc_data_direction);
+ } else {
+ if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) {
+ dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys,
+ psb->pCmd->request_bufflen,
+ psb->pCmd->sc_data_direction);
+ }
+ }
+
+ list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
+}
+
+static int
+lpfc_scsi_prep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct scatterlist *sgel = NULL;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ dma_addr_t physaddr;
+ uint32_t i, num_bde = 0;
+ int datadir = scsi_cmnd->sc_data_direction;
+ int dma_error;
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither. Start the lpfc command prep by
+ * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+ * data bde entry.
+ */
+ bpl += 2;
+ if (scsi_cmnd->use_sg) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ sgel = (struct scatterlist *)scsi_cmnd->request_buffer;
+ lpfc_cmd->seg_cnt = dma_map_sg(&phba->pcidev->dev, sgel,
+ scsi_cmnd->use_sg, datadir);
+ if (lpfc_cmd->seg_cnt == 0)
+ return 1;
+
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d",
+ __FUNCTION__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ dma_unmap_sg(&phba->pcidev->dev, sgel,
+ lpfc_cmd->seg_cnt, datadir);
+ return 1;
+ }
+
+ /*
+ * The driver established a maximum scatter-gather segment count
+ * during probe that limits the number of sg elements in any
+ * single scsi command. Just run through the seg_cnt and format
+ * the bde's.
+ */
+ for (i = 0; i < lpfc_cmd->seg_cnt; i++) {
+ physaddr = sg_dma_address(sgel);
+ bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = 0;
+ else
+ bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+ sgel++;
+ num_bde++;
+ }
+ } else if (scsi_cmnd->request_buffer && scsi_cmnd->request_bufflen) {
+ physaddr = dma_map_single(&phba->pcidev->dev,
+ scsi_cmnd->request_buffer,
+ scsi_cmnd->request_bufflen,
+ datadir);
+ dma_error = dma_mapping_error(physaddr);
+ if (dma_error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "%d:0718 Unable to dma_map_single "
+ "request_buffer: x%x\n",
+ phba->brd_no, dma_error);
+ return 1;
+ }
+
+ lpfc_cmd->nonsg_phys = physaddr;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+ bpl->tus.f.bdeSize = scsi_cmnd->request_bufflen;
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ num_bde = 1;
+ bpl++;
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are dependent on the
+ * scsi_cmnd request_buffer
+ */
+ iocb_cmd->un.fcpi64.bdl.bdeSize +=
+ (num_bde * sizeof (struct ulp_bde64));
+ iocb_cmd->ulpBdeCount = 1;
+ iocb_cmd->ulpLe = 1;
+ fcp_cmnd->fcpDl = be32_to_cpu(scsi_cmnd->request_bufflen);
+ return 0;
+}
+
+static void
+lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd;
+ struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
+ struct lpfc_hba *phba = lpfc_cmd->scsi_hba;
+ uint32_t fcpi_parm = lpfc_cmd->cur_iocbq.iocb.un.fcpi.fcpi_parm;
+ uint32_t resp_info = fcprsp->rspStatus2;
+ uint32_t scsi_status = fcprsp->rspStatus3;
+ uint32_t host_status = DID_OK;
+ uint32_t rsplen = 0;
+
+ /*
+ * If this is a task management command, there is no
+ * scsi packet associated with this lpfc_cmd. The driver
+ * consumes it.
+ */
+ if (fcpcmd->fcpCntl2) {
+ scsi_status = 0;
+ goto out;
+ }
+
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+ "%d:0730 FCP command failed: RSP "
+ "Data: x%x x%x x%x x%x x%x x%x\n",
+ phba->brd_no, resp_info, scsi_status,
+ be32_to_cpu(fcprsp->rspResId),
+ be32_to_cpu(fcprsp->rspSnsLen),
+ be32_to_cpu(fcprsp->rspRspLen),
+ fcprsp->rspInfo3);
+
+ if (resp_info & RSP_LEN_VALID) {
+ rsplen = be32_to_cpu(fcprsp->rspRspLen);
+ if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
+ (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
+ host_status = DID_ERROR;
+ goto out;
+ }
+ }
+
+ if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) {
+ uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen);
+ if (snslen > SCSI_SENSE_BUFFERSIZE)
+ snslen = SCSI_SENSE_BUFFERSIZE;
+
+ memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen);
+ }
+
+ cmnd->resid = 0;
+ if (resp_info & RESID_UNDER) {
+ cmnd->resid = be32_to_cpu(fcprsp->rspResId);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0716 FCP Read Underrun, expected %d, "
+ "residual %d Data: x%x x%x x%x\n", phba->brd_no,
+ be32_to_cpu(fcpcmd->fcpDl), cmnd->resid,
+ fcpi_parm, cmnd->cmnd[0], cmnd->underflow);
+
+ /*
+ * The cmnd->underflow is the minimum number of bytes that must
+ * be transfered for this command. Provided a sense condition
+ * is not present, make sure the actual amount transferred is at
+ * least the underflow value or fail.
+ */
+ if (!(resp_info & SNS_LEN_VALID) &&
+ (scsi_status == SAM_STAT_GOOD) &&
+ (cmnd->request_bufflen - cmnd->resid) < cmnd->underflow) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0717 FCP command x%x residual "
+ "underrun converted to error "
+ "Data: x%x x%x x%x\n", phba->brd_no,
+ cmnd->cmnd[0], cmnd->request_bufflen,
+ cmnd->resid, cmnd->underflow);
+
+ host_status = DID_ERROR;
+ }
+ } else if (resp_info & RESID_OVER) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+ "%d:0720 FCP command x%x residual "
+ "overrun error. Data: x%x x%x \n",
+ phba->brd_no, cmnd->cmnd[0],
+ cmnd->request_bufflen, cmnd->resid);
+ host_status = DID_ERROR;
+
+ /*
+ * Check SLI validation that all the transfer was actually done
+ * (fcpi_parm should be zero). Apply check only to reads.
+ */
+ } else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm &&
+ (cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+ "%d:0734 FCP Read Check Error Data: "
+ "x%x x%x x%x x%x\n", phba->brd_no,
+ be32_to_cpu(fcpcmd->fcpDl),
+ be32_to_cpu(fcprsp->rspResId),
+ fcpi_parm, cmnd->cmnd[0]);
+ host_status = DID_ERROR;
+ cmnd->resid = cmnd->request_bufflen;
+ }
+
+ out:
+ cmnd->result = ScsiResult(host_status, scsi_status);
+}
+
+static void
+lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct lpfc_scsi_buf *lpfc_cmd =
+ (struct lpfc_scsi_buf *) pIocbIn->context1;
+ struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
+ struct lpfc_nodelist *pnode = rdata->pnode;
+ struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+ unsigned long iflag;
+
+ lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
+ lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
+
+ if (lpfc_cmd->status) {
+ if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
+ (lpfc_cmd->result & IOERR_DRVR_MASK))
+ lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+ else if (lpfc_cmd->status >= IOSTAT_CNT)
+ lpfc_cmd->status = IOSTAT_DEFAULT;
+
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+ "%d:0729 FCP cmd x%x failed <%d/%d> status: "
+ "x%x result: x%x Data: x%x x%x\n",
+ phba->brd_no, cmd->cmnd[0], cmd->device->id,
+ cmd->device->lun, lpfc_cmd->status,
+ lpfc_cmd->result, pIocbOut->iocb.ulpContext,
+ lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
+
+ switch (lpfc_cmd->status) {
+ case IOSTAT_FCP_RSP_ERROR:
+ /* Call FCP RSP handler to determine result */
+ lpfc_handle_fcp_err(lpfc_cmd);
+ break;
+ case IOSTAT_NPORT_BSY:
+ case IOSTAT_FABRIC_BSY:
+ cmd->result = ScsiResult(DID_BUS_BUSY, 0);
+ break;
+ default:
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ break;
+ }
+
+ if (pnode) {
+ if (pnode->nlp_state != NLP_STE_MAPPED_NODE)
+ cmd->result = ScsiResult(DID_BUS_BUSY,
+ SAM_STAT_BUSY);
+ }
+ else {
+ cmd->result = ScsiResult(DID_NO_CONNECT, 0);
+ }
+ } else {
+ cmd->result = ScsiResult(DID_OK, 0);
+ }
+
+ if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) {
+ uint32_t *lp = (uint32_t *)cmd->sense_buffer;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0710 Iodone <%d/%d> cmd %p, error x%x "
+ "SNS x%x x%x Data: x%x x%x\n",
+ phba->brd_no, cmd->device->id,
+ cmd->device->lun, cmd, cmd->result,
+ *lp, *(lp + 3), cmd->retries, cmd->resid);
+ }
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ lpfc_free_scsi_buf(lpfc_cmd);
+ cmd->host_scribble = NULL;
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+
+ cmd->scsi_done(cmd);
+}
+
+static void
+lpfc_scsi_prep_cmnd(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd,
+ struct lpfc_nodelist *pnode)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
+ int datadir = scsi_cmnd->sc_data_direction;
+
+ lpfc_cmd->fcp_rsp->rspSnsLen = 0;
+
+ lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun);
+
+ memcpy(&fcp_cmnd->fcpCdb[0], scsi_cmnd->cmnd, 16);
+
+ if (scsi_cmnd->device->tagged_supported) {
+ switch (scsi_cmnd->tag) {
+ case HEAD_OF_QUEUE_TAG:
+ fcp_cmnd->fcpCntl1 = HEAD_OF_Q;
+ break;
+ case ORDERED_QUEUE_TAG:
+ fcp_cmnd->fcpCntl1 = ORDERED_Q;
+ break;
+ default:
+ fcp_cmnd->fcpCntl1 = SIMPLE_Q;
+ break;
+ }
+ } else
+ fcp_cmnd->fcpCntl1 = 0;
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither. Start the lpfc command prep by
+ * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+ * data bde entry.
+ */
+ if (scsi_cmnd->use_sg) {
+ if (datadir == DMA_TO_DEVICE) {
+ iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
+ iocb_cmd->un.fcpi.fcpi_parm = 0;
+ iocb_cmd->ulpPU = 0;
+ fcp_cmnd->fcpCntl3 = WRITE_DATA;
+ phba->fc4OutputRequests++;
+ } else {
+ iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
+ iocb_cmd->ulpPU = PARM_READ_CHECK;
+ iocb_cmd->un.fcpi.fcpi_parm =
+ scsi_cmnd->request_bufflen;
+ fcp_cmnd->fcpCntl3 = READ_DATA;
+ phba->fc4InputRequests++;
+ }
+ } else if (scsi_cmnd->request_buffer && scsi_cmnd->request_bufflen) {
+ if (datadir == DMA_TO_DEVICE) {
+ iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
+ iocb_cmd->un.fcpi.fcpi_parm = 0;
+ iocb_cmd->ulpPU = 0;
+ fcp_cmnd->fcpCntl3 = WRITE_DATA;
+ phba->fc4OutputRequests++;
+ } else {
+ iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
+ iocb_cmd->ulpPU = PARM_READ_CHECK;
+ iocb_cmd->un.fcpi.fcpi_parm =
+ scsi_cmnd->request_bufflen;
+ fcp_cmnd->fcpCntl3 = READ_DATA;
+ phba->fc4InputRequests++;
+ }
+ } else {
+ iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR;
+ iocb_cmd->un.fcpi.fcpi_parm = 0;
+ iocb_cmd->ulpPU = 0;
+ fcp_cmnd->fcpCntl3 = 0;
+ phba->fc4ControlRequests++;
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are independent
+ * of the scsi_cmnd request_buffer
+ */
+ piocbq->iocb.ulpContext = pnode->nlp_rpi;
+ if (pnode->nlp_fcp_info & NLP_FCP_2_DEVICE)
+ piocbq->iocb.ulpFCP2Rcvy = 1;
+
+ piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & 0x0f);
+ piocbq->context1 = lpfc_cmd;
+ piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
+ piocbq->iocb.ulpTimeout = lpfc_cmd->timeout;
+}
+
+static int
+lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd,
+ uint8_t task_mgmt_cmd)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_iocbq *piocbq;
+ IOCB_t *piocb;
+ struct fcp_cmnd *fcp_cmnd;
+ struct scsi_device *scsi_dev = lpfc_cmd->pCmd->device;
+ struct lpfc_rport_data *rdata = scsi_dev->hostdata;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+
+ if ((ndlp == 0) || (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
+ return 0;
+ }
+
+ psli = &phba->sli;
+ piocbq = &(lpfc_cmd->cur_iocbq);
+ piocb = &piocbq->iocb;
+
+ fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun);
+ fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
+
+ piocb->ulpCommand = CMD_FCP_ICMND64_CR;
+
+ piocb->ulpContext = ndlp->nlp_rpi;
+ if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) {
+ piocb->ulpFCP2Rcvy = 1;
+ }
+ piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f);
+
+ /* ulpTimeout is only one byte */
+ if (lpfc_cmd->timeout > 0xff) {
+ /*
+ * Do not timeout the command at the firmware level.
+ * The driver will provide the timeout mechanism.
+ */
+ piocb->ulpTimeout = 0;
+ } else {
+ piocb->ulpTimeout = lpfc_cmd->timeout;
+ }
+
+ lpfc_cmd->rdata = rdata;
+
+ switch (task_mgmt_cmd) {
+ case FCP_LUN_RESET:
+ /* Issue LUN Reset to TGT <num> LUN <num> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_FCP,
+ "%d:0703 Issue LUN Reset to TGT %d LUN %d "
+ "Data: x%x x%x\n",
+ phba->brd_no,
+ scsi_dev->id, scsi_dev->lun,
+ ndlp->nlp_rpi, ndlp->nlp_flag);
+
+ break;
+ case FCP_ABORT_TASK_SET:
+ /* Issue Abort Task Set to TGT <num> LUN <num> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_FCP,
+ "%d:0701 Issue Abort Task Set to TGT %d LUN %d "
+ "Data: x%x x%x\n",
+ phba->brd_no,
+ scsi_dev->id, scsi_dev->lun,
+ ndlp->nlp_rpi, ndlp->nlp_flag);
+
+ break;
+ case FCP_TARGET_RESET:
+ /* Issue Target Reset to TGT <num> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_FCP,
+ "%d:0702 Issue Target Reset to TGT %d "
+ "Data: x%x x%x\n",
+ phba->brd_no,
+ scsi_dev->id, ndlp->nlp_rpi,
+ ndlp->nlp_flag);
+ break;
+ }
+
+ return (1);
+}
+
+static int
+lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba)
+{
+ struct lpfc_iocbq *iocbq;
+ struct lpfc_iocbq *iocbqrsp = NULL;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ int ret;
+
+ ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET);
+ if (!ret)
+ return FAILED;
+
+ lpfc_cmd->scsi_hba = phba;
+ iocbq = &lpfc_cmd->cur_iocbq;
+ list_remove_head(lpfc_iocb_list, iocbqrsp, struct lpfc_iocbq, list);
+ if (!iocbqrsp)
+ return FAILED;
+ memset(iocbqrsp, 0, sizeof (struct lpfc_iocbq));
+
+ iocbq->iocb_flag |= LPFC_IO_POLL;
+ ret = lpfc_sli_issue_iocb_wait_high_priority(phba,
+ &phba->sli.ring[phba->sli.fcp_ring],
+ iocbq, SLI_IOCB_HIGH_PRIORITY,
+ iocbqrsp,
+ lpfc_cmd->timeout);
+ if (ret != IOCB_SUCCESS) {
+ lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+ ret = FAILED;
+ } else {
+ ret = SUCCESS;
+ lpfc_cmd->result = iocbqrsp->iocb.un.ulpWord[4];
+ lpfc_cmd->status = iocbqrsp->iocb.ulpStatus;
+ if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
+ (lpfc_cmd->result & IOERR_DRVR_MASK))
+ lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+ }
+
+ /*
+ * All outstanding txcmplq I/Os should have been aborted by the target.
+ * Unfortunately, some targets do not abide by this forcing the driver
+ * to double check.
+ */
+ lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+ lpfc_cmd->pCmd->device->id,
+ lpfc_cmd->pCmd->device->lun, 0, LPFC_CTX_TGT);
+
+ /* Return response IOCB to free list. */
+ list_add_tail(&iocbqrsp->list, lpfc_iocb_list);
+ return ret;
+}
+
+static void
+lpfc_scsi_cmd_iocb_cleanup (struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+ struct lpfc_iocbq *pIocbOut)
+{
+ unsigned long iflag;
+ struct lpfc_scsi_buf *lpfc_cmd =
+ (struct lpfc_scsi_buf *) pIocbIn->context1;
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ lpfc_free_scsi_buf(lpfc_cmd);
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+static void
+lpfc_scsi_cmd_iocb_cmpl_aborted(struct lpfc_hba *phba,
+ struct lpfc_iocbq *pIocbIn,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct scsi_cmnd *ml_cmd =
+ ((struct lpfc_scsi_buf *) pIocbIn->context1)->pCmd;
+
+ lpfc_scsi_cmd_iocb_cleanup (phba, pIocbIn, pIocbOut);
+ ml_cmd->host_scribble = NULL;
+}
+
+const char *
+lpfc_info(struct Scsi_Host *host)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0];
+ int len;
+ static char lpfcinfobuf[384];
+
+ memset(lpfcinfobuf,0,384);
+ if (phba && phba->pcidev){
+ strncpy(lpfcinfobuf, phba->ModelDesc, 256);
+ len = strlen(lpfcinfobuf);
+ snprintf(lpfcinfobuf + len,
+ 384-len,
+ " on PCI bus %02x device %02x irq %d",
+ phba->pcidev->bus->number,
+ phba->pcidev->devfn,
+ phba->pcidev->irq);
+ len = strlen(lpfcinfobuf);
+ if (phba->Port[0]) {
+ snprintf(lpfcinfobuf + len,
+ 384-len,
+ " port %s",
+ phba->Port);
+ }
+ }
+ return lpfcinfobuf;
+}
+
+static int
+lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+{
+ struct lpfc_hba *phba =
+ (struct lpfc_hba *) cmnd->device->host->hostdata[0];
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct lpfc_scsi_buf *lpfc_cmd = NULL;
+ struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+ int err = 0;
+
+ /*
+ * The target pointer is guaranteed not to be NULL because the driver
+ * only clears the device->hostdata field in lpfc_slave_destroy. This
+ * approach guarantees no further IO calls on this target.
+ */
+ if (!ndlp) {
+ cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+ goto out_fail_command;
+ }
+
+ /*
+ * A Fibre Channel target is present and functioning only when the node
+ * state is MAPPED. Any other state is a failure.
+ */
+ if (ndlp->nlp_state != NLP_STE_MAPPED_NODE) {
+ if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+ (ndlp->nlp_state == NLP_STE_UNUSED_NODE)) {
+ cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+ goto out_fail_command;
+ }
+ /*
+ * The device is most likely recovered and the driver
+ * needs a bit more time to finish. Ask the midlayer
+ * to retry.
+ */
+ goto out_host_busy;
+ }
+
+ list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+ if (lpfc_cmd == NULL) {
+ printk(KERN_WARNING "%s: No buffer available - list empty, "
+ "total count %d\n", __FUNCTION__, phba->total_scsi_bufs);
+ goto out_host_busy;
+ }
+
+ /*
+ * Store the midlayer's command structure for the completion phase
+ * and complete the command initialization.
+ */
+ lpfc_cmd->pCmd = cmnd;
+ lpfc_cmd->rdata = rdata;
+ lpfc_cmd->timeout = 0;
+ cmnd->host_scribble = (unsigned char *)lpfc_cmd;
+ cmnd->scsi_done = done;
+
+ err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ if (err)
+ goto out_host_busy_free_buf;
+
+ lpfc_scsi_prep_cmnd(phba, lpfc_cmd, ndlp);
+
+ err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
+ &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
+ if (err)
+ goto out_host_busy_free_buf;
+ return 0;
+
+ out_host_busy_free_buf:
+ lpfc_free_scsi_buf(lpfc_cmd);
+ cmnd->host_scribble = NULL;
+ out_host_busy:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ out_fail_command:
+ done(cmnd);
+ return 0;
+}
+
+static int
+lpfc_abort_handler(struct scsi_cmnd *cmnd)
+{
+ struct lpfc_hba *phba =
+ (struct lpfc_hba *)cmnd->device->host->hostdata[0];
+ struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring];
+ struct lpfc_iocbq *iocb, *next_iocb;
+ struct lpfc_iocbq *abtsiocb = NULL;
+ struct lpfc_scsi_buf *lpfc_cmd;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ IOCB_t *cmd, *icmd;
+ unsigned long snum;
+ unsigned int id, lun;
+ unsigned int loop_count = 0;
+ int ret = IOCB_SUCCESS;
+
+ /*
+ * If the host_scribble data area is NULL, then the driver has already
+ * completed this command, but the midlayer did not see the completion
+ * before the eh fired. Just return SUCCESS.
+ */
+ lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
+ if (!lpfc_cmd)
+ return SUCCESS;
+
+ /* save these now since lpfc_cmd can be freed */
+ id = lpfc_cmd->pCmd->device->id;
+ lun = lpfc_cmd->pCmd->device->lun;
+ snum = lpfc_cmd->pCmd->serial_number;
+
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ cmd = &iocb->iocb;
+ if (iocb->context1 != lpfc_cmd)
+ continue;
+
+ list_del_init(&iocb->list);
+ pring->txq_cnt--;
+ if (!iocb->iocb_cmpl) {
+ list_add_tail(&iocb->list, lpfc_iocb_list);
+ }
+ else {
+ cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ lpfc_scsi_cmd_iocb_cmpl_aborted(phba, iocb, iocb);
+ }
+
+ goto out;
+ }
+
+ list_remove_head(lpfc_iocb_list, abtsiocb, struct lpfc_iocbq, list);
+ if (abtsiocb == NULL)
+ return FAILED;
+
+ memset(abtsiocb, 0, sizeof (struct lpfc_iocbq));
+
+ /*
+ * The scsi command was not in the txq. Check the txcmplq and if it is
+ * found, send an abort to the FW.
+ */
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ if (iocb->context1 != lpfc_cmd)
+ continue;
+
+ iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl_aborted;
+ cmd = &iocb->iocb;
+ icmd = &abtsiocb->iocb;
+ icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+ icmd->un.acxri.abortContextTag = cmd->ulpContext;
+ icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+ icmd->ulpLe = 1;
+ icmd->ulpClass = cmd->ulpClass;
+ if (phba->hba_state >= LPFC_LINK_UP)
+ icmd->ulpCommand = CMD_ABORT_XRI_CN;
+ else
+ icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+
+ if (lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0) ==
+ IOCB_ERROR) {
+ list_add_tail(&abtsiocb->list, lpfc_iocb_list);
+ ret = IOCB_ERROR;
+ break;
+ }
+
+ /* Wait for abort to complete */
+ while (cmnd->host_scribble)
+ {
+ spin_unlock_irq(phba->host->host_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(LPFC_ABORT_WAIT*HZ);
+ spin_lock_irq(phba->host->host_lock);
+ if (++loop_count
+ > (2 * phba->cfg_nodev_tmo)/LPFC_ABORT_WAIT)
+ break;
+ }
+
+ if(cmnd->host_scribble) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "%d:0748 abort handler timed "
+ "out waiting for abort to "
+ "complete. Data: "
+ "x%x x%x x%x x%lx\n",
+ phba->brd_no, ret, id, lun, snum);
+ cmnd->host_scribble = NULL;
+ iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cleanup;
+ ret = IOCB_ERROR;
+ }
+
+ break;
+ }
+
+ out:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+ "%d:0749 SCSI layer issued abort device "
+ "Data: x%x x%x x%x x%lx\n",
+ phba->brd_no, ret, id, lun, snum);
+
+ return ret == IOCB_SUCCESS ? SUCCESS : FAILED;
+}
+
+static int
+lpfc_reset_lun_handler(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_scsi_buf *lpfc_cmd = NULL;
+ struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ struct lpfc_iocbq *iocbq, *iocbqrsp = NULL;
+ struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+ struct lpfc_nodelist *pnode = rdata->pnode;
+ int ret = FAILED;
+ int cnt, loopcnt;
+
+ /*
+ * If target is not in a MAPPED state, delay the reset until
+ * target is rediscovered or nodev timeout expires.
+ */
+ while ( 1 ) {
+ if (!pnode)
+ break;
+
+ if (pnode->nlp_state != NLP_STE_MAPPED_NODE) {
+ spin_unlock_irq(phba->host->host_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout( HZ/2);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ if ((pnode) && (pnode->nlp_state == NLP_STE_MAPPED_NODE))
+ break;
+ }
+
+ list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+ if (lpfc_cmd == NULL)
+ goto out;
+
+ lpfc_cmd->pCmd = cmnd;
+ lpfc_cmd->timeout = 60;
+ lpfc_cmd->scsi_hba = phba;
+
+ ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET);
+ if (!ret)
+ goto out_free_scsi_buf;
+
+ iocbq = &lpfc_cmd->cur_iocbq;
+
+ /* get a buffer for this IOCB command response */
+ list_remove_head(lpfc_iocb_list, iocbqrsp, struct lpfc_iocbq, list);
+ if (iocbqrsp == NULL)
+ goto out_free_scsi_buf;
+
+ memset(iocbqrsp, 0, sizeof (struct lpfc_iocbq));
+
+ iocbq->iocb_flag |= LPFC_IO_POLL;
+ iocbq->iocb_cmpl = lpfc_sli_wake_iocb_high_priority;
+
+ ret = lpfc_sli_issue_iocb_wait_high_priority(phba,
+ &phba->sli.ring[psli->fcp_ring],
+ iocbq, 0, iocbqrsp, 60);
+ if (ret == IOCB_SUCCESS)
+ ret = SUCCESS;
+
+ lpfc_cmd->result = iocbqrsp->iocb.un.ulpWord[4];
+ lpfc_cmd->status = iocbqrsp->iocb.ulpStatus;
+ if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT)
+ if (lpfc_cmd->result & IOERR_DRVR_MASK)
+ lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+
+ /*
+ * All outstanding txcmplq I/Os should have been aborted by the target.
+ * Unfortunately, some targets do not abide by this forcing the driver
+ * to double check.
+ */
+ lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+ cmnd->device->id, cmnd->device->lun, 0,
+ LPFC_CTX_LUN);
+
+ loopcnt = 0;
+ while((cnt = lpfc_sli_sum_iocb(phba,
+ &phba->sli.ring[phba->sli.fcp_ring],
+ cmnd->device->id, cmnd->device->lun,
+ LPFC_CTX_LUN))) {
+ spin_unlock_irq(phba->host->host_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(LPFC_RESET_WAIT*HZ);
+ spin_lock_irq(phba->host->host_lock);
+
+ if (++loopcnt
+ > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
+ break;
+ }
+
+ if (cnt) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0719 LUN Reset I/O flush failure: cnt x%x\n",
+ phba->brd_no, cnt);
+ }
+
+ list_add_tail(&iocbqrsp->list, lpfc_iocb_list);
+
+out_free_scsi_buf:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "%d:0713 SCSI layer issued LUN reset (%d, %d) "
+ "Data: x%x x%x x%x\n",
+ phba->brd_no, lpfc_cmd->pCmd->device->id,
+ lpfc_cmd->pCmd->device->lun, ret, lpfc_cmd->status,
+ lpfc_cmd->result);
+ lpfc_free_scsi_buf(lpfc_cmd);
+out:
+ return ret;
+}
+
+/*
+ * Note: midlayer calls this function with the host_lock held
+ */
+static int
+lpfc_reset_bus_handler(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+ struct lpfc_nodelist *ndlp = NULL;
+ int match;
+ int ret = FAILED, i, err_count = 0;
+ int cnt, loopcnt;
+ unsigned int midlayer_id = 0;
+ struct lpfc_scsi_buf * lpfc_cmd = NULL;
+ struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+
+ list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+ if (lpfc_cmd == NULL)
+ goto out;
+
+ /* The lpfc_cmd storage is reused. Set all loop invariants. */
+ lpfc_cmd->timeout = 60;
+ lpfc_cmd->pCmd = cmnd;
+ lpfc_cmd->scsi_hba = phba;
+
+ /*
+ * Since the driver manages a single bus device, reset all
+ * targets known to the driver. Should any target reset
+ * fail, this routine returns failure to the midlayer.
+ */
+ midlayer_id = cmnd->device->id;
+ for (i = 0; i < MAX_FCP_TARGET; i++) {
+ /* Search the mapped list for this target ID */
+ match = 0;
+ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+ if ((i == ndlp->nlp_sid) && ndlp->rport) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match)
+ continue;
+
+ lpfc_cmd->pCmd->device->id = i;
+ lpfc_cmd->pCmd->device->hostdata = ndlp->rport->dd_data;
+ ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba);
+ if (ret != SUCCESS) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0713 Bus Reset on target %d failed\n",
+ phba->brd_no, i);
+ err_count++;
+ }
+ }
+
+ cmnd->device->id = midlayer_id;
+ loopcnt = 0;
+ while((cnt = lpfc_sli_sum_iocb(phba,
+ &phba->sli.ring[phba->sli.fcp_ring],
+ 0, 0, LPFC_CTX_HOST))) {
+ spin_unlock_irq(phba->host->host_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(LPFC_RESET_WAIT*HZ);
+ spin_lock_irq(phba->host->host_lock);
+
+ if (++loopcnt
+ > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
+ break;
+ }
+
+ if (cnt) {
+ /* flush all outstanding commands on the host */
+ i = lpfc_sli_abort_iocb(phba,
+ &phba->sli.ring[phba->sli.fcp_ring], 0, 0, 0,
+ LPFC_CTX_HOST);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+ "%d:0715 Bus Reset I/O flush failure: cnt x%x left x%x\n",
+ phba->brd_no, cnt, i);
+ }
+
+ if (!err_count)
+ ret = SUCCESS;
+
+ lpfc_free_scsi_buf(lpfc_cmd);
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_FCP,
+ "%d:0714 SCSI layer issued Bus Reset Data: x%x\n",
+ phba->brd_no, ret);
+out:
+ return ret;
+}
+
+static int
+lpfc_slave_alloc(struct scsi_device *sdev)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)sdev->host->hostdata[0];
+ struct lpfc_nodelist *ndlp = NULL;
+ int match = 0;
+ struct lpfc_scsi_buf *scsi_buf = NULL;
+ uint32_t total = 0, i;
+ uint32_t num_to_alloc = 0;
+ unsigned long flags;
+ struct list_head *listp;
+ struct list_head *node_list[6];
+
+ /*
+ * Store the target pointer in the scsi_device hostdata pointer provided
+ * the driver has already discovered the target id.
+ */
+
+ /* Search the nlp lists other than unmap_list for this target ID */
+ node_list[0] = &phba->fc_npr_list;
+ node_list[1] = &phba->fc_nlpmap_list;
+ node_list[2] = &phba->fc_prli_list;
+ node_list[3] = &phba->fc_reglogin_list;
+ node_list[4] = &phba->fc_adisc_list;
+ node_list[5] = &phba->fc_plogi_list;
+
+ for (i = 0; i < 6 && !match; i++) {
+ listp = node_list[i];
+ if (list_empty(listp))
+ continue;
+ list_for_each_entry(ndlp, listp, nlp_listp) {
+ if ((sdev->id == ndlp->nlp_sid) && ndlp->rport) {
+ match = 1;
+ break;
+ }
+ }
+ }
+
+ if (!match)
+ return -ENXIO;
+
+ sdev->hostdata = ndlp->rport->dd_data;
+
+ /*
+ * Populate the cmds_per_lun count scsi_bufs into this host's globally
+ * available list of scsi buffers. Don't allocate more than the
+ * HBA limit conveyed to the midlayer via the host structure. Note
+ * that this list of scsi bufs exists for the lifetime of the driver.
+ */
+ total = phba->total_scsi_bufs;
+ num_to_alloc = LPFC_CMD_PER_LUN;
+ if (total >= phba->cfg_hba_queue_depth) {
+ printk(KERN_WARNING "%s, At config limitation of "
+ "%d allocated scsi_bufs\n", __FUNCTION__, total);
+ return 0;
+ } else if (total + num_to_alloc > phba->cfg_hba_queue_depth) {
+ num_to_alloc = phba->cfg_hba_queue_depth - total;
+ }
+
+ for (i = 0; i < num_to_alloc; i++) {
+ scsi_buf = lpfc_get_scsi_buf(phba);
+ if (!scsi_buf) {
+ printk(KERN_ERR "%s, failed to allocate "
+ "scsi_buf\n", __FUNCTION__);
+ break;
+ }
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ phba->total_scsi_bufs++;
+ list_add_tail(&scsi_buf->list, &phba->lpfc_scsi_buf_list);
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+ }
+ return 0;
+}
+
+static int
+lpfc_slave_configure(struct scsi_device *sdev)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *) sdev->host->hostdata[0];
+ struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, phba->cfg_lun_queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, phba->cfg_lun_queue_depth);
+
+ /*
+ * Initialize the fc transport attributes for the target
+ * containing this scsi device. Also note that the driver's
+ * target pointer is stored in the starget_data for the
+ * driver's sysfs entry point functions.
+ */
+ rport->dev_loss_tmo = phba->cfg_nodev_tmo + 5;
+
+ return 0;
+}
+
+static void
+lpfc_slave_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+ return;
+}
+
+struct scsi_host_template lpfc_template = {
+ .module = THIS_MODULE,
+ .name = LPFC_DRIVER_NAME,
+ .info = lpfc_info,
+ .queuecommand = lpfc_queuecommand,
+ .eh_abort_handler = lpfc_abort_handler,
+ .eh_device_reset_handler= lpfc_reset_lun_handler,
+ .eh_bus_reset_handler = lpfc_reset_bus_handler,
+ .slave_alloc = lpfc_slave_alloc,
+ .slave_configure = lpfc_slave_configure,
+ .slave_destroy = lpfc_slave_destroy,
+ .this_id = -1,
+ .sg_tablesize = LPFC_SG_SEG_CNT,
+ .cmd_per_lun = LPFC_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = lpfc_host_attrs,
+};
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
new file mode 100644
index 000000000000..4aafba47628d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -0,0 +1,157 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_scsi.h 1.83 2005/04/07 08:47:43EDT sf_support Exp $
+ */
+
+struct lpfc_hba;
+
+#define list_remove_head(list, entry, type, member) \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ list_del_init(&entry->member); \
+ }
+
+#define list_get_first(list, type, member) \
+ (list_empty(list)) ? NULL : \
+ list_entry((list)->next, type, member)
+
+/* per-port data that is allocated in the FC transport for us */
+struct lpfc_rport_data {
+ struct lpfc_nodelist *pnode; /* Pointer to the node structure. */
+};
+
+struct fcp_rsp {
+ uint32_t rspRsvd1; /* FC Word 0, byte 0:3 */
+ uint32_t rspRsvd2; /* FC Word 1, byte 0:3 */
+
+ uint8_t rspStatus0; /* FCP_STATUS byte 0 (reserved) */
+ uint8_t rspStatus1; /* FCP_STATUS byte 1 (reserved) */
+ uint8_t rspStatus2; /* FCP_STATUS byte 2 field validity */
+#define RSP_LEN_VALID 0x01 /* bit 0 */
+#define SNS_LEN_VALID 0x02 /* bit 1 */
+#define RESID_OVER 0x04 /* bit 2 */
+#define RESID_UNDER 0x08 /* bit 3 */
+ uint8_t rspStatus3; /* FCP_STATUS byte 3 SCSI status byte */
+
+ uint32_t rspResId; /* Residual xfer if residual count field set in
+ fcpStatus2 */
+ /* Received in Big Endian format */
+ uint32_t rspSnsLen; /* Length of sense data in fcpSnsInfo */
+ /* Received in Big Endian format */
+ uint32_t rspRspLen; /* Length of FCP response data in fcpRspInfo */
+ /* Received in Big Endian format */
+
+ uint8_t rspInfo0; /* FCP_RSP_INFO byte 0 (reserved) */
+ uint8_t rspInfo1; /* FCP_RSP_INFO byte 1 (reserved) */
+ uint8_t rspInfo2; /* FCP_RSP_INFO byte 2 (reserved) */
+ uint8_t rspInfo3; /* FCP_RSP_INFO RSP_CODE byte 3 */
+
+#define RSP_NO_FAILURE 0x00
+#define RSP_DATA_BURST_ERR 0x01
+#define RSP_CMD_FIELD_ERR 0x02
+#define RSP_RO_MISMATCH_ERR 0x03
+#define RSP_TM_NOT_SUPPORTED 0x04 /* Task mgmt function not supported */
+#define RSP_TM_NOT_COMPLETED 0x05 /* Task mgmt function not performed */
+
+ uint32_t rspInfoRsvd; /* FCP_RSP_INFO bytes 4-7 (reserved) */
+
+ uint8_t rspSnsInfo[128];
+#define SNS_ILLEGAL_REQ 0x05 /* sense key is byte 3 ([2]) */
+#define SNSCOD_BADCMD 0x20 /* sense code is byte 13 ([12]) */
+};
+
+struct fcp_cmnd {
+ uint32_t fcpLunMsl; /* most significant lun word (32 bits) */
+ uint32_t fcpLunLsl; /* least significant lun word (32 bits) */
+ /* # of bits to shift lun id to end up in right
+ * payload word, little endian = 8, big = 16.
+ */
+#if __BIG_ENDIAN
+#define FC_LUN_SHIFT 16
+#define FC_ADDR_MODE_SHIFT 24
+#else /* __LITTLE_ENDIAN */
+#define FC_LUN_SHIFT 8
+#define FC_ADDR_MODE_SHIFT 0
+#endif
+
+ uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */
+ uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */
+#define SIMPLE_Q 0x00
+#define HEAD_OF_Q 0x01
+#define ORDERED_Q 0x02
+#define ACA_Q 0x04
+#define UNTAGGED 0x05
+ uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */
+#define FCP_ABORT_TASK_SET 0x02 /* Bit 1 */
+#define FCP_CLEAR_TASK_SET 0x04 /* bit 2 */
+#define FCP_BUS_RESET 0x08 /* bit 3 */
+#define FCP_LUN_RESET 0x10 /* bit 4 */
+#define FCP_TARGET_RESET 0x20 /* bit 5 */
+#define FCP_CLEAR_ACA 0x40 /* bit 6 */
+#define FCP_TERMINATE_TASK 0x80 /* bit 7 */
+ uint8_t fcpCntl3;
+#define WRITE_DATA 0x01 /* Bit 0 */
+#define READ_DATA 0x02 /* Bit 1 */
+
+ uint8_t fcpCdb[16]; /* SRB cdb field is copied here */
+ uint32_t fcpDl; /* Total transfer length */
+
+};
+
+struct lpfc_scsi_buf {
+ struct list_head list;
+ struct scsi_cmnd *pCmd;
+ struct lpfc_hba *scsi_hba;
+ struct lpfc_rport_data *rdata;
+
+ uint32_t timeout;
+
+ uint16_t status; /* From IOCB Word 7- ulpStatus */
+ uint32_t result; /* From IOCB Word 4. */
+
+ uint32_t seg_cnt; /* Number of scatter-gather segments returned by
+ * dma_map_sg. The driver needs this for calls
+ * to dma_unmap_sg. */
+ dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
+
+ /*
+ * data and dma_handle are the kernel virutal and bus address of the
+ * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter
+ * gather bde list that supports the sg_tablesize value.
+ */
+ void *data;
+ dma_addr_t dma_handle;
+
+ struct fcp_cmnd *fcp_cmnd;
+ struct fcp_rsp *fcp_rsp;
+ struct ulp_bde64 *fcp_bpl;
+
+ /* cur_iocbq has phys of the dma-able buffer.
+ * Iotag is in here
+ */
+ struct lpfc_iocbq cur_iocbq;
+};
+
+#define LPFC_SCSI_DMA_EXT_SIZE 264
+#define LPFC_BPL_SIZE 1024
+
+#define MDAC_DIRECT_CMD 0x22
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
new file mode 100644
index 000000000000..8d14b28c80b9
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -0,0 +1,2885 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_sli.c 1.232 2005/04/13 11:59:16EDT sf_support Exp $
+ */
+
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_compat.h"
+
+/*
+ * Define macro to log: Mailbox command x%x cannot issue Data
+ * This allows multiple uses of lpfc_msgBlk0311
+ * w/o perturbing log msg utility.
+ */
+#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \
+ lpfc_printf_log(phba, \
+ KERN_INFO, \
+ LOG_MBOX | LOG_SLI, \
+ "%d:0311 Mailbox command x%x cannot issue " \
+ "Data: x%x x%x x%x\n", \
+ phba->brd_no, \
+ mb->mbxCommand, \
+ phba->hba_state, \
+ psli->sli_flag, \
+ flag);
+
+
+/* There are only four IOCB completion types. */
+typedef enum _lpfc_iocb_type {
+ LPFC_UNKNOWN_IOCB,
+ LPFC_UNSOL_IOCB,
+ LPFC_SOL_IOCB,
+ LPFC_ABORT_IOCB
+} lpfc_iocb_type;
+
+/*
+ * Translate the iocb command to an iocb command type used to decide the final
+ * disposition of each completed IOCB.
+ */
+static lpfc_iocb_type
+lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
+{
+ lpfc_iocb_type type = LPFC_UNKNOWN_IOCB;
+
+ if (iocb_cmnd > CMD_MAX_IOCB_CMD)
+ return 0;
+
+ switch (iocb_cmnd) {
+ case CMD_XMIT_SEQUENCE_CR:
+ case CMD_XMIT_SEQUENCE_CX:
+ case CMD_XMIT_BCAST_CN:
+ case CMD_XMIT_BCAST_CX:
+ case CMD_ELS_REQUEST_CR:
+ case CMD_ELS_REQUEST_CX:
+ case CMD_CREATE_XRI_CR:
+ case CMD_CREATE_XRI_CX:
+ case CMD_GET_RPI_CN:
+ case CMD_XMIT_ELS_RSP_CX:
+ case CMD_GET_RPI_CR:
+ case CMD_FCP_IWRITE_CR:
+ case CMD_FCP_IWRITE_CX:
+ case CMD_FCP_IREAD_CR:
+ case CMD_FCP_IREAD_CX:
+ case CMD_FCP_ICMND_CR:
+ case CMD_FCP_ICMND_CX:
+ case CMD_ADAPTER_MSG:
+ case CMD_ADAPTER_DUMP:
+ case CMD_XMIT_SEQUENCE64_CR:
+ case CMD_XMIT_SEQUENCE64_CX:
+ case CMD_XMIT_BCAST64_CN:
+ case CMD_XMIT_BCAST64_CX:
+ case CMD_ELS_REQUEST64_CR:
+ case CMD_ELS_REQUEST64_CX:
+ case CMD_FCP_IWRITE64_CR:
+ case CMD_FCP_IWRITE64_CX:
+ case CMD_FCP_IREAD64_CR:
+ case CMD_FCP_IREAD64_CX:
+ case CMD_FCP_ICMND64_CR:
+ case CMD_FCP_ICMND64_CX:
+ case CMD_GEN_REQUEST64_CR:
+ case CMD_GEN_REQUEST64_CX:
+ case CMD_XMIT_ELS_RSP64_CX:
+ type = LPFC_SOL_IOCB;
+ break;
+ case CMD_ABORT_XRI_CN:
+ case CMD_ABORT_XRI_CX:
+ case CMD_CLOSE_XRI_CN:
+ case CMD_CLOSE_XRI_CX:
+ case CMD_XRI_ABORTED_CX:
+ case CMD_ABORT_MXRI64_CN:
+ type = LPFC_ABORT_IOCB;
+ break;
+ case CMD_RCV_SEQUENCE_CX:
+ case CMD_RCV_ELS_REQ_CX:
+ case CMD_RCV_SEQUENCE64_CX:
+ case CMD_RCV_ELS_REQ64_CX:
+ type = LPFC_UNSOL_IOCB;
+ break;
+ default:
+ type = LPFC_UNKNOWN_IOCB;
+ break;
+ }
+
+ return type;
+}
+
+static int
+lpfc_sli_ring_map(struct lpfc_hba * phba, LPFC_MBOXQ_t *pmb)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ MAILBOX_t *pmbox = &pmb->mb;
+ int i, rc;
+
+ for (i = 0; i < psli->num_rings; i++) {
+ phba->hba_state = LPFC_INIT_MBX_CMDS;
+ lpfc_config_ring(phba, i, pmb);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0446 Adapter failed to init, "
+ "mbxCmd x%x CFG_RING, mbxStatus x%x, "
+ "ring %d\n",
+ phba->brd_no,
+ pmbox->mbxCommand,
+ pmbox->mbxStatus,
+ i);
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -ENXIO;
+ }
+ }
+ return 0;
+}
+
+static int
+lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb)
+{
+ uint16_t iotag;
+
+ list_add_tail(&piocb->list, &pring->txcmplq);
+ pring->txcmplq_cnt++;
+ if (unlikely(pring->ringno == LPFC_ELS_RING))
+ mod_timer(&phba->els_tmofunc,
+ jiffies + HZ * (phba->fc_ratov << 1));
+
+ if (pring->fast_lookup) {
+ /* Setup fast lookup based on iotag for completion */
+ iotag = piocb->iocb.ulpIoTag;
+ if (iotag && (iotag < pring->fast_iotag))
+ *(pring->fast_lookup + iotag) = piocb;
+ else {
+
+ /* Cmd ring <ringno> put: iotag <iotag> greater then
+ configured max <fast_iotag> wd0 <icmd> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_SLI,
+ "%d:0316 Cmd ring %d put: iotag x%x "
+ "greater then configured max x%x "
+ "wd0 x%x\n",
+ phba->brd_no,
+ pring->ringno, iotag,
+ pring->fast_iotag,
+ *(((uint32_t *)(&piocb->iocb)) + 7));
+ }
+ }
+ return (0);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+ struct list_head *dlp;
+ struct lpfc_iocbq *cmd_iocb;
+
+ dlp = &pring->txq;
+ cmd_iocb = NULL;
+ list_remove_head((&pring->txq), cmd_iocb,
+ struct lpfc_iocbq,
+ list);
+ if (cmd_iocb) {
+ /* If the first ptr is not equal to the list header,
+ * deque the IOCBQ_t and return it.
+ */
+ pring->txq_cnt--;
+ }
+ return (cmd_iocb);
+}
+
+static IOCB_t *
+lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
+{
+ MAILBOX_t *mbox = (MAILBOX_t *)phba->sli.MBhostaddr;
+ PGP *pgp = (PGP *)&mbox->us.s2.port[pring->ringno];
+ uint32_t max_cmd_idx = pring->numCiocb;
+ IOCB_t *iocb = NULL;
+
+ if ((pring->next_cmdidx == pring->cmdidx) &&
+ (++pring->next_cmdidx >= max_cmd_idx))
+ pring->next_cmdidx = 0;
+
+ if (unlikely(pring->local_getidx == pring->next_cmdidx)) {
+
+ pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+
+ if (unlikely(pring->local_getidx >= max_cmd_idx)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "%d:0315 Ring %d issue: portCmdGet %d "
+ "is bigger then cmd ring %d\n",
+ phba->brd_no, pring->ringno,
+ pring->local_getidx, max_cmd_idx);
+
+ phba->hba_state = LPFC_HBA_ERROR;
+ /*
+ * All error attention handlers are posted to
+ * worker thread
+ */
+ phba->work_ha |= HA_ERATT;
+ phba->work_hs = HS_FFER3;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+
+ return NULL;
+ }
+
+ if (pring->local_getidx == pring->next_cmdidx)
+ return NULL;
+ }
+
+ iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx);
+
+ return iocb;
+}
+
+static uint32_t
+lpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+ uint32_t search_start;
+
+ if (pring->fast_lookup == NULL) {
+ pring->iotag_ctr++;
+ if (pring->iotag_ctr >= pring->iotag_max)
+ pring->iotag_ctr = 1;
+ return pring->iotag_ctr;
+ }
+
+ search_start = pring->iotag_ctr;
+
+ do {
+ pring->iotag_ctr++;
+ if (pring->iotag_ctr >= pring->fast_iotag)
+ pring->iotag_ctr = 1;
+
+ if (*(pring->fast_lookup + pring->iotag_ctr) == NULL)
+ return pring->iotag_ctr;
+
+ } while (pring->iotag_ctr != search_start);
+
+ /*
+ * Outstanding I/O count for ring <ringno> is at max <fast_iotag>
+ */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_SLI,
+ "%d:0318 Outstanding I/O count for ring %d is at max x%x\n",
+ phba->brd_no,
+ pring->ringno,
+ pring->fast_iotag);
+ return (0);
+}
+
+static void
+lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ IOCB_t *iocb, struct lpfc_iocbq *nextiocb)
+{
+ /*
+ * Allocate and set up an iotag
+ */
+ nextiocb->iocb.ulpIoTag =
+ lpfc_sli_next_iotag(phba, &phba->sli.ring[phba->sli.fcp_ring]);
+
+ /*
+ * Issue iocb command to adapter
+ */
+ lpfc_sli_pcimem_bcopy(&nextiocb->iocb, iocb, sizeof (IOCB_t));
+ wmb();
+ pring->stats.iocb_cmd++;
+
+ /*
+ * If there is no completion routine to call, we can release the
+ * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF,
+ * that have no rsp ring completion, iocb_cmpl MUST be NULL.
+ */
+ if (nextiocb->iocb_cmpl)
+ lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb);
+ else {
+ list_add_tail(&nextiocb->list, &phba->lpfc_iocb_list);
+ }
+
+ /*
+ * Let the HBA know what IOCB slot will be the next one the
+ * driver will put a command into.
+ */
+ pring->cmdidx = pring->next_cmdidx;
+ writeb(pring->cmdidx, phba->MBslimaddr
+ + (SLIMOFF + (pring->ringno * 2)) * 4);
+}
+
+static void
+lpfc_sli_update_full_ring(struct lpfc_hba * phba,
+ struct lpfc_sli_ring *pring)
+{
+ int ringno = pring->ringno;
+
+ pring->flag |= LPFC_CALL_RING_AVAILABLE;
+
+ wmb();
+
+ /*
+ * Set ring 'ringno' to SET R0CE_REQ in Chip Att register.
+ * The HBA will tell us when an IOCB entry is available.
+ */
+ writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr);
+ readl(phba->CAregaddr); /* flush */
+
+ pring->stats.iocb_cmd_full++;
+}
+
+static void
+lpfc_sli_update_ring(struct lpfc_hba * phba,
+ struct lpfc_sli_ring *pring)
+{
+ int ringno = pring->ringno;
+
+ /*
+ * Tell the HBA that there is work to do in this ring.
+ */
+ wmb();
+ writel(CA_R0ATT << (ringno * 4), phba->CAregaddr);
+ readl(phba->CAregaddr); /* flush */
+}
+
+static void
+lpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+ IOCB_t *iocb;
+ struct lpfc_iocbq *nextiocb;
+
+ /*
+ * Check to see if:
+ * (a) there is anything on the txq to send
+ * (b) link is up
+ * (c) link attention events can be processed (fcp ring only)
+ * (d) IOCB processing is not blocked by the outstanding mbox command.
+ */
+ if (pring->txq_cnt &&
+ (phba->hba_state > LPFC_LINK_DOWN) &&
+ (pring->ringno != phba->sli.fcp_ring ||
+ phba->sli.sli_flag & LPFC_PROCESS_LA) &&
+ !(pring->flag & LPFC_STOP_IOCB_MBX)) {
+
+ while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) &&
+ (nextiocb = lpfc_sli_ringtx_get(phba, pring)))
+ lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb);
+
+ if (iocb)
+ lpfc_sli_update_ring(phba, pring);
+ else
+ lpfc_sli_update_full_ring(phba, pring);
+ }
+
+ return;
+}
+
+/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */
+static void
+lpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno)
+{
+ PGP *pgp =
+ ((PGP *) &
+ (((MAILBOX_t *)phba->sli.MBhostaddr)->us.s2.port[ringno]));
+
+ /* If the ring is active, flag it */
+ if (phba->sli.ring[ringno].cmdringaddr) {
+ if (phba->sli.ring[ringno].flag & LPFC_STOP_IOCB_MBX) {
+ phba->sli.ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX;
+ /*
+ * Force update of the local copy of cmdGetInx
+ */
+ phba->sli.ring[ringno].local_getidx
+ = le32_to_cpu(pgp->cmdGetInx);
+ spin_lock_irq(phba->host->host_lock);
+ lpfc_sli_resume_iocb(phba, &phba->sli.ring[ringno]);
+ spin_unlock_irq(phba->host->host_lock);
+ }
+ }
+}
+
+static int
+lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
+{
+ uint8_t ret;
+
+ switch (mbxCommand) {
+ case MBX_LOAD_SM:
+ case MBX_READ_NV:
+ case MBX_WRITE_NV:
+ case MBX_RUN_BIU_DIAG:
+ case MBX_INIT_LINK:
+ case MBX_DOWN_LINK:
+ case MBX_CONFIG_LINK:
+ case MBX_CONFIG_RING:
+ case MBX_RESET_RING:
+ case MBX_READ_CONFIG:
+ case MBX_READ_RCONFIG:
+ case MBX_READ_SPARM:
+ case MBX_READ_STATUS:
+ case MBX_READ_RPI:
+ case MBX_READ_XRI:
+ case MBX_READ_REV:
+ case MBX_READ_LNK_STAT:
+ case MBX_REG_LOGIN:
+ case MBX_UNREG_LOGIN:
+ case MBX_READ_LA:
+ case MBX_CLEAR_LA:
+ case MBX_DUMP_MEMORY:
+ case MBX_DUMP_CONTEXT:
+ case MBX_RUN_DIAGS:
+ case MBX_RESTART:
+ case MBX_UPDATE_CFG:
+ case MBX_DOWN_LOAD:
+ case MBX_DEL_LD_ENTRY:
+ case MBX_RUN_PROGRAM:
+ case MBX_SET_MASK:
+ case MBX_SET_SLIM:
+ case MBX_UNREG_D_ID:
+ case MBX_CONFIG_FARP:
+ case MBX_LOAD_AREA:
+ case MBX_RUN_BIU_DIAG64:
+ case MBX_CONFIG_PORT:
+ case MBX_READ_SPARM64:
+ case MBX_READ_RPI64:
+ case MBX_REG_LOGIN64:
+ case MBX_READ_LA64:
+ case MBX_FLASH_WR_ULA:
+ case MBX_SET_DEBUG:
+ case MBX_LOAD_EXP_ROM:
+ ret = mbxCommand;
+ break;
+ default:
+ ret = MBX_SHUTDOWN;
+ break;
+ }
+ return (ret);
+}
+static void
+lpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
+{
+ wait_queue_head_t *pdone_q;
+
+ /*
+ * If pdone_q is empty, the driver thread gave up waiting and
+ * continued running.
+ */
+ pdone_q = (wait_queue_head_t *) pmboxq->context1;
+ if (pdone_q)
+ wake_up_interruptible(pdone_q);
+ return;
+}
+
+void
+lpfc_sli_def_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+ struct lpfc_dmabuf *mp;
+ mp = (struct lpfc_dmabuf *) (pmb->context1);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ mempool_free( pmb, phba->mbox_mem_pool);
+ return;
+}
+
+int
+lpfc_sli_handle_mb_event(struct lpfc_hba * phba)
+{
+ MAILBOX_t *mbox;
+ MAILBOX_t *pmbox;
+ LPFC_MBOXQ_t *pmb;
+ struct lpfc_sli *psli;
+ int i, rc;
+ uint32_t process_next;
+
+ psli = &phba->sli;
+ /* We should only get here if we are in SLI2 mode */
+ if (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE)) {
+ return (1);
+ }
+
+ phba->sli.slistat.mbox_event++;
+
+ /* Get a Mailbox buffer to setup mailbox commands for callback */
+ if ((pmb = phba->sli.mbox_active)) {
+ pmbox = &pmb->mb;
+ mbox = (MAILBOX_t *) phba->sli.MBhostaddr;
+
+ /* First check out the status word */
+ lpfc_sli_pcimem_bcopy(mbox, pmbox, sizeof (uint32_t));
+
+ /* Sanity check to ensure the host owns the mailbox */
+ if (pmbox->mbxOwner != OWN_HOST) {
+ /* Lets try for a while */
+ for (i = 0; i < 10240; i++) {
+ /* First copy command data */
+ lpfc_sli_pcimem_bcopy(mbox, pmbox,
+ sizeof (uint32_t));
+ if (pmbox->mbxOwner == OWN_HOST)
+ goto mbout;
+ }
+ /* Stray Mailbox Interrupt, mbxCommand <cmd> mbxStatus
+ <status> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_MBOX | LOG_SLI,
+ "%d:0304 Stray Mailbox Interrupt "
+ "mbxCommand x%x mbxStatus x%x\n",
+ phba->brd_no,
+ pmbox->mbxCommand,
+ pmbox->mbxStatus);
+
+ spin_lock_irq(phba->host->host_lock);
+ phba->sli.sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irq(phba->host->host_lock);
+ return (1);
+ }
+
+ mbout:
+ del_timer_sync(&phba->sli.mbox_tmo);
+ phba->work_hba_events &= ~WORKER_MBOX_TMO;
+
+ /*
+ * It is a fatal error if unknown mbox command completion.
+ */
+ if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
+ MBX_SHUTDOWN) {
+
+ /* Unknow mailbox command compl */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_MBOX | LOG_SLI,
+ "%d:0323 Unknown Mailbox command %x Cmpl\n",
+ phba->brd_no,
+ pmbox->mbxCommand);
+ phba->hba_state = LPFC_HBA_ERROR;
+ phba->work_hs = HS_FFER3;
+ lpfc_handle_eratt(phba);
+ return (0);
+ }
+
+ phba->sli.mbox_active = NULL;
+ if (pmbox->mbxStatus) {
+ phba->sli.slistat.mbox_stat_err++;
+ if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) {
+ /* Mbox cmd cmpl error - RETRYing */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_MBOX | LOG_SLI,
+ "%d:0305 Mbox cmd cmpl error - "
+ "RETRYing Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ pmbox->mbxCommand,
+ pmbox->mbxStatus,
+ pmbox->un.varWords[0],
+ phba->hba_state);
+ pmbox->mbxStatus = 0;
+ pmbox->mbxOwner = OWN_HOST;
+ spin_lock_irq(phba->host->host_lock);
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc == MBX_SUCCESS)
+ return (0);
+ }
+ }
+
+ /* Mailbox cmd <cmd> Cmpl <cmpl> */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_MBOX | LOG_SLI,
+ "%d:0307 Mailbox cmd x%x Cmpl x%p "
+ "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ pmbox->mbxCommand,
+ pmb->mbox_cmpl,
+ *((uint32_t *) pmbox),
+ pmbox->un.varWords[0],
+ pmbox->un.varWords[1],
+ pmbox->un.varWords[2],
+ pmbox->un.varWords[3],
+ pmbox->un.varWords[4],
+ pmbox->un.varWords[5],
+ pmbox->un.varWords[6],
+ pmbox->un.varWords[7]);
+
+ if (pmb->mbox_cmpl) {
+ lpfc_sli_pcimem_bcopy(mbox, pmbox, MAILBOX_CMD_SIZE);
+ pmb->mbox_cmpl(phba,pmb);
+ }
+ }
+
+
+ do {
+ process_next = 0; /* by default don't loop */
+ spin_lock_irq(phba->host->host_lock);
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+
+ /* Process next mailbox command if there is one */
+ if ((pmb = lpfc_mbox_get(phba))) {
+ spin_unlock_irq(phba->host->host_lock);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ pmb->mb.mbxStatus = MBX_NOT_FINISHED;
+ pmb->mbox_cmpl(phba,pmb);
+ process_next = 1;
+ continue; /* loop back */
+ }
+ } else {
+ spin_unlock_irq(phba->host->host_lock);
+ /* Turn on IOCB processing */
+ for (i = 0; i < phba->sli.num_rings; i++) {
+ lpfc_sli_turn_on_ring(phba, i);
+ }
+
+ /* Free any lpfc_dmabuf's waiting for mbox cmd cmpls */
+ while (!list_empty(&phba->freebufList)) {
+ struct lpfc_dmabuf *mp;
+
+ mp = NULL;
+ list_remove_head((&phba->freebufList),
+ mp,
+ struct lpfc_dmabuf,
+ list);
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt,
+ mp->phys);
+ kfree(mp);
+ }
+ }
+ }
+
+ } while (process_next);
+
+ return (0);
+}
+static int
+lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *saveq)
+{
+ IOCB_t * irsp;
+ WORD5 * w5p;
+ uint32_t Rctl, Type;
+ uint32_t match, i;
+
+ match = 0;
+ irsp = &(saveq->iocb);
+ if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX)
+ || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) {
+ Rctl = FC_ELS_REQ;
+ Type = FC_ELS_DATA;
+ } else {
+ w5p =
+ (WORD5 *) & (saveq->iocb.un.
+ ulpWord[5]);
+ Rctl = w5p->hcsw.Rctl;
+ Type = w5p->hcsw.Type;
+
+ /* Firmware Workaround */
+ if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) &&
+ (irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX)) {
+ Rctl = FC_ELS_REQ;
+ Type = FC_ELS_DATA;
+ w5p->hcsw.Rctl = Rctl;
+ w5p->hcsw.Type = Type;
+ }
+ }
+ /* unSolicited Responses */
+ if (pring->prt[0].profile) {
+ (pring->prt[0].lpfc_sli_rcv_unsol_event) (phba, pring, saveq);
+ match = 1;
+ } else {
+ /* We must search, based on rctl / type
+ for the right routine */
+ for (i = 0; i < pring->num_mask;
+ i++) {
+ if ((pring->prt[i].rctl ==
+ Rctl)
+ && (pring->prt[i].
+ type == Type)) {
+ (pring->prt[i].lpfc_sli_rcv_unsol_event)
+ (phba, pring, saveq);
+ match = 1;
+ break;
+ }
+ }
+ }
+ if (match == 0) {
+ /* Unexpected Rctl / Type received */
+ /* Ring <ringno> handler: unexpected
+ Rctl <Rctl> Type <Type> received */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_SLI,
+ "%d:0313 Ring %d handler: unexpected Rctl x%x "
+ "Type x%x received \n",
+ phba->brd_no,
+ pring->ringno,
+ Rctl,
+ Type);
+ }
+ return(1);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_txcmpl_ring_search_slow(struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * prspiocb)
+{
+ IOCB_t *icmd = NULL;
+ IOCB_t *irsp = NULL;
+ struct lpfc_iocbq *cmd_iocb;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ uint16_t iotag;
+
+ irsp = &prspiocb->iocb;
+ iotag = irsp->ulpIoTag;
+ cmd_iocb = NULL;
+
+ /* Search through txcmpl from the begining */
+ list_for_each_entry_safe(iocb, next_iocb, &(pring->txcmplq), list) {
+ icmd = &iocb->iocb;
+ if (iotag == icmd->ulpIoTag) {
+ /* Found a match. */
+ cmd_iocb = iocb;
+ list_del(&iocb->list);
+ pring->txcmplq_cnt--;
+ break;
+ }
+ }
+
+ return (cmd_iocb);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_txcmpl_ring_iotag_lookup(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * prspiocb)
+{
+ IOCB_t *irsp = NULL;
+ struct lpfc_iocbq *cmd_iocb = NULL;
+ uint16_t iotag;
+
+ if (unlikely(pring->fast_lookup == NULL))
+ return NULL;
+
+ /* Use fast lookup based on iotag for completion */
+ irsp = &prspiocb->iocb;
+ iotag = irsp->ulpIoTag;
+ if (iotag < pring->fast_iotag) {
+ cmd_iocb = *(pring->fast_lookup + iotag);
+ *(pring->fast_lookup + iotag) = NULL;
+ if (cmd_iocb) {
+ list_del(&cmd_iocb->list);
+ pring->txcmplq_cnt--;
+ return cmd_iocb;
+ } else {
+ /*
+ * This is clearly an error. A ring that uses iotags
+ * should never have a interrupt for a completion that
+ * is not on the ring. Return NULL and log a error.
+ */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "%d:0327 Rsp ring %d error - command "
+ "completion for iotag x%x not found\n",
+ phba->brd_no, pring->ringno, iotag);
+ return NULL;
+ }
+ }
+
+ /*
+ * Rsp ring <ringno> get: iotag <iotag> greater then
+ * configured max <fast_iotag> wd0 <irsp>. This is an
+ * error. Just return NULL.
+ */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "%d:0317 Rsp ring %d get: iotag x%x greater then "
+ "configured max x%x wd0 x%x\n",
+ phba->brd_no, pring->ringno, iotag, pring->fast_iotag,
+ *(((uint32_t *) irsp) + 7));
+ return NULL;
+}
+
+static int
+lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq *saveq)
+{
+ struct lpfc_iocbq * cmdiocbp;
+ int rc = 1;
+ unsigned long iflag;
+
+ /* Based on the iotag field, get the cmd IOCB from the txcmplq */
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ cmdiocbp = lpfc_sli_txcmpl_ring_search_slow(pring, saveq);
+ if (cmdiocbp) {
+ if (cmdiocbp->iocb_cmpl) {
+ /*
+ * Post all ELS completions to the worker thread.
+ * All other are passed to the completion callback.
+ */
+ if (pring->ringno == LPFC_ELS_RING) {
+ spin_unlock_irqrestore(phba->host->host_lock,
+ iflag);
+ (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ }
+ else {
+ if (cmdiocbp->iocb_flag & LPFC_IO_POLL)
+ rc = 0;
+
+ spin_unlock_irqrestore(phba->host->host_lock,
+ iflag);
+ (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ }
+ } else {
+ list_add_tail(&cmdiocbp->list, &phba->lpfc_iocb_list);
+ }
+ } else {
+ /*
+ * Unknown initiating command based on the response iotag.
+ * This could be the case on the ELS ring because of
+ * lpfc_els_abort().
+ */
+ if (pring->ringno != LPFC_ELS_RING) {
+ /*
+ * Ring <ringno> handler: unexpected completion IoTag
+ * <IoTag>
+ */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_SLI,
+ "%d:0322 Ring %d handler: unexpected "
+ "completion IoTag x%x Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ pring->ringno,
+ saveq->iocb.ulpIoTag,
+ saveq->iocb.ulpStatus,
+ saveq->iocb.un.ulpWord[4],
+ saveq->iocb.ulpCommand,
+ saveq->iocb.ulpContext);
+ }
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return rc;
+}
+
+/*
+ * This routine presumes LPFC_FCP_RING handling and doesn't bother
+ * to check it explicitly.
+ */
+static int
+lpfc_sli_handle_fast_ring_event(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring, uint32_t mask)
+{
+ IOCB_t *irsp = NULL;
+ struct lpfc_iocbq *cmdiocbq = NULL;
+ struct lpfc_iocbq rspiocbq;
+ PGP *pgp;
+ uint32_t status;
+ uint32_t portRspPut, portRspMax;
+ int rc = 1;
+ lpfc_iocb_type type;
+ unsigned long iflag;
+ uint32_t rsp_cmpl = 0;
+ void __iomem *to_slim;
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ pring->stats.iocb_event++;
+
+ /* The driver assumes SLI-2 mode */
+ pgp = (PGP *) &((MAILBOX_t *) phba->sli.MBhostaddr)
+ ->us.s2.port[pring->ringno];
+
+ /*
+ * The next available response entry should never exceed the maximum
+ * entries. If it does, treat it as an adapter hardware error.
+ */
+ portRspMax = pring->numRiocb;
+ portRspPut = le32_to_cpu(pgp->rspPutInx);
+ if (unlikely(portRspPut >= portRspMax)) {
+ /*
+ * Ring <ringno> handler: portRspPut <portRspPut> is bigger then
+ * rsp ring <portRspMax>
+ */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "%d:0312 Ring %d handler: portRspPut %d "
+ "is bigger then rsp ring %d\n",
+ phba->brd_no, pring->ringno, portRspPut,
+ portRspMax);
+
+ phba->hba_state = LPFC_HBA_ERROR;
+
+ /* All error attention handlers are posted to worker thread */
+ phba->work_ha |= HA_ERATT;
+ phba->work_hs = HS_FFER3;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return 1;
+ }
+
+ rmb();
+ while (pring->rspidx != portRspPut) {
+ irsp = (IOCB_t *) IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+ type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK);
+ pring->stats.iocb_rsp++;
+ rsp_cmpl++;
+
+ if (unlikely(irsp->ulpStatus)) {
+ /* Rsp ring <ringno> error: IOCB */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "%d:0326 Rsp Ring %d error: IOCB Data: "
+ "x%x x%x x%x x%x x%x x%x x%x x%x\n",
+ phba->brd_no, pring->ringno,
+ irsp->un.ulpWord[0], irsp->un.ulpWord[1],
+ irsp->un.ulpWord[2], irsp->un.ulpWord[3],
+ irsp->un.ulpWord[4], irsp->un.ulpWord[5],
+ *(((uint32_t *) irsp) + 6),
+ *(((uint32_t *) irsp) + 7));
+ }
+
+ switch (type) {
+ case LPFC_ABORT_IOCB:
+ case LPFC_SOL_IOCB:
+ /*
+ * Idle exchange closed via ABTS from port. No iocb
+ * resources need to be recovered.
+ */
+ if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) {
+ printk(KERN_INFO "%s: IOCB cmd 0x%x processed. "
+ "Skipping completion\n", __FUNCTION__,
+ irsp->ulpCommand);
+ break;
+ }
+
+ rspiocbq.iocb.un.ulpWord[4] = irsp->un.ulpWord[4];
+ rspiocbq.iocb.ulpStatus = irsp->ulpStatus;
+ rspiocbq.iocb.ulpContext = irsp->ulpContext;
+ rspiocbq.iocb.ulpIoTag = irsp->ulpIoTag;
+ cmdiocbq = lpfc_sli_txcmpl_ring_iotag_lookup(phba,
+ pring,
+ &rspiocbq);
+ if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
+ spin_unlock_irqrestore(
+ phba->host->host_lock, iflag);
+ (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
+ &rspiocbq);
+ spin_lock_irqsave(phba->host->host_lock,
+ iflag);
+ }
+ break;
+ default:
+ if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
+ char adaptermsg[LPFC_MAX_ADPTMSG];
+ memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
+ memcpy(&adaptermsg[0], (uint8_t *) irsp,
+ MAX_MSG_DATA);
+ dev_warn(&((phba->pcidev)->dev), "lpfc%d: %s",
+ phba->brd_no, adaptermsg);
+ } else {
+ /* Unknown IOCB command */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "%d:0321 Unknown IOCB command "
+ "Data: x%x, x%x x%x x%x x%x\n",
+ phba->brd_no, type, irsp->ulpCommand,
+ irsp->ulpStatus, irsp->ulpIoTag,
+ irsp->ulpContext);
+ }
+ break;
+ }
+
+ /*
+ * The response IOCB has been processed. Update the ring
+ * pointer in SLIM. If the port response put pointer has not
+ * been updated, sync the pgp->rspPutInx and fetch the new port
+ * response put pointer.
+ */
+ if (++pring->rspidx >= portRspMax)
+ pring->rspidx = 0;
+
+ to_slim = phba->MBslimaddr +
+ (SLIMOFF + (pring->ringno * 2) + 1) * 4;
+ writeb(pring->rspidx, to_slim);
+
+ if (pring->rspidx == portRspPut)
+ portRspPut = le32_to_cpu(pgp->rspPutInx);
+ }
+
+ if ((rsp_cmpl > 0) && (mask & HA_R0RE_REQ)) {
+ pring->stats.iocb_rsp_full++;
+ status = ((CA_R0ATT | CA_R0RE_RSP) << (pring->ringno * 4));
+ writel(status, phba->CAregaddr);
+ readl(phba->CAregaddr);
+ }
+ if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
+ pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
+ pring->stats.iocb_cmd_empty++;
+
+ /* Force update of the local copy of cmdGetInx */
+ pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+ lpfc_sli_resume_iocb(phba, pring);
+
+ if ((pring->lpfc_sli_cmd_available))
+ (pring->lpfc_sli_cmd_available) (phba, pring);
+
+ }
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return rc;
+}
+
+
+int
+lpfc_sli_handle_slow_ring_event(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring, uint32_t mask)
+{
+ IOCB_t *entry;
+ IOCB_t *irsp = NULL;
+ struct lpfc_iocbq *rspiocbp = NULL;
+ struct lpfc_iocbq *next_iocb;
+ struct lpfc_iocbq *cmdiocbp;
+ struct lpfc_iocbq *saveq;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ HGP *hgp;
+ PGP *pgp;
+ MAILBOX_t *mbox;
+ uint8_t iocb_cmd_type;
+ lpfc_iocb_type type;
+ uint32_t status, free_saveq;
+ uint32_t portRspPut, portRspMax;
+ int rc = 1;
+ unsigned long iflag;
+ void __iomem *to_slim;
+
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ pring->stats.iocb_event++;
+
+ /* The driver assumes SLI-2 mode */
+ mbox = (MAILBOX_t *) phba->sli.MBhostaddr;
+ pgp = (PGP *) & mbox->us.s2.port[pring->ringno];
+ hgp = (HGP *) & mbox->us.s2.host[pring->ringno];
+
+ /*
+ * The next available response entry should never exceed the maximum
+ * entries. If it does, treat it as an adapter hardware error.
+ */
+ portRspMax = pring->numRiocb;
+ portRspPut = le32_to_cpu(pgp->rspPutInx);
+ if (portRspPut >= portRspMax) {
+ /*
+ * Ring <ringno> handler: portRspPut <portRspPut> is bigger then
+ * rsp ring <portRspMax>
+ */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_SLI,
+ "%d:0312 Ring %d handler: portRspPut %d "
+ "is bigger then rsp ring %d\n",
+ phba->brd_no,
+ pring->ringno, portRspPut, portRspMax);
+
+ phba->hba_state = LPFC_HBA_ERROR;
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+
+ phba->work_hs = HS_FFER3;
+ lpfc_handle_eratt(phba);
+
+ return 1;
+ }
+
+ rmb();
+ lpfc_iocb_list = &phba->lpfc_iocb_list;
+ while (pring->rspidx != portRspPut) {
+ /*
+ * Build a completion list and call the appropriate handler.
+ * The process is to get the next available response iocb, get
+ * a free iocb from the list, copy the response data into the
+ * free iocb, insert to the continuation list, and update the
+ * next response index to slim. This process makes response
+ * iocb's in the ring available to DMA as fast as possible but
+ * pays a penalty for a copy operation. Since the iocb is
+ * only 32 bytes, this penalty is considered small relative to
+ * the PCI reads for register values and a slim write. When
+ * the ulpLe field is set, the entire Command has been
+ * received.
+ */
+ entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+ list_remove_head(lpfc_iocb_list, rspiocbp, struct lpfc_iocbq,
+ list);
+ if (rspiocbp == NULL) {
+ printk(KERN_ERR "%s: out of buffers! Failing "
+ "completion.\n", __FUNCTION__);
+ break;
+ }
+
+ lpfc_sli_pcimem_bcopy(entry, &rspiocbp->iocb, sizeof (IOCB_t));
+ irsp = &rspiocbp->iocb;
+
+ if (++pring->rspidx >= portRspMax)
+ pring->rspidx = 0;
+
+ to_slim = phba->MBslimaddr + (SLIMOFF + (pring->ringno * 2)
+ + 1) * 4;
+ writeb(pring->rspidx, to_slim);
+
+ if (list_empty(&(pring->iocb_continueq))) {
+ list_add(&rspiocbp->list, &(pring->iocb_continueq));
+ } else {
+ list_add_tail(&rspiocbp->list,
+ &(pring->iocb_continueq));
+ }
+
+ pring->iocb_continueq_cnt++;
+ if (irsp->ulpLe) {
+ /*
+ * By default, the driver expects to free all resources
+ * associated with this iocb completion.
+ */
+ free_saveq = 1;
+ saveq = list_get_first(&pring->iocb_continueq,
+ struct lpfc_iocbq, list);
+ irsp = &(saveq->iocb);
+ list_del_init(&pring->iocb_continueq);
+ pring->iocb_continueq_cnt = 0;
+
+ pring->stats.iocb_rsp++;
+
+ if (irsp->ulpStatus) {
+ /* Rsp ring <ringno> error: IOCB */
+ lpfc_printf_log(phba,
+ KERN_WARNING,
+ LOG_SLI,
+ "%d:0328 Rsp Ring %d error: IOCB Data: "
+ "x%x x%x x%x x%x x%x x%x x%x x%x\n",
+ phba->brd_no,
+ pring->ringno,
+ irsp->un.ulpWord[0],
+ irsp->un.ulpWord[1],
+ irsp->un.ulpWord[2],
+ irsp->un.ulpWord[3],
+ irsp->un.ulpWord[4],
+ irsp->un.ulpWord[5],
+ *(((uint32_t *) irsp) + 6),
+ *(((uint32_t *) irsp) + 7));
+ }
+
+ /*
+ * Fetch the IOCB command type and call the correct
+ * completion routine. Solicited and Unsolicited
+ * IOCBs on the ELS ring get freed back to the
+ * lpfc_iocb_list by the discovery kernel thread.
+ */
+ iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
+ type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
+ if (type == LPFC_SOL_IOCB) {
+ spin_unlock_irqrestore(phba->host->host_lock,
+ iflag);
+ rc = lpfc_sli_process_sol_iocb(phba, pring,
+ saveq);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ } else if (type == LPFC_UNSOL_IOCB) {
+ spin_unlock_irqrestore(phba->host->host_lock,
+ iflag);
+ rc = lpfc_sli_process_unsol_iocb(phba, pring,
+ saveq);
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ } else if (type == LPFC_ABORT_IOCB) {
+ if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) &&
+ ((cmdiocbp =
+ lpfc_sli_txcmpl_ring_search_slow(pring,
+ saveq)))) {
+ /* Call the specified completion
+ routine */
+ if (cmdiocbp->iocb_cmpl) {
+ spin_unlock_irqrestore(
+ phba->host->host_lock,
+ iflag);
+ (cmdiocbp->iocb_cmpl) (phba,
+ cmdiocbp, saveq);
+ spin_lock_irqsave(
+ phba->host->host_lock,
+ iflag);
+ } else {
+ list_add_tail(&cmdiocbp->list,
+ lpfc_iocb_list);
+ }
+ }
+ } else if (type == LPFC_UNKNOWN_IOCB) {
+ if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
+
+ char adaptermsg[LPFC_MAX_ADPTMSG];
+
+ memset(adaptermsg, 0,
+ LPFC_MAX_ADPTMSG);
+ memcpy(&adaptermsg[0], (uint8_t *) irsp,
+ MAX_MSG_DATA);
+ dev_warn(&((phba->pcidev)->dev),
+ "lpfc%d: %s",
+ phba->brd_no, adaptermsg);
+ } else {
+ /* Unknown IOCB command */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_SLI,
+ "%d:0321 Unknown IOCB command "
+ "Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ irsp->ulpCommand,
+ irsp->ulpStatus,
+ irsp->ulpIoTag,
+ irsp->ulpContext);
+ }
+ }
+
+ if (free_saveq) {
+ if (!list_empty(&saveq->list)) {
+ list_for_each_entry_safe(rspiocbp,
+ next_iocb,
+ &saveq->list,
+ list) {
+ list_add_tail(&rspiocbp->list,
+ lpfc_iocb_list);
+ }
+ }
+
+ list_add_tail(&saveq->list, lpfc_iocb_list);
+ }
+ }
+
+ /*
+ * If the port response put pointer has not been updated, sync
+ * the pgp->rspPutInx in the MAILBOX_tand fetch the new port
+ * response put pointer.
+ */
+ if (pring->rspidx == portRspPut) {
+ portRspPut = le32_to_cpu(pgp->rspPutInx);
+ }
+ } /* while (pring->rspidx != portRspPut) */
+
+ if ((rspiocbp != 0) && (mask & HA_R0RE_REQ)) {
+ /* At least one response entry has been freed */
+ pring->stats.iocb_rsp_full++;
+ /* SET RxRE_RSP in Chip Att register */
+ status = ((CA_R0ATT | CA_R0RE_RSP) << (pring->ringno * 4));
+ writel(status, phba->CAregaddr);
+ readl(phba->CAregaddr); /* flush */
+ }
+ if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
+ pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
+ pring->stats.iocb_cmd_empty++;
+
+ /* Force update of the local copy of cmdGetInx */
+ pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+ lpfc_sli_resume_iocb(phba, pring);
+
+ if ((pring->lpfc_sli_cmd_available))
+ (pring->lpfc_sli_cmd_available) (phba, pring);
+
+ }
+
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+ return rc;
+}
+
+int
+lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
+{
+ struct lpfc_iocbq *iocb, *next_iocb;
+ IOCB_t *icmd = NULL, *cmd = NULL;
+ int errcnt;
+ uint16_t iotag;
+
+ errcnt = 0;
+
+ /* Error everything on txq and txcmplq
+ * First do the txq.
+ */
+ spin_lock_irq(phba->host->host_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ list_del_init(&iocb->list);
+ if (iocb->iocb_cmpl) {
+ icmd = &iocb->iocb;
+ icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&iocb->list, &phba->lpfc_iocb_list);
+ }
+ }
+ pring->txq_cnt = 0;
+ INIT_LIST_HEAD(&(pring->txq));
+
+ /* Next issue ABTS for everything on the txcmplq */
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ cmd = &iocb->iocb;
+
+ /*
+ * Imediate abort of IOCB, clear fast_lookup entry,
+ * if any, deque and call compl
+ */
+ iotag = cmd->ulpIoTag;
+ if (iotag && pring->fast_lookup &&
+ (iotag < pring->fast_iotag))
+ pring->fast_lookup[iotag] = NULL;
+
+ list_del_init(&iocb->list);
+ pring->txcmplq_cnt--;
+
+ if (iocb->iocb_cmpl) {
+ cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ spin_unlock_irq(phba->host->host_lock);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irq(phba->host->host_lock);
+ } else {
+ list_add_tail(&iocb->list, &phba->lpfc_iocb_list);
+ }
+ }
+
+ INIT_LIST_HEAD(&pring->txcmplq);
+ pring->txcmplq_cnt = 0;
+ spin_unlock_irq(phba->host->host_lock);
+
+ return errcnt;
+}
+
+/******************************************************************************
+* lpfc_sli_send_reset
+*
+* Note: After returning from this function, the HBA cannot be accessed for
+* 1 ms. Since we do not wish to delay in interrupt context, it is the
+* responsibility of the caller to perform the mdelay(1) and flush via readl().
+******************************************************************************/
+static int
+lpfc_sli_send_reset(struct lpfc_hba * phba, uint16_t skip_post)
+{
+ MAILBOX_t *swpmb;
+ volatile uint32_t word0;
+ void __iomem *to_slim;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+
+ /* A board reset must use REAL SLIM. */
+ phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE;
+
+ word0 = 0;
+ swpmb = (MAILBOX_t *) & word0;
+ swpmb->mbxCommand = MBX_RESTART;
+ swpmb->mbxHc = 1;
+
+ to_slim = phba->MBslimaddr;
+ writel(*(uint32_t *) swpmb, to_slim);
+ readl(to_slim); /* flush */
+
+ /* Only skip post after fc_ffinit is completed */
+ if (skip_post) {
+ word0 = 1; /* This is really setting up word1 */
+ } else {
+ word0 = 0; /* This is really setting up word1 */
+ }
+ to_slim = phba->MBslimaddr + sizeof (uint32_t);
+ writel(*(uint32_t *) swpmb, to_slim);
+ readl(to_slim); /* flush */
+
+ /* Turn off parity checking and serr during the physical reset */
+ pci_read_config_word(phba->pcidev, PCI_COMMAND, &phba->pci_cfg_value);
+ pci_write_config_word(phba->pcidev, PCI_COMMAND,
+ (phba->pci_cfg_value &
+ ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
+
+ writel(HC_INITFF, phba->HCregaddr);
+
+ phba->hba_state = LPFC_INIT_START;
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+
+ return 0;
+}
+
+static int
+lpfc_sli_brdreset(struct lpfc_hba * phba, uint16_t skip_post)
+{
+ struct lpfc_sli_ring *pring;
+ int i;
+ struct lpfc_dmabuf *mp, *next_mp;
+ unsigned long flags = 0;
+
+ lpfc_sli_send_reset(phba, skip_post);
+ mdelay(1);
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ /* Risk the write on flush case ie no delay after the readl */
+ readl(phba->HCregaddr); /* flush */
+ /* Now toggle INITFF bit set by lpfc_sli_send_reset */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+
+ /* Restore PCI cmd register */
+ pci_write_config_word(phba->pcidev, PCI_COMMAND, phba->pci_cfg_value);
+
+ /* perform board reset */
+ phba->fc_eventTag = 0;
+ phba->fc_myDID = 0;
+ phba->fc_prevDID = Mask_DID;
+
+ /* Reset HBA */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_SLI,
+ "%d:0325 Reset HBA Data: x%x x%x x%x\n",
+ phba->brd_no,
+ phba->hba_state,
+ phba->sli.sli_flag,
+ skip_post);
+
+ /* Initialize relevant SLI info */
+ for (i = 0; i < phba->sli.num_rings; i++) {
+ pring = &phba->sli.ring[i];
+ pring->flag = 0;
+ pring->rspidx = 0;
+ pring->next_cmdidx = 0;
+ pring->local_getidx = 0;
+ pring->cmdidx = 0;
+ pring->missbufcnt = 0;
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+
+ if (skip_post) {
+ mdelay(100);
+ } else {
+ mdelay(2000);
+ }
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ /* Cleanup preposted buffers on the ELS ring */
+ pring = &phba->sli.ring[LPFC_ELS_RING];
+ list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+ list_del(&mp->list);
+ pring->postbufq_cnt--;
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+
+ for (i = 0; i < phba->sli.num_rings; i++)
+ lpfc_sli_abort_iocb_ring(phba, &phba->sli.ring[i]);
+
+ return 0;
+}
+
+static int
+lpfc_sli_chipset_init(struct lpfc_hba *phba)
+{
+ uint32_t status, i = 0;
+
+ /* Read the HBA Host Status Register */
+ status = readl(phba->HSregaddr);
+
+ /* Check status register to see what current state is */
+ i = 0;
+ while ((status & (HS_FFRDY | HS_MBRDY)) != (HS_FFRDY | HS_MBRDY)) {
+
+ /* Check every 100ms for 5 retries, then every 500ms for 5, then
+ * every 2.5 sec for 5, then reset board and every 2.5 sec for
+ * 4.
+ */
+ if (i++ >= 20) {
+ /* Adapter failed to init, timeout, status reg
+ <status> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0436 Adapter failed to init, "
+ "timeout, status reg x%x\n",
+ phba->brd_no,
+ status);
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -ETIMEDOUT;
+ }
+
+ /* Check to see if any errors occurred during init */
+ if (status & HS_FFERM) {
+ /* ERROR: During chipset initialization */
+ /* Adapter failed to init, chipset, status reg
+ <status> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0437 Adapter failed to init, "
+ "chipset, status reg x%x\n",
+ phba->brd_no,
+ status);
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -EIO;
+ }
+
+ if (i <= 5) {
+ msleep(10);
+ } else if (i <= 10) {
+ msleep(500);
+ } else {
+ msleep(2500);
+ }
+
+ if (i == 15) {
+ lpfc_sli_brdreset(phba, 0);
+ }
+ /* Read the HBA Host Status Register */
+ status = readl(phba->HSregaddr);
+ }
+
+ /* Check to see if any errors occurred during init */
+ if (status & HS_FFERM) {
+ /* ERROR: During chipset initialization */
+ /* Adapter failed to init, chipset, status reg <status> */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_INIT,
+ "%d:0438 Adapter failed to init, chipset, "
+ "status reg x%x\n",
+ phba->brd_no,
+ status);
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -EIO;
+ }
+
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+
+ /* setup host attn register */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+ return 0;
+}
+
+int
+lpfc_sli_hba_setup(struct lpfc_hba * phba)
+{
+ LPFC_MBOXQ_t *pmb;
+ uint32_t resetcount = 0, rc = 0, done = 0;
+
+ pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ phba->hba_state = LPFC_HBA_ERROR;
+ return -ENOMEM;
+ }
+
+ while (resetcount < 2 && !done) {
+ phba->hba_state = 0;
+ lpfc_sli_brdreset(phba, 0);
+ msleep(2500);
+ rc = lpfc_sli_chipset_init(phba);
+ if (rc)
+ break;
+
+ resetcount++;
+
+ /* Call pre CONFIG_PORT mailbox command initialization. A value of 0
+ * means the call was successful. Any other nonzero value is a failure,
+ * but if ERESTART is returned, the driver may reset the HBA and try
+ * again.
+ */
+ rc = lpfc_config_port_prep(phba);
+ if (rc == -ERESTART) {
+ phba->hba_state = 0;
+ continue;
+ } else if (rc) {
+ break;
+ }
+
+ phba->hba_state = LPFC_INIT_MBX_CMDS;
+ lpfc_config_port(phba, pmb);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ if (rc == MBX_SUCCESS)
+ done = 1;
+ else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "%d:0442 Adapter failed to init, mbxCmd x%x "
+ "CONFIG_PORT, mbxStatus x%x Data: x%x\n",
+ phba->brd_no, pmb->mb.mbxCommand,
+ pmb->mb.mbxStatus, 0);
+ phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE;
+ }
+ }
+ if (!done)
+ goto lpfc_sli_hba_setup_error;
+
+ rc = lpfc_sli_ring_map(phba, pmb);
+
+ if (rc)
+ goto lpfc_sli_hba_setup_error;
+
+ phba->sli.sli_flag |= LPFC_PROCESS_LA;
+
+ rc = lpfc_config_port_post(phba);
+ if (rc)
+ goto lpfc_sli_hba_setup_error;
+
+ goto lpfc_sli_hba_setup_exit;
+lpfc_sli_hba_setup_error:
+ phba->hba_state = LPFC_HBA_ERROR;
+lpfc_sli_hba_setup_exit:
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return rc;
+}
+
+static void
+lpfc_mbox_abort(struct lpfc_hba * phba)
+{
+ LPFC_MBOXQ_t *pmbox;
+ MAILBOX_t *mb;
+
+ if (phba->sli.mbox_active) {
+ del_timer_sync(&phba->sli.mbox_tmo);
+ phba->work_hba_events &= ~WORKER_MBOX_TMO;
+ pmbox = phba->sli.mbox_active;
+ mb = &pmbox->mb;
+ phba->sli.mbox_active = NULL;
+ if (pmbox->mbox_cmpl) {
+ mb->mbxStatus = MBX_NOT_FINISHED;
+ (pmbox->mbox_cmpl) (phba, pmbox);
+ }
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ }
+
+ /* Abort all the non active mailbox commands. */
+ spin_lock_irq(phba->host->host_lock);
+ pmbox = lpfc_mbox_get(phba);
+ while (pmbox) {
+ mb = &pmbox->mb;
+ if (pmbox->mbox_cmpl) {
+ mb->mbxStatus = MBX_NOT_FINISHED;
+ spin_unlock_irq(phba->host->host_lock);
+ (pmbox->mbox_cmpl) (phba, pmbox);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ pmbox = lpfc_mbox_get(phba);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+}
+
+/*! lpfc_mbox_timeout
+ *
+ * \pre
+ * \post
+ * \param hba Pointer to per struct lpfc_hba structure
+ * \param l1 Pointer to the driver's mailbox queue.
+ * \return
+ * void
+ *
+ * \b Description:
+ *
+ * This routine handles mailbox timeout events at timer interrupt context.
+ */
+void
+lpfc_mbox_timeout(unsigned long ptr)
+{
+ struct lpfc_hba *phba;
+ unsigned long iflag;
+
+ phba = (struct lpfc_hba *)ptr;
+ spin_lock_irqsave(phba->host->host_lock, iflag);
+ if (!(phba->work_hba_events & WORKER_MBOX_TMO)) {
+ phba->work_hba_events |= WORKER_MBOX_TMO;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ }
+ spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+void
+lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmbox;
+ MAILBOX_t *mb;
+
+ spin_lock_irq(phba->host->host_lock);
+ if (!(phba->work_hba_events & WORKER_MBOX_TMO)) {
+ spin_unlock_irq(phba->host->host_lock);
+ return;
+ }
+
+ pmbox = phba->sli.mbox_active;
+ mb = &pmbox->mb;
+
+ /* Mbox cmd <mbxCommand> timeout */
+ lpfc_printf_log(phba,
+ KERN_ERR,
+ LOG_MBOX | LOG_SLI,
+ "%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
+ phba->brd_no,
+ mb->mbxCommand,
+ phba->hba_state,
+ phba->sli.sli_flag,
+ phba->sli.mbox_active);
+
+ if (phba->sli.mbox_active == pmbox) {
+ phba->sli.mbox_active = NULL;
+ if (pmbox->mbox_cmpl) {
+ mb->mbxStatus = MBX_NOT_FINISHED;
+ spin_unlock_irq(phba->host->host_lock);
+ (pmbox->mbox_cmpl) (phba, pmbox);
+ spin_lock_irq(phba->host->host_lock);
+ }
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ }
+
+ spin_unlock_irq(phba->host->host_lock);
+ lpfc_mbox_abort(phba);
+ return;
+}
+
+int
+lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag)
+{
+ MAILBOX_t *mbox;
+ MAILBOX_t *mb;
+ struct lpfc_sli *psli;
+ uint32_t status, evtctr;
+ uint32_t ha_copy;
+ int i;
+ unsigned long drvr_flag = 0;
+ volatile uint32_t word0, ldata;
+ void __iomem *to_slim;
+
+ psli = &phba->sli;
+
+ spin_lock_irqsave(phba->host->host_lock, drvr_flag);
+
+
+ mb = &pmbox->mb;
+ status = MBX_SUCCESS;
+
+ if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+ /* Polling for a mbox command when another one is already active
+ * is not allowed in SLI. Also, the driver must have established
+ * SLI2 mode to queue and process multiple mbox commands.
+ */
+
+ if (flag & MBX_POLL) {
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+
+ /* Mbox command <mbxCommand> cannot issue */
+ LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag)
+ return (MBX_NOT_FINISHED);
+ }
+
+ if (!(psli->sli_flag & LPFC_SLI2_ACTIVE)) {
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+ /* Mbox command <mbxCommand> cannot issue */
+ LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag)
+ return (MBX_NOT_FINISHED);
+ }
+
+ /* Handle STOP IOCB processing flag. This is only meaningful
+ * if we are not polling for mbox completion.
+ */
+ if (flag & MBX_STOP_IOCB) {
+ flag &= ~MBX_STOP_IOCB;
+ /* Now flag each ring */
+ for (i = 0; i < psli->num_rings; i++) {
+ /* If the ring is active, flag it */
+ if (psli->ring[i].cmdringaddr) {
+ psli->ring[i].flag |=
+ LPFC_STOP_IOCB_MBX;
+ }
+ }
+ }
+
+ /* Another mailbox command is still being processed, queue this
+ * command to be processed later.
+ */
+ lpfc_mbox_put(phba, pmbox);
+
+ /* Mbox cmd issue - BUSY */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_MBOX | LOG_SLI,
+ "%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n",
+ phba->brd_no,
+ mb->mbxCommand,
+ phba->hba_state,
+ psli->sli_flag,
+ flag);
+
+ psli->slistat.mbox_busy++;
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+
+ return (MBX_BUSY);
+ }
+
+ /* Handle STOP IOCB processing flag. This is only meaningful
+ * if we are not polling for mbox completion.
+ */
+ if (flag & MBX_STOP_IOCB) {
+ flag &= ~MBX_STOP_IOCB;
+ if (flag == MBX_NOWAIT) {
+ /* Now flag each ring */
+ for (i = 0; i < psli->num_rings; i++) {
+ /* If the ring is active, flag it */
+ if (psli->ring[i].cmdringaddr) {
+ psli->ring[i].flag |=
+ LPFC_STOP_IOCB_MBX;
+ }
+ }
+ }
+ }
+
+ psli->sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+
+ /* If we are not polling, we MUST be in SLI2 mode */
+ if (flag != MBX_POLL) {
+ if (!(psli->sli_flag & LPFC_SLI2_ACTIVE)) {
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+ /* Mbox command <mbxCommand> cannot issue */
+ LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag);
+ return (MBX_NOT_FINISHED);
+ }
+ /* timeout active mbox command */
+ mod_timer(&psli->mbox_tmo, jiffies + HZ * LPFC_MBOX_TMO);
+ }
+
+ /* Mailbox cmd <cmd> issue */
+ lpfc_printf_log(phba,
+ KERN_INFO,
+ LOG_MBOX | LOG_SLI,
+ "%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n",
+ phba->brd_no,
+ mb->mbxCommand,
+ phba->hba_state,
+ psli->sli_flag,
+ flag);
+
+ psli->slistat.mbox_cmd++;
+ evtctr = psli->slistat.mbox_event;
+
+ /* next set own bit for the adapter and copy over command word */
+ mb->mbxOwner = OWN_CHIP;
+
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+
+ /* First copy command data to host SLIM area */
+ mbox = (MAILBOX_t *) psli->MBhostaddr;
+ lpfc_sli_pcimem_bcopy(mb, mbox, MAILBOX_CMD_SIZE);
+ } else {
+ if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ /* copy command data into host mbox for cmpl */
+ mbox = (MAILBOX_t *) psli->MBhostaddr;
+ lpfc_sli_pcimem_bcopy(mb, mbox, MAILBOX_CMD_SIZE);
+ }
+
+ /* First copy mbox command data to HBA SLIM, skip past first
+ word */
+ to_slim = phba->MBslimaddr + sizeof (uint32_t);
+ lpfc_memcpy_to_slim(to_slim, &mb->un.varWords[0],
+ MAILBOX_CMD_SIZE - sizeof (uint32_t));
+
+ /* Next copy over first word, with mbxOwner set */
+ ldata = *((volatile uint32_t *)mb);
+ to_slim = phba->MBslimaddr;
+ writel(ldata, to_slim);
+ readl(to_slim); /* flush */
+
+ if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ /* switch over to host mailbox */
+ psli->sli_flag |= LPFC_SLI2_ACTIVE;
+ }
+ }
+
+ wmb();
+ /* interrupt board to doit right away */
+ writel(CA_MBATT, phba->CAregaddr);
+ readl(phba->CAregaddr); /* flush */
+
+ switch (flag) {
+ case MBX_NOWAIT:
+ /* Don't wait for it to finish, just return */
+ psli->mbox_active = pmbox;
+ break;
+
+ case MBX_POLL:
+ i = 0;
+ psli->mbox_active = NULL;
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ /* First read mbox status word */
+ mbox = (MAILBOX_t *) psli->MBhostaddr;
+ word0 = *((volatile uint32_t *)mbox);
+ word0 = le32_to_cpu(word0);
+ } else {
+ /* First read mbox status word */
+ word0 = readl(phba->MBslimaddr);
+ }
+
+ /* Read the HBA Host Attention Register */
+ ha_copy = readl(phba->HAregaddr);
+
+ /* Wait for command to complete */
+ while (((word0 & OWN_CHIP) == OWN_CHIP)
+ || !(ha_copy & HA_MBATT)) {
+ if (i++ >= 100) {
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+ return (MBX_NOT_FINISHED);
+ }
+
+ /* Check if we took a mbox interrupt while we were
+ polling */
+ if (((word0 & OWN_CHIP) != OWN_CHIP)
+ && (evtctr != psli->slistat.mbox_event))
+ break;
+
+ spin_unlock_irqrestore(phba->host->host_lock,
+ drvr_flag);
+
+ /* Can be in interrupt context, do not sleep */
+ /* (or might be called with interrupts disabled) */
+ mdelay(i);
+
+ spin_lock_irqsave(phba->host->host_lock, drvr_flag);
+
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ /* First copy command data */
+ mbox = (MAILBOX_t *) psli->MBhostaddr;
+ word0 = *((volatile uint32_t *)mbox);
+ word0 = le32_to_cpu(word0);
+ if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ MAILBOX_t *slimmb;
+ volatile uint32_t slimword0;
+ /* Check real SLIM for any errors */
+ slimword0 = readl(phba->MBslimaddr);
+ slimmb = (MAILBOX_t *) & slimword0;
+ if (((slimword0 & OWN_CHIP) != OWN_CHIP)
+ && slimmb->mbxStatus) {
+ psli->sli_flag &=
+ ~LPFC_SLI2_ACTIVE;
+ word0 = slimword0;
+ }
+ }
+ } else {
+ /* First copy command data */
+ word0 = readl(phba->MBslimaddr);
+ }
+ /* Read the HBA Host Attention Register */
+ ha_copy = readl(phba->HAregaddr);
+ }
+
+ if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ /* First copy command data */
+ mbox = (MAILBOX_t *) psli->MBhostaddr;
+ /* copy results back to user */
+ lpfc_sli_pcimem_bcopy(mbox, mb, MAILBOX_CMD_SIZE);
+ } else {
+ /* First copy command data */
+ lpfc_memcpy_from_slim(mb, phba->MBslimaddr,
+ MAILBOX_CMD_SIZE);
+ if ((mb->mbxCommand == MBX_DUMP_MEMORY) &&
+ pmbox->context2) {
+ lpfc_memcpy_from_slim((void *)pmbox->context2,
+ phba->MBslimaddr + DMP_RSP_OFFSET,
+ mb->un.varDmp.word_cnt);
+ }
+ }
+
+ writel(HA_MBATT, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ status = mb->mbxStatus;
+ }
+
+ spin_unlock_irqrestore(phba->host->host_lock, drvr_flag);
+ return (status);
+}
+
+static int
+lpfc_sli_ringtx_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * piocb)
+{
+ /* Insert the caller's iocb in the txq tail for later processing. */
+ list_add_tail(&piocb->list, &pring->txq);
+ pring->txq_cnt++;
+ return (0);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq ** piocb)
+{
+ struct lpfc_iocbq * nextiocb;
+
+ nextiocb = lpfc_sli_ringtx_get(phba, pring);
+ if (!nextiocb) {
+ nextiocb = *piocb;
+ *piocb = NULL;
+ }
+
+ return nextiocb;
+}
+
+int
+lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *piocb, uint32_t flag)
+{
+ struct lpfc_iocbq *nextiocb;
+ IOCB_t *iocb;
+
+ /*
+ * We should never get an IOCB if we are in a < LINK_DOWN state
+ */
+ if (unlikely(phba->hba_state < LPFC_LINK_DOWN))
+ return IOCB_ERROR;
+
+ /*
+ * Check to see if we are blocking IOCB processing because of a
+ * outstanding mbox command.
+ */
+ if (unlikely(pring->flag & LPFC_STOP_IOCB_MBX))
+ goto iocb_busy;
+
+ if (unlikely(phba->hba_state == LPFC_LINK_DOWN)) {
+ /*
+ * Only CREATE_XRI, CLOSE_XRI, ABORT_XRI, and QUE_RING_BUF
+ * can be issued if the link is not up.
+ */
+ switch (piocb->iocb.ulpCommand) {
+ case CMD_QUE_RING_BUF_CN:
+ case CMD_QUE_RING_BUF64_CN:
+ case CMD_CLOSE_XRI_CN:
+ case CMD_ABORT_XRI_CN:
+ /*
+ * For IOCBs, like QUE_RING_BUF, that have no rsp ring
+ * completion, iocb_cmpl MUST be 0.
+ */
+ if (piocb->iocb_cmpl)
+ piocb->iocb_cmpl = NULL;
+ /*FALLTHROUGH*/
+ case CMD_CREATE_XRI_CR:
+ break;
+ default:
+ goto iocb_busy;
+ }
+
+ /*
+ * For FCP commands, we must be in a state where we can process link
+ * attention events.
+ */
+ } else if (unlikely(pring->ringno == phba->sli.fcp_ring &&
+ !(phba->sli.sli_flag & LPFC_PROCESS_LA)))
+ goto iocb_busy;
+
+ /*
+ * Check to see if this is a high priority command.
+ * If so bypass tx queue processing.
+ */
+ if (unlikely((flag & SLI_IOCB_HIGH_PRIORITY) &&
+ (iocb = lpfc_sli_next_iocb_slot(phba, pring)))) {
+ lpfc_sli_submit_iocb(phba, pring, iocb, piocb);
+ piocb = NULL;
+ }
+
+ while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) &&
+ (nextiocb = lpfc_sli_next_iocb(phba, pring, &piocb)))
+ lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb);
+
+ if (iocb)
+ lpfc_sli_update_ring(phba, pring);
+ else
+ lpfc_sli_update_full_ring(phba, pring);
+
+ if (!piocb)
+ return IOCB_SUCCESS;
+
+ goto out_busy;
+
+ iocb_busy:
+ pring->stats.iocb_cmd_delay++;
+
+ out_busy:
+
+ if (!(flag & SLI_IOCB_RET_IOCB)) {
+ lpfc_sli_ringtx_put(phba, pring, piocb);
+ return IOCB_SUCCESS;
+ }
+
+ return IOCB_BUSY;
+}
+
+int
+lpfc_sli_setup(struct lpfc_hba *phba)
+{
+ int i, totiocb = 0;
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+
+ psli->num_rings = MAX_CONFIGURED_RINGS;
+ psli->sli_flag = 0;
+ psli->fcp_ring = LPFC_FCP_RING;
+ psli->next_ring = LPFC_FCP_NEXT_RING;
+ psli->ip_ring = LPFC_IP_RING;
+
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->ring[i];
+ switch (i) {
+ case LPFC_FCP_RING: /* ring 0 - FCP */
+ /* numCiocb and numRiocb are used in config_port */
+ pring->numCiocb = SLI2_IOCB_CMD_R0_ENTRIES;
+ pring->numRiocb = SLI2_IOCB_RSP_R0_ENTRIES;
+ pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+ pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+ pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+ pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+ pring->iotag_ctr = 0;
+ pring->iotag_max =
+ (phba->cfg_hba_queue_depth * 2);
+ pring->fast_iotag = pring->iotag_max;
+ pring->num_mask = 0;
+ break;
+ case LPFC_IP_RING: /* ring 1 - IP */
+ /* numCiocb and numRiocb are used in config_port */
+ pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES;
+ pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES;
+ pring->num_mask = 0;
+ break;
+ case LPFC_ELS_RING: /* ring 2 - ELS / CT */
+ /* numCiocb and numRiocb are used in config_port */
+ pring->numCiocb = SLI2_IOCB_CMD_R2_ENTRIES;
+ pring->numRiocb = SLI2_IOCB_RSP_R2_ENTRIES;
+ pring->fast_iotag = 0;
+ pring->iotag_ctr = 0;
+ pring->iotag_max = 4096;
+ pring->num_mask = 4;
+ pring->prt[0].profile = 0; /* Mask 0 */
+ pring->prt[0].rctl = FC_ELS_REQ;
+ pring->prt[0].type = FC_ELS_DATA;
+ pring->prt[0].lpfc_sli_rcv_unsol_event =
+ lpfc_els_unsol_event;
+ pring->prt[1].profile = 0; /* Mask 1 */
+ pring->prt[1].rctl = FC_ELS_RSP;
+ pring->prt[1].type = FC_ELS_DATA;
+ pring->prt[1].lpfc_sli_rcv_unsol_event =
+ lpfc_els_unsol_event;
+ pring->prt[2].profile = 0; /* Mask 2 */
+ /* NameServer Inquiry */
+ pring->prt[2].rctl = FC_UNSOL_CTL;
+ /* NameServer */
+ pring->prt[2].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[2].lpfc_sli_rcv_unsol_event =
+ lpfc_ct_unsol_event;
+ pring->prt[3].profile = 0; /* Mask 3 */
+ /* NameServer response */
+ pring->prt[3].rctl = FC_SOL_CTL;
+ /* NameServer */
+ pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[3].lpfc_sli_rcv_unsol_event =
+ lpfc_ct_unsol_event;
+ break;
+ }
+ totiocb += (pring->numCiocb + pring->numRiocb);
+ }
+ if (totiocb > MAX_SLI2_IOCB) {
+ /* Too many cmd / rsp ring entries in SLI2 SLIM */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "%d:0462 Too many cmd / rsp ring entries in "
+ "SLI2 SLIM Data: x%x x%x\n",
+ phba->brd_no, totiocb, MAX_SLI2_IOCB);
+ }
+
+ return 0;
+}
+
+int
+lpfc_sli_queue_setup(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+ int i, cnt;
+
+ psli = &phba->sli;
+ spin_lock_irq(phba->host->host_lock);
+ INIT_LIST_HEAD(&psli->mboxq);
+ /* Initialize list headers for txq and txcmplq as double linked lists */
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->ring[i];
+ pring->ringno = i;
+ pring->next_cmdidx = 0;
+ pring->local_getidx = 0;
+ pring->cmdidx = 0;
+ INIT_LIST_HEAD(&pring->txq);
+ INIT_LIST_HEAD(&pring->txcmplq);
+ INIT_LIST_HEAD(&pring->iocb_continueq);
+ INIT_LIST_HEAD(&pring->postbufq);
+ cnt = pring->fast_iotag;
+ spin_unlock_irq(phba->host->host_lock);
+ if (cnt) {
+ pring->fast_lookup =
+ kmalloc(cnt * sizeof (struct lpfc_iocbq *),
+ GFP_KERNEL);
+ if (pring->fast_lookup == 0) {
+ return (0);
+ }
+ memset((char *)pring->fast_lookup, 0,
+ cnt * sizeof (struct lpfc_iocbq *));
+ }
+ spin_lock_irq(phba->host->host_lock);
+ }
+ spin_unlock_irq(phba->host->host_lock);
+ return (1);
+}
+
+int
+lpfc_sli_hba_down(struct lpfc_hba * phba)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+ LPFC_MBOXQ_t *pmb;
+ struct lpfc_iocbq *iocb, *next_iocb;
+ IOCB_t *icmd = NULL;
+ int i;
+ unsigned long flags = 0;
+
+ psli = &phba->sli;
+ lpfc_hba_down_prep(phba);
+
+ spin_lock_irqsave(phba->host->host_lock, flags);
+
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->ring[i];
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+
+ /*
+ * Error everything on the txq since these iocbs have not been
+ * given to the FW yet.
+ */
+ pring->txq_cnt = 0;
+
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ list_del_init(&iocb->list);
+ if (iocb->iocb_cmpl) {
+ icmd = &iocb->iocb;
+ icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+ icmd->un.ulpWord[4] = IOERR_SLI_DOWN;
+ spin_unlock_irqrestore(phba->host->host_lock,
+ flags);
+ (iocb->iocb_cmpl) (phba, iocb, iocb);
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ } else {
+ list_add_tail(&iocb->list,
+ &phba->lpfc_iocb_list);
+ }
+ }
+
+ INIT_LIST_HEAD(&(pring->txq));
+
+ if (pring->fast_lookup) {
+ kfree(pring->fast_lookup);
+ pring->fast_lookup = NULL;
+ }
+
+ }
+
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+
+ /* Return any active mbox cmds */
+ del_timer_sync(&psli->mbox_tmo);
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ phba->work_hba_events &= ~WORKER_MBOX_TMO;
+ if (psli->mbox_active) {
+ pmb = psli->mbox_active;
+ pmb->mb.mbxStatus = MBX_NOT_FINISHED;
+ if (pmb->mbox_cmpl) {
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+ pmb->mbox_cmpl(phba,pmb);
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ }
+ }
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ psli->mbox_active = NULL;
+
+ /* Return any pending mbox cmds */
+ while ((pmb = lpfc_mbox_get(phba)) != NULL) {
+ pmb->mb.mbxStatus = MBX_NOT_FINISHED;
+ if (pmb->mbox_cmpl) {
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+ pmb->mbox_cmpl(phba,pmb);
+ spin_lock_irqsave(phba->host->host_lock, flags);
+ }
+ }
+
+ INIT_LIST_HEAD(&psli->mboxq);
+
+ spin_unlock_irqrestore(phba->host->host_lock, flags);
+
+ /*
+ * Provided the hba is not in an error state, reset it. It is not
+ * capable of IO anymore.
+ */
+ if (phba->hba_state != LPFC_HBA_ERROR) {
+ phba->hba_state = LPFC_INIT_START;
+ lpfc_sli_brdreset(phba, 1);
+ }
+
+ return 1;
+}
+
+void
+lpfc_sli_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt)
+{
+ uint32_t *src = srcp;
+ uint32_t *dest = destp;
+ uint32_t ldata;
+ int i;
+
+ for (i = 0; i < (int)cnt; i += sizeof (uint32_t)) {
+ ldata = *src;
+ ldata = le32_to_cpu(ldata);
+ *dest = ldata;
+ src++;
+ dest++;
+ }
+}
+
+int
+lpfc_sli_ringpostbuf_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+ struct lpfc_dmabuf * mp)
+{
+ /* Stick struct lpfc_dmabuf at end of postbufq so driver can look it up
+ later */
+ list_add_tail(&mp->list, &pring->postbufq);
+
+ pring->postbufq_cnt++;
+ return 0;
+}
+
+
+struct lpfc_dmabuf *
+lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ dma_addr_t phys)
+{
+ struct lpfc_dmabuf *mp, *next_mp;
+ struct list_head *slp = &pring->postbufq;
+
+ /* Search postbufq, from the begining, looking for a match on phys */
+ list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+ if (mp->phys == phys) {
+ list_del_init(&mp->list);
+ pring->postbufq_cnt--;
+ return mp;
+ }
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "%d:0410 Cannot find virtual addr for mapped buf on "
+ "ring %d Data x%llx x%p x%p x%x\n",
+ phba->brd_no, pring->ringno, (unsigned long long)phys,
+ slp->next, slp->prev, pring->postbufq_cnt);
+ return NULL;
+}
+
+static void
+lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+ struct lpfc_iocbq * rspiocb)
+{
+ struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
+ /* Free the resources associated with the ELS_REQUEST64 IOCB the driver
+ * just aborted.
+ * In this case, context2 = cmd, context2->next = rsp, context3 = bpl
+ */
+ if (cmdiocb->context2) {
+ buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+ /* Free the response IOCB before completing the abort
+ command. */
+ buf_ptr = NULL;
+ list_remove_head((&buf_ptr1->list), buf_ptr,
+ struct lpfc_dmabuf, list);
+ if (buf_ptr) {
+ lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+ kfree(buf_ptr);
+ }
+ lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
+ kfree(buf_ptr1);
+ }
+
+ if (cmdiocb->context3) {
+ buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3;
+ lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+ kfree(buf_ptr);
+ }
+
+ list_add_tail(&cmdiocb->list, &phba->lpfc_iocb_list);
+ return;
+}
+
+int
+lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * cmdiocb)
+{
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ struct lpfc_iocbq *abtsiocbp = NULL;
+ IOCB_t *icmd = NULL;
+ IOCB_t *iabt = NULL;
+
+ /* issue ABTS for this IOCB based on iotag */
+ list_remove_head(lpfc_iocb_list, abtsiocbp, struct lpfc_iocbq, list);
+ if (abtsiocbp == NULL)
+ return 0;
+ memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+
+ iabt = &abtsiocbp->iocb;
+ icmd = &cmdiocb->iocb;
+ switch (icmd->ulpCommand) {
+ case CMD_ELS_REQUEST64_CR:
+ /* Even though we abort the ELS command, the firmware may access
+ * the BPL or other resources before it processes our
+ * ABORT_MXRI64. Thus we must delay reusing the cmdiocb
+ * resources till the actual abort request completes.
+ */
+ abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand);
+ abtsiocbp->context2 = cmdiocb->context2;
+ abtsiocbp->context3 = cmdiocb->context3;
+ cmdiocb->context2 = NULL;
+ cmdiocb->context3 = NULL;
+ abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl;
+ break;
+ default:
+ list_add_tail(&abtsiocbp->list, lpfc_iocb_list);
+ return 0;
+ }
+
+ iabt->un.amxri.abortType = ABORT_TYPE_ABTS;
+ iabt->un.amxri.iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32;
+
+ iabt->ulpLe = 1;
+ iabt->ulpClass = CLASS3;
+ iabt->ulpCommand = CMD_ABORT_MXRI64_CN;
+
+ if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) {
+ list_add_tail(&abtsiocbp->list, lpfc_iocb_list);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+lpfc_sli_validate_iocb_cmd(struct lpfc_scsi_buf *lpfc_cmd, uint16_t tgt_id,
+ uint64_t lun_id, struct lpfc_iocbq *iocb,
+ uint32_t ctx, lpfc_ctx_cmd ctx_cmd)
+{
+ int rc = 1;
+
+ if (lpfc_cmd == NULL)
+ return rc;
+
+ switch (ctx_cmd) {
+ case LPFC_CTX_LUN:
+ if ((lpfc_cmd->pCmd->device->id == tgt_id) &&
+ (lpfc_cmd->pCmd->device->lun == lun_id))
+ rc = 0;
+ break;
+ case LPFC_CTX_TGT:
+ if (lpfc_cmd->pCmd->device->id == tgt_id)
+ rc = 0;
+ break;
+ case LPFC_CTX_CTX:
+ if (iocb->iocb.ulpContext == ctx)
+ rc = 0;
+ case LPFC_CTX_HOST:
+ rc = 0;
+ break;
+ default:
+ printk(KERN_ERR "%s: Unknown context cmd type, value %d\n",
+ __FUNCTION__, ctx_cmd);
+ break;
+ }
+
+ return rc;
+}
+
+int
+lpfc_sli_sum_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd ctx_cmd)
+{
+ struct lpfc_iocbq *iocb, *next_iocb;
+ IOCB_t *cmd = NULL;
+ struct lpfc_scsi_buf *lpfc_cmd;
+ int sum = 0, ret_val = 0;
+
+ /* Next check the txcmplq */
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ cmd = &iocb->iocb;
+
+ /* Must be a FCP command */
+ if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+ (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+ (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+ continue;
+ }
+
+ /* context1 MUST be a struct lpfc_scsi_buf */
+ lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+ ret_val = lpfc_sli_validate_iocb_cmd(lpfc_cmd, tgt_id, lun_id,
+ NULL, 0, ctx_cmd);
+ if (ret_val != 0)
+ continue;
+ sum++;
+ }
+ return sum;
+}
+
+int
+lpfc_sli_abort_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ uint16_t tgt_id, uint64_t lun_id, uint32_t ctx,
+ lpfc_ctx_cmd abort_cmd)
+{
+ struct lpfc_iocbq *iocb, *next_iocb;
+ struct lpfc_iocbq *abtsiocb = NULL;
+ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+ IOCB_t *cmd = NULL;
+ struct lpfc_scsi_buf *lpfc_cmd;
+ int errcnt = 0, ret_val = 0;
+
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ cmd = &iocb->iocb;
+
+ /* Must be a FCP command */
+ if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+ (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+ (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+ continue;
+ }
+
+ /* context1 MUST be a struct lpfc_scsi_buf */
+ lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+ ret_val = lpfc_sli_validate_iocb_cmd(lpfc_cmd, tgt_id, lun_id,
+ iocb, ctx, abort_cmd);
+ if (ret_val != 0)
+ continue;
+
+ /* issue ABTS for this IOCB based on iotag */
+ list_remove_head(lpfc_iocb_list, abtsiocb, struct lpfc_iocbq,
+ list);
+ if (abtsiocb == NULL) {
+ errcnt++;
+ continue;
+ }
+ memset(abtsiocb, 0, sizeof (struct lpfc_iocbq));
+
+ abtsiocb->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
+ abtsiocb->iocb.un.acxri.abortContextTag = cmd->ulpContext;
+ abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
+ abtsiocb->iocb.ulpLe = 1;
+ abtsiocb->iocb.ulpClass = cmd->ulpClass;
+
+ if (phba->hba_state >= LPFC_LINK_UP)
+ abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
+ else
+ abtsiocb->iocb.ulpCommand = CMD_CLOSE_XRI_CN;
+
+ ret_val = lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0);
+ if (ret_val == IOCB_ERROR) {
+ list_add_tail(&abtsiocb->list, lpfc_iocb_list);
+ errcnt++;
+ continue;
+ }
+ }
+
+ return errcnt;
+}
+
+void
+lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba,
+ struct lpfc_iocbq * queue1,
+ struct lpfc_iocbq * queue2)
+{
+ if (queue1->context2 && queue2)
+ memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq));
+
+ /* The waiter is looking for LPFC_IO_HIPRI bit to be set
+ as a signal to wake up */
+ queue1->iocb_flag |= LPFC_IO_HIPRI;
+ return;
+}
+
+int
+lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba,
+ struct lpfc_sli_ring * pring,
+ struct lpfc_iocbq * piocb,
+ uint32_t flag,
+ struct lpfc_iocbq * prspiocbq,
+ uint32_t timeout)
+{
+ int j, delay_time, retval = IOCB_ERROR;
+
+ /* The caller must left context1 empty. */
+ if (piocb->context_un.hipri_wait_queue != 0) {
+ return IOCB_ERROR;
+ }
+
+ /*
+ * If the caller has provided a response iocbq buffer, context2 must
+ * be NULL or its an error.
+ */
+ if (prspiocbq && piocb->context2) {
+ return IOCB_ERROR;
+ }
+
+ piocb->context2 = prspiocbq;
+
+ /* Setup callback routine and issue the command. */
+ piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority;
+ retval = lpfc_sli_issue_iocb(phba, pring, piocb,
+ flag | SLI_IOCB_HIGH_PRIORITY);
+ if (retval != IOCB_SUCCESS) {
+ piocb->context2 = NULL;
+ return IOCB_ERROR;
+ }
+
+ /*
+ * This high-priority iocb was sent out-of-band. Poll for its
+ * completion rather than wait for a signal. Note that the host_lock
+ * is held by the midlayer and must be released here to allow the
+ * interrupt handlers to complete the IO and signal this routine via
+ * the iocb_flag.
+ * Also, the delay_time is computed to be one second longer than
+ * the scsi command timeout to give the FW time to abort on
+ * timeout rather than the driver just giving up. Typically,
+ * the midlayer does not specify a time for this command so the
+ * driver is free to enforce its own timeout.
+ */
+
+ delay_time = ((timeout + 1) * 1000) >> 6;
+ retval = IOCB_ERROR;
+ spin_unlock_irq(phba->host->host_lock);
+ for (j = 0; j < 64; j++) {
+ msleep(delay_time);
+ if (piocb->iocb_flag & LPFC_IO_HIPRI) {
+ piocb->iocb_flag &= ~LPFC_IO_HIPRI;
+ retval = IOCB_SUCCESS;
+ break;
+ }
+ }
+
+ spin_lock_irq(phba->host->host_lock);
+ piocb->context2 = NULL;
+ return retval;
+}
+int
+lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq,
+ uint32_t timeout)
+{
+ DECLARE_WAIT_QUEUE_HEAD(done_q);
+ DECLARE_WAITQUEUE(wq_entry, current);
+ uint32_t timeleft = 0;
+ int retval;
+
+ /* The caller must leave context1 empty. */
+ if (pmboxq->context1 != 0) {
+ return (MBX_NOT_FINISHED);
+ }
+
+ /* setup wake call as IOCB callback */
+ pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
+ /* setup context field to pass wait_queue pointer to wake function */
+ pmboxq->context1 = &done_q;
+
+ /* start to sleep before we wait, to avoid races */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&done_q, &wq_entry);
+
+ /* now issue the command */
+ retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
+
+ if (retval == MBX_BUSY || retval == MBX_SUCCESS) {
+ timeleft = schedule_timeout(timeout * HZ);
+ pmboxq->context1 = NULL;
+ /* if schedule_timeout returns 0, we timed out and were not
+ woken up */
+ if (timeleft == 0) {
+ retval = MBX_TIMEOUT;
+ } else {
+ retval = MBX_SUCCESS;
+ }
+ }
+
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&done_q, &wq_entry);
+ return retval;
+}
+
+irqreturn_t
+lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct lpfc_hba *phba;
+ uint32_t ha_copy;
+ uint32_t work_ha_copy;
+ unsigned long status;
+ int i;
+ uint32_t control;
+
+ /*
+ * Get the driver's phba structure from the dev_id and
+ * assume the HBA is not interrupting.
+ */
+ phba = (struct lpfc_hba *) dev_id;
+
+ if (unlikely(!phba))
+ return IRQ_NONE;
+
+ phba->sli.slistat.sli_intr++;
+
+ /*
+ * Call the HBA to see if it is interrupting. If not, don't claim
+ * the interrupt
+ */
+
+ /* Ignore all interrupts during initialization. */
+ if (unlikely(phba->hba_state < LPFC_LINK_DOWN))
+ return IRQ_NONE;
+
+ /*
+ * Read host attention register to determine interrupt source
+ * Clear Attention Sources, except Error Attention (to
+ * preserve status) and Link Attention
+ */
+ spin_lock(phba->host->host_lock);
+ ha_copy = readl(phba->HAregaddr);
+ writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+ spin_unlock(phba->host->host_lock);
+
+ if (unlikely(!ha_copy))
+ return IRQ_NONE;
+
+ work_ha_copy = ha_copy & phba->work_ha_mask;
+
+ if (unlikely(work_ha_copy)) {
+ if (work_ha_copy & HA_LATT) {
+ if (phba->sli.sli_flag & LPFC_PROCESS_LA) {
+ /*
+ * Turn off Link Attention interrupts
+ * until CLEAR_LA done
+ */
+ spin_lock(phba->host->host_lock);
+ phba->sli.sli_flag &= ~LPFC_PROCESS_LA;
+ control = readl(phba->HCregaddr);
+ control &= ~HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock(phba->host->host_lock);
+ }
+ else
+ work_ha_copy &= ~HA_LATT;
+ }
+
+ if (work_ha_copy & ~(HA_ERATT|HA_MBATT|HA_LATT)) {
+ for (i = 0; i < phba->sli.num_rings; i++) {
+ if (work_ha_copy & (HA_RXATT << (4*i))) {
+ /*
+ * Turn off Slow Rings interrupts
+ */
+ spin_lock(phba->host->host_lock);
+ control = readl(phba->HCregaddr);
+ control &= ~(HC_R0INT_ENA << i);
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock(phba->host->host_lock);
+ }
+ }
+ }
+
+ if (work_ha_copy & HA_ERATT) {
+ phba->hba_state = LPFC_HBA_ERROR;
+ /*
+ * There was a link/board error. Read the
+ * status register to retrieve the error event
+ * and process it.
+ */
+ phba->sli.slistat.err_attn_event++;
+ /* Save status info */
+ phba->work_hs = readl(phba->HSregaddr);
+ phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
+ phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
+
+ /* Clear Chip error bit */
+ writel(HA_ERATT, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ /*
+ * Reseting the HBA is the only reliable way
+ * to shutdown interrupt when there is a
+ * ERROR.
+ */
+ lpfc_sli_send_reset(phba, phba->hba_state);
+ }
+
+ spin_lock(phba->host->host_lock);
+ phba->work_ha |= work_ha_copy;
+ if (phba->work_wait)
+ wake_up(phba->work_wait);
+ spin_unlock(phba->host->host_lock);
+ }
+
+ ha_copy &= ~(phba->work_ha_mask);
+
+ /*
+ * Process all events on FCP ring. Take the optimized path for
+ * FCP IO. Any other IO is slow path and is handled by
+ * the worker thread.
+ */
+ status = (ha_copy & (HA_RXMASK << (4*LPFC_FCP_RING)));
+ status >>= (4*LPFC_FCP_RING);
+ if (status & HA_RXATT)
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING],
+ status);
+ return IRQ_HANDLED;
+
+} /* lpfc_intr_handler */
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
new file mode 100644
index 000000000000..abd9a8c84e9e
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -0,0 +1,216 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_sli.h 1.42 2005/03/21 02:01:28EST sf_support Exp $
+ */
+
+/* forward declaration for LPFC_IOCB_t's use */
+struct lpfc_hba;
+
+/* Define the context types that SLI handles for abort and sums. */
+typedef enum _lpfc_ctx_cmd {
+ LPFC_CTX_LUN,
+ LPFC_CTX_TGT,
+ LPFC_CTX_CTX,
+ LPFC_CTX_HOST
+} lpfc_ctx_cmd;
+
+/* This structure is used to handle IOCB requests / responses */
+struct lpfc_iocbq {
+ /* lpfc_iocbqs are used in double linked lists */
+ struct list_head list;
+ IOCB_t iocb; /* IOCB cmd */
+ uint8_t retry; /* retry counter for IOCB cmd - if needed */
+ uint8_t iocb_flag;
+#define LPFC_IO_POLL 1 /* Polling mode iocb */
+#define LPFC_IO_LIBDFC 2 /* libdfc iocb */
+#define LPFC_IO_WAIT 4
+#define LPFC_IO_HIPRI 8 /* High Priority Queue signal flag */
+
+ uint8_t abort_count;
+ uint8_t rsvd2;
+ uint32_t drvrTimeout; /* driver timeout in seconds */
+ void *context1; /* caller context information */
+ void *context2; /* caller context information */
+ void *context3; /* caller context information */
+ union {
+ wait_queue_head_t *hipri_wait_queue; /* High Priority Queue wait
+ queue */
+ struct lpfc_iocbq *rsp_iocb;
+ struct lpfcMboxq *mbox;
+ } context_un;
+
+ void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *);
+
+};
+
+#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */
+#define SLI_IOCB_HIGH_PRIORITY 2 /* High priority command */
+
+#define IOCB_SUCCESS 0
+#define IOCB_BUSY 1
+#define IOCB_ERROR 2
+#define IOCB_TIMEDOUT 3
+
+typedef struct lpfcMboxq {
+ /* MBOXQs are used in single linked lists */
+ struct list_head list; /* ptr to next mailbox command */
+ MAILBOX_t mb; /* Mailbox cmd */
+ void *context1; /* caller context information */
+ void *context2; /* caller context information */
+
+ void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
+
+} LPFC_MBOXQ_t;
+
+#define MBX_POLL 1 /* poll mailbox till command done, then
+ return */
+#define MBX_NOWAIT 2 /* issue command then return immediately */
+#define MBX_STOP_IOCB 4 /* Stop iocb processing till mbox cmds
+ complete */
+
+#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per
+ ring */
+#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */
+
+struct lpfc_sli_ring;
+
+struct lpfc_sli_ring_mask {
+ uint8_t profile; /* profile associated with ring */
+ uint8_t rctl; /* rctl / type pair configured for ring */
+ uint8_t type; /* rctl / type pair configured for ring */
+ uint8_t rsvd;
+ /* rcv'd unsol event */
+ void (*lpfc_sli_rcv_unsol_event) (struct lpfc_hba *,
+ struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
+};
+
+
+/* Structure used to hold SLI statistical counters and info */
+struct lpfc_sli_ring_stat {
+ uint64_t iocb_event; /* IOCB event counters */
+ uint64_t iocb_cmd; /* IOCB cmd issued */
+ uint64_t iocb_rsp; /* IOCB rsp received */
+ uint64_t iocb_cmd_delay; /* IOCB cmd ring delay */
+ uint64_t iocb_cmd_full; /* IOCB cmd ring full */
+ uint64_t iocb_cmd_empty; /* IOCB cmd ring is now empty */
+ uint64_t iocb_rsp_full; /* IOCB rsp ring full */
+};
+
+/* Structure used to hold SLI ring information */
+struct lpfc_sli_ring {
+ uint16_t flag; /* ring flags */
+#define LPFC_DEFERRED_RING_EVENT 0x001 /* Deferred processing a ring event */
+#define LPFC_CALL_RING_AVAILABLE 0x002 /* indicates cmd was full */
+#define LPFC_STOP_IOCB_MBX 0x010 /* Stop processing IOCB cmds mbox */
+#define LPFC_STOP_IOCB_EVENT 0x020 /* Stop processing IOCB cmds event */
+#define LPFC_STOP_IOCB_MASK 0x030 /* Stop processing IOCB cmds mask */
+ uint16_t abtsiotag; /* tracks next iotag to use for ABTS */
+
+ uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */
+ uint32_t next_cmdidx; /* next_cmd index */
+ uint8_t rsvd;
+ uint8_t ringno; /* ring number */
+ uint8_t rspidx; /* current index in response ring */
+ uint8_t cmdidx; /* current index in command ring */
+ uint16_t numCiocb; /* number of command iocb's per ring */
+ uint16_t numRiocb; /* number of rsp iocb's per ring */
+
+ uint32_t fast_iotag; /* max fastlookup based iotag */
+ uint32_t iotag_ctr; /* keeps track of the next iotag to use */
+ uint32_t iotag_max; /* max iotag value to use */
+ struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by
+ iotag */
+ struct list_head txq;
+ uint16_t txq_cnt; /* current length of queue */
+ uint16_t txq_max; /* max length */
+ struct list_head txcmplq;
+ uint16_t txcmplq_cnt; /* current length of queue */
+ uint16_t txcmplq_max; /* max length */
+ uint32_t *cmdringaddr; /* virtual address for cmd rings */
+ uint32_t *rspringaddr; /* virtual address for rsp rings */
+ uint32_t missbufcnt; /* keep track of buffers to post */
+ struct list_head postbufq;
+ uint16_t postbufq_cnt; /* current length of queue */
+ uint16_t postbufq_max; /* max length */
+ struct list_head iocb_continueq;
+ uint16_t iocb_continueq_cnt; /* current length of queue */
+ uint16_t iocb_continueq_max; /* max length */
+
+ struct lpfc_sli_ring_mask prt[LPFC_MAX_RING_MASK];
+ uint32_t num_mask; /* number of mask entries in prt array */
+
+ struct lpfc_sli_ring_stat stats; /* SLI statistical info */
+
+ /* cmd ring available */
+ void (*lpfc_sli_cmd_available) (struct lpfc_hba *,
+ struct lpfc_sli_ring *);
+};
+
+/* Structure used to hold SLI statistical counters and info */
+struct lpfc_sli_stat {
+ uint64_t mbox_stat_err; /* Mbox cmds completed status error */
+ uint64_t mbox_cmd; /* Mailbox commands issued */
+ uint64_t sli_intr; /* Count of Host Attention interrupts */
+ uint32_t err_attn_event; /* Error Attn event counters */
+ uint32_t link_event; /* Link event counters */
+ uint32_t mbox_event; /* Mailbox event counters */
+ uint32_t mbox_busy; /* Mailbox cmd busy */
+};
+
+/* Structure used to hold SLI information */
+struct lpfc_sli {
+ uint32_t num_rings;
+ uint32_t sli_flag;
+
+ /* Additional sli_flags */
+#define LPFC_SLI_MBOX_ACTIVE 0x100 /* HBA mailbox is currently active */
+#define LPFC_SLI2_ACTIVE 0x200 /* SLI2 overlay in firmware is active */
+#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */
+
+ struct lpfc_sli_ring ring[LPFC_MAX_RING];
+ int fcp_ring; /* ring used for FCP initiator commands */
+ int next_ring;
+
+ int ip_ring; /* ring used for IP network drv cmds */
+
+ struct lpfc_sli_stat slistat; /* SLI statistical info */
+ struct list_head mboxq;
+ uint16_t mboxq_cnt; /* current length of queue */
+ uint16_t mboxq_max; /* max length */
+ LPFC_MBOXQ_t *mbox_active; /* active mboxq information */
+
+ struct timer_list mbox_tmo; /* Hold clk to timeout active mbox
+ cmd */
+
+ uint32_t *MBhostaddr; /* virtual address for mbox cmds */
+};
+
+/* Given a pointer to the start of the ring, and the slot number of
+ * the desired iocb entry, calc a pointer to that entry.
+ * (assume iocb entry size is 32 bytes, or 8 words)
+ */
+#define IOCB_ENTRY(ring,slot) ((IOCB_t *)(((char *)(ring)) + ((slot) * 32)))
+
+#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox
+ command */
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
new file mode 100644
index 000000000000..dfacd8d82097
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -0,0 +1,32 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Enterprise Fibre Channel Host Bus Adapters. *
+ * Refer to the README file included with this package for *
+ * driver version and adapter support. *
+ * Copyright (C) 2004 Emulex Corporation. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * 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, a copy of which *
+ * can be found in the file COPYING included with this package. *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_version.h 1.49 2005/04/13 15:07:19EDT sf_support Exp $
+ */
+
+#define LPFC_DRIVER_VERSION "8.0.28"
+
+#define LPFC_DRIVER_NAME "lpfc"
+
+#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
+ LPFC_DRIVER_VERSION
+
+#define DFC_API_VERSION "0.0.0"
OpenPOWER on IntegriCloud