module_param_array(missing_delay, int, NULL, 0);
 MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");
 
+static int mpt2sas_fwfault_debug;
+MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
+       "and halt firmware - (default=0)");
+
+static int disable_discovery = -1;
+module_param(disable_discovery, int, 0);
+MODULE_PARM_DESC(disable_discovery, " disable discovery ");
+
+
 /* diag_buffer_enable is bitwise
  * bit 0 set = TRACE
  * bit 1 set = SNAPSHOT
 MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
     "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
 
-static int mpt2sas_fwfault_debug;
-MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
-    "and halt firmware - (default=0)");
-
-static int disable_discovery = -1;
-module_param(disable_discovery, int, 0);
-MODULE_PARM_DESC(disable_discovery, " disable discovery ");
-
 /**
  * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
  *
                memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
        }
        ioc->base_cmds.status &= ~MPT2_CMD_PENDING;
+
        complete(&ioc->base_cmds.done);
        return 1;
 }
        return 0;
 }
 
+/**
+ * mpt2sas_port_enable_done - command completion routine for port enable
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
+ */
+u8
+mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+       u32 reply)
+{
+       MPI2DefaultReply_t *mpi_reply;
+       u16 ioc_status;
+
+       mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
+               return 1;
+
+       if (ioc->port_enable_cmds.status == MPT2_CMD_NOT_USED)
+               return 1;
+
+       ioc->port_enable_cmds.status |= MPT2_CMD_COMPLETE;
+       if (mpi_reply) {
+               ioc->port_enable_cmds.status |= MPT2_CMD_REPLY_VALID;
+               memcpy(ioc->port_enable_cmds.reply, mpi_reply,
+                   mpi_reply->MsgLength*4);
+       }
+       ioc->port_enable_cmds.status &= ~MPT2_CMD_PENDING;
+
+       ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+               ioc->port_enable_failed = 1;
+
+       if (ioc->is_driver_loading) {
+               if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+                       mpt2sas_port_enable_complete(ioc);
+                       return 1;
+               } else {
+                       ioc->start_scan_failed = ioc_status;
+                       ioc->start_scan = 0;
+                       return 1;
+               }
+       }
+       complete(&ioc->port_enable_cmds.done);
+       return 1;
+}
+
+
 /**
  * _base_send_port_enable - send port_enable(discovery stuff) to firmware
  * @ioc: per adapter object
 _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
 {
        Mpi2PortEnableRequest_t *mpi_request;
-       u32 ioc_state;
+       Mpi2PortEnableReply_t *mpi_reply;
        unsigned long timeleft;
        int r = 0;
        u16 smid;
+       u16 ioc_status;
 
        printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
 
-       if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
+       if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
                printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
                    ioc->name, __func__);
                return -EAGAIN;
        }
 
-       smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+       smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
        if (!smid) {
                printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
                    ioc->name, __func__);
                return -EAGAIN;
        }
 
-       ioc->base_cmds.status = MPT2_CMD_PENDING;
+       ioc->port_enable_cmds.status = MPT2_CMD_PENDING;
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
-       ioc->base_cmds.smid = smid;
+       ioc->port_enable_cmds.smid = smid;
        memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
        mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
-       mpi_request->VF_ID = 0; /* TODO */
-       mpi_request->VP_ID = 0;
 
+       init_completion(&ioc->port_enable_cmds.done);
        mpt2sas_base_put_smid_default(ioc, smid);
-       init_completion(&ioc->base_cmds.done);
-       timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+       timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done,
            300*HZ);
-       if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+       if (!(ioc->port_enable_cmds.status & MPT2_CMD_COMPLETE)) {
                printk(MPT2SAS_ERR_FMT "%s: timeout\n",
                    ioc->name, __func__);
                _debug_dump_mf(mpi_request,
                    sizeof(Mpi2PortEnableRequest_t)/4);
-               if (ioc->base_cmds.status & MPT2_CMD_RESET)
+               if (ioc->port_enable_cmds.status & MPT2_CMD_RESET)
                        r = -EFAULT;
                else
                        r = -ETIME;
                goto out;
-       } else
-               dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n",
-                   ioc->name, __func__));
+       }
+       mpi_reply = ioc->port_enable_cmds.reply;
 
