[SCSI] qla4xxx: Capture minidump for ISP82XX on firmware failure
Added support to capture dump (Minidump) which allows us to catpure a snapshot of the firmware/hardware states at the time of firmware failure Signed-off-by: Tej Parkash <tej.parkash@qlogic.com> Signed-off-by: Shyam Sundar <shyam.sundar@qlogic.com> Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
parent
f7b4aa6365
commit
068237c87c
|
@ -9,6 +9,140 @@
|
||||||
#include "ql4_glbl.h"
|
#include "ql4_glbl.h"
|
||||||
#include "ql4_dbg.h"
|
#include "ql4_dbg.h"
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
qla4_8xxx_sysfs_read_fw_dump(struct file *filep, struct kobject *kobj,
|
||||||
|
struct bin_attribute *ba, char *buf, loff_t off,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
|
||||||
|
struct device, kobj)));
|
||||||
|
|
||||||
|
if (!is_qla8022(ha))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!test_bit(AF_82XX_DUMP_READING, &ha->flags))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
|
||||||
|
ha->fw_dump_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
qla4_8xxx_sysfs_write_fw_dump(struct file *filep, struct kobject *kobj,
|
||||||
|
struct bin_attribute *ba, char *buf, loff_t off,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
|
||||||
|
struct device, kobj)));
|
||||||
|
uint32_t dev_state;
|
||||||
|
long reading;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!is_qla8022(ha))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (off != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
buf[1] = 0;
|
||||||
|
ret = kstrtol(buf, 10, &reading);
|
||||||
|
if (ret) {
|
||||||
|
ql4_printk(KERN_ERR, ha, "%s: Invalid input. Return err %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reading) {
|
||||||
|
case 0:
|
||||||
|
/* clear dump collection flags */
|
||||||
|
if (test_and_clear_bit(AF_82XX_DUMP_READING, &ha->flags)) {
|
||||||
|
clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
|
||||||
|
/* Reload minidump template */
|
||||||
|
qla4xxx_alloc_fw_dump(ha);
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"Firmware template reloaded\n"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* Set flag to read dump */
|
||||||
|
if (test_bit(AF_82XX_FW_DUMPED, &ha->flags) &&
|
||||||
|
!test_bit(AF_82XX_DUMP_READING, &ha->flags)) {
|
||||||
|
set_bit(AF_82XX_DUMP_READING, &ha->flags);
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"Raw firmware dump ready for read on (%ld).\n",
|
||||||
|
ha->host_no));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* Reset HBA */
|
||||||
|
qla4_8xxx_idc_lock(ha);
|
||||||
|
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
||||||
|
if (dev_state == QLA82XX_DEV_READY) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s: Setting Need reset, reset_owner is 0x%x.\n",
|
||||||
|
__func__, ha->func_num);
|
||||||
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
||||||
|
QLA82XX_DEV_NEED_RESET);
|
||||||
|
set_bit(AF_82XX_RST_OWNER, &ha->flags);
|
||||||
|
} else
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s: Reset not performed as device state is 0x%x\n",
|
||||||
|
__func__, dev_state);
|
||||||
|
|
||||||
|
qla4_8xxx_idc_unlock(ha);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bin_attribute sysfs_fw_dump_attr = {
|
||||||
|
.attr = {
|
||||||
|
.name = "fw_dump",
|
||||||
|
.mode = S_IRUSR | S_IWUSR,
|
||||||
|
},
|
||||||
|
.size = 0,
|
||||||
|
.read = qla4_8xxx_sysfs_read_fw_dump,
|
||||||
|
.write = qla4_8xxx_sysfs_write_fw_dump,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sysfs_entry {
|
||||||
|
char *name;
|
||||||
|
struct bin_attribute *attr;
|
||||||
|
} bin_file_entries[] = {
|
||||||
|
{ "fw_dump", &sysfs_fw_dump_attr },
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *host = ha->host;
|
||||||
|
struct sysfs_entry *iter;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (iter = bin_file_entries; iter->name; iter++) {
|
||||||
|
ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
|
||||||
|
iter->attr);
|
||||||
|
if (ret)
|
||||||
|
ql4_printk(KERN_ERR, ha,
|
||||||
|
"Unable to create sysfs %s binary attribute (%d).\n",
|
||||||
|
iter->name, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *host = ha->host;
|
||||||
|
struct sysfs_entry *iter;
|
||||||
|
|
||||||
|
for (iter = bin_file_entries; iter->name; iter++)
|
||||||
|
sysfs_remove_bin_file(&host->shost_gendev.kobj,
|
||||||
|
iter->attr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Scsi_Host attributes. */
|
/* Scsi_Host attributes. */
|
||||||
static ssize_t
|
static ssize_t
|
||||||
qla4xxx_fw_version_show(struct device *dev,
|
qla4xxx_fw_version_show(struct device *dev,
|
||||||
|
|
|
@ -398,6 +398,16 @@ struct isp_operations {
|
||||||
int (*get_sys_info) (struct scsi_qla_host *);
|
int (*get_sys_info) (struct scsi_qla_host *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ql4_mdump_size_table {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t size_cmask_02;
|
||||||
|
uint32_t size_cmask_04;
|
||||||
|
uint32_t size_cmask_08;
|
||||||
|
uint32_t size_cmask_10;
|
||||||
|
uint32_t size_cmask_FF;
|
||||||
|
uint32_t version;
|
||||||
|
};
|
||||||
|
|
||||||
/*qla4xxx ipaddress configuration details */
|
/*qla4xxx ipaddress configuration details */
|
||||||
struct ipaddress_config {
|
struct ipaddress_config {
|
||||||
uint16_t ipv4_options;
|
uint16_t ipv4_options;
|
||||||
|
@ -485,6 +495,10 @@ struct scsi_qla_host {
|
||||||
#define AF_EEH_BUSY 20 /* 0x00100000 */
|
#define AF_EEH_BUSY 20 /* 0x00100000 */
|
||||||
#define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */
|
#define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */
|
||||||
#define AF_BUILD_DDB_LIST 22 /* 0x00400000 */
|
#define AF_BUILD_DDB_LIST 22 /* 0x00400000 */
|
||||||
|
#define AF_82XX_FW_DUMPED 24 /* 0x01000000 */
|
||||||
|
#define AF_82XX_RST_OWNER 25 /* 0x02000000 */
|
||||||
|
#define AF_82XX_DUMP_READING 26 /* 0x04000000 */
|
||||||
|
|
||||||
unsigned long dpc_flags;
|
unsigned long dpc_flags;
|
||||||
|
|
||||||
#define DPC_RESET_HA 1 /* 0x00000002 */
|
#define DPC_RESET_HA 1 /* 0x00000002 */
|
||||||
|
@ -662,6 +676,11 @@ struct scsi_qla_host {
|
||||||
|
|
||||||
uint32_t nx_dev_init_timeout;
|
uint32_t nx_dev_init_timeout;
|
||||||
uint32_t nx_reset_timeout;
|
uint32_t nx_reset_timeout;
|
||||||
|
void *fw_dump;
|
||||||
|
uint32_t fw_dump_size;
|
||||||
|
uint32_t fw_dump_capture_mask;
|
||||||
|
void *fw_dump_tmplt_hdr;
|
||||||
|
uint32_t fw_dump_tmplt_size;
|
||||||
|
|
||||||
struct completion mbx_intr_comp;
|
struct completion mbx_intr_comp;
|
||||||
|
|
||||||
|
@ -936,4 +955,7 @@ static inline int ql4xxx_reset_active(struct scsi_qla_host *ha)
|
||||||
#define PROCESS_ALL_AENS 0
|
#define PROCESS_ALL_AENS 0
|
||||||
#define FLUSH_DDB_CHANGED_AENS 1
|
#define FLUSH_DDB_CHANGED_AENS 1
|
||||||
|
|
||||||
|
/* Defines for udev events */
|
||||||
|
#define QL4_UEVENT_CODE_FW_DUMP 0
|
||||||
|
|
||||||
#endif /*_QLA4XXX_H */
|
#endif /*_QLA4XXX_H */
|
||||||
|
|
|
@ -385,6 +385,11 @@ struct qla_flt_region {
|
||||||
#define MBOX_CMD_GET_IP_ADDR_STATE 0x0091
|
#define MBOX_CMD_GET_IP_ADDR_STATE 0x0091
|
||||||
#define MBOX_CMD_SEND_IPV6_ROUTER_SOL 0x0092
|
#define MBOX_CMD_SEND_IPV6_ROUTER_SOL 0x0092
|
||||||
#define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR 0x0093
|
#define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR 0x0093
|
||||||
|
#define MBOX_CMD_MINIDUMP 0x0129
|
||||||
|
|
||||||
|
/* Minidump subcommand */
|
||||||
|
#define MINIDUMP_GET_SIZE_SUBCOMMAND 0x00
|
||||||
|
#define MINIDUMP_GET_TMPLT_SUBCOMMAND 0x01
|
||||||
|
|
||||||
/* Mailbox 1 */
|
/* Mailbox 1 */
|
||||||
#define FW_STATE_READY 0x0000
|
#define FW_STATE_READY 0x0000
|
||||||
|
@ -1190,4 +1195,27 @@ struct ql_iscsi_stats {
|
||||||
uint8_t reserved2[264]; /* 0x0308 - 0x040F */
|
uint8_t reserved2[264]; /* 0x0308 - 0x040F */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define QLA82XX_DBG_STATE_ARRAY_LEN 16
|
||||||
|
#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN 8
|
||||||
|
#define QLA82XX_DBG_RSVD_ARRAY_LEN 8
|
||||||
|
|
||||||
|
struct qla4_8xxx_minidump_template_hdr {
|
||||||
|
uint32_t entry_type;
|
||||||
|
uint32_t first_entry_offset;
|
||||||
|
uint32_t size_of_template;
|
||||||
|
uint32_t capture_debug_level;
|
||||||
|
uint32_t num_of_entries;
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t driver_timestamp;
|
||||||
|
uint32_t checksum;
|
||||||
|
|
||||||
|
uint32_t driver_capture_mask;
|
||||||
|
uint32_t driver_info_word2;
|
||||||
|
uint32_t driver_info_word3;
|
||||||
|
uint32_t driver_info_word4;
|
||||||
|
|
||||||
|
uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN];
|
||||||
|
uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _QLA4X_FW_H */
|
#endif /* _QLA4X_FW_H */
|
||||||
|
|
|
@ -196,10 +196,18 @@ int qla4xxx_bsg_request(struct bsg_job *bsg_job);
|
||||||
int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job);
|
int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job);
|
||||||
|
|
||||||
void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry);
|
void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry);
|
||||||
|
int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
|
||||||
|
dma_addr_t phys_addr);
|
||||||
|
int qla4xxx_req_template_size(struct scsi_qla_host *ha);
|
||||||
|
void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha);
|
||||||
|
void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha);
|
||||||
|
void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha);
|
||||||
|
|
||||||
extern int ql4xextended_error_logging;
|
extern int ql4xextended_error_logging;
|
||||||
extern int ql4xdontresethba;
|
extern int ql4xdontresethba;
|
||||||
extern int ql4xenablemsix;
|
extern int ql4xenablemsix;
|
||||||
|
extern int ql4xmdcapmask;
|
||||||
|
extern int ql4xenablemd;
|
||||||
|
|
||||||
extern struct device_attribute *qla4xxx_host_attrs[];
|
extern struct device_attribute *qla4xxx_host_attrs[];
|
||||||
#endif /* _QLA4x_GBL_H */
|
#endif /* _QLA4x_GBL_H */
|
||||||
|
|
|
@ -277,6 +277,94 @@ qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha)
|
||||||
return ipv4_wait|ipv6_wait;
|
return ipv4_wait|ipv6_wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qla4xxx_alloc_fw_dump - Allocate memory for minidump data.
|
||||||
|
* @ha: pointer to host adapter structure.
|
||||||
|
**/
|
||||||
|
void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
uint32_t capture_debug_level;
|
||||||
|
int hdr_entry_bit, k;
|
||||||
|
void *md_tmp;
|
||||||
|
dma_addr_t md_tmp_dma;
|
||||||
|
struct qla4_8xxx_minidump_template_hdr *md_hdr;
|
||||||
|
|
||||||
|
if (ha->fw_dump) {
|
||||||
|
ql4_printk(KERN_WARNING, ha,
|
||||||
|
"Firmware dump previously allocated.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = qla4xxx_req_template_size(ha);
|
||||||
|
if (status != QLA_SUCCESS) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"scsi%ld: Failed to get template size\n",
|
||||||
|
ha->host_no);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
|
||||||
|
|
||||||
|
/* Allocate memory for saving the template */
|
||||||
|
md_tmp = dma_alloc_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
|
||||||
|
&md_tmp_dma, GFP_KERNEL);
|
||||||
|
|
||||||
|
/* Request template */
|
||||||
|
status = qla4xxx_get_minidump_template(ha, md_tmp_dma);
|
||||||
|
if (status != QLA_SUCCESS) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"scsi%ld: Failed to get minidump template\n",
|
||||||
|
ha->host_no);
|
||||||
|
goto alloc_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
md_hdr = (struct qla4_8xxx_minidump_template_hdr *)md_tmp;
|
||||||
|
|
||||||
|
capture_debug_level = md_hdr->capture_debug_level;
|
||||||
|
|
||||||
|
/* Get capture mask based on module loadtime setting. */
|
||||||
|
if (ql4xmdcapmask >= 0x3 && ql4xmdcapmask <= 0x7F)
|
||||||
|
ha->fw_dump_capture_mask = ql4xmdcapmask;
|
||||||
|
else
|
||||||
|
ha->fw_dump_capture_mask = capture_debug_level;
|
||||||
|
|
||||||
|
md_hdr->driver_capture_mask = ha->fw_dump_capture_mask;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Minimum num of entries = %d\n",
|
||||||
|
md_hdr->num_of_entries));
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Dump template size = %d\n",
|
||||||
|
ha->fw_dump_tmplt_size));
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Selected Capture mask =0x%x\n",
|
||||||
|
ha->fw_dump_capture_mask));
|
||||||
|
|
||||||
|
/* Calculate fw_dump_size */
|
||||||
|
for (hdr_entry_bit = 0x2, k = 1; (hdr_entry_bit & 0xFF);
|
||||||
|
hdr_entry_bit <<= 1, k++) {
|
||||||
|
if (hdr_entry_bit & ha->fw_dump_capture_mask)
|
||||||
|
ha->fw_dump_size += md_hdr->capture_size_array[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Total firmware dump size including command header */
|
||||||
|
ha->fw_dump_size += ha->fw_dump_tmplt_size;
|
||||||
|
ha->fw_dump = vmalloc(ha->fw_dump_size);
|
||||||
|
if (!ha->fw_dump)
|
||||||
|
goto alloc_cleanup;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"Minidump Tempalate Size = 0x%x KB\n",
|
||||||
|
ha->fw_dump_tmplt_size));
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"Total Minidump size = 0x%x KB\n", ha->fw_dump_size));
|
||||||
|
|
||||||
|
memcpy(ha->fw_dump, md_tmp, ha->fw_dump_tmplt_size);
|
||||||
|
ha->fw_dump_tmplt_hdr = ha->fw_dump;
|
||||||
|
|
||||||
|
alloc_cleanup:
|
||||||
|
dma_free_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
|
||||||
|
md_tmp, md_tmp_dma);
|
||||||
|
}
|
||||||
|
|
||||||
static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
|
static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
|
||||||
{
|
{
|
||||||
uint32_t timeout_count;
|
uint32_t timeout_count;
|
||||||
|
@ -445,9 +533,13 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
|
||||||
"control block\n", ha->host_no, __func__));
|
"control block\n", ha->host_no, __func__));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qla4xxx_fw_ready(ha))
|
if (!qla4xxx_fw_ready(ha))
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
if (is_qla8022(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
|
||||||
|
qla4xxx_alloc_fw_dump(ha);
|
||||||
|
|
||||||
return qla4xxx_get_firmware_status(ha);
|
return qla4xxx_get_firmware_status(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,6 +270,79 @@ mbox_exit:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qla4xxx_get_minidump_template - Get the firmware template
|
||||||
|
* @ha: Pointer to host adapter structure.
|
||||||
|
* @phys_addr: dma address for template
|
||||||
|
*
|
||||||
|
* Obtain the minidump template from firmware during initialization
|
||||||
|
* as it may not be available when minidump is desired.
|
||||||
|
**/
|
||||||
|
int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
|
||||||
|
dma_addr_t phys_addr)
|
||||||
|
{
|
||||||
|
uint32_t mbox_cmd[MBOX_REG_COUNT];
|
||||||
|
uint32_t mbox_sts[MBOX_REG_COUNT];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
memset(&mbox_cmd, 0, sizeof(mbox_cmd));
|
||||||
|
memset(&mbox_sts, 0, sizeof(mbox_sts));
|
||||||
|
|
||||||
|
mbox_cmd[0] = MBOX_CMD_MINIDUMP;
|
||||||
|
mbox_cmd[1] = MINIDUMP_GET_TMPLT_SUBCOMMAND;
|
||||||
|
mbox_cmd[2] = LSDW(phys_addr);
|
||||||
|
mbox_cmd[3] = MSDW(phys_addr);
|
||||||
|
mbox_cmd[4] = ha->fw_dump_tmplt_size;
|
||||||
|
mbox_cmd[5] = 0;
|
||||||
|
|
||||||
|
status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 2, &mbox_cmd[0],
|
||||||
|
&mbox_sts[0]);
|
||||||
|
if (status != QLA_SUCCESS) {
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"scsi%ld: %s: Cmd = %08X, mbx[0] = 0x%04x, mbx[1] = 0x%04x\n",
|
||||||
|
ha->host_no, __func__, mbox_cmd[0],
|
||||||
|
mbox_sts[0], mbox_sts[1]));
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qla4xxx_req_template_size - Get minidump template size from firmware.
|
||||||
|
* @ha: Pointer to host adapter structure.
|
||||||
|
**/
|
||||||
|
int qla4xxx_req_template_size(struct scsi_qla_host *ha)
|
||||||
|
{
|
||||||
|
uint32_t mbox_cmd[MBOX_REG_COUNT];
|
||||||
|
uint32_t mbox_sts[MBOX_REG_COUNT];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
memset(&mbox_cmd, 0, sizeof(mbox_cmd));
|
||||||
|
memset(&mbox_sts, 0, sizeof(mbox_sts));
|
||||||
|
|
||||||
|
mbox_cmd[0] = MBOX_CMD_MINIDUMP;
|
||||||
|
mbox_cmd[1] = MINIDUMP_GET_SIZE_SUBCOMMAND;
|
||||||
|
|
||||||
|
status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
|
||||||
|
&mbox_sts[0]);
|
||||||
|
if (status == QLA_SUCCESS) {
|
||||||
|
ha->fw_dump_tmplt_size = mbox_sts[1];
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s: sts[0]=0x%04x, template size=0x%04x, size_cm_02=0x%04x, size_cm_04=0x%04x, size_cm_08=0x%04x, size_cm_10=0x%04x, size_cm_FF=0x%04x, version=0x%04x\n",
|
||||||
|
__func__, mbox_sts[0], mbox_sts[1],
|
||||||
|
mbox_sts[2], mbox_sts[3], mbox_sts[4],
|
||||||
|
mbox_sts[5], mbox_sts[6], mbox_sts[7]));
|
||||||
|
if (ha->fw_dump_tmplt_size == 0)
|
||||||
|
status = QLA_ERROR;
|
||||||
|
} else {
|
||||||
|
ql4_printk(KERN_WARNING, ha,
|
||||||
|
"%s: Error sts[0]=0x%04x, mbx[1]=0x%04x\n",
|
||||||
|
__func__, mbox_sts[0], mbox_sts[1]);
|
||||||
|
status = QLA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha)
|
void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha)
|
||||||
{
|
{
|
||||||
set_bit(AF_FW_RECOVERY, &ha->flags);
|
set_bit(AF_FW_RECOVERY, &ha->flags);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/ratelimit.h>
|
||||||
#include "ql4_def.h"
|
#include "ql4_def.h"
|
||||||
#include "ql4_glbl.h"
|
#include "ql4_glbl.h"
|
||||||
|
|
||||||
|
@ -420,6 +421,38 @@ qla4_8xxx_rd_32(struct scsi_qla_host *ha, ulong off)
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Minidump related functions */
|
||||||
|
static int qla4_8xxx_md_rw_32(struct scsi_qla_host *ha, uint32_t off,
|
||||||
|
u32 data, uint8_t flag)
|
||||||
|
{
|
||||||
|
uint32_t win_read, off_value, rval = QLA_SUCCESS;
|
||||||
|
|
||||||
|
off_value = off & 0xFFFF0000;
|
||||||
|
writel(off_value, (void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
|
||||||
|
|
||||||
|
/* Read back value to make sure write has gone through before trying
|
||||||
|
* to use it.
|
||||||
|
*/
|
||||||
|
win_read = readl((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
|
||||||
|
if (win_read != off_value) {
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s: Written (0x%x) != Read (0x%x), off=0x%x\n",
|
||||||
|
__func__, off_value, win_read, off));
|
||||||
|
return QLA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_value = off & 0x0000FFFF;
|
||||||
|
|
||||||
|
if (flag)
|
||||||
|
writel(data, (void __iomem *)(off_value + CRB_INDIRECT_2M +
|
||||||
|
ha->nx_pcibase));
|
||||||
|
else
|
||||||
|
rval = readl((void __iomem *)(off_value + CRB_INDIRECT_2M +
|
||||||
|
ha->nx_pcibase));
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
#define CRB_WIN_LOCK_TIMEOUT 100000000
|
#define CRB_WIN_LOCK_TIMEOUT 100000000
|
||||||
|
|
||||||
int qla4_8xxx_crb_win_lock(struct scsi_qla_host *ha)
|
int qla4_8xxx_crb_win_lock(struct scsi_qla_host *ha)
|
||||||
|
@ -1252,9 +1285,9 @@ qla4_8xxx_pci_mem_read_2M(struct scsi_qla_host *ha,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j >= MAX_CTL_CHECK) {
|
if (j >= MAX_CTL_CHECK) {
|
||||||
if (printk_ratelimit())
|
printk_ratelimited(KERN_ERR
|
||||||
ql4_printk(KERN_ERR, ha,
|
"%s: failed to read through agent\n",
|
||||||
"failed to read through agent\n");
|
__func__);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1390,7 +1423,8 @@ qla4_8xxx_pci_mem_write_2M(struct scsi_qla_host *ha,
|
||||||
if (j >= MAX_CTL_CHECK) {
|
if (j >= MAX_CTL_CHECK) {
|
||||||
if (printk_ratelimit())
|
if (printk_ratelimit())
|
||||||
ql4_printk(KERN_ERR, ha,
|
ql4_printk(KERN_ERR, ha,
|
||||||
"failed to write through agent\n");
|
"%s: failed to read through agent\n",
|
||||||
|
__func__);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1462,6 +1496,8 @@ qla4_8xxx_set_drv_active(struct scsi_qla_host *ha)
|
||||||
|
|
||||||
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
||||||
drv_active |= (1 << (ha->func_num * 4));
|
drv_active |= (1 << (ha->func_num * 4));
|
||||||
|
ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
|
||||||
|
__func__, ha->host_no, drv_active);
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1472,6 +1508,8 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha)
|
||||||
|
|
||||||
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
||||||
drv_active &= ~(1 << (ha->func_num * 4));
|
drv_active &= ~(1 << (ha->func_num * 4));
|
||||||
|
ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
|
||||||
|
__func__, ha->host_no, drv_active);
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1497,6 +1535,8 @@ qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha)
|
||||||
|
|
||||||
drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
||||||
drv_state |= (1 << (ha->func_num * 4));
|
drv_state |= (1 << (ha->func_num * 4));
|
||||||
|
ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
|
||||||
|
__func__, ha->host_no, drv_state);
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1507,6 +1547,8 @@ qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha)
|
||||||
|
|
||||||
drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
||||||
drv_state &= ~(1 << (ha->func_num * 4));
|
drv_state &= ~(1 << (ha->func_num * 4));
|
||||||
|
ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
|
||||||
|
__func__, ha->host_no, drv_state);
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1601,6 +1643,629 @@ static void qla4_8xxx_rom_lock_recovery(struct scsi_qla_host *ha)
|
||||||
qla4_8xxx_rom_unlock(ha);
|
qla4_8xxx_rom_unlock(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_rdcrb(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t r_addr, r_stride, loop_cnt, i, r_value;
|
||||||
|
struct qla82xx_minidump_entry_crb *crb_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
crb_hdr = (struct qla82xx_minidump_entry_crb *)entry_hdr;
|
||||||
|
r_addr = crb_hdr->addr;
|
||||||
|
r_stride = crb_hdr->crb_strd.addr_stride;
|
||||||
|
loop_cnt = crb_hdr->op_count;
|
||||||
|
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_addr);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
r_addr += r_stride;
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qla4_8xxx_minidump_process_l2tag(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t addr, r_addr, c_addr, t_r_addr;
|
||||||
|
uint32_t i, k, loop_count, t_value, r_cnt, r_value;
|
||||||
|
unsigned long p_wait, w_time, p_mask;
|
||||||
|
uint32_t c_value_w, c_value_r;
|
||||||
|
struct qla82xx_minidump_entry_cache *cache_hdr;
|
||||||
|
int rval = QLA_ERROR;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
|
||||||
|
|
||||||
|
loop_count = cache_hdr->op_count;
|
||||||
|
r_addr = cache_hdr->read_addr;
|
||||||
|
c_addr = cache_hdr->control_addr;
|
||||||
|
c_value_w = cache_hdr->cache_ctrl.write_value;
|
||||||
|
|
||||||
|
t_r_addr = cache_hdr->tag_reg_addr;
|
||||||
|
t_value = cache_hdr->addr_ctrl.init_tag_value;
|
||||||
|
r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
|
||||||
|
p_wait = cache_hdr->cache_ctrl.poll_wait;
|
||||||
|
p_mask = cache_hdr->cache_ctrl.poll_mask;
|
||||||
|
|
||||||
|
for (i = 0; i < loop_count; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
|
||||||
|
|
||||||
|
if (c_value_w)
|
||||||
|
qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
|
||||||
|
|
||||||
|
if (p_mask) {
|
||||||
|
w_time = jiffies + p_wait;
|
||||||
|
do {
|
||||||
|
c_value_r = qla4_8xxx_md_rw_32(ha, c_addr,
|
||||||
|
0, 0);
|
||||||
|
if ((c_value_r & p_mask) == 0) {
|
||||||
|
break;
|
||||||
|
} else if (time_after_eq(jiffies, w_time)) {
|
||||||
|
/* capturing dump failed */
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = r_addr;
|
||||||
|
for (k = 0; k < r_cnt; k++) {
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
addr += cache_hdr->read_ctrl.read_addr_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_value += cache_hdr->addr_ctrl.tag_value_stride;
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
return QLA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qla4_8xxx_minidump_process_control(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr)
|
||||||
|
{
|
||||||
|
struct qla82xx_minidump_entry_crb *crb_entry;
|
||||||
|
uint32_t read_value, opcode, poll_time, addr, index, rval = QLA_SUCCESS;
|
||||||
|
uint32_t crb_addr;
|
||||||
|
unsigned long wtime;
|
||||||
|
struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
|
||||||
|
ha->fw_dump_tmplt_hdr;
|
||||||
|
crb_entry = (struct qla82xx_minidump_entry_crb *)entry_hdr;
|
||||||
|
|
||||||
|
crb_addr = crb_entry->addr;
|
||||||
|
for (i = 0; i < crb_entry->op_count; i++) {
|
||||||
|
opcode = crb_entry->crb_ctrl.opcode;
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_WR) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, crb_addr,
|
||||||
|
crb_entry->value_1, 1);
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_WR;
|
||||||
|
}
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_RW) {
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
|
||||||
|
qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_RW;
|
||||||
|
}
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_AND) {
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
|
||||||
|
read_value &= crb_entry->value_2;
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_AND;
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_OR) {
|
||||||
|
read_value |= crb_entry->value_3;
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_OR;
|
||||||
|
}
|
||||||
|
qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
|
||||||
|
}
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_OR) {
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
|
||||||
|
read_value |= crb_entry->value_3;
|
||||||
|
qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_OR;
|
||||||
|
}
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_POLL) {
|
||||||
|
poll_time = crb_entry->crb_strd.poll_timeout;
|
||||||
|
wtime = jiffies + poll_time;
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((read_value & crb_entry->value_2) ==
|
||||||
|
crb_entry->value_1)
|
||||||
|
break;
|
||||||
|
else if (time_after_eq(jiffies, wtime)) {
|
||||||
|
/* capturing dump failed */
|
||||||
|
rval = QLA_ERROR;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha,
|
||||||
|
crb_addr, 0, 0);
|
||||||
|
} while (1);
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_POLL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
|
||||||
|
if (crb_entry->crb_strd.state_index_a) {
|
||||||
|
index = crb_entry->crb_strd.state_index_a;
|
||||||
|
addr = tmplt_hdr->saved_state_array[index];
|
||||||
|
} else {
|
||||||
|
addr = crb_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
|
||||||
|
index = crb_entry->crb_ctrl.state_index_v;
|
||||||
|
tmplt_hdr->saved_state_array[index] = read_value;
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
|
||||||
|
if (crb_entry->crb_strd.state_index_a) {
|
||||||
|
index = crb_entry->crb_strd.state_index_a;
|
||||||
|
addr = tmplt_hdr->saved_state_array[index];
|
||||||
|
} else {
|
||||||
|
addr = crb_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crb_entry->crb_ctrl.state_index_v) {
|
||||||
|
index = crb_entry->crb_ctrl.state_index_v;
|
||||||
|
read_value =
|
||||||
|
tmplt_hdr->saved_state_array[index];
|
||||||
|
} else {
|
||||||
|
read_value = crb_entry->value_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qla4_8xxx_md_rw_32(ha, addr, read_value, 1);
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
|
||||||
|
index = crb_entry->crb_ctrl.state_index_v;
|
||||||
|
read_value = tmplt_hdr->saved_state_array[index];
|
||||||
|
read_value <<= crb_entry->crb_ctrl.shl;
|
||||||
|
read_value >>= crb_entry->crb_ctrl.shr;
|
||||||
|
if (crb_entry->value_2)
|
||||||
|
read_value &= crb_entry->value_2;
|
||||||
|
read_value |= crb_entry->value_3;
|
||||||
|
read_value += crb_entry->value_1;
|
||||||
|
tmplt_hdr->saved_state_array[index] = read_value;
|
||||||
|
opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE;
|
||||||
|
}
|
||||||
|
crb_addr += crb_entry->crb_strd.addr_stride;
|
||||||
|
}
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s\n", __func__));
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_rdocm(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t r_addr, r_stride, loop_cnt, i, r_value;
|
||||||
|
struct qla82xx_minidump_entry_rdocm *ocm_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
ocm_hdr = (struct qla82xx_minidump_entry_rdocm *)entry_hdr;
|
||||||
|
r_addr = ocm_hdr->read_addr;
|
||||||
|
r_stride = ocm_hdr->read_addr_stride;
|
||||||
|
loop_cnt = ocm_hdr->op_count;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: r_addr: 0x%x, r_stride: 0x%x, loop_cnt: 0x%x\n",
|
||||||
|
__func__, r_addr, r_stride, loop_cnt));
|
||||||
|
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
r_value = readl((void __iomem *)(r_addr + ha->nx_pcibase));
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
r_addr += r_stride;
|
||||||
|
}
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%lx\n",
|
||||||
|
__func__, (loop_cnt * sizeof(uint32_t))));
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_rdmux(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
|
||||||
|
struct qla82xx_minidump_entry_mux *mux_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
mux_hdr = (struct qla82xx_minidump_entry_mux *)entry_hdr;
|
||||||
|
r_addr = mux_hdr->read_addr;
|
||||||
|
s_addr = mux_hdr->select_addr;
|
||||||
|
s_stride = mux_hdr->select_value_stride;
|
||||||
|
s_value = mux_hdr->select_value;
|
||||||
|
loop_cnt = mux_hdr->op_count;
|
||||||
|
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, s_addr, s_value, 1);
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(s_value);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
s_value += s_stride;
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_l1cache(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t addr, r_addr, c_addr, t_r_addr;
|
||||||
|
uint32_t i, k, loop_count, t_value, r_cnt, r_value;
|
||||||
|
uint32_t c_value_w;
|
||||||
|
struct qla82xx_minidump_entry_cache *cache_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
|
||||||
|
loop_count = cache_hdr->op_count;
|
||||||
|
r_addr = cache_hdr->read_addr;
|
||||||
|
c_addr = cache_hdr->control_addr;
|
||||||
|
c_value_w = cache_hdr->cache_ctrl.write_value;
|
||||||
|
|
||||||
|
t_r_addr = cache_hdr->tag_reg_addr;
|
||||||
|
t_value = cache_hdr->addr_ctrl.init_tag_value;
|
||||||
|
r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
|
||||||
|
|
||||||
|
for (i = 0; i < loop_count; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
|
||||||
|
qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
|
||||||
|
addr = r_addr;
|
||||||
|
for (k = 0; k < r_cnt; k++) {
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
addr += cache_hdr->read_ctrl.read_addr_stride;
|
||||||
|
}
|
||||||
|
t_value += cache_hdr->addr_ctrl.tag_value_stride;
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_queue(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t s_addr, r_addr;
|
||||||
|
uint32_t r_stride, r_value, r_cnt, qid = 0;
|
||||||
|
uint32_t i, k, loop_cnt;
|
||||||
|
struct qla82xx_minidump_entry_queue *q_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
q_hdr = (struct qla82xx_minidump_entry_queue *)entry_hdr;
|
||||||
|
s_addr = q_hdr->select_addr;
|
||||||
|
r_cnt = q_hdr->rd_strd.read_addr_cnt;
|
||||||
|
r_stride = q_hdr->rd_strd.read_addr_stride;
|
||||||
|
loop_cnt = q_hdr->op_count;
|
||||||
|
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, s_addr, qid, 1);
|
||||||
|
r_addr = q_hdr->read_addr;
|
||||||
|
for (k = 0; k < r_cnt; k++) {
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
r_addr += r_stride;
|
||||||
|
}
|
||||||
|
qid += q_hdr->q_strd.queue_id_stride;
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MD_DIRECT_ROM_WINDOW 0x42110030
|
||||||
|
#define MD_DIRECT_ROM_READ_BASE 0x42150000
|
||||||
|
|
||||||
|
static void qla4_8xxx_minidump_process_rdrom(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t r_addr, r_value;
|
||||||
|
uint32_t i, loop_cnt;
|
||||||
|
struct qla82xx_minidump_entry_rdrom *rom_hdr;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
rom_hdr = (struct qla82xx_minidump_entry_rdrom *)entry_hdr;
|
||||||
|
r_addr = rom_hdr->read_addr;
|
||||||
|
loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t);
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: flash_addr: 0x%x, read_data_size: 0x%x\n",
|
||||||
|
__func__, r_addr, loop_cnt));
|
||||||
|
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW,
|
||||||
|
(r_addr & 0xFFFF0000), 1);
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha,
|
||||||
|
MD_DIRECT_ROM_READ_BASE +
|
||||||
|
(r_addr & 0x0000FFFF), 0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_value);
|
||||||
|
r_addr += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MD_MIU_TEST_AGT_CTRL 0x41000090
|
||||||
|
#define MD_MIU_TEST_AGT_ADDR_LO 0x41000094
|
||||||
|
#define MD_MIU_TEST_AGT_ADDR_HI 0x41000098
|
||||||
|
|
||||||
|
static int qla4_8xxx_minidump_process_rdmem(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
uint32_t **d_ptr)
|
||||||
|
{
|
||||||
|
uint32_t r_addr, r_value, r_data;
|
||||||
|
uint32_t i, j, loop_cnt;
|
||||||
|
struct qla82xx_minidump_entry_rdmem *m_hdr;
|
||||||
|
unsigned long flags;
|
||||||
|
uint32_t *data_ptr = *d_ptr;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
|
||||||
|
m_hdr = (struct qla82xx_minidump_entry_rdmem *)entry_hdr;
|
||||||
|
r_addr = m_hdr->read_addr;
|
||||||
|
loop_cnt = m_hdr->read_data_size/16;
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: Read addr: 0x%x, read_data_size: 0x%x\n",
|
||||||
|
__func__, r_addr, m_hdr->read_data_size));
|
||||||
|
|
||||||
|
if (r_addr & 0xf) {
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: Read addr 0x%x not 16 bytes alligned\n",
|
||||||
|
__func__, r_addr));
|
||||||
|
return QLA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hdr->read_data_size % 16) {
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: Read data[0x%x] not multiple of 16 bytes\n",
|
||||||
|
__func__, m_hdr->read_data_size));
|
||||||
|
return QLA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n",
|
||||||
|
__func__, r_addr, m_hdr->read_data_size, loop_cnt));
|
||||||
|
|
||||||
|
write_lock_irqsave(&ha->hw_lock, flags);
|
||||||
|
for (i = 0; i < loop_cnt; i++) {
|
||||||
|
qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1);
|
||||||
|
r_value = 0;
|
||||||
|
qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1);
|
||||||
|
r_value = MIU_TA_CTL_ENABLE;
|
||||||
|
qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
|
||||||
|
r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
|
||||||
|
qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
|
||||||
|
|
||||||
|
for (j = 0; j < MAX_CTL_CHECK; j++) {
|
||||||
|
r_value = qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL,
|
||||||
|
0, 0);
|
||||||
|
if ((r_value & MIU_TA_CTL_BUSY) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j >= MAX_CTL_CHECK) {
|
||||||
|
printk_ratelimited(KERN_ERR
|
||||||
|
"%s: failed to read through agent\n",
|
||||||
|
__func__);
|
||||||
|
write_unlock_irqrestore(&ha->hw_lock, flags);
|
||||||
|
return QLA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 4; j++) {
|
||||||
|
r_data = qla4_8xxx_md_rw_32(ha,
|
||||||
|
MD_MIU_TEST_AGT_RDDATA[j],
|
||||||
|
0, 0);
|
||||||
|
*data_ptr++ = cpu_to_le32(r_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
r_addr += 16;
|
||||||
|
}
|
||||||
|
write_unlock_irqrestore(&ha->hw_lock, flags);
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%x\n",
|
||||||
|
__func__, (loop_cnt * 16)));
|
||||||
|
|
||||||
|
*d_ptr = data_ptr;
|
||||||
|
return QLA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ql4_8xxx_mark_entry_skipped(struct scsi_qla_host *ha,
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr,
|
||||||
|
int index)
|
||||||
|
{
|
||||||
|
entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG;
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"scsi(%ld): Skipping entry[%d]: ETYPE[0x%x]-ELEVEL[0x%x]\n",
|
||||||
|
ha->host_no, index, entry_hdr->entry_type,
|
||||||
|
entry_hdr->d_ctrl.entry_capture_mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qla82xx_collect_md_data - Retrieve firmware minidump data.
|
||||||
|
* @ha: pointer to adapter structure
|
||||||
|
**/
|
||||||
|
static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
|
||||||
|
{
|
||||||
|
int num_entry_hdr = 0;
|
||||||
|
struct qla82xx_minidump_entry_hdr *entry_hdr;
|
||||||
|
struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
|
||||||
|
uint32_t *data_ptr;
|
||||||
|
uint32_t data_collected = 0;
|
||||||
|
int i, rval = QLA_ERROR;
|
||||||
|
uint64_t now;
|
||||||
|
uint32_t timestamp;
|
||||||
|
|
||||||
|
if (!ha->fw_dump) {
|
||||||
|
ql4_printk(KERN_INFO, ha, "%s(%ld) No buffer to dump\n",
|
||||||
|
__func__, ha->host_no);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
|
||||||
|
ha->fw_dump_tmplt_hdr;
|
||||||
|
data_ptr = (uint32_t *)((uint8_t *)ha->fw_dump +
|
||||||
|
ha->fw_dump_tmplt_size);
|
||||||
|
data_collected += ha->fw_dump_tmplt_size;
|
||||||
|
|
||||||
|
num_entry_hdr = tmplt_hdr->num_of_entries;
|
||||||
|
ql4_printk(KERN_INFO, ha, "[%s]: starting data ptr: %p\n",
|
||||||
|
__func__, data_ptr);
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"[%s]: no of entry headers in Template: 0x%x\n",
|
||||||
|
__func__, num_entry_hdr);
|
||||||
|
ql4_printk(KERN_INFO, ha, "[%s]: Capture Mask obtained: 0x%x\n",
|
||||||
|
__func__, ha->fw_dump_capture_mask);
|
||||||
|
ql4_printk(KERN_INFO, ha, "[%s]: Total_data_size 0x%x, %d obtained\n",
|
||||||
|
__func__, ha->fw_dump_size, ha->fw_dump_size);
|
||||||
|
|
||||||
|
/* Update current timestamp before taking dump */
|
||||||
|
now = get_jiffies_64();
|
||||||
|
timestamp = (u32)(jiffies_to_msecs(now) / 1000);
|
||||||
|
tmplt_hdr->driver_timestamp = timestamp;
|
||||||
|
|
||||||
|
entry_hdr = (struct qla82xx_minidump_entry_hdr *)
|
||||||
|
(((uint8_t *)ha->fw_dump_tmplt_hdr) +
|
||||||
|
tmplt_hdr->first_entry_offset);
|
||||||
|
|
||||||
|
/* Walk through the entry headers - validate/perform required action */
|
||||||
|
for (i = 0; i < num_entry_hdr; i++) {
|
||||||
|
if (data_collected >= ha->fw_dump_size) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"Data collected: [0x%x], Total Dump size: [0x%x]\n",
|
||||||
|
data_collected, ha->fw_dump_size);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(entry_hdr->d_ctrl.entry_capture_mask &
|
||||||
|
ha->fw_dump_capture_mask)) {
|
||||||
|
entry_hdr->d_ctrl.driver_flags |=
|
||||||
|
QLA82XX_DBG_SKIPPED_FLAG;
|
||||||
|
goto skip_nxt_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"Data collected: [0x%x], Dump size left:[0x%x]\n",
|
||||||
|
data_collected,
|
||||||
|
(ha->fw_dump_size - data_collected)));
|
||||||
|
|
||||||
|
/* Decode the entry type and take required action to capture
|
||||||
|
* debug data
|
||||||
|
*/
|
||||||
|
switch (entry_hdr->entry_type) {
|
||||||
|
case QLA82XX_RDEND:
|
||||||
|
ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
|
||||||
|
break;
|
||||||
|
case QLA82XX_CNTRL:
|
||||||
|
rval = qla4_8xxx_minidump_process_control(ha,
|
||||||
|
entry_hdr);
|
||||||
|
if (rval != QLA_SUCCESS) {
|
||||||
|
ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
|
||||||
|
goto md_failed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QLA82XX_RDCRB:
|
||||||
|
qla4_8xxx_minidump_process_rdcrb(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_RDMEM:
|
||||||
|
rval = qla4_8xxx_minidump_process_rdmem(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
if (rval != QLA_SUCCESS) {
|
||||||
|
ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
|
||||||
|
goto md_failed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QLA82XX_BOARD:
|
||||||
|
case QLA82XX_RDROM:
|
||||||
|
qla4_8xxx_minidump_process_rdrom(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_L2DTG:
|
||||||
|
case QLA82XX_L2ITG:
|
||||||
|
case QLA82XX_L2DAT:
|
||||||
|
case QLA82XX_L2INS:
|
||||||
|
rval = qla4_8xxx_minidump_process_l2tag(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
if (rval != QLA_SUCCESS) {
|
||||||
|
ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
|
||||||
|
goto md_failed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QLA82XX_L1DAT:
|
||||||
|
case QLA82XX_L1INS:
|
||||||
|
qla4_8xxx_minidump_process_l1cache(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_RDOCM:
|
||||||
|
qla4_8xxx_minidump_process_rdocm(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_RDMUX:
|
||||||
|
qla4_8xxx_minidump_process_rdmux(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_QUEUE:
|
||||||
|
qla4_8xxx_minidump_process_queue(ha, entry_hdr,
|
||||||
|
&data_ptr);
|
||||||
|
break;
|
||||||
|
case QLA82XX_RDNOP:
|
||||||
|
default:
|
||||||
|
ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_collected = (uint8_t *)data_ptr -
|
||||||
|
((uint8_t *)((uint8_t *)ha->fw_dump +
|
||||||
|
ha->fw_dump_tmplt_size));
|
||||||
|
skip_nxt_entry:
|
||||||
|
/* next entry in the template */
|
||||||
|
entry_hdr = (struct qla82xx_minidump_entry_hdr *)
|
||||||
|
(((uint8_t *)entry_hdr) +
|
||||||
|
entry_hdr->entry_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data_collected + ha->fw_dump_tmplt_size) != ha->fw_dump_size) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"Dump data mismatch: Data collected: [0x%x], total_data_size:[0x%x]\n",
|
||||||
|
data_collected, ha->fw_dump_size);
|
||||||
|
goto md_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s Last entry: 0x%x\n",
|
||||||
|
__func__, i));
|
||||||
|
md_failed:
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qla4_8xxx_uevent_emit - Send uevent when the firmware dump is ready.
|
||||||
|
* @ha: pointer to adapter structure
|
||||||
|
**/
|
||||||
|
static void qla4_8xxx_uevent_emit(struct scsi_qla_host *ha, u32 code)
|
||||||
|
{
|
||||||
|
char event_string[40];
|
||||||
|
char *envp[] = { event_string, NULL };
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case QL4_UEVENT_CODE_FW_DUMP:
|
||||||
|
snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
|
||||||
|
ha->host_no);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*do nothing*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
kobject_uevent_env(&(&ha->pdev->dev)->kobj, KOBJ_CHANGE, envp);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qla4_8xxx_device_bootstrap - Initialize device, set DEV_READY, start fw
|
* qla4_8xxx_device_bootstrap - Initialize device, set DEV_READY, start fw
|
||||||
* @ha: pointer to adapter structure
|
* @ha: pointer to adapter structure
|
||||||
|
@ -1659,6 +2324,15 @@ dev_initialize:
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION);
|
||||||
|
|
||||||
qla4_8xxx_idc_unlock(ha);
|
qla4_8xxx_idc_unlock(ha);
|
||||||
|
if (ql4xenablemd && test_bit(AF_FW_RECOVERY, &ha->flags) &&
|
||||||
|
!test_and_set_bit(AF_82XX_FW_DUMPED, &ha->flags)) {
|
||||||
|
if (!qla4_8xxx_collect_md_data(ha)) {
|
||||||
|
qla4_8xxx_uevent_emit(ha, QL4_UEVENT_CODE_FW_DUMP);
|
||||||
|
} else {
|
||||||
|
ql4_printk(KERN_INFO, ha, "Unable to collect minidump\n");
|
||||||
|
clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
rval = qla4_8xxx_try_start_fw(ha);
|
rval = qla4_8xxx_try_start_fw(ha);
|
||||||
qla4_8xxx_idc_lock(ha);
|
qla4_8xxx_idc_lock(ha);
|
||||||
|
|
||||||
|
@ -1686,6 +2360,7 @@ static void
|
||||||
qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
|
qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
|
||||||
{
|
{
|
||||||
uint32_t dev_state, drv_state, drv_active;
|
uint32_t dev_state, drv_state, drv_active;
|
||||||
|
uint32_t active_mask = 0xFFFFFFFF;
|
||||||
unsigned long reset_timeout;
|
unsigned long reset_timeout;
|
||||||
|
|
||||||
ql4_printk(KERN_INFO, ha,
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
@ -1697,7 +2372,14 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
|
||||||
qla4_8xxx_idc_lock(ha);
|
qla4_8xxx_idc_lock(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
qla4_8xxx_set_rst_ready(ha);
|
if (!test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
|
||||||
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s(%ld): reset acknowledged\n",
|
||||||
|
__func__, ha->host_no));
|
||||||
|
qla4_8xxx_set_rst_ready(ha);
|
||||||
|
} else {
|
||||||
|
active_mask = (~(1 << (ha->func_num * 4)));
|
||||||
|
}
|
||||||
|
|
||||||
/* wait for 10 seconds for reset ack from all functions */
|
/* wait for 10 seconds for reset ack from all functions */
|
||||||
reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
|
reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
|
||||||
|
@ -1709,12 +2391,24 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
|
||||||
"%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
|
"%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
|
||||||
__func__, ha->host_no, drv_state, drv_active);
|
__func__, ha->host_no, drv_state, drv_active);
|
||||||
|
|
||||||
while (drv_state != drv_active) {
|
while (drv_state != (drv_active & active_mask)) {
|
||||||
if (time_after_eq(jiffies, reset_timeout)) {
|
if (time_after_eq(jiffies, reset_timeout)) {
|
||||||
printk("%s: RESET TIMEOUT!\n", DRIVER_NAME);
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s: RESET TIMEOUT! drv_state: 0x%08x, drv_active: 0x%08x\n",
|
||||||
|
DRIVER_NAME, drv_state, drv_active);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When reset_owner times out, check which functions
|
||||||
|
* acked/did not ack
|
||||||
|
*/
|
||||||
|
if (test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
|
||||||
|
ql4_printk(KERN_INFO, ha,
|
||||||
|
"%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
|
||||||
|
__func__, ha->host_no, drv_state,
|
||||||
|
drv_active);
|
||||||
|
}
|
||||||
qla4_8xxx_idc_unlock(ha);
|
qla4_8xxx_idc_unlock(ha);
|
||||||
msleep(1000);
|
msleep(1000);
|
||||||
qla4_8xxx_idc_lock(ha);
|
qla4_8xxx_idc_lock(ha);
|
||||||
|
@ -1723,14 +2417,18 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
|
||||||
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Clear RESET OWNER as we are not going to use it any further */
|
||||||
|
clear_bit(AF_82XX_RST_OWNER, &ha->flags);
|
||||||
|
|
||||||
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
||||||
ql4_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state,
|
ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n", dev_state,
|
||||||
dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
|
dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
|
||||||
|
|
||||||
/* Force to DEV_COLD unless someone else is starting a reset */
|
/* Force to DEV_COLD unless someone else is starting a reset */
|
||||||
if (dev_state != QLA82XX_DEV_INITIALIZING) {
|
if (dev_state != QLA82XX_DEV_INITIALIZING) {
|
||||||
ql4_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n");
|
ql4_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n");
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
|
||||||
|
qla4_8xxx_set_rst_ready(ha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1765,8 +2463,9 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
||||||
ql4_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state,
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
|
||||||
dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
|
dev_state, dev_state < MAX_STATES ?
|
||||||
|
qdev_state[dev_state] : "Unknown"));
|
||||||
|
|
||||||
/* wait for 30 seconds for device to go ready */
|
/* wait for 30 seconds for device to go ready */
|
||||||
dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
|
dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
|
||||||
|
@ -1775,15 +2474,19 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
if (time_after_eq(jiffies, dev_init_timeout)) {
|
if (time_after_eq(jiffies, dev_init_timeout)) {
|
||||||
ql4_printk(KERN_WARNING, ha, "Device init failed!\n");
|
ql4_printk(KERN_WARNING, ha,
|
||||||
|
"%s: Device Init Failed 0x%x = %s\n",
|
||||||
|
DRIVER_NAME,
|
||||||
|
dev_state, dev_state < MAX_STATES ?
|
||||||
|
qdev_state[dev_state] : "Unknown");
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
||||||
QLA82XX_DEV_FAILED);
|
QLA82XX_DEV_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
||||||
ql4_printk(KERN_INFO, ha,
|
ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
|
||||||
"2:Device state is 0x%x = %s\n", dev_state,
|
dev_state, dev_state < MAX_STATES ?
|
||||||
dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
|
qdev_state[dev_state] : "Unknown");
|
||||||
|
|
||||||
/* NOTE: Make sure idc unlocked upon exit of switch statement */
|
/* NOTE: Make sure idc unlocked upon exit of switch statement */
|
||||||
switch (dev_state) {
|
switch (dev_state) {
|
||||||
|
@ -2184,6 +2887,7 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
|
||||||
ql4_printk(KERN_INFO, ha, "HW State: NEED RESET\n");
|
ql4_printk(KERN_INFO, ha, "HW State: NEED RESET\n");
|
||||||
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
||||||
QLA82XX_DEV_NEED_RESET);
|
QLA82XX_DEV_NEED_RESET);
|
||||||
|
set_bit(AF_82XX_RST_OWNER, &ha->flags);
|
||||||
} else
|
} else
|
||||||
ql4_printk(KERN_INFO, ha, "HW State: DEVICE INITIALIZING\n");
|
ql4_printk(KERN_INFO, ha, "HW State: DEVICE INITIALIZING\n");
|
||||||
|
|
||||||
|
@ -2195,8 +2899,10 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
|
||||||
qla4_8xxx_clear_rst_ready(ha);
|
qla4_8xxx_clear_rst_ready(ha);
|
||||||
qla4_8xxx_idc_unlock(ha);
|
qla4_8xxx_idc_unlock(ha);
|
||||||
|
|
||||||
if (rval == QLA_SUCCESS)
|
if (rval == QLA_SUCCESS) {
|
||||||
|
ql4_printk(KERN_INFO, ha, "Clearing AF_RECOVERY in qla4_8xxx_isp_reset\n");
|
||||||
clear_bit(AF_FW_RECOVERY, &ha->flags);
|
clear_bit(AF_FW_RECOVERY, &ha->flags);
|
||||||
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -792,4 +792,196 @@ struct crb_addr_pair {
|
||||||
#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0)
|
#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0)
|
||||||
#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x0b4)
|
#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x0b4)
|
||||||
|
|
||||||
|
/* Minidump related */
|
||||||
|
|
||||||
|
/* Entry Type Defines */
|
||||||
|
#define QLA82XX_RDNOP 0
|
||||||
|
#define QLA82XX_RDCRB 1
|
||||||
|
#define QLA82XX_RDMUX 2
|
||||||
|
#define QLA82XX_QUEUE 3
|
||||||
|
#define QLA82XX_BOARD 4
|
||||||
|
#define QLA82XX_RDOCM 6
|
||||||
|
#define QLA82XX_PREGS 7
|
||||||
|
#define QLA82XX_L1DTG 8
|
||||||
|
#define QLA82XX_L1ITG 9
|
||||||
|
#define QLA82XX_L1DAT 11
|
||||||
|
#define QLA82XX_L1INS 12
|
||||||
|
#define QLA82XX_L2DTG 21
|
||||||
|
#define QLA82XX_L2ITG 22
|
||||||
|
#define QLA82XX_L2DAT 23
|
||||||
|
#define QLA82XX_L2INS 24
|
||||||
|
#define QLA82XX_RDROM 71
|
||||||
|
#define QLA82XX_RDMEM 72
|
||||||
|
#define QLA82XX_CNTRL 98
|
||||||
|
#define QLA82XX_RDEND 255
|
||||||
|
|
||||||
|
/* Opcodes for Control Entries.
|
||||||
|
* These Flags are bit fields.
|
||||||
|
*/
|
||||||
|
#define QLA82XX_DBG_OPCODE_WR 0x01
|
||||||
|
#define QLA82XX_DBG_OPCODE_RW 0x02
|
||||||
|
#define QLA82XX_DBG_OPCODE_AND 0x04
|
||||||
|
#define QLA82XX_DBG_OPCODE_OR 0x08
|
||||||
|
#define QLA82XX_DBG_OPCODE_POLL 0x10
|
||||||
|
#define QLA82XX_DBG_OPCODE_RDSTATE 0x20
|
||||||
|
#define QLA82XX_DBG_OPCODE_WRSTATE 0x40
|
||||||
|
#define QLA82XX_DBG_OPCODE_MDSTATE 0x80
|
||||||
|
|
||||||
|
/* Driver Flags */
|
||||||
|
#define QLA82XX_DBG_SKIPPED_FLAG 0x80 /* driver skipped this entry */
|
||||||
|
#define QLA82XX_DBG_SIZE_ERR_FLAG 0x40 /* Entry vs Capture size
|
||||||
|
* mismatch */
|
||||||
|
|
||||||
|
/* Driver_code is for driver to write some info about the entry
|
||||||
|
* currently not used.
|
||||||
|
*/
|
||||||
|
struct qla82xx_minidump_entry_hdr {
|
||||||
|
uint32_t entry_type;
|
||||||
|
uint32_t entry_size;
|
||||||
|
uint32_t entry_capture_size;
|
||||||
|
struct {
|
||||||
|
uint8_t entry_capture_mask;
|
||||||
|
uint8_t entry_code;
|
||||||
|
uint8_t driver_code;
|
||||||
|
uint8_t driver_flags;
|
||||||
|
} d_ctrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read CRB entry header */
|
||||||
|
struct qla82xx_minidump_entry_crb {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t addr;
|
||||||
|
struct {
|
||||||
|
uint8_t addr_stride;
|
||||||
|
uint8_t state_index_a;
|
||||||
|
uint16_t poll_timeout;
|
||||||
|
} crb_strd;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint32_t op_count;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t opcode;
|
||||||
|
uint8_t state_index_v;
|
||||||
|
uint8_t shl;
|
||||||
|
uint8_t shr;
|
||||||
|
} crb_ctrl;
|
||||||
|
|
||||||
|
uint32_t value_1;
|
||||||
|
uint32_t value_2;
|
||||||
|
uint32_t value_3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct qla82xx_minidump_entry_cache {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t tag_reg_addr;
|
||||||
|
struct {
|
||||||
|
uint16_t tag_value_stride;
|
||||||
|
uint16_t init_tag_value;
|
||||||
|
} addr_ctrl;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint32_t op_count;
|
||||||
|
uint32_t control_addr;
|
||||||
|
struct {
|
||||||
|
uint16_t write_value;
|
||||||
|
uint8_t poll_mask;
|
||||||
|
uint8_t poll_wait;
|
||||||
|
} cache_ctrl;
|
||||||
|
uint32_t read_addr;
|
||||||
|
struct {
|
||||||
|
uint8_t read_addr_stride;
|
||||||
|
uint8_t read_addr_cnt;
|
||||||
|
uint16_t rsvd_1;
|
||||||
|
} read_ctrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read OCM */
|
||||||
|
struct qla82xx_minidump_entry_rdocm {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t rsvd_0;
|
||||||
|
uint32_t rsvd_1;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint32_t op_count;
|
||||||
|
uint32_t rsvd_2;
|
||||||
|
uint32_t rsvd_3;
|
||||||
|
uint32_t read_addr;
|
||||||
|
uint32_t read_addr_stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read Memory */
|
||||||
|
struct qla82xx_minidump_entry_rdmem {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t rsvd[6];
|
||||||
|
uint32_t read_addr;
|
||||||
|
uint32_t read_data_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read ROM */
|
||||||
|
struct qla82xx_minidump_entry_rdrom {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t rsvd[6];
|
||||||
|
uint32_t read_addr;
|
||||||
|
uint32_t read_data_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Mux entry */
|
||||||
|
struct qla82xx_minidump_entry_mux {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t select_addr;
|
||||||
|
uint32_t rsvd_0;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint32_t op_count;
|
||||||
|
uint32_t select_value;
|
||||||
|
uint32_t select_value_stride;
|
||||||
|
uint32_t read_addr;
|
||||||
|
uint32_t rsvd_1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Queue entry */
|
||||||
|
struct qla82xx_minidump_entry_queue {
|
||||||
|
struct qla82xx_minidump_entry_hdr h;
|
||||||
|
uint32_t select_addr;
|
||||||
|
struct {
|
||||||
|
uint16_t queue_id_stride;
|
||||||
|
uint16_t rsvd_0;
|
||||||
|
} q_strd;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint32_t op_count;
|
||||||
|
uint32_t rsvd_1;
|
||||||
|
uint32_t rsvd_2;
|
||||||
|
uint32_t read_addr;
|
||||||
|
struct {
|
||||||
|
uint8_t read_addr_stride;
|
||||||
|
uint8_t read_addr_cnt;
|
||||||
|
uint16_t rsvd_3;
|
||||||
|
} rd_strd;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define QLA82XX_MINIDUMP_OCM0_SIZE (256 * 1024)
|
||||||
|
#define QLA82XX_MINIDUMP_L1C_SIZE (256 * 1024)
|
||||||
|
#define QLA82XX_MINIDUMP_L2C_SIZE 1572864
|
||||||
|
#define QLA82XX_MINIDUMP_COMMON_STR_SIZE 0
|
||||||
|
#define QLA82XX_MINIDUMP_FCOE_STR_SIZE 0
|
||||||
|
#define QLA82XX_MINIDUMP_MEM_SIZE 0
|
||||||
|
#define QLA82XX_MAX_ENTRY_HDR 4
|
||||||
|
|
||||||
|
struct qla82xx_minidump {
|
||||||
|
uint32_t md_ocm0_data[QLA82XX_MINIDUMP_OCM0_SIZE];
|
||||||
|
uint32_t md_l1c_data[QLA82XX_MINIDUMP_L1C_SIZE];
|
||||||
|
uint32_t md_l2c_data[QLA82XX_MINIDUMP_L2C_SIZE];
|
||||||
|
uint32_t md_cs_data[QLA82XX_MINIDUMP_COMMON_STR_SIZE];
|
||||||
|
uint32_t md_fcoes_data[QLA82XX_MINIDUMP_FCOE_STR_SIZE];
|
||||||
|
uint32_t md_mem_data[QLA82XX_MINIDUMP_MEM_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE 0x129
|
||||||
|
#define RQST_TMPLT_SIZE 0x0
|
||||||
|
#define RQST_TMPLT 0x1
|
||||||
|
#define MD_DIRECT_ROM_WINDOW 0x42110030
|
||||||
|
#define MD_DIRECT_ROM_READ_BASE 0x42150000
|
||||||
|
#define MD_MIU_TEST_AGT_CTRL 0x41000090
|
||||||
|
#define MD_MIU_TEST_AGT_ADDR_LO 0x41000094
|
||||||
|
#define MD_MIU_TEST_AGT_ADDR_HI 0x41000098
|
||||||
|
|
||||||
|
static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
|
||||||
|
0x410000AC, 0x410000B8, 0x410000BC };
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -82,6 +82,20 @@ MODULE_PARM_DESC(ql4xsess_recovery_tmo,
|
||||||
" Target Session Recovery Timeout.\n"
|
" Target Session Recovery Timeout.\n"
|
||||||
"\t\t Default: 120 sec.");
|
"\t\t Default: 120 sec.");
|
||||||
|
|
||||||
|
int ql4xmdcapmask = 0x1F;
|
||||||
|
module_param(ql4xmdcapmask, int, S_IRUGO);
|
||||||
|
MODULE_PARM_DESC(ql4xmdcapmask,
|
||||||
|
" Set the Minidump driver capture mask level.\n"
|
||||||
|
"\t\t Default is 0x1F.\n"
|
||||||
|
"\t\t Can be set to 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F");
|
||||||
|
|
||||||
|
int ql4xenablemd = 1;
|
||||||
|
module_param(ql4xenablemd, int, S_IRUGO | S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(ql4xenablemd,
|
||||||
|
" Set to enable minidump.\n"
|
||||||
|
"\t\t 0 - disable minidump\n"
|
||||||
|
"\t\t 1 - enable minidump (Default)");
|
||||||
|
|
||||||
static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
|
static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
|
||||||
/*
|
/*
|
||||||
* SCSI host template entry points
|
* SCSI host template entry points
|
||||||
|
@ -2265,6 +2279,9 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
|
||||||
dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
|
dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
|
||||||
ha->queues_dma);
|
ha->queues_dma);
|
||||||
|
|
||||||
|
if (ha->fw_dump)
|
||||||
|
vfree(ha->fw_dump);
|
||||||
|
|
||||||
ha->queues_len = 0;
|
ha->queues_len = 0;
|
||||||
ha->queues = NULL;
|
ha->queues = NULL;
|
||||||
ha->queues_dma = 0;
|
ha->queues_dma = 0;
|
||||||
|
@ -2274,6 +2291,8 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
|
||||||
ha->response_dma = 0;
|
ha->response_dma = 0;
|
||||||
ha->shadow_regs = NULL;
|
ha->shadow_regs = NULL;
|
||||||
ha->shadow_regs_dma = 0;
|
ha->shadow_regs_dma = 0;
|
||||||
|
ha->fw_dump = NULL;
|
||||||
|
ha->fw_dump_size = 0;
|
||||||
|
|
||||||
/* Free srb pool. */
|
/* Free srb pool. */
|
||||||
if (ha->srb_mempool)
|
if (ha->srb_mempool)
|
||||||
|
@ -5068,6 +5087,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
|
||||||
|
|
||||||
set_bit(AF_INIT_DONE, &ha->flags);
|
set_bit(AF_INIT_DONE, &ha->flags);
|
||||||
|
|
||||||
|
qla4_8xxx_alloc_sysfs_attr(ha);
|
||||||
|
|
||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
" QLogic iSCSI HBA Driver version: %s\n"
|
" QLogic iSCSI HBA Driver version: %s\n"
|
||||||
" QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
|
" QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
|
||||||
|
@ -5194,6 +5215,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
|
||||||
iscsi_boot_destroy_kset(ha->boot_kset);
|
iscsi_boot_destroy_kset(ha->boot_kset);
|
||||||
|
|
||||||
qla4xxx_destroy_fw_ddb_session(ha);
|
qla4xxx_destroy_fw_ddb_session(ha);
|
||||||
|
qla4_8xxx_free_sysfs_attr(ha);
|
||||||
|
|
||||||
scsi_remove_host(ha->host);
|
scsi_remove_host(ha->host);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue