summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>2013-11-22 05:28:17 -0500
committerJames Bottomley <JBottomley@Parallels.com>2013-12-19 20:56:25 -0800
commitdf86f771578209599acc871a03f02346c8d7885b (patch)
tree1532bc2abf8f6479a5bd34238ba917bc0a144c69
parent2da11ad218b1a30f46b4c8fdfa39aff6584c370b (diff)
[SCSI] qla4xxx: Added support for Diagnostics MBOX command
Added support for Diagnostics MBOX command via BSG Vendor HST_VENDOR interface. This command provides various tests for validating hardware functionality. Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com> Reviewed-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.c360
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.h13
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h8
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h14
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h2
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c19
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c43
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c3
8 files changed, 455 insertions, 7 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_bsg.c b/drivers/scsi/qla4xxx/ql4_bsg.c
index cf8fdf1d1257..04a0027dbca0 100644
--- a/drivers/scsi/qla4xxx/ql4_bsg.c
+++ b/drivers/scsi/qla4xxx/ql4_bsg.c
@@ -446,6 +446,363 @@ leave:
return rval;
}
+static void ql4xxx_execute_diag_cmd(struct bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
+ struct scsi_qla_host *ha = to_qla_host(host);
+ struct iscsi_bsg_request *bsg_req = bsg_job->request;
+ struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
+ uint8_t *rsp_ptr = NULL;
+ uint32_t mbox_cmd[MBOX_REG_COUNT];
+ uint32_t mbox_sts[MBOX_REG_COUNT];
+ int status = QLA_ERROR;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
+
+ if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+ ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
+ __func__);
+ bsg_reply->result = DID_ERROR << 16;
+ goto exit_diag_mem_test;
+ }
+
+ bsg_reply->reply_payload_rcv_len = 0;
+ memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
+ sizeof(uint32_t) * MBOX_REG_COUNT);
+
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
+ __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
+ mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
+ mbox_cmd[7]));
+
+ status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
+ &mbox_sts[0]);
+
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\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 (status == QLA_SUCCESS)
+ bsg_reply->result = DID_OK << 16;
+ else
+ bsg_reply->result = DID_ERROR << 16;
+
+ /* Send mbox_sts to application */
+ bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
+ rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
+ memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
+
+exit_diag_mem_test:
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: bsg_reply->result = x%x, status = %s\n",
+ __func__, bsg_reply->result, STATUS(status)));
+
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+}
+
+static int qla4_83xx_wait_for_loopback_config_comp(struct scsi_qla_host *ha,
+ int wait_for_link)
+{
+ int status = QLA_SUCCESS;
+
+ if (!wait_for_completion_timeout(&ha->idc_comp, (IDC_COMP_TOV * HZ))) {
+ ql4_printk(KERN_INFO, ha, "%s: IDC Complete notification not received, Waiting for another %d timeout",
+ __func__, ha->idc_extend_tmo);
+ if (ha->idc_extend_tmo) {
+ if (!wait_for_completion_timeout(&ha->idc_comp,
+ (ha->idc_extend_tmo * HZ))) {
+ ha->notify_idc_comp = 0;
+ ha->notify_link_up_comp = 0;
+ ql4_printk(KERN_WARNING, ha, "%s: IDC Complete notification not received",
+ __func__);
+ status = QLA_ERROR;
+ goto exit_wait;
+ } else {
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: IDC Complete notification received\n",
+ __func__));
+ }
+ }
+ } else {
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: IDC Complete notification received\n",
+ __func__));
+ }
+ ha->notify_idc_comp = 0;
+
+ if (wait_for_link) {
+ if (!wait_for_completion_timeout(&ha->link_up_comp,
+ (IDC_COMP_TOV * HZ))) {
+ ha->notify_link_up_comp = 0;
+ ql4_printk(KERN_WARNING, ha, "%s: LINK UP notification not received",
+ __func__);
+ status = QLA_ERROR;
+ goto exit_wait;
+ } else {
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: LINK UP notification received\n",
+ __func__));
+ }
+ ha->notify_link_up_comp = 0;
+ }
+
+exit_wait:
+ return status;
+}
+
+static int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha,
+ uint32_t *mbox_cmd)
+{
+ uint32_t config = 0;
+ int status = QLA_SUCCESS;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
+
+ status = qla4_83xx_get_port_config(ha, &config);
+ if (status != QLA_SUCCESS)
+ goto exit_pre_loopback_config;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Default port config=%08X\n",
+ __func__, config));
+
+ if ((config & ENABLE_INTERNAL_LOOPBACK) ||
+ (config & ENABLE_EXTERNAL_LOOPBACK)) {
+ ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid requiest\n",
+ __func__);
+ goto exit_pre_loopback_config;
+ }
+
+ if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
+ config |= ENABLE_INTERNAL_LOOPBACK;
+
+ if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
+ config |= ENABLE_EXTERNAL_LOOPBACK;
+
+ config &= ~ENABLE_DCBX;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: New port config=%08X\n",
+ __func__, config));
+
+ ha->notify_idc_comp = 1;
+ ha->notify_link_up_comp = 1;
+
+ /* get the link state */
+ qla4xxx_get_firmware_state(ha);
+
+ status = qla4_83xx_set_port_config(ha, &config);
+ if (status != QLA_SUCCESS) {
+ ha->notify_idc_comp = 0;
+ ha->notify_link_up_comp = 0;
+ goto exit_pre_loopback_config;
+ }
+exit_pre_loopback_config:
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
+ STATUS(status)));
+ return status;
+}
+
+static int qla4_83xx_post_loopback_config(struct scsi_qla_host *ha,
+ uint32_t *mbox_cmd)
+{
+ int status = QLA_SUCCESS;
+ uint32_t config = 0;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
+
+ status = qla4_83xx_get_port_config(ha, &config);
+ if (status != QLA_SUCCESS)
+ goto exit_post_loopback_config;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: port config=%08X\n", __func__,
+ config));
+
+ if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
+ config &= ~ENABLE_INTERNAL_LOOPBACK;
+ else if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
+ config &= ~ENABLE_EXTERNAL_LOOPBACK;
+
+ config |= ENABLE_DCBX;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: Restore default port config=%08X\n", __func__,
+ config));
+
+ ha->notify_idc_comp = 1;
+ if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP)
+ ha->notify_link_up_comp = 1;
+
+ status = qla4_83xx_set_port_config(ha, &config);
+ if (status != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "%s: Scheduling adapter reset\n",
+ __func__);
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+ clear_bit(AF_LOOPBACK, &ha->flags);
+ goto exit_post_loopback_config;
+ }
+
+exit_post_loopback_config:
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
+ STATUS(status)));
+ return status;
+}
+
+static void qla4xxx_execute_diag_loopback_cmd(struct bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
+ struct scsi_qla_host *ha = to_qla_host(host);
+ struct iscsi_bsg_request *bsg_req = bsg_job->request;
+ struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
+ uint8_t *rsp_ptr = NULL;
+ uint32_t mbox_cmd[MBOX_REG_COUNT];
+ uint32_t mbox_sts[MBOX_REG_COUNT];
+ int wait_for_link = 1;
+ int status = QLA_ERROR;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
+
+ bsg_reply->reply_payload_rcv_len = 0;
+
+ if (test_bit(AF_LOOPBACK, &ha->flags)) {
+ ql4_printk(KERN_INFO, ha, "%s: Loopback Diagnostics already in progress. Invalid Request\n",
+ __func__);
+ bsg_reply->result = DID_ERROR << 16;
+ goto exit_loopback_cmd;
+ }
+
+ if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+ ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
+ __func__);
+ bsg_reply->result = DID_ERROR << 16;
+ goto exit_loopback_cmd;
+ }
+
+ memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
+ sizeof(uint32_t) * MBOX_REG_COUNT);
+
+ if (is_qla8032(ha) || is_qla8042(ha)) {
+ status = qla4_83xx_pre_loopback_config(ha, mbox_cmd);
+ if (status != QLA_SUCCESS) {
+ bsg_reply->result = DID_ERROR << 16;
+ goto exit_loopback_cmd;
+ }
+
+ status = qla4_83xx_wait_for_loopback_config_comp(ha,
+ wait_for_link);
+ if (status != QLA_SUCCESS) {
+ bsg_reply->result = DID_TIME_OUT << 16;
+ goto restore;
+ }
+ }
+
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
+ __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
+ mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
+ mbox_cmd[7]));
+
+ status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
+ &mbox_sts[0]);
+
+ if (status == QLA_SUCCESS)
+ bsg_reply->result = DID_OK << 16;
+ else
+ bsg_reply->result = DID_ERROR << 16;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\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]));
+
+ /* Send mbox_sts to application */
+ bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
+ rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
+ memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
+restore:
+ if (is_qla8032(ha) || is_qla8042(ha)) {
+ status = qla4_83xx_post_loopback_config(ha, mbox_cmd);
+ if (status != QLA_SUCCESS) {
+ bsg_reply->result = DID_ERROR << 16;
+ goto exit_loopback_cmd;
+ }
+
+ /* for pre_loopback_config() wait for LINK UP only
+ * if PHY LINK is UP */
+ if (!(ha->addl_fw_state & FW_ADDSTATE_LINK_UP))
+ wait_for_link = 0;
+
+ status = qla4_83xx_wait_for_loopback_config_comp(ha,
+ wait_for_link);
+ if (status != QLA_SUCCESS) {
+ bsg_reply->result = DID_TIME_OUT << 16;
+ goto exit_loopback_cmd;
+ }
+ }
+exit_loopback_cmd:
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: bsg_reply->result = x%x, status = %s\n",
+ __func__, bsg_reply->result, STATUS(status)));
+ bsg_job_done(bsg_job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+}
+
+static int qla4xxx_execute_diag_test(struct bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
+ struct scsi_qla_host *ha = to_qla_host(host);
+ struct iscsi_bsg_request *bsg_req = bsg_job->request;
+ uint32_t diag_cmd;
+ int rval = -EINVAL;
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
+
+ diag_cmd = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
+ if (diag_cmd == MBOX_CMD_DIAG_TEST) {
+ switch (bsg_req->rqst_data.h_vendor.vendor_cmd[2]) {
+ case QL_DIAG_CMD_TEST_DDR_SIZE:
+ case QL_DIAG_CMD_TEST_DDR_RW:
+ case QL_DIAG_CMD_TEST_ONCHIP_MEM_RW:
+ case QL_DIAG_CMD_TEST_NVRAM:
+ case QL_DIAG_CMD_TEST_FLASH_ROM:
+ case QL_DIAG_CMD_TEST_DMA_XFER:
+ case QL_DIAG_CMD_SELF_DDR_RW:
+ case QL_DIAG_CMD_SELF_ONCHIP_MEM_RW:
+ /* Execute diag test for adapter RAM/FLASH */
+ ql4xxx_execute_diag_cmd(bsg_job);
+ /* Always return success as we want to sent bsg_reply
+ * to Application */
+ rval = QLA_SUCCESS;
+ break;
+
+ case QL_DIAG_CMD_TEST_INT_LOOPBACK:
+ case QL_DIAG_CMD_TEST_EXT_LOOPBACK:
+ /* Execute diag test for Network */
+ qla4xxx_execute_diag_loopback_cmd(bsg_job);
+ /* Always return success as we want to sent bsg_reply
+ * to Application */
+ rval = QLA_SUCCESS;
+ break;
+ default:
+ ql4_printk(KERN_ERR, ha, "%s: Invalid diag test: 0x%x\n",
+ __func__,
+ bsg_req->rqst_data.h_vendor.vendor_cmd[2]);
+ }
+ } else if ((diag_cmd == MBOX_CMD_SET_LED_CONFIG) ||
+ (diag_cmd == MBOX_CMD_GET_LED_CONFIG)) {
+ ql4xxx_execute_diag_cmd(bsg_job);
+ rval = QLA_SUCCESS;
+ } else {
+ ql4_printk(KERN_ERR, ha, "%s: Invalid diag cmd: 0x%x\n",
+ __func__, diag_cmd);
+ }
+
+ return rval;
+}
+
/**
* qla4xxx_process_vendor_specific - handle vendor specific bsg request
* @job: iscsi_bsg_job to handle
@@ -479,6 +836,9 @@ int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job)
case QLISCSI_VND_GET_ACB:
return qla4xxx_bsg_get_acb(bsg_job);
+ case QLISCSI_VND_DIAG_TEST:
+ return qla4xxx_execute_diag_test(bsg_job);
+
default:
ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: "
"0x%x\n", __func__, bsg_req->msgcode);
diff --git a/drivers/scsi/qla4xxx/ql4_bsg.h b/drivers/scsi/qla4xxx/ql4_bsg.h
index c6a0364509fd..88c2401910c0 100644
--- a/drivers/scsi/qla4xxx/ql4_bsg.h
+++ b/drivers/scsi/qla4xxx/ql4_bsg.h
@@ -15,5 +15,18 @@
#define QLISCSI_VND_UPDATE_NVRAM 5
#define QLISCSI_VND_RESTORE_DEFAULTS 6
#define QLISCSI_VND_GET_ACB 7
+#define QLISCSI_VND_DIAG_TEST 8
+
+/* QLISCSI_VND_DIAG_CMD sub code */
+#define QL_DIAG_CMD_TEST_DDR_SIZE 0x2
+#define QL_DIAG_CMD_TEST_DDR_RW 0x3
+#define QL_DIAG_CMD_TEST_ONCHIP_MEM_RW 0x4
+#define QL_DIAG_CMD_TEST_NVRAM 0x5 /* Only ISP4XXX */
+#define QL_DIAG_CMD_TEST_FLASH_ROM 0x6
+#define QL_DIAG_CMD_TEST_INT_LOOPBACK 0x7
+#define QL_DIAG_CMD_TEST_EXT_LOOPBACK 0x8
+#define QL_DIAG_CMD_TEST_DMA_XFER 0x9 /* Only ISP4XXX */
+#define QL_DIAG_CMD_SELF_DDR_RW 0xC
+#define QL_DIAG_CMD_SELF_ONCHIP_MEM_RW 0xD
#endif
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index e2be308c0d9e..aa67bb9a4426 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -73,6 +73,7 @@
#define QLA_SUCCESS 0
#define QLA_ERROR 1
+#define STATUS(status) status == QLA_ERROR ? "FAILED" : "SUCCEEDED"
/*
* Data bit definitions
@@ -210,6 +211,8 @@
#define MAX_RESET_HA_RETRIES 2
#define FW_ALIVE_WAIT_TOV 3
#define IDC_EXTEND_TOV 8
+#define IDC_COMP_TOV 5
+#define LINK_UP_COMP_TOV 30
#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
@@ -822,6 +825,11 @@ struct scsi_qla_host {
uint32_t pf_bit;
struct qla4_83xx_idc_information idc_info;
struct addr_ctrl_blk *saved_acb;
+ int notify_idc_comp;
+ int notify_link_up_comp;
+ int idc_extend_tmo;
+ struct completion idc_comp;
+ struct completion link_up_comp;
};
struct ql4_task_data {
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h
index 306065275707..fef311d9caa3 100644
--- a/drivers/scsi/qla4xxx/ql4_fw.h
+++ b/drivers/scsi/qla4xxx/ql4_fw.h
@@ -410,6 +410,7 @@ struct qla_flt_region {
#define DDB_DS_LOGIN_IN_PROCESS 0x07
#define MBOX_CMD_GET_FW_STATE 0x0069
#define MBOX_CMD_GET_INIT_FW_CTRL_BLOCK_DEFAULTS 0x006A
+#define MBOX_CMD_DIAG_TEST 0x0075
#define MBOX_CMD_GET_SYS_INFO 0x0078
#define MBOX_CMD_GET_NVRAM 0x0078 /* For 40xx */
#define MBOX_CMD_SET_NVRAM 0x0079 /* For 40xx */
@@ -425,8 +426,17 @@ struct qla_flt_region {
#define MBOX_CMD_GET_IP_ADDR_STATE 0x0091
#define MBOX_CMD_SEND_IPV6_ROUTER_SOL 0x0092
#define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR 0x0093
+#define MBOX_CMD_SET_PORT_CONFIG 0x0122
+#define MBOX_CMD_GET_PORT_CONFIG 0x0123
+#define MBOX_CMD_SET_LED_CONFIG 0x0125
+#define MBOX_CMD_GET_LED_CONFIG 0x0126
#define MBOX_CMD_MINIDUMP 0x0129
+/* Port Config */
+#define ENABLE_INTERNAL_LOOPBACK 0x04
+#define ENABLE_EXTERNAL_LOOPBACK 0x08
+#define ENABLE_DCBX 0x10
+
/* Minidump subcommand */
#define MINIDUMP_GET_SIZE_SUBCOMMAND 0x00
#define MINIDUMP_GET_TMPLT_SUBCOMMAND 0x01
@@ -535,10 +545,6 @@ struct qla_flt_region {
#define FLASH_OPT_COMMIT 2
#define FLASH_OPT_RMW_COMMIT 3
-/* Loopback type */
-#define ENABLE_INTERNAL_LOOPBACK 0x04
-#define ENABLE_EXTERNAL_LOOPBACK 0x08
-
/* generic defines to enable/disable params */
#define QL4_PARAM_DISABLE 0
#define QL4_PARAM_ENABLE 1
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h
index b8e87b33b48e..d67c50e0b896 100644
--- a/drivers/scsi/qla4xxx/ql4_glbl.h
+++ b/drivers/scsi/qla4xxx/ql4_glbl.h
@@ -277,6 +277,8 @@ int qla4_84xx_config_acb(struct scsi_qla_host *ha, int acb_config);
int qla4_83xx_ms_mem_write_128b(struct scsi_qla_host *ha,
uint64_t addr, uint32_t *data, uint32_t count);
uint8_t qla4xxx_set_ipaddr_state(uint8_t fw_ipaddr_state);
+int qla4_83xx_get_port_config(struct scsi_qla_host *ha, uint32_t *config);
+int qla4_83xx_set_port_config(struct scsi_qla_host *ha, uint32_t *config);
extern int ql4xextended_error_logging;
extern int ql4xdontresethba;
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index 66cc9c1ba53c..a3c8bc7706c2 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -650,6 +650,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
int i;
uint32_t mbox_sts[MBOX_AEN_REG_COUNT];
__le32 __iomem *mailbox_out;
+ uint32_t opcode = 0;
if (is_qla8032(ha) || is_qla8042(ha))
mailbox_out = &ha->qla4_83xx_reg->mailbox_out[0];
@@ -728,6 +729,11 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKUP,
sizeof(mbox_sts),
(uint8_t *) mbox_sts);
+
+ if ((is_qla8032(ha) || is_qla8042(ha)) &&
+ ha->notify_link_up_comp)
+ complete(&ha->link_up_comp);
+
break;
case MBOX_ASTS_LINK_DOWN:
@@ -873,8 +879,6 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
break;
case MBOX_ASTS_IDC_REQUEST_NOTIFICATION:
- {
- uint32_t opcode;
if (is_qla8032(ha) || is_qla8042(ha)) {
DEBUG2(ql4_printk(KERN_INFO, ha,
"scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x\n",
@@ -894,7 +898,6 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
}
}
break;
- }
case MBOX_ASTS_IDC_COMPLETE:
if (is_qla8032(ha) || is_qla8042(ha)) {
@@ -907,6 +910,14 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
"scsi:%ld: AEN %04x IDC Complete notification\n",
ha->host_no, mbox_sts[0]));
+ opcode = mbox_sts[1] >> 16;
+ if (ha->notify_idc_comp)
+ complete(&ha->idc_comp);
+
+ if ((opcode == MBOX_CMD_SET_PORT_CONFIG) ||
+ (opcode == MBOX_CMD_PORT_RESET))
+ ha->idc_info.info2 = mbox_sts[3];
+
if (qla4_83xx_loopback_in_progress(ha)) {
set_bit(AF_LOOPBACK, &ha->flags);
} else {
@@ -939,6 +950,8 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
DEBUG2(ql4_printk(KERN_INFO, ha,
"scsi%ld: AEN %04x Received IDC Extend Timeout notification\n",
ha->host_no, mbox_sts[0]));
+ /* new IDC timeout */
+ ha->idc_extend_tmo = mbox_sts[1];
break;
case MBOX_ASTS_INITIALIZATION_FAILED:
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index 351793c0e18f..9ae8ca3b69f9 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -2416,3 +2416,46 @@ exit_config_acb:
rval == QLA_SUCCESS ? "SUCCEEDED" : "FAILED"));
return rval;
}
+
+int qla4_83xx_get_port_config(struct scsi_qla_host *ha, uint32_t *config)
+{
+ 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_GET_PORT_CONFIG;
+
+ status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, MBOX_REG_COUNT,
+ mbox_cmd, mbox_sts);
+ if (status == QLA_SUCCESS)
+ *config = mbox_sts[1];
+ else
+ ql4_printk(KERN_ERR, ha, "%s: failed status %04X\n", __func__,
+ mbox_sts[0]);
+
+ return status;
+}
+
+int qla4_83xx_set_port_config(struct scsi_qla_host *ha, uint32_t *config)
+{
+ 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_SET_PORT_CONFIG;
+ mbox_cmd[1] = *config;
+
+ status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, MBOX_REG_COUNT,
+ mbox_cmd, mbox_sts);
+ if (status != QLA_SUCCESS)
+ ql4_printk(KERN_ERR, ha, "%s: failed status %04X\n", __func__,
+ mbox_sts[0]);
+
+ return status;
+}
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 4706b8c0ec64..1d86f5bbd1d0 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -8400,6 +8400,9 @@ static int qla4xxx_probe_adapter(struct pci_dev *pdev,
mutex_init(&ha->chap_sem);
init_completion(&ha->mbx_intr_comp);
init_completion(&ha->disable_acb_comp);
+ init_completion(&ha->idc_comp);
+ init_completion(&ha->link_up_comp);
+ init_completion(&ha->disable_acb_comp);
spin_lock_init(&ha->hardware_lock);
spin_lock_init(&ha->work_lock);