-       ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL,
-           60, sleep_flag);
-       if (ioc_state) {
-               printk(MPT2SAS_ERR_FMT "%s: failed going to operational state "
-                   " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
+       ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+               printk(MPT2SAS_ERR_FMT "%s: failed with (ioc_status=0x%08x)\n",
+                   ioc->name, __func__, ioc_status);
                r = -EFAULT;
+               goto out;
        }
  out:
-       ioc->base_cmds.status = MPT2_CMD_NOT_USED;
-       printk(MPT2SAS_INFO_FMT "port enable: %s\n",
-           ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
+       ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED;
+       printk(MPT2SAS_INFO_FMT "port enable: %s\n", ioc->name, ((r == 0) ?
+           "SUCCESS" : "FAILED"));
        return r;
 }
 
+/**
+ * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply)
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc)
+{
+       Mpi2PortEnableRequest_t *mpi_request;
+       u16 smid;
+
+       printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
+
+       if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
+               printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
+                   ioc->name, __func__);
+               return -EAGAIN;
+       }
+
+       smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+       if (!smid) {
+               printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+                   ioc->name, __func__);
+               return -EAGAIN;
+       }
+
+       ioc->port_enable_cmds.status = MPT2_CMD_PENDING;
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+       ioc->port_enable_cmds.smid = smid;
+       memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+       mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+       mpt2sas_base_put_smid_default(ioc, smid);
+       return 0;
+}
+
+/**
+ * _base_determine_wait_on_discovery - desposition
+ * @ioc: per adapter object
+ *
+ * Decide whether to wait on discovery to complete. Used to either
+ * locate boot device, or report volumes ahead of physical devices.
+ *
+ * Returns 1 for wait, 0 for don't wait
+ */
+static int
+_base_determine_wait_on_discovery(struct MPT2SAS_ADAPTER *ioc)
+{
+       /* We wait for discovery to complete if IR firmware is loaded.
+        * The sas topology events arrive before PD events, so we need time to
+        * turn on the bit in ioc->pd_handles to indicate PD
+        * Also, it maybe required to report Volumes ahead of physical
+        * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set.
+        */
+       if (ioc->ir_firmware)
+               return 1;
+
+       /* if no Bios, then we don't need to wait */
+       if (!ioc->bios_pg3.BiosVersion)
+               return 0;
+
+       /* Bios is present, then we drop down here.
+        *
+        * If there any entries in the Bios Page 2, then we wait
+        * for discovery to complete.
+        */
+
+       /* Current Boot Device */
+       if ((ioc->bios_pg2.CurrentBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+       /* Request Boot Device */
+          (ioc->bios_pg2.ReqBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+       /* Alternate Request Boot Device */
+          (ioc->bios_pg2.ReqAltBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED)
+               return 0;
+
+       return 1;
+}
+
+
 /**
  * _base_unmask_events - turn on notification for this event
  * @ioc: per adapter object
  skip_init_reply_post_host_index:
 
        _base_unmask_interrupts(ioc);
+
        r = _base_event_notification(ioc, sleep_flag);
        if (r)
                return r;
        if (sleep_flag == CAN_SLEEP)
                _base_static_config_pages(ioc);
 
-       if (ioc->wait_for_port_enable_to_complete && ioc->is_warpdrive) {
+
+       if (ioc->is_driver_loading) {
+
+
+
+               ioc->wait_for_discovery_to_complete =
+                   _base_determine_wait_on_discovery(ioc);
+               return r; /* scan_start and scan_finished support */
+       }
+
+
+       if (ioc->wait_for_discovery_to_complete && ioc->is_warpdrive) {
                if (ioc->manu_pg10.OEMIdentifier  == 0x80) {
                        hide_flag = (u8) (ioc->manu_pg10.OEMSpecificFlags0 &
                            MFG_PAGE10_HIDE_SSDS_MASK);
                }
        }
 
-       if (ioc->wait_for_port_enable_to_complete) {
-               if (diag_buffer_enable != 0)
-                       mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
-               if (disable_discovery > 0)
-                       return r;
-       }
-
        r = _base_send_port_enable(ioc, sleep_flag);
        if (r)
                return r;
        ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
        ioc->base_cmds.status = MPT2_CMD_NOT_USED;
 
+       /* port_enable command bits */
+       ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+       ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED;
+
        /* transport internal command bits */
        ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
        ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
                goto out_free_resources;
        }
 
-       init_completion(&ioc->shost_recovery_done);
-
        for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
                ioc->event_masks[i] = -1;
 
                _base_update_missing_delay(ioc, missing_delay[0],
                    missing_delay[1]);
 
-       mpt2sas_base_start_watchdog(ioc);
        return 0;
 
  out_free_resources:
        kfree(ioc->scsih_cmds.reply);
        kfree(ioc->config_cmds.reply);
        kfree(ioc->base_cmds.reply);
+       kfree(ioc->port_enable_cmds.reply);
        kfree(ioc->ctl_cmds.reply);
        kfree(ioc->ctl_cmds.sense);
        kfree(ioc->pfacts);
        kfree(ioc->ctl_cmds.reply);
        kfree(ioc->ctl_cmds.sense);
        kfree(ioc->base_cmds.reply);
+       kfree(ioc->port_enable_cmds.reply);
        kfree(ioc->tm_cmds.reply);
        kfree(ioc->transport_cmds.reply);
        kfree(ioc->scsih_cmds.reply);
                        mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid);
                        complete(&ioc->base_cmds.done);
                }
+               if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
+                       ioc->port_enable_failed = 1;
+                       ioc->port_enable_cmds.status |= MPT2_CMD_RESET;
+                       mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid);
+                       if (ioc->is_driver_loading) {
+                               ioc->start_scan_failed =
+                                   MPI2_IOCSTATUS_INTERNAL_ERROR;
+                               ioc->start_scan = 0;
+                               ioc->port_enable_cmds.status =
+                                               MPT2_CMD_NOT_USED;
+                       } else
+                               complete(&ioc->port_enable_cmds.done);
+
+               }
                if (ioc->config_cmds.status & MPT2_CMD_PENDING) {
                        ioc->config_cmds.status |= MPT2_CMD_RESET;
                        mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid);
 {
        int r;
        unsigned long flags;
-       u8 pe_complete = ioc->wait_for_port_enable_to_complete;
 
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
            __func__));
        /* If this hard reset is called while port enable is active, then
         * there is no reason to call make_ioc_operational
         */
-       if (pe_complete) {
+       if (ioc->is_driver_loading && ioc->port_enable_failed) {
+               ioc->remove_host = 1;
                r = -EFAULT;
                goto out;
        }
        spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
        ioc->ioc_reset_in_progress_status = r;
        ioc->shost_recovery = 0;
-       complete(&ioc->shost_recovery_done);
        spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
        mutex_unlock(&ioc->reset_in_progress_mutex);
 
 
 
 static u8 _scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid);
 
+static void _scsih_scan_start(struct Scsi_Host *shost);
+static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time);
+
 /* global parameters */
 LIST_HEAD(mpt2sas_ioc_list);
 
 static u8 tm_cb_idx = -1;
 static u8 ctl_cb_idx = -1;
 static u8 base_cb_idx = -1;
+static u8 port_enable_cb_idx = -1;
 static u8 transport_cb_idx = -1;
 static u8 scsih_cb_idx = -1;
 static u8 config_cb_idx = -1;
 module_param(max_lun, int, 0);
 MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
 
+/* diag_buffer_enable is bitwise
+ * bit 0 set = TRACE
+ * bit 1 set = SNAPSHOT
+ * bit 2 set = EXTENDED
+ *
+ * Either bit can be set, or both
+ */
+static int diag_buffer_enable = -1;
+module_param(diag_buffer_enable, int, 0);
+MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
+       "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+
 /**
  * struct sense_info - common structure for obtaining sense keys
  * @skey: sense key
 
 
 #define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC)
-#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF)
-
+#define MPT2SAS_PORT_ENABLE_COMPLETE (0xFFFD)
+#define MPT2SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF)
 /**
  * struct fw_event_work - firmware event struct
  * @list: link list framework
        u16 slot;
 
         /* only process this function when driver loads */
