162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QLogic iSCSI HBA Driver
462306a36Sopenharmony_ci * Copyright (c) 2011-2013 QLogic Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "ql4_def.h"
862306a36Sopenharmony_ci#include "ql4_glbl.h"
962306a36Sopenharmony_ci#include "ql4_bsg.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic int
1262306a36Sopenharmony_ciqla4xxx_read_flash(struct bsg_job *bsg_job)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
1562306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
1662306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
1762306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
1862306a36Sopenharmony_ci	uint32_t offset = 0;
1962306a36Sopenharmony_ci	uint32_t length = 0;
2062306a36Sopenharmony_ci	dma_addr_t flash_dma;
2162306a36Sopenharmony_ci	uint8_t *flash = NULL;
2262306a36Sopenharmony_ci	int rval = -EINVAL;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
2762306a36Sopenharmony_ci		goto leave;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
3062306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
3162306a36Sopenharmony_ci		rval = -EBUSY;
3262306a36Sopenharmony_ci		goto leave;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (ha->flash_state != QLFLASH_WAITING) {
3662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: another flash operation "
3762306a36Sopenharmony_ci			   "active\n", __func__);
3862306a36Sopenharmony_ci		rval = -EBUSY;
3962306a36Sopenharmony_ci		goto leave;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	ha->flash_state = QLFLASH_READING;
4362306a36Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
4462306a36Sopenharmony_ci	length = bsg_job->reply_payload.payload_len;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma,
4762306a36Sopenharmony_ci				   GFP_KERNEL);
4862306a36Sopenharmony_ci	if (!flash) {
4962306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
5062306a36Sopenharmony_ci			   "data\n", __func__);
5162306a36Sopenharmony_ci		rval = -ENOMEM;
5262306a36Sopenharmony_ci		goto leave;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	rval = qla4xxx_get_flash(ha, flash_dma, offset, length);
5662306a36Sopenharmony_ci	if (rval) {
5762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get flash failed\n", __func__);
5862306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
5962306a36Sopenharmony_ci		rval = -EIO;
6062306a36Sopenharmony_ci	} else {
6162306a36Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
6262306a36Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
6362306a36Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
6462306a36Sopenharmony_ci					    flash, length);
6562306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
6962306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
7062306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma);
7162306a36Sopenharmony_cileave:
7262306a36Sopenharmony_ci	ha->flash_state = QLFLASH_WAITING;
7362306a36Sopenharmony_ci	return rval;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int
7762306a36Sopenharmony_ciqla4xxx_update_flash(struct bsg_job *bsg_job)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
8062306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
8162306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
8262306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
8362306a36Sopenharmony_ci	uint32_t length = 0;
8462306a36Sopenharmony_ci	uint32_t offset = 0;
8562306a36Sopenharmony_ci	uint32_t options = 0;
8662306a36Sopenharmony_ci	dma_addr_t flash_dma;
8762306a36Sopenharmony_ci	uint8_t *flash = NULL;
8862306a36Sopenharmony_ci	int rval = -EINVAL;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
9362306a36Sopenharmony_ci		goto leave;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
9662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
9762306a36Sopenharmony_ci		rval = -EBUSY;
9862306a36Sopenharmony_ci		goto leave;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (ha->flash_state != QLFLASH_WAITING) {
10262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: another flash operation "
10362306a36Sopenharmony_ci			   "active\n", __func__);
10462306a36Sopenharmony_ci		rval = -EBUSY;
10562306a36Sopenharmony_ci		goto leave;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ha->flash_state = QLFLASH_WRITING;
10962306a36Sopenharmony_ci	length = bsg_job->request_payload.payload_len;
11062306a36Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
11162306a36Sopenharmony_ci	options = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma,
11462306a36Sopenharmony_ci				   GFP_KERNEL);
11562306a36Sopenharmony_ci	if (!flash) {
11662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
11762306a36Sopenharmony_ci			   "data\n", __func__);
11862306a36Sopenharmony_ci		rval = -ENOMEM;
11962306a36Sopenharmony_ci		goto leave;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
12362306a36Sopenharmony_ci			  bsg_job->request_payload.sg_cnt, flash, length);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	rval = qla4xxx_set_flash(ha, flash_dma, offset, length, options);
12662306a36Sopenharmony_ci	if (rval) {
12762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set flash failed\n", __func__);
12862306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
12962306a36Sopenharmony_ci		rval = -EIO;
13062306a36Sopenharmony_ci	} else
13162306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
13462306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
13562306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma);
13662306a36Sopenharmony_cileave:
13762306a36Sopenharmony_ci	ha->flash_state = QLFLASH_WAITING;
13862306a36Sopenharmony_ci	return rval;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int
14262306a36Sopenharmony_ciqla4xxx_get_acb_state(struct bsg_job *bsg_job)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
14562306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
14662306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
14762306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
14862306a36Sopenharmony_ci	uint32_t status[MBOX_REG_COUNT];
14962306a36Sopenharmony_ci	uint32_t acb_idx;
15062306a36Sopenharmony_ci	uint32_t ip_idx;
15162306a36Sopenharmony_ci	int rval = -EINVAL;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
15662306a36Sopenharmony_ci		goto leave;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* Only 4022 and above adapters are supported */
15962306a36Sopenharmony_ci	if (is_qla4010(ha))
16062306a36Sopenharmony_ci		goto leave;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
16362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
16462306a36Sopenharmony_ci		rval = -EBUSY;
16562306a36Sopenharmony_ci		goto leave;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (bsg_job->reply_payload.payload_len < sizeof(status)) {
16962306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid payload len %d\n",
17062306a36Sopenharmony_ci			   __func__, bsg_job->reply_payload.payload_len);
17162306a36Sopenharmony_ci		rval = -EINVAL;
17262306a36Sopenharmony_ci		goto leave;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	acb_idx = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
17662306a36Sopenharmony_ci	ip_idx = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	rval = qla4xxx_get_ip_state(ha, acb_idx, ip_idx, status);
17962306a36Sopenharmony_ci	if (rval) {
18062306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get ip state failed\n",
18162306a36Sopenharmony_ci			   __func__);
18262306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
18362306a36Sopenharmony_ci		rval = -EIO;
18462306a36Sopenharmony_ci	} else {
18562306a36Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
18662306a36Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
18762306a36Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
18862306a36Sopenharmony_ci					    status, sizeof(status));
18962306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
19362306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
19462306a36Sopenharmony_cileave:
19562306a36Sopenharmony_ci	return rval;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int
19962306a36Sopenharmony_ciqla4xxx_read_nvram(struct bsg_job *bsg_job)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
20262306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
20362306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
20462306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
20562306a36Sopenharmony_ci	uint32_t offset = 0;
20662306a36Sopenharmony_ci	uint32_t len = 0;
20762306a36Sopenharmony_ci	uint32_t total_len = 0;
20862306a36Sopenharmony_ci	dma_addr_t nvram_dma;
20962306a36Sopenharmony_ci	uint8_t *nvram = NULL;
21062306a36Sopenharmony_ci	int rval = -EINVAL;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
21562306a36Sopenharmony_ci		goto leave;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Only 40xx adapters are supported */
21862306a36Sopenharmony_ci	if (!(is_qla4010(ha) || is_qla4022(ha) || is_qla4032(ha)))
21962306a36Sopenharmony_ci		goto leave;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
22262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
22362306a36Sopenharmony_ci		rval = -EBUSY;
22462306a36Sopenharmony_ci		goto leave;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
22862306a36Sopenharmony_ci	len = bsg_job->reply_payload.payload_len;
22962306a36Sopenharmony_ci	total_len = offset + len;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* total len should not be greater than max NVRAM size */
23262306a36Sopenharmony_ci	if ((is_qla4010(ha) && total_len > QL4010_NVRAM_SIZE) ||
23362306a36Sopenharmony_ci	    ((is_qla4022(ha) || is_qla4032(ha)) &&
23462306a36Sopenharmony_ci	     total_len > QL40X2_NVRAM_SIZE)) {
23562306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: offset+len greater than max"
23662306a36Sopenharmony_ci			   " nvram size, offset=%d len=%d\n",
23762306a36Sopenharmony_ci			   __func__, offset, len);
23862306a36Sopenharmony_ci		goto leave;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	nvram = dma_alloc_coherent(&ha->pdev->dev, len, &nvram_dma,
24262306a36Sopenharmony_ci				   GFP_KERNEL);
24362306a36Sopenharmony_ci	if (!nvram) {
24462306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for nvram "
24562306a36Sopenharmony_ci			   "data\n", __func__);
24662306a36Sopenharmony_ci		rval = -ENOMEM;
24762306a36Sopenharmony_ci		goto leave;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	rval = qla4xxx_get_nvram(ha, nvram_dma, offset, len);
25162306a36Sopenharmony_ci	if (rval) {
25262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get nvram failed\n", __func__);
25362306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
25462306a36Sopenharmony_ci		rval = -EIO;
25562306a36Sopenharmony_ci	} else {
25662306a36Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
25762306a36Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
25862306a36Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
25962306a36Sopenharmony_ci					    nvram, len);
26062306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
26462306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
26562306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, nvram, nvram_dma);
26662306a36Sopenharmony_cileave:
26762306a36Sopenharmony_ci	return rval;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int
27162306a36Sopenharmony_ciqla4xxx_update_nvram(struct bsg_job *bsg_job)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
27462306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
27562306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
27662306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
27762306a36Sopenharmony_ci	uint32_t offset = 0;
27862306a36Sopenharmony_ci	uint32_t len = 0;
27962306a36Sopenharmony_ci	uint32_t total_len = 0;
28062306a36Sopenharmony_ci	dma_addr_t nvram_dma;
28162306a36Sopenharmony_ci	uint8_t *nvram = NULL;
28262306a36Sopenharmony_ci	int rval = -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
28762306a36Sopenharmony_ci		goto leave;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!(is_qla4010(ha) || is_qla4022(ha) || is_qla4032(ha)))
29062306a36Sopenharmony_ci		goto leave;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
29362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
29462306a36Sopenharmony_ci		rval = -EBUSY;
29562306a36Sopenharmony_ci		goto leave;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
29962306a36Sopenharmony_ci	len = bsg_job->request_payload.payload_len;
30062306a36Sopenharmony_ci	total_len = offset + len;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* total len should not be greater than max NVRAM size */
30362306a36Sopenharmony_ci	if ((is_qla4010(ha) && total_len > QL4010_NVRAM_SIZE) ||
30462306a36Sopenharmony_ci	    ((is_qla4022(ha) || is_qla4032(ha)) &&
30562306a36Sopenharmony_ci	     total_len > QL40X2_NVRAM_SIZE)) {
30662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: offset+len greater than max"
30762306a36Sopenharmony_ci			   " nvram size, offset=%d len=%d\n",
30862306a36Sopenharmony_ci			   __func__, offset, len);
30962306a36Sopenharmony_ci		goto leave;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	nvram = dma_alloc_coherent(&ha->pdev->dev, len, &nvram_dma,
31362306a36Sopenharmony_ci				   GFP_KERNEL);
31462306a36Sopenharmony_ci	if (!nvram) {
31562306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
31662306a36Sopenharmony_ci			   "data\n", __func__);
31762306a36Sopenharmony_ci		rval = -ENOMEM;
31862306a36Sopenharmony_ci		goto leave;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
32262306a36Sopenharmony_ci			  bsg_job->request_payload.sg_cnt, nvram, len);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	rval = qla4xxx_set_nvram(ha, nvram_dma, offset, len);
32562306a36Sopenharmony_ci	if (rval) {
32662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set nvram failed\n", __func__);
32762306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
32862306a36Sopenharmony_ci		rval = -EIO;
32962306a36Sopenharmony_ci	} else
33062306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
33362306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
33462306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, nvram, nvram_dma);
33562306a36Sopenharmony_cileave:
33662306a36Sopenharmony_ci	return rval;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int
34062306a36Sopenharmony_ciqla4xxx_restore_defaults(struct bsg_job *bsg_job)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
34362306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
34462306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
34562306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
34662306a36Sopenharmony_ci	uint32_t region = 0;
34762306a36Sopenharmony_ci	uint32_t field0 = 0;
34862306a36Sopenharmony_ci	uint32_t field1 = 0;
34962306a36Sopenharmony_ci	int rval = -EINVAL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
35462306a36Sopenharmony_ci		goto leave;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (is_qla4010(ha))
35762306a36Sopenharmony_ci		goto leave;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
36062306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
36162306a36Sopenharmony_ci		rval = -EBUSY;
36262306a36Sopenharmony_ci		goto leave;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	region = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
36662306a36Sopenharmony_ci	field0 = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
36762306a36Sopenharmony_ci	field1 = bsg_req->rqst_data.h_vendor.vendor_cmd[3];
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	rval = qla4xxx_restore_factory_defaults(ha, region, field0, field1);
37062306a36Sopenharmony_ci	if (rval) {
37162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set nvram failed\n", __func__);
37262306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
37362306a36Sopenharmony_ci		rval = -EIO;
37462306a36Sopenharmony_ci	} else
37562306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
37862306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
37962306a36Sopenharmony_cileave:
38062306a36Sopenharmony_ci	return rval;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int
38462306a36Sopenharmony_ciqla4xxx_bsg_get_acb(struct bsg_job *bsg_job)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
38762306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
38862306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
38962306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
39062306a36Sopenharmony_ci	uint32_t acb_type = 0;
39162306a36Sopenharmony_ci	uint32_t len = 0;
39262306a36Sopenharmony_ci	dma_addr_t acb_dma;
39362306a36Sopenharmony_ci	uint8_t *acb = NULL;
39462306a36Sopenharmony_ci	int rval = -EINVAL;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
39962306a36Sopenharmony_ci		goto leave;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Only 4022 and above adapters are supported */
40262306a36Sopenharmony_ci	if (is_qla4010(ha))
40362306a36Sopenharmony_ci		goto leave;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
40662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
40762306a36Sopenharmony_ci		rval = -EBUSY;
40862306a36Sopenharmony_ci		goto leave;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	acb_type = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
41262306a36Sopenharmony_ci	len = bsg_job->reply_payload.payload_len;
41362306a36Sopenharmony_ci	if (len < sizeof(struct addr_ctrl_blk)) {
41462306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid acb len %d\n",
41562306a36Sopenharmony_ci			   __func__, len);
41662306a36Sopenharmony_ci		rval = -EINVAL;
41762306a36Sopenharmony_ci		goto leave;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	acb = dma_alloc_coherent(&ha->pdev->dev, len, &acb_dma, GFP_KERNEL);
42162306a36Sopenharmony_ci	if (!acb) {
42262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for acb "
42362306a36Sopenharmony_ci			   "data\n", __func__);
42462306a36Sopenharmony_ci		rval = -ENOMEM;
42562306a36Sopenharmony_ci		goto leave;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	rval = qla4xxx_get_acb(ha, acb_dma, acb_type, len);
42962306a36Sopenharmony_ci	if (rval) {
43062306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get acb failed\n", __func__);
43162306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
43262306a36Sopenharmony_ci		rval = -EIO;
43362306a36Sopenharmony_ci	} else {
43462306a36Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
43562306a36Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
43662306a36Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
43762306a36Sopenharmony_ci					    acb, len);
43862306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
44262306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
44362306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, acb, acb_dma);
44462306a36Sopenharmony_cileave:
44562306a36Sopenharmony_ci	return rval;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void ql4xxx_execute_diag_cmd(struct bsg_job *bsg_job)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
45162306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
45262306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
45362306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
45462306a36Sopenharmony_ci	uint8_t *rsp_ptr = NULL;
45562306a36Sopenharmony_ci	uint32_t mbox_cmd[MBOX_REG_COUNT];
45662306a36Sopenharmony_ci	uint32_t mbox_sts[MBOX_REG_COUNT];
45762306a36Sopenharmony_ci	int status = QLA_ERROR;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
46262306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
46362306a36Sopenharmony_ci			   __func__);
46462306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
46562306a36Sopenharmony_ci		goto exit_diag_mem_test;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
46962306a36Sopenharmony_ci	memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
47062306a36Sopenharmony_ci	       sizeof(uint32_t) * MBOX_REG_COUNT);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
47362306a36Sopenharmony_ci			  "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
47462306a36Sopenharmony_ci			  __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
47562306a36Sopenharmony_ci			  mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
47662306a36Sopenharmony_ci			  mbox_cmd[7]));
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
47962306a36Sopenharmony_ci					 &mbox_sts[0]);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
48262306a36Sopenharmony_ci			  "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n",
48362306a36Sopenharmony_ci			  __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2],
48462306a36Sopenharmony_ci			  mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6],
48562306a36Sopenharmony_ci			  mbox_sts[7]));
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (status == QLA_SUCCESS)
48862306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
48962306a36Sopenharmony_ci	else
49062306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* Send mbox_sts to application */
49362306a36Sopenharmony_ci	bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
49462306a36Sopenharmony_ci	rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
49562306a36Sopenharmony_ci	memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ciexit_diag_mem_test:
49862306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
49962306a36Sopenharmony_ci			  "%s: bsg_reply->result = x%x, status = %s\n",
50062306a36Sopenharmony_ci			  __func__, bsg_reply->result, STATUS(status)));
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
50362306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int qla4_83xx_wait_for_loopback_config_comp(struct scsi_qla_host *ha,
50762306a36Sopenharmony_ci						   int wait_for_link)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	int status = QLA_SUCCESS;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&ha->idc_comp, (IDC_COMP_TOV * HZ))) {
51262306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: IDC Complete notification not received, Waiting for another %d timeout",
51362306a36Sopenharmony_ci			   __func__, ha->idc_extend_tmo);
51462306a36Sopenharmony_ci		if (ha->idc_extend_tmo) {
51562306a36Sopenharmony_ci			if (!wait_for_completion_timeout(&ha->idc_comp,
51662306a36Sopenharmony_ci						(ha->idc_extend_tmo * HZ))) {
51762306a36Sopenharmony_ci				ha->notify_idc_comp = 0;
51862306a36Sopenharmony_ci				ha->notify_link_up_comp = 0;
51962306a36Sopenharmony_ci				ql4_printk(KERN_WARNING, ha, "%s: Aborting: IDC Complete notification not received",
52062306a36Sopenharmony_ci					   __func__);
52162306a36Sopenharmony_ci				status = QLA_ERROR;
52262306a36Sopenharmony_ci				goto exit_wait;
52362306a36Sopenharmony_ci			} else {
52462306a36Sopenharmony_ci				DEBUG2(ql4_printk(KERN_INFO, ha,
52562306a36Sopenharmony_ci						  "%s: IDC Complete notification received\n",
52662306a36Sopenharmony_ci						  __func__));
52762306a36Sopenharmony_ci			}
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	} else {
53062306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha,
53162306a36Sopenharmony_ci				  "%s: IDC Complete notification received\n",
53262306a36Sopenharmony_ci				  __func__));
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci	ha->notify_idc_comp = 0;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (wait_for_link) {
53762306a36Sopenharmony_ci		if (!wait_for_completion_timeout(&ha->link_up_comp,
53862306a36Sopenharmony_ci						 (IDC_COMP_TOV * HZ))) {
53962306a36Sopenharmony_ci			ha->notify_link_up_comp = 0;
54062306a36Sopenharmony_ci			ql4_printk(KERN_WARNING, ha, "%s: Aborting: LINK UP notification not received",
54162306a36Sopenharmony_ci				   __func__);
54262306a36Sopenharmony_ci			status = QLA_ERROR;
54362306a36Sopenharmony_ci			goto exit_wait;
54462306a36Sopenharmony_ci		} else {
54562306a36Sopenharmony_ci			DEBUG2(ql4_printk(KERN_INFO, ha,
54662306a36Sopenharmony_ci					  "%s: LINK UP notification received\n",
54762306a36Sopenharmony_ci					  __func__));
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci		ha->notify_link_up_comp = 0;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ciexit_wait:
55362306a36Sopenharmony_ci	return status;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha,
55762306a36Sopenharmony_ci					 uint32_t *mbox_cmd)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	uint32_t config = 0;
56062306a36Sopenharmony_ci	int status = QLA_SUCCESS;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	status = qla4_83xx_get_port_config(ha, &config);
56562306a36Sopenharmony_ci	if (status != QLA_SUCCESS)
56662306a36Sopenharmony_ci		goto exit_pre_loopback_config;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Default port config=%08X\n",
56962306a36Sopenharmony_ci			  __func__, config));
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if ((config & ENABLE_INTERNAL_LOOPBACK) ||
57262306a36Sopenharmony_ci	    (config & ENABLE_EXTERNAL_LOOPBACK)) {
57362306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid request\n",
57462306a36Sopenharmony_ci			   __func__);
57562306a36Sopenharmony_ci		goto exit_pre_loopback_config;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
57962306a36Sopenharmony_ci		config |= ENABLE_INTERNAL_LOOPBACK;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
58262306a36Sopenharmony_ci		config |= ENABLE_EXTERNAL_LOOPBACK;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	config &= ~ENABLE_DCBX;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: New port config=%08X\n",
58762306a36Sopenharmony_ci			  __func__, config));
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ha->notify_idc_comp = 1;
59062306a36Sopenharmony_ci	ha->notify_link_up_comp = 1;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* get the link state */
59362306a36Sopenharmony_ci	qla4xxx_get_firmware_state(ha);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	status = qla4_83xx_set_port_config(ha, &config);
59662306a36Sopenharmony_ci	if (status != QLA_SUCCESS) {
59762306a36Sopenharmony_ci		ha->notify_idc_comp = 0;
59862306a36Sopenharmony_ci		ha->notify_link_up_comp = 0;
59962306a36Sopenharmony_ci		goto exit_pre_loopback_config;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ciexit_pre_loopback_config:
60262306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
60362306a36Sopenharmony_ci			  STATUS(status)));
60462306a36Sopenharmony_ci	return status;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int qla4_83xx_post_loopback_config(struct scsi_qla_host *ha,
60862306a36Sopenharmony_ci					  uint32_t *mbox_cmd)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	int status = QLA_SUCCESS;
61162306a36Sopenharmony_ci	uint32_t config = 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	status = qla4_83xx_get_port_config(ha, &config);
61662306a36Sopenharmony_ci	if (status != QLA_SUCCESS)
61762306a36Sopenharmony_ci		goto exit_post_loopback_config;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: port config=%08X\n", __func__,
62062306a36Sopenharmony_ci			  config));
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
62362306a36Sopenharmony_ci		config &= ~ENABLE_INTERNAL_LOOPBACK;
62462306a36Sopenharmony_ci	else if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
62562306a36Sopenharmony_ci		config &= ~ENABLE_EXTERNAL_LOOPBACK;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	config |= ENABLE_DCBX;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
63062306a36Sopenharmony_ci			  "%s: Restore default port config=%08X\n", __func__,
63162306a36Sopenharmony_ci			  config));
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ha->notify_idc_comp = 1;
63462306a36Sopenharmony_ci	if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP)
63562306a36Sopenharmony_ci		ha->notify_link_up_comp = 1;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	status = qla4_83xx_set_port_config(ha, &config);
63862306a36Sopenharmony_ci	if (status != QLA_SUCCESS) {
63962306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Scheduling adapter reset\n",
64062306a36Sopenharmony_ci			   __func__);
64162306a36Sopenharmony_ci		set_bit(DPC_RESET_HA, &ha->dpc_flags);
64262306a36Sopenharmony_ci		clear_bit(AF_LOOPBACK, &ha->flags);
64362306a36Sopenharmony_ci		goto exit_post_loopback_config;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ciexit_post_loopback_config:
64762306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
64862306a36Sopenharmony_ci			  STATUS(status)));
64962306a36Sopenharmony_ci	return status;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic void qla4xxx_execute_diag_loopback_cmd(struct bsg_job *bsg_job)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
65562306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
65662306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
65762306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
65862306a36Sopenharmony_ci	uint8_t *rsp_ptr = NULL;
65962306a36Sopenharmony_ci	uint32_t mbox_cmd[MBOX_REG_COUNT];
66062306a36Sopenharmony_ci	uint32_t mbox_sts[MBOX_REG_COUNT];
66162306a36Sopenharmony_ci	int wait_for_link = 1;
66262306a36Sopenharmony_ci	int status = QLA_ERROR;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (test_bit(AF_LOOPBACK, &ha->flags)) {
66962306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Loopback Diagnostics already in progress. Invalid Request\n",
67062306a36Sopenharmony_ci			   __func__);
67162306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
67262306a36Sopenharmony_ci		goto exit_loopback_cmd;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
67662306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
67762306a36Sopenharmony_ci			   __func__);
67862306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
67962306a36Sopenharmony_ci		goto exit_loopback_cmd;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
68362306a36Sopenharmony_ci	       sizeof(uint32_t) * MBOX_REG_COUNT);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (is_qla8032(ha) || is_qla8042(ha)) {
68662306a36Sopenharmony_ci		status = qla4_83xx_pre_loopback_config(ha, mbox_cmd);
68762306a36Sopenharmony_ci		if (status != QLA_SUCCESS) {
68862306a36Sopenharmony_ci			bsg_reply->result = DID_ERROR << 16;
68962306a36Sopenharmony_ci			goto exit_loopback_cmd;
69062306a36Sopenharmony_ci		}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		status = qla4_83xx_wait_for_loopback_config_comp(ha,
69362306a36Sopenharmony_ci								 wait_for_link);
69462306a36Sopenharmony_ci		if (status != QLA_SUCCESS) {
69562306a36Sopenharmony_ci			bsg_reply->result = DID_TIME_OUT << 16;
69662306a36Sopenharmony_ci			goto restore;
69762306a36Sopenharmony_ci		}
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
70162306a36Sopenharmony_ci			  "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
70262306a36Sopenharmony_ci			  __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
70362306a36Sopenharmony_ci			  mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
70462306a36Sopenharmony_ci			  mbox_cmd[7]));
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
70762306a36Sopenharmony_ci				&mbox_sts[0]);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (status == QLA_SUCCESS)
71062306a36Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
71162306a36Sopenharmony_ci	else
71262306a36Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
71562306a36Sopenharmony_ci			  "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n",
71662306a36Sopenharmony_ci			  __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2],
71762306a36Sopenharmony_ci			  mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6],
71862306a36Sopenharmony_ci			  mbox_sts[7]));
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* Send mbox_sts to application */
72162306a36Sopenharmony_ci	bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
72262306a36Sopenharmony_ci	rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
72362306a36Sopenharmony_ci	memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
72462306a36Sopenharmony_cirestore:
72562306a36Sopenharmony_ci	if (is_qla8032(ha) || is_qla8042(ha)) {
72662306a36Sopenharmony_ci		status = qla4_83xx_post_loopback_config(ha, mbox_cmd);
72762306a36Sopenharmony_ci		if (status != QLA_SUCCESS) {
72862306a36Sopenharmony_ci			bsg_reply->result = DID_ERROR << 16;
72962306a36Sopenharmony_ci			goto exit_loopback_cmd;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		/* for pre_loopback_config() wait for LINK UP only
73362306a36Sopenharmony_ci		 * if PHY LINK is UP */
73462306a36Sopenharmony_ci		if (!(ha->addl_fw_state & FW_ADDSTATE_LINK_UP))
73562306a36Sopenharmony_ci			wait_for_link = 0;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		status = qla4_83xx_wait_for_loopback_config_comp(ha,
73862306a36Sopenharmony_ci								 wait_for_link);
73962306a36Sopenharmony_ci		if (status != QLA_SUCCESS) {
74062306a36Sopenharmony_ci			bsg_reply->result = DID_TIME_OUT << 16;
74162306a36Sopenharmony_ci			goto exit_loopback_cmd;
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ciexit_loopback_cmd:
74562306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
74662306a36Sopenharmony_ci			  "%s: bsg_reply->result = x%x, status = %s\n",
74762306a36Sopenharmony_ci			  __func__, bsg_reply->result, STATUS(status)));
74862306a36Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
74962306a36Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int qla4xxx_execute_diag_test(struct bsg_job *bsg_job)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
75562306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
75662306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
75762306a36Sopenharmony_ci	uint32_t diag_cmd;
75862306a36Sopenharmony_ci	int rval = -EINVAL;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	diag_cmd = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
76362306a36Sopenharmony_ci	if (diag_cmd == MBOX_CMD_DIAG_TEST) {
76462306a36Sopenharmony_ci		switch (bsg_req->rqst_data.h_vendor.vendor_cmd[2]) {
76562306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_DDR_SIZE:
76662306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_DDR_RW:
76762306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_ONCHIP_MEM_RW:
76862306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_NVRAM:
76962306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_FLASH_ROM:
77062306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_DMA_XFER:
77162306a36Sopenharmony_ci		case QL_DIAG_CMD_SELF_DDR_RW:
77262306a36Sopenharmony_ci		case QL_DIAG_CMD_SELF_ONCHIP_MEM_RW:
77362306a36Sopenharmony_ci			/* Execute diag test for adapter RAM/FLASH */
77462306a36Sopenharmony_ci			ql4xxx_execute_diag_cmd(bsg_job);
77562306a36Sopenharmony_ci			/* Always return success as we want to sent bsg_reply
77662306a36Sopenharmony_ci			 * to Application */
77762306a36Sopenharmony_ci			rval = QLA_SUCCESS;
77862306a36Sopenharmony_ci			break;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_INT_LOOPBACK:
78162306a36Sopenharmony_ci		case QL_DIAG_CMD_TEST_EXT_LOOPBACK:
78262306a36Sopenharmony_ci			/* Execute diag test for Network */
78362306a36Sopenharmony_ci			qla4xxx_execute_diag_loopback_cmd(bsg_job);
78462306a36Sopenharmony_ci			/* Always return success as we want to sent bsg_reply
78562306a36Sopenharmony_ci			 * to Application */
78662306a36Sopenharmony_ci			rval = QLA_SUCCESS;
78762306a36Sopenharmony_ci			break;
78862306a36Sopenharmony_ci		default:
78962306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: Invalid diag test: 0x%x\n",
79062306a36Sopenharmony_ci				   __func__,
79162306a36Sopenharmony_ci				   bsg_req->rqst_data.h_vendor.vendor_cmd[2]);
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci	} else if ((diag_cmd == MBOX_CMD_SET_LED_CONFIG) ||
79462306a36Sopenharmony_ci		   (diag_cmd == MBOX_CMD_GET_LED_CONFIG)) {
79562306a36Sopenharmony_ci		ql4xxx_execute_diag_cmd(bsg_job);
79662306a36Sopenharmony_ci		rval = QLA_SUCCESS;
79762306a36Sopenharmony_ci	} else {
79862306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Invalid diag cmd: 0x%x\n",
79962306a36Sopenharmony_ci			   __func__, diag_cmd);
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return rval;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/**
80662306a36Sopenharmony_ci * qla4xxx_process_vendor_specific - handle vendor specific bsg request
80762306a36Sopenharmony_ci * @bsg_job: iscsi_bsg_job to handle
80862306a36Sopenharmony_ci **/
80962306a36Sopenharmony_ciint qla4xxx_process_vendor_specific(struct bsg_job *bsg_job)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
81262306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
81362306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
81462306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	switch (bsg_req->rqst_data.h_vendor.vendor_cmd[0]) {
81762306a36Sopenharmony_ci	case QLISCSI_VND_READ_FLASH:
81862306a36Sopenharmony_ci		return qla4xxx_read_flash(bsg_job);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	case QLISCSI_VND_UPDATE_FLASH:
82162306a36Sopenharmony_ci		return qla4xxx_update_flash(bsg_job);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	case QLISCSI_VND_GET_ACB_STATE:
82462306a36Sopenharmony_ci		return qla4xxx_get_acb_state(bsg_job);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	case QLISCSI_VND_READ_NVRAM:
82762306a36Sopenharmony_ci		return qla4xxx_read_nvram(bsg_job);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	case QLISCSI_VND_UPDATE_NVRAM:
83062306a36Sopenharmony_ci		return qla4xxx_update_nvram(bsg_job);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	case QLISCSI_VND_RESTORE_DEFAULTS:
83362306a36Sopenharmony_ci		return qla4xxx_restore_defaults(bsg_job);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	case QLISCSI_VND_GET_ACB:
83662306a36Sopenharmony_ci		return qla4xxx_bsg_get_acb(bsg_job);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	case QLISCSI_VND_DIAG_TEST:
83962306a36Sopenharmony_ci		return qla4xxx_execute_diag_test(bsg_job);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	default:
84262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: "
84362306a36Sopenharmony_ci			   "0x%x\n", __func__, bsg_req->msgcode);
84462306a36Sopenharmony_ci		bsg_reply->result = (DID_ERROR << 16);
84562306a36Sopenharmony_ci		bsg_reply->reply_payload_rcv_len = 0;
84662306a36Sopenharmony_ci		bsg_job_done(bsg_job, bsg_reply->result,
84762306a36Sopenharmony_ci			     bsg_reply->reply_payload_rcv_len);
84862306a36Sopenharmony_ci		return -ENOSYS;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci/**
85362306a36Sopenharmony_ci * qla4xxx_bsg_request - handle bsg request from ISCSI transport
85462306a36Sopenharmony_ci * @bsg_job: iscsi_bsg_job to handle
85562306a36Sopenharmony_ci */
85662306a36Sopenharmony_ciint qla4xxx_bsg_request(struct bsg_job *bsg_job)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
85962306a36Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
86062306a36Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	switch (bsg_req->msgcode) {
86362306a36Sopenharmony_ci	case ISCSI_BSG_HST_VENDOR:
86462306a36Sopenharmony_ci		return qla4xxx_process_vendor_specific(bsg_job);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	default:
86762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid BSG command: 0x%x\n",
86862306a36Sopenharmony_ci			   __func__, bsg_req->msgcode);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return -ENOSYS;
87262306a36Sopenharmony_ci}
873