]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/lpfc/lpfc_init.c
[SCSI] lpfc 8.3.0 : Add active interrupt test for enabling MSI/MSI-X/INTx
[linux-2.6-omap-h63xx.git] / drivers / scsi / lpfc / lpfc_init.c
index d51a2a4b43eb662f61a6603a21b3b5feb2337385..e07f12a0871b442ad0b55e51535dcd9a80960368 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
 #include "lpfc.h"
@@ -52,17 +53,20 @@ static struct scsi_transport_template *lpfc_transport_template = NULL;
 static struct scsi_transport_template *lpfc_vport_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.                            */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_config_port_prep: Perform lpfc initialization prior to config port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine will do LPFC initialization prior to issuing the CONFIG_PORT
+ * mailbox command. It retrieves the revision information from the HBA and
+ * collects the Vital Product Data (VPD) about the HBA for preparing the
+ * configuration of the HBA.
+ *
+ * Return codes:
+ *   0 - success.
+ *   -ERESTART - requests the SLI layer to reset the HBA and try again.
+ *   Any other value - indicates an error.
+ **/
 int
 lpfc_config_port_prep(struct lpfc_hba *phba)
 {
@@ -180,12 +184,9 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                                                sizeof (phba->RandomData));
 
        /* 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;
+               goto out_free_mbox;
 
        do {
                lpfc_dump_mem(phba, pmb, offset);
@@ -200,21 +201,29 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                }
                if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
                        mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
-               lpfc_sli_pcimem_bcopy(pmb->context2, lpfc_vpd_data + offset,
+               lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
+                                     lpfc_vpd_data + offset,
                                      mb->un.varDmp.word_cnt);
                offset += mb->un.varDmp.word_cnt;
        } while (mb->un.varDmp.word_cnt && offset < DMP_VPD_SIZE);
        lpfc_parse_vpd(phba, lpfc_vpd_data, offset);
 
        kfree(lpfc_vpd_data);
-out_free_context2:
-       kfree(pmb->context2);
 out_free_mbox:
        mempool_free(pmb, phba->mbox_mem_pool);
        return 0;
 }
 
-/* Completion handler for config async event mailbox command. */
+/**
+ * lpfc_config_async_cmpl: Completion handler for config async event mbox cmd.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the completion handler for driver's configuring asynchronous event
+ * mailbox command to the device. If the mailbox command returns successfully,
+ * it will set internal async event support flag to 1; otherwise, it will
+ * set internal async event support flag to 0.
+ **/
 static void
 lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
 {
@@ -226,16 +235,61 @@ lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
        return;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*    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.                                               */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_dump_wakeup_param_cmpl: Completion handler for dump memory mailbox
+ *     command used for getting wake up parameters.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the completion handler for dump mailbox command for getting
+ * wake up parameters. When this command complete, the response contain
+ * Option rom version of the HBA. This function translate the version number
+ * into a human readable string and store it in OptionROMVersion.
+ **/
+static void
+lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+{
+       struct prog_id *prg;
+       uint32_t prog_id_word;
+       char dist = ' ';
+       /* character array used for decoding dist type. */
+       char dist_char[] = "nabx";
+
+       if (pmboxq->mb.mbxStatus != MBX_SUCCESS)
+               return;
+
+       prg = (struct prog_id *) &prog_id_word;
+
+       /* word 7 contain option rom version */
+       prog_id_word = pmboxq->mb.un.varWords[7];
+
+       /* Decode the Option rom version word to a readable string */
+       if (prg->dist < 4)
+               dist = dist_char[prg->dist];
+
+       if ((prg->dist == 3) && (prg->num == 0))
+               sprintf(phba->OptionROMVersion, "%d.%d%d",
+                       prg->ver, prg->rev, prg->lev);
+       else
+               sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+                       prg->ver, prg->rev, prg->lev,
+                       dist, prg->num);
+       return;
+}
+
+/**
+ * lpfc_config_port_post: Perform lpfc initialization after config port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine will do LPFC initialization after the CONFIG_PORT mailbox
+ * command call. It performs all internal resource and state setups on the
+ * port: post IOCB buffers, enable appropriate host interrupt attentions,
+ * ELS ring timers, etc.
+ *
+ * Return codes
+ *   0 - success.
+ *   Any other value - error.
+ **/
 int
 lpfc_config_port_post(struct lpfc_hba *phba)
 {
@@ -378,6 +432,29 @@ lpfc_config_port_post(struct lpfc_hba *phba)
        if (phba->sli_rev != 3)
                lpfc_post_rcv_buf(phba);
 
+       /*
+        * Configure HBA MSI-X attention conditions to messages if MSI-X mode
+        */
+       if (phba->intr_type == MSIX) {
+               rc = lpfc_config_msi(phba, pmb);
+               if (rc) {
+                       mempool_free(pmb, phba->mbox_mem_pool);
+                       return -EIO;
+               }
+               rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+               if (rc != MBX_SUCCESS) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                                       "0352 Config MSI mailbox command "
+                                       "failed, mbxCmd x%x, mbxStatus x%x\n",
+                                       pmb->mb.mbxCommand, pmb->mb.mbxStatus);
+                       mempool_free(pmb, phba->mbox_mem_pool);
+                       return -EIO;
+               }
+       }
+
+       /* Initialize ERATT handling flag */
+       phba->hba_flag &= ~HBA_ERATT_HANDLED;
+
        /* Enable appropriate host interrupts */
        spin_lock_irq(&phba->hbalock);
        status = readl(phba->HCregaddr);
@@ -393,26 +470,26 @@ lpfc_config_port_post(struct lpfc_hba *phba)
 
        if ((phba->cfg_poll & ENABLE_FCP_RING_POLLING) &&
            (phba->cfg_poll & DISABLE_FCP_RING_INT))
-               status &= ~(HC_R0INT_ENA << LPFC_FCP_RING);
+               status &= ~(HC_R0INT_ENA);
 
        writel(status, phba->HCregaddr);
        readl(phba->HCregaddr); /* flush */
        spin_unlock_irq(&phba->hbalock);
 
-       /*
-        * Setup the ring 0 (els)  timeout handler
-        */
-       timeout = phba->fc_ratov << 1;
+       /* Set up ring-0 (ELS) timer */
+       timeout = phba->fc_ratov * 2;
        mod_timer(&vport->els_tmofunc, jiffies + HZ * timeout);
+       /* Set up heart beat (HB) timer */
        mod_timer(&phba->hb_tmofunc, jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
        phba->hb_outstanding = 0;
        phba->last_completion_time = jiffies;
+       /* Set up error attention (ERATT) polling timer */
+       mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
 
        lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
        pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
-       pmb->vport = vport;
-       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
        lpfc_set_loopback_flag(phba);
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
        if (rc != MBX_SUCCESS) {
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                                "0454 Adapter failed to init, mbxCmd x%x "
@@ -447,19 +524,34 @@ lpfc_config_port_post(struct lpfc_hba *phba)
                                rc);
                mempool_free(pmb, phba->mbox_mem_pool);
        }
