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