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