-       return (0);
+
+       /* Get Option rom version */
+       pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       lpfc_dump_wakeup_param(phba, pmb);
+       pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
+       pmb->vport = phba->pport;
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+
+       if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+                               "to get Option ROM version status x%x\n.", rc);
+               mempool_free(pmb, phba->mbox_mem_pool);
+       }
+
+       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.                                               */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_hba_down_prep: Perform lpfc uninitialization prior to HBA reset.
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do LPFC uninitialization before the HBA is reset when
+ * bringing down the SLI Layer.
+ *
+ * Return codes
+ *   0 - success.
+ *   Any other value - error.
+ **/
 int
 lpfc_hba_down_prep(struct lpfc_hba *phba)
 {
@@ -481,15 +573,17 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
        return 0;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*    lpfc_hba_down_post                                                */
-/*    This routine will do uninitialization after the HBA is reset      */
-/*    when bringing down the SLI Layer.                                 */
-/*    This routine returns 0 on success. Any other return value         */
-/*    indicates an error.                                               */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_hba_down_post: Perform lpfc uninitialization after HBA reset.
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do uninitialization after the HBA is reset when bring
+ * down the SLI Layer.
+ *
+ * Return codes
+ *   0 - sucess.
+ *   Any other value - error.
+ **/
 int
 lpfc_hba_down_post(struct lpfc_hba *phba)
 {
@@ -548,7 +642,18 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
        return 0;
 }
 
-/* HBA heart beat timeout handler */
+/**
+ * lpfc_hb_timeout: The HBA-timer timeout handler.
+ * @ptr: unsigned long holds the pointer to lpfc hba data structure.
+ *
+ * This is the HBA-timer timeout handler registered to the lpfc driver. When
+ * this timer fires, a HBA timeout event shall be posted to the lpfc driver
+ * work-port-events bitmap and the worker thread is notified. This timeout
+ * event will be used by the worker thread to invoke the actual timeout
+ * handler routine, lpfc_hb_timeout_handler. Any periodical operations will
+ * be performed in the timeout handler and the HBA timeout event bit shall
+ * be cleared by the worker thread after it has taken the event bitmap out.
+ **/
 static void
 lpfc_hb_timeout(unsigned long ptr)
 {
@@ -557,17 +662,36 @@ lpfc_hb_timeout(unsigned long ptr)
        unsigned long iflag;
 
        phba = (struct lpfc_hba *)ptr;
+
+       /* Check for heart beat timeout conditions */
        spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
        tmo_posted = phba->pport->work_port_events & WORKER_HB_TMO;
        if (!tmo_posted)
                phba->pport->work_port_events |= WORKER_HB_TMO;
        spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
 
+       /* Tell the worker thread there is work to do */
        if (!tmo_posted)
                lpfc_worker_wake_up(phba);
        return;
 }
 
+/**
+ * lpfc_hb_mbox_cmpl: The lpfc heart-beat mailbox command callback function.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the callback function to the lpfc heart-beat mailbox command.
+ * If configured, the lpfc driver issues the heart-beat mailbox command to
+ * the HBA every LPFC_HB_MBOX_INTERVAL (current 5) seconds. At the time the
+ * heart-beat mailbox command is issued, the driver shall set up heart-beat
+ * timeout timer to LPFC_HB_MBOX_TIMEOUT (current 30) seconds and marks
+ * heart-beat outstanding state. Once the mailbox command comes back and
+ * no error conditions detected, the heart-beat mailbox command timer is
+ * reset to LPFC_HB_MBOX_INTERVAL seconds and the heart-beat outstanding
+ * state is cleared for the next heart-beat. If the timer expired with the
+ * heart-beat outstanding state set, the driver will put the HBA offline.
+ **/
 static void
 lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
 {
@@ -577,6 +701,7 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
        phba->hb_outstanding = 0;
        spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
 
+       /* Check and reset heart-beat timer is necessary */
        mempool_free(pmboxq, phba->mbox_mem_pool);
        if (!(phba->pport->fc_flag & FC_OFFLINE_MODE) &&
                !(phba->link_state == LPFC_HBA_ERROR) &&
@@ -586,6 +711,22 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
        return;
 }
 
+/**
+ * lpfc_hb_timeout_handler: The HBA-timer timeout handler.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This is the actual HBA-timer timeout handler to be invoked by the worker
+ * thread whenever the HBA timer fired and HBA-timeout event posted. This
+ * handler performs any periodic operations needed for the device. If such
+ * periodic event has already been attended to either in the interrupt handler
+ * or by processing slow-ring or fast-ring events within the HBA-timer
+ * timeout window (LPFC_HB_MBOX_INTERVAL), this handler just simply resets
+ * the timer for the next timeout period. If lpfc heart-beat mailbox command
+ * is configured and there is no heart-beat mailbox command outstanding, a
+ * heart-beat mailbox is issued and timer set properly. Otherwise, if there
+ * has been a heart-beat mailbox command outstanding, the HBA shall be put
+ * to offline.
+ **/
 void
 lpfc_hb_timeout_handler(struct lpfc_hba *phba)
 {
@@ -601,11 +742,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
                return;
 
        spin_lock_irq(&phba->pport->work_port_lock);
-       /* If the timer is already canceled do nothing */
-       if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
-               spin_unlock_irq(&phba->pport->work_port_lock);
-               return;
-       }
 
        if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
                jiffies)) {
@@ -684,6 +820,13 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
        }
 }
 
+/**
+ * lpfc_offline_eratt: Bring lpfc offline on hardware error attention.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to bring the HBA offline when HBA hardware error
+ * other than Port Error 6 has been detected.
+ **/
 static void
 lpfc_offline_eratt(struct lpfc_hba *phba)
 {
@@ -704,14 +847,16 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
        return;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*    lpfc_handle_eratt                                                 */
-/*    This routine will handle processing a Host Attention              */
-/*    Error Status event. This will be initialized                      */
-/*    as a SLI layer callback routine.                                  */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_handle_eratt: The HBA hardware error handler.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to handle the following HBA hardware error
+ * conditions:
+ * 1 - HBA error attention interrupt
+ * 2 - DMA ring index out of range
+ * 3 - Mailbox command came back as unknown
+ **/
 void
 lpfc_handle_eratt(struct lpfc_hba *phba)
 {
@@ -722,6 +867,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        unsigned long temperature;
        struct temp_event temp_event_data;
        struct Scsi_Host  *shost;
+       struct lpfc_board_event_header board_event;
 
        /* If the pci channel is offline, ignore possible errors,
         * since we cannot communicate with the pci card anyway. */
@@ -731,6 +877,15 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        if (!phba->cfg_enable_hba_reset)
                return;
 
+       /* Send an internal error event to mgmt application */
+       board_event.event_type = FC_REG_BOARD_EVENT;
+       board_event.subcategory = LPFC_EVENT_PORTINTERR;
+       shost = lpfc_shost_from_vport(phba->pport);
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+                                 sizeof(board_event),
+                                 (char *) &board_event,
+                                 LPFC_NL_VENDOR_ID);
+
        if (phba->work_hs & HS_FFER6) {
                /* Re-establishing Link */
                lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
@@ -771,7 +926,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                temp_event_data.data = (uint32_t)temperature;
 
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0459 Adapter maximum temperature exceeded "
+                               "0406 Adapter maximum temperature exceeded "
                                "(%ld), taking this port offline "
                                "Data: x%x x%x x%x\n",
                                temperature, phba->work_hs,
@@ -791,8 +946,8 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
 
        } 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.
+                * 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,
                                "0457 Adapter Hardware Error "
@@ -808,16 +963,16 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
 
                lpfc_offline_eratt(phba);
        }
