]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/hpsa.c
Merge tag 'pinctrl-v3.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[karo-tx-linux.git] / drivers / scsi / hpsa.c
index a02ea7f43d675c03586d961e614aa522b6ae9b46..95d581c45413fb38dd778e2b4695e7f7159c9c0b 100644 (file)
 #define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
 #define HPSA "hpsa"
 
-/* How long to wait (in milliseconds) for board to go into simple mode */
-#define MAX_CONFIG_WAIT 30000
+/* How long to wait for CISS doorbell communication */
+#define CLEAR_EVENT_WAIT_INTERVAL 20   /* ms for each msleep() call */
+#define MODE_CHANGE_WAIT_INTERVAL 10   /* ms for each msleep() call */
+#define MAX_CLEAR_EVENT_WAIT 30000     /* times 20 ms = 600 s */
+#define MAX_MODE_CHANGE_WAIT 2000      /* times 10 ms = 20 s */
 #define MAX_IOCTL_CONFIG_WAIT 1000
 
 /*define how many times we will try a command because of bus resets */
@@ -165,24 +168,24 @@ static struct board_type products[] = {
        {0x1926103C, "Smart Array P731m", &SA5_access},
        {0x1928103C, "Smart Array P230i", &SA5_access},
        {0x1929103C, "Smart Array P530", &SA5_access},
-       {0x21BD103C, "Smart Array", &SA5_access},
-       {0x21BE103C, "Smart Array", &SA5_access},
-       {0x21BF103C, "Smart Array", &SA5_access},
-       {0x21C0103C, "Smart Array", &SA5_access},
-       {0x21C1103C, "Smart Array", &SA5_access},
-       {0x21C2103C, "Smart Array", &SA5_access},
-       {0x21C3103C, "Smart Array", &SA5_access},
+       {0x21BD103C, "Smart Array P244br", &SA5_access},
+       {0x21BE103C, "Smart Array P741m", &SA5_access},
+       {0x21BF103C, "Smart HBA H240ar", &SA5_access},
+       {0x21C0103C, "Smart Array P440ar", &SA5_access},
+       {0x21C1103C, "Smart Array P840ar", &SA5_access},
+       {0x21C2103C, "Smart Array P440", &SA5_access},
+       {0x21C3103C, "Smart Array P441", &SA5_access},
        {0x21C4103C, "Smart Array", &SA5_access},
-       {0x21C5103C, "Smart Array", &SA5_access},
-       {0x21C6103C, "Smart Array", &SA5_access},
-       {0x21C7103C, "Smart Array", &SA5_access},
-       {0x21C8103C, "Smart Array", &SA5_access},
+       {0x21C5103C, "Smart Array P841", &SA5_access},
+       {0x21C6103C, "Smart HBA H244br", &SA5_access},
+       {0x21C7103C, "Smart HBA H240", &SA5_access},
+       {0x21C8103C, "Smart HBA H241", &SA5_access},
        {0x21C9103C, "Smart Array", &SA5_access},
-       {0x21CA103C, "Smart Array", &SA5_access},
-       {0x21CB103C, "Smart Array", &SA5_access},
+       {0x21CA103C, "Smart Array P246br", &SA5_access},
+       {0x21CB103C, "Smart Array P840", &SA5_access},
        {0x21CC103C, "Smart Array", &SA5_access},
        {0x21CD103C, "Smart Array", &SA5_access},
-       {0x21CE103C, "Smart Array", &SA5_access},
+       {0x21CE103C, "Smart HBA", &SA5_access},
        {0x00761590, "HP Storage P1224 Array Controller", &SA5_access},
        {0x00871590, "HP Storage P1224e Array Controller", &SA5_access},
        {0x007D1590, "HP Storage P1228 Array Controller", &SA5_access},
@@ -240,7 +243,7 @@ static int hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id);
 static int hpsa_wait_for_board_state(struct pci_dev *pdev, void __iomem *vaddr,
                                     int wait_for_ready);
 static inline void finish_cmd(struct CommandList *c);
-static void hpsa_wait_for_mode_change_ack(struct ctlr_info *h);
+static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h);
 #define BOARD_NOT_READY 0
 #define BOARD_READY 1
 static void hpsa_drain_accel_commands(struct ctlr_info *h);
