|
@@ -7,6 +7,8 @@
|
|
|
#include "qla_def.h"
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/pci.h>
|
|
|
+#include <linux/ratelimit.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
#include <scsi/scsi_tcq.h>
|
|
|
|
|
|
#define MASK(n) ((1ULL<<(n))-1)
|
|
@@ -328,7 +330,7 @@ unsigned qla82xx_crb_hub_agt[64] = {
|
|
|
};
|
|
|
|
|
|
/* Device states */
|
|
|
-char *qdev_state[] = {
|
|
|
+char *q_dev_state[] = {
|
|
|
"Unknown",
|
|
|
"Cold",
|
|
|
"Initializing",
|
|
@@ -339,6 +341,11 @@ char *qdev_state[] = {
|
|
|
"Quiescent",
|
|
|
};
|
|
|
|
|
|
+char *qdev_state(uint32_t dev_state)
|
|
|
+{
|
|
|
+ return q_dev_state[dev_state];
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* In: 'off' is offset from CRB space in 128M pci map
|
|
|
* Out: 'off' is 2M pci map addr
|
|
@@ -2355,9 +2362,13 @@ qla82xx_need_reset(struct qla_hw_data *ha)
|
|
|
uint32_t drv_state;
|
|
|
int rval;
|
|
|
|
|
|
- drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
- rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
|
|
|
- return rval;
|
|
|
+ if (ha->flags.isp82xx_reset_owner)
|
|
|
+ return 1;
|
|
|
+ else {
|
|
|
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
+ rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static inline void
|
|
@@ -2374,8 +2385,8 @@ qla82xx_set_rst_ready(struct qla_hw_data *ha)
|
|
|
drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
}
|
|
|
drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
|
|
|
- ql_log(ql_log_info, vha, 0x00bb,
|
|
|
- "drv_state = 0x%x.\n", drv_state);
|
|
|
+ ql_dbg(ql_dbg_init, vha, 0x00bb,
|
|
|
+ "drv_state = 0x%08x.\n", drv_state);
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
|
|
|
}
|
|
|
|
|
@@ -3528,7 +3539,7 @@ qla82xx_dev_failed_handler(scsi_qla_host_t *vha)
|
|
|
static void
|
|
|
qla82xx_need_reset_handler(scsi_qla_host_t *vha)
|
|
|
{
|
|
|
- uint32_t dev_state, drv_state, drv_active;
|
|
|
+ uint32_t dev_state, drv_state, drv_active, active_mask;
|
|
|
unsigned long reset_timeout;
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
struct req_que *req = ha->req_q_map[0];
|
|
@@ -3541,15 +3552,32 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha)
|
|
|
qla82xx_idc_lock(ha);
|
|
|
}
|
|
|
|
|
|
- qla82xx_set_rst_ready(ha);
|
|
|
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
|
|
+ if (!ha->flags.isp82xx_reset_owner) {
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb028,
|
|
|
+ "reset_acknowledged by 0x%x\n", ha->portnum);
|
|
|
+ qla82xx_set_rst_ready(ha);
|
|
|
+ } else {
|
|
|
+ active_mask = ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
|
|
|
+ drv_active &= active_mask;
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb029,
|
|
|
+ "active_mask: 0x%08x\n", active_mask);
|
|
|
+ }
|
|
|
|
|
|
/* wait for 10 seconds for reset ack from all functions */
|
|
|
reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
|
|
|
|
|
|
drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
|
|
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
|
|
|
- while (drv_state != drv_active) {
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb02a,
|
|
|
+ "drv_state: 0x%08x, drv_active: 0x%08x, "
|
|
|
+ "dev_state: 0x%08x, active_mask: 0x%08x\n",
|
|
|
+ drv_state, drv_active, dev_state, active_mask);
|
|
|
+
|
|
|
+ while (drv_state != drv_active &&
|
|
|
+ dev_state != QLA82XX_DEV_INITIALIZING) {
|
|
|
if (time_after_eq(jiffies, reset_timeout)) {
|
|
|
ql_log(ql_log_warn, vha, 0x00b5,
|
|
|
"Reset timeout.\n");
|
|
@@ -3560,22 +3588,78 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha)
|
|
|
qla82xx_idc_lock(ha);
|
|
|
drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
|
|
+ if (ha->flags.isp82xx_reset_owner)
|
|
|
+ drv_active &= active_mask;
|
|
|
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
}
|
|
|
|
|
|
- dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb02b,
|
|
|
+ "drv_state: 0x%08x, drv_active: 0x%08x, "
|
|
|
+ "dev_state: 0x%08x, active_mask: 0x%08x\n",
|
|
|
+ drv_state, drv_active, dev_state, active_mask);
|
|
|
+
|
|
|
ql_log(ql_log_info, vha, 0x00b6,
|
|
|
"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 */
|
|
|
- if (dev_state != QLA82XX_DEV_INITIALIZING) {
|
|
|
+ if (dev_state != QLA82XX_DEV_INITIALIZING &&
|
|
|
+ dev_state != QLA82XX_DEV_COLD) {
|
|
|
ql_log(ql_log_info, vha, 0x00b7,
|
|
|
"HW State: COLD/RE-INIT.\n");
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
|
|
|
+ if (ql2xmdenable) {
|
|
|
+ if (qla82xx_md_collect(vha))
|
|
|
+ ql_log(ql_log_warn, vha, 0xb02c,
|
|
|
+ "Not able to collect minidump.\n");
|
|
|
+ } else
|
|
|
+ ql_log(ql_log_warn, vha, 0xb04f,
|
|
|
+ "Minidump disabled.\n");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+qla82xx_check_md_needed(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint16_t fw_major_version, fw_minor_version, fw_subminor_version;
|
|
|
+ uint16_t fw_attributes;
|
|
|
+ uint32_t fw_memory_size, mpi_capabilities;
|
|
|
+ uint8_t mpi_version[3], phy_version[3];
|
|
|
+
|
|
|
+ if (!ha->fw_dumped) {
|
|
|
+ qla2x00_get_fw_version(vha,
|
|
|
+ &fw_major_version,
|
|
|
+ &fw_minor_version,
|
|
|
+ &fw_subminor_version,
|
|
|
+ &fw_attributes, &fw_memory_size,
|
|
|
+ mpi_version, &mpi_capabilities,
|
|
|
+ phy_version);
|
|
|
+
|
|
|
+ if (fw_major_version != ha->fw_major_version ||
|
|
|
+ fw_minor_version != ha->fw_minor_version ||
|
|
|
+ fw_subminor_version != ha->fw_subminor_version) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb02d,
|
|
|
+ "Firmware version differs "
|
|
|
+ "Previous version: %d:%d:%d - "
|
|
|
+ "New version: %d:%d:%d\n",
|
|
|
+ ha->fw_major_version,
|
|
|
+ ha->fw_minor_version, ha->fw_subminor_version,
|
|
|
+ fw_major_version, fw_minor_version,
|
|
|
+ fw_subminor_version);
|
|
|
+ /* Release MiniDump resources */
|
|
|
+ qla82xx_md_free(vha);
|
|
|
+ /* ALlocate MiniDump resources */
|
|
|
+ qla82xx_md_prep(vha);
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ ql_log(ql_log_info, vha, 0xb02e,
|
|
|
+ "Firmware dump available to retrieve\n",
|
|
|
+ vha->host_no);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
int
|
|
|
qla82xx_check_fw_alive(scsi_qla_host_t *vha)
|
|
|
{
|
|
@@ -3637,7 +3721,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
|
|
|
ql_log(ql_log_info, vha, 0x009b,
|
|
|
"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");
|
|
|
|
|
|
/* wait for 30 seconds for device to go ready */
|
|
|
dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
|
|
@@ -3659,16 +3743,18 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
|
|
|
ql_log(ql_log_info, vha, 0x009d,
|
|
|
"Device state is 0x%x = %s.\n",
|
|
|
dev_state,
|
|
|
- dev_state < MAX_STATES ? qdev_state[dev_state] :
|
|
|
+ dev_state < MAX_STATES ? qdev_state(dev_state) :
|
|
|
"Unknown");
|
|
|
}
|
|
|
|
|
|
switch (dev_state) {
|
|
|
case QLA82XX_DEV_READY:
|
|
|
+ qla82xx_check_md_needed(vha);
|
|
|
+ ha->flags.isp82xx_reset_owner = 0;
|
|
|
goto exit;
|
|
|
case QLA82XX_DEV_COLD:
|
|
|
rval = qla82xx_device_bootstrap(vha);
|
|
|
- goto exit;
|
|
|
+ break;
|
|
|
case QLA82XX_DEV_INITIALIZING:
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
msleep(1000);
|
|
@@ -3791,6 +3877,28 @@ int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+qla82xx_set_reset_owner(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t dev_state;
|
|
|
+
|
|
|
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
+ if (dev_state == QLA82XX_DEV_READY) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb02f,
|
|
|
+ "HW State: NEED RESET\n");
|
|
|
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
+ QLA82XX_DEV_NEED_RESET);
|
|
|
+ ha->flags.isp82xx_reset_owner = 1;
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb030,
|
|
|
+ "reset_owner is 0x%x\n", ha->portnum);
|
|
|
+ } else
|
|
|
+ ql_log(ql_log_info, vha, 0xb031,
|
|
|
+ "Device state is 0x%x = %s.\n",
|
|
|
+ dev_state,
|
|
|
+ dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* qla82xx_abort_isp
|
|
|
* Resets ISP and aborts all outstanding commands.
|
|
@@ -3806,7 +3914,6 @@ qla82xx_abort_isp(scsi_qla_host_t *vha)
|
|
|
{
|
|
|
int rval;
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
- uint32_t dev_state;
|
|
|
|
|
|
if (vha->device_flags & DFLG_DEV_FAILED) {
|
|
|
ql_log(ql_log_warn, vha, 0x8024,
|
|
@@ -3816,16 +3923,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha)
|
|
|
ha->flags.isp82xx_reset_hdlr_active = 1;
|
|
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
- dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
- if (dev_state == QLA82XX_DEV_READY) {
|
|
|
- ql_log(ql_log_info, vha, 0x8025,
|
|
|
- "HW State: NEED RESET.\n");
|
|
|
- qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_NEED_RESET);
|
|
|
- } else
|
|
|
- ql_log(ql_log_info, vha, 0x8026,
|
|
|
- "Hw State: %s.\n", dev_state < MAX_STATES ?
|
|
|
- qdev_state[dev_state] : "Unknown");
|
|
|
+ qla82xx_set_reset_owner(vha);
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
|
|
rval = qla82xx_device_state_handler(vha);
|
|
@@ -4016,3 +4114,763 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* Minidump related functions */
|
|
|
+int
|
|
|
+qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag)
|
|
|
+{
|
|
|
+ uint32_t off_value, rval = 0;
|
|
|
+
|
|
|
+ WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase),
|
|
|
+ (off & 0xFFFF0000));
|
|
|
+
|
|
|
+ /* Read back value to make sure write has gone through */
|
|
|
+ RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
|
|
|
+ off_value = (off & 0x0000FFFF);
|
|
|
+
|
|
|
+ if (flag)
|
|
|
+ WRT_REG_DWORD((void *)
|
|
|
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase),
|
|
|
+ data);
|
|
|
+ else
|
|
|
+ rval = RD_REG_DWORD((void *)
|
|
|
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase));
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qla82xx_minidump_process_control(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ struct qla82xx_md_entry_crb *crb_entry;
|
|
|
+ uint32_t read_value, opcode, poll_time;
|
|
|
+ uint32_t addr, index, crb_addr;
|
|
|
+ unsigned long wtime;
|
|
|
+ struct qla82xx_md_template_hdr *tmplt_hdr;
|
|
|
+ uint32_t rval = QLA_SUCCESS;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
|
|
|
+ crb_entry = (struct qla82xx_md_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) {
|
|
|
+ qla82xx_md_rw_32(ha, crb_addr,
|
|
|
+ crb_entry->value_1, 1);
|
|
|
+ opcode &= ~QLA82XX_DBG_OPCODE_WR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opcode & QLA82XX_DBG_OPCODE_RW) {
|
|
|
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
|
|
|
+ qla82xx_md_rw_32(ha, crb_addr, read_value, 1);
|
|
|
+ opcode &= ~QLA82XX_DBG_OPCODE_RW;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opcode & QLA82XX_DBG_OPCODE_AND) {
|
|
|
+ read_value = qla82xx_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;
|
|
|
+ }
|
|
|
+ qla82xx_md_rw_32(ha, crb_addr, read_value, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opcode & QLA82XX_DBG_OPCODE_OR) {
|
|
|
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
|
|
|
+ read_value |= crb_entry->value_3;
|
|
|
+ qla82xx_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 = qla82xx_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_FUNCTION_FAILED;
|
|
|
+ break;
|
|
|
+ } else
|
|
|
+ read_value = qla82xx_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 = qla82xx_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;
|
|
|
+
|
|
|
+ qla82xx_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;
|
|
|
+ }
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t r_addr, r_stride, loop_cnt, i, r_value;
|
|
|
+ struct qla82xx_md_entry_rdocm *ocm_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ ocm_hdr = (struct qla82xx_md_entry_rdocm *)entry_hdr;
|
|
|
+ r_addr = ocm_hdr->read_addr;
|
|
|
+ r_stride = ocm_hdr->read_addr_stride;
|
|
|
+ loop_cnt = ocm_hdr->op_count;
|
|
|
+
|
|
|
+ for (i = 0; i < loop_cnt; i++) {
|
|
|
+ r_value = RD_REG_DWORD((void *)(r_addr + ha->nx_pcibase));
|
|
|
+ *data_ptr++ = cpu_to_le32(r_value);
|
|
|
+ r_addr += r_stride;
|
|
|
+ }
|
|
|
+ *d_ptr = data_ptr;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+qla82xx_minidump_process_rdmux(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
|
|
|
+ struct qla82xx_md_entry_mux *mux_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ mux_hdr = (struct qla82xx_md_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++) {
|
|
|
+ qla82xx_md_rw_32(ha, s_addr, s_value, 1);
|
|
|
+ r_value = qla82xx_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
|
|
|
+qla82xx_minidump_process_rdcrb(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t r_addr, r_stride, loop_cnt, i, r_value;
|
|
|
+ struct qla82xx_md_entry_crb *crb_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ crb_hdr = (struct qla82xx_md_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 = qla82xx_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
|
|
|
+qla82xx_minidump_process_l2tag(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ 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_md_entry_cache *cache_hdr;
|
|
|
+ int rval = QLA_FUNCTION_FAILED;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ cache_hdr = (struct qla82xx_md_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++) {
|
|
|
+ qla82xx_md_rw_32(ha, t_r_addr, t_value, 1);
|
|
|
+ if (c_value_w)
|
|
|
+ qla82xx_md_rw_32(ha, c_addr, c_value_w, 1);
|
|
|
+
|
|
|
+ if (p_mask) {
|
|
|
+ w_time = jiffies + p_wait;
|
|
|
+ do {
|
|
|
+ c_value_r = qla82xx_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 */
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb032,
|
|
|
+ "c_value_r: 0x%x, poll_mask: 0x%lx, "
|
|
|
+ "w_time: 0x%lx\n",
|
|
|
+ c_value_r, p_mask, w_time);
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
+ } while (1);
|
|
|
+ }
|
|
|
+
|
|
|
+ addr = r_addr;
|
|
|
+ for (k = 0; k < r_cnt; k++) {
|
|
|
+ r_value = qla82xx_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 void
|
|
|
+qla82xx_minidump_process_l1cache(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ 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_md_entry_cache *cache_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ cache_hdr = (struct qla82xx_md_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++) {
|
|
|
+ qla82xx_md_rw_32(ha, t_r_addr, t_value, 1);
|
|
|
+ qla82xx_md_rw_32(ha, c_addr, c_value_w, 1);
|
|
|
+ addr = r_addr;
|
|
|
+ for (k = 0; k < r_cnt; k++) {
|
|
|
+ r_value = qla82xx_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
|
|
|
+qla82xx_minidump_process_queue(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t s_addr, r_addr;
|
|
|
+ uint32_t r_stride, r_value, r_cnt, qid = 0;
|
|
|
+ uint32_t i, k, loop_cnt;
|
|
|
+ struct qla82xx_md_entry_queue *q_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ q_hdr = (struct qla82xx_md_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++) {
|
|
|
+ qla82xx_md_rw_32(ha, s_addr, qid, 1);
|
|
|
+ r_addr = q_hdr->read_addr;
|
|
|
+ for (k = 0; k < r_cnt; k++) {
|
|
|
+ r_value = qla82xx_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;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+qla82xx_minidump_process_rdrom(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t r_addr, r_value;
|
|
|
+ uint32_t i, loop_cnt;
|
|
|
+ struct qla82xx_md_entry_rdrom *rom_hdr;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ rom_hdr = (struct qla82xx_md_entry_rdrom *)entry_hdr;
|
|
|
+ r_addr = rom_hdr->read_addr;
|
|
|
+ loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t);
|
|
|
+
|
|
|
+ for (i = 0; i < loop_cnt; i++) {
|
|
|
+ qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW,
|
|
|
+ (r_addr & 0xFFFF0000), 1);
|
|
|
+ r_value = qla82xx_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;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t r_addr, r_value, r_data;
|
|
|
+ uint32_t i, j, loop_cnt;
|
|
|
+ struct qla82xx_md_entry_rdmem *m_hdr;
|
|
|
+ unsigned long flags;
|
|
|
+ int rval = QLA_FUNCTION_FAILED;
|
|
|
+ uint32_t *data_ptr = *d_ptr;
|
|
|
+
|
|
|
+ m_hdr = (struct qla82xx_md_entry_rdmem *)entry_hdr;
|
|
|
+ r_addr = m_hdr->read_addr;
|
|
|
+ loop_cnt = m_hdr->read_data_size/16;
|
|
|
+
|
|
|
+ if (r_addr & 0xf) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb033,
|
|
|
+ "Read addr 0x%x not 16 bytes alligned\n", r_addr);
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_hdr->read_data_size % 16) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb034,
|
|
|
+ "Read data[0x%x] not multiple of 16 bytes\n",
|
|
|
+ m_hdr->read_data_size);
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
+
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb035,
|
|
|
+ "[%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++) {
|
|
|
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1);
|
|
|
+ r_value = 0;
|
|
|
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1);
|
|
|
+ r_value = MIU_TA_CTL_ENABLE;
|
|
|
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
|
|
|
+ r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
|
|
|
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
|
|
|
+
|
|
|
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
|
|
|
+ r_value = qla82xx_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
|
|
|
+ "failed to read through agent\n");
|
|
|
+ write_unlock_irqrestore(&ha->hw_lock, flags);
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (j = 0; j < 4; j++) {
|
|
|
+ r_data = qla82xx_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);
|
|
|
+ *d_ptr = data_ptr;
|
|
|
+ return QLA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qla82xx_validate_template_chksum(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint64_t chksum = 0;
|
|
|
+ uint32_t *d_ptr = (uint32_t *)ha->md_tmplt_hdr;
|
|
|
+ int count = ha->md_template_size/sizeof(uint32_t);
|
|
|
+
|
|
|
+ while (count-- > 0)
|
|
|
+ chksum += *d_ptr++;
|
|
|
+ while (chksum >> 32)
|
|
|
+ chksum = (chksum & 0xFFFFFFFF) + (chksum >> 32);
|
|
|
+ return ~chksum;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+qla82xx_mark_entry_skipped(scsi_qla_host_t *vha,
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr, int index)
|
|
|
+{
|
|
|
+ entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG;
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb036,
|
|
|
+ "Skipping entry[%d]: "
|
|
|
+ "ETYPE[0x%x]-ELEVEL[0x%x]\n",
|
|
|
+ index, entry_hdr->entry_type,
|
|
|
+ entry_hdr->d_ctrl.entry_capture_mask);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla82xx_md_collect(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ int no_entry_hdr = 0;
|
|
|
+ qla82xx_md_entry_hdr_t *entry_hdr;
|
|
|
+ struct qla82xx_md_template_hdr *tmplt_hdr;
|
|
|
+ uint32_t *data_ptr;
|
|
|
+ uint32_t total_data_size = 0, f_capture_mask, data_collected = 0;
|
|
|
+ int i = 0, rval = QLA_FUNCTION_FAILED;
|
|
|
+
|
|
|
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
|
|
|
+ data_ptr = (uint32_t *)ha->md_dump;
|
|
|
+
|
|
|
+ if (ha->fw_dumped) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb037,
|
|
|
+ "Firmware dump available to retrive\n");
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ ha->fw_dumped = 0;
|
|
|
+
|
|
|
+ if (!ha->md_tmplt_hdr || !ha->md_dump) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb038,
|
|
|
+ "Memory not allocated for minidump capture\n");
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (qla82xx_validate_template_chksum(vha)) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb039,
|
|
|
+ "Template checksum validation error\n");
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ no_entry_hdr = tmplt_hdr->num_of_entries;
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb03a,
|
|
|
+ "No of entry headers in Template: 0x%x\n", no_entry_hdr);
|
|
|
+
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb03b,
|
|
|
+ "Capture Mask obtained: 0x%x\n", tmplt_hdr->capture_debug_level);
|
|
|
+
|
|
|
+ f_capture_mask = tmplt_hdr->capture_debug_level & 0xFF;
|
|
|
+
|
|
|
+ /* Validate whether required debug level is set */
|
|
|
+ if ((f_capture_mask & 0x3) != 0x3) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb03c,
|
|
|
+ "Minimum required capture mask[0x%x] level not set\n",
|
|
|
+ f_capture_mask);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+ tmplt_hdr->driver_capture_mask = ql2xmdcapmask;
|
|
|
+
|
|
|
+ tmplt_hdr->driver_info[0] = vha->host_no;
|
|
|
+ tmplt_hdr->driver_info[1] = (QLA_DRIVER_MAJOR_VER << 24) |
|
|
|
+ (QLA_DRIVER_MINOR_VER << 16) | (QLA_DRIVER_PATCH_VER << 8) |
|
|
|
+ QLA_DRIVER_BETA_VER;
|
|
|
+
|
|
|
+ total_data_size = ha->md_dump_size;
|
|
|
+
|
|
|
+ ql_dbg(ql_log_info, vha, 0xb03d,
|
|
|
+ "Total minidump data_size 0x%x to be captured\n", total_data_size);
|
|
|
+
|
|
|
+ /* Check whether template obtained is valid */
|
|
|
+ if (tmplt_hdr->entry_type != QLA82XX_TLHDR) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb04e,
|
|
|
+ "Bad template header entry type: 0x%x obtained\n",
|
|
|
+ tmplt_hdr->entry_type);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ entry_hdr = (qla82xx_md_entry_hdr_t *) \
|
|
|
+ (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset);
|
|
|
+
|
|
|
+ /* Walk through the entry headers */
|
|
|
+ for (i = 0; i < no_entry_hdr; i++) {
|
|
|
+
|
|
|
+ if (data_collected > total_data_size) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb03e,
|
|
|
+ "More MiniDump data collected: [0x%x]\n",
|
|
|
+ data_collected);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(entry_hdr->d_ctrl.entry_capture_mask &
|
|
|
+ ql2xmdcapmask)) {
|
|
|
+ entry_hdr->d_ctrl.driver_flags |=
|
|
|
+ QLA82XX_DBG_SKIPPED_FLAG;
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb03f,
|
|
|
+ "Skipping entry[%d]: "
|
|
|
+ "ETYPE[0x%x]-ELEVEL[0x%x]\n",
|
|
|
+ i, entry_hdr->entry_type,
|
|
|
+ entry_hdr->d_ctrl.entry_capture_mask);
|
|
|
+ goto skip_nxt_entry;
|
|
|
+ }
|
|
|
+
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb040,
|
|
|
+ "[%s]: data ptr[%d]: %p, entry_hdr: %p\n"
|
|
|
+ "entry_type: 0x%x, captrue_mask: 0x%x\n",
|
|
|
+ __func__, i, data_ptr, entry_hdr,
|
|
|
+ entry_hdr->entry_type,
|
|
|
+ entry_hdr->d_ctrl.entry_capture_mask);
|
|
|
+
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb041,
|
|
|
+ "Data collected: [0x%x], Dump size left:[0x%x]\n",
|
|
|
+ data_collected, (ha->md_dump_size - data_collected));
|
|
|
+
|
|
|
+ /* Decode the entry type and take
|
|
|
+ * required action to capture debug data */
|
|
|
+ switch (entry_hdr->entry_type) {
|
|
|
+ case QLA82XX_RDEND:
|
|
|
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
|
|
|
+ break;
|
|
|
+ case QLA82XX_CNTRL:
|
|
|
+ rval = qla82xx_minidump_process_control(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ if (rval != QLA_SUCCESS) {
|
|
|
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case QLA82XX_RDCRB:
|
|
|
+ qla82xx_minidump_process_rdcrb(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_RDMEM:
|
|
|
+ rval = qla82xx_minidump_process_rdmem(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ if (rval != QLA_SUCCESS) {
|
|
|
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case QLA82XX_BOARD:
|
|
|
+ case QLA82XX_RDROM:
|
|
|
+ qla82xx_minidump_process_rdrom(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_L2DTG:
|
|
|
+ case QLA82XX_L2ITG:
|
|
|
+ case QLA82XX_L2DAT:
|
|
|
+ case QLA82XX_L2INS:
|
|
|
+ rval = qla82xx_minidump_process_l2tag(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ if (rval != QLA_SUCCESS) {
|
|
|
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case QLA82XX_L1DAT:
|
|
|
+ case QLA82XX_L1INS:
|
|
|
+ qla82xx_minidump_process_l1cache(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_RDOCM:
|
|
|
+ qla82xx_minidump_process_rdocm(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_RDMUX:
|
|
|
+ qla82xx_minidump_process_rdmux(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_QUEUE:
|
|
|
+ qla82xx_minidump_process_queue(vha,
|
|
|
+ entry_hdr, &data_ptr);
|
|
|
+ break;
|
|
|
+ case QLA82XX_RDNOP:
|
|
|
+ default:
|
|
|
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb042,
|
|
|
+ "[%s]: data ptr[%d]: %p\n", __func__, i, data_ptr);
|
|
|
+
|
|
|
+ data_collected = (uint8_t *)data_ptr -
|
|
|
+ (uint8_t *)ha->md_dump;
|
|
|
+skip_nxt_entry:
|
|
|
+ entry_hdr = (qla82xx_md_entry_hdr_t *) \
|
|
|
+ (((uint8_t *)entry_hdr) + entry_hdr->entry_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data_collected != total_data_size) {
|
|
|
+ ql_dbg(ql_log_warn, vha, 0xb043,
|
|
|
+ "MiniDump data mismatch: Data collected: [0x%x],"
|
|
|
+ "total_data_size:[0x%x]\n",
|
|
|
+ data_collected, total_data_size);
|
|
|
+ goto md_failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ ql_log(ql_log_info, vha, 0xb044,
|
|
|
+ "Firmware dump saved to temp buffer (%ld/%p %ld/%p).\n",
|
|
|
+ vha->host_no, ha->md_tmplt_hdr, vha->host_no, ha->md_dump);
|
|
|
+ ha->fw_dumped = 1;
|
|
|
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
|
|
|
+
|
|
|
+md_failed:
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla82xx_md_alloc(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ int i, k;
|
|
|
+ struct qla82xx_md_template_hdr *tmplt_hdr;
|
|
|
+
|
|
|
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
|
|
|
+
|
|
|
+ if (ql2xmdcapmask < 0x3 || ql2xmdcapmask > 0x7F) {
|
|
|
+ ql2xmdcapmask = tmplt_hdr->capture_debug_level & 0xFF;
|
|
|
+ ql_log(ql_log_info, vha, 0xb045,
|
|
|
+ "Forcing driver capture mask to firmware default capture mask: 0x%x.\n",
|
|
|
+ ql2xmdcapmask);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0x2, k = 1; (i & QLA82XX_DEFAULT_CAP_MASK); i <<= 1, k++) {
|
|
|
+ if (i & ql2xmdcapmask)
|
|
|
+ ha->md_dump_size += tmplt_hdr->capture_size_array[k];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ha->md_dump) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb046,
|
|
|
+ "Firmware dump previously allocated.\n");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ha->md_dump = vmalloc(ha->md_dump_size);
|
|
|
+ if (ha->md_dump == NULL) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb047,
|
|
|
+ "Unable to allocate memory for Minidump size "
|
|
|
+ "(0x%x).\n", ha->md_dump_size);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+qla82xx_md_free(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+
|
|
|
+ /* Release the template header allocated */
|
|
|
+ if (ha->md_tmplt_hdr) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb048,
|
|
|
+ "Free MiniDump template: %p, size (%d KB)\n",
|
|
|
+ ha->md_tmplt_hdr, ha->md_template_size / 1024);
|
|
|
+ dma_free_coherent(&ha->pdev->dev, ha->md_template_size,
|
|
|
+ ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
|
|
|
+ ha->md_tmplt_hdr = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Release the template data buffer allocated */
|
|
|
+ if (ha->md_dump) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb049,
|
|
|
+ "Free MiniDump memory: %p, size (%d KB)\n",
|
|
|
+ ha->md_dump, ha->md_dump_size / 1024);
|
|
|
+ vfree(ha->md_dump);
|
|
|
+ ha->md_dump_size = 0;
|
|
|
+ ha->md_dump = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+qla82xx_md_prep(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ int rval;
|
|
|
+
|
|
|
+ /* Get Minidump template size */
|
|
|
+ rval = qla82xx_md_get_template_size(vha);
|
|
|
+ if (rval == QLA_SUCCESS) {
|
|
|
+ ql_log(ql_log_info, vha, 0xb04a,
|
|
|
+ "MiniDump Template size obtained (%d KB)\n",
|
|
|
+ ha->md_template_size / 1024);
|
|
|
+
|
|
|
+ /* Get Minidump template */
|
|
|
+ rval = qla82xx_md_get_template(vha);
|
|
|
+ if (rval == QLA_SUCCESS) {
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb04b,
|
|
|
+ "MiniDump Template obtained\n");
|
|
|
+
|
|
|
+ /* Allocate memory for minidump */
|
|
|
+ rval = qla82xx_md_alloc(vha);
|
|
|
+ if (rval == QLA_SUCCESS)
|
|
|
+ ql_log(ql_log_info, vha, 0xb04c,
|
|
|
+ "MiniDump memory allocated (%d KB)\n",
|
|
|
+ ha->md_dump_size / 1024);
|
|
|
+ else {
|
|
|
+ ql_log(ql_log_info, vha, 0xb04d,
|
|
|
+ "Free MiniDump template: %p, size: (%d KB)\n",
|
|
|
+ ha->md_tmplt_hdr,
|
|
|
+ ha->md_template_size / 1024);
|
|
|
+ dma_free_coherent(&ha->pdev->dev,
|
|
|
+ ha->md_template_size,
|
|
|
+ ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
|
|
|
+ ha->md_tmplt_hdr = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|