|
@@ -17,9 +17,11 @@
|
|
|
* 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);
|