+       return;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*    lpfc_handle_latt                                                  */
-/*    This routine will handle processing a Host Attention              */
-/*    Link Status event. This will be initialized                       */
-/*    as a SLI layer callback routine.                                  */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_handle_latt: The HBA link event handler.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked from the worker thread to handle a HBA host
+ * attention link event.
+ **/
 void
 lpfc_handle_latt(struct lpfc_hba *phba)
 {
@@ -898,12 +1053,20 @@ lpfc_handle_latt_err_exit:
        return;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_parse_vpd                                                     */
-/*   This routine will parse the VPD data                               */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_parse_vpd: Parse VPD (Vital Product Data).
+ * @phba: pointer to lpfc hba data structure.
+ * @vpd: pointer to the vital product data.
+ * @len: length of the vital product data in bytes.
+ *
+ * This routine parses the Vital Product Data (VPD). The VPD is treated as
+ * an array of characters. In this routine, the ModelName, ProgramType, and
+ * ModelDesc, etc. fields of the phba data structure will be populated.
+ *
+ * Return codes
+ *   0 - pointer to the VPD passed in is NULL
+ *   1 - success
+ **/
 static int
 lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
 {
@@ -1040,12 +1203,25 @@ lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
        return(1);
 }
 
+/**
+ * lpfc_get_hba_model_desc: Retrieve HBA device model name and description.
+ * @phba: pointer to lpfc hba data structure.
+ * @mdp: pointer to the data structure to hold the derived model name.
+ * @descp: pointer to the data structure to hold the derived description.
+ *
+ * This routine retrieves HBA's description based on its registered PCI device
+ * ID. The @descp passed into this function points to an array of 256 chars. It
+ * shall be returned with the model name, maximum speed, and the host bus type.
+ * The @mdp passed into this function points to an array of 80 chars. When the
+ * function returns, the @mdp will be filled with the model name.
+ **/
 static void
 lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
 {
        lpfc_vpd_t *vp;
        uint16_t dev_id = phba->pcidev->device;
        int max_speed;
+       int GE = 0;
        struct {
                char * name;
                int    max_speed;
@@ -1177,6 +1353,19 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
        case PCI_DEVICE_ID_SAT_S:
                m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"};
                break;
+       case PCI_DEVICE_ID_HORNET:
+               m = (typeof(m)){"LP21000", max_speed, "PCIe"};
+               GE = 1;
+               break;
+       case PCI_DEVICE_ID_PROTEUS_VF:
+               m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+               break;
+       case PCI_DEVICE_ID_PROTEUS_PF:
+               m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+               break;
+       case PCI_DEVICE_ID_PROTEUS_S:
+               m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
+               break;
        default:
                m = (typeof(m)){ NULL };
                break;
@@ -1186,18 +1375,25 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
                snprintf(mdp, 79,"%s", m.name);
        if (descp && descp[0] == '\0')
                snprintf(descp, 255,
-                        "Emulex %s %dGb %s Fibre Channel Adapter",
-                        m.name, m.max_speed, m.bus);
+                       "Emulex %s %d%s %s %s",
+                       m.name, m.max_speed,
+                       (GE) ? "GE" : "Gb",
+                       m.bus,
+                       (GE) ? "FCoE Adapter" : "Fibre Channel Adapter");
 }
 
-/**************************************************/
-/*   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.    */
-/**************************************************/
+/**
+ * lpfc_post_buffer: Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring.
+ * @phba: pointer to lpfc hba data structure.
+ * @pring: pointer to a IOCB ring.
+ * @cnt: the number of IOCBs to be posted to the IOCB ring.
+ *
+ * This routine posts a given number of IOCBs with the associated DMA buffer
+ * descriptors specified by the cnt argument to the given IOCB ring.
+ *
+ * Return codes
+ *   The number of IOCBs NOT able to be posted to the IOCB ring.
+ **/
 int
 lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
 {
@@ -1287,12 +1483,17 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
        return 0;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_post_rcv_buf                                                  */
-/*   This routine post initial rcv buffers to the configured rings      */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_post_rcv_buf: Post the initial receive IOCB buffers to ELS ring.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine posts initial receive IOCB buffers to the ELS ring. The
+ * current number of initial IOCB buffers specified by LPFC_BUF_RING0 is
+ * set to 64 IOCBs.
+ *
+ * Return codes
+ *   0 - success (currently always success)
+ **/
 static int
 lpfc_post_rcv_buf(struct lpfc_hba *phba)
 {
@@ -1307,11 +1508,13 @@ lpfc_post_rcv_buf(struct lpfc_hba *phba)
 
 #define S(N,V) (((V)<<(N))|((V)>>(32-(N))))
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_sha_init                                                      */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_sha_init: Set up initial array of hash table entries.
+ * @HashResultPointer: pointer to an array as hash table.
+ *
+ * This routine sets up the initial values to the array of hash table entries
+ * for the LC HBAs.
+ **/
 static void
 lpfc_sha_init(uint32_t * HashResultPointer)
 {
@@ -1322,11 +1525,16 @@ lpfc_sha_init(uint32_t * HashResultPointer)
        HashResultPointer[4] = 0xC3D2E1F0;
 }
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_sha_iterate                                                   */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_sha_iterate: Iterate initial hash table with the working hash table.
+ * @HashResultPointer: pointer to an initial/result hash table.
+ * @HashWorkingPointer: pointer to an working hash table.
+ *
+ * This routine iterates an initial hash table pointed by @HashResultPointer
+ * with the values from the working hash table pointeed by @HashWorkingPointer.
+ * The results are putting back to the initial hash table, returned through
+ * the @HashResultPointer as the result hash table.
+ **/
 static void
 lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
 {
@@ -1374,22 +1582,29 @@ lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
 
 }
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_challenge_key                                                 */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_challenge_key: Create challenge key based on WWPN of the HBA.
+ * @RandomChallenge: pointer to the entry of host challenge random number array.
+ * @HashWorking: pointer to the entry of the working hash array.
+ *
+ * This routine calculates the working hash array referred by @HashWorking
+ * from the challenge random numbers associated with the host, referred by
+ * @RandomChallenge. The result is put into the entry of the working hash
+ * array and returned by reference through @HashWorking.
+ **/
 static void
 lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking)
 {
        *HashWorking = (*RandomChallenge ^ *HashWorking);
 }
 
-/************************************************************************/
-/*                                                                      */
-/*   lpfc_hba_init                                                      */
-/*                                                                      */
-/************************************************************************/
+/**
+ * lpfc_hba_init: Perform special handling for LC HBA initialization.
+ * @phba: pointer to lpfc hba data structure.
+ * @hbainit: pointer to an array of unsigned 32-bit integers.
+ *
+ * This routine performs the special handling for LC HBA initialization.
+ **/
 void
 lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
 {
@@ -1412,6 +1627,15 @@ lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
        kfree(HashWorking);
 }
 
+/**
+ * lpfc_cleanup: Performs vport cleanups before deleting a vport.
+ * @vport: pointer to a virtual N_Port data structure.
+ *
+ * This routine performs the necessary cleanups before deleting the @vport.
+ * It invokes the discovery state machine to perform necessary state
+ * transitions and to release the ndlps associated with the @vport. Note,
+ * the physical port is treated as @vport 0.
+ **/
 void
 lpfc_cleanup(struct lpfc_vport *vport)
 {
@@ -1459,14 +1683,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
                lpfc_disc_state_machine(vport, ndlp, NULL,
                                             NLP_EVT_DEVICE_RM);
 
-               /* nlp_type zero is not defined, nlp_flag zero also not defined,
-                * nlp_state is unused, this happens when
-                * an initiator has logged
-                * into us so cleanup this ndlp.
-                */
-               if ((ndlp->nlp_type == 0) && (ndlp->nlp_flag == 0) &&
-                       (ndlp->nlp_state == 0))
-                       lpfc_nlp_put(ndlp);
        }
 
        /* At this point, ALL ndlp's should be gone
@@ -1482,7 +1698,7 @@ lpfc_cleanup(struct lpfc_vport *vport)
                                                &vport->fc_nodes, nlp_listp) {
                                lpfc_printf_vlog(ndlp->vport, KERN_ERR,
                                                LOG_NODE,
-                                               "0282: did:x%x ndlp:x%p "
+                                               "0282 did:x%x ndlp:x%p "
                                                "usgmap:x%x refcnt:%d\n",
                                                ndlp->nlp_DID, (void *)ndlp,
                                                ndlp->nlp_usg_map,
@@ -1498,6 +1714,14 @@ lpfc_cleanup(struct lpfc_vport *vport)
        return;
 }
 
+/**
+ * lpfc_stop_vport_timers: Stop all the timers associated with a vport.
+ * @vport: pointer to a virtual N_Port data structure.
+ *
+ * This routine stops all the timers associated with a @vport. This function
+ * is invoked before disabling or deleting a @vport. Note that the physical
+ * port is treated as @vport 0.
+ **/
 void
 lpfc_stop_vport_timers(struct lpfc_vport *vport)
 {
@@ -1507,6 +1731,13 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
        return;
 }
 
+/**
+ * lpfc_stop_phba_timers: Stop all the timers associated with an HBA.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine stops all the timers associated with a HBA. This function is
+ * invoked before either putting a HBA offline or unloading the driver.
+ **/
 static void
 lpfc_stop_phba_timers(struct lpfc_hba *phba)
 {
@@ -1516,9 +1747,20 @@ lpfc_stop_phba_timers(struct lpfc_hba *phba)
        del_timer_sync(&phba->fabric_block_timer);
        phba->hb_outstanding = 0;
        del_timer_sync(&phba->hb_tmofunc);
+       del_timer_sync(&phba->eratt_poll);
        return;
 }
 
+/**
+ * lpfc_block_mgmt_io: Mark a HBA's management interface as blocked.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine marks a HBA's management interface as blocked. Once the HBA's
+ * management interface is marked as blocked, all the user space access to
+ * the HBA, whether they are from sysfs interface or libdfc interface will
+ * all be blocked. The HBA is set to block the management interface when the
+ * driver prepares the HBA interface for online or offline.
+ **/
 static void
 lpfc_block_mgmt_io(struct lpfc_hba * phba)
 {
@@ -1529,6 +1771,18 @@ lpfc_block_mgmt_io(struct lpfc_hba * phba)
        spin_unlock_irqrestore(&phba->hbalock, iflag);
 }
 
+/**
+ * lpfc_online: Initialize and bring a HBA online.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine initializes the HBA and brings a HBA online. During this
+ * process, the management interface is blocked to prevent user space access
+ * to the HBA interfering with the driver initialization.
+ *
+ * Return codes
+ *   0 - successful
+ *   1 - failed
+ **/
 int
 lpfc_online(struct lpfc_hba *phba)
 {
@@ -1574,6 +1828,17 @@ lpfc_online(struct lpfc_hba *phba)
        return 0;
 }
 
+/**
+ * lpfc_unblock_mgmt_io: Mark a HBA's management interface to be not blocked.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine marks a HBA's management interface as not blocked. Once the
+ * HBA's management interface is marked as not blocked, all the user space
+ * access to the HBA, whether they are from sysfs interface or libdfc
+ * interface will be allowed. The HBA is set to block the management interface
+ * when the driver prepares the HBA interface for online or offline and then
+ * set to unblock the management interface afterwards.
+ **/
 void
 lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
 {
@@ -1584,6 +1849,14 @@ lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
        spin_unlock_irqrestore(&phba->hbalock, iflag);
 }
 
+/**
+ * lpfc_offline_prep: Prepare a HBA to be brought offline.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to prepare a HBA to be brought offline. It performs
+ * unregistration login to all the nodes on all vports and flushes the mailbox
+ * queue to make it ready to be brought offline.
+ **/
 void
 lpfc_offline_prep(struct lpfc_hba * phba)
 {
@@ -1633,6 +1906,14 @@ lpfc_offline_prep(struct lpfc_hba * phba)
        lpfc_sli_flush_mbox_queue(phba);
 }
 
+/**
+ * lpfc_offline: Bring a HBA offline.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine actually brings a HBA offline. It stops all the timers
+ * associated with the HBA, brings down the SLI layer, and eventually
+ * marks the HBA as in offline state for the upper layer protocol.
+ **/
 void
 lpfc_offline(struct lpfc_hba *phba)
 {
@@ -1670,12 +1951,17 @@ lpfc_offline(struct lpfc_hba *phba)
        lpfc_destroy_vport_work_array(phba, vports);
 }
 
-/******************************************************************************
-* Function name: lpfc_scsi_free
-*
-* Description: Called from lpfc_pci_remove_one free internal driver resources
-*
-******************************************************************************/
+/**
+ * lpfc_scsi_free: Free all the SCSI buffers and IOCBs from driver lists.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to free all the SCSI buffers and IOCBs from the driver
+ * list back to kernel. It is called from lpfc_pci_remove_one to free
+ * the internal resources before the device is removed from the system.
+ *
+ * Return codes
+ *   0 - successful (for now, it always returns 0)
+ **/
 static int
 lpfc_scsi_free(struct lpfc_hba *phba)
 {
@@ -1704,6 +1990,22 @@ lpfc_scsi_free(struct lpfc_hba *phba)
        return 0;
 }
 
+/**
+ * lpfc_create_port: Create an FC port.
+ * @phba: pointer to lpfc hba data structure.
+ * @instance: a unique integer ID to this FC port.
+ * @dev: pointer to the device data structure.
+ *
+ * This routine creates a FC port for the upper layer protocol. The FC port
+ * can be created on top of either a physical port or a virtual port provided
+ * by the HBA. This routine also allocates a SCSI host data structure (shost)
+ * and associates the FC port created before adding the shost into the SCSI
+ * layer.
+ *
+ * Return codes
+ *   @vport - pointer to the virtual N_Port data structure.
+ *   NULL - port create failed.
+ **/
 struct lpfc_vport *
 lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
 {
@@ -1777,14 +2079,19 @@ out:
        return NULL;
 }
 
+/**
+ * destroy_port: Destroy an FC port.
+ * @vport: pointer to an lpfc virtual N_Port data structure.
+ *
+ * This routine destroys a FC port from the upper layer protocol. All the
+ * resources associated with the port are released.
+ **/
 void
 destroy_port(struct lpfc_vport *vport)
 {
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        struct lpfc_hba  *phba = vport->phba;
 
-       kfree(vport->vname);
-
        lpfc_debugfs_terminate(vport);
        fc_remove_host(shost);
        scsi_remove_host(shost);
@@ -1797,6 +2104,16 @@ destroy_port(struct lpfc_vport *vport)
        return;
 }
 
+/**
+ * lpfc_get_instance: Get a unique integer ID.
+ *
+ * This routine allocates a unique integer ID from lpfc_hba_index pool. It
+ * uses the kernel idr facility to perform the task.
+ *
+ * Return codes:
+ *   instance - a unique integer ID allocated as the new instance.
+ *   -1 - lpfc get instance failed.
+ **/
 int
 lpfc_get_instance(void)
 {
@@ -1810,11 +2127,21 @@ lpfc_get_instance(void)
        return instance;
 }
 
-/*
- * Note: there is no scan_start function as adapter initialization
- * will have asynchronously kicked off the link initialization.
- */
-
+/**
+ * lpfc_scan_finished: method for SCSI layer to detect whether scan is done.
+ * @shost: pointer to SCSI host data structure.
+ * @time: elapsed time of the scan in jiffies.
+ *
+ * This routine is called by the SCSI layer with a SCSI host to determine
+ * whether the scan host is finished.
+ *
+ * Note: there is no scan_start function as adapter initialization will have
+ * asynchronously kicked off the link initialization.
+ *
+ * Return codes
+ *   0 - SCSI host scan is not over yet.
+ *   1 - SCSI host scan is over.
+ **/
 int lpfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
@@ -1858,6 +2185,13 @@ finished:
        return stat;
 }
 
+/**
+ * lpfc_host_attrib_init: Initialize SCSI host attributes on a FC port.
+ * @shost: pointer to SCSI host data structure.
+ *
+ * This routine initializes a given SCSI host attributes on a FC port. The
+ * SCSI host can be either on top of a physical port or a virtual port.
+ **/
 void lpfc_host_attrib_init(struct Scsi_Host *shost)
 {
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
@@ -1906,42 +2240,345 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
        spin_unlock_irq(shost->host_lock);
 }
 
+/**
+ * lpfc_enable_msix: Enable MSI-X interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI-X interrupt vectors. The kernel
+ * function pci_enable_msix() is called to enable the MSI-X vectors. Note that
+ * pci_enable_msix(), once invoked, enables either all or nothing, depending
+ * on the current availability of PCI vector resources. The device driver is
+ * responsible for calling the individual request_irq() to register each MSI-X
+ * vector with a interrupt handler, which is done in this function. Note that
+ * later when device is unloading, the driver should always call free_irq()
+ * on all MSI-X vectors it has done request_irq() on before calling
+ * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
+ * will be left with MSI-X enabled and leaks its vectors.
+ *
+ * Return codes
+ *   0 - sucessful
+ *   other values - error
+ **/
 static int
 lpfc_enable_msix(struct lpfc_hba *phba)
 {
-       int error;
+       int rc, i;
+       LPFC_MBOXQ_t *pmb;
 
-       phba->msix_entries[0].entry = 0;
-       phba->msix_entries[0].vector = 0;
+       /* Set up MSI-X multi-message vectors */
+       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+               phba->msix_entries[i].entry = i;
 
-       error = pci_enable_msix(phba->pcidev, phba->msix_entries,
+       /* Configure MSI-X capability structure */
+       rc = pci_enable_msix(phba->pcidev, phba->msix_entries,
                                ARRAY_SIZE(phba->msix_entries));
-       if (error) {
+       if (rc) {
                lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                               "0420 Enable MSI-X failed (%d), continuing "
-                               "with MSI\n", error);
-               pci_disable_msix(phba->pcidev);
-               return error;
+                               "0420 PCI enable MSI-X failed (%d)\n", rc);
+               goto msi_fail_out;
+       } else
+               for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "0477 MSI-X entry[%d]: vector=x%x "
+                                       "message=%d\n", i,
+                                       phba->msix_entries[i].vector,
+                                       phba->msix_entries[i].entry);
+       /*
+        * Assign MSI-X vectors to interrupt handlers
+        */
+
+       /* vector-0 is associated to slow-path handler */
+       rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
+                        IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0421 MSI-X slow-path request_irq failed "
+                               "(%d)\n", rc);
+               goto msi_fail_out;
        }
 
