Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df86f771 authored by Vikas Chaudhary's avatar Vikas Chaudhary Committed by James Bottomley
Browse files

[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: default avatarVikas Chaudhary <vikas.chaudhary@qlogic.com>
Reviewed-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 2da11ad2
Loading
Loading
Loading
Loading
+360 −0
Original line number Diff line number Diff line
@@ -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);
+13 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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 {
+10 −4
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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;
Loading