-       if (!ioc->wait_for_port_enable_to_complete)
+       if (!ioc->is_driver_loading)
+               return;
+
+        /* no Bios, return immediately */
+       if (!ioc->bios_pg3.BiosVersion)
                return;
 
        if (!is_raid) {
 
 
 /**
- * _scsih_queue_rescan - queue a topology rescan from user context
+ * _scsih_error_recovery_delete_devices - remove devices not responding
  * @ioc: per adapter object
  *
  * Return nothing.
  */
 static void
-_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)
+_scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc)
 {
        struct fw_event_work *fw_event;
 
-       if (ioc->wait_for_port_enable_to_complete)
+       if (ioc->is_driver_loading)
                return;
+       fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES;
+       fw_event->ioc = ioc;
+       _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * mpt2sas_port_enable_complete - port enable completed (fake event)
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc)
+{
+       struct fw_event_work *fw_event;
+
        fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
        if (!fw_event)
                return;
-       fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET;
+       fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
 }
        /* get device name */
        sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
 
-       if (ioc->wait_for_port_enable_to_complete)
+       if (ioc->wait_for_discovery_to_complete)
                _scsih_sas_device_init_add(ioc, sas_device);
        else
                _scsih_sas_device_add(ioc, sas_device);
        termination_count = 0;
        query_count = 0;
        for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
-               if (ioc->ioc_reset_in_progress_status)
+               if (ioc->shost_recovery)
                        goto out;
                scmd = _scsih_scsi_lookup_get(ioc, smid);
                if (!scmd)
                lun = sas_device_priv_data->lun;
                query_count++;
 
-               if (ioc->ioc_reset_in_progress_status)
+               if (ioc->shost_recovery)
                        goto out;
 
                spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
                        goto broadcast_aen_retry;
                }
 
-               if (ioc->ioc_reset_in_progress_status)
+               if (ioc->shost_recovery)
                        goto out_no_lock;
 
                r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id,
            ioc->name, __func__, query_count, termination_count));
 
        ioc->broadcast_aen_busy = 0;
-       if (!ioc->ioc_reset_in_progress_status)
+       if (!ioc->shost_recovery)
                _scsih_ublock_io_all_device(ioc);
        mutex_unlock(&ioc->tm_cmds.mutex);
 }
        raid_device->handle = handle;
        raid_device->wwid = wwid;
        _scsih_raid_device_add(ioc, raid_device);