-       error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0,
-                           LPFC_DRIVER_NAME, phba);
-       if (error) {
+       /* vector-1 is associated to fast-path handler */
+       rc = request_irq(phba->msix_entries[1].vector, &lpfc_fp_intr_handler,
+                        IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
+
+       if (rc) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0429 MSI-X fast-path request_irq failed "
+                               "(%d)\n", rc);
+               goto irq_fail_out;
+       }
+
+       /*
+        * Configure HBA MSI-X attention conditions to messages
+        */
+       pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+       if (!pmb) {
+               rc = -ENOMEM;
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0421 MSI-X request_irq failed (%d), "
-                               "continuing with MSI\n", error);
-               pci_disable_msix(phba->pcidev);
+                               "0474 Unable to allocate memory for issuing "
+                               "MBOX_CONFIG_MSI command\n");
+               goto mem_fail_out;
        }
-       return error;
+       rc = lpfc_config_msi(phba, pmb);
+       if (rc)
+               goto mbx_fail_out;
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+                               "0351 Config MSI mailbox command failed, "
+                               "mbxCmd x%x, mbxStatus x%x\n",
+                               pmb->mb.mbxCommand, pmb->mb.mbxStatus);
+               goto mbx_fail_out;
+       }
+
+       /* Free memory allocated for mailbox command */
+       mempool_free(pmb, phba->mbox_mem_pool);
+       return rc;
+
+mbx_fail_out:
+       /* Free memory allocated for mailbox command */
+       mempool_free(pmb, phba->mbox_mem_pool);
+
+mem_fail_out:
+       /* free the irq already requested */
+       free_irq(phba->msix_entries[1].vector, phba);
+
+irq_fail_out:
+       /* free the irq already requested */
+       free_irq(phba->msix_entries[0].vector, phba);
+
+msi_fail_out:
+       /* Unconfigure MSI-X capability structure */
+       pci_disable_msix(phba->pcidev);
+       return rc;
 }
 
