summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla2xxx/qla_nx2.c
diff options
context:
space:
mode:
authorPratik Mohanty <pratik.mohanty@qlogic.com>2014-04-11 20:54:15 (GMT)
committerChristoph Hellwig <hch@lst.de>2014-05-19 11:31:00 (GMT)
commit804df800256c1f2ff24b0350589d0bdad5300026 (patch)
tree664b1070b4f9d494ecfabd2beba4551b6e3ef0fb /drivers/scsi/qla2xxx/qla_nx2.c
parentedaa5c74177a6856f108b4909407985b6d3f951c (diff)
downloadlinux-804df800256c1f2ff24b0350589d0bdad5300026.tar.xz
qla2xxx: Support of new firmware dump opcodes QLA8044_RDDFE(38), QLA8044_RDMDIO(39),QLA8044_POLLWR(40).
Signed-off-by: Pratik Mohanty <pratik.mohanty@qlogic.com> Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_nx2.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c
index d36a623..6d73e2e 100644
--- a/drivers/scsi/qla2xxx/qla_nx2.c
+++ b/drivers/scsi/qla2xxx/qla_nx2.c
@@ -12,6 +12,8 @@
#include <linux/delay.h>
+#define TIMEOUT_100_MS 100
+
/* 8044 Flash Read/Write functions */
uint32_t
qla8044_rd_reg(struct qla_hw_data *ha, ulong addr)
@@ -117,6 +119,96 @@ qla8044_read_write_crb_reg(struct scsi_qla_host *vha,
qla8044_wr_reg_indirect(vha, waddr, value);
}
+static int
+qla8044_poll_wait_for_ready(struct scsi_qla_host *vha, uint32_t addr1,
+ uint32_t mask)
+{
+ unsigned long timeout;
+ uint32_t temp;
+
+ /* jiffies after 100ms */
+ timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS);
+ do {
+ qla8044_rd_reg_indirect(vha, addr1, &temp);
+ if ((temp & mask) != 0)
+ break;
+ if (time_after_eq(jiffies, timeout)) {
+ ql_log(ql_log_warn, vha, 0xb151,
+ "Error in processing rdmdio entry\n");
+ return -1;
+ }
+ } while (1);
+
+ return 0;
+}
+
+static uint32_t
+qla8044_ipmdio_rd_reg(struct scsi_qla_host *vha,
+ uint32_t addr1, uint32_t addr3, uint32_t mask, uint32_t addr)
+{
+ uint32_t temp;
+ int ret = 0;
+
+ ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
+ if (ret == -1)
+ return -1;
+
+ temp = (0x40000000 | addr);
+ qla8044_wr_reg_indirect(vha, addr1, temp);
+
+ ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
+ if (ret == -1)
+ return 0;
+
+ qla8044_rd_reg_indirect(vha, addr3, &ret);
+
+ return ret;
+}
+
+
+static int
+qla8044_poll_wait_ipmdio_bus_idle(struct scsi_qla_host *vha,
+ uint32_t addr1, uint32_t addr2, uint32_t addr3, uint32_t mask)
+{
+ unsigned long timeout;
+ uint32_t temp;
+
+ /* jiffies after 100 msecs */
+ timeout = jiffies + (HZ / 1000) * TIMEOUT_100_MS;
+ do {
+ temp = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr2);
+ if ((temp & 0x1) != 1)
+ break;
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (time_after_eq(jiffies, timeout)) {
+ ql_log(ql_log_warn, vha, 0xb152,
+ "Error in processing mdiobus idle\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qla8044_ipmdio_wr_reg(struct scsi_qla_host *vha, uint32_t addr1,
+ uint32_t addr3, uint32_t mask, uint32_t addr, uint32_t value)
+{
+ int ret = 0;
+
+ ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
+ if (ret == -1)
+ return -1;
+
+ qla8044_wr_reg_indirect(vha, addr3, value);
+ qla8044_wr_reg_indirect(vha, addr1, addr);
+
+ ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
+ if (ret == -1)
+ return -1;
+
+ return 0;
+}
/*
* qla8044_rmw_crb_reg - Read value from raddr, AND with test_mask,
* Shift Left,Right/OR/XOR with values RMW header and write value to waddr.
@@ -2897,6 +2989,231 @@ error_exit:
return rval;
}
+static uint32_t
+qla8044_minidump_process_rddfe(struct scsi_qla_host *vha,
+ struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
+{
+ int loop_cnt;
+ uint32_t addr1, addr2, value, data, temp, wrVal;
+ uint8_t stride, stride2;
+ uint16_t count;
+ uint32_t poll, mask, data_size, modify_mask;
+ uint32_t wait_count = 0;
+
+ uint32_t *data_ptr = *d_ptr;
+
+ struct qla8044_minidump_entry_rddfe *rddfe;
+ rddfe = (struct qla8044_minidump_entry_rddfe *) entry_hdr;
+
+ addr1 = rddfe->addr_1;
+ value = rddfe->value;
+ stride = rddfe->stride;
+ stride2 = rddfe->stride2;
+ count = rddfe->count;
+
+ poll = rddfe->poll;
+ mask = rddfe->mask;
+ modify_mask = rddfe->modify_mask;
+ data_size = rddfe->data_size;
+
+ addr2 = addr1 + stride;
+
+ for (loop_cnt = 0x0; loop_cnt < count; loop_cnt++) {
+ qla8044_wr_reg_indirect(vha, addr1, (0x40000000 | value));
+
+ wait_count = 0;
+ while (wait_count < poll) {
+ qla8044_rd_reg_indirect(vha, addr1, &temp);
+ if ((temp & mask) != 0)
+ break;
+ wait_count++;
+ }
+
+ if (wait_count == poll) {
+ ql_log(ql_log_warn, vha, 0xb153,
+ "%s: TIMEOUT\n", __func__);
+ goto error;
+ } else {
+ qla8044_rd_reg_indirect(vha, addr2, &temp);
+ temp = temp & modify_mask;
+ temp = (temp | ((loop_cnt << 16) | loop_cnt));
+ wrVal = ((temp << 16) | temp);
+
+ qla8044_wr_reg_indirect(vha, addr2, wrVal);
+ qla8044_wr_reg_indirect(vha, addr1, value);
+
+ wait_count = 0;
+ while (wait_count < poll) {
+ qla8044_rd_reg_indirect(vha, addr1, &temp);
+ if ((temp & mask) != 0)
+ break;
+ wait_count++;
+ }
+ if (wait_count == poll) {
+ ql_log(ql_log_warn, vha, 0xb154,
+ "%s: TIMEOUT\n", __func__);
+ goto error;
+ }
+
+ qla8044_wr_reg_indirect(vha, addr1,
+ ((0x40000000 | value) + stride2));
+ wait_count = 0;
+ while (wait_count < poll) {
+ qla8044_rd_reg_indirect(vha, addr1, &temp);
+ if ((temp & mask) != 0)
+ break;
+ wait_count++;
+ }
+
+ if (wait_count == poll) {
+ ql_log(ql_log_warn, vha, 0xb155,
+ "%s: TIMEOUT\n", __func__);
+ goto error;
+ }
+
+ qla8044_rd_reg_indirect(vha, addr2, &data);
+
+ *data_ptr++ = wrVal;
+ *data_ptr++ = data;
+ }
+
+ }
+
+ *d_ptr = data_ptr;
+ return QLA_SUCCESS;
+
+error:
+ return -1;
+
+}
+
+static uint32_t
+qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha,
+ struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
+{
+ int ret = 0;
+ uint32_t addr1, addr2, value1, value2, data, selVal;
+ uint8_t stride1, stride2;
+ uint32_t addr3, addr4, addr5, addr6, addr7;
+ uint16_t count, loop_cnt;
+ uint32_t poll, mask;
+ uint32_t *data_ptr = *d_ptr;
+
+ struct qla8044_minidump_entry_rdmdio *rdmdio;
+
+ rdmdio = (struct qla8044_minidump_entry_rdmdio *) entry_hdr;
+
+ addr1 = rdmdio->addr_1;
+ addr2 = rdmdio->addr_2;
+ value1 = rdmdio->value_1;
+ stride1 = rdmdio->stride_1;
+ stride2 = rdmdio->stride_2;
+ count = rdmdio->count;
+
+ poll = rdmdio->poll;
+ mask = rdmdio->mask;
+ value2 = rdmdio->value_2;
+
+ addr3 = addr1 + stride1;
+
+ for (loop_cnt = 0; loop_cnt < count; loop_cnt++) {
+ ret = qla8044_poll_wait_ipmdio_bus_idle(vha, addr1, addr2,
+ addr3, mask);
+ if (ret == -1)
+ goto error;
+
+ addr4 = addr2 - stride1;
+ ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask, addr4,
+ value2);
+ if (ret == -1)
+ goto error;
+
+ addr5 = addr2 - (2 * stride1);
+ ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask, addr5,
+ value1);
+ if (ret == -1)
+ goto error;
+
+ addr6 = addr2 - (3 * stride1);
+ ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask,
+ addr6, 0x2);
+ if (ret == -1)
+ goto error;
+
+ ret = qla8044_poll_wait_ipmdio_bus_idle(vha, addr1, addr2,
+ addr3, mask);
+ if (ret == -1)
+ goto error;
+
+ addr7 = addr2 - (4 * stride1);
+ data = qla8044_ipmdio_rd_reg(vha, addr1, addr3,
+ mask, addr7);
+ if (data == -1)
+ goto error;
+
+ selVal = (value2 << 18) | (value1 << 2) | 2;
+
+ stride2 = rdmdio->stride_2;
+ *data_ptr++ = selVal;
+ *data_ptr++ = data;
+
+ value1 = value1 + stride2;
+ *d_ptr = data_ptr;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+static uint32_t qla8044_minidump_process_pollwr(struct scsi_qla_host *vha,
+ struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
+{
+ uint32_t addr1, addr2, value1, value2, poll, mask, r_value;
+ uint32_t wait_count = 0;
+ struct qla8044_minidump_entry_pollwr *pollwr_hdr;
+
+ pollwr_hdr = (struct qla8044_minidump_entry_pollwr *)entry_hdr;
+ addr1 = pollwr_hdr->addr_1;
+ addr2 = pollwr_hdr->addr_2;
+ value1 = pollwr_hdr->value_1;
+ value2 = pollwr_hdr->value_2;
+
+ poll = pollwr_hdr->poll;
+ mask = pollwr_hdr->mask;
+
+ while (wait_count < poll) {
+ qla8044_rd_reg_indirect(vha, addr1, &r_value);
+
+ if ((r_value & poll) != 0)
+ break;
+ wait_count++;
+ }
+
+ if (wait_count == poll) {
+ ql_log(ql_log_warn, vha, 0xb156, "%s: TIMEOUT\n", __func__);
+ goto error;
+ }
+
+ qla8044_wr_reg_indirect(vha, addr2, value2);
+ qla8044_wr_reg_indirect(vha, addr1, value1);
+
+ wait_count = 0;
+ while (wait_count < poll) {
+ qla8044_rd_reg_indirect(vha, addr1, &r_value);
+
+ if ((r_value & poll) != 0)
+ break;
+ wait_count++;
+ }
+
+ return QLA_SUCCESS;
+
+error:
+ return -1;
+}
+
/*
*
* qla8044_collect_md_data - Retrieve firmware minidump data.
@@ -3104,6 +3421,24 @@ qla8044_collect_md_data(struct scsi_qla_host *vha)
if (rval != QLA_SUCCESS)
qla8044_mark_entry_skipped(vha, entry_hdr, i);
break;
+ case QLA8044_RDDFE:
+ rval = qla8044_minidump_process_rddfe(vha, entry_hdr,
+ &data_ptr);
+ if (rval != QLA_SUCCESS)
+ qla8044_mark_entry_skipped(vha, entry_hdr, i);
+ break;
+ case QLA8044_RDMDIO:
+ rval = qla8044_minidump_process_rdmdio(vha, entry_hdr,
+ &data_ptr);
+ if (rval != QLA_SUCCESS)
+ qla8044_mark_entry_skipped(vha, entry_hdr, i);
+ break;
+ case QLA8044_POLLWR:
+ rval = qla8044_minidump_process_pollwr(vha, entry_hdr,
+ &data_ptr);
+ if (rval != QLA_SUCCESS)
+ qla8044_mark_entry_skipped(vha, entry_hdr, i);
+ break;
case QLA82XX_RDNOP:
default:
qla8044_mark_entry_skipped(vha, entry_hdr, i);