-       if (!ioc->wait_for_port_enable_to_complete) {
+       if (!ioc->wait_for_discovery_to_complete) {
                rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
                    raid_device->id, 0);
                if (rc)
                _scsih_sas_ir_config_change_event_debug(ioc, event_data);
 
 #endif
+
+       if (ioc->shost_recovery)
+               return;
+
        foreign_config = (le32_to_cpu(event_data->Flags) &
            MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
 
        int rc;
        Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
 
+       if (ioc->shost_recovery)
+               return;
+
        if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
                return;
 
        Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
        u64 sas_address;
 
+       if (ioc->shost_recovery)
+               return;
+
        if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
                return;
 
        u32 device_info;
        u16 slot;
 
-       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+       printk(MPT2SAS_INFO_FMT "search for end-devices: start\n", ioc->name);
 
        if (list_empty(&ioc->sas_device_list))
-               return;
+               goto out;
 
        handle = 0xFFFF;
        while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
                _scsih_mark_responding_sas_device(ioc, sas_address, slot,
                    handle);
        }
+out:
+       printk(MPT2SAS_INFO_FMT "search for end-devices: complete\n",
+           ioc->name);
 }
 
 /**
        u16 handle;
        u8 phys_disk_num;
 
-       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+       if (!ioc->ir_firmware)
+               return;
+
+       printk(MPT2SAS_INFO_FMT "search for raid volumes: start\n",
+           ioc->name);
 
        if (list_empty(&ioc->raid_device_list))
-               return;
+               goto out;
 
        handle = 0xFFFF;
        while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
                        set_bit(handle, ioc->pd_handles);
                }
        }
+out:
+       printk(MPT2SAS_INFO_FMT "search for responding raid volumes: "
+           "complete\n", ioc->name);
 }
 
 /**
        u64 sas_address;
        u16 handle;
 
-       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+       printk(MPT2SAS_INFO_FMT "search for expanders: start\n", ioc->name);
 
        if (list_empty(&ioc->sas_expander_list))
-               return;
+               goto out;
 
        handle = 0xFFFF;
        while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
                _scsih_mark_responding_expander(ioc, sas_address, handle);
        }
 
+ out:
+       printk(MPT2SAS_INFO_FMT "search for expanders: complete\n", ioc->name);
 }
 
 /**
        struct _sas_node *sas_expander;
        struct _raid_device *raid_device, *raid_device_next;
 
+       printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n",
+           ioc->name);
 
        list_for_each_entry_safe(sas_device, sas_device_next,
            &ioc->sas_device_list, list) {
                _scsih_remove_device(ioc, sas_device);
        }
 
+       if (!ioc->ir_firmware)
+               goto retry_expander_search;
+
        list_for_each_entry_safe(raid_device, raid_device_next,
            &ioc->raid_device_list, list) {
                if (raid_device->responding) {
                mpt2sas_expander_remove(ioc, sas_expander->sas_address);
                goto retry_expander_search;
        }
+       printk(MPT2SAS_INFO_FMT "removing unresponding devices: complete\n",
+           ioc->name);
+       /* unblock devices */
+       _scsih_ublock_io_all_device(ioc);
+}
+
+static void
+_scsih_refresh_expander_links(struct MPT2SAS_ADAPTER *ioc,
+       struct _sas_node *sas_expander, u16 handle)
+{
+       Mpi2ExpanderPage1_t expander_pg1;
+       Mpi2ConfigReply_t mpi_reply;
+       int i;
+
+       for (i = 0 ; i < sas_expander->num_phys ; i++) {
+               if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply,
+                   &expander_pg1, i, handle))) {
+                       printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                           ioc->name, __FILE__, __LINE__, __func__);
+                       return;
+               }
+
+               mpt2sas_transport_update_links(ioc, sas_expander->sas_address,
+                   le16_to_cpu(expander_pg1.AttachedDevHandle), i,
+                   expander_pg1.NegotiatedLinkRate >> 4);
+       }
 }
 
 /**
- * _scsih_hide_unhide_sas_devices - add/remove device to/from OS
+ * _scsih_scan_for_devices_after_reset - scan for devices after host reset
  * @ioc: per adapter object
  *
  * Return nothing.
  */
 static void
