162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/scsi/esas2r/esas2r_io.c
362306a36Sopenharmony_ci *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2001-2013 ATTO Technology, Inc.
662306a36Sopenharmony_ci *  (mailto:linuxdrivers@attotech.com)mpt3sas/mpt3sas_trigger_diag.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
962306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License
1062306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2
1162306a36Sopenharmony_ci * of the License, or (at your option) any later version.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful,
1462306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
1562306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1662306a36Sopenharmony_ci * GNU General Public License for more details.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * NO WARRANTY
1962306a36Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2062306a36Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2162306a36Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2262306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2362306a36Sopenharmony_ci * solely responsible for determining the appropriateness of using and
2462306a36Sopenharmony_ci * distributing the Program and assumes all risks associated with its
2562306a36Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to
2662306a36Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data,
2762306a36Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * DISCLAIMER OF LIABILITY
3062306a36Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3162306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3262306a36Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3362306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3462306a36Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3562306a36Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3662306a36Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
3962306a36Sopenharmony_ci * along with this program; if not, write to the Free Software
4062306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
4162306a36Sopenharmony_ci * USA.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include "esas2r.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_civoid esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct esas2r_target *t = NULL;
4962306a36Sopenharmony_ci	struct esas2r_request *startrq = rq;
5062306a36Sopenharmony_ci	unsigned long flags;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (unlikely(test_bit(AF_DEGRADED_MODE, &a->flags) ||
5362306a36Sopenharmony_ci		     test_bit(AF_POWER_DOWN, &a->flags))) {
5462306a36Sopenharmony_ci		if (rq->vrq->scsi.function == VDA_FUNC_SCSI)
5562306a36Sopenharmony_ci			rq->req_stat = RS_SEL2;
5662306a36Sopenharmony_ci		else
5762306a36Sopenharmony_ci			rq->req_stat = RS_DEGRADED;
5862306a36Sopenharmony_ci	} else if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
5962306a36Sopenharmony_ci		t = a->targetdb + rq->target_id;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		if (unlikely(t >= a->targetdb_end
6262306a36Sopenharmony_ci			     || !(t->flags & TF_USED))) {
6362306a36Sopenharmony_ci			rq->req_stat = RS_SEL;
6462306a36Sopenharmony_ci		} else {
6562306a36Sopenharmony_ci			/* copy in the target ID. */
6662306a36Sopenharmony_ci			rq->vrq->scsi.target_id = cpu_to_le16(t->virt_targ_id);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci			/*
6962306a36Sopenharmony_ci			 * Test if we want to report RS_SEL for missing target.
7062306a36Sopenharmony_ci			 * Note that if AF_DISC_PENDING is set than this will
7162306a36Sopenharmony_ci			 * go on the defer queue.
7262306a36Sopenharmony_ci			 */
7362306a36Sopenharmony_ci			if (unlikely(t->target_state != TS_PRESENT &&
7462306a36Sopenharmony_ci				     !test_bit(AF_DISC_PENDING, &a->flags)))
7562306a36Sopenharmony_ci				rq->req_stat = RS_SEL;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (unlikely(rq->req_stat != RS_PENDING)) {
8062306a36Sopenharmony_ci		esas2r_complete_request(a, rq);
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	esas2r_trace("rq=%p", rq);
8562306a36Sopenharmony_ci	esas2r_trace("rq->vrq->scsi.handle=%x", rq->vrq->scsi.handle);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
8862306a36Sopenharmony_ci		esas2r_trace("rq->target_id=%d", rq->target_id);
8962306a36Sopenharmony_ci		esas2r_trace("rq->vrq->scsi.flags=%x", rq->vrq->scsi.flags);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (likely(list_empty(&a->defer_list) &&
9562306a36Sopenharmony_ci		   !test_bit(AF_CHPRST_PENDING, &a->flags) &&
9662306a36Sopenharmony_ci		   !test_bit(AF_FLASHING, &a->flags) &&
9762306a36Sopenharmony_ci		   !test_bit(AF_DISC_PENDING, &a->flags)))
9862306a36Sopenharmony_ci		esas2r_local_start_request(a, startrq);
9962306a36Sopenharmony_ci	else
10062306a36Sopenharmony_ci		list_add_tail(&startrq->req_list, &a->defer_list);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * Starts the specified request.  all requests have RS_PENDING set when this
10762306a36Sopenharmony_ci * routine is called.  The caller is usually esas2r_start_request, but
10862306a36Sopenharmony_ci * esas2r_do_deferred_processes will start request that are deferred.
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * The caller must ensure that requests can be started.
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * esas2r_start_request will defer a request if there are already requests
11362306a36Sopenharmony_ci * waiting or there is a chip reset pending.  once the reset condition clears,
11462306a36Sopenharmony_ci * esas2r_do_deferred_processes will call this function to start the request.
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * When a request is started, it is placed on the active list and queued to
11762306a36Sopenharmony_ci * the controller.
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_civoid esas2r_local_start_request(struct esas2r_adapter *a,
12062306a36Sopenharmony_ci				struct esas2r_request *rq)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	esas2r_trace_enter();
12362306a36Sopenharmony_ci	esas2r_trace("rq=%p", rq);
12462306a36Sopenharmony_ci	esas2r_trace("rq->vrq:%p", rq->vrq);
12562306a36Sopenharmony_ci	esas2r_trace("rq->vrq_md->phys_addr:%x", rq->vrq_md->phys_addr);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (unlikely(rq->vrq->scsi.function == VDA_FUNC_FLASH
12862306a36Sopenharmony_ci		     && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT))
12962306a36Sopenharmony_ci		set_bit(AF_FLASHING, &a->flags);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	list_add_tail(&rq->req_list, &a->active_list);
13262306a36Sopenharmony_ci	esas2r_start_vda_request(a, rq);
13362306a36Sopenharmony_ci	esas2r_trace_exit();
13462306a36Sopenharmony_ci	return;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_civoid esas2r_start_vda_request(struct esas2r_adapter *a,
13862306a36Sopenharmony_ci			      struct esas2r_request *rq)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct esas2r_inbound_list_source_entry *element;
14162306a36Sopenharmony_ci	u32 dw;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	rq->req_stat = RS_STARTED;
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * Calculate the inbound list entry location and the current state of
14662306a36Sopenharmony_ci	 * toggle bit.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	a->last_write++;
14962306a36Sopenharmony_ci	if (a->last_write >= a->list_size) {
15062306a36Sopenharmony_ci		a->last_write = 0;
15162306a36Sopenharmony_ci		/* update the toggle bit */
15262306a36Sopenharmony_ci		if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
15362306a36Sopenharmony_ci			clear_bit(AF_COMM_LIST_TOGGLE, &a->flags);
15462306a36Sopenharmony_ci		else
15562306a36Sopenharmony_ci			set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	element =
15962306a36Sopenharmony_ci		(struct esas2r_inbound_list_source_entry *)a->inbound_list_md.
16062306a36Sopenharmony_ci		virt_addr
16162306a36Sopenharmony_ci		+ a->last_write;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Set the VDA request size if it was never modified */
16462306a36Sopenharmony_ci	if (rq->vda_req_sz == RQ_SIZE_DEFAULT)
16562306a36Sopenharmony_ci		rq->vda_req_sz = (u16)(a->max_vdareq_size / sizeof(u32));
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	element->address = cpu_to_le64(rq->vrq_md->phys_addr);
16862306a36Sopenharmony_ci	element->length = cpu_to_le32(rq->vda_req_sz);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Update the write pointer */
17162306a36Sopenharmony_ci	dw = a->last_write;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
17462306a36Sopenharmony_ci		dw |= MU_ILW_TOGGLE;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	esas2r_trace("rq->vrq->scsi.handle:%x", rq->vrq->scsi.handle);
17762306a36Sopenharmony_ci	esas2r_trace("dw:%x", dw);
17862306a36Sopenharmony_ci	esas2r_trace("rq->vda_req_sz:%x", rq->vda_req_sz);
17962306a36Sopenharmony_ci	esas2r_write_register_dword(a, MU_IN_LIST_WRITE, dw);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * Build the scatter/gather list for an I/O request according to the
18462306a36Sopenharmony_ci * specifications placed in the s/g context.  The caller must initialize
18562306a36Sopenharmony_ci * context prior to the initial call by calling esas2r_sgc_init().
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_cibool esas2r_build_sg_list_sge(struct esas2r_adapter *a,
18862306a36Sopenharmony_ci			      struct esas2r_sg_context *sgc)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
19162306a36Sopenharmony_ci	union atto_vda_req *vrq = rq->vrq;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	while (sgc->length) {
19462306a36Sopenharmony_ci		u32 rem = 0;
19562306a36Sopenharmony_ci		u64 addr;
19662306a36Sopenharmony_ci		u32 len;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		len = (*sgc->get_phys_addr)(sgc, &addr);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		if (unlikely(len == 0))
20162306a36Sopenharmony_ci			return false;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		/* if current length is more than what's left, stop there */
20462306a36Sopenharmony_ci		if (unlikely(len > sgc->length))
20562306a36Sopenharmony_ci			len = sgc->length;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cianother_entry:
20862306a36Sopenharmony_ci		/* limit to a round number less than the maximum length */
20962306a36Sopenharmony_ci		if (len > SGE_LEN_MAX) {
21062306a36Sopenharmony_ci			/*
21162306a36Sopenharmony_ci			 * Save the remainder of the split.  Whenever we limit
21262306a36Sopenharmony_ci			 * an entry we come back around to build entries out
21362306a36Sopenharmony_ci			 * of the leftover.  We do this to prevent multiple
21462306a36Sopenharmony_ci			 * calls to the get_phys_addr() function for an SGE
21562306a36Sopenharmony_ci			 * that is too large.
21662306a36Sopenharmony_ci			 */
21762306a36Sopenharmony_ci			rem = len - SGE_LEN_MAX;
21862306a36Sopenharmony_ci			len = SGE_LEN_MAX;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		/* See if we need to allocate a new SGL */
22262306a36Sopenharmony_ci		if (unlikely(sgc->sge.a64.curr > sgc->sge.a64.limit)) {
22362306a36Sopenharmony_ci			u8 sgelen;
22462306a36Sopenharmony_ci			struct esas2r_mem_desc *sgl;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			/*
22762306a36Sopenharmony_ci			 * If no SGls are available, return failure.  The
22862306a36Sopenharmony_ci			 * caller can call us later with the current context
22962306a36Sopenharmony_ci			 * to pick up here.
23062306a36Sopenharmony_ci			 */
23162306a36Sopenharmony_ci			sgl = esas2r_alloc_sgl(a);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			if (unlikely(sgl == NULL))
23462306a36Sopenharmony_ci				return false;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			/* Calculate the length of the last SGE filled in */
23762306a36Sopenharmony_ci			sgelen = (u8)((u8 *)sgc->sge.a64.curr
23862306a36Sopenharmony_ci				      - (u8 *)sgc->sge.a64.last);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			/*
24162306a36Sopenharmony_ci			 * Copy the last SGE filled in to the first entry of
24262306a36Sopenharmony_ci			 * the new SGL to make room for the chain entry.
24362306a36Sopenharmony_ci			 */
24462306a36Sopenharmony_ci			memcpy(sgl->virt_addr, sgc->sge.a64.last, sgelen);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci			/* Figure out the new curr pointer in the new segment */
24762306a36Sopenharmony_ci			sgc->sge.a64.curr =
24862306a36Sopenharmony_ci				(struct atto_vda_sge *)((u8 *)sgl->virt_addr +
24962306a36Sopenharmony_ci							sgelen);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci			/* Set the limit pointer and build the chain entry */
25262306a36Sopenharmony_ci			sgc->sge.a64.limit =
25362306a36Sopenharmony_ci				(struct atto_vda_sge *)((u8 *)sgl->virt_addr
25462306a36Sopenharmony_ci							+ sgl_page_size
25562306a36Sopenharmony_ci							- sizeof(struct
25662306a36Sopenharmony_ci								 atto_vda_sge));
25762306a36Sopenharmony_ci			sgc->sge.a64.last->length = cpu_to_le32(
25862306a36Sopenharmony_ci				SGE_CHAIN | SGE_ADDR_64);
25962306a36Sopenharmony_ci			sgc->sge.a64.last->address =
26062306a36Sopenharmony_ci				cpu_to_le64(sgl->phys_addr);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			/*
26362306a36Sopenharmony_ci			 * Now, if there was a previous chain entry, then
26462306a36Sopenharmony_ci			 * update it to contain the length of this segment
26562306a36Sopenharmony_ci			 * and size of this chain.  otherwise this is the
26662306a36Sopenharmony_ci			 * first SGL, so set the chain_offset in the request.
26762306a36Sopenharmony_ci			 */
26862306a36Sopenharmony_ci			if (sgc->sge.a64.chain) {
26962306a36Sopenharmony_ci				sgc->sge.a64.chain->length |=
27062306a36Sopenharmony_ci					cpu_to_le32(
27162306a36Sopenharmony_ci						((u8 *)(sgc->sge.a64.
27262306a36Sopenharmony_ci							last + 1)
27362306a36Sopenharmony_ci						 - (u8 *)rq->sg_table->
27462306a36Sopenharmony_ci						 virt_addr)
27562306a36Sopenharmony_ci						+ sizeof(struct atto_vda_sge) *
27662306a36Sopenharmony_ci						LOBIT(SGE_CHAIN_SZ));
27762306a36Sopenharmony_ci			} else {
27862306a36Sopenharmony_ci				vrq->scsi.chain_offset = (u8)
27962306a36Sopenharmony_ci							 ((u8 *)sgc->
28062306a36Sopenharmony_ci							  sge.a64.last -
28162306a36Sopenharmony_ci							  (u8 *)vrq);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci				/*
28462306a36Sopenharmony_ci				 * This is the first SGL, so set the
28562306a36Sopenharmony_ci				 * chain_offset and the VDA request size in
28662306a36Sopenharmony_ci				 * the request.
28762306a36Sopenharmony_ci				 */
28862306a36Sopenharmony_ci				rq->vda_req_sz =
28962306a36Sopenharmony_ci					(vrq->scsi.chain_offset +
29062306a36Sopenharmony_ci					 sizeof(struct atto_vda_sge) +
29162306a36Sopenharmony_ci					 3)
29262306a36Sopenharmony_ci					/ sizeof(u32);
29362306a36Sopenharmony_ci			}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci			/*
29662306a36Sopenharmony_ci			 * Remember this so when we get a new SGL filled in we
29762306a36Sopenharmony_ci			 * can update the length of this chain entry.
29862306a36Sopenharmony_ci			 */
29962306a36Sopenharmony_ci			sgc->sge.a64.chain = sgc->sge.a64.last;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci			/* Now link the new SGL onto the primary request. */
30262306a36Sopenharmony_ci			list_add(&sgl->next_desc, &rq->sg_table_head);
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		/* Update last one filled in */
30662306a36Sopenharmony_ci		sgc->sge.a64.last = sgc->sge.a64.curr;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		/* Build the new SGE and update the S/G context */
30962306a36Sopenharmony_ci		sgc->sge.a64.curr->length = cpu_to_le32(SGE_ADDR_64 | len);
31062306a36Sopenharmony_ci		sgc->sge.a64.curr->address = cpu_to_le32(addr);
31162306a36Sopenharmony_ci		sgc->sge.a64.curr++;
31262306a36Sopenharmony_ci		sgc->cur_offset += len;
31362306a36Sopenharmony_ci		sgc->length -= len;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		/*
31662306a36Sopenharmony_ci		 * Check if we previously split an entry.  If so we have to
31762306a36Sopenharmony_ci		 * pick up where we left off.
31862306a36Sopenharmony_ci		 */
31962306a36Sopenharmony_ci		if (rem) {
32062306a36Sopenharmony_ci			addr += len;
32162306a36Sopenharmony_ci			len = rem;
32262306a36Sopenharmony_ci			rem = 0;
32362306a36Sopenharmony_ci			goto another_entry;
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Mark the end of the SGL */
32862306a36Sopenharmony_ci	sgc->sge.a64.last->length |= cpu_to_le32(SGE_LAST);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * If there was a previous chain entry, update the length to indicate
33262306a36Sopenharmony_ci	 * the length of this last segment.
33362306a36Sopenharmony_ci	 */
33462306a36Sopenharmony_ci	if (sgc->sge.a64.chain) {
33562306a36Sopenharmony_ci		sgc->sge.a64.chain->length |= cpu_to_le32(
33662306a36Sopenharmony_ci			((u8 *)(sgc->sge.a64.curr) -
33762306a36Sopenharmony_ci			 (u8 *)rq->sg_table->virt_addr));
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		u16 reqsize;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		/*
34262306a36Sopenharmony_ci		 * The entire VDA request was not used so lets
34362306a36Sopenharmony_ci		 * set the size of the VDA request to be DMA'd
34462306a36Sopenharmony_ci		 */
34562306a36Sopenharmony_ci		reqsize =
34662306a36Sopenharmony_ci			((u16)((u8 *)sgc->sge.a64.last - (u8 *)vrq)
34762306a36Sopenharmony_ci			 + sizeof(struct atto_vda_sge) + 3) / sizeof(u32);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		/*
35062306a36Sopenharmony_ci		 * Only update the request size if it is bigger than what is
35162306a36Sopenharmony_ci		 * already there.  We can come in here twice for some management
35262306a36Sopenharmony_ci		 * commands.
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci		if (reqsize > rq->vda_req_sz)
35562306a36Sopenharmony_ci			rq->vda_req_sz = reqsize;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	return true;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/*
36262306a36Sopenharmony_ci * Create PRD list for each I-block consumed by the command. This routine
36362306a36Sopenharmony_ci * determines how much data is required from each I-block being consumed
36462306a36Sopenharmony_ci * by the command. The first and last I-blocks can be partials and all of
36562306a36Sopenharmony_ci * the I-blocks in between are for a full I-block of data.
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * The interleave size is used to determine the number of bytes in the 1st
36862306a36Sopenharmony_ci * I-block and the remaining I-blocks are what remeains.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_cistatic bool esas2r_build_prd_iblk(struct esas2r_adapter *a,
37162306a36Sopenharmony_ci				  struct esas2r_sg_context *sgc)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
37462306a36Sopenharmony_ci	u64 addr;
37562306a36Sopenharmony_ci	u32 len;
37662306a36Sopenharmony_ci	struct esas2r_mem_desc *sgl;
37762306a36Sopenharmony_ci	u32 numchain = 1;
37862306a36Sopenharmony_ci	u32 rem = 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	while (sgc->length) {
38162306a36Sopenharmony_ci		/* Get the next address/length pair */
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		len = (*sgc->get_phys_addr)(sgc, &addr);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (unlikely(len == 0))
38662306a36Sopenharmony_ci			return false;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		/* If current length is more than what's left, stop there */
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if (unlikely(len > sgc->length))
39162306a36Sopenharmony_ci			len = sgc->length;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cianother_entry:
39462306a36Sopenharmony_ci		/* Limit to a round number less than the maximum length */
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		if (len > PRD_LEN_MAX) {
39762306a36Sopenharmony_ci			/*
39862306a36Sopenharmony_ci			 * Save the remainder of the split.  whenever we limit
39962306a36Sopenharmony_ci			 * an entry we come back around to build entries out
40062306a36Sopenharmony_ci			 * of the leftover.  We do this to prevent multiple
40162306a36Sopenharmony_ci			 * calls to the get_phys_addr() function for an SGE
40262306a36Sopenharmony_ci			 * that is too large.
40362306a36Sopenharmony_ci			 */
40462306a36Sopenharmony_ci			rem = len - PRD_LEN_MAX;
40562306a36Sopenharmony_ci			len = PRD_LEN_MAX;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* See if we need to allocate a new SGL */
40962306a36Sopenharmony_ci		if (sgc->sge.prd.sge_cnt == 0) {
41062306a36Sopenharmony_ci			if (len == sgc->length) {
41162306a36Sopenharmony_ci				/*
41262306a36Sopenharmony_ci				 * We only have 1 PRD entry left.
41362306a36Sopenharmony_ci				 * It can be placed where the chain
41462306a36Sopenharmony_ci				 * entry would have gone
41562306a36Sopenharmony_ci				 */
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci				/* Build the simple SGE */
41862306a36Sopenharmony_ci				sgc->sge.prd.curr->ctl_len = cpu_to_le32(
41962306a36Sopenharmony_ci					PRD_DATA | len);
42062306a36Sopenharmony_ci				sgc->sge.prd.curr->address = cpu_to_le64(addr);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci				/* Adjust length related fields */
42362306a36Sopenharmony_ci				sgc->cur_offset += len;
42462306a36Sopenharmony_ci				sgc->length -= len;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci				/* We use the reserved chain entry for data */
42762306a36Sopenharmony_ci				numchain = 0;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci				break;
43062306a36Sopenharmony_ci			}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci			if (sgc->sge.prd.chain) {
43362306a36Sopenharmony_ci				/*
43462306a36Sopenharmony_ci				 * Fill # of entries of current SGL in previous
43562306a36Sopenharmony_ci				 * chain the length of this current SGL may not
43662306a36Sopenharmony_ci				 * full.
43762306a36Sopenharmony_ci				 */
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci				sgc->sge.prd.chain->ctl_len |= cpu_to_le32(
44062306a36Sopenharmony_ci					sgc->sge.prd.sgl_max_cnt);
44162306a36Sopenharmony_ci			}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci			/*
44462306a36Sopenharmony_ci			 * If no SGls are available, return failure.  The
44562306a36Sopenharmony_ci			 * caller can call us later with the current context
44662306a36Sopenharmony_ci			 * to pick up here.
44762306a36Sopenharmony_ci			 */
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci			sgl = esas2r_alloc_sgl(a);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci			if (unlikely(sgl == NULL))
45262306a36Sopenharmony_ci				return false;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci			/*
45562306a36Sopenharmony_ci			 * Link the new SGL onto the chain
45662306a36Sopenharmony_ci			 * They are in reverse order
45762306a36Sopenharmony_ci			 */
45862306a36Sopenharmony_ci			list_add(&sgl->next_desc, &rq->sg_table_head);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci			/*
46162306a36Sopenharmony_ci			 * An SGL was just filled in and we are starting
46262306a36Sopenharmony_ci			 * a new SGL. Prime the chain of the ending SGL with
46362306a36Sopenharmony_ci			 * info that points to the new SGL. The length gets
46462306a36Sopenharmony_ci			 * filled in when the new SGL is filled or ended
46562306a36Sopenharmony_ci			 */
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			sgc->sge.prd.chain = sgc->sge.prd.curr;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci			sgc->sge.prd.chain->ctl_len = cpu_to_le32(PRD_CHAIN);
47062306a36Sopenharmony_ci			sgc->sge.prd.chain->address =
47162306a36Sopenharmony_ci				cpu_to_le64(sgl->phys_addr);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			/*
47462306a36Sopenharmony_ci			 * Start a new segment.
47562306a36Sopenharmony_ci			 * Take one away and save for chain SGE
47662306a36Sopenharmony_ci			 */
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci			sgc->sge.prd.curr =
47962306a36Sopenharmony_ci				(struct atto_physical_region_description *)sgl
48062306a36Sopenharmony_ci				->
48162306a36Sopenharmony_ci				virt_addr;
48262306a36Sopenharmony_ci			sgc->sge.prd.sge_cnt = sgc->sge.prd.sgl_max_cnt - 1;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		sgc->sge.prd.sge_cnt--;
48662306a36Sopenharmony_ci		/* Build the simple SGE */
48762306a36Sopenharmony_ci		sgc->sge.prd.curr->ctl_len = cpu_to_le32(PRD_DATA | len);
48862306a36Sopenharmony_ci		sgc->sge.prd.curr->address = cpu_to_le64(addr);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		/* Used another element.  Point to the next one */
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		sgc->sge.prd.curr++;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		/* Adjust length related fields */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		sgc->cur_offset += len;
49762306a36Sopenharmony_ci		sgc->length -= len;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		/*
50062306a36Sopenharmony_ci		 * Check if we previously split an entry.  If so we have to
50162306a36Sopenharmony_ci		 * pick up where we left off.
50262306a36Sopenharmony_ci		 */
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		if (rem) {
50562306a36Sopenharmony_ci			addr += len;
50662306a36Sopenharmony_ci			len = rem;
50762306a36Sopenharmony_ci			rem = 0;
50862306a36Sopenharmony_ci			goto another_entry;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (!list_empty(&rq->sg_table_head)) {
51362306a36Sopenharmony_ci		if (sgc->sge.prd.chain) {
51462306a36Sopenharmony_ci			sgc->sge.prd.chain->ctl_len |=
51562306a36Sopenharmony_ci				cpu_to_le32(sgc->sge.prd.sgl_max_cnt
51662306a36Sopenharmony_ci					    - sgc->sge.prd.sge_cnt
51762306a36Sopenharmony_ci					    - numchain);
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return true;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cibool esas2r_build_sg_list_prd(struct esas2r_adapter *a,
52562306a36Sopenharmony_ci			      struct esas2r_sg_context *sgc)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct esas2r_request *rq = sgc->first_req;
52862306a36Sopenharmony_ci	u32 len = sgc->length;
52962306a36Sopenharmony_ci	struct esas2r_target *t = a->targetdb + rq->target_id;
53062306a36Sopenharmony_ci	u8 is_i_o = 0;
53162306a36Sopenharmony_ci	u16 reqsize;
53262306a36Sopenharmony_ci	struct atto_physical_region_description *curr_iblk_chn;
53362306a36Sopenharmony_ci	u8 *cdb = (u8 *)&rq->vrq->scsi.cdb[0];
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * extract LBA from command so we can determine
53762306a36Sopenharmony_ci	 * the I-Block boundary
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (rq->vrq->scsi.function == VDA_FUNC_SCSI
54162306a36Sopenharmony_ci	    && t->target_state == TS_PRESENT
54262306a36Sopenharmony_ci	    && !(t->flags & TF_PASS_THRU)) {
54362306a36Sopenharmony_ci		u32 lbalo = 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		switch (rq->vrq->scsi.cdb[0]) {
54662306a36Sopenharmony_ci		case    READ_16:
54762306a36Sopenharmony_ci		case    WRITE_16:
54862306a36Sopenharmony_ci		{
54962306a36Sopenharmony_ci			lbalo =
55062306a36Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[9],
55162306a36Sopenharmony_ci						   cdb[8]),
55262306a36Sopenharmony_ci					  MAKEWORD(cdb[7],
55362306a36Sopenharmony_ci						   cdb[6]));
55462306a36Sopenharmony_ci			is_i_o = 1;
55562306a36Sopenharmony_ci			break;
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		case    READ_12:
55962306a36Sopenharmony_ci		case    WRITE_12:
56062306a36Sopenharmony_ci		case    READ_10:
56162306a36Sopenharmony_ci		case    WRITE_10:
56262306a36Sopenharmony_ci		{
56362306a36Sopenharmony_ci			lbalo =
56462306a36Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[5],
56562306a36Sopenharmony_ci						   cdb[4]),
56662306a36Sopenharmony_ci					  MAKEWORD(cdb[3],
56762306a36Sopenharmony_ci						   cdb[2]));
56862306a36Sopenharmony_ci			is_i_o = 1;
56962306a36Sopenharmony_ci			break;
57062306a36Sopenharmony_ci		}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		case    READ_6:
57362306a36Sopenharmony_ci		case    WRITE_6:
57462306a36Sopenharmony_ci		{
57562306a36Sopenharmony_ci			lbalo =
57662306a36Sopenharmony_ci				MAKEDWORD(MAKEWORD(cdb[3],
57762306a36Sopenharmony_ci						   cdb[2]),
57862306a36Sopenharmony_ci					  MAKEWORD(cdb[1] & 0x1F,
57962306a36Sopenharmony_ci						   0));
58062306a36Sopenharmony_ci			is_i_o = 1;
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		default:
58562306a36Sopenharmony_ci			break;
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		if (is_i_o) {
58962306a36Sopenharmony_ci			u32 startlba;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci			rq->vrq->scsi.iblk_cnt_prd = 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci			/* Determine size of 1st I-block PRD list       */
59462306a36Sopenharmony_ci			startlba = t->inter_block - (lbalo & (t->inter_block -
59562306a36Sopenharmony_ci							      1));
59662306a36Sopenharmony_ci			sgc->length = startlba * t->block_size;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci			/* Chk if the 1st iblk chain starts at base of Iblock */
59962306a36Sopenharmony_ci			if ((lbalo & (t->inter_block - 1)) == 0)
60062306a36Sopenharmony_ci				rq->flags |= RF_1ST_IBLK_BASE;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			if (sgc->length > len)
60362306a36Sopenharmony_ci				sgc->length = len;
60462306a36Sopenharmony_ci		} else {
60562306a36Sopenharmony_ci			sgc->length = len;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		sgc->length = len;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* get our starting chain address   */
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	curr_iblk_chn =
61462306a36Sopenharmony_ci		(struct atto_physical_region_description *)sgc->sge.a64.curr;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	sgc->sge.prd.sgl_max_cnt = sgl_page_size /
61762306a36Sopenharmony_ci				   sizeof(struct
61862306a36Sopenharmony_ci					  atto_physical_region_description);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* create all of the I-block PRD lists          */
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	while (len) {
62362306a36Sopenharmony_ci		sgc->sge.prd.sge_cnt = 0;
62462306a36Sopenharmony_ci		sgc->sge.prd.chain = NULL;
62562306a36Sopenharmony_ci		sgc->sge.prd.curr = curr_iblk_chn;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		/* increment to next I-Block    */
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		len -= sgc->length;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		/* go build the next I-Block PRD list   */
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		if (unlikely(!esas2r_build_prd_iblk(a, sgc)))
63462306a36Sopenharmony_ci			return false;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		curr_iblk_chn++;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		if (is_i_o) {
63962306a36Sopenharmony_ci			rq->vrq->scsi.iblk_cnt_prd++;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			if (len > t->inter_byte)
64262306a36Sopenharmony_ci				sgc->length = t->inter_byte;
64362306a36Sopenharmony_ci			else
64462306a36Sopenharmony_ci				sgc->length = len;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* figure out the size used of the VDA request */
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	reqsize = ((u16)((u8 *)curr_iblk_chn - (u8 *)rq->vrq))
65162306a36Sopenharmony_ci		  / sizeof(u32);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/*
65462306a36Sopenharmony_ci	 * only update the request size if it is bigger than what is
65562306a36Sopenharmony_ci	 * already there.  we can come in here twice for some management
65662306a36Sopenharmony_ci	 * commands.
65762306a36Sopenharmony_ci	 */
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (reqsize > rq->vda_req_sz)
66062306a36Sopenharmony_ci		rq->vda_req_sz = reqsize;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return true;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void esas2r_handle_pending_reset(struct esas2r_adapter *a, u32 currtime)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	u32 delta = currtime - a->chip_init_time;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (delta <= ESAS2R_CHPRST_WAIT_TIME) {
67062306a36Sopenharmony_ci		/* Wait before accessing registers */
67162306a36Sopenharmony_ci	} else if (delta >= ESAS2R_CHPRST_TIME) {
67262306a36Sopenharmony_ci		/*
67362306a36Sopenharmony_ci		 * The last reset failed so try again. Reset
67462306a36Sopenharmony_ci		 * processing will give up after three tries.
67562306a36Sopenharmony_ci		 */
67662306a36Sopenharmony_ci		esas2r_local_reset_adapter(a);
67762306a36Sopenharmony_ci	} else {
67862306a36Sopenharmony_ci		/* We can now see if the firmware is ready */
67962306a36Sopenharmony_ci		u32 doorbell;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
68262306a36Sopenharmony_ci		if (doorbell == 0xFFFFFFFF || !(doorbell & DRBL_FORCE_INT)) {
68362306a36Sopenharmony_ci			esas2r_force_interrupt(a);
68462306a36Sopenharmony_ci		} else {
68562306a36Sopenharmony_ci			u32 ver = (doorbell & DRBL_FW_VER_MSK);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci			/* Driver supports API version 0 and 1 */
68862306a36Sopenharmony_ci			esas2r_write_register_dword(a, MU_DOORBELL_OUT,
68962306a36Sopenharmony_ci						    doorbell);
69062306a36Sopenharmony_ci			if (ver == DRBL_FW_VER_0) {
69162306a36Sopenharmony_ci				set_bit(AF_CHPRST_DETECTED, &a->flags);
69262306a36Sopenharmony_ci				set_bit(AF_LEGACY_SGE_MODE, &a->flags);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci				a->max_vdareq_size = 128;
69562306a36Sopenharmony_ci				a->build_sgl = esas2r_build_sg_list_sge;
69662306a36Sopenharmony_ci			} else if (ver == DRBL_FW_VER_1) {
69762306a36Sopenharmony_ci				set_bit(AF_CHPRST_DETECTED, &a->flags);
69862306a36Sopenharmony_ci				clear_bit(AF_LEGACY_SGE_MODE, &a->flags);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci				a->max_vdareq_size = 1024;
70162306a36Sopenharmony_ci				a->build_sgl = esas2r_build_sg_list_prd;
70262306a36Sopenharmony_ci			} else {
70362306a36Sopenharmony_ci				esas2r_local_reset_adapter(a);
70462306a36Sopenharmony_ci			}
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/* This function must be called once per timer tick */
71162306a36Sopenharmony_civoid esas2r_timer_tick(struct esas2r_adapter *a)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	u32 currtime = jiffies_to_msecs(jiffies);
71462306a36Sopenharmony_ci	u32 deltatime = currtime - a->last_tick_time;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	a->last_tick_time = currtime;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* count down the uptime */
71962306a36Sopenharmony_ci	if (a->chip_uptime &&
72062306a36Sopenharmony_ci	    !test_bit(AF_CHPRST_PENDING, &a->flags) &&
72162306a36Sopenharmony_ci	    !test_bit(AF_DISC_PENDING, &a->flags)) {
72262306a36Sopenharmony_ci		if (deltatime >= a->chip_uptime)
72362306a36Sopenharmony_ci			a->chip_uptime = 0;
72462306a36Sopenharmony_ci		else
72562306a36Sopenharmony_ci			a->chip_uptime -= deltatime;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (test_bit(AF_CHPRST_PENDING, &a->flags)) {
72962306a36Sopenharmony_ci		if (!test_bit(AF_CHPRST_NEEDED, &a->flags) &&
73062306a36Sopenharmony_ci		    !test_bit(AF_CHPRST_DETECTED, &a->flags))
73162306a36Sopenharmony_ci			esas2r_handle_pending_reset(a, currtime);
73262306a36Sopenharmony_ci	} else {
73362306a36Sopenharmony_ci		if (test_bit(AF_DISC_PENDING, &a->flags))
73462306a36Sopenharmony_ci			esas2r_disc_check_complete(a);
73562306a36Sopenharmony_ci		if (test_bit(AF_HEARTBEAT_ENB, &a->flags)) {
73662306a36Sopenharmony_ci			if (test_bit(AF_HEARTBEAT, &a->flags)) {
73762306a36Sopenharmony_ci				if ((currtime - a->heartbeat_time) >=
73862306a36Sopenharmony_ci				    ESAS2R_HEARTBEAT_TIME) {
73962306a36Sopenharmony_ci					clear_bit(AF_HEARTBEAT, &a->flags);
74062306a36Sopenharmony_ci					esas2r_hdebug("heartbeat failed");
74162306a36Sopenharmony_ci					esas2r_log(ESAS2R_LOG_CRIT,
74262306a36Sopenharmony_ci						   "heartbeat failed");
74362306a36Sopenharmony_ci					esas2r_bugon();
74462306a36Sopenharmony_ci					esas2r_local_reset_adapter(a);
74562306a36Sopenharmony_ci				}
74662306a36Sopenharmony_ci			} else {
74762306a36Sopenharmony_ci				set_bit(AF_HEARTBEAT, &a->flags);
74862306a36Sopenharmony_ci				a->heartbeat_time = currtime;
74962306a36Sopenharmony_ci				esas2r_force_interrupt(a);
75062306a36Sopenharmony_ci			}
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
75562306a36Sopenharmony_ci		esas2r_do_deferred_processes(a);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/*
75962306a36Sopenharmony_ci * Send the specified task management function to the target and LUN
76062306a36Sopenharmony_ci * specified in rqaux.  in addition, immediately abort any commands that
76162306a36Sopenharmony_ci * are queued but not sent to the device according to the rules specified
76262306a36Sopenharmony_ci * by the task management function.
76362306a36Sopenharmony_ci */
76462306a36Sopenharmony_cibool esas2r_send_task_mgmt(struct esas2r_adapter *a,
76562306a36Sopenharmony_ci			   struct esas2r_request *rqaux, u8 task_mgt_func)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	u16 targetid = rqaux->target_id;
76862306a36Sopenharmony_ci	u8 lun = (u8)le32_to_cpu(rqaux->vrq->scsi.flags);
76962306a36Sopenharmony_ci	bool ret = false;
77062306a36Sopenharmony_ci	struct esas2r_request *rq;
77162306a36Sopenharmony_ci	struct list_head *next, *element;
77262306a36Sopenharmony_ci	unsigned long flags;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	LIST_HEAD(comp_list);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	esas2r_trace_enter();
77762306a36Sopenharmony_ci	esas2r_trace("rqaux:%p", rqaux);
77862306a36Sopenharmony_ci	esas2r_trace("task_mgt_func:%x", task_mgt_func);
77962306a36Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	/* search the defer queue looking for requests for the device */
78262306a36Sopenharmony_ci	list_for_each_safe(element, next, &a->defer_list) {
78362306a36Sopenharmony_ci		rq = list_entry(element, struct esas2r_request, req_list);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (rq->vrq->scsi.function == VDA_FUNC_SCSI
78662306a36Sopenharmony_ci		    && rq->target_id == targetid
78762306a36Sopenharmony_ci		    && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
78862306a36Sopenharmony_ci			|| task_mgt_func == 0x20)) { /* target reset */
78962306a36Sopenharmony_ci			/* Found a request affected by the task management */
79062306a36Sopenharmony_ci			if (rq->req_stat == RS_PENDING) {
79162306a36Sopenharmony_ci				/*
79262306a36Sopenharmony_ci				 * The request is pending or waiting.  We can
79362306a36Sopenharmony_ci				 * safelycomplete the request now.
79462306a36Sopenharmony_ci				 */
79562306a36Sopenharmony_ci				if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
79662306a36Sopenharmony_ci					list_add_tail(&rq->comp_list,
79762306a36Sopenharmony_ci						      &comp_list);
79862306a36Sopenharmony_ci			}
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* Send the task management request to the firmware */
80362306a36Sopenharmony_ci	rqaux->sense_len = 0;
80462306a36Sopenharmony_ci	rqaux->vrq->scsi.length = 0;
80562306a36Sopenharmony_ci	rqaux->target_id = targetid;
80662306a36Sopenharmony_ci	rqaux->vrq->scsi.flags |= cpu_to_le32(lun);
80762306a36Sopenharmony_ci	memset(rqaux->vrq->scsi.cdb, 0, sizeof(rqaux->vrq->scsi.cdb));
80862306a36Sopenharmony_ci	rqaux->vrq->scsi.flags |=
80962306a36Sopenharmony_ci		cpu_to_le16(task_mgt_func * LOBIT(FCP_CMND_TM_MASK));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (test_bit(AF_FLASHING, &a->flags)) {
81262306a36Sopenharmony_ci		/* Assume success.  if there are active requests, return busy */
81362306a36Sopenharmony_ci		rqaux->req_stat = RS_SUCCESS;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		list_for_each_safe(element, next, &a->active_list) {
81662306a36Sopenharmony_ci			rq = list_entry(element, struct esas2r_request,
81762306a36Sopenharmony_ci					req_list);
81862306a36Sopenharmony_ci			if (rq->vrq->scsi.function == VDA_FUNC_SCSI
81962306a36Sopenharmony_ci			    && rq->target_id == targetid
82062306a36Sopenharmony_ci			    && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
82162306a36Sopenharmony_ci				|| task_mgt_func == 0x20))  /* target reset */
82262306a36Sopenharmony_ci				rqaux->req_stat = RS_BUSY;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		ret = true;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (!test_bit(AF_FLASHING, &a->flags))
83162306a36Sopenharmony_ci		esas2r_start_request(a, rqaux);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	esas2r_comp_list_drain(a, &comp_list);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
83662306a36Sopenharmony_ci		esas2r_do_deferred_processes(a);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	esas2r_trace_exit();
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return ret;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_civoid esas2r_reset_bus(struct esas2r_adapter *a)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	esas2r_log(ESAS2R_LOG_INFO, "performing a bus reset");
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
84862306a36Sopenharmony_ci	    !test_bit(AF_CHPRST_PENDING, &a->flags) &&
84962306a36Sopenharmony_ci	    !test_bit(AF_DISC_PENDING, &a->flags)) {
85062306a36Sopenharmony_ci		set_bit(AF_BUSRST_NEEDED, &a->flags);
85162306a36Sopenharmony_ci		set_bit(AF_BUSRST_PENDING, &a->flags);
85262306a36Sopenharmony_ci		set_bit(AF_OS_RESET, &a->flags);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		esas2r_schedule_tasklet(a);
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cibool esas2r_ioreq_aborted(struct esas2r_adapter *a, struct esas2r_request *rq,
85962306a36Sopenharmony_ci			  u8 status)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	esas2r_trace_enter();
86262306a36Sopenharmony_ci	esas2r_trace("rq:%p", rq);
86362306a36Sopenharmony_ci	list_del_init(&rq->req_list);
86462306a36Sopenharmony_ci	if (rq->timeout > RQ_MAX_TIMEOUT) {
86562306a36Sopenharmony_ci		/*
86662306a36Sopenharmony_ci		 * The request timed out, but we could not abort it because a
86762306a36Sopenharmony_ci		 * chip reset occurred.  Return busy status.
86862306a36Sopenharmony_ci		 */
86962306a36Sopenharmony_ci		rq->req_stat = RS_BUSY;
87062306a36Sopenharmony_ci		esas2r_trace_exit();
87162306a36Sopenharmony_ci		return true;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	rq->req_stat = status;
87562306a36Sopenharmony_ci	esas2r_trace_exit();
87662306a36Sopenharmony_ci	return true;
87762306a36Sopenharmony_ci}
878