Merge branch 'char-misc-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[pandora-kernel.git] / drivers / scsi / mpt2sas / mpt2sas_scsih.c
index 5202de3..4e041f6 100644 (file)
@@ -71,6 +71,9 @@ static void _firmware_event_work(struct work_struct *work);
 
 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);
 
@@ -79,6 +82,7 @@ static u8 scsi_io_cb_idx = -1;
 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;
@@ -103,6 +107,18 @@ static int max_lun = MPT2SAS_MAX_LUN;
 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
@@ -117,8 +133,8 @@ struct sense_info {
 
 
 #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
@@ -372,31 +388,34 @@ _scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,
        Mpi2SasDevicePage0_t sas_device_pg0;
        Mpi2ConfigReply_t mpi_reply;
        u32 ioc_status;
+       *sas_address = 0;
 
        if (handle <= ioc->sas_hba.num_phys) {
                *sas_address = ioc->sas_hba.sas_address;
                return 0;
-       } else
-               *sas_address = 0;
+       }
 
        if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
            MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
-               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-                   ioc->name, __FILE__, __LINE__, __func__);
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name,
+               __FILE__, __LINE__, __func__);
                return -ENXIO;
        }
 
-       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
-           MPI2_IOCSTATUS_MASK;
-       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
-               printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
-                   "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
-                    __FILE__, __LINE__, __func__);
-               return -EIO;
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+               *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+               return 0;
        }
 
-       *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
-       return 0;
+       /* we hit this becuase the given parent handle doesn't exist */
+       if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+               return -ENXIO;
+       /* else error case */
+       printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x), "
+           "failure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
+            __FILE__, __LINE__, __func__);
+       return -EIO;
 }
 
 /**
@@ -424,7 +443,11 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,
        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) {
@@ -587,8 +610,15 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
-            sas_device->sas_address_parent))
+            sas_device->sas_address_parent)) {
                _scsih_sas_device_remove(ioc, sas_device);
+               } else if (!sas_device->starget) {
+                       if (!ioc->is_driver_loading)
+                               mpt2sas_transport_port_remove(ioc,
+                               sas_device->sas_address,
+                           sas_device->sas_address_parent);
+                       _scsih_sas_device_remove(ioc, sas_device);
+               }
 }
 
 /**
@@ -1400,6 +1430,10 @@ _scsih_slave_destroy(struct scsi_device *sdev)
 {
        struct MPT2SAS_TARGET *sas_target_priv_data;
        struct scsi_target *starget;
+       struct Scsi_Host *shost;
+       struct MPT2SAS_ADAPTER *ioc;
+       struct _sas_device *sas_device;
+       unsigned long flags;
 
        if (!sdev->hostdata)
                return;
@@ -1407,6 +1441,19 @@ _scsih_slave_destroy(struct scsi_device *sdev)
        starget = scsi_target(sdev);
        sas_target_priv_data = starget->hostdata;
        sas_target_priv_data->num_luns--;
+
+       shost = dev_to_shost(&starget->dev);
+       ioc = shost_priv(shost);
+
+       if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
+               spin_lock_irqsave(&ioc->sas_device_lock, flags);
+               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+                  sas_target_priv_data->sas_address);
+               if (sas_device)
+                       sas_device->starget = NULL;
+               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       }
+
        kfree(sdev->hostdata);
        sdev->hostdata = NULL;
 }
@@ -1598,8 +1645,10 @@ _scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device)
  * _scsih_get_volume_capabilities - volume capabilities
  * @ioc: per adapter object
  * @sas_device: the raid_device object
+ *
+ * Returns 0 for success, else 1
  */
-static void
+static int
 _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
     struct _raid_device *raid_device)
 {
@@ -1612,9 +1661,10 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
 
        if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,
            &num_pds)) || !num_pds) {
-               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-                   ioc->name, __FILE__, __LINE__, __func__);
-               return;
+               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                   "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+                   __func__));
+               return 1;
        }
 
        raid_device->num_pds = num_pds;
@@ -1622,17 +1672,19 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
            sizeof(Mpi2RaidVol0PhysDisk_t));
        vol_pg0 = kzalloc(sz, GFP_KERNEL);
        if (!vol_pg0) {
-               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-                   ioc->name, __FILE__, __LINE__, __func__);
-               return;
+               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                   "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+                   __func__));
+               return 1;
        }
 
        if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
             MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