-_scsih_hide_unhide_sas_devices(struct MPT2SAS_ADAPTER *ioc)
+_scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc)
 {
-       struct _sas_device *sas_device, *sas_device_next;
+       Mpi2ExpanderPage0_t expander_pg0;
+       Mpi2SasDevicePage0_t sas_device_pg0;
+       Mpi2RaidVolPage1_t volume_pg1;
+       Mpi2RaidVolPage0_t volume_pg0;
+       Mpi2RaidPhysDiskPage0_t pd_pg0;
+       Mpi2EventIrConfigElement_t element;
+       Mpi2ConfigReply_t mpi_reply;
+       u8 phys_disk_num;
+       u16 ioc_status;
+       u16 handle, parent_handle;
+       u64 sas_address;
+       struct _sas_device *sas_device;
+       struct _sas_node *expander_device;
+       static struct _raid_device *raid_device;
 
-       if (!ioc->is_warpdrive || ioc->mfg_pg10_hide_flag !=
-           MFG_PAGE10_HIDE_IF_VOL_PRESENT)
-               return;
+       printk(MPT2SAS_INFO_FMT "scan devices: start\n", ioc->name);
 
-       if (ioc->hide_drives) {
-               if (_scsih_get_num_volumes(ioc))
-                       return;
-               ioc->hide_drives = 0;
-               list_for_each_entry_safe(sas_device, sas_device_next,
-                   &ioc->sas_device_list, list) {
-                       if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
-                               sas_device->sas_address_parent)) {
-                               _scsih_sas_device_remove(ioc, sas_device);
-                       } else if (!sas_device->starget) {
-                               mpt2sas_transport_port_remove(ioc,
-                                   sas_device->sas_address,
-                                   sas_device->sas_address_parent);
-                               _scsih_sas_device_remove(ioc, sas_device);
-                       }
+       _scsih_sas_host_refresh(ioc);
+
+       /* expanders */
+       handle = 0xFFFF;
+       while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+           MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
+               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+                   MPI2_IOCSTATUS_MASK;
+               if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+                       break;
+               handle = le16_to_cpu(expander_pg0.DevHandle);
+               expander_device = mpt2sas_scsih_expander_find_by_sas_address(
+                   ioc, le64_to_cpu(expander_pg0.SASAddress));
+               if (expander_device)
+                       _scsih_refresh_expander_links(ioc, expander_device,
+                           handle);
+               else
+                       _scsih_expander_add(ioc, handle);
+       }
+
+       if (!ioc->ir_firmware)
+               goto skip_to_sas;
+
+       /* phys disk */
+       phys_disk_num = 0xFF;
+       while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+           &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
+           phys_disk_num))) {
+               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+                   MPI2_IOCSTATUS_MASK;
+               if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+                       break;
+               phys_disk_num = pd_pg0.PhysDiskNum;
+               handle = le16_to_cpu(pd_pg0.DevHandle);
+               sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+               if (sas_device)
+                       continue;
+               if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+                   &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+                   handle) != 0)
+                       continue;
+               parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+               if (!_scsih_get_sas_address(ioc, parent_handle,
+                   &sas_address)) {
+                       mpt2sas_transport_update_links(ioc, sas_address,
+                           handle, sas_device_pg0.PhyNum,
+                           MPI2_SAS_NEG_LINK_RATE_1_5);
+                       set_bit(handle, ioc->pd_handles);
+                       _scsih_add_device(ioc, handle, 0, 1);
                }
-       } else {
-               if (!_scsih_get_num_volumes(ioc))
-                       return;
-               ioc->hide_drives = 1;
-               list_for_each_entry_safe(sas_device, sas_device_next,
-                   &ioc->sas_device_list, list) {
-                       mpt2sas_transport_port_remove(ioc,
-                           sas_device->sas_address,
-                           sas_device->sas_address_parent);
+       }
+
+       /* volumes */
+       handle = 0xFFFF;
+       while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+           &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+                   MPI2_IOCSTATUS_MASK;
+               if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+                       break;
+               handle = le16_to_cpu(volume_pg1.DevHandle);
+               raid_device = _scsih_raid_device_find_by_wwid(ioc,
+                   le64_to_cpu(volume_pg1.WWID));
+               if (raid_device)
+                       continue;
+               if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
+                   &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+                    sizeof(Mpi2RaidVolPage0_t)))
+                       continue;
+               if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
+                   volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
+                   volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) {
+                       memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t));
+                       element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED;
+                       element.VolDevHandle = volume_pg1.DevHandle;
+                       _scsih_sas_volume_add(ioc, &element);
+               }
+       }
+
+ skip_to_sas:
+
+       /* sas devices */
+       handle = 0xFFFF;
+       while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+           &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+           handle))) {
+               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+                   MPI2_IOCSTATUS_MASK;
+               if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+                       break;
+               handle = le16_to_cpu(sas_device_pg0.DevHandle);
+               if (!(_scsih_is_end_device(
+                   le32_to_cpu(sas_device_pg0.DeviceInfo))))
+                       continue;
+               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+                   le64_to_cpu(sas_device_pg0.SASAddress));
+               if (sas_device)
+                       continue;
+               parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+               if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) {
+                       mpt2sas_transport_update_links(ioc, sas_address, handle,
+                           sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+                       _scsih_add_device(ioc, handle, 0, 0);
                }
        }
+
+       printk(MPT2SAS_INFO_FMT "scan devices: complete\n", ioc->name);
 }
 