@@ -821,19 +824,21 @@ static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h,
 static void enqueue_cmd_and_start_io(struct ctlr_info *h,
        struct CommandList *c)
 {
+       dial_down_lockup_detection_during_fw_flash(h, c);
+       atomic_inc(&h->commands_outstanding);
        switch (c->cmd_type) {
        case CMD_IOACCEL1:
                set_ioaccel1_performant_mode(h, c);
+               writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
                break;
        case CMD_IOACCEL2:
                set_ioaccel2_performant_mode(h, c);
+               writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
                break;
        default:
                set_performant_mode(h, c);
+               h->access.submit_command(h, c);
        }
-       dial_down_lockup_detection_during_fw_flash(h, c);
-       atomic_inc(&h->commands_outstanding);
-       h->access.submit_command(h, c);
 }
 
 static inline int is_hba_lunid(unsigned char scsi3addr[])
@@ -1764,7 +1769,7 @@ static void complete_scsi_command(struct CommandList *cp)
        unsigned long sense_data_size;
 
        ei = cp->err_info;
-       cmd = (struct scsi_cmnd *) cp->scsi_cmd;
+       cmd = cp->scsi_cmd;
        h = cp->h;
        dev = cmd->device->hostdata;
 
@@ -1882,9 +1887,8 @@ static void complete_scsi_command(struct CommandList *cp)
        case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
                break;
        case CMD_DATA_OVERRUN:
-               dev_warn(&h->pdev->dev, "cp %p has"
-                       " completed with data overrun "
-                       "reported\n", cp);
+               dev_warn(&h->pdev->dev,
+                       "CDB %16phN data overrun\n", cp->Request.CDB);
                break;
        case CMD_INVALID: {
                /* print_bytes(cp, sizeof(*cp), 1, 0);
@@ -1900,34 +1904,38 @@ static void complete_scsi_command(struct CommandList *cp)
                break;
        case CMD_PROTOCOL_ERR:
                cmd->result = DID_ERROR << 16;
-               dev_warn(&h->pdev->dev, "cp %p has "
-                       "protocol error\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN : protocol error\n",
+                               cp->Request.CDB);
                break;
        case CMD_HARDWARE_ERR:
                cmd->result = DID_ERROR << 16;
-               dev_warn(&h->pdev->dev, "cp %p had  hardware error\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN : hardware error\n",
+                       cp->Request.CDB);
                break;
        case CMD_CONNECTION_LOST:
                cmd->result = DID_ERROR << 16;
-               dev_warn(&h->pdev->dev, "cp %p had connection lost\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN : connection lost\n",
+                       cp->Request.CDB);
                break;
        case CMD_ABORTED:
                cmd->result = DID_ABORT << 16;
-               dev_warn(&h->pdev->dev, "cp %p was aborted with status 0x%x\n",
-                               cp, ei->ScsiStatus);
+               dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
+                               cp->Request.CDB, ei->ScsiStatus);
                break;
        case CMD_ABORT_FAILED:
                cmd->result = DID_ERROR << 16;
-               dev_warn(&h->pdev->dev, "cp %p reports abort failed\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n",
+                       cp->Request.CDB);
                break;
        case CMD_UNSOLICITED_ABORT:
                cmd->result = DID_SOFT_ERROR << 16; /* retry the command */
-               dev_warn(&h->pdev->dev, "cp %p aborted due to an unsolicited "
-                       "abort\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN : unsolicited abort\n",
+                       cp->Request.CDB);
                break;
        case CMD_TIMEOUT:
                cmd->result = DID_TIME_OUT << 16;
-               dev_warn(&h->pdev->dev, "cp %p timedout\n", cp);
+               dev_warn(&h->pdev->dev, "CDB %16phN timed out\n",
+                       cp->Request.CDB);
                break;
        case CMD_UNABORTABLE:
                cmd->result = DID_ERROR << 16;
@@ -3231,6 +3239,17 @@ out:
        kfree(id_phys);
 }
 
+static void hpsa_set_sg_descriptor(struct SGDescriptor *desc,
+                                  struct scatterlist *sg)
+{
+       u64 addr64 = (u64) sg_dma_address(sg);
+       unsigned int len = sg_dma_len(sg);
+
+       desc->Addr = cpu_to_le64(addr64);
+       desc->Len = cpu_to_le32(len);
+       desc->Ext = 0;
+}
+
 /*
  * hpsa_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
  * dma mapping  and fills in the scatter gather entries of the
@@ -3240,9 +3259,7 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
                struct CommandList *cp,
                struct scsi_cmnd *cmd)
 {
-       unsigned int len;
        struct scatterlist *sg;
-       u64 addr64;
        int use_sg, i, sg_index, chained;
        struct SGDescriptor *curr_sg;
 
@@ -3265,13 +3282,11 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
                        curr_sg = h->cmd_sg_list[cp->cmdindex];
                        sg_index = 0;
                }
-               addr64 = (u64) sg_dma_address(sg);
-               len  = sg_dma_len(sg);
-               curr_sg->Addr = cpu_to_le64(addr64);
-               curr_sg->Len = cpu_to_le32(len);
-               curr_sg->Ext = cpu_to_le32(0);
+               hpsa_set_sg_descriptor(curr_sg, sg);
                curr_sg++;
        }
+
+       /* Back the pointer up to the last entry and mark it as "last". */
        (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
 
        if (use_sg + chained > h->maxSG)
@@ -4137,25 +4152,14 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
        return hpsa_ciss_submit(h, c, cmd, scsi3addr);
 }
 