+/**
+ * lpfc_disable_msix: Disable MSI-X interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release the MSI-X vectors and then disable the
+ * MSI-X interrupt mode.
+ **/
 static void
 lpfc_disable_msix(struct lpfc_hba *phba)
 {
-       free_irq(phba->msix_entries[0].vector, phba);
+       int i;
+
+       /* Free up MSI-X multi-message vectors */
+       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+               free_irq(phba->msix_entries[i].vector, phba);
+       /* Disable MSI-X */
        pci_disable_msix(phba->pcidev);
 }
 
+/**
+ * lpfc_enable_msi: Enable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode. The kernel
+ * function pci_enable_msi() is called to enable the MSI vector. The
+ * device driver is responsible for calling the request_irq() to register
+ * MSI vector with a interrupt the handler, which is done in this function.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ */
+static int
+lpfc_enable_msi(struct lpfc_hba *phba)
+{
+       int rc;
+
+       rc = pci_enable_msi(phba->pcidev);
+       if (!rc)
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0462 PCI enable MSI mode success.\n");
+       else {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0471 PCI enable MSI mode failed (%d)\n", rc);
+               return rc;
+       }
+
+       rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+       if (rc) {
+               pci_disable_msi(phba->pcidev);
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0478 MSI request_irq failed (%d)\n", rc);
+       }
+       return rc;
+}
+
+/**
+ * lpfc_disable_msi: Disable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode. The driver
+ * calls free_irq() on MSI vector it has done request_irq() on before
+ * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
+ * a device will be left with MSI enabled and leaks its vector.
+ */
+
+static void
+lpfc_disable_msi(struct lpfc_hba *phba)
+{
+       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msi(phba->pcidev);
+       return;
+}
+
+/**
+ * lpfc_log_intr_mode: Log the active interrupt mode
+ * @phba: pointer to lpfc hba data structure.
+ * @intr_mode: active interrupt mode adopted.
+ *
+ * This routine it invoked to log the currently used active interrupt mode
+ * to the device.
+ */
+static void
+lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+{
+       switch (intr_mode) {
+       case 0:
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0470 Enable INTx interrupt mode.\n");
+               break;
+       case 1:
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0481 Enabled MSI interrupt mode.\n");
+               break;
+       case 2:
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0480 Enabled MSI-X interrupt mode.\n");
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0482 Illegal interrupt mode.\n");
+               break;
+       }
+       return;
+}
+
+static void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+       /* 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 */
+
+       /* Reset some HBA SLI setup states */
+       lpfc_stop_phba_timers(phba);
+       phba->pport->work_port_events = 0;
+
+       return;
+}
+
+/**
+ * lpfc_enable_intr: Enable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
+ * mode configured to the driver, the driver will try to fallback from the
+ * configured interrupt mode to an interrupt mode which is supported by the
+ * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ *   0 - sucessful
+ *   other values - error
+ **/
+static uint32_t
+lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+       uint32_t intr_mode = LPFC_INTR_ERROR;
+       int retval;
+
+       if (cfg_mode == 2) {
+               /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+               retval = lpfc_sli_config_port(phba, 3);
+               if (!retval) {
+                       /* Now, try to enable MSI-X interrupt mode */
+                       retval = lpfc_enable_msix(phba);
+                       if (!retval) {
+                               /* Indicate initialization to MSI-X mode */
+                               phba->intr_type = MSIX;
+                               intr_mode = 2;
+                       }
+               }
+       }
+
+       /* Fallback to MSI if MSI-X initialization failed */
+       if (cfg_mode >= 1 && phba->intr_type == NONE) {
+               retval = lpfc_enable_msi(phba);
+               if (!retval) {
+                       /* Indicate initialization to MSI mode */
+                       phba->intr_type = MSI;
+                       intr_mode = 1;
+               }
+       }
+
+       /* Fallback to INTx if both MSI-X/MSI initalization failed */
+       if (phba->intr_type == NONE) {
+               retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+               if (!retval) {
+                       /* Indicate initialization to INTx mode */
+                       phba->intr_type = INTx;
+                       intr_mode = 0;
+               }
+       }
+       return intr_mode;
+}
+
+/**
+ * lpfc_disable_intr: Disable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s). Depending on the
+ * interrupt mode, the driver will release the interrupt vector(s) for the
+ * message signaled interrupt.
+ **/
+static void
+lpfc_disable_intr(struct lpfc_hba *phba)
+{
+       /* Disable the currently initialized interrupt mode */
+       if (phba->intr_type == MSIX)
+               lpfc_disable_msix(phba);
+       else if (phba->intr_type == MSI)
+               lpfc_disable_msi(phba);
+       else if (phba->intr_type == INTx)
+               free_irq(phba->pcidev->irq, phba);
+
+       /* Reset interrupt management states */
+       phba->intr_type = NONE;
+       phba->sli.slistat.sli_intr = 0;
+
+       return;
+}
+
+/**
+ * lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA is presented in PCI bus, the kernel PCI subsystem looks at
+ * PCI device-specific information of the device and driver to see if the
+ * driver state that it can support this kind of device. If the match is
+ * successful, the driver core invokes this routine. If this routine
+ * determines it can claim the HBA, it does all the initialization that it
+ * needs to do to handle the HBA properly.
+ *
+ * Return code
+ *   0 - driver can claim the device
+ *   negative value - driver can not claim the device
+ **/
 static int __devinit
 lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
 {
@@ -1955,7 +2592,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        int error = -ENODEV, retval;
        int  i, hbq_count;
        uint16_t iotag;
+       uint32_t cfg_mode, intr_mode;
        int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+       struct lpfc_adapter_event_header adapter_event;
 
        if (pci_enable_device_mem(pdev))
                goto out;
@@ -1966,6 +2605,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        if (!phba)
                goto out_release_regions;
 
+       atomic_set(&phba->fast_event_count, 0);
        spin_lock_init(&phba->hbalock);
 
        /* Initialize ndlp management spinlock */
@@ -1978,6 +2618,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
                goto out_free_phba;
 
        INIT_LIST_HEAD(&phba->port_list);
+       init_waitqueue_head(&phba->wait_4_mlo_m_q);
        /*
         * Get all the module params for configuring this host and then
         * establish the host.
@@ -2000,8 +2641,12 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        init_timer(&phba->fabric_block_timer);
        phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
        phba->fabric_block_timer.data = (unsigned long) phba;
+       init_timer(&phba->eratt_poll);
+       phba->eratt_poll.function = lpfc_poll_eratt;
+       phba->eratt_poll.data = (unsigned long) phba;
 
        pci_set_master(pdev);
+       pci_save_state(pdev);
        pci_try_set_mwi(pdev);
 
        if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
@@ -2019,7 +2664,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        bar2map_len        = pci_resource_len(phba->pcidev, 2);
 
        /* Map HBA SLIM to a kernel virtual address. */
-       phba->slim_memmap_p      = ioremap(phba->pci_bar0_map, bar0map_len);
+       phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
        if (!phba->slim_memmap_p) {
                error = -ENODEV;
                dev_printk(KERN_ERR, &pdev->dev,
@@ -2037,12 +2682,18 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        }
 
        /* 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)
+       phba->slim2p.virt = dma_alloc_coherent(&phba->pcidev->dev,
+                                              SLI2_SLIM_SIZE,
+                                              &phba->slim2p.phys,
+                                              GFP_KERNEL);
+       if (!phba->slim2p.virt)
                goto out_iounmap;
 
-       memset(phba->slim2p, 0, SLI2_SLIM_SIZE);
+       memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
+       phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
+       phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
+       phba->IOCBs = (phba->slim2p.virt +
+                      offsetof(struct lpfc_sli2_slim, IOCBs));
 
        phba->hbqslimp.virt = dma_alloc_coherent(&phba->pcidev->dev,
                                                 lpfc_sli_hbq_size(),
@@ -2111,7 +2762,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        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_ERATT | HA_MBATT | HA_LATT);
        phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
 
        /* Initialize the wait queue head for the kernel thread */
@@ -2144,37 +2795,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        lpfc_debugfs_initialize(vport);
 
        pci_set_drvdata(pdev, shost);
-       phba->intr_type = NONE;
-
-       if (phba->cfg_use_msi == 2) {
-               error = lpfc_enable_msix(phba);
-               if (!error)
-                       phba->intr_type = MSIX;
-       }
-
-       /* Fallback to MSI if MSI-X initialization failed */
-       if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
-               retval = pci_enable_msi(phba->pcidev);
-               if (!retval)
-                       phba->intr_type = MSI;
-               else
-                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                                       "0452 Enable MSI failed, continuing "
-                                       "with IRQ\n");
-       }
-
-       /* MSI-X is the only case the doesn't need to call request_irq */
-       if (phba->intr_type != MSIX) {
-               retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
-                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
-               if (retval) {
-                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
-                                       "interrupt handler failed\n");
-                       error = retval;
-                       goto out_disable_msi;
-               } else if (phba->intr_type != MSI)
-                       phba->intr_type = INTx;
-       }
 
        phba->MBslimaddr = phba->slim_memmap_p;
        phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
