18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/drivers/scsi/esas2r/esas2r_io.c
38c2ecf20Sopenharmony_ci *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2001-2013 ATTO Technology, Inc.
68c2ecf20Sopenharmony_ci *  (mailto:linuxdrivers@attotech.com)mpt3sas/mpt3sas_trigger_diag.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
98c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License
108c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 2
118c2ecf20Sopenharmony_ci * of the License, or (at your option) any later version.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168c2ecf20Sopenharmony_ci * GNU General Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * NO WARRANTY
198c2ecf20Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
208c2ecf20Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
218c2ecf20Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
228c2ecf20Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
238c2ecf20Sopenharmony_ci * solely responsible for determining the appropriateness of using and
248c2ecf20Sopenharmony_ci * distributing the Program and assumes all risks associated with its
258c2ecf20Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to
268c2ecf20Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data,
278c2ecf20Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * DISCLAIMER OF LIABILITY
308c2ecf20Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
318c2ecf20Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
328c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
338c2ecf20Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
348c2ecf20Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
358c2ecf20Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
368c2ecf20Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
398c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
408c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
418c2ecf20Sopenharmony_ci * USA.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include "esas2r.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct esas2r_target *t = NULL;
498c2ecf20Sopenharmony_ci	struct esas2r_request *startrq = rq;
508c2ecf20Sopenharmony_ci	unsigned long flags;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (unlikely(test_bit(AF_DEGRADED_MODE, &a->flags) ||
538c2ecf20Sopenharmony_ci		     test_bit(AF_POWER_DOWN, &a->flags))) {
548c2ecf20Sopenharmony_ci		if (rq->vrq->scsi.function == VDA_FUNC_SCSI)
558c2ecf20Sopenharmony_ci			rq->req_stat = RS_SEL2;
568c2ecf20Sopenharmony_ci		else
578c2ecf20Sopenharmony_ci			rq->req_stat = RS_DEGRADED;
588c2ecf20Sopenharmony_ci	} else if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
598c2ecf20Sopenharmony_ci		t = a->targetdb + rq->target_id;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		if (unlikely(t >= a->targetdb_end
628c2ecf20Sopenharmony_ci			     || !(t->flags & TF_USED))) {
638c2ecf20Sopenharmony_ci			rq->req_stat = RS_SEL;
648c2ecf20Sopenharmony_ci		} else {
658c2ecf20Sopenharmony_ci			/* copy in the target ID. */
668c2ecf20Sopenharmony_ci			rq->vrq->scsi.target_id = cpu_to_le16(t->virt_targ_id);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci			/*
698c2ecf20Sopenharmony_ci			 * Test if we want to report RS_SEL for missing target.
708c2ecf20Sopenharmony_ci			 * Note that if AF_DISC_PENDING is set than this will
718c2ecf20Sopenharmony_ci			 * go on the defer queue.
728c2ecf20Sopenharmony_ci			 */
738c2ecf20Sopenharmony_ci			if (unlikely(t->target_state != TS_PRESENT &&
748c2ecf20Sopenharmony_ci				     !test_bit(AF_DISC_PENDING, &a->flags)))
758c2ecf20Sopenharmony_ci				rq->req_stat = RS_SEL;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (unlikely(rq->req_stat != RS_PENDING)) {
808c2ecf20Sopenharmony_ci		esas2r_complete_request(a, rq);
818c2ecf20Sopenharmony_ci		return;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	esas2r_trace("rq=%p", rq);
858c2ecf20Sopenharmony_ci	esas2r_trace("rq->vrq->scsi.handle=%x", rq->vrq->scsi.handle);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
888c2ecf20Sopenharmony_ci		esas2r_trace("rq->target_id=%d", rq->target_id);
898c2ecf20Sopenharmony_ci		esas2r_trace("rq->vrq->scsi.flags=%x", rq->vrq->scsi.flags);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (likely(list_empty(&a->defer_list) &&
958c2ecf20Sopenharmony_ci		   !test_bit(AF_CHPRST_PENDING, &a->flags) &&
968c2ecf20Sopenharmony_ci		   !test_bit(AF_FLASHING, &a->flags) &&
978c2ecf20Sopenharmony_ci		   !test_bit(AF_DISC_PENDING, &a->flags)))
988c2ecf20Sopenharmony_ci		esas2r_local_start_request(a, startrq);
998c2ecf20Sopenharmony_ci	else
1008c2ecf20Sopenharmony_ci		list_add_tail(&startrq->req_list, &a->defer_list);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * Starts the specified request.  all requests have RS_PENDING set when this
1078c2ecf20Sopenharmony_ci * routine is called.  The caller is usually esas2r_start_request, but
1088c2ecf20Sopenharmony_ci * esas2r_do_deferred_processes will start request that are deferred.
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * The caller must ensure that requests can be started.
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * esas2r_start_request will defer a request if there are already requests
1138c2ecf20Sopenharmony_ci * waiting or there is a chip reset pending.  once the reset condition clears,
1148c2ecf20Sopenharmony_ci * esas2r_do_deferred_processes will call this function to start the request.
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci * When a request is started, it is placed on the active list and queued to
1178c2ecf20Sopenharmony_ci * the controller.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_civoid esas2r_local_start_request(struct esas2r_adapter *a,
1208c2ecf20Sopenharmony_ci				struct esas2r_request *rq)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	esas2r_trace_enter();
1238c2ecf20Sopenharmony_ci	esas2r_trace("rq=%p", rq);
1248c2ecf20Sopenharmony_ci	esas2r_trace("rq->vrq:%p", rq->vrq);
1258c2ecf20Sopenharmony_ci	esas2r_trace("rq->vrq_md->phys_addr:%x", rq->vrq_md->phys_addr);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (unlikely(rq->vrq->scsi.function == VDA_FUNC_FLASH
1288c2ecf20Sopenharmony_ci		     && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT))
1298c2ecf20Sopenharmony_ci		set_bit(AF_FLASHING, &a->flags);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	list_add_tail(&rq->req_list, &a->active_list);
1328c2ecf20Sopenharmony_ci	esas2r_start_vda_request(a, rq);
1338c2ecf20Sopenharmony_ci	esas2r_trace_exit();
1348c2ecf20Sopenharmony_ci	return;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_civoid esas2r_start_vda_request(struct esas2r_adapter *a,
1388c2ecf20Sopenharmony_ci			      struct esas2r_request *rq)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct esas2r_inbound_list_source_entry *element;
1418c2ecf20Sopenharmony_ci	u32 dw;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	rq->req_stat = RS_STARTED;
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci	 * Calculate the inbound list entry location and the current state of
1468c2ecf20Sopenharmony_ci	 * toggle bit.
1478c2ecf20Sopenharmony_ci	 */
1488c2ecf20Sopenharmony_ci	a->last_write++;
1498c2ecf20Sopenharmony_ci	if (a->last_write >= a->list_size) {
1508c2ecf20Sopenharmony_ci		a->last_write = 0;
1518c2ecf20Sopenharmony_ci		/* update the toggle bit */
1528c2ecf20Sopenharmony_ci		if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
1538c2ecf20Sopenharmony_ci			clear_bit(AF_COMM_LIST_TOGGLE, &a->flags);
1548c2ecf20Sopenharmony_ci		else
1558c2ecf20Sopenharmony_ci			set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	element =
1598c2ecf20Sopenharmony_ci		(struct esas2r_inbound_list_source_entry *)a->inbound_list_md.
1608c2ecf20Sopenharmony_ci		virt_addr
1618c2ecf20Sopenharmony_ci		+ a->last_write;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Set the VDA request size if it was never modified */
1648c2ecf20Sopenharmony_ci	if (rq->vda_req_sz == RQ_SIZE_DEFAULT)
1658c2ecf20Sopenharmony_ci		rq->vda_req_sz = (u16)(a->max_vdareq_size / sizeof(u32));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	element->address = cpu_to_le64(rq->vrq_md->phys_addr);
1688c2ecf20Sopenharmony_ci	element->length = cpu_to_le32(rq->vda_req_sz);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Update the write pointer */
1718c2ecf20Sopenharmony_ci	dw = a->last_write;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
1748c2ecf20Sopenharmony_ci		dw |= MU_ILW_TOGGLE;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	esas2r_trace("rq->vrq->scsi.handle:%x", rq->vrq->scsi.handle);
1778c2ecf20Sopenharmony_ci	esas2r_trace("dw:%x", dw);
1788c2ecf20Sopenharmony_ci	esas2r_trace("rq->vda_req_sz:%x", rq->vda_req_sz);
1798c2ecf20Sopenharmony_ci	esas2r_write_register_dword(a, MU_IN_LIST_WRITE, dw);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/*
1838c2ecf20Sopenharmony_ci * Build the scatter/gather list for an I/O request according to the
1848c2ecf20Sopenharmony_ci * specifications placed in the s/g context.  The caller must initialize
1858c2ecf20Sopenharmony_ci * context prior to the initial call by calling esas2r_sgc_init().
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cibool esas2r_build_sg_list_sge(struct esas2r_adapter *a,
1888c2ecf20Sopenharmony_ci			      struct esas2r_sg_context *sgc)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
1918c2ecf20Sopenharmony_ci	union atto_vda_req *vrq = rq->vrq;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	while (sgc->length) {
1948c2ecf20Sopenharmony_ci		u32 rem = 0;
1958c2ecf20Sopenharmony_ci		u64 addr;
1968c2ecf20Sopenharmony_ci		u32 len;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		len = (*sgc->get_phys_addr)(sgc, &addr);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (unlikely(len == 0))
2018c2ecf20Sopenharmony_ci			return false;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		/* if current length is more than what's left, stop there */
2048c2ecf20Sopenharmony_ci		if (unlikely(len > sgc->length))
2058c2ecf20Sopenharmony_ci			len = sgc->length;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cianother_entry:
2088c2ecf20Sopenharmony_ci		/* limit to a round number less than the maximum length */
2098c2ecf20Sopenharmony_ci		if (len > SGE_LEN_MAX) {
2108c2ecf20Sopenharmony_ci			/*
2118c2ecf20Sopenharmony_ci			 * Save the remainder of the split.  Whenever we limit
2128c2ecf20Sopenharmony_ci			 * an entry we come back around to build entries out
2138c2ecf20Sopenharmony_ci			 * of the leftover.  We do this to prevent multiple
2148c2ecf20Sopenharmony_ci			 * calls to the get_phys_addr() function for an SGE
2158c2ecf20Sopenharmony_ci			 * that is too large.
2168c2ecf20Sopenharmony_ci			 */
2178c2ecf20Sopenharmony_ci			rem = len - SGE_LEN_MAX;
2188c2ecf20Sopenharmony_ci			len = SGE_LEN_MAX;
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		/* See if we need to allocate a new SGL */
2228c2ecf20Sopenharmony_ci		if (unlikely(sgc->sge.a64.curr > sgc->sge.a64.limit)) {
2238c2ecf20Sopenharmony_ci			u8 sgelen;
2248c2ecf20Sopenharmony_ci			struct esas2r_mem_desc *sgl;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci			/*
2278c2ecf20Sopenharmony_ci			 * If no SGls are available, return failure.  The
2288c2ecf20Sopenharmony_ci			 * caller can call us later with the current context
2298c2ecf20Sopenharmony_ci			 * to pick up here.
2308c2ecf20Sopenharmony_ci			 */
2318c2ecf20Sopenharmony_ci			sgl = esas2r_alloc_sgl(a);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci			if (unlikely(sgl == NULL))
2348c2ecf20Sopenharmony_ci				return false;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			/* Calculate the length of the last SGE filled in */
2378c2ecf20Sopenharmony_ci			sgelen = (u8)((u8 *)sgc->sge.a64.curr
2388c2ecf20Sopenharmony_ci				      - (u8 *)sgc->sge.a64.last);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci			/*
2418c2ecf20Sopenharmony_ci			 * Copy the last SGE filled in to the first entry of
2428c2ecf20Sopenharmony_ci			 * the new SGL to make room for the chain entry.
2438c2ecf20Sopenharmony_ci			 */
2448c2ecf20Sopenharmony_ci			memcpy(sgl->virt_addr, sgc->sge.a64.last, sgelen);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			/* Figure out the new curr pointer in the new segment */
2478c2ecf20Sopenharmony_ci			sgc->sge.a64.curr =
2488c2ecf20Sopenharmony_ci				(struct atto_vda_sge *)((u8 *)sgl->virt_addr +
2498c2ecf20Sopenharmony_ci							sgelen);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci			/* Set the limit pointer and build the chain entry */
2528c2ecf20Sopenharmony_ci			sgc->sge.a64.limit =
2538c2ecf20Sopenharmony_ci				(struct atto_vda_sge *)((u8 *)sgl->virt_addr
2548c2ecf20Sopenharmony_ci							+ sgl_page_size
2558c2ecf20Sopenharmony_ci							- sizeof(struct
2568c2ecf20Sopenharmony_ci								 atto_vda_sge));
2578c2ecf20Sopenharmony_ci			sgc->sge.a64.last->length = cpu_to_le32(
2588c2ecf20Sopenharmony_ci				SGE_CHAIN | SGE_ADDR_64);
2598c2ecf20Sopenharmony_ci			sgc->sge.a64.last->address =
2608c2ecf20Sopenharmony_ci				cpu_to_le64(sgl->phys_addr);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci			/*
2638c2ecf20Sopenharmony_ci			 * Now, if there was a previous chain entry, then
2648c2ecf20Sopenharmony_ci			 * update it to contain the length of this segment
2658c2ecf20Sopenharmony_ci			 * and size of this chain.  otherwise this is the
2668c2ecf20Sopenharmony_ci			 * first SGL, so set the chain_offset in the request.
2678c2ecf20Sopenharmony_ci			 */
2688c2ecf20Sopenharmony_ci			if (sgc->sge.a64.chain) {
2698c2ecf20Sopenharmony_ci				sgc->sge.a64.chain->length |=
2708c2ecf20Sopenharmony_ci					cpu_to_le32(
2718c2ecf20Sopenharmony_ci						((u8 *)(sgc->sge.a64.
2728c2ecf20Sopenharmony_ci							last + 1)
2738c2ecf20Sopenharmony_ci						 - (u8 *)rq->sg_table->
2748c2ecf20Sopenharmony_ci						 virt_addr)
2758c2ecf20Sopenharmony_ci						+ sizeof(struct atto_vda_sge) *
2768c2ecf20Sopenharmony_ci						LOBIT(SGE_CHAIN_SZ));
2778c2ecf20Sopenharmony_ci			} else {
2788c2ecf20Sopenharmony_ci				vrq->scsi.chain_offset = (u8)
2798c2ecf20Sopenharmony_ci							 ((u8 *)sgc->
2808c2ecf20Sopenharmony_ci							  sge.a64.last -
2818c2ecf20Sopenharmony_ci							  (u8 *)vrq);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci				/*
2848c2ecf20Sopenharmony_ci				 * This is the first SGL, so set the
2858c2ecf20Sopenharmony_ci				 * chain_offset and the VDA request size in
2868c2ecf20Sopenharmony_ci				 * the request.
2878c2ecf20Sopenharmony_ci				 */
2888c2ecf20Sopenharmony_ci				rq->vda_req_sz =
2898c2ecf20Sopenharmony_ci					(vrq->scsi.chain_offset +
2908c2ecf20Sopenharmony_ci					 sizeof(struct atto_vda_sge) +
2918c2ecf20Sopenharmony_ci					 3)
2928c2ecf20Sopenharmony_ci					/ sizeof(u32);
2938c2ecf20Sopenharmony_ci			}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci			/*
2968c2ecf20Sopenharmony_ci			 * Remember this so when we get a new SGL filled in we
2978c2ecf20Sopenharmony_ci			 * can update the length of this chain entry.
2988c2ecf20Sopenharmony_ci			 */
2998c2ecf20Sopenharmony_ci			sgc->sge.a64.chain = sgc->sge.a64.last;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci			/* Now link the new SGL onto the primary request. */
3028c2ecf20Sopenharmony_ci			list_add(&sgl->next_desc, &rq->sg_table_head);
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		/* Update last one filled in */
3068c2ecf20Sopenharmony_ci		sgc->sge.a64.last = sgc->sge.a64.curr;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		/* Build the new SGE and update the S/G context */
3098c2ecf20Sopenharmony_ci		sgc->sge.a64.curr->length = cpu_to_le32(SGE_ADDR_64 | len);
3108c2ecf20Sopenharmony_ci		sgc->sge.a64.curr->address = cpu_to_le32(addr);
3118c2ecf20Sopenharmony_ci		sgc->sge.a64.curr++;
3128c2ecf20Sopenharmony_ci		sgc->cur_offset += len;
3138c2ecf20Sopenharmony_ci		sgc->length -= len;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		/*
3168c2ecf20Sopenharmony_ci		 * Check if we previously split an entry.  If so we have to
3178c2ecf20Sopenharmony_ci		 * pick up where we left off.
3188c2ecf20Sopenharmony_ci		 */
3198c2ecf20Sopenharmony_ci		if (rem) {
3208c2ecf20Sopenharmony_ci			addr += len;
3218c2ecf20Sopenharmony_ci			len = rem;
3228c2ecf20Sopenharmony_ci			rem = 0;
3238c2ecf20Sopenharmony_ci			goto another_entry;
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* Mark the end of the SGL */
3288c2ecf20Sopenharmony_ci	sgc->sge.a64.last->length |= cpu_to_le32(SGE_LAST);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/*
3318c2ecf20Sopenharmony_ci	 * If there was a previous chain entry, update the length to indicate
3328c2ecf20Sopenharmony_ci	 * the length of this last segment.
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	if (sgc->sge.a64.chain) {
3358c2ecf20Sopenharmony_ci		sgc->sge.a64.chain->length |= cpu_to_le32(
3368c2ecf20Sopenharmony_ci			((u8 *)(sgc->sge.a64.curr) -
3378c2ecf20Sopenharmony_ci			 (u8 *)rq->sg_table->virt_addr));
3388c2ecf20Sopenharmony_ci	} else {
3398c2ecf20Sopenharmony_ci		u16 reqsize;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		/*
3428c2ecf20Sopenharmony_ci		 * The entire VDA request was not used so lets
3438c2ecf20Sopenharmony_ci		 * set the size of the VDA request to be DMA'd
3448c2ecf20Sopenharmony_ci		 */
3458c2ecf20Sopenharmony_ci		reqsize =
3468c2ecf20Sopenharmony_ci			((u16)((u8 *)sgc->sge.a64.last - (u8 *)vrq)
3478c2ecf20Sopenharmony_ci			 + sizeof(struct atto_vda_sge) + 3) / sizeof(u32);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		/*
3508c2ecf20Sopenharmony_ci		 * Only update the request size if it is bigger than what is
3518c2ecf20Sopenharmony_ci		 * already there.  We can come in here twice for some management
3528c2ecf20Sopenharmony_ci		 * commands.
3538c2ecf20Sopenharmony_ci		 */
3548c2ecf20Sopenharmony_ci		if (reqsize > rq->vda_req_sz)
3558c2ecf20Sopenharmony_ci			rq->vda_req_sz = reqsize;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	return true;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * Create PRD list for each I-block consumed by the command. This routine
3638c2ecf20Sopenharmony_ci * determines how much data is required from each I-block being consumed
3648c2ecf20Sopenharmony_ci * by the command. The first and last I-blocks can be partials and all of
3658c2ecf20Sopenharmony_ci * the I-blocks in between are for a full I-block of data.
3668c2ecf20Sopenharmony_ci *
3678c2ecf20Sopenharmony_ci * The interleave size is used to determine the number of bytes in the 1st
3688c2ecf20Sopenharmony_ci * I-block and the remaining I-blocks are what remeains.
3698c2ecf20Sopenharmony_ci */
3708c2ecf20Sopenharmony_cistatic bool esas2r_build_prd_iblk(struct esas2r_adapter *a,
3718c2ecf20Sopenharmony_ci				  struct esas2r_sg_context *sgc)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
3748c2ecf20Sopenharmony_ci	u64 addr;
3758c2ecf20Sopenharmony_ci	u32 len;
3768c2ecf20Sopenharmony_ci	struct esas2r_mem_desc *sgl;
3778c2ecf20Sopenharmony_ci	u32 numchain = 1;
3788c2ecf20Sopenharmony_ci	u32 rem = 0;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	while (sgc->length) {
3818c2ecf20Sopenharmony_ci		/* Get the next address/length pair */
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		len = (*sgc->get_phys_addr)(sgc, &addr);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		if (unlikely(len == 0))
3868c2ecf20Sopenharmony_ci			return false;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		/* If current length is more than what's left, stop there */
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		if (unlikely(len > sgc->length))
3918c2ecf20Sopenharmony_ci			len = sgc->length;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cianother_entry:
3948c2ecf20Sopenharmony_ci		/* Limit to a round number less than the maximum length */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (len > PRD_LEN_MAX) {
3978c2ecf20Sopenharmony_ci			/*
3988c2ecf20Sopenharmony_ci			 * Save the remainder of the split.  whenever we limit
3998c2ecf20Sopenharmony_ci			 * an entry we come back around to build entries out
4008c2ecf20Sopenharmony_ci			 * of the leftover.  We do this to prevent multiple
4018c2ecf20Sopenharmony_ci			 * calls to the get_phys_addr() function for an SGE
4028c2ecf20Sopenharmony_ci			 * that is too large.
4038c2ecf20Sopenharmony_ci			 */
4048c2ecf20Sopenharmony_ci			rem = len - PRD_LEN_MAX;
4058c2ecf20Sopenharmony_ci			len = PRD_LEN_MAX;
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		/* See if we need to allocate a new SGL */
4098c2ecf20Sopenharmony_ci		if (sgc->sge.prd.sge_cnt == 0) {
4108c2ecf20Sopenharmony_ci			if (len == sgc->length) {
4118c2ecf20Sopenharmony_ci				/*
4128c2ecf20Sopenharmony_ci				 * We only have 1 PRD entry left.
4138c2ecf20Sopenharmony_ci				 * It can be placed where the chain
4148c2ecf20Sopenharmony_ci				 * entry would have gone
4158c2ecf20Sopenharmony_ci				 */
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci				/* Build the simple SGE */
4188c2ecf20Sopenharmony_ci				sgc->sge.prd.curr->ctl_len = cpu_to_le32(
4198c2ecf20Sopenharmony_ci					PRD_DATA | len);
4208c2ecf20Sopenharmony_ci				sgc->sge.prd.curr->address = cpu_to_le64(addr);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci				/* Adjust length related fields */
4238c2ecf20Sopenharmony_ci				sgc->cur_offset += len;
4248c2ecf20Sopenharmony_ci				sgc->length -= len;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci				/* We use the reserved chain entry for data */
4278c2ecf20Sopenharmony_ci				numchain = 0;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci				break;
4308c2ecf20Sopenharmony_ci			}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci			if (sgc->sge.prd.chain) {
4338c2ecf20Sopenharmony_ci				/*
4348c2ecf20Sopenharmony_ci				 * Fill # of entries of current SGL in previous
4358c2ecf20Sopenharmony_ci				 * chain the length of this current SGL may not
4368c2ecf20Sopenharmony_ci				 * full.
4378c2ecf20Sopenharmony_ci				 */
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci				sgc->sge.prd.chain->ctl_len |= cpu_to_le32(
4408c2ecf20Sopenharmony_ci					sgc->sge.prd.sgl_max_cnt);
4418c2ecf20Sopenharmony_ci			}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci			/*
4448c2ecf20Sopenharmony_ci			 * If no SGls are available, return failure.  The
4458c2ecf20Sopenharmony_ci			 * caller can call us later with the current context
4468c2ecf20Sopenharmony_ci			 * to pick up here.
4478c2ecf20Sopenharmony_ci			 */
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci			sgl = esas2r_alloc_sgl(a);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci			if (unlikely(sgl == NULL))
4528c2ecf20Sopenharmony_ci				return false;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci			/*
4558c2ecf20Sopenharmony_ci			 * Link the new SGL onto the chain
4568c2ecf20Sopenharmony_ci			 * They are in reverse order
4578c2ecf20Sopenharmony_ci			 */
4588c2ecf20Sopenharmony_ci			list_add(&sgl->next_desc, &rq->sg_table_head);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci			/*
4618c2ecf20Sopenharmony_ci			 * An SGL was just filled in and we are starting
4628c2ecf20Sopenharmony_ci			 * a new SGL. Prime the chain of the ending SGL with
4638c2ecf20Sopenharmony_ci			 * info that points to the new SGL. The length gets
4648c2ecf20Sopenharmony_ci			 * filled in when the new SGL is filled or ended
4658c2ecf20Sopenharmony_ci			 */
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci			sgc->sge.prd.chain = sgc->sge.prd.curr;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci			sgc->sge.prd.chain->ctl_len = cpu_to_le32(PRD_CHAIN);
4708c2ecf20Sopenharmony_ci			sgc->sge.prd.chain->address =
4718c2ecf20Sopenharmony_ci				cpu_to_le64(sgl->phys_addr);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci			/*
4748c2ecf20Sopenharmony_ci			 * Start a new segment.
4758c2ecf20Sopenharmony_ci			 * Take one away and save for chain SGE
4768c2ecf20Sopenharmony_ci			 */
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci			sgc->sge.prd.curr =
4798c2ecf20Sopenharmony_ci				(struct atto_physical_region_description *)sgl
4808c2ecf20Sopenharmony_ci				->
4818c2ecf20Sopenharmony_ci				virt_addr;
4828c2ecf20Sopenharmony_ci			sgc->sge.prd.sge_cnt = sgc->sge.prd.sgl_max_cnt - 1;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		sgc->sge.prd.sge_cnt--;
4868c2ecf20Sopenharmony_ci		/* Build the simple SGE */
4878c2ecf20Sopenharmony_ci		sgc->sge.prd.curr->ctl_len = cpu_to_le32(PRD_DATA | len);
4888c2ecf20Sopenharmony_ci		sgc->sge.prd.curr->address = cpu_to_le64(addr);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* Used another element.  Point to the next one */
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		sgc->sge.prd.curr++;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		/* Adjust length related fields */
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		sgc->cur_offset += len;
4978c2ecf20Sopenharmony_ci		sgc->length -= len;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		/*
5008c2ecf20Sopenharmony_ci		 * Check if we previously split an entry.  If so we have to
5018c2ecf20Sopenharmony_ci		 * pick up where we left off.
5028c2ecf20Sopenharmony_ci		 */
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		if (rem) {
5058c2ecf20Sopenharmony_ci			addr += len;
5068c2ecf20Sopenharmony_ci			len = rem;
5078c2ecf20Sopenharmony_ci			rem = 0;
5088c2ecf20Sopenharmony_ci			goto another_entry;
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (!list_empty(&rq->sg_table_head)) {
5138c2ecf20Sopenharmony_ci		if (sgc->sge.prd.chain) {
5148c2ecf20Sopenharmony_ci			sgc->sge.prd.chain->ctl_len |=
5158c2ecf20Sopenharmony_ci				cpu_to_le32(sgc->sge.prd.sgl_max_cnt
5168c2ecf20Sopenharmony_ci					    - sgc->sge.prd.sge_cnt
5178c2ecf20Sopenharmony_ci					    - numchain);
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return true;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cibool esas2r_build_sg_list_prd(struct esas2r_adapter *a,
5258c2ecf20Sopenharmony_ci			      struct esas2r_sg_context *sgc)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
5288c2ecf20Sopenharmony_ci	u32 len = sgc->length;
5298c2ecf20Sopenharmony_ci	struct esas2r_target *t = a->targetdb + rq->target_id;
5308c2ecf20Sopenharmony_ci	u8 is_i_o = 0;
5318c2ecf20Sopenharmony_ci	u16 reqsize;
5328c2ecf20Sopenharmony_ci	struct atto_physical_region_description *curr_iblk_chn;
5338c2ecf20Sopenharmony_ci	u8 *cdb = (u8 *)&rq->vrq->scsi.cdb[0];
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/*
5368c2ecf20Sopenharmony_ci	 * extract LBA from command so we can determine
5378c2ecf20Sopenharmony_ci	 * the I-Block boundary
5388c2ecf20Sopenharmony_ci	 */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (rq->vrq->scsi.function == VDA_FUNC_SCSI
5418c2ecf20Sopenharmony_ci	    && t->target_state == TS_PRESENT
5428c2ecf20Sopenharmony_ci	    && !(t->flags & TF_PASS_THRU)) {
5438c2ecf20Sopenharmony_ci		u32 lbalo = 0;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		switch (rq->vrq->scsi.cdb[0]) {
5468c2ecf20Sopenharmony_ci		case    READ_16:
5478c2ecf20Sopenharmony_ci		case    WRITE_16:
5488c2ecf20Sopenharmony_ci		{
5498c2ecf20Sopenharmony_ci			lbalo =
5508c2ecf20Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[9],
5518c2ecf20Sopenharmony_ci						   cdb[8]),
5528c2ecf20Sopenharmony_ci					  MAKEWORD(cdb[7],
5538c2ecf20Sopenharmony_ci						   cdb[6]));
5548c2ecf20Sopenharmony_ci			is_i_o = 1;
5558c2ecf20Sopenharmony_ci			break;
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		case    READ_12:
5598c2ecf20Sopenharmony_ci		case    WRITE_12:
5608c2ecf20Sopenharmony_ci		case    READ_10:
5618c2ecf20Sopenharmony_ci		case    WRITE_10:
5628c2ecf20Sopenharmony_ci		{
5638c2ecf20Sopenharmony_ci			lbalo =
5648c2ecf20Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[5],
5658c2ecf20Sopenharmony_ci						   cdb[4]),
5668c2ecf20Sopenharmony_ci					  MAKEWORD(cdb[3],
5678c2ecf20Sopenharmony_ci						   cdb[2]));
5688c2ecf20Sopenharmony_ci			is_i_o = 1;
5698c2ecf20Sopenharmony_ci			break;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		case    READ_6:
5738c2ecf20Sopenharmony_ci		case    WRITE_6:
5748c2ecf20Sopenharmony_ci		{
5758c2ecf20Sopenharmony_ci			lbalo =
5768c2ecf20Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[3],
5778c2ecf20Sopenharmony_ci						   cdb[2]),
5788c2ecf20Sopenharmony_ci					  MAKEWORD(cdb[1] & 0x1F,
5798c2ecf20Sopenharmony_ci						   0));
5808c2ecf20Sopenharmony_ci			is_i_o = 1;
5818c2ecf20Sopenharmony_ci			break;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		default:
5858c2ecf20Sopenharmony_ci			break;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		if (is_i_o) {
5898c2ecf20Sopenharmony_ci			u32 startlba;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci			rq->vrq->scsi.iblk_cnt_prd = 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci			/* Determine size of 1st I-block PRD list       */
5948c2ecf20Sopenharmony_ci			startlba = t->inter_block - (lbalo & (t->inter_block -
5958c2ecf20Sopenharmony_ci							      1));
5968c2ecf20Sopenharmony_ci			sgc->length = startlba * t->block_size;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci			/* Chk if the 1st iblk chain starts at base of Iblock */
5998c2ecf20Sopenharmony_ci			if ((lbalo & (t->inter_block - 1)) == 0)
6008c2ecf20Sopenharmony_ci				rq->flags |= RF_1ST_IBLK_BASE;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci			if (sgc->length > len)
6038c2ecf20Sopenharmony_ci				sgc->length = len;
6048c2ecf20Sopenharmony_ci		} else {
6058c2ecf20Sopenharmony_ci			sgc->length = len;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci	} else {
6088c2ecf20Sopenharmony_ci		sgc->length = len;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* get our starting chain address   */
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	curr_iblk_chn =
6148c2ecf20Sopenharmony_ci		(struct atto_physical_region_description *)sgc->sge.a64.curr;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	sgc->sge.prd.sgl_max_cnt = sgl_page_size /
6178c2ecf20Sopenharmony_ci				   sizeof(struct
6188c2ecf20Sopenharmony_ci					  atto_physical_region_description);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* create all of the I-block PRD lists          */
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	while (len) {
6238c2ecf20Sopenharmony_ci		sgc->sge.prd.sge_cnt = 0;
6248c2ecf20Sopenharmony_ci		sgc->sge.prd.chain = NULL;
6258c2ecf20Sopenharmony_ci		sgc->sge.prd.curr = curr_iblk_chn;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		/* increment to next I-Block    */
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		len -= sgc->length;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		/* go build the next I-Block PRD list   */
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		if (unlikely(!esas2r_build_prd_iblk(a, sgc)))
6348c2ecf20Sopenharmony_ci			return false;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		curr_iblk_chn++;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		if (is_i_o) {
6398c2ecf20Sopenharmony_ci			rq->vrq->scsi.iblk_cnt_prd++;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci			if (len > t->inter_byte)
6428c2ecf20Sopenharmony_ci				sgc->length = t->inter_byte;
6438c2ecf20Sopenharmony_ci			else
6448c2ecf20Sopenharmony_ci				sgc->length = len;
6458c2ecf20Sopenharmony_ci		}
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* figure out the size used of the VDA request */
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	reqsize = ((u16)((u8 *)curr_iblk_chn - (u8 *)rq->vrq))
6518c2ecf20Sopenharmony_ci		  / sizeof(u32);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * only update the request size if it is bigger than what is
6558c2ecf20Sopenharmony_ci	 * already there.  we can come in here twice for some management
6568c2ecf20Sopenharmony_ci	 * commands.
6578c2ecf20Sopenharmony_ci	 */
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (reqsize > rq->vda_req_sz)
6608c2ecf20Sopenharmony_ci		rq->vda_req_sz = reqsize;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return true;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic void esas2r_handle_pending_reset(struct esas2r_adapter *a, u32 currtime)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	u32 delta = currtime - a->chip_init_time;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (delta <= ESAS2R_CHPRST_WAIT_TIME) {
6708c2ecf20Sopenharmony_ci		/* Wait before accessing registers */
6718c2ecf20Sopenharmony_ci	} else if (delta >= ESAS2R_CHPRST_TIME) {
6728c2ecf20Sopenharmony_ci		/*
6738c2ecf20Sopenharmony_ci		 * The last reset failed so try again. Reset
6748c2ecf20Sopenharmony_ci		 * processing will give up after three tries.
6758c2ecf20Sopenharmony_ci		 */
6768c2ecf20Sopenharmony_ci		esas2r_local_reset_adapter(a);
6778c2ecf20Sopenharmony_ci	} else {
6788c2ecf20Sopenharmony_ci		/* We can now see if the firmware is ready */
6798c2ecf20Sopenharmony_ci		u32 doorbell;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
6828c2ecf20Sopenharmony_ci		if (doorbell == 0xFFFFFFFF || !(doorbell & DRBL_FORCE_INT)) {
6838c2ecf20Sopenharmony_ci			esas2r_force_interrupt(a);
6848c2ecf20Sopenharmony_ci		} else {
6858c2ecf20Sopenharmony_ci			u32 ver = (doorbell & DRBL_FW_VER_MSK);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci			/* Driver supports API version 0 and 1 */
6888c2ecf20Sopenharmony_ci			esas2r_write_register_dword(a, MU_DOORBELL_OUT,
6898c2ecf20Sopenharmony_ci						    doorbell);
6908c2ecf20Sopenharmony_ci			if (ver == DRBL_FW_VER_0) {
6918c2ecf20Sopenharmony_ci				set_bit(AF_CHPRST_DETECTED, &a->flags);
6928c2ecf20Sopenharmony_ci				set_bit(AF_LEGACY_SGE_MODE, &a->flags);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci				a->max_vdareq_size = 128;
6958c2ecf20Sopenharmony_ci				a->build_sgl = esas2r_build_sg_list_sge;
6968c2ecf20Sopenharmony_ci			} else if (ver == DRBL_FW_VER_1) {
6978c2ecf20Sopenharmony_ci				set_bit(AF_CHPRST_DETECTED, &a->flags);
6988c2ecf20Sopenharmony_ci				clear_bit(AF_LEGACY_SGE_MODE, &a->flags);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci				a->max_vdareq_size = 1024;
7018c2ecf20Sopenharmony_ci				a->build_sgl = esas2r_build_sg_list_prd;
7028c2ecf20Sopenharmony_ci			} else {
7038c2ecf20Sopenharmony_ci				esas2r_local_reset_adapter(a);
7048c2ecf20Sopenharmony_ci			}
7058c2ecf20Sopenharmony_ci		}
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci/* This function must be called once per timer tick */
7118c2ecf20Sopenharmony_civoid esas2r_timer_tick(struct esas2r_adapter *a)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	u32 currtime = jiffies_to_msecs(jiffies);
7148c2ecf20Sopenharmony_ci	u32 deltatime = currtime - a->last_tick_time;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	a->last_tick_time = currtime;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* count down the uptime */
7198c2ecf20Sopenharmony_ci	if (a->chip_uptime &&
7208c2ecf20Sopenharmony_ci	    !test_bit(AF_CHPRST_PENDING, &a->flags) &&
7218c2ecf20Sopenharmony_ci	    !test_bit(AF_DISC_PENDING, &a->flags)) {
7228c2ecf20Sopenharmony_ci		if (deltatime >= a->chip_uptime)
7238c2ecf20Sopenharmony_ci			a->chip_uptime = 0;
7248c2ecf20Sopenharmony_ci		else
7258c2ecf20Sopenharmony_ci			a->chip_uptime -= deltatime;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (test_bit(AF_CHPRST_PENDING, &a->flags)) {
7298c2ecf20Sopenharmony_ci		if (!test_bit(AF_CHPRST_NEEDED, &a->flags) &&
7308c2ecf20Sopenharmony_ci		    !test_bit(AF_CHPRST_DETECTED, &a->flags))
7318c2ecf20Sopenharmony_ci			esas2r_handle_pending_reset(a, currtime);
7328c2ecf20Sopenharmony_ci	} else {
7338c2ecf20Sopenharmony_ci		if (test_bit(AF_DISC_PENDING, &a->flags))
7348c2ecf20Sopenharmony_ci			esas2r_disc_check_complete(a);
7358c2ecf20Sopenharmony_ci		if (test_bit(AF_HEARTBEAT_ENB, &a->flags)) {
7368c2ecf20Sopenharmony_ci			if (test_bit(AF_HEARTBEAT, &a->flags)) {
7378c2ecf20Sopenharmony_ci				if ((currtime - a->heartbeat_time) >=
7388c2ecf20Sopenharmony_ci				    ESAS2R_HEARTBEAT_TIME) {
7398c2ecf20Sopenharmony_ci					clear_bit(AF_HEARTBEAT, &a->flags);
7408c2ecf20Sopenharmony_ci					esas2r_hdebug("heartbeat failed");
7418c2ecf20Sopenharmony_ci					esas2r_log(ESAS2R_LOG_CRIT,
7428c2ecf20Sopenharmony_ci						   "heartbeat failed");
7438c2ecf20Sopenharmony_ci					esas2r_bugon();
7448c2ecf20Sopenharmony_ci					esas2r_local_reset_adapter(a);
7458c2ecf20Sopenharmony_ci				}
7468c2ecf20Sopenharmony_ci			} else {
7478c2ecf20Sopenharmony_ci				set_bit(AF_HEARTBEAT, &a->flags);
7488c2ecf20Sopenharmony_ci				a->heartbeat_time = currtime;
7498c2ecf20Sopenharmony_ci				esas2r_force_interrupt(a);
7508c2ecf20Sopenharmony_ci			}
7518c2ecf20Sopenharmony_ci		}
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
7558c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci/*
7598c2ecf20Sopenharmony_ci * Send the specified task management function to the target and LUN
7608c2ecf20Sopenharmony_ci * specified in rqaux.  in addition, immediately abort any commands that
7618c2ecf20Sopenharmony_ci * are queued but not sent to the device according to the rules specified
7628c2ecf20Sopenharmony_ci * by the task management function.
7638c2ecf20Sopenharmony_ci */
7648c2ecf20Sopenharmony_cibool esas2r_send_task_mgmt(struct esas2r_adapter *a,
7658c2ecf20Sopenharmony_ci			   struct esas2r_request *rqaux, u8 task_mgt_func)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	u16 targetid = rqaux->target_id;
7688c2ecf20Sopenharmony_ci	u8 lun = (u8)le32_to_cpu(rqaux->vrq->scsi.flags);
7698c2ecf20Sopenharmony_ci	bool ret = false;
7708c2ecf20Sopenharmony_ci	struct esas2r_request *rq;
7718c2ecf20Sopenharmony_ci	struct list_head *next, *element;
7728c2ecf20Sopenharmony_ci	unsigned long flags;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	LIST_HEAD(comp_list);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	esas2r_trace_enter();
7778c2ecf20Sopenharmony_ci	esas2r_trace("rqaux:%p", rqaux);
7788c2ecf20Sopenharmony_ci	esas2r_trace("task_mgt_func:%x", task_mgt_func);
7798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* search the defer queue looking for requests for the device */
7828c2ecf20Sopenharmony_ci	list_for_each_safe(element, next, &a->defer_list) {
7838c2ecf20Sopenharmony_ci		rq = list_entry(element, struct esas2r_request, req_list);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		if (rq->vrq->scsi.function == VDA_FUNC_SCSI
7868c2ecf20Sopenharmony_ci		    && rq->target_id == targetid
7878c2ecf20Sopenharmony_ci		    && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
7888c2ecf20Sopenharmony_ci			|| task_mgt_func == 0x20)) { /* target reset */
7898c2ecf20Sopenharmony_ci			/* Found a request affected by the task management */
7908c2ecf20Sopenharmony_ci			if (rq->req_stat == RS_PENDING) {
7918c2ecf20Sopenharmony_ci				/*
7928c2ecf20Sopenharmony_ci				 * The request is pending or waiting.  We can
7938c2ecf20Sopenharmony_ci				 * safelycomplete the request now.
7948c2ecf20Sopenharmony_ci				 */
7958c2ecf20Sopenharmony_ci				if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
7968c2ecf20Sopenharmony_ci					list_add_tail(&rq->comp_list,
7978c2ecf20Sopenharmony_ci						      &comp_list);
7988c2ecf20Sopenharmony_ci			}
7998c2ecf20Sopenharmony_ci		}
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* Send the task management request to the firmware */
8038c2ecf20Sopenharmony_ci	rqaux->sense_len = 0;
8048c2ecf20Sopenharmony_ci	rqaux->vrq->scsi.length = 0;
8058c2ecf20Sopenharmony_ci	rqaux->target_id = targetid;
8068c2ecf20Sopenharmony_ci	rqaux->vrq->scsi.flags |= cpu_to_le32(lun);
8078c2ecf20Sopenharmony_ci	memset(rqaux->vrq->scsi.cdb, 0, sizeof(rqaux->vrq->scsi.cdb));
8088c2ecf20Sopenharmony_ci	rqaux->vrq->scsi.flags |=
8098c2ecf20Sopenharmony_ci		cpu_to_le16(task_mgt_func * LOBIT(FCP_CMND_TM_MASK));
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (test_bit(AF_FLASHING, &a->flags)) {
8128c2ecf20Sopenharmony_ci		/* Assume success.  if there are active requests, return busy */
8138c2ecf20Sopenharmony_ci		rqaux->req_stat = RS_SUCCESS;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		list_for_each_safe(element, next, &a->active_list) {
8168c2ecf20Sopenharmony_ci			rq = list_entry(element, struct esas2r_request,
8178c2ecf20Sopenharmony_ci					req_list);
8188c2ecf20Sopenharmony_ci			if (rq->vrq->scsi.function == VDA_FUNC_SCSI
8198c2ecf20Sopenharmony_ci			    && rq->target_id == targetid
8208c2ecf20Sopenharmony_ci			    && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
8218c2ecf20Sopenharmony_ci				|| task_mgt_func == 0x20))  /* target reset */
8228c2ecf20Sopenharmony_ci				rqaux->req_stat = RS_BUSY;
8238c2ecf20Sopenharmony_ci		}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		ret = true;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!test_bit(AF_FLASHING, &a->flags))
8318c2ecf20Sopenharmony_ci		esas2r_start_request(a, rqaux);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	esas2r_comp_list_drain(a, &comp_list);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
8368c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	esas2r_trace_exit();
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return ret;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_civoid esas2r_reset_bus(struct esas2r_adapter *a)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	esas2r_log(ESAS2R_LOG_INFO, "performing a bus reset");
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
8488c2ecf20Sopenharmony_ci	    !test_bit(AF_CHPRST_PENDING, &a->flags) &&
8498c2ecf20Sopenharmony_ci	    !test_bit(AF_DISC_PENDING, &a->flags)) {
8508c2ecf20Sopenharmony_ci		set_bit(AF_BUSRST_NEEDED, &a->flags);
8518c2ecf20Sopenharmony_ci		set_bit(AF_BUSRST_PENDING, &a->flags);
8528c2ecf20Sopenharmony_ci		set_bit(AF_OS_RESET, &a->flags);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		esas2r_schedule_tasklet(a);
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cibool esas2r_ioreq_aborted(struct esas2r_adapter *a, struct esas2r_request *rq,
8598c2ecf20Sopenharmony_ci			  u8 status)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	esas2r_trace_enter();
8628c2ecf20Sopenharmony_ci	esas2r_trace("rq:%p", rq);
8638c2ecf20Sopenharmony_ci	list_del_init(&rq->req_list);
8648c2ecf20Sopenharmony_ci	if (rq->timeout > RQ_MAX_TIMEOUT) {
8658c2ecf20Sopenharmony_ci		/*
8668c2ecf20Sopenharmony_ci		 * The request timed out, but we could not abort it because a
8678c2ecf20Sopenharmony_ci		 * chip reset occurred.  Return busy status.
8688c2ecf20Sopenharmony_ci		 */
8698c2ecf20Sopenharmony_ci		rq->req_stat = RS_BUSY;
8708c2ecf20Sopenharmony_ci		esas2r_trace_exit();
8718c2ecf20Sopenharmony_ci		return true;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	rq->req_stat = status;
8758c2ecf20Sopenharmony_ci	esas2r_trace_exit();
8768c2ecf20Sopenharmony_ci	return true;
8778c2ecf20Sopenharmony_ci}
878