-static int do_not_scan_if_controller_locked_up(struct ctlr_info *h)
+static void hpsa_scan_complete(struct ctlr_info *h)
 {
        unsigned long flags;
 
-       /*
-        * Don't let rescans be initiated on a controller known
-        * to be locked up.  If the controller locks up *during*
-        * a rescan, that thread is probably hosed, but at least
-        * we can prevent new rescan threads from piling up on a
-        * locked up controller.
-        */
-       if (unlikely(lockup_detected(h))) {
-               spin_lock_irqsave(&h->scan_lock, flags);
-               h->scan_finished = 1;
-               wake_up_all(&h->scan_wait_queue);
-               spin_unlock_irqrestore(&h->scan_lock, flags);
-               return 1;
-       }
-       return 0;
+       spin_lock_irqsave(&h->scan_lock, flags);
+       h->scan_finished = 1;
+       wake_up_all(&h->scan_wait_queue);
+       spin_unlock_irqrestore(&h->scan_lock, flags);
 }
 
 static void hpsa_scan_start(struct Scsi_Host *sh)
@@ -4163,8 +4167,14 @@ static void hpsa_scan_start(struct Scsi_Host *sh)
        struct ctlr_info *h = shost_to_hba(sh);
        unsigned long flags;
 
-       if (do_not_scan_if_controller_locked_up(h))
-               return;
+       /*
+        * Don't let rescans be initiated on a controller known to be locked
+        * up.  If the controller locks up *during* a rescan, that thread is
+        * probably hosed, but at least we can prevent new rescan threads from
+        * piling up on a locked up controller.
+        */
+       if (unlikely(lockup_detected(h)))
+               return hpsa_scan_complete(h);
 
        /* wait until any scan already in progress is finished. */
        while (1) {
@@ -4182,15 +4192,12 @@ static void hpsa_scan_start(struct Scsi_Host *sh)
        h->scan_finished = 0; /* mark scan as in progress */
        spin_unlock_irqrestore(&h->scan_lock, flags);
 
-       if (do_not_scan_if_controller_locked_up(h))
-               return;
+       if (unlikely(lockup_detected(h)))
+               return hpsa_scan_complete(h);
 
        hpsa_update_scsi_devices(h, h->scsi_host->host_no);
 
-       spin_lock_irqsave(&h->scan_lock, flags);
-       h->scan_finished = 1; /* mark scan as finished. */
-       wake_up_all(&h->scan_wait_queue);
-       spin_unlock_irqrestore(&h->scan_lock, flags);
+       hpsa_scan_complete(h);
 }
 
 static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth)
@@ -4466,7 +4473,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
        unsigned char *psa = &phys_scsi3addr[0];
 
        /* Get a pointer to the hpsa logical device. */
-       scmd = (struct scsi_cmnd *) abort->scsi_cmd;
+       scmd = abort->scsi_cmd;
        dev = (struct hpsa_scsi_dev_t *)(scmd->device->hostdata);
        if (dev == NULL) {
                dev_warn(&h->pdev->dev,
@@ -4604,7 +4611,7 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
        }
        hpsa_get_tag(h, abort, &taglower, &tagupper);
        ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower);
-       as  = (struct scsi_cmnd *) abort->scsi_cmd;
+       as  = abort->scsi_cmd;
        if (as != NULL)
                ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ",
                        as->cmnd[0], as->serial_number);
@@ -6184,24 +6191,27 @@ static inline void hpsa_p600_dma_prefetch_quirk(struct ctlr_info *h)
        writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG);
 }
 