@@ -2182,14 +2802,58 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
        phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
 
+       /* Configure sysfs attributes */
        if (lpfc_alloc_sysfs_attr(vport)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1476 Failed to allocate sysfs attr\n");
                error = -ENOMEM;
-               goto out_free_irq;
+               goto out_destroy_port;
        }
 
-       if (lpfc_sli_hba_setup(phba)) {
-               error = -ENODEV;
-               goto out_remove_device;
+       cfg_mode = phba->cfg_use_msi;
+       while (true) {
+               /* Configure and enable interrupt */
+               intr_mode = lpfc_enable_intr(phba, cfg_mode);
+               if (intr_mode == LPFC_INTR_ERROR) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0426 Failed to enable interrupt.\n");
+                       goto out_free_sysfs_attr;
+               }
+               /* HBA SLI setup */
+               if (lpfc_sli_hba_setup(phba)) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "1477 Failed to set up hba\n");
+                       error = -ENODEV;
+                       goto out_remove_device;
+               }
+
+               /* Wait 50ms for the interrupts of previous mailbox commands */
+               msleep(50);
+               /* Check active interrupts received */
+               if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+                       /* Log the current active interrupt mode */
+                       phba->intr_mode = intr_mode;
+                       lpfc_log_intr_mode(phba, intr_mode);
+                       break;
+               } else {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "0451 Configure interrupt mode (%d) "
+                                       "failed active interrupt test.\n",
+                                       intr_mode);
+                       if (intr_mode == 0) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                               "0479 Failed to enable "
+                                               "interrupt.\n");
+                               error = -ENODEV;
+                               goto out_remove_device;
+                       }
+                       /* Stop HBA SLI setups */
+                       lpfc_stop_port(phba);
+                       /* Disable the current interrupt mode */
+                       lpfc_disable_intr(phba);
+                       /* Try next level of interrupt mode */
+                       cfg_mode = --intr_mode;
+               }
        }
 
        /*
@@ -2206,27 +2870,28 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
                spin_unlock_irq(shost->host_lock);
        }
 
-       scsi_scan_host(shost);
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0428 Perform SCSI scan\n");
+       /* Send board arrival event to upper layer */
+       adapter_event.event_type = FC_REG_ADAPTER_EVENT;
+       adapter_event.subcategory = LPFC_EVENT_ARRIVAL;
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+               sizeof(adapter_event),
+               (char *) &adapter_event,
+               LPFC_NL_VENDOR_ID);
 
        return 0;
 
 out_remove_device:
-       lpfc_free_sysfs_attr(vport);
        spin_lock_irq(shost->host_lock);
        vport->load_flag |= FC_UNLOADING;
        spin_unlock_irq(shost->host_lock);
-out_free_irq:
        lpfc_stop_phba_timers(phba);
        phba->pport->work_port_events = 0;
-
-       if (phba->intr_type == MSIX)
-               lpfc_disable_msix(phba);
-       else
-               free_irq(phba->pcidev->irq, phba);
-
-out_disable_msi:
-       if (phba->intr_type == MSI)
-               pci_disable_msi(phba->pcidev);
+       lpfc_disable_intr(phba);
+out_free_sysfs_attr:
+       lpfc_free_sysfs_attr(vport);
+out_destroy_port:
        destroy_port(vport);
 out_kthread_stop:
        kthread_stop(phba->worker_thread);
@@ -2238,11 +2903,11 @@ out_free_iocbq:
        }
        lpfc_mem_free(phba);
 out_free_hbqslimp:
-       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(), phba->hbqslimp.virt,
-                         phba->hbqslimp.phys);
+       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
+                         phba->hbqslimp.virt, phba->hbqslimp.phys);
 out_free_slim:
-       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, phba->slim2p,
-                                                       phba->slim2p_mapping);
+       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+                         phba->slim2p.virt, phba->slim2p.phys);
 out_iounmap:
        iounmap(phba->ctrl_regs_memmap_p);
 out_iounmap_slim:
@@ -2262,23 +2927,40 @@ out:
        return error;
 }
 
+/**
+ * lpfc_pci_remove_one: lpfc PCI func to unregister device from PCI subsystem.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
+ * for the HBA device to be removed from the PCI subsystem properly.
+ **/
 static void __devexit
 lpfc_pci_remove_one(struct pci_dev *pdev)
 {
        struct Scsi_Host  *shost = pci_get_drvdata(pdev);
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+       struct lpfc_vport **vports;
        struct lpfc_hba   *phba = vport->phba;
+       int i;
        int bars = pci_select_bars(pdev, IORESOURCE_MEM);
 
        spin_lock_irq(&phba->hbalock);
        vport->load_flag |= FC_UNLOADING;
        spin_unlock_irq(&phba->hbalock);
 
-       kfree(vport->vname);
        lpfc_free_sysfs_attr(vport);
 
        kthread_stop(phba->worker_thread);
 
+       /* Release all the vports against this physical port */
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL)
+               for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+                       fc_vport_terminate(vports[i]->fc_vport);
+       lpfc_destroy_vport_work_array(phba, vports);
+
+       /* Remove FC host and then SCSI host with the physical port */
        fc_remove_host(shost);
        scsi_remove_host(shost);
        lpfc_cleanup(vport);
@@ -2298,13 +2980,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
 
        lpfc_debugfs_terminate(vport);
 
-       if (phba->intr_type == MSIX)
-               lpfc_disable_msix(phba);
-       else {
-               free_irq(phba->pcidev->irq, phba);
-               if (phba->intr_type == MSI)
-                       pci_disable_msi(phba->pcidev);
-       }
+       /* Disable interrupt */
+       lpfc_disable_intr(phba);
 
        pci_set_drvdata(pdev, NULL);
        scsi_host_put(shost);
@@ -2316,12 +2993,12 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
        lpfc_scsi_free(phba);
        lpfc_mem_free(phba);
 
-       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(), phba->hbqslimp.virt,
-                         phba->hbqslimp.phys);
+       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
+                         phba->hbqslimp.virt, phba->hbqslimp.phys);
 
        /* Free resources associated with SLI2 interface */
        dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
-                         phba->slim2p, phba->slim2p_mapping);
+                         phba->slim2p.virt, phba->slim2p.phys);
 
        /* unmap adapter SLIM and Control Registers */
        iounmap(phba->ctrl_regs_memmap_p);
@@ -2336,13 +3013,130 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
 }
 
 /**
- * lpfc_io_error_detected - called when PCI error is detected
- * @pdev: Pointer to PCI device
- * @state: The current pci conneection state
+ * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
+ * @pdev: pointer to PCI device
+ * @msg: power management message
  *
- * This function is called after a PCI bus error affecting
- * this device has been detected.
- */
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it quiesces the
+ * device by stopping the driver's worker thread for the device, turning off
+ * device's interrupt and DMA, and bring the device offline. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's PM
+ * support for suspend/resume -- all the possible PM messages (SUSPEND,
+ * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
+ * and the driver will fully reinitialize its device during resume() method
+ * call, the driver will set device to PCI_D3hot state in PCI config space
+ * instead of setting it according to the @msg provided by the PM.
+ *
+ * Return code
+ *   0 - driver suspended the device
+ *   Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0473 PCI device Power Management suspend.\n");
+
+       /* Bring down the device */
+       lpfc_offline_prep(phba);
+       lpfc_offline(phba);
+       kthread_stop(phba->worker_thread);
+
+       /* Disable interrupt from device */
+       lpfc_disable_intr(phba);
+
+       /* Save device state to PCI config space */
+       pci_save_state(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
+/**
+ * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it restores
+ * the device's PCI config space state and fully reinitializes the device
+ * and brings it online. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the device will be
+ * set to PCI_D0 directly in PCI config space before restoring the state.
+ *
+ * Return code
+ *   0 - driver suspended the device
+ *   Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       uint32_t intr_mode;
+       int error;
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0452 PCI device Power Management resume.\n");
+
+       /* Restore device state from PCI config space */
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (pdev->is_busmaster)
+               pci_set_master(pdev);
+
+       /* 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);
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0434 PM resume failed to start worker "
+                               "thread: error=x%x.\n", error);
+               return error;
+       }
+
+       /* Configure and enable interrupt */
+       intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+       if (intr_mode == LPFC_INTR_ERROR) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0430 PM resume Failed to enable interrupt\n");
+               return -EIO;
+       } else
+               phba->intr_mode = intr_mode;
+
+       /* Restart HBA and bring it online */
+       lpfc_sli_brdrestart(phba);
+       lpfc_online(phba);
+
+       /* Log the current active interrupt mode */
+       lpfc_log_intr_mode(phba, phba->intr_mode);
+
+       return 0;
+}
+
+/**
+ * lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
+ * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
+ *
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called by the PCI subsystem after a PCI bus error affecting
+ * this device has been detected. When this function is invoked, it will
+ * need to stop all the I/Os and interrupt(s) to the device. Once that is
+ * done, it will return PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to
+ * perform proper recovery as desired.
+ *
+ * Return codes
+ *   PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ *   PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
 static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
                                pci_channel_state_t state)
 {
@@ -2351,8 +3145,15 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring  *pring;
 
-       if (state == pci_channel_io_perm_failure)
+       if (state == pci_channel_io_perm_failure) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0472 PCI channel I/O permanent failure\n");
+               /* Block all SCSI devices' I/Os on the host */
+               lpfc_scsi_dev_block(phba);
+               /* Clean up all driver's outstanding SCSI I/Os */
+               lpfc_sli_flush_fcp_rings(phba);
                return PCI_ERS_RESULT_DISCONNECT;
