aboutsummaryrefslogtreecommitdiff
path: root/drivers/message/fusion/mptbase.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/message/fusion/mptbase.c')
-rw-r--r--drivers/message/fusion/mptbase.c5946
1 files changed, 5946 insertions, 0 deletions
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
new file mode 100644
index 00000000000..942cc1c2a18
--- /dev/null
+++ b/drivers/message/fusion/mptbase.c
@@ -0,0 +1,5946 @@
+/*
+ * linux/drivers/message/fusion/mptbase.c
+ * High performance SCSI + LAN / Fibre Channel device drivers.
+ * This is the Fusion MPT base driver which supports multiple
+ * (SCSI + LAN) specialized protocol drivers.
+ * For use with PCI chip/adapter(s):
+ * LSIFC9xx/LSI409xx Fibre Channel
+ * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
+ *
+ * Credits:
+ * There are lots of people not mentioned below that deserve credit
+ * and thanks but won't get it here - sorry in advance that you
+ * got overlooked.
+ *
+ * This driver would not exist if not for Alan Cox's development
+ * of the linux i2o driver.
+ *
+ * A special thanks to Noah Romer (LSI Logic) for tons of work
+ * and tough debugging on the LAN driver, especially early on;-)
+ * And to Roger Hickerson (LSI Logic) for tirelessly supporting
+ * this driver project.
+ *
+ * A special thanks to Pamela Delaney (LSI Logic) for tons of work
+ * and countless enhancements while adding support for the 1030
+ * chip family. Pam has been instrumental in the development of
+ * of the 2.xx.xx series fusion drivers, and her contributions are
+ * far too numerous to hope to list in one place.
+ *
+ * All manner of help from Stephen Shirron (LSI Logic):
+ * low-level FC analysis, debug + various fixes in FCxx firmware,
+ * initial port to alpha platform, various driver code optimizations,
+ * being a faithful sounding board on all sorts of issues & ideas,
+ * etc.
+ *
+ * A huge debt of gratitude is owed to David S. Miller (DaveM)
+ * for fixing much of the stupid and broken stuff in the early
+ * driver while porting to sparc64 platform. THANK YOU!
+ *
+ * Special thanks goes to the I2O LAN driver people at the
+ * University of Helsinki, who, unbeknownst to them, provided
+ * the inspiration and initial structure for this driver.
+ *
+ * A really huge debt of gratitude is owed to Eddie C. Dost
+ * for gobs of hard work fixing and optimizing LAN code.
+ * THANK YOU!
+ *
+ * Copyright (c) 1999-2004 LSI Logic Corporation
+ * Originally By: Steven J. Ralston
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:mpt_linux_developer@lsil.com)
+ *
+ * $Id: mptbase.c,v 1.126 2002/12/16 15:28:45 pdelaney Exp $
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ 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; version 2 of the License.
+
+ 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.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h> /* needed for in_interrupt() proto */
+#include <asm/io.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#ifdef __sparc__
+#include <asm/irq.h> /* needed for __irq_itoa() proto */
+#endif
+
+#include "mptbase.h"
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#define my_NAME "Fusion MPT base driver"
+#define my_VERSION MPT_LINUX_VERSION_COMMON
+#define MYNAM "mptbase"
+
+MODULE_AUTHOR(MODULEAUTHOR);
+MODULE_DESCRIPTION(my_NAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * cmd line parameters
+ */
+#ifdef MFCNT
+static int mfcounter = 0;
+#define PRINT_MF_COUNT 20000
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Public data...
+ */
+int mpt_lan_index = -1;
+int mpt_stm_index = -1;
+
+struct proc_dir_entry *mpt_proc_root_dir;
+
+#define WHOINIT_UNKNOWN 0xAA
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Private data...
+ */
+ /* Adapter link list */
+LIST_HEAD(ioc_list);
+ /* Callback lookup table */
+static MPT_CALLBACK MptCallbacks[MPT_MAX_PROTOCOL_DRIVERS];
+ /* Protocol driver class lookup table */
+static int MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS];
+ /* Event handler lookup table */
+static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];
+ /* Reset handler lookup table */
+static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS];
+static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS];
+
+static int mpt_base_index = -1;
+static int last_drv_idx = -1;
+
+static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Forward protos...
+ */
+static irqreturn_t mpt_interrupt(int irq, void *bus_id, struct pt_regs *r);
+static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
+static int mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes,
+ u32 *req, int replyBytes, u16 *u16reply, int maxwait,
+ int sleepFlag);
+static int mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag);
+static void mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev);
+static void mpt_adapter_disable(MPT_ADAPTER *ioc);
+static void mpt_adapter_dispose(MPT_ADAPTER *ioc);
+
+static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc);
+static int MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag);
+//static u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked);
+static int GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason);
+static int GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag);
+static int SendIocInit(MPT_ADAPTER *ioc, int sleepFlag);
+static int SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag);
+static int mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag);
+static int mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag);
+static int mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag);
+static int KickStart(MPT_ADAPTER *ioc, int ignore, int sleepFlag);
+static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag);
+static int PrimeIocFifos(MPT_ADAPTER *ioc);
+static int WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
+static int WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
+static int WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
+static int GetLanConfigPages(MPT_ADAPTER *ioc);
+static int GetFcPortPage0(MPT_ADAPTER *ioc, int portnum);
+static int GetIoUnitPage2(MPT_ADAPTER *ioc);
+static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);
+static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);
+static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc);
+static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc);
+static void mpt_timer_expired(unsigned long data);
+static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch);
+static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);
+
+#ifdef CONFIG_PROC_FS
+static int procmpt_summary_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
+static int procmpt_version_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
+static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
+#endif
+static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
+
+//int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
+static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers);
+static void mpt_sp_ioc_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf);
+static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);
+static void mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info);
+
+/* module entry point */
+static int __devinit mptbase_probe (struct pci_dev *, const struct pci_device_id *);
+static void __devexit mptbase_remove(struct pci_dev *);
+static void mptbase_shutdown(struct device * );
+static int __init fusion_init (void);
+static void __exit fusion_exit (void);
+
+/****************************************************************************
+ * Supported hardware
+ */
+
+static struct pci_device_id mptbase_pci_table[] = {
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1030,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_1030_53C1035,
+ PCI_ANY_ID, PCI_ANY_ID },
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, mptbase_pci_table);
+
+#define CHIPREG_READ32(addr) readl_relaxed(addr)
+#define CHIPREG_READ32_dmasync(addr) readl(addr)
+#define CHIPREG_WRITE32(addr,val) writel(val, addr)
+#define CHIPREG_PIO_WRITE32(addr,val) outl(val, (unsigned long)addr)
+#define CHIPREG_PIO_READ32(addr) inl((unsigned long)addr)
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mpt_interrupt - MPT adapter (IOC) specific interrupt handler.
+ * @irq: irq number (not used)
+ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
+ * @r: pt_regs pointer (not used)
+ *
+ * This routine is registered via the request_irq() kernel API call,
+ * and handles all interrupts generated from a specific MPT adapter
+ * (also referred to as a IO Controller or IOC).
+ * This routine must clear the interrupt from the adapter and does
+ * so by reading the reply FIFO. Multiple replies may be processed
+ * per single call to this routine; up to MPT_MAX_REPLIES_PER_ISR
+ * which is currently set to 32 in mptbase.h.
+ *
+ * This routine handles register-level access of the adapter but
+ * dispatches (calls) a protocol-specific callback routine to handle
+ * the protocol-specific details of the MPT request completion.
+ */
+static irqreturn_t
+mpt_interrupt(int irq, void *bus_id, struct pt_regs *r)
+{
+ MPT_ADAPTER *ioc;
+ MPT_FRAME_HDR *mf;
+ MPT_FRAME_HDR *mr;
+ u32 pa;
+ int req_idx;
+ int cb_idx;
+ int type;
+ int freeme;
+
+ ioc = (MPT_ADAPTER *)bus_id;
+
+ /*
+ * Drain the reply FIFO!
+ *
+ * NOTES: I've seen up to 10 replies processed in this loop, so far...
+ * Update: I've seen up to 9182 replies processed in this loop! ??
+ * Update: Limit ourselves to processing max of N replies
+ * (bottom of loop).
+ */
+ while (1) {
+
+ if ((pa = CHIPREG_READ32_dmasync(&ioc->chip->ReplyFifo)) == 0xFFFFFFFF)
+ return IRQ_HANDLED;
+
+ cb_idx = 0;
+ freeme = 0;
+
+ /*
+ * Check for non-TURBO reply!
+ */
+ if (pa & MPI_ADDRESS_REPLY_A_BIT) {
+ u32 reply_dma_low;
+ u16 ioc_stat;
+
+ /* non-TURBO reply! Hmmm, something may be up...
+ * Newest turbo reply mechanism; get address
+ * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)!
+ */
+
+ /* Map DMA address of reply header to cpu address.
+ * pa is 32 bits - but the dma address may be 32 or 64 bits
+ * get offset based only only the low addresses
+ */
+ reply_dma_low = (pa = (pa << 1));
+ mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames +
+ (reply_dma_low - ioc->reply_frames_low_dma));
+
+ req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx);
+ cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx;
+ mf = MPT_INDEX_2_MFPTR(ioc, req_idx);
+
+ dmfprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p req_idx=%x\n",
+ ioc->name, mr, req_idx));
+ DBG_DUMP_REPLY_FRAME(mr)
+
+ /* NEW! 20010301 -sralston
+ * Check/log IOC log info
+ */
+ ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus);
+ if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
+ u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo);
+ if (ioc->bus_type == FC)
+ mpt_fc_log_info(ioc, log_info);
+ else if (ioc->bus_type == SCSI)
+ mpt_sp_log_info(ioc, log_info);
+ }
+ if (ioc_stat & MPI_IOCSTATUS_MASK) {
+ if (ioc->bus_type == SCSI)
+ mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf);
+ }
+ } else {
+ /*
+ * Process turbo (context) reply...
+ */
+ dmfprintk((MYIOC_s_INFO_FMT "Got TURBO reply req_idx=%08x\n", ioc->name, pa));
+ type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT);
+ if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) {
+ cb_idx = mpt_stm_index;
+ mf = NULL;
+ mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa);
+ } else if (type == MPI_CONTEXT_REPLY_TYPE_LAN) {
+ cb_idx = mpt_lan_index;
+ /*
+ * BUG FIX! 20001218 -sralston
+ * Blind set of mf to NULL here was fatal
+ * after lan_reply says "freeme"
+ * Fix sort of combined with an optimization here;
+ * added explicit check for case where lan_reply
+ * was just returning 1 and doing nothing else.
+ * For this case skip the callback, but set up
+ * proper mf value first here:-)
+ */
+ if ((pa & 0x58000000) == 0x58000000) {
+ req_idx = pa & 0x0000FFFF;
+ mf = MPT_INDEX_2_MFPTR(ioc, req_idx);
+ freeme = 1;
+ /*
+ * IMPORTANT! Invalidate the callback!
+ */
+ cb_idx = 0;
+ } else {
+ mf = NULL;
+ }
+ mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa);
+ } else {
+ req_idx = pa & 0x0000FFFF;
+ cb_idx = (pa & 0x00FF0000) >> 16;
+ mf = MPT_INDEX_2_MFPTR(ioc, req_idx);
+ mr = NULL;
+ }
+ pa = 0; /* No reply flush! */
+ }
+
+#ifdef MPT_DEBUG_IRQ
+ if (ioc->bus_type == SCSI) {
+ /* Verify mf, mr are reasonable.
+ */
+ if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))
+ || (mf < ioc->req_frames)) ) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth))
+ || (mr < ioc->reply_frames)) ) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ }
+#endif
+
+ /* Check for (valid) IO callback! */
+ if (cb_idx) {
+ /* Do the callback! */
+ freeme = (*(MptCallbacks[cb_idx]))(ioc, mf, mr);
+ }
+
+ if (pa) {
+ /* Flush (non-TURBO) reply with a WRITE! */
+ CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa);
+ }
+
+ if (freeme) {
+ unsigned long flags;
+
+ /* Put Request back on FreeQ! */
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ);
+#ifdef MFCNT
+ ioc->mfcnt--;
+#endif
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ }
+
+ mb();
+ } /* drain reply FIFO */
+
+ return IRQ_HANDLED;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mpt_base_reply - MPT base driver's callback routine; all base driver
+ * "internal" request/reply processing is routed here.
+ * Currently used for EventNotification and EventAck handling.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @mf: Pointer to original MPT request frame
+ * @reply: Pointer to MPT reply frame (NULL if TurboReply)
+ *
+ * Returns 1 indicating original alloc'd request frame ptr
+ * should be freed, or 0 if it shouldn't.
+ */
+static int
+mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
+{
+ int freereq = 1;
+ u8 func;
+
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply() called\n", ioc->name));
+
+ if ((mf == NULL) ||
+ (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
+ printk(MYIOC_s_ERR_FMT "NULL or BAD request frame ptr! (=%p)\n",
+ ioc->name, (void *)mf);
+ return 1;
+ }
+
+ if (reply == NULL) {
+ dprintk((MYIOC_s_ERR_FMT "Unexpected NULL Event (turbo?) reply!\n",
+ ioc->name));
+ return 1;
+ }
+
+ if (!(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) {
+ dmfprintk((KERN_INFO MYNAM ": Original request frame (@%p) header\n", mf));
+ DBG_DUMP_REQUEST_FRAME_HDR(mf)
+ }
+
+ func = reply->u.hdr.Function;
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, Function=%02Xh\n",
+ ioc->name, func));
+
+ if (func == MPI_FUNCTION_EVENT_NOTIFICATION) {
+ EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply;
+ int evHandlers = 0;
+ int results;
+
+ results = ProcessEventNotification(ioc, pEvReply, &evHandlers);
+ if (results != evHandlers) {
+ /* CHECKME! Any special handling needed here? */
+ devtprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n",
+ ioc->name, evHandlers, results));
+ }
+
+ /*
+ * Hmmm... It seems that EventNotificationReply is an exception
+ * to the rule of one reply per request.
+ */
+ if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)
+ freereq = 0;
+
+#ifdef CONFIG_PROC_FS
+// LogEvent(ioc, pEvReply);
+#endif
+
+ } else if (func == MPI_FUNCTION_EVENT_ACK) {
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, EventAck reply received\n",
+ ioc->name));
+ } else if (func == MPI_FUNCTION_CONFIG ||
+ func == MPI_FUNCTION_TOOLBOX) {
+ CONFIGPARMS *pCfg;
+ unsigned long flags;
+
+ dcprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
+ ioc->name, mf, reply));
+
+ pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
+
+ if (pCfg) {
+ /* disable timer and remove from linked list */
+ del_timer(&pCfg->timer);
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ list_del(&pCfg->linkage);
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ /*
+ * If IOC Status is SUCCESS, save the header
+ * and set the status code to GOOD.
+ */
+ pCfg->status = MPT_CONFIG_ERROR;
+ if (reply) {
+ ConfigReply_t *pReply = (ConfigReply_t *)reply;
+ u16 status;
+
+ status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ dcprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+ status, le32_to_cpu(pReply->IOCLogInfo)));
+
+ pCfg->status = status;
+ if (status == MPI_IOCSTATUS_SUCCESS) {
+ pCfg->hdr->PageVersion = pReply->Header.PageVersion;
+ pCfg->hdr->PageLength = pReply->Header.PageLength;
+ pCfg->hdr->PageNumber = pReply->Header.PageNumber;
+ pCfg->hdr->PageType = pReply->Header.PageType;
+ }
+ }
+
+ /*
+ * Wake up the original calling thread
+ */
+ pCfg->wait_done = 1;
+ wake_up(&mpt_waitq);
+ }
+ } else {
+ printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n",
+ ioc->name, func);
+ }
+
+ /*
+ * Conditionally tell caller to free the original
+ * EventNotification/EventAck/unexpected request frame!
+ */
+ return freereq;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_register - Register protocol-specific main callback handler.
+ * @cbfunc: callback function pointer
+ * @dclass: Protocol driver's class (%MPT_DRIVER_CLASS enum value)
+ *
+ * This routine is called by a protocol-specific driver (SCSI host,
+ * LAN, SCSI target) to register it's reply callback routine. Each
+ * protocol-specific driver must do this before it will be able to
+ * use any IOC resources, such as obtaining request frames.
+ *
+ * NOTES: The SCSI protocol driver currently calls this routine thrice
+ * in order to register separate callbacks; one for "normal" SCSI IO;
+ * one for MptScsiTaskMgmt requests; one for Scan/DV requests.
+ *
+ * Returns a positive integer valued "handle" in the
+ * range (and S.O.D. order) {N,...,7,6,5,...,1} if successful.
+ * Any non-positive return value (including zero!) should be considered
+ * an error by the caller.
+ */
+int
+mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass)
+{
+ int i;
+
+ last_drv_idx = -1;
+
+ /*
+ * Search for empty callback slot in this order: {N,...,7,6,5,...,1}
+ * (slot/handle 0 is reserved!)
+ */
+ for (i = MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) {
+ if (MptCallbacks[i] == NULL) {
+ MptCallbacks[i] = cbfunc;
+ MptDriverClass[i] = dclass;
+ MptEvHandlers[i] = NULL;
+ last_drv_idx = i;
+ break;
+ }
+ }
+
+ return last_drv_idx;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_deregister - Deregister a protocol drivers resources.
+ * @cb_idx: previously registered callback handle
+ *
+ * Each protocol-specific driver should call this routine when it's
+ * module is unloaded.
+ */
+void
+mpt_deregister(int cb_idx)
+{
+ if ((cb_idx >= 0) && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) {
+ MptCallbacks[cb_idx] = NULL;
+ MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER;
+ MptEvHandlers[cb_idx] = NULL;
+
+ last_drv_idx++;
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_event_register - Register protocol-specific event callback
+ * handler.
+ * @cb_idx: previously registered (via mpt_register) callback handle
+ * @ev_cbfunc: callback function
+ *
+ * This routine can be called by one or more protocol-specific drivers
+ * if/when they choose to be notified of MPT events.
+ *
+ * Returns 0 for success.
+ */
+int
+mpt_event_register(int cb_idx, MPT_EVHANDLER ev_cbfunc)
+{
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS)
+ return -1;
+
+ MptEvHandlers[cb_idx] = ev_cbfunc;
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_event_deregister - Deregister protocol-specific event callback
+ * handler.
+ * @cb_idx: previously registered callback handle
+ *
+ * Each protocol-specific driver should call this routine
+ * when it does not (or can no longer) handle events,
+ * or when it's module is unloaded.
+ */
+void
+mpt_event_deregister(int cb_idx)
+{
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS)
+ return;
+
+ MptEvHandlers[cb_idx] = NULL;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_reset_register - Register protocol-specific IOC reset handler.
+ * @cb_idx: previously registered (via mpt_register) callback handle
+ * @reset_func: reset function
+ *
+ * This routine can be called by one or more protocol-specific drivers
+ * if/when they choose to be notified of IOC resets.
+ *
+ * Returns 0 for success.
+ */
+int
+mpt_reset_register(int cb_idx, MPT_RESETHANDLER reset_func)
+{
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS)
+ return -1;
+
+ MptResetHandlers[cb_idx] = reset_func;
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_reset_deregister - Deregister protocol-specific IOC reset handler.
+ * @cb_idx: previously registered callback handle
+ *
+ * Each protocol-specific driver should call this routine
+ * when it does not (or can no longer) handle IOC reset handling,
+ * or when it's module is unloaded.
+ */
+void
+mpt_reset_deregister(int cb_idx)
+{
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS)
+ return;
+
+ MptResetHandlers[cb_idx] = NULL;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_device_driver_register - Register device driver hooks
+ */
+int
+mpt_device_driver_register(struct mpt_pci_driver * dd_cbfunc, int cb_idx)
+{
+ MPT_ADAPTER *ioc;
+ int error=0;
+
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) {
+ error= -EINVAL;
+ return error;
+ }
+
+ MptDeviceDriverHandlers[cb_idx] = dd_cbfunc;
+
+ /* call per pci device probe entry point */
+ list_for_each_entry(ioc, &ioc_list, list) {
+ if(dd_cbfunc->probe) {
+ error = dd_cbfunc->probe(ioc->pcidev,
+ ioc->pcidev->driver->id_table);
+ if(error != 0)
+ return error;
+ }
+ }
+
+ return error;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_device_driver_deregister - DeRegister device driver hooks
+ */
+void
+mpt_device_driver_deregister(int cb_idx)
+{
+ struct mpt_pci_driver *dd_cbfunc;
+ MPT_ADAPTER *ioc;
+
+ if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS)
+ return;
+
+ dd_cbfunc = MptDeviceDriverHandlers[cb_idx];
+
+ list_for_each_entry(ioc, &ioc_list, list) {
+ if (dd_cbfunc->remove)
+ dd_cbfunc->remove(ioc->pcidev);
+ }
+
+ MptDeviceDriverHandlers[cb_idx] = NULL;
+}
+
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024)
+ * allocated per MPT adapter.
+ * @handle: Handle of registered MPT protocol driver
+ * @ioc: Pointer to MPT adapter structure
+ *
+ * Returns pointer to a MPT request frame or %NULL if none are available
+ * or IOC is not active.
+ */
+MPT_FRAME_HDR*
+mpt_get_msg_frame(int handle, MPT_ADAPTER *ioc)
+{
+ MPT_FRAME_HDR *mf;
+ unsigned long flags;
+ u16 req_idx; /* Request index */
+
+ /* validate handle and ioc identifier */
+
+#ifdef MFCNT
+ if (!ioc->active)
+ printk(KERN_WARNING "IOC Not Active! mpt_get_msg_frame returning NULL!\n");
+#endif
+
+ /* If interrupts are not attached, do not return a request frame */
+ if (!ioc->active)
+ return NULL;
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (!list_empty(&ioc->FreeQ)) {
+ int req_offset;
+
+ mf = list_entry(ioc->FreeQ.next, MPT_FRAME_HDR,
+ u.frame.linkage.list);
+ list_del(&mf->u.frame.linkage.list);
+ mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */
+ req_offset = (u8 *)mf - (u8 *)ioc->req_frames;
+ /* u16! */
+ req_idx = cpu_to_le16(req_offset / ioc->req_sz);
+ mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx;
+ mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0;
+ ioc->RequestNB[req_idx] = ioc->NB_for_64_byte_frame; /* Default, will be changed if necessary in SG generation */
+#ifdef MFCNT
+ ioc->mfcnt++;
+#endif
+ }
+ else
+ mf = NULL;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+#ifdef MFCNT
+ if (mf == NULL)
+ printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", ioc->mfcnt, ioc->req_depth);
+ mfcounter++;
+ if (mfcounter == PRINT_MF_COUNT)
+ printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", ioc->mfcnt, ioc->req_depth);
+#endif
+
+ dmfprintk((KERN_INFO MYNAM ": %s: mpt_get_msg_frame(%d,%d), got mf=%p\n",
+ ioc->name, handle, ioc->id, mf));
+ return mf;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_put_msg_frame - Send a protocol specific MPT request frame
+ * to a IOC.
+ * @handle: Handle of registered MPT protocol driver
+ * @ioc: Pointer to MPT adapter structure
+ * @mf: Pointer to MPT request frame
+ *
+ * This routine posts a MPT request frame to the request post FIFO of a
+ * specific MPT adapter.
+ */
+void
+mpt_put_msg_frame(int handle, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
+{
+ u32 mf_dma_addr;
+ int req_offset;
+ u16 req_idx; /* Request index */
+
+ /* ensure values are reset properly! */
+ mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */
+ req_offset = (u8 *)mf - (u8 *)ioc->req_frames;
+ /* u16! */
+ req_idx = cpu_to_le16(req_offset / ioc->req_sz);
+ mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx;
+ mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0;
+
+#ifdef MPT_DEBUG_MSG_FRAME
+ {
+ u32 *m = mf->u.frame.hwhdr.__hdr;
+ int ii, n;
+
+ printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ",
+ ioc->name, m);
+ n = ioc->req_sz/4 - 1;
+ while (m[n] == 0)
+ n--;
+ for (ii=0; ii<=n; ii++) {
+ if (ii && ((ii%8)==0))
+ printk("\n" KERN_INFO " ");
+ printk(" %08x", le32_to_cpu(m[ii]));
+ }
+ printk("\n");
+ }
+#endif
+
+ mf_dma_addr = (ioc->req_frames_low_dma + req_offset) | ioc->RequestNB[req_idx];
+ dsgprintk((MYIOC_s_INFO_FMT "mf_dma_addr=%x req_idx=%d RequestNB=%x\n", ioc->name, mf_dma_addr, req_idx, ioc->RequestNB[req_idx]));
+ CHIPREG_WRITE32(&ioc->chip->RequestFifo, mf_dma_addr);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_free_msg_frame - Place MPT request frame back on FreeQ.
+ * @handle: Handle of registered MPT protocol driver
+ * @ioc: Pointer to MPT adapter structure
+ * @mf: Pointer to MPT request frame
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+void
+mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
+{
+ unsigned long flags;
+
+ /* Put Request back on FreeQ! */
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ);
+#ifdef MFCNT
+ ioc->mfcnt--;
+#endif
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_add_sge - Place a simple SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @flagslength: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+void
+mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ u32 tmp = dma_addr & 0xFFFFFFFF;
+
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32) ((u64)dma_addr >> 32);
+ pSge->Address.High = cpu_to_le32(tmp);
+
+ } else {
+ SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address = cpu_to_le32(dma_addr);
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_send_handshake_request - Send MPT request via doorbell
+ * handshake method.
+ * @handle: Handle of registered MPT protocol driver
+ * @ioc: Pointer to MPT adapter structure
+ * @reqBytes: Size of the request in bytes
+ * @req: Pointer to MPT request frame
+ * @sleepFlag: Use schedule if CAN_SLEEP else use udelay.
+ *
+ * This routine is used exclusively to send MptScsiTaskMgmt
+ * requests since they are required to be sent via doorbell handshake.
+ *
+ * NOTE: It is the callers responsibility to byte-swap fields in the
+ * request which are greater than 1 byte in size.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt_send_handshake_request(int handle, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag)
+{
+ int r = 0;
+ u8 *req_as_bytes;
+ int ii;
+
+ /* State is known to be good upon entering
+ * this function so issue the bus reset
+ * request.
+ */
+
+ /*
+ * Emulate what mpt_put_msg_frame() does /wrt to sanity
+ * setting cb_idx/req_idx. But ONLY if this request
+ * is in proper (pre-alloc'd) request buffer range...
+ */
+ ii = MFPTR_2_MPT_INDEX(ioc,(MPT_FRAME_HDR*)req);
+ if (reqBytes >= 12 && ii >= 0 && ii < ioc->req_depth) {
+ MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req;
+ mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii);
+ mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle;
+ }
+
+ /* Make sure there are no doorbells */
+ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+
+ CHIPREG_WRITE32(&ioc->chip->Doorbell,
+ ((MPI_FUNCTION_HANDSHAKE<<MPI_DOORBELL_FUNCTION_SHIFT) |
+ ((reqBytes/4)<<MPI_DOORBELL_ADD_DWORDS_SHIFT)));
+
+ /* Wait for IOC doorbell int */
+ if ((ii = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) {
+ return ii;
+ }
+
+ /* Read doorbell and check for active bit */
+ if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE))
+ return -5;
+
+ dhsprintk((KERN_INFO MYNAM ": %s: mpt_send_handshake_request start, WaitCnt=%d\n",
+ ioc->name, ii));
+
+ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+
+ if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) {
+ return -2;
+ }
+
+ /* Send request via doorbell handshake */
+ req_as_bytes = (u8 *) req;
+ for (ii = 0; ii < reqBytes/4; ii++) {
+ u32 word;
+
+ word = ((req_as_bytes[(ii*4) + 0] << 0) |
+ (req_as_bytes[(ii*4) + 1] << 8) |
+ (req_as_bytes[(ii*4) + 2] << 16) |
+ (req_as_bytes[(ii*4) + 3] << 24));
+ CHIPREG_WRITE32(&ioc->chip->Doorbell, word);
+ if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) {
+ r = -3;
+ break;
+ }
+ }
+
+ if (r >= 0 && WaitForDoorbellInt(ioc, 10, sleepFlag) >= 0)
+ r = 0;
+ else
+ r = -4;
+
+ /* Make sure there are no doorbells */
+ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+
+ return r;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_verify_adapter - Given a unique IOC identifier, set pointer to
+ * the associated MPT adapter structure.
+ * @iocid: IOC unique identifier (integer)
+ * @iocpp: Pointer to pointer to IOC adapter
+ *
+ * Returns iocid and sets iocpp.
+ */
+int
+mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp)
+{
+ MPT_ADAPTER *ioc;
+
+ list_for_each_entry(ioc,&ioc_list,list) {
+ if (ioc->id == iocid) {
+ *iocpp =ioc;
+ return iocid;
+ }
+ }
+
+ *iocpp = NULL;
+ return -1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptbase_probe - Install a PCI intelligent MPT adapter.
+ * @pdev: Pointer to pci_dev structure
+ *
+ * This routine performs all the steps necessary to bring the IOC of
+ * a MPT adapter to a OPERATIONAL state. This includes registering
+ * memory regions, registering the interrupt, and allocating request
+ * and reply memory pools.
+ *
+ * This routine also pre-fetches the LAN MAC address of a Fibre Channel
+