Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6
[kernel.git] / drivers / scsi / qla1280.c
index 8ef8778..b8166ec 100644 (file)
 * General Public License for more details.
 *
 ******************************************************************************/
-#define QLA1280_VERSION      "3.27"
+#define QLA1280_VERSION      "3.27.1"
 /*****************************************************************************
     Revision History:
+    Rev  3.27.1, February 8, 2010, Michael Reed
+       - Retain firmware image for error recovery.
     Rev  3.27, February 10, 2009, Michael Reed
        - General code cleanup.
        - Improve error recovery.
@@ -537,9 +539,9 @@ __setup("qla1280=", qla1280_setup);
 /*****************************************/
 
 struct qla_boards {
-       unsigned char name[9];  /* Board ID String */
+       char *name;             /* Board ID String */
        int numPorts;           /* Number of SCSI ports */
-       char *fwname;           /* firmware name        */
+       int fw_index;           /* index into qla1280_fw_tbl for firmware */
 };
 
 /* NOTE: the last argument in each entry is used to index ql1280_board_tbl */
@@ -560,15 +562,30 @@ static struct pci_device_id qla1280_pci_tbl[] = {
 };
 MODULE_DEVICE_TABLE(pci, qla1280_pci_tbl);
 
+DEFINE_MUTEX(qla1280_firmware_mutex);
+
+struct qla_fw {
+       char *fwname;
+       const struct firmware *fw;
+};
+
+#define QL_NUM_FW_IMAGES 3
+
+struct qla_fw qla1280_fw_tbl[QL_NUM_FW_IMAGES] = {
+       {"qlogic/1040.bin",  NULL},     /* image 0 */
+       {"qlogic/1280.bin",  NULL},     /* image 1 */
+       {"qlogic/12160.bin", NULL},     /* image 2 */
+};
+
+/* NOTE: Order of boards in this table must match order in qla1280_pci_tbl */
 static struct qla_boards ql1280_board_tbl[] = {
-       /* Name ,  Number of ports, FW details */
-       {"QLA12160",    2, "qlogic/12160.bin"},
-       {"QLA1040",     1, "qlogic/1040.bin"},
-       {"QLA1080",     1, "qlogic/1280.bin"},
-       {"QLA1240",     2, "qlogic/1280.bin"},
-       {"QLA1280",     2, "qlogic/1280.bin"},
-       {"QLA10160",    1, "qlogic/12160.bin"},
-       {"        ",    0, "   "},
+       {.name = "QLA12160", .numPorts = 2, .fw_index = 2},
+       {.name = "QLA1040" , .numPorts = 1, .fw_index = 0},
+       {.name = "QLA1080" , .numPorts = 1, .fw_index = 1},
+       {.name = "QLA1240" , .numPorts = 2, .fw_index = 1},
+       {.name = "QLA1280" , .numPorts = 2, .fw_index = 1},
+       {.name = "QLA10160", .numPorts = 1, .fw_index = 2},
+       {.name = "        ", .numPorts = 0, .fw_index = -1},
 };
 
 static int qla1280_verbose = 1;
@@ -1510,6 +1527,63 @@ qla1280_initialize_adapter(struct scsi_qla_host *ha)
        return status;
 }
 
+/*
+ * qla1280_request_firmware
+ *      Acquire firmware for chip.  Retain in memory
+ *      for error recovery.
+ *
+ * Input:
+ *      ha = adapter block pointer.
+ *
+ * Returns:
+ *      Pointer to firmware image or an error code
+ *      cast to pointer via ERR_PTR().
+ */
+static const struct firmware *
+qla1280_request_firmware(struct scsi_qla_host *ha)
+{
+       const struct firmware *fw;
+       int err;
+       int index;
+       char *fwname;
+
+       spin_unlock_irq(ha->host->host_lock);
+       mutex_lock(&qla1280_firmware_mutex);
+
+       index = ql1280_board_tbl[ha->devnum].fw_index;
+       fw = qla1280_fw_tbl[index].fw;
+       if (fw)
+               goto out;
+
+       fwname = qla1280_fw_tbl[index].fwname;
+       err = request_firmware(&fw, fwname, &ha->pdev->dev);
+
+       if (err) {
+               printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
+                      fwname, err);
+               fw = ERR_PTR(err);
+               goto unlock;
+       }
+       if ((fw->size % 2) || (fw->size < 6)) {
+               printk(KERN_ERR "Invalid firmware length %zu in image \"%s\"\n",
+                      fw->size, fwname);
+               release_firmware(fw);
+               fw = ERR_PTR(-EINVAL);
+               goto unlock;
+       }
+
+       qla1280_fw_tbl[index].fw = fw;
+
+ out:
+       ha->fwver1 = fw->data[0];
+       ha->fwver2 = fw->data[1];
+       ha->fwver3 = fw->data[2];
+ unlock:
+       mutex_unlock(&qla1280_firmware_mutex);
+       spin_lock_irq(ha->host->host_lock);
+       return fw;
+}
+
 /*
  * Chip diagnostics
  *      Test chip for proper operation.
@@ -1633,30 +1707,18 @@ qla1280_chip_diag(struct scsi_qla_host *ha)
 static int
 qla1280_load_firmware_pio(struct scsi_qla_host *ha)
 {
+       /* enter with host_lock acquired */
+
        const struct firmware *fw;
        const __le16 *fw_data;
        uint16_t risc_address, risc_code_size;
        uint16_t mb[MAILBOX_REGISTER_COUNT], i;