+       }
 
        pci_disable_device(pdev);
        /*
@@ -2363,30 +3164,36 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
        pring = &psli->ring[psli->fcp_ring];
        lpfc_sli_abort_iocb_ring(phba, pring);
 
-       if (phba->intr_type == MSIX)
-               lpfc_disable_msix(phba);
-       else {
-               free_irq(phba->pcidev->irq, phba);
-               if (phba->intr_type == MSI)
-                       pci_disable_msi(phba->pcidev);
-       }
+       /* Disable interrupt */
+       lpfc_disable_intr(phba);
 
        /* Request a slot reset. */
        return PCI_ERS_RESULT_NEED_RESET;
 }
 
 /**
- * lpfc_io_slot_reset - called after the pci bus has been reset.
- * @pdev: Pointer to PCI device
+ * lpfc_io_slot_reset: Restart a PCI device from scratch.
+ * @pdev: pointer to PCI device.
  *
- * Restart the card from scratch, as if from a cold-boot.
+ * This routine is registered to the PCI subsystem for error handling. This is
+ * called after PCI bus has been reset to restart the PCI card from scratch,
+ * as if from a cold-boot. During the PCI subsystem error recovery, after the
+ * driver returns PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform
+ * proper error recovery and then call this routine before calling the .resume
+ * method to recover the device. This function will initialize the HBA device,
+ * enable the interrupt, but it will just put the HBA to offline state without
+ * passing any I/O traffic.
+ *
+ * Return codes
+ *   PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ *   PCI_ERS_RESULT_DISCONNECT - device could not be recovered
  */
 static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
        struct lpfc_sli *psli = &phba->sli;
-       int error, retval;
+       uint32_t intr_mode;
 
        dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
        if (pci_enable_device_mem(pdev)) {
@@ -2395,56 +3202,42 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
-       pci_set_master(pdev);
+       pci_restore_state(pdev);
+       if (pdev->is_busmaster)
+               pci_set_master(pdev);
 
        spin_lock_irq(&phba->hbalock);
        psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
        spin_unlock_irq(&phba->hbalock);
 
-       /* Enable configured interrupt method */
-       phba->intr_type = NONE;
-       if (phba->cfg_use_msi == 2) {
-               error = lpfc_enable_msix(phba);
-               if (!error)
-                       phba->intr_type = MSIX;
-       }
-
-       /* Fallback to MSI if MSI-X initialization failed */
-       if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
-               retval = pci_enable_msi(phba->pcidev);
-               if (!retval)
-                       phba->intr_type = MSI;
-               else
-                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                                       "0470 Enable MSI failed, continuing "
-                                       "with IRQ\n");
-       }
-
-       /* MSI-X is the only case the doesn't need to call request_irq */
-       if (phba->intr_type != MSIX) {
-               retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
-                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
-               if (retval) {
-                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                       "0471 Enable interrupt handler "
-                                       "failed\n");
-               } else if (phba->intr_type != MSI)
-                       phba->intr_type = INTx;
-       }
+       /* Configure and enable interrupt */
+       intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+       if (intr_mode == LPFC_INTR_ERROR) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0427 Cannot re-enable interrupt after "
+                               "slot reset.\n");
+               return PCI_ERS_RESULT_DISCONNECT;
+       } else
+               phba->intr_mode = intr_mode;
 
        /* Take device offline; this will perform cleanup */
        lpfc_offline(phba);
        lpfc_sli_brdrestart(phba);
 
+       /* Log the current active interrupt mode */
+       lpfc_log_intr_mode(phba, phba->intr_mode);
+
        return PCI_ERS_RESULT_RECOVERED;
 }
 
 /**
- * lpfc_io_resume - called when traffic can start flowing again.
- * @pdev: Pointer to PCI device
+ * lpfc_io_resume: Resume PCI I/O operation.
+ * @pdev: pointer to PCI device
  *
- * This callback is called when the error recovery driver tells us that
- * its OK to resume normal operation.
+ * This routine is registered to the PCI subsystem for error handling. It is
+ * called when kernel error recovery tells the lpfc driver that it is ok to
+ * resume normal PCI operation after PCI bus error recovery. After this call,
+ * traffic can start to flow from this device again.
  */
 static void lpfc_io_resume(struct pci_dev *pdev)
 {
@@ -2491,6 +3284,8 @@ static struct pci_device_id lpfc_id_table[] = {
                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_HORNET,
+               PCI_ANY_ID, PCI_ANY_ID, },
        {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_SCSP,
                PCI_ANY_ID, PCI_ANY_ID, },
        {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_DCSP,
@@ -2521,6 +3316,12 @@ static struct pci_device_id lpfc_id_table[] = {
                PCI_ANY_ID, PCI_ANY_ID, },
        {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_S,
                PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_VF,
+               PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_PF,
+               PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
+               PCI_ANY_ID, PCI_ANY_ID, },
        { 0 }
 };
 
@@ -2537,9 +3338,23 @@ static struct pci_driver lpfc_driver = {
        .id_table       = lpfc_id_table,
        .probe          = lpfc_pci_probe_one,
        .remove         = __devexit_p(lpfc_pci_remove_one),
+       .suspend        = lpfc_pci_suspend_one,
+       .resume         = lpfc_pci_resume_one,
        .err_handler    = &lpfc_err_handler,
 };
 
+/**
+ * lpfc_init: lpfc module initialization routine.
+ *
+ * This routine is to be invoked when the lpfc module is loaded into the
+ * kernel. The special kernel macro module_init() is used to indicate the
+ * role of this routine to the kernel as lpfc module entry point.
+ *
+ * Return codes
+ *   0 - successful
+ *   -ENOMEM - FC attach transport failed
+ *   all others - failed
+ */
 static int __init
 lpfc_init(void)
 {
@@ -2567,12 +3382,20 @@ lpfc_init(void)
        error = pci_register_driver(&lpfc_driver);
        if (error) {
                fc_release_transport(lpfc_transport_template);
-               fc_release_transport(lpfc_vport_transport_template);
+               if (lpfc_enable_npiv)
+                       fc_release_transport(lpfc_vport_transport_template);
        }
 
        return error;
 }
 
+/**
+ * lpfc_exit: lpfc module removal routine.
+ *
+ * This routine is invoked when the lpfc module is removed from the kernel.
+ * The special kernel macro module_exit() is used to indicate the role of
+ * this routine to the kernel as lpfc module exit point.
+ */
 static void __exit
 lpfc_exit(void)
 {