162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Aic94xx Task Management Functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci#include <linux/gfp.h>
1162306a36Sopenharmony_ci#include "aic94xx.h"
1262306a36Sopenharmony_ci#include "aic94xx_sas.h"
1362306a36Sopenharmony_ci#include "aic94xx_hwi.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* ---------- Internal enqueue ---------- */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int asd_enqueue_internal(struct asd_ascb *ascb,
1862306a36Sopenharmony_ci		void (*tasklet_complete)(struct asd_ascb *,
1962306a36Sopenharmony_ci					 struct done_list_struct *),
2062306a36Sopenharmony_ci				void (*timed_out)(struct timer_list *t))
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	int res;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	ascb->tasklet_complete = tasklet_complete;
2562306a36Sopenharmony_ci	ascb->uldd_timer = 1;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	ascb->timer.function = timed_out;
2862306a36Sopenharmony_ci	ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	add_timer(&ascb->timer);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	res = asd_post_ascb_list(ascb->ha, ascb, 1);
3362306a36Sopenharmony_ci	if (unlikely(res))
3462306a36Sopenharmony_ci		del_timer(&ascb->timer);
3562306a36Sopenharmony_ci	return res;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* ---------- CLEAR NEXUS ---------- */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct tasklet_completion_status {
4162306a36Sopenharmony_ci	int	dl_opcode;
4262306a36Sopenharmony_ci	int	tmf_state;
4362306a36Sopenharmony_ci	u8	tag_valid:1;
4462306a36Sopenharmony_ci	__be16	tag;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define DECLARE_TCS(tcs) \
4862306a36Sopenharmony_ci	struct tasklet_completion_status tcs = { \
4962306a36Sopenharmony_ci		.dl_opcode = 0, \
5062306a36Sopenharmony_ci		.tmf_state = 0, \
5162306a36Sopenharmony_ci		.tag_valid = 0, \
5262306a36Sopenharmony_ci		.tag = 0, \
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
5762306a36Sopenharmony_ci					     struct done_list_struct *dl)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
6062306a36Sopenharmony_ci	ASD_DPRINTK("%s: here\n", __func__);
6162306a36Sopenharmony_ci	if (!del_timer(&ascb->timer)) {
6262306a36Sopenharmony_ci		ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	ASD_DPRINTK("%s: opcode: 0x%x\n", __func__, dl->opcode);
6662306a36Sopenharmony_ci	tcs->dl_opcode = dl->opcode;
6762306a36Sopenharmony_ci	complete(ascb->completion);
6862306a36Sopenharmony_ci	asd_ascb_free(ascb);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void asd_clear_nexus_timedout(struct timer_list *t)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct asd_ascb *ascb = from_timer(ascb, t, timer);
7462306a36Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ASD_DPRINTK("%s: here\n", __func__);
7762306a36Sopenharmony_ci	tcs->dl_opcode = TMF_RESP_FUNC_FAILED;
7862306a36Sopenharmony_ci	complete(ascb->completion);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define CLEAR_NEXUS_PRE         \
8262306a36Sopenharmony_ci	struct asd_ascb *ascb; \
8362306a36Sopenharmony_ci	struct scb *scb; \
8462306a36Sopenharmony_ci	int res; \
8562306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion); \
8662306a36Sopenharmony_ci	DECLARE_TCS(tcs); \
8762306a36Sopenharmony_ci		\
8862306a36Sopenharmony_ci	ASD_DPRINTK("%s: PRE\n", __func__); \
8962306a36Sopenharmony_ci        res = 1;                \
9062306a36Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
9162306a36Sopenharmony_ci	if (!ascb)              \
9262306a36Sopenharmony_ci		return -ENOMEM; \
9362306a36Sopenharmony_ci                                \
9462306a36Sopenharmony_ci	ascb->completion = &completion; \
9562306a36Sopenharmony_ci	ascb->uldd_task = &tcs; \
9662306a36Sopenharmony_ci	scb = ascb->scb;        \
9762306a36Sopenharmony_ci	scb->header.opcode = CLEAR_NEXUS
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define CLEAR_NEXUS_POST        \
10062306a36Sopenharmony_ci	ASD_DPRINTK("%s: POST\n", __func__); \
10162306a36Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
10262306a36Sopenharmony_ci				   asd_clear_nexus_timedout);              \
10362306a36Sopenharmony_ci	if (res)                \
10462306a36Sopenharmony_ci		goto out_err;   \
10562306a36Sopenharmony_ci	ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __func__); \
10662306a36Sopenharmony_ci	wait_for_completion(&completion); \
10762306a36Sopenharmony_ci	res = tcs.dl_opcode; \
10862306a36Sopenharmony_ci	if (res == TC_NO_ERROR) \
10962306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;   \
11062306a36Sopenharmony_ci	return res; \
11162306a36Sopenharmony_ciout_err:                        \
11262306a36Sopenharmony_ci	asd_ascb_free(ascb);    \
11362306a36Sopenharmony_ci	return res
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
12062306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_ADAPTER;
12162306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint asd_clear_nexus_port(struct asd_sas_port *port)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = port->ha->lldd_ha;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
12962306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_PORT;
13062306a36Sopenharmony_ci	scb->clear_nexus.conn_mask = port->phy_mask;
13162306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cienum clear_nexus_phase {
13562306a36Sopenharmony_ci	NEXUS_PHASE_PRE,
13662306a36Sopenharmony_ci	NEXUS_PHASE_POST,
13762306a36Sopenharmony_ci	NEXUS_PHASE_RESUME,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int asd_clear_nexus_I_T(struct domain_device *dev,
14162306a36Sopenharmony_ci			       enum clear_nexus_phase phase)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
14662306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_I_T;
14762306a36Sopenharmony_ci	switch (phase) {
14862306a36Sopenharmony_ci	case NEXUS_PHASE_PRE:
14962306a36Sopenharmony_ci		scb->clear_nexus.flags = EXEC_Q | SUSPEND_TX;
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci	case NEXUS_PHASE_POST:
15262306a36Sopenharmony_ci		scb->clear_nexus.flags = SEND_Q | NOTINQ;
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci	case NEXUS_PHASE_RESUME:
15562306a36Sopenharmony_ci		scb->clear_nexus.flags = RESUME_TX;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
15862306a36Sopenharmony_ci						   dev->lldd_dev);
15962306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ciint asd_I_T_nexus_reset(struct domain_device *dev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int res, tmp_res, i;
16562306a36Sopenharmony_ci	struct sas_phy *phy = sas_get_local_phy(dev);
16662306a36Sopenharmony_ci	/* Standard mandates link reset for ATA  (type 0) and
16762306a36Sopenharmony_ci	 * hard reset for SSP (type 1) */
16862306a36Sopenharmony_ci	int reset_type = (dev->dev_type == SAS_SATA_DEV ||
16962306a36Sopenharmony_ci			  (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE);
17262306a36Sopenharmony_ci	/* send a hard reset */
17362306a36Sopenharmony_ci	ASD_DPRINTK("sending %s reset to %s\n",
17462306a36Sopenharmony_ci		    reset_type ? "hard" : "soft", dev_name(&phy->dev));
17562306a36Sopenharmony_ci	res = sas_phy_reset(phy, reset_type);
17662306a36Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) {
17762306a36Sopenharmony_ci		/* wait for the maximum settle time */
17862306a36Sopenharmony_ci		msleep(500);
17962306a36Sopenharmony_ci		/* clear all outstanding commands (keep nexus suspended) */
18062306a36Sopenharmony_ci		asd_clear_nexus_I_T(dev, NEXUS_PHASE_POST);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	for (i = 0 ; i < 3; i++) {
18362306a36Sopenharmony_ci		tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
18462306a36Sopenharmony_ci		if (tmp_res == TC_RESUME)
18562306a36Sopenharmony_ci			goto out;
18662306a36Sopenharmony_ci		msleep(500);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* This is a bit of a problem:  the sequencer is still suspended
19062306a36Sopenharmony_ci	 * and is refusing to resume.  Hope it will resume on a bigger hammer
19162306a36Sopenharmony_ci	 * or the disk is lost */
19262306a36Sopenharmony_ci	dev_printk(KERN_ERR, &phy->dev,
19362306a36Sopenharmony_ci		   "Failed to resume nexus after reset 0x%x\n", tmp_res);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	res = TMF_RESP_FUNC_FAILED;
19662306a36Sopenharmony_ci out:
19762306a36Sopenharmony_ci	sas_put_local_phy(phy);
19862306a36Sopenharmony_ci	return res;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
20662306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_I_T_L;
20762306a36Sopenharmony_ci	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
20862306a36Sopenharmony_ci	memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);
20962306a36Sopenharmony_ci	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
21062306a36Sopenharmony_ci						   dev->lldd_dev);
21162306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int asd_clear_nexus_tag(struct sas_task *task)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
21762306a36Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
22062306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_TAG;
22162306a36Sopenharmony_ci	memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);
22262306a36Sopenharmony_ci	scb->clear_nexus.ssp_task.tag = tascb->tag;
22362306a36Sopenharmony_ci	if (task->dev->tproto)
22462306a36Sopenharmony_ci		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
22562306a36Sopenharmony_ci							  task->dev->lldd_dev);
22662306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int asd_clear_nexus_index(struct sas_task *task)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
23262306a36Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	CLEAR_NEXUS_PRE;
23562306a36Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_TRANS_CX;
23662306a36Sopenharmony_ci	if (task->dev->tproto)
23762306a36Sopenharmony_ci		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
23862306a36Sopenharmony_ci							  task->dev->lldd_dev);
23962306a36Sopenharmony_ci	scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);
24062306a36Sopenharmony_ci	CLEAR_NEXUS_POST;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/* ---------- TMFs ---------- */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void asd_tmf_timedout(struct timer_list *t)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct asd_ascb *ascb = from_timer(ascb, t, timer);
24862306a36Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ASD_DPRINTK("tmf timed out\n");
25162306a36Sopenharmony_ci	tcs->tmf_state = TMF_RESP_FUNC_FAILED;
25262306a36Sopenharmony_ci	complete(ascb->completion);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
25662306a36Sopenharmony_ci				    struct done_list_struct *dl)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
25962306a36Sopenharmony_ci	unsigned long flags;
26062306a36Sopenharmony_ci	struct tc_resp_sb_struct {
26162306a36Sopenharmony_ci		__le16 index_escb;
26262306a36Sopenharmony_ci		u8     len_lsb;
26362306a36Sopenharmony_ci		u8     flags;
26462306a36Sopenharmony_ci	} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
26762306a36Sopenharmony_ci	struct asd_ascb *escb;
26862306a36Sopenharmony_ci	struct asd_dma_tok *edb;
26962306a36Sopenharmony_ci	struct ssp_frame_hdr *fh;
27062306a36Sopenharmony_ci	struct ssp_response_iu   *ru;
27162306a36Sopenharmony_ci	int res = TMF_RESP_FUNC_FAILED;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ASD_DPRINTK("tmf resp tasklet\n");
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
27662306a36Sopenharmony_ci	escb = asd_tc_index_find(&asd_ha->seq,
27762306a36Sopenharmony_ci				 (int)le16_to_cpu(resp_sb->index_escb));
27862306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (!escb) {
28162306a36Sopenharmony_ci		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
28262306a36Sopenharmony_ci		return res;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
28662306a36Sopenharmony_ci	ascb->tag = *(__be16 *)(edb->vaddr+4);
28762306a36Sopenharmony_ci	fh = edb->vaddr + 16;
28862306a36Sopenharmony_ci	ru = edb->vaddr + 16 + sizeof(*fh);
28962306a36Sopenharmony_ci	res = ru->status;
29062306a36Sopenharmony_ci	if (ru->datapres == SAS_DATAPRES_RESPONSE_DATA)
29162306a36Sopenharmony_ci		res = ru->resp_data[3];
29262306a36Sopenharmony_ci#if 0
29362306a36Sopenharmony_ci	ascb->tag = fh->tag;
29462306a36Sopenharmony_ci#endif
29562306a36Sopenharmony_ci	ascb->tag_valid = 1;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	asd_invalidate_edb(escb, edb_id);
29862306a36Sopenharmony_ci	return res;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
30262306a36Sopenharmony_ci				     struct done_list_struct *dl)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct tasklet_completion_status *tcs;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (!del_timer(&ascb->timer))
30762306a36Sopenharmony_ci		return;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	tcs = ascb->uldd_task;
31062306a36Sopenharmony_ci	ASD_DPRINTK("tmf tasklet complete\n");
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	tcs->dl_opcode = dl->opcode;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (dl->opcode == TC_SSP_RESP) {
31562306a36Sopenharmony_ci		tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl);
31662306a36Sopenharmony_ci		tcs->tag_valid = ascb->tag_valid;
31762306a36Sopenharmony_ci		tcs->tag = ascb->tag;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	complete(ascb->completion);
32162306a36Sopenharmony_ci	asd_ascb_free(ascb);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int asd_clear_nexus(struct sas_task *task)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	int res = TMF_RESP_FUNC_FAILED;
32762306a36Sopenharmony_ci	int leftover;
32862306a36Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
32962306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
33062306a36Sopenharmony_ci	unsigned long flags;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	tascb->completion = &completion;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	ASD_DPRINTK("task not done, clearing nexus\n");
33562306a36Sopenharmony_ci	if (tascb->tag_valid)
33662306a36Sopenharmony_ci		res = asd_clear_nexus_tag(task);
33762306a36Sopenharmony_ci	else
33862306a36Sopenharmony_ci		res = asd_clear_nexus_index(task);
33962306a36Sopenharmony_ci	leftover = wait_for_completion_timeout(&completion,
34062306a36Sopenharmony_ci					       AIC94XX_SCB_TIMEOUT);
34162306a36Sopenharmony_ci	tascb->completion = NULL;
34262306a36Sopenharmony_ci	ASD_DPRINTK("came back from clear nexus\n");
34362306a36Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
34462306a36Sopenharmony_ci	if (leftover < 1)
34562306a36Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
34662306a36Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE)
34762306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
34862306a36Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return res;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/**
35462306a36Sopenharmony_ci * asd_abort_task -- ABORT TASK TMF
35562306a36Sopenharmony_ci * @task: the task to be aborted
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * Before calling ABORT TASK the task state flags should be ORed with
35862306a36Sopenharmony_ci * SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under
35962306a36Sopenharmony_ci * the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * Implements the ABORT TASK TMF, I_T_L_Q nexus.
36262306a36Sopenharmony_ci * Returns: SAS TMF responses (see sas_task.h),
36362306a36Sopenharmony_ci *          -ENOMEM,
36462306a36Sopenharmony_ci *          -SAS_QUEUE_FULL.
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * When ABORT TASK returns, the caller of ABORT TASK checks first the
36762306a36Sopenharmony_ci * task->task_state_flags, and then the return value of ABORT TASK.
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * If the task has task state bit SAS_TASK_STATE_DONE set, then the
37062306a36Sopenharmony_ci * task was completed successfully prior to it being aborted.  The
37162306a36Sopenharmony_ci * caller of ABORT TASK has responsibility to call task->task_done()
37262306a36Sopenharmony_ci * xor free the task, depending on their framework.  The return code
37362306a36Sopenharmony_ci * is TMF_RESP_FUNC_FAILED in this case.
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Else the SAS_TASK_STATE_DONE bit is not set,
37662306a36Sopenharmony_ci * 	If the return code is TMF_RESP_FUNC_COMPLETE, then
37762306a36Sopenharmony_ci * 		the task was aborted successfully.  The caller of
37862306a36Sopenharmony_ci * 		ABORT TASK has responsibility to call task->task_done()
37962306a36Sopenharmony_ci *              to finish the task, xor free the task depending on their
38062306a36Sopenharmony_ci *		framework.
38162306a36Sopenharmony_ci *	else
38262306a36Sopenharmony_ci * 		the ABORT TASK returned some kind of error. The task
38362306a36Sopenharmony_ci *              was _not_ cancelled.  Nothing can be assumed.
38462306a36Sopenharmony_ci *		The caller of ABORT TASK may wish to retry.
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_ciint asd_abort_task(struct sas_task *task)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
38962306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = tascb->ha;
39062306a36Sopenharmony_ci	int res = 1;
39162306a36Sopenharmony_ci	unsigned long flags;
39262306a36Sopenharmony_ci	struct asd_ascb *ascb = NULL;
39362306a36Sopenharmony_ci	struct scb *scb;
39462306a36Sopenharmony_ci	int leftover;
39562306a36Sopenharmony_ci	DECLARE_TCS(tcs);
39662306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
39762306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(tascb_completion);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	tascb->completion = &tascb_completion;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
40262306a36Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
40362306a36Sopenharmony_ci		spin_unlock_irqrestore(&task->task_state_lock, flags);
40462306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
40562306a36Sopenharmony_ci		ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
40662306a36Sopenharmony_ci		goto out_done;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
41162306a36Sopenharmony_ci	if (!ascb)
41262306a36Sopenharmony_ci		return -ENOMEM;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ascb->uldd_task = &tcs;
41562306a36Sopenharmony_ci	ascb->completion = &completion;
41662306a36Sopenharmony_ci	scb = ascb->scb;
41762306a36Sopenharmony_ci	scb->header.opcode = SCB_ABORT_TASK;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	switch (task->task_proto) {
42062306a36Sopenharmony_ci	case SAS_PROTOCOL_SATA:
42162306a36Sopenharmony_ci	case SAS_PROTOCOL_STP:
42262306a36Sopenharmony_ci		scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case SAS_PROTOCOL_SSP:
42562306a36Sopenharmony_ci		scb->abort_task.proto_conn_rate  = (1 << 4); /* SSP */
42662306a36Sopenharmony_ci		scb->abort_task.proto_conn_rate |= task->dev->linkrate;
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	case SAS_PROTOCOL_SMP:
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	default:
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (task->task_proto == SAS_PROTOCOL_SSP) {
43562306a36Sopenharmony_ci		scb->abort_task.ssp_frame.frame_type = SSP_TASK;
43662306a36Sopenharmony_ci		memcpy(scb->abort_task.ssp_frame.hashed_dest_addr,
43762306a36Sopenharmony_ci		       task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
43862306a36Sopenharmony_ci		memcpy(scb->abort_task.ssp_frame.hashed_src_addr,
43962306a36Sopenharmony_ci		       task->dev->port->ha->hashed_sas_addr,
44062306a36Sopenharmony_ci		       HASHED_SAS_ADDR_SIZE);
44162306a36Sopenharmony_ci		scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8);
44462306a36Sopenharmony_ci		scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK;
44562306a36Sopenharmony_ci		scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	scb->abort_task.sister_scb = cpu_to_le16(0xFFFF);
44962306a36Sopenharmony_ci	scb->abort_task.conn_handle = cpu_to_le16(
45062306a36Sopenharmony_ci		(u16)(unsigned long)task->dev->lldd_dev);
45162306a36Sopenharmony_ci	scb->abort_task.retry_count = 1;
45262306a36Sopenharmony_ci	scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
45362306a36Sopenharmony_ci	scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
45662306a36Sopenharmony_ci				   asd_tmf_timedout);
45762306a36Sopenharmony_ci	if (res)
45862306a36Sopenharmony_ci		goto out_free;
45962306a36Sopenharmony_ci	wait_for_completion(&completion);
46062306a36Sopenharmony_ci	ASD_DPRINTK("tmf came back\n");
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	tascb->tag = tcs.tag;
46362306a36Sopenharmony_ci	tascb->tag_valid = tcs.tag_valid;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
46662306a36Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
46762306a36Sopenharmony_ci		spin_unlock_irqrestore(&task->task_state_lock, flags);
46862306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
46962306a36Sopenharmony_ci		ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
47062306a36Sopenharmony_ci		goto out_done;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (tcs.dl_opcode == TC_SSP_RESP) {
47562306a36Sopenharmony_ci		/* The task to be aborted has been sent to the device.
47662306a36Sopenharmony_ci		 * We got a Response IU for the ABORT TASK TMF. */
47762306a36Sopenharmony_ci		if (tcs.tmf_state == TMF_RESP_FUNC_COMPLETE)
47862306a36Sopenharmony_ci			res = asd_clear_nexus(task);
47962306a36Sopenharmony_ci		else
48062306a36Sopenharmony_ci			res = tcs.tmf_state;
48162306a36Sopenharmony_ci	} else if (tcs.dl_opcode == TC_NO_ERROR &&
48262306a36Sopenharmony_ci		   tcs.tmf_state == TMF_RESP_FUNC_FAILED) {
48362306a36Sopenharmony_ci		/* timeout */
48462306a36Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
48562306a36Sopenharmony_ci	} else {
48662306a36Sopenharmony_ci		/* In the following we assume that the managing layer
48762306a36Sopenharmony_ci		 * will _never_ make a mistake, when issuing ABORT
48862306a36Sopenharmony_ci		 * TASK.
48962306a36Sopenharmony_ci		 */
49062306a36Sopenharmony_ci		switch (tcs.dl_opcode) {
49162306a36Sopenharmony_ci		default:
49262306a36Sopenharmony_ci			res = asd_clear_nexus(task);
49362306a36Sopenharmony_ci			fallthrough;
49462306a36Sopenharmony_ci		case TC_NO_ERROR:
49562306a36Sopenharmony_ci			break;
49662306a36Sopenharmony_ci			/* The task hasn't been sent to the device xor
49762306a36Sopenharmony_ci			 * we never got a (sane) Response IU for the
49862306a36Sopenharmony_ci			 * ABORT TASK TMF.
49962306a36Sopenharmony_ci			 */
50062306a36Sopenharmony_ci		case TF_NAK_RECV:
50162306a36Sopenharmony_ci			res = TMF_RESP_INVALID_FRAME;
50262306a36Sopenharmony_ci			break;
50362306a36Sopenharmony_ci		case TF_TMF_TASK_DONE:	/* done but not reported yet */
50462306a36Sopenharmony_ci			res = TMF_RESP_FUNC_FAILED;
50562306a36Sopenharmony_ci			leftover =
50662306a36Sopenharmony_ci				wait_for_completion_timeout(&tascb_completion,
50762306a36Sopenharmony_ci							  AIC94XX_SCB_TIMEOUT);
50862306a36Sopenharmony_ci			spin_lock_irqsave(&task->task_state_lock, flags);
50962306a36Sopenharmony_ci			if (leftover < 1)
51062306a36Sopenharmony_ci				res = TMF_RESP_FUNC_FAILED;
51162306a36Sopenharmony_ci			if (task->task_state_flags & SAS_TASK_STATE_DONE)
51262306a36Sopenharmony_ci				res = TMF_RESP_FUNC_COMPLETE;
51362306a36Sopenharmony_ci			spin_unlock_irqrestore(&task->task_state_lock, flags);
51462306a36Sopenharmony_ci			break;
51562306a36Sopenharmony_ci		case TF_TMF_NO_TAG:
51662306a36Sopenharmony_ci		case TF_TMF_TAG_FREE: /* the tag is in the free list */
51762306a36Sopenharmony_ci		case TF_TMF_NO_CONN_HANDLE: /* no such device */
51862306a36Sopenharmony_ci			res = TMF_RESP_FUNC_COMPLETE;
51962306a36Sopenharmony_ci			break;
52062306a36Sopenharmony_ci		case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */
52162306a36Sopenharmony_ci			res = TMF_RESP_FUNC_ESUPP;
52262306a36Sopenharmony_ci			break;
52362306a36Sopenharmony_ci		}
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci out_done:
52662306a36Sopenharmony_ci	tascb->completion = NULL;
52762306a36Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE) {
52862306a36Sopenharmony_ci		task->lldd_task = NULL;
52962306a36Sopenharmony_ci		mb();
53062306a36Sopenharmony_ci		asd_ascb_free(tascb);
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci	ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
53362306a36Sopenharmony_ci	return res;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci out_free:
53662306a36Sopenharmony_ci	asd_ascb_free(ascb);
53762306a36Sopenharmony_ci	ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
53862306a36Sopenharmony_ci	return res;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/**
54262306a36Sopenharmony_ci * asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus
54362306a36Sopenharmony_ci * @dev: pointer to struct domain_device of interest
54462306a36Sopenharmony_ci * @lun: pointer to u8[8] which is the LUN
54562306a36Sopenharmony_ci * @tmf: the TMF to be performed (see sas_task.h or the SAS spec)
54662306a36Sopenharmony_ci * @index: the transaction context of the task to be queried if QT TMF
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * This function is used to send ABORT TASK SET, CLEAR ACA,
54962306a36Sopenharmony_ci * CLEAR TASK SET, LU RESET and QUERY TASK TMFs.
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * No SCBs should be queued to the I_T_L nexus when this SCB is
55262306a36Sopenharmony_ci * pending.
55362306a36Sopenharmony_ci *
55462306a36Sopenharmony_ci * Returns: TMF response code (see sas_task.h or the SAS spec)
55562306a36Sopenharmony_ci */
55662306a36Sopenharmony_cistatic int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
55762306a36Sopenharmony_ci				int tmf, int index)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
56062306a36Sopenharmony_ci	struct asd_ascb *ascb;
56162306a36Sopenharmony_ci	int res = 1;
56262306a36Sopenharmony_ci	struct scb *scb;
56362306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
56462306a36Sopenharmony_ci	DECLARE_TCS(tcs);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (!(dev->tproto & SAS_PROTOCOL_SSP))
56762306a36Sopenharmony_ci		return TMF_RESP_FUNC_ESUPP;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
57062306a36Sopenharmony_ci	if (!ascb)
57162306a36Sopenharmony_ci		return -ENOMEM;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	ascb->completion = &completion;
57462306a36Sopenharmony_ci	ascb->uldd_task = &tcs;
57562306a36Sopenharmony_ci	scb = ascb->scb;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (tmf == TMF_QUERY_TASK)
57862306a36Sopenharmony_ci		scb->header.opcode = QUERY_SSP_TASK;
57962306a36Sopenharmony_ci	else
58062306a36Sopenharmony_ci		scb->header.opcode = INITIATE_SSP_TMF;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	scb->ssp_tmf.proto_conn_rate  = (1 << 4); /* SSP */
58362306a36Sopenharmony_ci	scb->ssp_tmf.proto_conn_rate |= dev->linkrate;
58462306a36Sopenharmony_ci	/* SSP frame header */
58562306a36Sopenharmony_ci	scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK;
58662306a36Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr,
58762306a36Sopenharmony_ci	       dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
58862306a36Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr,
58962306a36Sopenharmony_ci	       dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
59062306a36Sopenharmony_ci	scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF);
59162306a36Sopenharmony_ci	/* SSP Task IU */
59262306a36Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8);
59362306a36Sopenharmony_ci	scb->ssp_tmf.ssp_task.tmf = tmf;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF);
59662306a36Sopenharmony_ci	scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long)
59762306a36Sopenharmony_ci					      dev->lldd_dev);
59862306a36Sopenharmony_ci	scb->ssp_tmf.retry_count = 1;
59962306a36Sopenharmony_ci	scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
60062306a36Sopenharmony_ci	if (tmf == TMF_QUERY_TASK)
60162306a36Sopenharmony_ci		scb->ssp_tmf.index = cpu_to_le16(index);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
60462306a36Sopenharmony_ci				   asd_tmf_timedout);
60562306a36Sopenharmony_ci	if (res)
60662306a36Sopenharmony_ci		goto out_err;
60762306a36Sopenharmony_ci	wait_for_completion(&completion);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	switch (tcs.dl_opcode) {
61062306a36Sopenharmony_ci	case TC_NO_ERROR:
61162306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case TF_NAK_RECV:
61462306a36Sopenharmony_ci		res = TMF_RESP_INVALID_FRAME;
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	case TF_TMF_TASK_DONE:
61762306a36Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	case TF_TMF_NO_TAG:
62062306a36Sopenharmony_ci	case TF_TMF_TAG_FREE: /* the tag is in the free list */
62162306a36Sopenharmony_ci	case TF_TMF_NO_CONN_HANDLE: /* no such device */
62262306a36Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
62362306a36Sopenharmony_ci		break;
62462306a36Sopenharmony_ci	case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */
62562306a36Sopenharmony_ci		res = TMF_RESP_FUNC_ESUPP;
62662306a36Sopenharmony_ci		break;
62762306a36Sopenharmony_ci	default:
62862306a36Sopenharmony_ci		/* Allow TMF response codes to propagate upwards */
62962306a36Sopenharmony_ci		res = tcs.dl_opcode;
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	return res;
63362306a36Sopenharmony_ciout_err:
63462306a36Sopenharmony_ci	asd_ascb_free(ascb);
63562306a36Sopenharmony_ci	return res;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ciint asd_abort_task_set(struct domain_device *dev, u8 *lun)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
64362306a36Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
64462306a36Sopenharmony_ci	return res;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ciint asd_clear_task_set(struct domain_device *dev, u8 *lun)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
65262306a36Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
65362306a36Sopenharmony_ci	return res;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ciint asd_lu_reset(struct domain_device *dev, u8 *lun)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
66162306a36Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
66262306a36Sopenharmony_ci	return res;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/**
66662306a36Sopenharmony_ci * asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
66762306a36Sopenharmony_ci * @task: pointer to sas_task struct of interest
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
67062306a36Sopenharmony_ci * or TMF_RESP_FUNC_SUCC if the task is in the task set.
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * Normally the management layer sets the task to aborted state,
67362306a36Sopenharmony_ci * and then calls query task and then abort task.
67462306a36Sopenharmony_ci */
67562306a36Sopenharmony_ciint asd_query_task(struct sas_task *task)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct asd_ascb *ascb = task->lldd_task;
67862306a36Sopenharmony_ci	int index;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (ascb) {
68162306a36Sopenharmony_ci		index = ascb->tc_index;
68262306a36Sopenharmony_ci		return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN,
68362306a36Sopenharmony_ci					    TMF_QUERY_TASK, index);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci	return TMF_RESP_FUNC_COMPLETE;
68662306a36Sopenharmony_ci}
687