+
 /**
  * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
  * @ioc: per adapter object
                }
                _scsih_fw_event_cleanup_queue(ioc);
                _scsih_flush_running_cmds(ioc);
-               _scsih_queue_rescan(ioc);
                break;
        case MPT2_IOC_DONE_RESET:
                dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
                _scsih_search_responding_sas_devices(ioc);
                _scsih_search_responding_raid_devices(ioc);
                _scsih_search_responding_expanders(ioc);
+                       _scsih_error_recovery_delete_devices(ioc);
                break;
        }
 }
 {
        struct fw_event_work *fw_event = container_of(work,
            struct fw_event_work, delayed_work.work);
-       unsigned long flags;
        struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
 
        /* the queue is being flushed so ignore this event */
                return;
        }
 
-       if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) {
-               _scsih_fw_event_free(ioc, fw_event);
-               spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
-               if (ioc->shost_recovery) {
-                       init_completion(&ioc->shost_recovery_done);
-                       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
-                           flags);
-                       wait_for_completion(&ioc->shost_recovery_done);
-               } else
-                       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
-                           flags);
+       switch (fw_event->event) {
+       case MPT2SAS_REMOVE_UNRESPONDING_DEVICES:
+               while (scsi_host_in_recovery(ioc->shost))
+                       ssleep(1);
                _scsih_remove_unresponding_sas_devices(ioc);
-               _scsih_hide_unhide_sas_devices(ioc);
-               return;
-       }
+               _scsih_scan_for_devices_after_reset(ioc);
+               break;
+       case MPT2SAS_PORT_ENABLE_COMPLETE:
+               if (!ioc->is_driver_loading && ioc->shost_recovery) {
+                       _scsih_prep_device_scan(ioc);
+                       _scsih_search_responding_sas_devices(ioc);
+                       _scsih_search_responding_raid_devices(ioc);
+                       _scsih_search_responding_expanders(ioc);
+               }
 
-       switch (fw_event->event) {
+               if (ioc->start_scan)
+                       ioc->start_scan = 0;
+               else
+                       complete(&ioc->port_enable_done);
+
+
+
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "port enable: complete "
+                   "from worker thread\n", ioc->name));
+               break;
        case MPT2SAS_TURN_ON_FAULT_LED:
                _scsih_turn_on_fault_led(ioc, fw_event->device_handle);
                break;
        .slave_configure                = _scsih_slave_configure,
        .target_destroy                 = _scsih_target_destroy,
        .slave_destroy                  = _scsih_slave_destroy,
+       .scan_finished                  = _scsih_scan_finished,
+       .scan_start                     = _scsih_scan_start,
        .change_queue_depth             = _scsih_change_queue_depth,
        .change_queue_type              = _scsih_change_queue_type,
        .eh_abort_handler               = _scsih_abort,
        unsigned long flags;
        int rc;
 
+        /* no Bios, return immediately */
+       if (!ioc->bios_pg3.BiosVersion)
+               return;
+
        device = NULL;