-static void hpsa_wait_for_clear_event_notify_ack(struct ctlr_info *h)
+static int hpsa_wait_for_clear_event_notify_ack(struct ctlr_info *h)
 {
        int i;
        u32 doorbell_value;
        unsigned long flags;
        /* wait until the clear_event_notify bit 6 is cleared by controller. */
-       for (i = 0; i < MAX_CONFIG_WAIT; i++) {
+       for (i = 0; i < MAX_CLEAR_EVENT_WAIT; i++) {
                spin_lock_irqsave(&h->lock, flags);
                doorbell_value = readl(h->vaddr + SA5_DOORBELL);
                spin_unlock_irqrestore(&h->lock, flags);
                if (!(doorbell_value & DOORBELL_CLEAR_EVENTS))
-                       break;
+                       goto done;
                /* delay and try again */
-               msleep(20);
+               msleep(CLEAR_EVENT_WAIT_INTERVAL);
        }
+       return -ENODEV;
+done:
+       return 0;
 }
 
-static void hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
+static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
 {
        int i;
        u32 doorbell_value;
@@ -6211,17 +6221,21 @@ static void hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
         * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
         * as we enter this code.)
         */
-       for (i = 0; i < MAX_CONFIG_WAIT; i++) {
+       for (i = 0; i < MAX_MODE_CHANGE_WAIT; i++) {
                spin_lock_irqsave(&h->lock, flags);
                doorbell_value = readl(h->vaddr + SA5_DOORBELL);
                spin_unlock_irqrestore(&h->lock, flags);
                if (!(doorbell_value & CFGTBL_ChangeReq))
-                       break;
+                       goto done;
                /* delay and try again */
-               usleep_range(10000, 20000);
+               msleep(MODE_CHANGE_WAIT_INTERVAL);
        }
+       return -ENODEV;
+done:
+       return 0;
 }
 
+/* return -ENODEV or other reason on error, 0 on success */
 static int hpsa_enter_simple_mode(struct ctlr_info *h)
 {
        u32 trans_support;
@@ -6236,7 +6250,8 @@ static int hpsa_enter_simple_mode(struct ctlr_info *h)
        writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest));
        writel(0, &h->cfgtable->HostWrite.command_pool_addr_hi);
        writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
-       hpsa_wait_for_mode_change_ack(h);
+       if (hpsa_wait_for_mode_change_ack(h))
+               goto error;
        print_cfg_table(&h->pdev->dev, h->cfgtable);
        if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple))
                goto error;
@@ -6772,14 +6787,14 @@ static int hpsa_offline_devices_ready(struct ctlr_info *h)
        return 0;
 }
 
-
-static void hpsa_monitor_ctlr_worker(struct work_struct *work)
+static void hpsa_rescan_ctlr_worker(struct work_struct *work)
 {
        unsigned long flags;
        struct ctlr_info *h = container_of(to_delayed_work(work),
-                                       struct ctlr_info, monitor_ctlr_work);
-       detect_controller_lockup(h);
-       if (lockup_detected(h))
+                                       struct ctlr_info, rescan_ctlr_work);
+
+
+       if (h->remove_in_progress)
                return;
 
        if (hpsa_ctlr_needs_rescan(h) || hpsa_offline_devices_ready(h)) {
@@ -6788,17 +6803,44 @@ static void hpsa_monitor_ctlr_worker(struct work_struct *work)
                hpsa_scan_start(h->scsi_host);
                scsi_host_put(h->scsi_host);
        }
-
        spin_lock_irqsave(&h->lock, flags);
-       if (h->remove_in_progress) {
-               spin_unlock_irqrestore(&h->lock, flags);
+       if (!h->remove_in_progress)
+               queue_delayed_work(h->rescan_ctlr_wq, &h->rescan_ctlr_work,
+                               h->heartbeat_sample_interval);
+       spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static void hpsa_monitor_ctlr_worker(struct work_struct *work)
+{
+       unsigned long flags;
+       struct ctlr_info *h = container_of(to_delayed_work(work),
+                                       struct ctlr_info, monitor_ctlr_work);
+
+       detect_controller_lockup(h);
+       if (lockup_detected(h))
                return;
-       }
-       schedule_delayed_work(&h->monitor_ctlr_work,
+
+       spin_lock_irqsave(&h->lock, flags);
+       if (!h->remove_in_progress)
+               schedule_delayed_work(&h->monitor_ctlr_work,
                                h->heartbeat_sample_interval);
        spin_unlock_irqrestore(&h->lock, flags);
 }
 
+static struct workqueue_struct *hpsa_create_controller_wq(struct ctlr_info *h,
+                                               char *name)
+{
+       struct workqueue_struct *wq = NULL;
+       char wq_name[20];
+
+       snprintf(wq_name, sizeof(wq_name), "%s_%d_hpsa", name, h->ctlr);
+       wq = alloc_ordered_workqueue(wq_name, 0);
+       if (!wq)
+               dev_err(&h->pdev->dev, "failed to create %s workqueue\n", name);
+
+       return wq;
+}
+
 static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        int dac, rc;
@@ -6841,12 +6883,18 @@ reinit_after_soft_reset:
        spin_lock_init(&h->scan_lock);
        atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS);
 
