18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Aic94xx Task Management Functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
108c2ecf20Sopenharmony_ci#include <linux/gfp.h>
118c2ecf20Sopenharmony_ci#include "aic94xx.h"
128c2ecf20Sopenharmony_ci#include "aic94xx_sas.h"
138c2ecf20Sopenharmony_ci#include "aic94xx_hwi.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* ---------- Internal enqueue ---------- */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int asd_enqueue_internal(struct asd_ascb *ascb,
188c2ecf20Sopenharmony_ci		void (*tasklet_complete)(struct asd_ascb *,
198c2ecf20Sopenharmony_ci					 struct done_list_struct *),
208c2ecf20Sopenharmony_ci				void (*timed_out)(struct timer_list *t))
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	int res;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	ascb->tasklet_complete = tasklet_complete;
258c2ecf20Sopenharmony_ci	ascb->uldd_timer = 1;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	ascb->timer.function = timed_out;
288c2ecf20Sopenharmony_ci	ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	add_timer(&ascb->timer);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	res = asd_post_ascb_list(ascb->ha, ascb, 1);
338c2ecf20Sopenharmony_ci	if (unlikely(res))
348c2ecf20Sopenharmony_ci		del_timer(&ascb->timer);
358c2ecf20Sopenharmony_ci	return res;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* ---------- CLEAR NEXUS ---------- */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct tasklet_completion_status {
418c2ecf20Sopenharmony_ci	int	dl_opcode;
428c2ecf20Sopenharmony_ci	int	tmf_state;
438c2ecf20Sopenharmony_ci	u8	tag_valid:1;
448c2ecf20Sopenharmony_ci	__be16	tag;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define DECLARE_TCS(tcs) \
488c2ecf20Sopenharmony_ci	struct tasklet_completion_status tcs = { \
498c2ecf20Sopenharmony_ci		.dl_opcode = 0, \
508c2ecf20Sopenharmony_ci		.tmf_state = 0, \
518c2ecf20Sopenharmony_ci		.tag_valid = 0, \
528c2ecf20Sopenharmony_ci		.tag = 0, \
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
578c2ecf20Sopenharmony_ci					     struct done_list_struct *dl)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
608c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: here\n", __func__);
618c2ecf20Sopenharmony_ci	if (!del_timer(&ascb->timer)) {
628c2ecf20Sopenharmony_ci		ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
638c2ecf20Sopenharmony_ci		return;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: opcode: 0x%x\n", __func__, dl->opcode);
668c2ecf20Sopenharmony_ci	tcs->dl_opcode = dl->opcode;
678c2ecf20Sopenharmony_ci	complete(ascb->completion);
688c2ecf20Sopenharmony_ci	asd_ascb_free(ascb);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void asd_clear_nexus_timedout(struct timer_list *t)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct asd_ascb *ascb = from_timer(ascb, t, timer);
748c2ecf20Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: here\n", __func__);
778c2ecf20Sopenharmony_ci	tcs->dl_opcode = TMF_RESP_FUNC_FAILED;
788c2ecf20Sopenharmony_ci	complete(ascb->completion);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define CLEAR_NEXUS_PRE         \
828c2ecf20Sopenharmony_ci	struct asd_ascb *ascb; \
838c2ecf20Sopenharmony_ci	struct scb *scb; \
848c2ecf20Sopenharmony_ci	int res; \
858c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion); \
868c2ecf20Sopenharmony_ci	DECLARE_TCS(tcs); \
878c2ecf20Sopenharmony_ci		\
888c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: PRE\n", __func__); \
898c2ecf20Sopenharmony_ci        res = 1;                \
908c2ecf20Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
918c2ecf20Sopenharmony_ci	if (!ascb)              \
928c2ecf20Sopenharmony_ci		return -ENOMEM; \
938c2ecf20Sopenharmony_ci                                \
948c2ecf20Sopenharmony_ci	ascb->completion = &completion; \
958c2ecf20Sopenharmony_ci	ascb->uldd_task = &tcs; \
968c2ecf20Sopenharmony_ci	scb = ascb->scb;        \
978c2ecf20Sopenharmony_ci	scb->header.opcode = CLEAR_NEXUS
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define CLEAR_NEXUS_POST        \
1008c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: POST\n", __func__); \
1018c2ecf20Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
1028c2ecf20Sopenharmony_ci				   asd_clear_nexus_timedout);              \
1038c2ecf20Sopenharmony_ci	if (res)                \
1048c2ecf20Sopenharmony_ci		goto out_err;   \
1058c2ecf20Sopenharmony_ci	ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __func__); \
1068c2ecf20Sopenharmony_ci	wait_for_completion(&completion); \
1078c2ecf20Sopenharmony_ci	res = tcs.dl_opcode; \
1088c2ecf20Sopenharmony_ci	if (res == TC_NO_ERROR) \
1098c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;   \
1108c2ecf20Sopenharmony_ci	return res; \
1118c2ecf20Sopenharmony_ciout_err:                        \
1128c2ecf20Sopenharmony_ci	asd_ascb_free(ascb);    \
1138c2ecf20Sopenharmony_ci	return res
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
1208c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_ADAPTER;
1218c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciint asd_clear_nexus_port(struct asd_sas_port *port)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = port->ha->lldd_ha;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
1298c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_PORT;
1308c2ecf20Sopenharmony_ci	scb->clear_nexus.conn_mask = port->phy_mask;
1318c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cienum clear_nexus_phase {
1358c2ecf20Sopenharmony_ci	NEXUS_PHASE_PRE,
1368c2ecf20Sopenharmony_ci	NEXUS_PHASE_POST,
1378c2ecf20Sopenharmony_ci	NEXUS_PHASE_RESUME,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int asd_clear_nexus_I_T(struct domain_device *dev,
1418c2ecf20Sopenharmony_ci			       enum clear_nexus_phase phase)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
1468c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_I_T;
1478c2ecf20Sopenharmony_ci	switch (phase) {
1488c2ecf20Sopenharmony_ci	case NEXUS_PHASE_PRE:
1498c2ecf20Sopenharmony_ci		scb->clear_nexus.flags = EXEC_Q | SUSPEND_TX;
1508c2ecf20Sopenharmony_ci		break;
1518c2ecf20Sopenharmony_ci	case NEXUS_PHASE_POST:
1528c2ecf20Sopenharmony_ci		scb->clear_nexus.flags = SEND_Q | NOTINQ;
1538c2ecf20Sopenharmony_ci		break;
1548c2ecf20Sopenharmony_ci	case NEXUS_PHASE_RESUME:
1558c2ecf20Sopenharmony_ci		scb->clear_nexus.flags = RESUME_TX;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
1588c2ecf20Sopenharmony_ci						   dev->lldd_dev);
1598c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciint asd_I_T_nexus_reset(struct domain_device *dev)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int res, tmp_res, i;
1658c2ecf20Sopenharmony_ci	struct sas_phy *phy = sas_get_local_phy(dev);
1668c2ecf20Sopenharmony_ci	/* Standard mandates link reset for ATA  (type 0) and
1678c2ecf20Sopenharmony_ci	 * hard reset for SSP (type 1) */
1688c2ecf20Sopenharmony_ci	int reset_type = (dev->dev_type == SAS_SATA_DEV ||
1698c2ecf20Sopenharmony_ci			  (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE);
1728c2ecf20Sopenharmony_ci	/* send a hard reset */
1738c2ecf20Sopenharmony_ci	ASD_DPRINTK("sending %s reset to %s\n",
1748c2ecf20Sopenharmony_ci		    reset_type ? "hard" : "soft", dev_name(&phy->dev));
1758c2ecf20Sopenharmony_ci	res = sas_phy_reset(phy, reset_type);
1768c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) {
1778c2ecf20Sopenharmony_ci		/* wait for the maximum settle time */
1788c2ecf20Sopenharmony_ci		msleep(500);
1798c2ecf20Sopenharmony_ci		/* clear all outstanding commands (keep nexus suspended) */
1808c2ecf20Sopenharmony_ci		asd_clear_nexus_I_T(dev, NEXUS_PHASE_POST);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	for (i = 0 ; i < 3; i++) {
1838c2ecf20Sopenharmony_ci		tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
1848c2ecf20Sopenharmony_ci		if (tmp_res == TC_RESUME)
1858c2ecf20Sopenharmony_ci			goto out;
1868c2ecf20Sopenharmony_ci		msleep(500);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* This is a bit of a problem:  the sequencer is still suspended
1908c2ecf20Sopenharmony_ci	 * and is refusing to resume.  Hope it will resume on a bigger hammer
1918c2ecf20Sopenharmony_ci	 * or the disk is lost */
1928c2ecf20Sopenharmony_ci	dev_printk(KERN_ERR, &phy->dev,
1938c2ecf20Sopenharmony_ci		   "Failed to resume nexus after reset 0x%x\n", tmp_res);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	res = TMF_RESP_FUNC_FAILED;
1968c2ecf20Sopenharmony_ci out:
1978c2ecf20Sopenharmony_ci	sas_put_local_phy(phy);
1988c2ecf20Sopenharmony_ci	return res;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
2068c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_I_T_L;
2078c2ecf20Sopenharmony_ci	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
2088c2ecf20Sopenharmony_ci	memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);
2098c2ecf20Sopenharmony_ci	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
2108c2ecf20Sopenharmony_ci						   dev->lldd_dev);
2118c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int asd_clear_nexus_tag(struct sas_task *task)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
2178c2ecf20Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
2208c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_TAG;
2218c2ecf20Sopenharmony_ci	memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);
2228c2ecf20Sopenharmony_ci	scb->clear_nexus.ssp_task.tag = tascb->tag;
2238c2ecf20Sopenharmony_ci	if (task->dev->tproto)
2248c2ecf20Sopenharmony_ci		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
2258c2ecf20Sopenharmony_ci							  task->dev->lldd_dev);
2268c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int asd_clear_nexus_index(struct sas_task *task)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
2328c2ecf20Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	CLEAR_NEXUS_PRE;
2358c2ecf20Sopenharmony_ci	scb->clear_nexus.nexus = NEXUS_TRANS_CX;
2368c2ecf20Sopenharmony_ci	if (task->dev->tproto)
2378c2ecf20Sopenharmony_ci		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
2388c2ecf20Sopenharmony_ci							  task->dev->lldd_dev);
2398c2ecf20Sopenharmony_ci	scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);
2408c2ecf20Sopenharmony_ci	CLEAR_NEXUS_POST;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/* ---------- TMFs ---------- */
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void asd_tmf_timedout(struct timer_list *t)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct asd_ascb *ascb = from_timer(ascb, t, timer);
2488c2ecf20Sopenharmony_ci	struct tasklet_completion_status *tcs = ascb->uldd_task;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ASD_DPRINTK("tmf timed out\n");
2518c2ecf20Sopenharmony_ci	tcs->tmf_state = TMF_RESP_FUNC_FAILED;
2528c2ecf20Sopenharmony_ci	complete(ascb->completion);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
2568c2ecf20Sopenharmony_ci				    struct done_list_struct *dl)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
2598c2ecf20Sopenharmony_ci	unsigned long flags;
2608c2ecf20Sopenharmony_ci	struct tc_resp_sb_struct {
2618c2ecf20Sopenharmony_ci		__le16 index_escb;
2628c2ecf20Sopenharmony_ci		u8     len_lsb;
2638c2ecf20Sopenharmony_ci		u8     flags;
2648c2ecf20Sopenharmony_ci	} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
2678c2ecf20Sopenharmony_ci	struct asd_ascb *escb;
2688c2ecf20Sopenharmony_ci	struct asd_dma_tok *edb;
2698c2ecf20Sopenharmony_ci	struct ssp_frame_hdr *fh;
2708c2ecf20Sopenharmony_ci	struct ssp_response_iu   *ru;
2718c2ecf20Sopenharmony_ci	int res = TMF_RESP_FUNC_FAILED;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ASD_DPRINTK("tmf resp tasklet\n");
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
2768c2ecf20Sopenharmony_ci	escb = asd_tc_index_find(&asd_ha->seq,
2778c2ecf20Sopenharmony_ci				 (int)le16_to_cpu(resp_sb->index_escb));
2788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (!escb) {
2818c2ecf20Sopenharmony_ci		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
2828c2ecf20Sopenharmony_ci		return res;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
2868c2ecf20Sopenharmony_ci	ascb->tag = *(__be16 *)(edb->vaddr+4);
2878c2ecf20Sopenharmony_ci	fh = edb->vaddr + 16;
2888c2ecf20Sopenharmony_ci	ru = edb->vaddr + 16 + sizeof(*fh);
2898c2ecf20Sopenharmony_ci	res = ru->status;
2908c2ecf20Sopenharmony_ci	if (ru->datapres == 1)	  /* Response data present */
2918c2ecf20Sopenharmony_ci		res = ru->resp_data[3];
2928c2ecf20Sopenharmony_ci#if 0
2938c2ecf20Sopenharmony_ci	ascb->tag = fh->tag;
2948c2ecf20Sopenharmony_ci#endif
2958c2ecf20Sopenharmony_ci	ascb->tag_valid = 1;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	asd_invalidate_edb(escb, edb_id);
2988c2ecf20Sopenharmony_ci	return res;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
3028c2ecf20Sopenharmony_ci				     struct done_list_struct *dl)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct tasklet_completion_status *tcs;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (!del_timer(&ascb->timer))
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	tcs = ascb->uldd_task;
3108c2ecf20Sopenharmony_ci	ASD_DPRINTK("tmf tasklet complete\n");
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	tcs->dl_opcode = dl->opcode;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (dl->opcode == TC_SSP_RESP) {
3158c2ecf20Sopenharmony_ci		tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl);
3168c2ecf20Sopenharmony_ci		tcs->tag_valid = ascb->tag_valid;
3178c2ecf20Sopenharmony_ci		tcs->tag = ascb->tag;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	complete(ascb->completion);
3218c2ecf20Sopenharmony_ci	asd_ascb_free(ascb);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int asd_clear_nexus(struct sas_task *task)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int res = TMF_RESP_FUNC_FAILED;
3278c2ecf20Sopenharmony_ci	int leftover;
3288c2ecf20Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
3298c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
3308c2ecf20Sopenharmony_ci	unsigned long flags;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	tascb->completion = &completion;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	ASD_DPRINTK("task not done, clearing nexus\n");
3358c2ecf20Sopenharmony_ci	if (tascb->tag_valid)
3368c2ecf20Sopenharmony_ci		res = asd_clear_nexus_tag(task);
3378c2ecf20Sopenharmony_ci	else
3388c2ecf20Sopenharmony_ci		res = asd_clear_nexus_index(task);
3398c2ecf20Sopenharmony_ci	leftover = wait_for_completion_timeout(&completion,
3408c2ecf20Sopenharmony_ci					       AIC94XX_SCB_TIMEOUT);
3418c2ecf20Sopenharmony_ci	tascb->completion = NULL;
3428c2ecf20Sopenharmony_ci	ASD_DPRINTK("came back from clear nexus\n");
3438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
3448c2ecf20Sopenharmony_ci	if (leftover < 1)
3458c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
3468c2ecf20Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE)
3478c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
3488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return res;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * asd_abort_task -- ABORT TASK TMF
3558c2ecf20Sopenharmony_ci * @task: the task to be aborted
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Before calling ABORT TASK the task state flags should be ORed with
3588c2ecf20Sopenharmony_ci * SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under
3598c2ecf20Sopenharmony_ci * the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.
3608c2ecf20Sopenharmony_ci *
3618c2ecf20Sopenharmony_ci * Implements the ABORT TASK TMF, I_T_L_Q nexus.
3628c2ecf20Sopenharmony_ci * Returns: SAS TMF responses (see sas_task.h),
3638c2ecf20Sopenharmony_ci *          -ENOMEM,
3648c2ecf20Sopenharmony_ci *          -SAS_QUEUE_FULL.
3658c2ecf20Sopenharmony_ci *
3668c2ecf20Sopenharmony_ci * When ABORT TASK returns, the caller of ABORT TASK checks first the
3678c2ecf20Sopenharmony_ci * task->task_state_flags, and then the return value of ABORT TASK.
3688c2ecf20Sopenharmony_ci *
3698c2ecf20Sopenharmony_ci * If the task has task state bit SAS_TASK_STATE_DONE set, then the
3708c2ecf20Sopenharmony_ci * task was completed successfully prior to it being aborted.  The
3718c2ecf20Sopenharmony_ci * caller of ABORT TASK has responsibility to call task->task_done()
3728c2ecf20Sopenharmony_ci * xor free the task, depending on their framework.  The return code
3738c2ecf20Sopenharmony_ci * is TMF_RESP_FUNC_FAILED in this case.
3748c2ecf20Sopenharmony_ci *
3758c2ecf20Sopenharmony_ci * Else the SAS_TASK_STATE_DONE bit is not set,
3768c2ecf20Sopenharmony_ci * 	If the return code is TMF_RESP_FUNC_COMPLETE, then
3778c2ecf20Sopenharmony_ci * 		the task was aborted successfully.  The caller of
3788c2ecf20Sopenharmony_ci * 		ABORT TASK has responsibility to call task->task_done()
3798c2ecf20Sopenharmony_ci *              to finish the task, xor free the task depending on their
3808c2ecf20Sopenharmony_ci *		framework.
3818c2ecf20Sopenharmony_ci *	else
3828c2ecf20Sopenharmony_ci * 		the ABORT TASK returned some kind of error. The task
3838c2ecf20Sopenharmony_ci *              was _not_ cancelled.  Nothing can be assumed.
3848c2ecf20Sopenharmony_ci *		The caller of ABORT TASK may wish to retry.
3858c2ecf20Sopenharmony_ci */
3868c2ecf20Sopenharmony_ciint asd_abort_task(struct sas_task *task)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct asd_ascb *tascb = task->lldd_task;
3898c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = tascb->ha;
3908c2ecf20Sopenharmony_ci	int res = 1;
3918c2ecf20Sopenharmony_ci	unsigned long flags;
3928c2ecf20Sopenharmony_ci	struct asd_ascb *ascb = NULL;
3938c2ecf20Sopenharmony_ci	struct scb *scb;
3948c2ecf20Sopenharmony_ci	int leftover;
3958c2ecf20Sopenharmony_ci	DECLARE_TCS(tcs);
3968c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
3978c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(tascb_completion);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	tascb->completion = &tascb_completion;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
4028c2ecf20Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
4038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&task->task_state_lock, flags);
4048c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
4058c2ecf20Sopenharmony_ci		ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
4068c2ecf20Sopenharmony_ci		goto out_done;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
4118c2ecf20Sopenharmony_ci	if (!ascb)
4128c2ecf20Sopenharmony_ci		return -ENOMEM;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ascb->uldd_task = &tcs;
4158c2ecf20Sopenharmony_ci	ascb->completion = &completion;
4168c2ecf20Sopenharmony_ci	scb = ascb->scb;
4178c2ecf20Sopenharmony_ci	scb->header.opcode = SCB_ABORT_TASK;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (task->task_proto) {
4208c2ecf20Sopenharmony_ci	case SAS_PROTOCOL_SATA:
4218c2ecf20Sopenharmony_ci	case SAS_PROTOCOL_STP:
4228c2ecf20Sopenharmony_ci		scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	case SAS_PROTOCOL_SSP:
4258c2ecf20Sopenharmony_ci		scb->abort_task.proto_conn_rate  = (1 << 4); /* SSP */
4268c2ecf20Sopenharmony_ci		scb->abort_task.proto_conn_rate |= task->dev->linkrate;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case SAS_PROTOCOL_SMP:
4298c2ecf20Sopenharmony_ci		break;
4308c2ecf20Sopenharmony_ci	default:
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (task->task_proto == SAS_PROTOCOL_SSP) {
4358c2ecf20Sopenharmony_ci		scb->abort_task.ssp_frame.frame_type = SSP_TASK;
4368c2ecf20Sopenharmony_ci		memcpy(scb->abort_task.ssp_frame.hashed_dest_addr,
4378c2ecf20Sopenharmony_ci		       task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
4388c2ecf20Sopenharmony_ci		memcpy(scb->abort_task.ssp_frame.hashed_src_addr,
4398c2ecf20Sopenharmony_ci		       task->dev->port->ha->hashed_sas_addr,
4408c2ecf20Sopenharmony_ci		       HASHED_SAS_ADDR_SIZE);
4418c2ecf20Sopenharmony_ci		scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8);
4448c2ecf20Sopenharmony_ci		scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK;
4458c2ecf20Sopenharmony_ci		scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF);
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	scb->abort_task.sister_scb = cpu_to_le16(0xFFFF);
4498c2ecf20Sopenharmony_ci	scb->abort_task.conn_handle = cpu_to_le16(
4508c2ecf20Sopenharmony_ci		(u16)(unsigned long)task->dev->lldd_dev);
4518c2ecf20Sopenharmony_ci	scb->abort_task.retry_count = 1;
4528c2ecf20Sopenharmony_ci	scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
4538c2ecf20Sopenharmony_ci	scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
4568c2ecf20Sopenharmony_ci				   asd_tmf_timedout);
4578c2ecf20Sopenharmony_ci	if (res)
4588c2ecf20Sopenharmony_ci		goto out_free;
4598c2ecf20Sopenharmony_ci	wait_for_completion(&completion);
4608c2ecf20Sopenharmony_ci	ASD_DPRINTK("tmf came back\n");
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	tascb->tag = tcs.tag;
4638c2ecf20Sopenharmony_ci	tascb->tag_valid = tcs.tag_valid;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&task->task_state_lock, flags);
4668c2ecf20Sopenharmony_ci	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
4678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&task->task_state_lock, flags);
4688c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
4698c2ecf20Sopenharmony_ci		ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
4708c2ecf20Sopenharmony_ci		goto out_done;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&task->task_state_lock, flags);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (tcs.dl_opcode == TC_SSP_RESP) {
4758c2ecf20Sopenharmony_ci		/* The task to be aborted has been sent to the device.
4768c2ecf20Sopenharmony_ci		 * We got a Response IU for the ABORT TASK TMF. */
4778c2ecf20Sopenharmony_ci		if (tcs.tmf_state == TMF_RESP_FUNC_COMPLETE)
4788c2ecf20Sopenharmony_ci			res = asd_clear_nexus(task);
4798c2ecf20Sopenharmony_ci		else
4808c2ecf20Sopenharmony_ci			res = tcs.tmf_state;
4818c2ecf20Sopenharmony_ci	} else if (tcs.dl_opcode == TC_NO_ERROR &&
4828c2ecf20Sopenharmony_ci		   tcs.tmf_state == TMF_RESP_FUNC_FAILED) {
4838c2ecf20Sopenharmony_ci		/* timeout */
4848c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
4858c2ecf20Sopenharmony_ci	} else {
4868c2ecf20Sopenharmony_ci		/* In the following we assume that the managing layer
4878c2ecf20Sopenharmony_ci		 * will _never_ make a mistake, when issuing ABORT
4888c2ecf20Sopenharmony_ci		 * TASK.
4898c2ecf20Sopenharmony_ci		 */
4908c2ecf20Sopenharmony_ci		switch (tcs.dl_opcode) {
4918c2ecf20Sopenharmony_ci		default:
4928c2ecf20Sopenharmony_ci			res = asd_clear_nexus(task);
4938c2ecf20Sopenharmony_ci			fallthrough;
4948c2ecf20Sopenharmony_ci		case TC_NO_ERROR:
4958c2ecf20Sopenharmony_ci			break;
4968c2ecf20Sopenharmony_ci			/* The task hasn't been sent to the device xor
4978c2ecf20Sopenharmony_ci			 * we never got a (sane) Response IU for the
4988c2ecf20Sopenharmony_ci			 * ABORT TASK TMF.
4998c2ecf20Sopenharmony_ci			 */
5008c2ecf20Sopenharmony_ci		case TF_NAK_RECV:
5018c2ecf20Sopenharmony_ci			res = TMF_RESP_INVALID_FRAME;
5028c2ecf20Sopenharmony_ci			break;
5038c2ecf20Sopenharmony_ci		case TF_TMF_TASK_DONE:	/* done but not reported yet */
5048c2ecf20Sopenharmony_ci			res = TMF_RESP_FUNC_FAILED;
5058c2ecf20Sopenharmony_ci			leftover =
5068c2ecf20Sopenharmony_ci				wait_for_completion_timeout(&tascb_completion,
5078c2ecf20Sopenharmony_ci							  AIC94XX_SCB_TIMEOUT);
5088c2ecf20Sopenharmony_ci			spin_lock_irqsave(&task->task_state_lock, flags);
5098c2ecf20Sopenharmony_ci			if (leftover < 1)
5108c2ecf20Sopenharmony_ci				res = TMF_RESP_FUNC_FAILED;
5118c2ecf20Sopenharmony_ci			if (task->task_state_flags & SAS_TASK_STATE_DONE)
5128c2ecf20Sopenharmony_ci				res = TMF_RESP_FUNC_COMPLETE;
5138c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&task->task_state_lock, flags);
5148c2ecf20Sopenharmony_ci			break;
5158c2ecf20Sopenharmony_ci		case TF_TMF_NO_TAG:
5168c2ecf20Sopenharmony_ci		case TF_TMF_TAG_FREE: /* the tag is in the free list */
5178c2ecf20Sopenharmony_ci		case TF_TMF_NO_CONN_HANDLE: /* no such device */
5188c2ecf20Sopenharmony_ci			res = TMF_RESP_FUNC_COMPLETE;
5198c2ecf20Sopenharmony_ci			break;
5208c2ecf20Sopenharmony_ci		case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */
5218c2ecf20Sopenharmony_ci			res = TMF_RESP_FUNC_ESUPP;
5228c2ecf20Sopenharmony_ci			break;
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci out_done:
5268c2ecf20Sopenharmony_ci	tascb->completion = NULL;
5278c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE) {
5288c2ecf20Sopenharmony_ci		task->lldd_task = NULL;
5298c2ecf20Sopenharmony_ci		mb();
5308c2ecf20Sopenharmony_ci		asd_ascb_free(tascb);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
5338c2ecf20Sopenharmony_ci	return res;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci out_free:
5368c2ecf20Sopenharmony_ci	asd_ascb_free(ascb);
5378c2ecf20Sopenharmony_ci	ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
5388c2ecf20Sopenharmony_ci	return res;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/**
5428c2ecf20Sopenharmony_ci * asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus
5438c2ecf20Sopenharmony_ci * @dev: pointer to struct domain_device of interest
5448c2ecf20Sopenharmony_ci * @lun: pointer to u8[8] which is the LUN
5458c2ecf20Sopenharmony_ci * @tmf: the TMF to be performed (see sas_task.h or the SAS spec)
5468c2ecf20Sopenharmony_ci * @index: the transaction context of the task to be queried if QT TMF
5478c2ecf20Sopenharmony_ci *
5488c2ecf20Sopenharmony_ci * This function is used to send ABORT TASK SET, CLEAR ACA,
5498c2ecf20Sopenharmony_ci * CLEAR TASK SET, LU RESET and QUERY TASK TMFs.
5508c2ecf20Sopenharmony_ci *
5518c2ecf20Sopenharmony_ci * No SCBs should be queued to the I_T_L nexus when this SCB is
5528c2ecf20Sopenharmony_ci * pending.
5538c2ecf20Sopenharmony_ci *
5548c2ecf20Sopenharmony_ci * Returns: TMF response code (see sas_task.h or the SAS spec)
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_cistatic int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
5578c2ecf20Sopenharmony_ci				int tmf, int index)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
5608c2ecf20Sopenharmony_ci	struct asd_ascb *ascb;
5618c2ecf20Sopenharmony_ci	int res = 1;
5628c2ecf20Sopenharmony_ci	struct scb *scb;
5638c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(completion);
5648c2ecf20Sopenharmony_ci	DECLARE_TCS(tcs);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (!(dev->tproto & SAS_PROTOCOL_SSP))
5678c2ecf20Sopenharmony_ci		return TMF_RESP_FUNC_ESUPP;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
5708c2ecf20Sopenharmony_ci	if (!ascb)
5718c2ecf20Sopenharmony_ci		return -ENOMEM;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	ascb->completion = &completion;
5748c2ecf20Sopenharmony_ci	ascb->uldd_task = &tcs;
5758c2ecf20Sopenharmony_ci	scb = ascb->scb;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (tmf == TMF_QUERY_TASK)
5788c2ecf20Sopenharmony_ci		scb->header.opcode = QUERY_SSP_TASK;
5798c2ecf20Sopenharmony_ci	else
5808c2ecf20Sopenharmony_ci		scb->header.opcode = INITIATE_SSP_TMF;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	scb->ssp_tmf.proto_conn_rate  = (1 << 4); /* SSP */
5838c2ecf20Sopenharmony_ci	scb->ssp_tmf.proto_conn_rate |= dev->linkrate;
5848c2ecf20Sopenharmony_ci	/* SSP frame header */
5858c2ecf20Sopenharmony_ci	scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK;
5868c2ecf20Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr,
5878c2ecf20Sopenharmony_ci	       dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
5888c2ecf20Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr,
5898c2ecf20Sopenharmony_ci	       dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
5908c2ecf20Sopenharmony_ci	scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF);
5918c2ecf20Sopenharmony_ci	/* SSP Task IU */
5928c2ecf20Sopenharmony_ci	memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8);
5938c2ecf20Sopenharmony_ci	scb->ssp_tmf.ssp_task.tmf = tmf;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF);
5968c2ecf20Sopenharmony_ci	scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long)
5978c2ecf20Sopenharmony_ci					      dev->lldd_dev);
5988c2ecf20Sopenharmony_ci	scb->ssp_tmf.retry_count = 1;
5998c2ecf20Sopenharmony_ci	scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
6008c2ecf20Sopenharmony_ci	if (tmf == TMF_QUERY_TASK)
6018c2ecf20Sopenharmony_ci		scb->ssp_tmf.index = cpu_to_le16(index);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
6048c2ecf20Sopenharmony_ci				   asd_tmf_timedout);
6058c2ecf20Sopenharmony_ci	if (res)
6068c2ecf20Sopenharmony_ci		goto out_err;
6078c2ecf20Sopenharmony_ci	wait_for_completion(&completion);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	switch (tcs.dl_opcode) {
6108c2ecf20Sopenharmony_ci	case TC_NO_ERROR:
6118c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	case TF_NAK_RECV:
6148c2ecf20Sopenharmony_ci		res = TMF_RESP_INVALID_FRAME;
6158c2ecf20Sopenharmony_ci		break;
6168c2ecf20Sopenharmony_ci	case TF_TMF_TASK_DONE:
6178c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_FAILED;
6188c2ecf20Sopenharmony_ci		break;
6198c2ecf20Sopenharmony_ci	case TF_TMF_NO_TAG:
6208c2ecf20Sopenharmony_ci	case TF_TMF_TAG_FREE: /* the tag is in the free list */
6218c2ecf20Sopenharmony_ci	case TF_TMF_NO_CONN_HANDLE: /* no such device */
6228c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_COMPLETE;
6238c2ecf20Sopenharmony_ci		break;
6248c2ecf20Sopenharmony_ci	case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */
6258c2ecf20Sopenharmony_ci		res = TMF_RESP_FUNC_ESUPP;
6268c2ecf20Sopenharmony_ci		break;
6278c2ecf20Sopenharmony_ci	default:
6288c2ecf20Sopenharmony_ci		/* Allow TMF response codes to propagate upwards */
6298c2ecf20Sopenharmony_ci		res = tcs.dl_opcode;
6308c2ecf20Sopenharmony_ci		break;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci	return res;
6338c2ecf20Sopenharmony_ciout_err:
6348c2ecf20Sopenharmony_ci	asd_ascb_free(ascb);
6358c2ecf20Sopenharmony_ci	return res;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciint asd_abort_task_set(struct domain_device *dev, u8 *lun)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
6438c2ecf20Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
6448c2ecf20Sopenharmony_ci	return res;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ciint asd_clear_aca(struct domain_device *dev, u8 *lun)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
6528c2ecf20Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
6538c2ecf20Sopenharmony_ci	return res;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ciint asd_clear_task_set(struct domain_device *dev, u8 *lun)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
6618c2ecf20Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
6628c2ecf20Sopenharmony_ci	return res;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ciint asd_lu_reset(struct domain_device *dev, u8 *lun)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (res == TMF_RESP_FUNC_COMPLETE)
6708c2ecf20Sopenharmony_ci		asd_clear_nexus_I_T_L(dev, lun);
6718c2ecf20Sopenharmony_ci	return res;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/**
6758c2ecf20Sopenharmony_ci * asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
6768c2ecf20Sopenharmony_ci * @task: pointer to sas_task struct of interest
6778c2ecf20Sopenharmony_ci *
6788c2ecf20Sopenharmony_ci * Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
6798c2ecf20Sopenharmony_ci * or TMF_RESP_FUNC_SUCC if the task is in the task set.
6808c2ecf20Sopenharmony_ci *
6818c2ecf20Sopenharmony_ci * Normally the management layer sets the task to aborted state,
6828c2ecf20Sopenharmony_ci * and then calls query task and then abort task.
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_ciint asd_query_task(struct sas_task *task)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct asd_ascb *ascb = task->lldd_task;
6878c2ecf20Sopenharmony_ci	int index;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (ascb) {
6908c2ecf20Sopenharmony_ci		index = ascb->tc_index;
6918c2ecf20Sopenharmony_ci		return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN,
6928c2ecf20Sopenharmony_ci					    TMF_QUERY_TASK, index);
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci	return TMF_RESP_FUNC_COMPLETE;
6958c2ecf20Sopenharmony_ci}
696