+       is_raid = 0;
        if (ioc->req_boot_device.device) {
                device =  ioc->req_boot_device.device;
                is_raid = ioc->req_boot_device.is_raid;
 static void
 _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
 {
-       u16 volume_mapping_flags =
-           le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
-           MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+       u16 volume_mapping_flags;
 
        if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))
                return;  /* return when IOC doesn't support initiator mode */
        _scsih_probe_boot_devices(ioc);
 
        if (ioc->ir_firmware) {
-               if ((volume_mapping_flags &
-                    MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) {
-                       _scsih_probe_sas(ioc);
+               volume_mapping_flags =
+                   le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
+                   MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+               if (volume_mapping_flags ==
+                   MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
                        _scsih_probe_raid(ioc);
+                       _scsih_probe_sas(ioc);
                } else {
-                       _scsih_probe_raid(ioc);
                        _scsih_probe_sas(ioc);
+                       _scsih_probe_raid(ioc);
                }
        } else
                _scsih_probe_sas(ioc);
 }
 
+
+/**
+ * _scsih_scan_start - scsi lld callback for .scan_start
+ * @shost: SCSI host pointer
+ *
+ * The shost has the ability to discover targets on its own instead
+ * of scanning the entire bus.  In our implemention, we will kick off
+ * firmware discovery.
+ */
+static void
+_scsih_scan_start(struct Scsi_Host *shost)
+{
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+       int rc;
+
+       if (diag_buffer_enable != -1 && diag_buffer_enable != 0)
+               mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
+
+       ioc->start_scan = 1;
+       rc = mpt2sas_port_enable(ioc);
+
+       if (rc != 0)
+               printk(MPT2SAS_INFO_FMT "port enable: FAILED\n", ioc->name);
+}
+
+/**
+ * _scsih_scan_finished - scsi lld callback for .scan_finished
+ * @shost: SCSI host pointer
+ * @time: elapsed time of the scan in jiffies
+ *
+ * This function will be called periodically until it returns 1 with the
+ * scsi_host and the elapsed time of the scan in jiffies. In our implemention,
+ * we wait for firmware discovery to complete, then return 1.
+ */
+static int
+_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+       if (time >= (300 * HZ)) {
+               ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+               printk(MPT2SAS_INFO_FMT "port enable: FAILED with timeout "
+                   "(timeout=300s)\n", ioc->name);
+               ioc->is_driver_loading = 0;
+               return 1;
+       }
+
+       if (ioc->start_scan)
+               return 0;
+
+       if (ioc->start_scan_failed) {
+               printk(MPT2SAS_INFO_FMT "port enable: FAILED with "
+                   "(ioc_status=0x%08x)\n", ioc->name, ioc->start_scan_failed);
+               ioc->is_driver_loading = 0;
+               ioc->wait_for_discovery_to_complete = 0;
+               ioc->remove_host = 1;
+               return 1;
+       }
+
+       printk(MPT2SAS_INFO_FMT "port enable: SUCCESS\n", ioc->name);
+       ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+
+       if (ioc->wait_for_discovery_to_complete) {
+               ioc->wait_for_discovery_to_complete = 0;
+               _scsih_probe_devices(ioc);
+       }
+       mpt2sas_base_start_watchdog(ioc);
+       ioc->is_driver_loading = 0;
+       return 1;
+}
+
+
 /**
  * _scsih_probe - attach and add scsi host
  * @pdev: PCI device struct
        ioc->tm_cb_idx = tm_cb_idx;
        ioc->ctl_cb_idx = ctl_cb_idx;
        ioc->base_cb_idx = base_cb_idx;
+       ioc->port_enable_cb_idx = port_enable_cb_idx;
        ioc->transport_cb_idx = transport_cb_idx;
        ioc->scsih_cb_idx = scsih_cb_idx;
        ioc->config_cb_idx = config_cb_idx;
                goto out_thread_fail;
        }
 
-       ioc->wait_for_port_enable_to_complete = 1;
+       ioc->is_driver_loading = 1;
        if ((mpt2sas_base_attach(ioc))) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
                goto out_attach_fail;
        }
 
-       ioc->wait_for_port_enable_to_complete = 0;
+       scsi_scan_host(shost);
        if (ioc->is_warpdrive) {
                if (ioc->mfg_pg10_hide_flag ==  MFG_PAGE10_EXPOSE_ALL_DISKS)
                        ioc->hide_drives = 0;
  out_thread_fail:
        list_del(&ioc->list);
        scsi_remove_host(shost);
+       scsi_host_put(shost);
  out_add_shost_fail:
        return -ENODEV;
 }
 
        /* base internal commands callback handler */
        base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done);
+       port_enable_cb_idx = mpt2sas_base_register_callback_handler(
+               mpt2sas_port_enable_done);
 
        /* transport internal commands callback handler */
        transport_cb_idx = mpt2sas_base_register_callback_handler(
        mpt2sas_base_release_callback_handler(scsi_io_cb_idx);
        mpt2sas_base_release_callback_handler(tm_cb_idx);
        mpt2sas_base_release_callback_handler(base_cb_idx);
+       mpt2sas_base_release_callback_handler(port_enable_cb_idx);
        mpt2sas_base_release_callback_handler(transport_cb_idx);
        mpt2sas_base_release_callback_handler(scsih_cb_idx);
        mpt2sas_base_release_callback_handler(config_cb_idx);