-               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-                   ioc->name, __FILE__, __LINE__, __func__);
+               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                   "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+                   __func__));
                kfree(vol_pg0);
-               return;
+               return 1;
        }
 
        raid_device->volume_type = vol_pg0->VolumeType;
@@ -1652,6 +1704,7 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
        }
 
        kfree(vol_pg0);
+       return 0;
 }
 /**
  * _scsih_disable_ddio - Disable direct I/O for all the volumes
@@ -1922,13 +1975,20 @@ _scsih_slave_configure(struct scsi_device *sdev)
                     sas_target_priv_data->handle);
                spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
                if (!raid_device) {
-                       printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-                           ioc->name, __FILE__, __LINE__, __func__);
-                       return 0;
+                       dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                           "failure at %s:%d/%s()!\n", ioc->name, __FILE__,
+                           __LINE__, __func__));
+                       return 1;
                }
 
                _scsih_get_volume_capabilities(ioc, raid_device);
 
+               if (_scsih_get_volume_capabilities(ioc, raid_device)) {
+                       dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                           "failure at %s:%d/%s()!\n", ioc->name, __FILE__,
+                           __LINE__, __func__));
+                       return 1;
+               }
                /*
                 * WARPDRIVE: Initialize the required data for Direct IO
                 */
@@ -2002,11 +2062,22 @@ _scsih_slave_configure(struct scsi_device *sdev)
        if (sas_device) {
                if (sas_target_priv_data->flags &
                    MPT_TARGET_FLAGS_RAID_COMPONENT) {
-                       mpt2sas_config_get_volume_handle(ioc,
-                           sas_device->handle, &sas_device->volume_handle);
-                       mpt2sas_config_get_volume_wwid(ioc,
+                       if (mpt2sas_config_get_volume_handle(ioc,
+                           sas_device->handle, &sas_device->volume_handle)) {
+                               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                                   "failure at %s:%d/%s()!\n", ioc->name,
+                                   __FILE__, __LINE__, __func__));
+                               return 1;
+                       }
+                       if (sas_device->volume_handle &&
+                           mpt2sas_config_get_volume_wwid(ioc,
                            sas_device->volume_handle,
-                           &sas_device->volume_wwid);
+                           &sas_device->volume_wwid)) {
+                               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                                   "failure at %s:%d/%s()!\n", ioc->name,
+                                   __FILE__, __LINE__, __func__));
+                               return 1;
+                       }
                }
                if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
                        qdepth = MPT2SAS_SAS_QUEUE_DEPTH;
@@ -2035,6 +2106,11 @@ _scsih_slave_configure(struct scsi_device *sdev)
 
                if (!ssp_target)
                        _scsih_display_sata_capabilities(ioc, sas_device, sdev);
+       } else {
+               dfailprintk(ioc, printk(MPT2SAS_WARN_FMT
+                   "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+                   __func__));
+               return 1;
        }
 
        _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
@@ -2161,6 +2237,7 @@ _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
                return 1;
        if (ioc->tm_cmds.smid != smid)
                return 1;
+       mpt2sas_base_flush_reply_queues(ioc);
        ioc->tm_cmds.status |= MPT2_CMD_COMPLETE;
        mpi_reply =  mpt2sas_base_get_reply_virt_addr(ioc, reply);
        if (mpi_reply) {
@@ -2713,22 +2790,43 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
 
 
 /**
- * _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 = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       if (!fw_event)
                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);
 }
@@ -2976,14 +3074,27 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        Mpi2SCSITaskManagementRequest_t *mpi_request;
        u16 smid;
        struct _sas_device *sas_device;
-       struct MPT2SAS_TARGET *sas_target_priv_data;
+       struct MPT2SAS_TARGET *sas_target_priv_data = NULL;
+       u64 sas_address = 0;
        unsigned long flags;
        struct _tr_list *delayed_tr;
+       u32 ioc_state;
 
-       if (ioc->shost_recovery || ioc->remove_host ||
-           ioc->pci_error_recovery) {
-               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
-                  "progress!\n", __func__, ioc->name));
+       if (ioc->remove_host) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been "
+                   "removed: handle(0x%04x)\n", __func__, ioc->name, handle));
+               return;
+       } else if (ioc->pci_error_recovery) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci "
+                   "error recovery: handle(0x%04x)\n", __func__, ioc->name,
+                   handle));
+               return;
+       }
+       ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+       if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not "
+                  "operational: handle(0x%04x)\n", __func__, ioc->name,
+                  handle));
                return;
        }
 
@@ -2997,13 +3108,18 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
             sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
                sas_target_priv_data->deleted = 1;
-               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
-                   "setting delete flag: handle(0x%04x), "
-                   "sas_addr(0x%016llx)\n", ioc->name, handle,
-                   (unsigned long long) sas_device->sas_address));
+               sas_address = sas_device->sas_address;
        }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
+       if (sas_target_priv_data) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "setting delete flag: "
+               "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle,
+                       (unsigned long long)sas_address));
+               _scsih_ublock_io_device(ioc, handle);
+               sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE;
+       }
+
        smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
        if (!smid) {
                delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
@@ -3184,11 +3300,21 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
            mpt2sas_base_get_reply_virt_addr(ioc, reply);
        Mpi2SasIoUnitControlRequest_t *mpi_request;
        u16 smid_sas_ctrl;
+       u32 ioc_state;
 
-       if (ioc->shost_recovery || ioc->remove_host ||
-           ioc->pci_error_recovery) {
-               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
-                  "progress!\n", __func__, ioc->name));
+       if (ioc->remove_host) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been "
+                  "removed\n", __func__, ioc->name));
+               return 1;
+       } else if (ioc->pci_error_recovery) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci "
+                   "error recovery\n", __func__, ioc->name));
+               return 1;
+       }
+       ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+       if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not "
+                   "operational\n", __func__, ioc->name));
                return 1;
        }
 
@@ -5098,7 +5224,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
        /* 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);
@@ -5134,6 +5260,9 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc,
        if (sas_device_backup.starget && sas_device_backup.starget->hostdata) {
                sas_target_priv_data = sas_device_backup.starget->hostdata;
                sas_target_priv_data->deleted = 1;
+               _scsih_ublock_io_device(ioc, sas_device_backup.handle);
+               sas_target_priv_data->handle =
+                    MPT2SAS_INVALID_DEVICE_HANDLE;
        }
 
        _scsih_ublock_io_device(ioc, sas_device_backup.handle);
@@ -5287,7 +5416,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                _scsih_sas_topology_change_event_debug(ioc, event_data);
 #endif
 
-       if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery)
+       if (ioc->remove_host || ioc->pci_error_recovery)
                return;
 
        if (!ioc->sas_hba.num_phys)
@@ -5348,6 +5477,9 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                switch (reason_code) {
                case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
 
+                       if (ioc->shost_recovery)
+                               break;
+
                        if (link_rate == prev_link_rate)
                                break;
 
@@ -5361,6 +5493,9 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
 
+                       if (ioc->shost_recovery)
+                               break;
+
                        mpt2sas_transport_update_links(ioc, sas_address,
                            handle, phy_number, link_rate);
 
@@ -5621,7 +5756,7 @@ broadcast_aen_retry:
        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)
@@ -5643,7 +5778,7 @@ broadcast_aen_retry:
                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);
@@ -5685,7 +5820,7 @@ broadcast_aen_retry:
                        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,
@@ -5724,7 +5859,7 @@ broadcast_aen_retry:
            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);
 }
@@ -5788,8 +5923,11 @@ _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
 static void
 _scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach)
 {
-       struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata;
+       struct MPT2SAS_TARGET *sas_target_priv_data;
 
+       if (starget == NULL)
+               return;
+       sas_target_priv_data = starget->hostdata;
        if (no_uld_attach)
                sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
        else
@@ -5844,7 +5982,7 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,
        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)
@@ -6126,6 +6264,10 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,
                _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;
 
@@ -6184,6 +6326,9 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc,
        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;
 
@@ -6266,6 +6411,9 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
        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;
 
@@ -6509,10 +6657,10 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
        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,
@@ -6531,6 +6679,9 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
                _scsih_mark_responding_sas_device(ioc, sas_address, slot,
                    handle);
        }
+out:
+       printk(MPT2SAS_INFO_FMT "search for end-devices: complete\n",
+           ioc->name);
 }
 
 /**
@@ -6606,10 +6757,14 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)
        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,
@@ -6648,6 +6803,9 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)
                        set_bit(handle, ioc->pd_handles);
                }
        }
+out:
+       printk(MPT2SAS_INFO_FMT "search for responding raid volumes: "
+           "complete\n", ioc->name);
 }
 
 /**
@@ -6707,10 +6865,10 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)
        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,
@@ -6729,6 +6887,8 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)
                _scsih_mark_responding_expander(ioc, sas_address, handle);
        }
 
+ out:
+       printk(MPT2SAS_INFO_FMT "search for expanders: complete\n", ioc->name);
 }
 
 /**
@@ -6744,6 +6904,8 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
        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) {
@@ -6763,6 +6925,9 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
                _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) {
@@ -6789,52 +6954,170 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
                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
@@ -6870,7 +7153,6 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
                }
                _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: "
@@ -6880,6 +7162,13 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
                _scsih_search_responding_sas_devices(ioc);
                _scsih_search_responding_raid_devices(ioc);
                _scsih_search_responding_expanders(ioc);
+               if (!ioc->is_driver_loading) {
+                       _scsih_prep_device_scan(ioc);
+                       _scsih_search_responding_sas_devices(ioc);
+                       _scsih_search_responding_raid_devices(ioc);
+                       _scsih_search_responding_expanders(ioc);
+                       _scsih_error_recovery_delete_devices(ioc);
+               }
                break;
        }
 }
@@ -6897,7 +7186,6 @@ _firmware_event_work(struct work_struct *work)
 {
        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 */
@@ -6907,23 +7195,21 @@ _firmware_event_work(struct work_struct *work)
                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:
+               ioc->start_scan = 0;
 
-       switch (fw_event->event) {
+
+
+               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;
@@ -7120,6 +7406,8 @@ static struct scsi_host_template scsih_driver_template = {
        .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,
@@ -7353,6 +7641,7 @@ _scsih_remove(struct pci_dev *pdev)
        }
 
        sas_remove_host(shost);
+       mpt2sas_base_detach(ioc);
        list_del(&ioc->list);
        scsi_remove_host(shost);
        scsi_host_put(shost);
@@ -7379,7 +7668,12 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
        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;
@@ -7415,8 +7709,9 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
                    sas_device->sas_address_parent)) {
                        _scsih_sas_device_remove(ioc, sas_device);
                } else if (!sas_device->starget) {
-                       mpt2sas_transport_port_remove(ioc, sas_address,
-                           sas_address_parent);
+                       if (!ioc->is_driver_loading)
+                               mpt2sas_transport_port_remove(ioc, sas_address,
+                                       sas_address_parent);
                        _scsih_sas_device_remove(ioc, sas_device);
                }
        }
@@ -7460,22 +7755,28 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
        /* SAS Device List */
        list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
            list) {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               list_move_tail(&sas_device->list, &ioc->sas_device_list);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
                if (ioc->hide_drives)
                        continue;
 
                if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
                    sas_device->sas_address_parent)) {
-                       _scsih_sas_device_remove(ioc, sas_device);
+                       list_del(&sas_device->list);
+                       kfree(sas_device);
+                       continue;
                } 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);
+                       if (!ioc->is_driver_loading)
+                               mpt2sas_transport_port_remove(ioc,
+                                       sas_device->sas_address,
+                                       sas_device->sas_address_parent);
+                       list_del(&sas_device->list);
+                       kfree(sas_device);
+                       continue;
+
                }
+               spin_lock_irqsave(&ioc->sas_device_lock, flags);
+               list_move_tail(&sas_device->list, &ioc->sas_device_list);
+               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 }
 
@@ -7488,9 +7789,7 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
 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 */
@@ -7498,18 +7797,93 @@ _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
        _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
@@ -7546,6 +7920,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        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;
@@ -7618,14 +7993,14 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                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;
@@ -7648,6 +8023,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
  out_thread_fail:
        list_del(&ioc->list);
        scsi_remove_host(shost);
+       scsi_host_put(shost);
  out_add_shost_fail:
        return -ENODEV;
 }
@@ -7894,6 +8270,8 @@ _scsih_init(void)
 
        /* 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(
@@ -7948,6 +8326,7 @@ _scsih_exit(void)
        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);