-       int err;
+       int err = 0;
+
+       fw = qla1280_request_firmware(ha);
+       if (IS_ERR(fw))
+               return PTR_ERR(fw);
 
-       spin_unlock_irq(ha->host->host_lock);
-       err = request_firmware(&fw, ql1280_board_tbl[ha->devnum].fwname,
-                              &ha->pdev->dev);
-       spin_lock_irq(ha->host->host_lock);
-       if (err) {
-               printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
-                      ql1280_board_tbl[ha->devnum].fwname, err);
-               return err;
-       }
-       if ((fw->size % 2) || (fw->size < 6)) {
-               printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
-                      fw->size, ql1280_board_tbl[ha->devnum].fwname);
-               err = -EINVAL;
-               goto out;
-       }
-       ha->fwver1 = fw->data[0];
-       ha->fwver2 = fw->data[1];
-       ha->fwver3 = fw->data[2];
        fw_data = (const __le16 *)&fw->data[0];
        ha->fwstart = __le16_to_cpu(fw_data[2]);
 
@@ -1674,11 +1736,10 @@ qla1280_load_firmware_pio(struct scsi_qla_host *ha)
                if (err) {
                        printk(KERN_ERR "scsi(%li): Failed to load firmware\n",
                                        ha->host_no);
-                       goto out;
+                       break;
                }
        }
-out:
-       release_firmware(fw);
+
        return err;
 }
 
@@ -1686,6 +1747,7 @@ out:
 static int
 qla1280_load_firmware_dma(struct scsi_qla_host *ha)
 {
+       /* enter with host_lock acquired */
        const struct firmware *fw;
        const __le16 *fw_data;
        uint16_t risc_address, risc_code_size;
@@ -1700,24 +1762,10 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
                return -ENOMEM;
 #endif
 
-       spin_unlock_irq(ha->host->host_lock);
-       err = request_firmware(&fw, ql1280_board_tbl[ha->devnum].fwname,
-                              &ha->pdev->dev);
-       spin_lock_irq(ha->host->host_lock);
-       if (err) {
-               printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
-                      ql1280_board_tbl[ha->devnum].fwname, err);
-               return err;
-       }
-       if ((fw->size % 2) || (fw->size < 6)) {
-               printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
-                      fw->size, ql1280_board_tbl[ha->devnum].fwname);
-               err = -EINVAL;
-               goto out;
-       }
-       ha->fwver1 = fw->data[0];
-       ha->fwver2 = fw->data[1];
-       ha->fwver3 = fw->data[2];
+       fw = qla1280_request_firmware(ha);
+       if (IS_ERR(fw))
+               return PTR_ERR(fw);
+
        fw_data = (const __le16 *)&fw->data[0];
        ha->fwstart = __le16_to_cpu(fw_data[2]);
 
@@ -1802,7 +1850,6 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
 #if DUMP_IT_BACK
        pci_free_consistent(ha->pdev, 8000, tbuf, p_tbuf);
 #endif
-       release_firmware(fw);
        return err;
 }
 
@@ -1841,6 +1888,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha)
 static int
 qla1280_load_firmware(struct scsi_qla_host *ha)
 {
+       /* enter with host_lock taken */
        int err;
 
        err = qla1280_chip_diag(ha);
@@ -4419,7 +4467,16 @@ qla1280_init(void)
 static void __exit
 qla1280_exit(void)
 {
+       int i;
+
        pci_unregister_driver(&qla1280_pci_driver);
+       /* release any allocated firmware images */
+       for (i = 0; i < QL_NUM_FW_IMAGES; i++) {
+               if (qla1280_fw_tbl[i].fw) {
+                       release_firmware(qla1280_fw_tbl[i].fw);
+                       qla1280_fw_tbl[i].fw = NULL;
+               }
+       }
 }
 
 module_init(qla1280_init);