18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * QLogic iSCSI HBA Driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2013 QLogic Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "ql4_def.h"
88c2ecf20Sopenharmony_ci#include "ql4_glbl.h"
98c2ecf20Sopenharmony_ci#include "ql4_bsg.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic int
128c2ecf20Sopenharmony_ciqla4xxx_read_flash(struct bsg_job *bsg_job)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
158c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
168c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
178c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
188c2ecf20Sopenharmony_ci	uint32_t offset = 0;
198c2ecf20Sopenharmony_ci	uint32_t length = 0;
208c2ecf20Sopenharmony_ci	dma_addr_t flash_dma;
218c2ecf20Sopenharmony_ci	uint8_t *flash = NULL;
228c2ecf20Sopenharmony_ci	int rval = -EINVAL;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
278c2ecf20Sopenharmony_ci		goto leave;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
308c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
318c2ecf20Sopenharmony_ci		rval = -EBUSY;
328c2ecf20Sopenharmony_ci		goto leave;
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (ha->flash_state != QLFLASH_WAITING) {
368c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: another flash operation "
378c2ecf20Sopenharmony_ci			   "active\n", __func__);
388c2ecf20Sopenharmony_ci		rval = -EBUSY;
398c2ecf20Sopenharmony_ci		goto leave;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	ha->flash_state = QLFLASH_READING;
438c2ecf20Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
448c2ecf20Sopenharmony_ci	length = bsg_job->reply_payload.payload_len;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma,
478c2ecf20Sopenharmony_ci				   GFP_KERNEL);
488c2ecf20Sopenharmony_ci	if (!flash) {
498c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
508c2ecf20Sopenharmony_ci			   "data\n", __func__);
518c2ecf20Sopenharmony_ci		rval = -ENOMEM;
528c2ecf20Sopenharmony_ci		goto leave;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	rval = qla4xxx_get_flash(ha, flash_dma, offset, length);
568c2ecf20Sopenharmony_ci	if (rval) {
578c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get flash failed\n", __func__);
588c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
598c2ecf20Sopenharmony_ci		rval = -EIO;
608c2ecf20Sopenharmony_ci	} else {
618c2ecf20Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
628c2ecf20Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
638c2ecf20Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
648c2ecf20Sopenharmony_ci					    flash, length);
658c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
698c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
708c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma);
718c2ecf20Sopenharmony_cileave:
728c2ecf20Sopenharmony_ci	ha->flash_state = QLFLASH_WAITING;
738c2ecf20Sopenharmony_ci	return rval;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int
778c2ecf20Sopenharmony_ciqla4xxx_update_flash(struct bsg_job *bsg_job)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
808c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
818c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
828c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
838c2ecf20Sopenharmony_ci	uint32_t length = 0;
848c2ecf20Sopenharmony_ci	uint32_t offset = 0;
858c2ecf20Sopenharmony_ci	uint32_t options = 0;
868c2ecf20Sopenharmony_ci	dma_addr_t flash_dma;
878c2ecf20Sopenharmony_ci	uint8_t *flash = NULL;
888c2ecf20Sopenharmony_ci	int rval = -EINVAL;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
938c2ecf20Sopenharmony_ci		goto leave;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
968c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
978c2ecf20Sopenharmony_ci		rval = -EBUSY;
988c2ecf20Sopenharmony_ci		goto leave;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (ha->flash_state != QLFLASH_WAITING) {
1028c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: another flash operation "
1038c2ecf20Sopenharmony_ci			   "active\n", __func__);
1048c2ecf20Sopenharmony_ci		rval = -EBUSY;
1058c2ecf20Sopenharmony_ci		goto leave;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ha->flash_state = QLFLASH_WRITING;
1098c2ecf20Sopenharmony_ci	length = bsg_job->request_payload.payload_len;
1108c2ecf20Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
1118c2ecf20Sopenharmony_ci	options = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma,
1148c2ecf20Sopenharmony_ci				   GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	if (!flash) {
1168c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
1178c2ecf20Sopenharmony_ci			   "data\n", __func__);
1188c2ecf20Sopenharmony_ci		rval = -ENOMEM;
1198c2ecf20Sopenharmony_ci		goto leave;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
1238c2ecf20Sopenharmony_ci			  bsg_job->request_payload.sg_cnt, flash, length);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	rval = qla4xxx_set_flash(ha, flash_dma, offset, length, options);
1268c2ecf20Sopenharmony_ci	if (rval) {
1278c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set flash failed\n", __func__);
1288c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
1298c2ecf20Sopenharmony_ci		rval = -EIO;
1308c2ecf20Sopenharmony_ci	} else
1318c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
1348c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
1358c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma);
1368c2ecf20Sopenharmony_cileave:
1378c2ecf20Sopenharmony_ci	ha->flash_state = QLFLASH_WAITING;
1388c2ecf20Sopenharmony_ci	return rval;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int
1428c2ecf20Sopenharmony_ciqla4xxx_get_acb_state(struct bsg_job *bsg_job)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
1458c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
1468c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
1478c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
1488c2ecf20Sopenharmony_ci	uint32_t status[MBOX_REG_COUNT];
1498c2ecf20Sopenharmony_ci	uint32_t acb_idx;
1508c2ecf20Sopenharmony_ci	uint32_t ip_idx;
1518c2ecf20Sopenharmony_ci	int rval = -EINVAL;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
1568c2ecf20Sopenharmony_ci		goto leave;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* Only 4022 and above adapters are supported */
1598c2ecf20Sopenharmony_ci	if (is_qla4010(ha))
1608c2ecf20Sopenharmony_ci		goto leave;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
1638c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
1648c2ecf20Sopenharmony_ci		rval = -EBUSY;
1658c2ecf20Sopenharmony_ci		goto leave;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (bsg_job->reply_payload.payload_len < sizeof(status)) {
1698c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid payload len %d\n",
1708c2ecf20Sopenharmony_ci			   __func__, bsg_job->reply_payload.payload_len);
1718c2ecf20Sopenharmony_ci		rval = -EINVAL;
1728c2ecf20Sopenharmony_ci		goto leave;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	acb_idx = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
1768c2ecf20Sopenharmony_ci	ip_idx = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	rval = qla4xxx_get_ip_state(ha, acb_idx, ip_idx, status);
1798c2ecf20Sopenharmony_ci	if (rval) {
1808c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get ip state failed\n",
1818c2ecf20Sopenharmony_ci			   __func__);
1828c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
1838c2ecf20Sopenharmony_ci		rval = -EIO;
1848c2ecf20Sopenharmony_ci	} else {
1858c2ecf20Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
1868c2ecf20Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
1878c2ecf20Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
1888c2ecf20Sopenharmony_ci					    status, sizeof(status));
1898c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
1938c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
1948c2ecf20Sopenharmony_cileave:
1958c2ecf20Sopenharmony_ci	return rval;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int
1998c2ecf20Sopenharmony_ciqla4xxx_read_nvram(struct bsg_job *bsg_job)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
2028c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
2038c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
2048c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
2058c2ecf20Sopenharmony_ci	uint32_t offset = 0;
2068c2ecf20Sopenharmony_ci	uint32_t len = 0;
2078c2ecf20Sopenharmony_ci	uint32_t total_len = 0;
2088c2ecf20Sopenharmony_ci	dma_addr_t nvram_dma;
2098c2ecf20Sopenharmony_ci	uint8_t *nvram = NULL;
2108c2ecf20Sopenharmony_ci	int rval = -EINVAL;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
2158c2ecf20Sopenharmony_ci		goto leave;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Only 40xx adapters are supported */
2188c2ecf20Sopenharmony_ci	if (!(is_qla4010(ha) || is_qla4022(ha) || is_qla4032(ha)))
2198c2ecf20Sopenharmony_ci		goto leave;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
2228c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
2238c2ecf20Sopenharmony_ci		rval = -EBUSY;
2248c2ecf20Sopenharmony_ci		goto leave;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
2288c2ecf20Sopenharmony_ci	len = bsg_job->reply_payload.payload_len;
2298c2ecf20Sopenharmony_ci	total_len = offset + len;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* total len should not be greater than max NVRAM size */
2328c2ecf20Sopenharmony_ci	if ((is_qla4010(ha) && total_len > QL4010_NVRAM_SIZE) ||
2338c2ecf20Sopenharmony_ci	    ((is_qla4022(ha) || is_qla4032(ha)) &&
2348c2ecf20Sopenharmony_ci	     total_len > QL40X2_NVRAM_SIZE)) {
2358c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: offset+len greater than max"
2368c2ecf20Sopenharmony_ci			   " nvram size, offset=%d len=%d\n",
2378c2ecf20Sopenharmony_ci			   __func__, offset, len);
2388c2ecf20Sopenharmony_ci		goto leave;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	nvram = dma_alloc_coherent(&ha->pdev->dev, len, &nvram_dma,
2428c2ecf20Sopenharmony_ci				   GFP_KERNEL);
2438c2ecf20Sopenharmony_ci	if (!nvram) {
2448c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for nvram "
2458c2ecf20Sopenharmony_ci			   "data\n", __func__);
2468c2ecf20Sopenharmony_ci		rval = -ENOMEM;
2478c2ecf20Sopenharmony_ci		goto leave;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	rval = qla4xxx_get_nvram(ha, nvram_dma, offset, len);
2518c2ecf20Sopenharmony_ci	if (rval) {
2528c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get nvram failed\n", __func__);
2538c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
2548c2ecf20Sopenharmony_ci		rval = -EIO;
2558c2ecf20Sopenharmony_ci	} else {
2568c2ecf20Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
2578c2ecf20Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
2588c2ecf20Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
2598c2ecf20Sopenharmony_ci					    nvram, len);
2608c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
2648c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
2658c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, nvram, nvram_dma);
2668c2ecf20Sopenharmony_cileave:
2678c2ecf20Sopenharmony_ci	return rval;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int
2718c2ecf20Sopenharmony_ciqla4xxx_update_nvram(struct bsg_job *bsg_job)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
2748c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
2758c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
2768c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
2778c2ecf20Sopenharmony_ci	uint32_t offset = 0;
2788c2ecf20Sopenharmony_ci	uint32_t len = 0;
2798c2ecf20Sopenharmony_ci	uint32_t total_len = 0;
2808c2ecf20Sopenharmony_ci	dma_addr_t nvram_dma;
2818c2ecf20Sopenharmony_ci	uint8_t *nvram = NULL;
2828c2ecf20Sopenharmony_ci	int rval = -EINVAL;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
2878c2ecf20Sopenharmony_ci		goto leave;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!(is_qla4010(ha) || is_qla4022(ha) || is_qla4032(ha)))
2908c2ecf20Sopenharmony_ci		goto leave;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
2938c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
2948c2ecf20Sopenharmony_ci		rval = -EBUSY;
2958c2ecf20Sopenharmony_ci		goto leave;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
2998c2ecf20Sopenharmony_ci	len = bsg_job->request_payload.payload_len;
3008c2ecf20Sopenharmony_ci	total_len = offset + len;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* total len should not be greater than max NVRAM size */
3038c2ecf20Sopenharmony_ci	if ((is_qla4010(ha) && total_len > QL4010_NVRAM_SIZE) ||
3048c2ecf20Sopenharmony_ci	    ((is_qla4022(ha) || is_qla4032(ha)) &&
3058c2ecf20Sopenharmony_ci	     total_len > QL40X2_NVRAM_SIZE)) {
3068c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: offset+len greater than max"
3078c2ecf20Sopenharmony_ci			   " nvram size, offset=%d len=%d\n",
3088c2ecf20Sopenharmony_ci			   __func__, offset, len);
3098c2ecf20Sopenharmony_ci		goto leave;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	nvram = dma_alloc_coherent(&ha->pdev->dev, len, &nvram_dma,
3138c2ecf20Sopenharmony_ci				   GFP_KERNEL);
3148c2ecf20Sopenharmony_ci	if (!nvram) {
3158c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash "
3168c2ecf20Sopenharmony_ci			   "data\n", __func__);
3178c2ecf20Sopenharmony_ci		rval = -ENOMEM;
3188c2ecf20Sopenharmony_ci		goto leave;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
3228c2ecf20Sopenharmony_ci			  bsg_job->request_payload.sg_cnt, nvram, len);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	rval = qla4xxx_set_nvram(ha, nvram_dma, offset, len);
3258c2ecf20Sopenharmony_ci	if (rval) {
3268c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set nvram failed\n", __func__);
3278c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
3288c2ecf20Sopenharmony_ci		rval = -EIO;
3298c2ecf20Sopenharmony_ci	} else
3308c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
3338c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
3348c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, nvram, nvram_dma);
3358c2ecf20Sopenharmony_cileave:
3368c2ecf20Sopenharmony_ci	return rval;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int
3408c2ecf20Sopenharmony_ciqla4xxx_restore_defaults(struct bsg_job *bsg_job)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
3438c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
3448c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
3458c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
3468c2ecf20Sopenharmony_ci	uint32_t region = 0;
3478c2ecf20Sopenharmony_ci	uint32_t field0 = 0;
3488c2ecf20Sopenharmony_ci	uint32_t field1 = 0;
3498c2ecf20Sopenharmony_ci	int rval = -EINVAL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
3548c2ecf20Sopenharmony_ci		goto leave;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (is_qla4010(ha))
3578c2ecf20Sopenharmony_ci		goto leave;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
3608c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
3618c2ecf20Sopenharmony_ci		rval = -EBUSY;
3628c2ecf20Sopenharmony_ci		goto leave;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	region = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
3668c2ecf20Sopenharmony_ci	field0 = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
3678c2ecf20Sopenharmony_ci	field1 = bsg_req->rqst_data.h_vendor.vendor_cmd[3];
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	rval = qla4xxx_restore_factory_defaults(ha, region, field0, field1);
3708c2ecf20Sopenharmony_ci	if (rval) {
3718c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: set nvram failed\n", __func__);
3728c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
3738c2ecf20Sopenharmony_ci		rval = -EIO;
3748c2ecf20Sopenharmony_ci	} else
3758c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
3788c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
3798c2ecf20Sopenharmony_cileave:
3808c2ecf20Sopenharmony_ci	return rval;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int
3848c2ecf20Sopenharmony_ciqla4xxx_bsg_get_acb(struct bsg_job *bsg_job)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
3878c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
3888c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
3898c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
3908c2ecf20Sopenharmony_ci	uint32_t acb_type = 0;
3918c2ecf20Sopenharmony_ci	uint32_t len = 0;
3928c2ecf20Sopenharmony_ci	dma_addr_t acb_dma;
3938c2ecf20Sopenharmony_ci	uint8_t *acb = NULL;
3948c2ecf20Sopenharmony_ci	int rval = -EINVAL;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (unlikely(pci_channel_offline(ha->pdev)))
3998c2ecf20Sopenharmony_ci		goto leave;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* Only 4022 and above adapters are supported */
4028c2ecf20Sopenharmony_ci	if (is_qla4010(ha))
4038c2ecf20Sopenharmony_ci		goto leave;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (ql4xxx_reset_active(ha)) {
4068c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: reset active\n", __func__);
4078c2ecf20Sopenharmony_ci		rval = -EBUSY;
4088c2ecf20Sopenharmony_ci		goto leave;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	acb_type = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
4128c2ecf20Sopenharmony_ci	len = bsg_job->reply_payload.payload_len;
4138c2ecf20Sopenharmony_ci	if (len < sizeof(struct addr_ctrl_blk)) {
4148c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid acb len %d\n",
4158c2ecf20Sopenharmony_ci			   __func__, len);
4168c2ecf20Sopenharmony_ci		rval = -EINVAL;
4178c2ecf20Sopenharmony_ci		goto leave;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	acb = dma_alloc_coherent(&ha->pdev->dev, len, &acb_dma, GFP_KERNEL);
4218c2ecf20Sopenharmony_ci	if (!acb) {
4228c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for acb "
4238c2ecf20Sopenharmony_ci			   "data\n", __func__);
4248c2ecf20Sopenharmony_ci		rval = -ENOMEM;
4258c2ecf20Sopenharmony_ci		goto leave;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	rval = qla4xxx_get_acb(ha, acb_dma, acb_type, len);
4298c2ecf20Sopenharmony_ci	if (rval) {
4308c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: get acb failed\n", __func__);
4318c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
4328c2ecf20Sopenharmony_ci		rval = -EIO;
4338c2ecf20Sopenharmony_ci	} else {
4348c2ecf20Sopenharmony_ci		bsg_reply->reply_payload_rcv_len =
4358c2ecf20Sopenharmony_ci			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
4368c2ecf20Sopenharmony_ci					    bsg_job->reply_payload.sg_cnt,
4378c2ecf20Sopenharmony_ci					    acb, len);
4388c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
4428c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
4438c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, len, acb, acb_dma);
4448c2ecf20Sopenharmony_cileave:
4458c2ecf20Sopenharmony_ci	return rval;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void ql4xxx_execute_diag_cmd(struct bsg_job *bsg_job)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
4518c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
4528c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
4538c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
4548c2ecf20Sopenharmony_ci	uint8_t *rsp_ptr = NULL;
4558c2ecf20Sopenharmony_ci	uint32_t mbox_cmd[MBOX_REG_COUNT];
4568c2ecf20Sopenharmony_ci	uint32_t mbox_sts[MBOX_REG_COUNT];
4578c2ecf20Sopenharmony_ci	int status = QLA_ERROR;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
4628c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
4638c2ecf20Sopenharmony_ci			   __func__);
4648c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
4658c2ecf20Sopenharmony_ci		goto exit_diag_mem_test;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
4698c2ecf20Sopenharmony_ci	memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
4708c2ecf20Sopenharmony_ci	       sizeof(uint32_t) * MBOX_REG_COUNT);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
4738c2ecf20Sopenharmony_ci			  "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
4748c2ecf20Sopenharmony_ci			  __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
4758c2ecf20Sopenharmony_ci			  mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
4768c2ecf20Sopenharmony_ci			  mbox_cmd[7]));
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
4798c2ecf20Sopenharmony_ci					 &mbox_sts[0]);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
4828c2ecf20Sopenharmony_ci			  "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n",
4838c2ecf20Sopenharmony_ci			  __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2],
4848c2ecf20Sopenharmony_ci			  mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6],
4858c2ecf20Sopenharmony_ci			  mbox_sts[7]));
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	if (status == QLA_SUCCESS)
4888c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
4898c2ecf20Sopenharmony_ci	else
4908c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/* Send mbox_sts to application */
4938c2ecf20Sopenharmony_ci	bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
4948c2ecf20Sopenharmony_ci	rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
4958c2ecf20Sopenharmony_ci	memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciexit_diag_mem_test:
4988c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
4998c2ecf20Sopenharmony_ci			  "%s: bsg_reply->result = x%x, status = %s\n",
5008c2ecf20Sopenharmony_ci			  __func__, bsg_reply->result, STATUS(status)));
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
5038c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int qla4_83xx_wait_for_loopback_config_comp(struct scsi_qla_host *ha,
5078c2ecf20Sopenharmony_ci						   int wait_for_link)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	int status = QLA_SUCCESS;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&ha->idc_comp, (IDC_COMP_TOV * HZ))) {
5128c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: IDC Complete notification not received, Waiting for another %d timeout",
5138c2ecf20Sopenharmony_ci			   __func__, ha->idc_extend_tmo);
5148c2ecf20Sopenharmony_ci		if (ha->idc_extend_tmo) {
5158c2ecf20Sopenharmony_ci			if (!wait_for_completion_timeout(&ha->idc_comp,
5168c2ecf20Sopenharmony_ci						(ha->idc_extend_tmo * HZ))) {
5178c2ecf20Sopenharmony_ci				ha->notify_idc_comp = 0;
5188c2ecf20Sopenharmony_ci				ha->notify_link_up_comp = 0;
5198c2ecf20Sopenharmony_ci				ql4_printk(KERN_WARNING, ha, "%s: Aborting: IDC Complete notification not received",
5208c2ecf20Sopenharmony_ci					   __func__);
5218c2ecf20Sopenharmony_ci				status = QLA_ERROR;
5228c2ecf20Sopenharmony_ci				goto exit_wait;
5238c2ecf20Sopenharmony_ci			} else {
5248c2ecf20Sopenharmony_ci				DEBUG2(ql4_printk(KERN_INFO, ha,
5258c2ecf20Sopenharmony_ci						  "%s: IDC Complete notification received\n",
5268c2ecf20Sopenharmony_ci						  __func__));
5278c2ecf20Sopenharmony_ci			}
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci	} else {
5308c2ecf20Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha,
5318c2ecf20Sopenharmony_ci				  "%s: IDC Complete notification received\n",
5328c2ecf20Sopenharmony_ci				  __func__));
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	ha->notify_idc_comp = 0;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (wait_for_link) {
5378c2ecf20Sopenharmony_ci		if (!wait_for_completion_timeout(&ha->link_up_comp,
5388c2ecf20Sopenharmony_ci						 (IDC_COMP_TOV * HZ))) {
5398c2ecf20Sopenharmony_ci			ha->notify_link_up_comp = 0;
5408c2ecf20Sopenharmony_ci			ql4_printk(KERN_WARNING, ha, "%s: Aborting: LINK UP notification not received",
5418c2ecf20Sopenharmony_ci				   __func__);
5428c2ecf20Sopenharmony_ci			status = QLA_ERROR;
5438c2ecf20Sopenharmony_ci			goto exit_wait;
5448c2ecf20Sopenharmony_ci		} else {
5458c2ecf20Sopenharmony_ci			DEBUG2(ql4_printk(KERN_INFO, ha,
5468c2ecf20Sopenharmony_ci					  "%s: LINK UP notification received\n",
5478c2ecf20Sopenharmony_ci					  __func__));
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		ha->notify_link_up_comp = 0;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ciexit_wait:
5538c2ecf20Sopenharmony_ci	return status;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha,
5578c2ecf20Sopenharmony_ci					 uint32_t *mbox_cmd)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	uint32_t config = 0;
5608c2ecf20Sopenharmony_ci	int status = QLA_SUCCESS;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	status = qla4_83xx_get_port_config(ha, &config);
5658c2ecf20Sopenharmony_ci	if (status != QLA_SUCCESS)
5668c2ecf20Sopenharmony_ci		goto exit_pre_loopback_config;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Default port config=%08X\n",
5698c2ecf20Sopenharmony_ci			  __func__, config));
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if ((config & ENABLE_INTERNAL_LOOPBACK) ||
5728c2ecf20Sopenharmony_ci	    (config & ENABLE_EXTERNAL_LOOPBACK)) {
5738c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid request\n",
5748c2ecf20Sopenharmony_ci			   __func__);
5758c2ecf20Sopenharmony_ci		goto exit_pre_loopback_config;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
5798c2ecf20Sopenharmony_ci		config |= ENABLE_INTERNAL_LOOPBACK;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
5828c2ecf20Sopenharmony_ci		config |= ENABLE_EXTERNAL_LOOPBACK;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	config &= ~ENABLE_DCBX;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: New port config=%08X\n",
5878c2ecf20Sopenharmony_ci			  __func__, config));
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ha->notify_idc_comp = 1;
5908c2ecf20Sopenharmony_ci	ha->notify_link_up_comp = 1;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* get the link state */
5938c2ecf20Sopenharmony_ci	qla4xxx_get_firmware_state(ha);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	status = qla4_83xx_set_port_config(ha, &config);
5968c2ecf20Sopenharmony_ci	if (status != QLA_SUCCESS) {
5978c2ecf20Sopenharmony_ci		ha->notify_idc_comp = 0;
5988c2ecf20Sopenharmony_ci		ha->notify_link_up_comp = 0;
5998c2ecf20Sopenharmony_ci		goto exit_pre_loopback_config;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ciexit_pre_loopback_config:
6028c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
6038c2ecf20Sopenharmony_ci			  STATUS(status)));
6048c2ecf20Sopenharmony_ci	return status;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int qla4_83xx_post_loopback_config(struct scsi_qla_host *ha,
6088c2ecf20Sopenharmony_ci					  uint32_t *mbox_cmd)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	int status = QLA_SUCCESS;
6118c2ecf20Sopenharmony_ci	uint32_t config = 0;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	status = qla4_83xx_get_port_config(ha, &config);
6168c2ecf20Sopenharmony_ci	if (status != QLA_SUCCESS)
6178c2ecf20Sopenharmony_ci		goto exit_post_loopback_config;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: port config=%08X\n", __func__,
6208c2ecf20Sopenharmony_ci			  config));
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK)
6238c2ecf20Sopenharmony_ci		config &= ~ENABLE_INTERNAL_LOOPBACK;
6248c2ecf20Sopenharmony_ci	else if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK)
6258c2ecf20Sopenharmony_ci		config &= ~ENABLE_EXTERNAL_LOOPBACK;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	config |= ENABLE_DCBX;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
6308c2ecf20Sopenharmony_ci			  "%s: Restore default port config=%08X\n", __func__,
6318c2ecf20Sopenharmony_ci			  config));
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	ha->notify_idc_comp = 1;
6348c2ecf20Sopenharmony_ci	if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP)
6358c2ecf20Sopenharmony_ci		ha->notify_link_up_comp = 1;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	status = qla4_83xx_set_port_config(ha, &config);
6388c2ecf20Sopenharmony_ci	if (status != QLA_SUCCESS) {
6398c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Scheduling adapter reset\n",
6408c2ecf20Sopenharmony_ci			   __func__);
6418c2ecf20Sopenharmony_ci		set_bit(DPC_RESET_HA, &ha->dpc_flags);
6428c2ecf20Sopenharmony_ci		clear_bit(AF_LOOPBACK, &ha->flags);
6438c2ecf20Sopenharmony_ci		goto exit_post_loopback_config;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ciexit_post_loopback_config:
6478c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__,
6488c2ecf20Sopenharmony_ci			  STATUS(status)));
6498c2ecf20Sopenharmony_ci	return status;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic void qla4xxx_execute_diag_loopback_cmd(struct bsg_job *bsg_job)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
6558c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
6568c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
6578c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
6588c2ecf20Sopenharmony_ci	uint8_t *rsp_ptr = NULL;
6598c2ecf20Sopenharmony_ci	uint32_t mbox_cmd[MBOX_REG_COUNT];
6608c2ecf20Sopenharmony_ci	uint32_t mbox_sts[MBOX_REG_COUNT];
6618c2ecf20Sopenharmony_ci	int wait_for_link = 1;
6628c2ecf20Sopenharmony_ci	int status = QLA_ERROR;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (test_bit(AF_LOOPBACK, &ha->flags)) {
6698c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Loopback Diagnostics already in progress. Invalid Request\n",
6708c2ecf20Sopenharmony_ci			   __func__);
6718c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
6728c2ecf20Sopenharmony_ci		goto exit_loopback_cmd;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
6768c2ecf20Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n",
6778c2ecf20Sopenharmony_ci			   __func__);
6788c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
6798c2ecf20Sopenharmony_ci		goto exit_loopback_cmd;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1],
6838c2ecf20Sopenharmony_ci	       sizeof(uint32_t) * MBOX_REG_COUNT);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (is_qla8032(ha) || is_qla8042(ha)) {
6868c2ecf20Sopenharmony_ci		status = qla4_83xx_pre_loopback_config(ha, mbox_cmd);
6878c2ecf20Sopenharmony_ci		if (status != QLA_SUCCESS) {
6888c2ecf20Sopenharmony_ci			bsg_reply->result = DID_ERROR << 16;
6898c2ecf20Sopenharmony_ci			goto exit_loopback_cmd;
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		status = qla4_83xx_wait_for_loopback_config_comp(ha,
6938c2ecf20Sopenharmony_ci								 wait_for_link);
6948c2ecf20Sopenharmony_ci		if (status != QLA_SUCCESS) {
6958c2ecf20Sopenharmony_ci			bsg_reply->result = DID_TIME_OUT << 16;
6968c2ecf20Sopenharmony_ci			goto restore;
6978c2ecf20Sopenharmony_ci		}
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
7018c2ecf20Sopenharmony_ci			  "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n",
7028c2ecf20Sopenharmony_ci			  __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2],
7038c2ecf20Sopenharmony_ci			  mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6],
7048c2ecf20Sopenharmony_ci			  mbox_cmd[7]));
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
7078c2ecf20Sopenharmony_ci				&mbox_sts[0]);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (status == QLA_SUCCESS)
7108c2ecf20Sopenharmony_ci		bsg_reply->result = DID_OK << 16;
7118c2ecf20Sopenharmony_ci	else
7128c2ecf20Sopenharmony_ci		bsg_reply->result = DID_ERROR << 16;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
7158c2ecf20Sopenharmony_ci			  "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n",
7168c2ecf20Sopenharmony_ci			  __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2],
7178c2ecf20Sopenharmony_ci			  mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6],
7188c2ecf20Sopenharmony_ci			  mbox_sts[7]));
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Send mbox_sts to application */
7218c2ecf20Sopenharmony_ci	bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts);
7228c2ecf20Sopenharmony_ci	rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply);
7238c2ecf20Sopenharmony_ci	memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts));
7248c2ecf20Sopenharmony_cirestore:
7258c2ecf20Sopenharmony_ci	if (is_qla8032(ha) || is_qla8042(ha)) {
7268c2ecf20Sopenharmony_ci		status = qla4_83xx_post_loopback_config(ha, mbox_cmd);
7278c2ecf20Sopenharmony_ci		if (status != QLA_SUCCESS) {
7288c2ecf20Sopenharmony_ci			bsg_reply->result = DID_ERROR << 16;
7298c2ecf20Sopenharmony_ci			goto exit_loopback_cmd;
7308c2ecf20Sopenharmony_ci		}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci		/* for pre_loopback_config() wait for LINK UP only
7338c2ecf20Sopenharmony_ci		 * if PHY LINK is UP */
7348c2ecf20Sopenharmony_ci		if (!(ha->addl_fw_state & FW_ADDSTATE_LINK_UP))
7358c2ecf20Sopenharmony_ci			wait_for_link = 0;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		status = qla4_83xx_wait_for_loopback_config_comp(ha,
7388c2ecf20Sopenharmony_ci								 wait_for_link);
7398c2ecf20Sopenharmony_ci		if (status != QLA_SUCCESS) {
7408c2ecf20Sopenharmony_ci			bsg_reply->result = DID_TIME_OUT << 16;
7418c2ecf20Sopenharmony_ci			goto exit_loopback_cmd;
7428c2ecf20Sopenharmony_ci		}
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ciexit_loopback_cmd:
7458c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
7468c2ecf20Sopenharmony_ci			  "%s: bsg_reply->result = x%x, status = %s\n",
7478c2ecf20Sopenharmony_ci			  __func__, bsg_reply->result, STATUS(status)));
7488c2ecf20Sopenharmony_ci	bsg_job_done(bsg_job, bsg_reply->result,
7498c2ecf20Sopenharmony_ci		     bsg_reply->reply_payload_rcv_len);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic int qla4xxx_execute_diag_test(struct bsg_job *bsg_job)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
7558c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
7568c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
7578c2ecf20Sopenharmony_ci	uint32_t diag_cmd;
7588c2ecf20Sopenharmony_ci	int rval = -EINVAL;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__));
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	diag_cmd = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
7638c2ecf20Sopenharmony_ci	if (diag_cmd == MBOX_CMD_DIAG_TEST) {
7648c2ecf20Sopenharmony_ci		switch (bsg_req->rqst_data.h_vendor.vendor_cmd[2]) {
7658c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_DDR_SIZE:
7668c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_DDR_RW:
7678c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_ONCHIP_MEM_RW:
7688c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_NVRAM:
7698c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_FLASH_ROM:
7708c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_DMA_XFER:
7718c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_SELF_DDR_RW:
7728c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_SELF_ONCHIP_MEM_RW:
7738c2ecf20Sopenharmony_ci			/* Execute diag test for adapter RAM/FLASH */
7748c2ecf20Sopenharmony_ci			ql4xxx_execute_diag_cmd(bsg_job);
7758c2ecf20Sopenharmony_ci			/* Always return success as we want to sent bsg_reply
7768c2ecf20Sopenharmony_ci			 * to Application */
7778c2ecf20Sopenharmony_ci			rval = QLA_SUCCESS;
7788c2ecf20Sopenharmony_ci			break;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_INT_LOOPBACK:
7818c2ecf20Sopenharmony_ci		case QL_DIAG_CMD_TEST_EXT_LOOPBACK:
7828c2ecf20Sopenharmony_ci			/* Execute diag test for Network */
7838c2ecf20Sopenharmony_ci			qla4xxx_execute_diag_loopback_cmd(bsg_job);
7848c2ecf20Sopenharmony_ci			/* Always return success as we want to sent bsg_reply
7858c2ecf20Sopenharmony_ci			 * to Application */
7868c2ecf20Sopenharmony_ci			rval = QLA_SUCCESS;
7878c2ecf20Sopenharmony_ci			break;
7888c2ecf20Sopenharmony_ci		default:
7898c2ecf20Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: Invalid diag test: 0x%x\n",
7908c2ecf20Sopenharmony_ci				   __func__,
7918c2ecf20Sopenharmony_ci				   bsg_req->rqst_data.h_vendor.vendor_cmd[2]);
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci	} else if ((diag_cmd == MBOX_CMD_SET_LED_CONFIG) ||
7948c2ecf20Sopenharmony_ci		   (diag_cmd == MBOX_CMD_GET_LED_CONFIG)) {
7958c2ecf20Sopenharmony_ci		ql4xxx_execute_diag_cmd(bsg_job);
7968c2ecf20Sopenharmony_ci		rval = QLA_SUCCESS;
7978c2ecf20Sopenharmony_ci	} else {
7988c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Invalid diag cmd: 0x%x\n",
7998c2ecf20Sopenharmony_ci			   __func__, diag_cmd);
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	return rval;
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/**
8068c2ecf20Sopenharmony_ci * qla4xxx_process_vendor_specific - handle vendor specific bsg request
8078c2ecf20Sopenharmony_ci * @bsg_job: iscsi_bsg_job to handle
8088c2ecf20Sopenharmony_ci **/
8098c2ecf20Sopenharmony_ciint qla4xxx_process_vendor_specific(struct bsg_job *bsg_job)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	struct iscsi_bsg_reply *bsg_reply = bsg_job->reply;
8128c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
8138c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
8148c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	switch (bsg_req->rqst_data.h_vendor.vendor_cmd[0]) {
8178c2ecf20Sopenharmony_ci	case QLISCSI_VND_READ_FLASH:
8188c2ecf20Sopenharmony_ci		return qla4xxx_read_flash(bsg_job);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	case QLISCSI_VND_UPDATE_FLASH:
8218c2ecf20Sopenharmony_ci		return qla4xxx_update_flash(bsg_job);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	case QLISCSI_VND_GET_ACB_STATE:
8248c2ecf20Sopenharmony_ci		return qla4xxx_get_acb_state(bsg_job);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	case QLISCSI_VND_READ_NVRAM:
8278c2ecf20Sopenharmony_ci		return qla4xxx_read_nvram(bsg_job);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	case QLISCSI_VND_UPDATE_NVRAM:
8308c2ecf20Sopenharmony_ci		return qla4xxx_update_nvram(bsg_job);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	case QLISCSI_VND_RESTORE_DEFAULTS:
8338c2ecf20Sopenharmony_ci		return qla4xxx_restore_defaults(bsg_job);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	case QLISCSI_VND_GET_ACB:
8368c2ecf20Sopenharmony_ci		return qla4xxx_bsg_get_acb(bsg_job);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	case QLISCSI_VND_DIAG_TEST:
8398c2ecf20Sopenharmony_ci		return qla4xxx_execute_diag_test(bsg_job);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	default:
8428c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: "
8438c2ecf20Sopenharmony_ci			   "0x%x\n", __func__, bsg_req->msgcode);
8448c2ecf20Sopenharmony_ci		bsg_reply->result = (DID_ERROR << 16);
8458c2ecf20Sopenharmony_ci		bsg_reply->reply_payload_rcv_len = 0;
8468c2ecf20Sopenharmony_ci		bsg_job_done(bsg_job, bsg_reply->result,
8478c2ecf20Sopenharmony_ci			     bsg_reply->reply_payload_rcv_len);
8488c2ecf20Sopenharmony_ci		return -ENOSYS;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci/**
8538c2ecf20Sopenharmony_ci * qla4xxx_bsg_request - handle bsg request from ISCSI transport
8548c2ecf20Sopenharmony_ci * @bsg_job: iscsi_bsg_job to handle
8558c2ecf20Sopenharmony_ci */
8568c2ecf20Sopenharmony_ciint qla4xxx_bsg_request(struct bsg_job *bsg_job)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct iscsi_bsg_request *bsg_req = bsg_job->request;
8598c2ecf20Sopenharmony_ci	struct Scsi_Host *host = iscsi_job_to_shost(bsg_job);
8608c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = to_qla_host(host);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	switch (bsg_req->msgcode) {
8638c2ecf20Sopenharmony_ci	case ISCSI_BSG_HST_VENDOR:
8648c2ecf20Sopenharmony_ci		return qla4xxx_process_vendor_specific(bsg_job);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	default:
8678c2ecf20Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: invalid BSG command: 0x%x\n",
8688c2ecf20Sopenharmony_ci			   __func__, bsg_req->msgcode);
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return -ENOSYS;
8728c2ecf20Sopenharmony_ci}
873