-       h->resubmit_wq = alloc_workqueue("hpsa", WQ_MEM_RECLAIM, 0);
+       h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
+       if (!h->rescan_ctlr_wq) {
+               rc = -ENOMEM;
+               goto clean1;
+       }
+
+       h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
        if (!h->resubmit_wq) {
-               dev_err(&h->pdev->dev, "Failed to allocate work queue\n");
                rc = -ENOMEM;
                goto clean1;
        }
+
        /* Allocate and clear per-cpu variable lockup_detected */
        h->lockup_detected = alloc_percpu(u32);
        if (!h->lockup_detected) {
@@ -6970,6 +7018,9 @@ reinit_after_soft_reset:
        INIT_DELAYED_WORK(&h->monitor_ctlr_work, hpsa_monitor_ctlr_worker);
        schedule_delayed_work(&h->monitor_ctlr_work,
                                h->heartbeat_sample_interval);
+       INIT_DELAYED_WORK(&h->rescan_ctlr_work, hpsa_rescan_ctlr_worker);
+       queue_delayed_work(h->rescan_ctlr_wq, &h->rescan_ctlr_work,
+                               h->heartbeat_sample_interval);
        return 0;
 
 clean4:
@@ -6981,6 +7032,8 @@ clean2:
 clean1:
        if (h->resubmit_wq)
                destroy_workqueue(h->resubmit_wq);
+       if (h->rescan_ctlr_wq)
+               destroy_workqueue(h->rescan_ctlr_wq);
        if (h->lockup_detected)
                free_percpu(h->lockup_detected);
        kfree(h);
@@ -7054,11 +7107,13 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        /* Get rid of any controller monitoring work items */
        spin_lock_irqsave(&h->lock, flags);
        h->remove_in_progress = 1;
-       cancel_delayed_work(&h->monitor_ctlr_work);
        spin_unlock_irqrestore(&h->lock, flags);
+       cancel_delayed_work_sync(&h->monitor_ctlr_work);
+       cancel_delayed_work_sync(&h->rescan_ctlr_work);
+       destroy_workqueue(h->rescan_ctlr_wq);
+       destroy_workqueue(h->resubmit_wq);
        hpsa_unregister_scsi(h);        /* unhook from SCSI subsystem */
        hpsa_shutdown(pdev);
-       destroy_workqueue(h->resubmit_wq);
        iounmap(h->vaddr);
        iounmap(h->transtable);
        iounmap(h->cfgtable);
@@ -7137,7 +7192,8 @@ static void  calc_bucket_map(int bucket[], int num_buckets,
        }
 }
 
-static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
+/* return -ENODEV or other reason on error, 0 on success */
+static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
 {
        int i;
        unsigned long register_value;
@@ -7229,12 +7285,16 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
                }
        }
        writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
-       hpsa_wait_for_mode_change_ack(h);
+       if (hpsa_wait_for_mode_change_ack(h)) {
+               dev_err(&h->pdev->dev,
+                       "performant mode problem - doorbell timeout\n");
+               return -ENODEV;
+       }
        register_value = readl(&(h->cfgtable->TransportActive));
        if (!(register_value & CFGTBL_Trans_Performant)) {
                dev_err(&h->pdev->dev,
                        "performant mode problem - transport not active\n");
-               return;
+               return -ENODEV;
        }
        /* Change the access methods to the performant access methods */
        h->access = access;
@@ -7242,7 +7302,7 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
 
        if (!((trans_support & CFGTBL_Trans_io_accel1) ||
                (trans_support & CFGTBL_Trans_io_accel2)))
-               return;
+               return 0;
 
        if (trans_support & CFGTBL_Trans_io_accel1) {
                /* Set up I/O accelerator mode */
@@ -7306,7 +7366,12 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
                        writel(bft2[i], &h->ioaccel2_bft2_regs[i]);
        }
        writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
-       hpsa_wait_for_mode_change_ack(h);
+       if (hpsa_wait_for_mode_change_ack(h)) {
+               dev_err(&h->pdev->dev,
+                       "performant mode problem - enabling ioaccel mode\n");
+               return -ENODEV;
+       }
+       return 0;
 }
 
 static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)