18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * QLogic iSCSI HBA Driver
48c2ecf20Sopenharmony_ci * Copyright (c)  2003-2013 QLogic Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "ql4_def.h"
88c2ecf20Sopenharmony_ci#include "ql4_glbl.h"
98c2ecf20Sopenharmony_ci#include "ql4_dbg.h"
108c2ecf20Sopenharmony_ci#include "ql4_inline.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int
158c2ecf20Sopenharmony_ciqla4xxx_space_in_req_ring(struct scsi_qla_host *ha, uint16_t req_cnt)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	uint16_t cnt;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	/* Calculate number of free request entries. */
208c2ecf20Sopenharmony_ci	if ((req_cnt + 2) >= ha->req_q_count) {
218c2ecf20Sopenharmony_ci		cnt = (uint16_t) ha->isp_ops->rd_shdw_req_q_out(ha);
228c2ecf20Sopenharmony_ci		if (ha->request_in < cnt)
238c2ecf20Sopenharmony_ci			ha->req_q_count = cnt - ha->request_in;
248c2ecf20Sopenharmony_ci		else
258c2ecf20Sopenharmony_ci			ha->req_q_count = REQUEST_QUEUE_DEPTH -
268c2ecf20Sopenharmony_ci						(ha->request_in - cnt);
278c2ecf20Sopenharmony_ci	}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* Check if room for request in request ring. */
308c2ecf20Sopenharmony_ci	if ((req_cnt + 2) < ha->req_q_count)
318c2ecf20Sopenharmony_ci		return 1;
328c2ecf20Sopenharmony_ci	else
338c2ecf20Sopenharmony_ci		return 0;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void qla4xxx_advance_req_ring_ptr(struct scsi_qla_host *ha)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	/* Advance request queue pointer */
398c2ecf20Sopenharmony_ci	if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1)) {
408c2ecf20Sopenharmony_ci		ha->request_in = 0;
418c2ecf20Sopenharmony_ci		ha->request_ptr = ha->request_ring;
428c2ecf20Sopenharmony_ci	} else {
438c2ecf20Sopenharmony_ci		ha->request_in++;
448c2ecf20Sopenharmony_ci		ha->request_ptr++;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/**
498c2ecf20Sopenharmony_ci * qla4xxx_get_req_pkt - returns a valid entry in request queue.
508c2ecf20Sopenharmony_ci * @ha: Pointer to host adapter structure.
518c2ecf20Sopenharmony_ci * @queue_entry: Pointer to pointer to queue entry structure
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * This routine performs the following tasks:
548c2ecf20Sopenharmony_ci *	- returns the current request_in pointer (if queue not full)
558c2ecf20Sopenharmony_ci *	- advances the request_in pointer
568c2ecf20Sopenharmony_ci *	- checks for queue full
578c2ecf20Sopenharmony_ci **/
588c2ecf20Sopenharmony_cistatic int qla4xxx_get_req_pkt(struct scsi_qla_host *ha,
598c2ecf20Sopenharmony_ci			       struct queue_entry **queue_entry)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	uint16_t req_cnt = 1;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (qla4xxx_space_in_req_ring(ha, req_cnt)) {
648c2ecf20Sopenharmony_ci		*queue_entry = ha->request_ptr;
658c2ecf20Sopenharmony_ci		memset(*queue_entry, 0, sizeof(**queue_entry));
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		qla4xxx_advance_req_ring_ptr(ha);
688c2ecf20Sopenharmony_ci		ha->req_q_count -= req_cnt;
698c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return QLA_ERROR;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/**
768c2ecf20Sopenharmony_ci * qla4xxx_send_marker_iocb - issues marker iocb to HBA
778c2ecf20Sopenharmony_ci * @ha: Pointer to host adapter structure.
788c2ecf20Sopenharmony_ci * @ddb_entry: Pointer to device database entry
798c2ecf20Sopenharmony_ci * @lun: SCSI LUN
808c2ecf20Sopenharmony_ci * @mrkr_mod: marker identifier
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * This routine issues a marker IOCB.
838c2ecf20Sopenharmony_ci **/
848c2ecf20Sopenharmony_ciint qla4xxx_send_marker_iocb(struct scsi_qla_host *ha,
858c2ecf20Sopenharmony_ci	struct ddb_entry *ddb_entry, uint64_t lun, uint16_t mrkr_mod)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct qla4_marker_entry *marker_entry;
888c2ecf20Sopenharmony_ci	unsigned long flags = 0;
898c2ecf20Sopenharmony_ci	uint8_t status = QLA_SUCCESS;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* Acquire hardware specific lock */
928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Get pointer to the queue entry for the marker */
958c2ecf20Sopenharmony_ci	if (qla4xxx_get_req_pkt(ha, (struct queue_entry **) &marker_entry) !=
968c2ecf20Sopenharmony_ci	    QLA_SUCCESS) {
978c2ecf20Sopenharmony_ci		status = QLA_ERROR;
988c2ecf20Sopenharmony_ci		goto exit_send_marker;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Put the marker in the request queue */
1028c2ecf20Sopenharmony_ci	marker_entry->hdr.entryType = ET_MARKER;
1038c2ecf20Sopenharmony_ci	marker_entry->hdr.entryCount = 1;
1048c2ecf20Sopenharmony_ci	marker_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index);
1058c2ecf20Sopenharmony_ci	marker_entry->modifier = cpu_to_le16(mrkr_mod);
1068c2ecf20Sopenharmony_ci	int_to_scsilun(lun, &marker_entry->lun);
1078c2ecf20Sopenharmony_ci	wmb();
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Tell ISP it's got a new I/O request */
1108c2ecf20Sopenharmony_ci	ha->isp_ops->queue_iocb(ha);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciexit_send_marker:
1138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
1148c2ecf20Sopenharmony_ci	return status;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct continuation_t1_entry *
1188c2ecf20Sopenharmony_ciqla4xxx_alloc_cont_entry(struct scsi_qla_host *ha)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct continuation_t1_entry *cont_entry;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	cont_entry = (struct continuation_t1_entry *)ha->request_ptr;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	qla4xxx_advance_req_ring_ptr(ha);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Load packet defaults */
1278c2ecf20Sopenharmony_ci	cont_entry->hdr.entryType = ET_CONTINUE;
1288c2ecf20Sopenharmony_ci	cont_entry->hdr.entryCount = 1;
1298c2ecf20Sopenharmony_ci	cont_entry->hdr.systemDefined = (uint8_t) cpu_to_le16(ha->request_in);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return cont_entry;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic uint16_t qla4xxx_calc_request_entries(uint16_t dsds)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	uint16_t iocbs;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	iocbs = 1;
1398c2ecf20Sopenharmony_ci	if (dsds > COMMAND_SEG) {
1408c2ecf20Sopenharmony_ci		iocbs += (dsds - COMMAND_SEG) / CONTINUE_SEG;
1418c2ecf20Sopenharmony_ci		if ((dsds - COMMAND_SEG) % CONTINUE_SEG)
1428c2ecf20Sopenharmony_ci			iocbs++;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci	return iocbs;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void qla4xxx_build_scsi_iocbs(struct srb *srb,
1488c2ecf20Sopenharmony_ci				     struct command_t3_entry *cmd_entry,
1498c2ecf20Sopenharmony_ci				     uint16_t tot_dsds)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha;
1528c2ecf20Sopenharmony_ci	uint16_t avail_dsds;
1538c2ecf20Sopenharmony_ci	struct data_seg_a64 *cur_dsd;
1548c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
1558c2ecf20Sopenharmony_ci	struct scatterlist *sg;
1568c2ecf20Sopenharmony_ci	int i;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	cmd = srb->cmd;
1598c2ecf20Sopenharmony_ci	ha = srb->ha;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
1628c2ecf20Sopenharmony_ci		/* No data being transferred */
1638c2ecf20Sopenharmony_ci		cmd_entry->ttlByteCnt = __constant_cpu_to_le32(0);
1648c2ecf20Sopenharmony_ci		return;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	avail_dsds = COMMAND_SEG;
1688c2ecf20Sopenharmony_ci	cur_dsd = (struct data_seg_a64 *) & (cmd_entry->dataseg[0]);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	scsi_for_each_sg(cmd, sg, tot_dsds, i) {
1718c2ecf20Sopenharmony_ci		dma_addr_t sle_dma;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		/* Allocate additional continuation packets? */
1748c2ecf20Sopenharmony_ci		if (avail_dsds == 0) {
1758c2ecf20Sopenharmony_ci			struct continuation_t1_entry *cont_entry;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci			cont_entry = qla4xxx_alloc_cont_entry(ha);
1788c2ecf20Sopenharmony_ci			cur_dsd =
1798c2ecf20Sopenharmony_ci				(struct data_seg_a64 *)
1808c2ecf20Sopenharmony_ci				&cont_entry->dataseg[0];
1818c2ecf20Sopenharmony_ci			avail_dsds = CONTINUE_SEG;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		sle_dma = sg_dma_address(sg);
1858c2ecf20Sopenharmony_ci		cur_dsd->base.addrLow = cpu_to_le32(LSDW(sle_dma));
1868c2ecf20Sopenharmony_ci		cur_dsd->base.addrHigh = cpu_to_le32(MSDW(sle_dma));
1878c2ecf20Sopenharmony_ci		cur_dsd->count = cpu_to_le32(sg_dma_len(sg));
1888c2ecf20Sopenharmony_ci		avail_dsds--;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		cur_dsd++;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_civoid qla4_83xx_queue_iocb(struct scsi_qla_host *ha)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	writel(ha->request_in, &ha->qla4_83xx_reg->req_q_in);
1978c2ecf20Sopenharmony_ci	readl(&ha->qla4_83xx_reg->req_q_in);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_civoid qla4_83xx_complete_iocb(struct scsi_qla_host *ha)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	writel(ha->response_out, &ha->qla4_83xx_reg->rsp_q_out);
2038c2ecf20Sopenharmony_ci	readl(&ha->qla4_83xx_reg->rsp_q_out);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/**
2078c2ecf20Sopenharmony_ci * qla4_82xx_queue_iocb - Tell ISP it's got new request(s)
2088c2ecf20Sopenharmony_ci * @ha: pointer to host adapter structure.
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * This routine notifies the ISP that one or more new request
2118c2ecf20Sopenharmony_ci * queue entries have been placed on the request queue.
2128c2ecf20Sopenharmony_ci **/
2138c2ecf20Sopenharmony_civoid qla4_82xx_queue_iocb(struct scsi_qla_host *ha)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	uint32_t dbval = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	dbval = 0x14 | (ha->func_num << 5);
2188c2ecf20Sopenharmony_ci	dbval = dbval | (0 << 8) | (ha->request_in << 16);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	qla4_82xx_wr_32(ha, ha->nx_db_wr_ptr, ha->request_in);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/**
2248c2ecf20Sopenharmony_ci * qla4_82xx_complete_iocb - Tell ISP we're done with response(s)
2258c2ecf20Sopenharmony_ci * @ha: pointer to host adapter structure.
2268c2ecf20Sopenharmony_ci *
2278c2ecf20Sopenharmony_ci * This routine notifies the ISP that one or more response/completion
2288c2ecf20Sopenharmony_ci * queue entries have been processed by the driver.
2298c2ecf20Sopenharmony_ci * This also clears the interrupt.
2308c2ecf20Sopenharmony_ci **/
2318c2ecf20Sopenharmony_civoid qla4_82xx_complete_iocb(struct scsi_qla_host *ha)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	writel(ha->response_out, &ha->qla4_82xx_reg->rsp_q_out);
2348c2ecf20Sopenharmony_ci	readl(&ha->qla4_82xx_reg->rsp_q_out);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/**
2388c2ecf20Sopenharmony_ci * qla4xxx_queue_iocb - Tell ISP it's got new request(s)
2398c2ecf20Sopenharmony_ci * @ha: pointer to host adapter structure.
2408c2ecf20Sopenharmony_ci *
2418c2ecf20Sopenharmony_ci * This routine is notifies the ISP that one or more new request
2428c2ecf20Sopenharmony_ci * queue entries have been placed on the request queue.
2438c2ecf20Sopenharmony_ci **/
2448c2ecf20Sopenharmony_civoid qla4xxx_queue_iocb(struct scsi_qla_host *ha)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	writel(ha->request_in, &ha->reg->req_q_in);
2478c2ecf20Sopenharmony_ci	readl(&ha->reg->req_q_in);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/**
2518c2ecf20Sopenharmony_ci * qla4xxx_complete_iocb - Tell ISP we're done with response(s)
2528c2ecf20Sopenharmony_ci * @ha: pointer to host adapter structure.
2538c2ecf20Sopenharmony_ci *
2548c2ecf20Sopenharmony_ci * This routine is notifies the ISP that one or more response/completion
2558c2ecf20Sopenharmony_ci * queue entries have been processed by the driver.
2568c2ecf20Sopenharmony_ci * This also clears the interrupt.
2578c2ecf20Sopenharmony_ci **/
2588c2ecf20Sopenharmony_civoid qla4xxx_complete_iocb(struct scsi_qla_host *ha)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	writel(ha->response_out, &ha->reg->rsp_q_out);
2618c2ecf20Sopenharmony_ci	readl(&ha->reg->rsp_q_out);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/**
2658c2ecf20Sopenharmony_ci * qla4xxx_send_command_to_isp - issues command to HBA
2668c2ecf20Sopenharmony_ci * @ha: pointer to host adapter structure.
2678c2ecf20Sopenharmony_ci * @srb: pointer to SCSI Request Block to be sent to ISP
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * This routine is called by qla4xxx_queuecommand to build an ISP
2708c2ecf20Sopenharmony_ci * command and pass it to the ISP for execution.
2718c2ecf20Sopenharmony_ci **/
2728c2ecf20Sopenharmony_ciint qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = srb->cmd;
2758c2ecf20Sopenharmony_ci	struct ddb_entry *ddb_entry;
2768c2ecf20Sopenharmony_ci	struct command_t3_entry *cmd_entry;
2778c2ecf20Sopenharmony_ci	int nseg;
2788c2ecf20Sopenharmony_ci	uint16_t tot_dsds;
2798c2ecf20Sopenharmony_ci	uint16_t req_cnt;
2808c2ecf20Sopenharmony_ci	unsigned long flags;
2818c2ecf20Sopenharmony_ci	uint32_t index;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* Get real lun and adapter */
2848c2ecf20Sopenharmony_ci	ddb_entry = srb->ddb;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	tot_dsds = 0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Acquire hardware specific lock */
2898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	index = (uint32_t)cmd->request->tag;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/*
2948c2ecf20Sopenharmony_ci	 * Check to see if adapter is online before placing request on
2958c2ecf20Sopenharmony_ci	 * request queue.  If a reset occurs and a request is in the queue,
2968c2ecf20Sopenharmony_ci	 * the firmware will still attempt to process the request, retrieving
2978c2ecf20Sopenharmony_ci	 * garbage for pointers.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	if (!test_bit(AF_ONLINE, &ha->flags)) {
3008c2ecf20Sopenharmony_ci		DEBUG2(printk("scsi%ld: %s: Adapter OFFLINE! "
3018c2ecf20Sopenharmony_ci			      "Do not issue command.\n",
3028c2ecf20Sopenharmony_ci			      ha->host_no, __func__));
3038c2ecf20Sopenharmony_ci		goto queuing_error;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Calculate the number of request entries needed. */
3078c2ecf20Sopenharmony_ci	nseg = scsi_dma_map(cmd);
3088c2ecf20Sopenharmony_ci	if (nseg < 0)
3098c2ecf20Sopenharmony_ci		goto queuing_error;
3108c2ecf20Sopenharmony_ci	tot_dsds = nseg;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	req_cnt = qla4xxx_calc_request_entries(tot_dsds);
3138c2ecf20Sopenharmony_ci	if (!qla4xxx_space_in_req_ring(ha, req_cnt))
3148c2ecf20Sopenharmony_ci		goto queuing_error;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* total iocbs active */
3178c2ecf20Sopenharmony_ci	if ((ha->iocb_cnt + req_cnt) >= ha->iocb_hiwat)
3188c2ecf20Sopenharmony_ci		goto queuing_error;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Build command packet */
3218c2ecf20Sopenharmony_ci	cmd_entry = (struct command_t3_entry *) ha->request_ptr;
3228c2ecf20Sopenharmony_ci	memset(cmd_entry, 0, sizeof(struct command_t3_entry));
3238c2ecf20Sopenharmony_ci	cmd_entry->hdr.entryType = ET_COMMAND;
3248c2ecf20Sopenharmony_ci	cmd_entry->handle = cpu_to_le32(index);
3258c2ecf20Sopenharmony_ci	cmd_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	int_to_scsilun(cmd->device->lun, &cmd_entry->lun);
3288c2ecf20Sopenharmony_ci	cmd_entry->ttlByteCnt = cpu_to_le32(scsi_bufflen(cmd));
3298c2ecf20Sopenharmony_ci	memcpy(cmd_entry->cdb, cmd->cmnd, cmd->cmd_len);
3308c2ecf20Sopenharmony_ci	cmd_entry->dataSegCnt = cpu_to_le16(tot_dsds);
3318c2ecf20Sopenharmony_ci	cmd_entry->hdr.entryCount = req_cnt;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Set data transfer direction control flags
3348c2ecf20Sopenharmony_ci	 * NOTE: Look at data_direction bits iff there is data to be
3358c2ecf20Sopenharmony_ci	 *	 transferred, as the data direction bit is sometimed filled
3368c2ecf20Sopenharmony_ci	 *	 in when there is no data to be transferred */
3378c2ecf20Sopenharmony_ci	cmd_entry->control_flags = CF_NO_DATA;
3388c2ecf20Sopenharmony_ci	if (scsi_bufflen(cmd)) {
3398c2ecf20Sopenharmony_ci		if (cmd->sc_data_direction == DMA_TO_DEVICE)
3408c2ecf20Sopenharmony_ci			cmd_entry->control_flags = CF_WRITE;
3418c2ecf20Sopenharmony_ci		else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
3428c2ecf20Sopenharmony_ci			cmd_entry->control_flags = CF_READ;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		ha->bytes_xfered += scsi_bufflen(cmd);
3458c2ecf20Sopenharmony_ci		if (ha->bytes_xfered & ~0xFFFFF){
3468c2ecf20Sopenharmony_ci			ha->total_mbytes_xferred += ha->bytes_xfered >> 20;
3478c2ecf20Sopenharmony_ci			ha->bytes_xfered &= 0xFFFFF;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Set tagged queueing control flags */
3528c2ecf20Sopenharmony_ci	cmd_entry->control_flags |= CF_SIMPLE_TAG;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	qla4xxx_advance_req_ring_ptr(ha);
3558c2ecf20Sopenharmony_ci	qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds);
3568c2ecf20Sopenharmony_ci	wmb();
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	srb->cmd->host_scribble = (unsigned char *)(unsigned long)index;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* update counters */
3618c2ecf20Sopenharmony_ci	srb->state = SRB_ACTIVE_STATE;
3628c2ecf20Sopenharmony_ci	srb->flags |= SRB_DMA_VALID;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* Track IOCB used */
3658c2ecf20Sopenharmony_ci	ha->iocb_cnt += req_cnt;
3668c2ecf20Sopenharmony_ci	srb->iocb_cnt = req_cnt;
3678c2ecf20Sopenharmony_ci	ha->req_q_count -= req_cnt;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ha->isp_ops->queue_iocb(ha);
3708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ciqueuing_error:
3758c2ecf20Sopenharmony_ci	if (tot_dsds)
3768c2ecf20Sopenharmony_ci		scsi_dma_unmap(cmd);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return QLA_ERROR;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciint qla4xxx_send_passthru0(struct iscsi_task *task)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct passthru0 *passthru_iocb;
3868c2ecf20Sopenharmony_ci	struct iscsi_session *sess = task->conn->session;
3878c2ecf20Sopenharmony_ci	struct ddb_entry *ddb_entry = sess->dd_data;
3888c2ecf20Sopenharmony_ci	struct scsi_qla_host *ha = ddb_entry->ha;
3898c2ecf20Sopenharmony_ci	struct ql4_task_data *task_data = task->dd_data;
3908c2ecf20Sopenharmony_ci	uint16_t ctrl_flags = 0;
3918c2ecf20Sopenharmony_ci	unsigned long flags;
3928c2ecf20Sopenharmony_ci	int ret = QLA_ERROR;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
3958c2ecf20Sopenharmony_ci	task_data->iocb_req_cnt = 1;
3968c2ecf20Sopenharmony_ci	/* Put the IOCB on the request queue */
3978c2ecf20Sopenharmony_ci	if (!qla4xxx_space_in_req_ring(ha, task_data->iocb_req_cnt))
3988c2ecf20Sopenharmony_ci		goto queuing_error;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	passthru_iocb = (struct passthru0 *) ha->request_ptr;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	memset(passthru_iocb, 0, sizeof(struct passthru0));
4038c2ecf20Sopenharmony_ci	passthru_iocb->hdr.entryType = ET_PASSTHRU0;
4048c2ecf20Sopenharmony_ci	passthru_iocb->hdr.systemDefined = SD_ISCSI_PDU;
4058c2ecf20Sopenharmony_ci	passthru_iocb->hdr.entryCount = task_data->iocb_req_cnt;
4068c2ecf20Sopenharmony_ci	passthru_iocb->handle = task->itt;
4078c2ecf20Sopenharmony_ci	passthru_iocb->target = cpu_to_le16(ddb_entry->fw_ddb_index);
4088c2ecf20Sopenharmony_ci	passthru_iocb->timeout = cpu_to_le16(PT_DEFAULT_TIMEOUT);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Setup the out & in DSDs */
4118c2ecf20Sopenharmony_ci	if (task_data->req_len) {
4128c2ecf20Sopenharmony_ci		memcpy((uint8_t *)task_data->req_buffer +
4138c2ecf20Sopenharmony_ci		       sizeof(struct iscsi_hdr), task->data, task->data_count);
4148c2ecf20Sopenharmony_ci		ctrl_flags |= PT_FLAG_SEND_BUFFER;
4158c2ecf20Sopenharmony_ci		passthru_iocb->out_dsd.base.addrLow =
4168c2ecf20Sopenharmony_ci					cpu_to_le32(LSDW(task_data->req_dma));
4178c2ecf20Sopenharmony_ci		passthru_iocb->out_dsd.base.addrHigh =
4188c2ecf20Sopenharmony_ci					cpu_to_le32(MSDW(task_data->req_dma));
4198c2ecf20Sopenharmony_ci		passthru_iocb->out_dsd.count =
4208c2ecf20Sopenharmony_ci					cpu_to_le32(task->data_count +
4218c2ecf20Sopenharmony_ci						    sizeof(struct iscsi_hdr));
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	if (task_data->resp_len) {
4248c2ecf20Sopenharmony_ci		passthru_iocb->in_dsd.base.addrLow =
4258c2ecf20Sopenharmony_ci					cpu_to_le32(LSDW(task_data->resp_dma));
4268c2ecf20Sopenharmony_ci		passthru_iocb->in_dsd.base.addrHigh =
4278c2ecf20Sopenharmony_ci					cpu_to_le32(MSDW(task_data->resp_dma));
4288c2ecf20Sopenharmony_ci		passthru_iocb->in_dsd.count =
4298c2ecf20Sopenharmony_ci			cpu_to_le32(task_data->resp_len);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ctrl_flags |= (PT_FLAG_ISCSI_PDU | PT_FLAG_WAIT_4_RESPONSE);
4338c2ecf20Sopenharmony_ci	passthru_iocb->control_flags = cpu_to_le16(ctrl_flags);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Update the request pointer */
4368c2ecf20Sopenharmony_ci	qla4xxx_advance_req_ring_ptr(ha);
4378c2ecf20Sopenharmony_ci	wmb();
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Track IOCB used */
4408c2ecf20Sopenharmony_ci	ha->iocb_cnt += task_data->iocb_req_cnt;
4418c2ecf20Sopenharmony_ci	ha->req_q_count -= task_data->iocb_req_cnt;
4428c2ecf20Sopenharmony_ci	ha->isp_ops->queue_iocb(ha);
4438c2ecf20Sopenharmony_ci	ret = QLA_SUCCESS;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ciqueuing_error:
4468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
4478c2ecf20Sopenharmony_ci	return ret;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic struct mrb *qla4xxx_get_new_mrb(struct scsi_qla_host *ha)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct mrb *mrb;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	mrb = kzalloc(sizeof(*mrb), GFP_KERNEL);
4558c2ecf20Sopenharmony_ci	if (!mrb)
4568c2ecf20Sopenharmony_ci		return mrb;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	mrb->ha = ha;
4598c2ecf20Sopenharmony_ci	return mrb;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int qla4xxx_send_mbox_iocb(struct scsi_qla_host *ha, struct mrb *mrb,
4638c2ecf20Sopenharmony_ci				  uint32_t *in_mbox)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	int rval = QLA_SUCCESS;
4668c2ecf20Sopenharmony_ci	uint32_t i;
4678c2ecf20Sopenharmony_ci	unsigned long flags;
4688c2ecf20Sopenharmony_ci	uint32_t index = 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Acquire hardware specific lock */
4718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Get pointer to the queue entry for the marker */
4748c2ecf20Sopenharmony_ci	rval = qla4xxx_get_req_pkt(ha, (struct queue_entry **) &(mrb->mbox));
4758c2ecf20Sopenharmony_ci	if (rval != QLA_SUCCESS)
4768c2ecf20Sopenharmony_ci		goto exit_mbox_iocb;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	index = ha->mrb_index;
4798c2ecf20Sopenharmony_ci	/* get valid mrb index*/
4808c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_MRB; i++) {
4818c2ecf20Sopenharmony_ci		index++;
4828c2ecf20Sopenharmony_ci		if (index == MAX_MRB)
4838c2ecf20Sopenharmony_ci			index = 1;
4848c2ecf20Sopenharmony_ci		if (ha->active_mrb_array[index] == NULL) {
4858c2ecf20Sopenharmony_ci			ha->mrb_index = index;
4868c2ecf20Sopenharmony_ci			break;
4878c2ecf20Sopenharmony_ci		}
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	mrb->iocb_cnt = 1;
4918c2ecf20Sopenharmony_ci	ha->active_mrb_array[index] = mrb;
4928c2ecf20Sopenharmony_ci	mrb->mbox->handle = index;
4938c2ecf20Sopenharmony_ci	mrb->mbox->hdr.entryType = ET_MBOX_CMD;
4948c2ecf20Sopenharmony_ci	mrb->mbox->hdr.entryCount = mrb->iocb_cnt;
4958c2ecf20Sopenharmony_ci	memcpy(mrb->mbox->in_mbox, in_mbox, 32);
4968c2ecf20Sopenharmony_ci	mrb->mbox_cmd = in_mbox[0];
4978c2ecf20Sopenharmony_ci	wmb();
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ha->iocb_cnt += mrb->iocb_cnt;
5008c2ecf20Sopenharmony_ci	ha->isp_ops->queue_iocb(ha);
5018c2ecf20Sopenharmony_ciexit_mbox_iocb:
5028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
5038c2ecf20Sopenharmony_ci	return rval;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ciint qla4xxx_ping_iocb(struct scsi_qla_host *ha, uint32_t options,
5078c2ecf20Sopenharmony_ci		      uint32_t payload_size, uint32_t pid, uint8_t *ipaddr)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	uint32_t in_mbox[8];
5108c2ecf20Sopenharmony_ci	struct mrb *mrb = NULL;
5118c2ecf20Sopenharmony_ci	int rval = QLA_SUCCESS;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	memset(in_mbox, 0, sizeof(in_mbox));
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	mrb = qla4xxx_get_new_mrb(ha);
5168c2ecf20Sopenharmony_ci	if (!mrb) {
5178c2ecf20Sopenharmony_ci		DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: fail to get new mrb\n",
5188c2ecf20Sopenharmony_ci				  __func__));
5198c2ecf20Sopenharmony_ci		rval = QLA_ERROR;
5208c2ecf20Sopenharmony_ci		goto exit_ping;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	in_mbox[0] = MBOX_CMD_PING;
5248c2ecf20Sopenharmony_ci	in_mbox[1] = options;
5258c2ecf20Sopenharmony_ci	memcpy(&in_mbox[2], &ipaddr[0], 4);
5268c2ecf20Sopenharmony_ci	memcpy(&in_mbox[3], &ipaddr[4], 4);
5278c2ecf20Sopenharmony_ci	memcpy(&in_mbox[4], &ipaddr[8], 4);
5288c2ecf20Sopenharmony_ci	memcpy(&in_mbox[5], &ipaddr[12], 4);
5298c2ecf20Sopenharmony_ci	in_mbox[6] = payload_size;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	mrb->pid = pid;
5328c2ecf20Sopenharmony_ci	rval = qla4xxx_send_mbox_iocb(ha, mrb, in_mbox);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (rval != QLA_SUCCESS)
5358c2ecf20Sopenharmony_ci		goto exit_ping;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	return rval;
5388c2ecf20Sopenharmony_ciexit_ping:
5398c2ecf20Sopenharmony_ci	kfree(mrb);
5408c2ecf20Sopenharmony_ci	return rval;
5418c2ecf20Sopenharmony_ci}
542