| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Support for SATA devices on Serial Attached SCSI (SAS) controllers | 
 | 3 |  * | 
 | 4 |  * Copyright (C) 2006 IBM Corporation | 
 | 5 |  * | 
 | 6 |  * Written by: Darrick J. Wong <djwong@us.ibm.com>, IBM Corporation | 
 | 7 |  * | 
 | 8 |  * This program is free software; you can redistribute it and/or | 
 | 9 |  * modify it under the terms of the GNU General Public License as | 
 | 10 |  * published by the Free Software Foundation; either version 2 of the | 
 | 11 |  * License, or (at your option) any later version. | 
 | 12 |  * | 
 | 13 |  * This program is distributed in the hope that it will be useful, but | 
 | 14 |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 15 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 16 |  * General Public License for more details. | 
 | 17 |  * | 
 | 18 |  * You should have received a copy of the GNU General Public License | 
 | 19 |  * along with this program; if not, write to the Free Software | 
 | 20 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | 
 | 21 |  * USA | 
 | 22 |  */ | 
 | 23 |  | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 24 | #include <linux/scatterlist.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 25 | #include <linux/slab.h> | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 26 |  | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 27 | #include <scsi/sas_ata.h> | 
 | 28 | #include "sas_internal.h" | 
 | 29 | #include <scsi/scsi_host.h> | 
 | 30 | #include <scsi/scsi_device.h> | 
 | 31 | #include <scsi/scsi_tcq.h> | 
 | 32 | #include <scsi/scsi.h> | 
 | 33 | #include <scsi/scsi_transport.h> | 
 | 34 | #include <scsi/scsi_transport_sas.h> | 
 | 35 | #include "../scsi_sas_internal.h" | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 36 | #include "../scsi_transport_api.h" | 
 | 37 | #include <scsi/scsi_eh.h> | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 38 |  | 
 | 39 | static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) | 
 | 40 | { | 
 | 41 | 	/* Cheesy attempt to translate SAS errors into ATA.  Hah! */ | 
 | 42 |  | 
 | 43 | 	/* transport error */ | 
 | 44 | 	if (ts->resp == SAS_TASK_UNDELIVERED) | 
 | 45 | 		return AC_ERR_ATA_BUS; | 
 | 46 |  | 
 | 47 | 	/* ts->resp == SAS_TASK_COMPLETE */ | 
 | 48 | 	/* task delivered, what happened afterwards? */ | 
 | 49 | 	switch (ts->stat) { | 
 | 50 | 		case SAS_DEV_NO_RESPONSE: | 
 | 51 | 			return AC_ERR_TIMEOUT; | 
 | 52 |  | 
 | 53 | 		case SAS_INTERRUPTED: | 
 | 54 | 		case SAS_PHY_DOWN: | 
 | 55 | 		case SAS_NAK_R_ERR: | 
 | 56 | 			return AC_ERR_ATA_BUS; | 
 | 57 |  | 
 | 58 |  | 
 | 59 | 		case SAS_DATA_UNDERRUN: | 
 | 60 | 			/* | 
 | 61 | 			 * Some programs that use the taskfile interface | 
 | 62 | 			 * (smartctl in particular) can cause underrun | 
 | 63 | 			 * problems.  Ignore these errors, perhaps at our | 
 | 64 | 			 * peril. | 
 | 65 | 			 */ | 
 | 66 | 			return 0; | 
 | 67 |  | 
 | 68 | 		case SAS_DATA_OVERRUN: | 
 | 69 | 		case SAS_QUEUE_FULL: | 
 | 70 | 		case SAS_DEVICE_UNKNOWN: | 
 | 71 | 		case SAS_SG_ERR: | 
 | 72 | 			return AC_ERR_INVALID; | 
 | 73 |  | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 74 | 		case SAS_OPEN_TO: | 
 | 75 | 		case SAS_OPEN_REJECT: | 
 | 76 | 			SAS_DPRINTK("%s: Saw error %d.  What to do?\n", | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 77 | 				    __func__, ts->stat); | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 78 | 			return AC_ERR_OTHER; | 
 | 79 |  | 
| James Bottomley | 75c0b38 | 2011-01-23 08:16:24 -0600 | [diff] [blame] | 80 | 		case SAM_STAT_CHECK_CONDITION: | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 81 | 		case SAS_ABORTED_TASK: | 
 | 82 | 			return AC_ERR_DEV; | 
 | 83 |  | 
 | 84 | 		case SAS_PROTO_RESPONSE: | 
 | 85 | 			/* This means the ending_fis has the error | 
 | 86 | 			 * value; return 0 here to collect it */ | 
 | 87 | 			return 0; | 
 | 88 | 		default: | 
 | 89 | 			return 0; | 
 | 90 | 	} | 
 | 91 | } | 
 | 92 |  | 
 | 93 | static void sas_ata_task_done(struct sas_task *task) | 
 | 94 | { | 
 | 95 | 	struct ata_queued_cmd *qc = task->uldd_task; | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 96 | 	struct domain_device *dev; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 97 | 	struct task_status_struct *stat = &task->task_status; | 
 | 98 | 	struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 99 | 	struct sas_ha_struct *sas_ha; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 100 | 	enum ata_completion_errors ac; | 
| Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 101 | 	unsigned long flags; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 102 |  | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 103 | 	if (!qc) | 
 | 104 | 		goto qc_already_gone; | 
 | 105 |  | 
 | 106 | 	dev = qc->ap->private_data; | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 107 | 	sas_ha = dev->port->ha; | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 108 |  | 
| Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 109 | 	spin_lock_irqsave(dev->sata_dev.ap->lock, flags); | 
| James Bottomley | 75c0b38 | 2011-01-23 08:16:24 -0600 | [diff] [blame] | 110 | 	if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || | 
 | 111 | 	    ((stat->stat == SAM_STAT_CHECK_CONDITION && | 
 | 112 | 	      dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 113 | 		ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); | 
 | 114 | 		qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); | 
 | 115 | 		dev->sata_dev.sstatus = resp->sstatus; | 
 | 116 | 		dev->sata_dev.serror = resp->serror; | 
 | 117 | 		dev->sata_dev.scontrol = resp->scontrol; | 
| James Bottomley | 75c0b38 | 2011-01-23 08:16:24 -0600 | [diff] [blame] | 118 | 	} else { | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 119 | 		ac = sas_to_ata_err(stat); | 
 | 120 | 		if (ac) { | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 121 | 			SAS_DPRINTK("%s: SAS error %x\n", __func__, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 122 | 				    stat->stat); | 
 | 123 | 			/* We saw a SAS error. Send a vague error. */ | 
 | 124 | 			qc->err_mask = ac; | 
 | 125 | 			dev->sata_dev.tf.feature = 0x04; /* status err */ | 
 | 126 | 			dev->sata_dev.tf.command = ATA_ERR; | 
 | 127 | 		} | 
 | 128 | 	} | 
 | 129 |  | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 130 | 	qc->lldd_task = NULL; | 
| Darrick J. Wong | fe059f1 | 2007-01-30 01:18:55 -0800 | [diff] [blame] | 131 | 	if (qc->scsicmd) | 
 | 132 | 		ASSIGN_SAS_TASK(qc->scsicmd, NULL); | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 133 | 	ata_qc_complete(qc); | 
| Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 134 | 	spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); | 
 | 135 |  | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 136 | 	/* | 
 | 137 | 	 * If the sas_task has an ata qc, a scsi_cmnd and the aborted | 
 | 138 | 	 * flag is set, then we must have come in via the libsas EH | 
 | 139 | 	 * functions.  When we exit this function, we need to put the | 
 | 140 | 	 * scsi_cmnd on the list of finished errors.  The ata_qc_complete | 
 | 141 | 	 * call cleans up the libata side of things but we're protected | 
 | 142 | 	 * from the scsi_cmnd going away because the scsi_cmnd is owned | 
 | 143 | 	 * by the EH, making libata's call to scsi_done a NOP. | 
 | 144 | 	 */ | 
 | 145 | 	spin_lock_irqsave(&task->task_state_lock, flags); | 
 | 146 | 	if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED) | 
 | 147 | 		scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q); | 
 | 148 | 	spin_unlock_irqrestore(&task->task_state_lock, flags); | 
 | 149 |  | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 150 | qc_already_gone: | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 151 | 	list_del_init(&task->list); | 
 | 152 | 	sas_free_task(task); | 
 | 153 | } | 
 | 154 |  | 
 | 155 | static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) | 
 | 156 | { | 
| Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 157 | 	int res; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 158 | 	struct sas_task *task; | 
 | 159 | 	struct domain_device *dev = qc->ap->private_data; | 
 | 160 | 	struct sas_ha_struct *sas_ha = dev->port->ha; | 
 | 161 | 	struct Scsi_Host *host = sas_ha->core.shost; | 
 | 162 | 	struct sas_internal *i = to_sas_internal(host->transportt); | 
 | 163 | 	struct scatterlist *sg; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 164 | 	unsigned int xfer = 0; | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 165 | 	unsigned int si; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 166 |  | 
| Darrick J. Wong | 56dd2c0 | 2010-10-01 13:55:47 -0700 | [diff] [blame] | 167 | 	/* If the device fell off, no sense in issuing commands */ | 
 | 168 | 	if (dev->gone) | 
 | 169 | 		return AC_ERR_SYSTEM; | 
 | 170 |  | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 171 | 	task = sas_alloc_task(GFP_ATOMIC); | 
 | 172 | 	if (!task) | 
| Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 173 | 		return AC_ERR_SYSTEM; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 174 | 	task->dev = dev; | 
 | 175 | 	task->task_proto = SAS_PROTOCOL_STP; | 
 | 176 | 	task->task_done = sas_ata_task_done; | 
 | 177 |  | 
 | 178 | 	if (qc->tf.command == ATA_CMD_FPDMA_WRITE || | 
 | 179 | 	    qc->tf.command == ATA_CMD_FPDMA_READ) { | 
 | 180 | 		/* Need to zero out the tag libata assigned us */ | 
 | 181 | 		qc->tf.nsect = 0; | 
 | 182 | 	} | 
 | 183 |  | 
| James Bottomley | 110dd8f | 2007-07-20 13:11:44 -0500 | [diff] [blame] | 184 | 	ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis); | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 185 | 	task->uldd_task = qc; | 
| Tejun Heo | 405e66b | 2007-11-27 19:28:53 +0900 | [diff] [blame] | 186 | 	if (ata_is_atapi(qc->tf.protocol)) { | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 187 | 		memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); | 
| James Bottomley | dde2020 | 2008-02-19 11:36:56 +0100 | [diff] [blame] | 188 | 		task->total_xfer_len = qc->nbytes; | 
 | 189 | 		task->num_scatter = qc->n_elem; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 190 | 	} else { | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 191 | 		for_each_sg(qc->sg, sg, qc->n_elem, si) | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 192 | 			xfer += sg->length; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 193 |  | 
 | 194 | 		task->total_xfer_len = xfer; | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 195 | 		task->num_scatter = si; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 196 | 	} | 
 | 197 |  | 
 | 198 | 	task->data_dir = qc->dma_dir; | 
| Tejun Heo | ff2aeb1 | 2007-12-05 16:43:11 +0900 | [diff] [blame] | 199 | 	task->scatter = qc->sg; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 200 | 	task->ata_task.retry_count = 1; | 
 | 201 | 	task->task_state_flags = SAS_TASK_STATE_PENDING; | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 202 | 	qc->lldd_task = task; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 203 |  | 
 | 204 | 	switch (qc->tf.protocol) { | 
 | 205 | 	case ATA_PROT_NCQ: | 
 | 206 | 		task->ata_task.use_ncq = 1; | 
 | 207 | 		/* fall through */ | 
| Tejun Heo | 0dc3688 | 2007-12-18 16:34:43 -0500 | [diff] [blame] | 208 | 	case ATAPI_PROT_DMA: | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 209 | 	case ATA_PROT_DMA: | 
 | 210 | 		task->ata_task.dma_xfer = 1; | 
 | 211 | 		break; | 
 | 212 | 	} | 
 | 213 |  | 
| Darrick J. Wong | fe059f1 | 2007-01-30 01:18:55 -0800 | [diff] [blame] | 214 | 	if (qc->scsicmd) | 
 | 215 | 		ASSIGN_SAS_TASK(qc->scsicmd, task); | 
 | 216 |  | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 217 | 	if (sas_ha->lldd_max_execute_num < 2) | 
 | 218 | 		res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); | 
 | 219 | 	else | 
 | 220 | 		res = sas_queue_up(task); | 
 | 221 |  | 
 | 222 | 	/* Examine */ | 
 | 223 | 	if (res) { | 
 | 224 | 		SAS_DPRINTK("lldd_execute_task returned: %d\n", res); | 
 | 225 |  | 
| Darrick J. Wong | fe059f1 | 2007-01-30 01:18:55 -0800 | [diff] [blame] | 226 | 		if (qc->scsicmd) | 
 | 227 | 			ASSIGN_SAS_TASK(qc->scsicmd, NULL); | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 228 | 		sas_free_task(task); | 
| Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 229 | 		return AC_ERR_SYSTEM; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 230 | 	} | 
 | 231 |  | 
| Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 232 | 	return 0; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 233 | } | 
 | 234 |  | 
| Tejun Heo | 4c9bf4e | 2008-04-07 22:47:20 +0900 | [diff] [blame] | 235 | static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) | 
 | 236 | { | 
 | 237 | 	struct domain_device *dev = qc->ap->private_data; | 
 | 238 |  | 
 | 239 | 	memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf)); | 
 | 240 | 	return true; | 
 | 241 | } | 
 | 242 |  | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 243 | static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, | 
 | 244 | 			       unsigned long deadline) | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 245 | { | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 246 | 	struct ata_port *ap = link->ap; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 247 | 	struct domain_device *dev = ap->private_data; | 
 | 248 | 	struct sas_internal *i = | 
 | 249 | 		to_sas_internal(dev->port->ha->core.shost->transportt); | 
| James Bottomley | a29c051 | 2008-02-23 23:38:44 -0600 | [diff] [blame] | 250 | 	int res = TMF_RESP_FUNC_FAILED; | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 251 | 	int ret = 0; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 252 |  | 
 | 253 | 	if (i->dft->lldd_I_T_nexus_reset) | 
 | 254 | 		res = i->dft->lldd_I_T_nexus_reset(dev); | 
 | 255 |  | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 256 | 	if (res != TMF_RESP_FUNC_COMPLETE) { | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 257 | 		SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 258 | 		ret = -EAGAIN; | 
 | 259 | 	} | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 260 |  | 
 | 261 | 	switch (dev->sata_dev.command_set) { | 
 | 262 | 		case ATA_COMMAND_SET: | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 263 | 			SAS_DPRINTK("%s: Found ATA device.\n", __func__); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 264 | 			*class = ATA_DEV_ATA; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 265 | 			break; | 
 | 266 | 		case ATAPI_COMMAND_SET: | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 267 | 			SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 268 | 			*class = ATA_DEV_ATAPI; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 269 | 			break; | 
 | 270 | 		default: | 
 | 271 | 			SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", | 
| Harvey Harrison | cadbd4a | 2008-07-03 23:47:27 -0700 | [diff] [blame] | 272 | 				    __func__, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 273 | 				    dev->sata_dev.command_set); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 274 | 			*class = ATA_DEV_UNKNOWN; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 275 | 			break; | 
 | 276 | 	} | 
 | 277 |  | 
 | 278 | 	ap->cbl = ATA_CBL_SATA; | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 279 | 	return ret; | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 280 | } | 
 | 281 |  | 
 | 282 | static void sas_ata_post_internal(struct ata_queued_cmd *qc) | 
 | 283 | { | 
 | 284 | 	if (qc->flags & ATA_QCFLAG_FAILED) | 
 | 285 | 		qc->err_mask |= AC_ERR_OTHER; | 
 | 286 |  | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 287 | 	if (qc->err_mask) { | 
 | 288 | 		/* | 
 | 289 | 		 * Find the sas_task and kill it.  By this point, | 
 | 290 | 		 * libata has decided to kill the qc, so we needn't | 
 | 291 | 		 * bother with sas_ata_task_done.  But we still | 
 | 292 | 		 * ought to abort the task. | 
 | 293 | 		 */ | 
 | 294 | 		struct sas_task *task = qc->lldd_task; | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 295 | 		unsigned long flags; | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 296 |  | 
 | 297 | 		qc->lldd_task = NULL; | 
 | 298 | 		if (task) { | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 299 | 			/* Should this be a AT(API) device reset? */ | 
 | 300 | 			spin_lock_irqsave(&task->task_state_lock, flags); | 
 | 301 | 			task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; | 
 | 302 | 			spin_unlock_irqrestore(&task->task_state_lock, flags); | 
 | 303 |  | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 304 | 			task->uldd_task = NULL; | 
 | 305 | 			__sas_task_abort(task); | 
 | 306 | 		} | 
| Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame] | 307 | 	} | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 308 | } | 
 | 309 |  | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 310 | static struct ata_port_operations sas_sata_ops = { | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 311 | 	.prereset		= ata_std_prereset, | 
 | 312 | 	.softreset		= NULL, | 
 | 313 | 	.hardreset		= sas_ata_hard_reset, | 
 | 314 | 	.postreset		= ata_std_postreset, | 
 | 315 | 	.error_handler		= ata_std_error_handler, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 316 | 	.post_internal_cmd	= sas_ata_post_internal, | 
| David Milburn | f0ad30d | 2010-09-03 17:13:03 -0500 | [diff] [blame] | 317 | 	.qc_defer               = ata_std_qc_defer, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 318 | 	.qc_prep		= ata_noop_qc_prep, | 
 | 319 | 	.qc_issue		= sas_ata_qc_issue, | 
| Tejun Heo | 4c9bf4e | 2008-04-07 22:47:20 +0900 | [diff] [blame] | 320 | 	.qc_fill_rtf		= sas_ata_qc_fill_rtf, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 321 | 	.port_start		= ata_sas_port_start, | 
 | 322 | 	.port_stop		= ata_sas_port_stop, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 323 | }; | 
 | 324 |  | 
 | 325 | static struct ata_port_info sata_port_info = { | 
| Sergei Shtylyov | 9cbe056 | 2011-02-04 22:05:48 +0300 | [diff] [blame] | 326 | 	.flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, | 
| Sergei Shtylyov | 0f2e033 | 2011-01-21 20:32:01 +0300 | [diff] [blame] | 327 | 	.pio_mask = ATA_PIO4, | 
 | 328 | 	.mwdma_mask = ATA_MWDMA2, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 329 | 	.udma_mask = ATA_UDMA6, | 
 | 330 | 	.port_ops = &sas_sata_ops | 
 | 331 | }; | 
 | 332 |  | 
 | 333 | int sas_ata_init_host_and_port(struct domain_device *found_dev, | 
 | 334 | 			       struct scsi_target *starget) | 
 | 335 | { | 
 | 336 | 	struct Scsi_Host *shost = dev_to_shost(&starget->dev); | 
 | 337 | 	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | 
 | 338 | 	struct ata_port *ap; | 
 | 339 |  | 
 | 340 | 	ata_host_init(&found_dev->sata_dev.ata_host, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 341 | 		      ha->dev, | 
| Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 342 | 		      sata_port_info.flags, | 
 | 343 | 		      &sas_sata_ops); | 
 | 344 | 	ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, | 
 | 345 | 				&sata_port_info, | 
 | 346 | 				shost); | 
 | 347 | 	if (!ap) { | 
 | 348 | 		SAS_DPRINTK("ata_sas_port_alloc failed.\n"); | 
 | 349 | 		return -ENODEV; | 
 | 350 | 	} | 
 | 351 |  | 
 | 352 | 	ap->private_data = found_dev; | 
 | 353 | 	ap->cbl = ATA_CBL_SATA; | 
 | 354 | 	ap->scsi_host = shost; | 
 | 355 | 	found_dev->sata_dev.ap = ap; | 
 | 356 |  | 
 | 357 | 	return 0; | 
 | 358 | } | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 359 |  | 
 | 360 | void sas_ata_task_abort(struct sas_task *task) | 
 | 361 | { | 
 | 362 | 	struct ata_queued_cmd *qc = task->uldd_task; | 
 | 363 | 	struct completion *waiting; | 
 | 364 |  | 
 | 365 | 	/* Bounce SCSI-initiated commands to the SCSI EH */ | 
 | 366 | 	if (qc->scsicmd) { | 
| James Bottomley | 1b4d0d8 | 2010-05-13 09:31:54 -0500 | [diff] [blame] | 367 | 		struct request_queue *q = qc->scsicmd->device->request_queue; | 
 | 368 | 		unsigned long flags; | 
 | 369 |  | 
| Tejun Heo | 70b25f8 | 2010-04-15 09:00:08 +0900 | [diff] [blame] | 370 | 		spin_lock_irqsave(q->queue_lock, flags); | 
| Jens Axboe | 242f9dc | 2008-09-14 05:55:09 -0700 | [diff] [blame] | 371 | 		blk_abort_request(qc->scsicmd->request); | 
| Tejun Heo | 70b25f8 | 2010-04-15 09:00:08 +0900 | [diff] [blame] | 372 | 		spin_unlock_irqrestore(q->queue_lock, flags); | 
| Darrick J. Wong | 3a2755a | 2007-01-30 01:18:58 -0800 | [diff] [blame] | 373 | 		scsi_schedule_eh(qc->scsicmd->device->host); | 
 | 374 | 		return; | 
 | 375 | 	} | 
 | 376 |  | 
 | 377 | 	/* Internal command, fake a timeout and complete. */ | 
 | 378 | 	qc->flags &= ~ATA_QCFLAG_ACTIVE; | 
 | 379 | 	qc->flags |= ATA_QCFLAG_FAILED; | 
 | 380 | 	qc->err_mask |= AC_ERR_TIMEOUT; | 
 | 381 | 	waiting = qc->private_data; | 
 | 382 | 	complete(waiting); | 
 | 383 | } | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 384 |  | 
 | 385 | static void sas_task_timedout(unsigned long _task) | 
 | 386 | { | 
 | 387 | 	struct sas_task *task = (void *) _task; | 
 | 388 | 	unsigned long flags; | 
 | 389 |  | 
 | 390 | 	spin_lock_irqsave(&task->task_state_lock, flags); | 
 | 391 | 	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) | 
 | 392 | 		task->task_state_flags |= SAS_TASK_STATE_ABORTED; | 
 | 393 | 	spin_unlock_irqrestore(&task->task_state_lock, flags); | 
 | 394 |  | 
 | 395 | 	complete(&task->completion); | 
 | 396 | } | 
 | 397 |  | 
 | 398 | static void sas_disc_task_done(struct sas_task *task) | 
 | 399 | { | 
 | 400 | 	if (!del_timer(&task->timer)) | 
 | 401 | 		return; | 
 | 402 | 	complete(&task->completion); | 
 | 403 | } | 
 | 404 |  | 
 | 405 | #define SAS_DEV_TIMEOUT 10 | 
 | 406 |  | 
 | 407 | /** | 
 | 408 |  * sas_execute_task -- Basic task processing for discovery | 
 | 409 |  * @task: the task to be executed | 
 | 410 |  * @buffer: pointer to buffer to do I/O | 
 | 411 |  * @size: size of @buffer | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 412 |  * @dma_dir: DMA direction.  DMA_xxx | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 413 |  */ | 
 | 414 | static int sas_execute_task(struct sas_task *task, void *buffer, int size, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 415 | 			    enum dma_data_direction dma_dir) | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 416 | { | 
 | 417 | 	int res = 0; | 
 | 418 | 	struct scatterlist *scatter = NULL; | 
 | 419 | 	struct task_status_struct *ts = &task->task_status; | 
 | 420 | 	int num_scatter = 0; | 
 | 421 | 	int retries = 0; | 
 | 422 | 	struct sas_internal *i = | 
 | 423 | 		to_sas_internal(task->dev->port->ha->core.shost->transportt); | 
 | 424 |  | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 425 | 	if (dma_dir != DMA_NONE) { | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 426 | 		scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); | 
 | 427 | 		if (!scatter) | 
 | 428 | 			goto out; | 
 | 429 |  | 
 | 430 | 		sg_init_one(scatter, buffer, size); | 
 | 431 | 		num_scatter = 1; | 
 | 432 | 	} | 
 | 433 |  | 
 | 434 | 	task->task_proto = task->dev->tproto; | 
 | 435 | 	task->scatter = scatter; | 
 | 436 | 	task->num_scatter = num_scatter; | 
 | 437 | 	task->total_xfer_len = size; | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 438 | 	task->data_dir = dma_dir; | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 439 | 	task->task_done = sas_disc_task_done; | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 440 | 	if (dma_dir != DMA_NONE && | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 441 | 	    sas_protocol_ata(task->task_proto)) { | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 442 | 		task->num_scatter = dma_map_sg(task->dev->port->ha->dev, | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 443 | 					       task->scatter, | 
 | 444 | 					       task->num_scatter, | 
 | 445 | 					       task->data_dir); | 
 | 446 | 	} | 
 | 447 |  | 
 | 448 | 	for (retries = 0; retries < 5; retries++) { | 
 | 449 | 		task->task_state_flags = SAS_TASK_STATE_PENDING; | 
 | 450 | 		init_completion(&task->completion); | 
 | 451 |  | 
 | 452 | 		task->timer.data = (unsigned long) task; | 
 | 453 | 		task->timer.function = sas_task_timedout; | 
 | 454 | 		task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; | 
 | 455 | 		add_timer(&task->timer); | 
 | 456 |  | 
 | 457 | 		res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); | 
 | 458 | 		if (res) { | 
 | 459 | 			del_timer(&task->timer); | 
 | 460 | 			SAS_DPRINTK("executing SAS discovery task failed:%d\n", | 
 | 461 | 				    res); | 
 | 462 | 			goto ex_err; | 
 | 463 | 		} | 
 | 464 | 		wait_for_completion(&task->completion); | 
| James Bottomley | 32e8ae3 | 2007-12-30 12:37:31 -0600 | [diff] [blame] | 465 | 		res = -ECOMM; | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 466 | 		if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { | 
 | 467 | 			int res2; | 
 | 468 | 			SAS_DPRINTK("task aborted, flags:0x%x\n", | 
 | 469 | 				    task->task_state_flags); | 
 | 470 | 			res2 = i->dft->lldd_abort_task(task); | 
 | 471 | 			SAS_DPRINTK("came back from abort task\n"); | 
 | 472 | 			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { | 
 | 473 | 				if (res2 == TMF_RESP_FUNC_COMPLETE) | 
 | 474 | 					continue; /* Retry the task */ | 
 | 475 | 				else | 
 | 476 | 					goto ex_err; | 
 | 477 | 			} | 
 | 478 | 		} | 
| James Bottomley | df64d3c | 2010-07-27 15:51:13 -0500 | [diff] [blame] | 479 | 		if (task->task_status.stat == SAM_STAT_BUSY || | 
 | 480 | 			   task->task_status.stat == SAM_STAT_TASK_SET_FULL || | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 481 | 			   task->task_status.stat == SAS_QUEUE_FULL) { | 
 | 482 | 			SAS_DPRINTK("task: q busy, sleeping...\n"); | 
 | 483 | 			schedule_timeout_interruptible(HZ); | 
| James Bottomley | df64d3c | 2010-07-27 15:51:13 -0500 | [diff] [blame] | 484 | 		} else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) { | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 485 | 			struct scsi_sense_hdr shdr; | 
 | 486 |  | 
 | 487 | 			if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, | 
 | 488 | 						  &shdr)) { | 
 | 489 | 				SAS_DPRINTK("couldn't normalize sense\n"); | 
 | 490 | 				continue; | 
 | 491 | 			} | 
 | 492 | 			if ((shdr.sense_key == 6 && shdr.asc == 0x29) || | 
 | 493 | 			    (shdr.sense_key == 2 && shdr.asc == 4 && | 
 | 494 | 			     shdr.ascq == 1)) { | 
 | 495 | 				SAS_DPRINTK("device %016llx LUN: %016llx " | 
 | 496 | 					    "powering up or not ready yet, " | 
 | 497 | 					    "sleeping...\n", | 
 | 498 | 					    SAS_ADDR(task->dev->sas_addr), | 
 | 499 | 					    SAS_ADDR(task->ssp_task.LUN)); | 
 | 500 |  | 
 | 501 | 				schedule_timeout_interruptible(5*HZ); | 
 | 502 | 			} else if (shdr.sense_key == 1) { | 
 | 503 | 				res = 0; | 
 | 504 | 				break; | 
 | 505 | 			} else if (shdr.sense_key == 5) { | 
 | 506 | 				break; | 
 | 507 | 			} else { | 
 | 508 | 				SAS_DPRINTK("dev %016llx LUN: %016llx " | 
 | 509 | 					    "sense key:0x%x ASC:0x%x ASCQ:0x%x" | 
 | 510 | 					    "\n", | 
 | 511 | 					    SAS_ADDR(task->dev->sas_addr), | 
 | 512 | 					    SAS_ADDR(task->ssp_task.LUN), | 
 | 513 | 					    shdr.sense_key, | 
 | 514 | 					    shdr.asc, shdr.ascq); | 
 | 515 | 			} | 
 | 516 | 		} else if (task->task_status.resp != SAS_TASK_COMPLETE || | 
| James Bottomley | df64d3c | 2010-07-27 15:51:13 -0500 | [diff] [blame] | 517 | 			   task->task_status.stat != SAM_STAT_GOOD) { | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 518 | 			SAS_DPRINTK("task finished with resp:0x%x, " | 
 | 519 | 				    "stat:0x%x\n", | 
 | 520 | 				    task->task_status.resp, | 
 | 521 | 				    task->task_status.stat); | 
 | 522 | 			goto ex_err; | 
 | 523 | 		} else { | 
 | 524 | 			res = 0; | 
 | 525 | 			break; | 
 | 526 | 		} | 
 | 527 | 	} | 
 | 528 | ex_err: | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 529 | 	if (dma_dir != DMA_NONE) { | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 530 | 		if (sas_protocol_ata(task->task_proto)) | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 531 | 			dma_unmap_sg(task->dev->port->ha->dev, | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 532 | 				     task->scatter, task->num_scatter, | 
 | 533 | 				     task->data_dir); | 
 | 534 | 		kfree(scatter); | 
 | 535 | 	} | 
 | 536 | out: | 
 | 537 | 	return res; | 
 | 538 | } | 
 | 539 |  | 
 | 540 | /* ---------- SATA ---------- */ | 
 | 541 |  | 
 | 542 | static void sas_get_ata_command_set(struct domain_device *dev) | 
 | 543 | { | 
 | 544 | 	struct dev_to_host_fis *fis = | 
 | 545 | 		(struct dev_to_host_fis *) dev->frame_rcvd; | 
 | 546 |  | 
 | 547 | 	if ((fis->sector_count == 1 && /* ATA */ | 
 | 548 | 	     fis->lbal         == 1 && | 
 | 549 | 	     fis->lbam         == 0 && | 
 | 550 | 	     fis->lbah         == 0 && | 
 | 551 | 	     fis->device       == 0) | 
 | 552 | 	    || | 
 | 553 | 	    (fis->sector_count == 0 && /* CE-ATA (mATA) */ | 
 | 554 | 	     fis->lbal         == 0 && | 
 | 555 | 	     fis->lbam         == 0xCE && | 
 | 556 | 	     fis->lbah         == 0xAA && | 
 | 557 | 	     (fis->device & ~0x10) == 0)) | 
 | 558 |  | 
 | 559 | 		dev->sata_dev.command_set = ATA_COMMAND_SET; | 
 | 560 |  | 
 | 561 | 	else if ((fis->interrupt_reason == 1 &&	/* ATAPI */ | 
 | 562 | 		  fis->lbal             == 1 && | 
 | 563 | 		  fis->byte_count_low   == 0x14 && | 
 | 564 | 		  fis->byte_count_high  == 0xEB && | 
 | 565 | 		  (fis->device & ~0x10) == 0)) | 
 | 566 |  | 
 | 567 | 		dev->sata_dev.command_set = ATAPI_COMMAND_SET; | 
 | 568 |  | 
 | 569 | 	else if ((fis->sector_count == 1 && /* SEMB */ | 
 | 570 | 		  fis->lbal         == 1 && | 
 | 571 | 		  fis->lbam         == 0x3C && | 
 | 572 | 		  fis->lbah         == 0xC3 && | 
 | 573 | 		  fis->device       == 0) | 
 | 574 | 		|| | 
 | 575 | 		 (fis->interrupt_reason == 1 &&	/* SATA PM */ | 
 | 576 | 		  fis->lbal             == 1 && | 
 | 577 | 		  fis->byte_count_low   == 0x69 && | 
 | 578 | 		  fis->byte_count_high  == 0x96 && | 
 | 579 | 		  (fis->device & ~0x10) == 0)) | 
 | 580 |  | 
 | 581 | 		/* Treat it as a superset? */ | 
 | 582 | 		dev->sata_dev.command_set = ATAPI_COMMAND_SET; | 
 | 583 | } | 
 | 584 |  | 
 | 585 | /** | 
 | 586 |  * sas_issue_ata_cmd -- Basic SATA command processing for discovery | 
 | 587 |  * @dev: the device to send the command to | 
 | 588 |  * @command: the command register | 
 | 589 |  * @features: the features register | 
 | 590 |  * @buffer: pointer to buffer to do I/O | 
 | 591 |  * @size: size of @buffer | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 592 |  * @dma_dir: DMA direction.  DMA_xxx | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 593 |  */ | 
 | 594 | static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, | 
 | 595 | 			     u8 features, void *buffer, int size, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 596 | 			     enum dma_data_direction dma_dir) | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 597 | { | 
 | 598 | 	int res = 0; | 
 | 599 | 	struct sas_task *task; | 
 | 600 | 	struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) | 
 | 601 | 		&dev->frame_rcvd[0]; | 
 | 602 |  | 
 | 603 | 	res = -ENOMEM; | 
 | 604 | 	task = sas_alloc_task(GFP_KERNEL); | 
 | 605 | 	if (!task) | 
 | 606 | 		goto out; | 
 | 607 |  | 
 | 608 | 	task->dev = dev; | 
 | 609 |  | 
 | 610 | 	task->ata_task.fis.fis_type = 0x27; | 
 | 611 | 	task->ata_task.fis.command = command; | 
 | 612 | 	task->ata_task.fis.features = features; | 
 | 613 | 	task->ata_task.fis.device = d2h_fis->device; | 
 | 614 | 	task->ata_task.retry_count = 1; | 
 | 615 |  | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 616 | 	res = sas_execute_task(task, buffer, size, dma_dir); | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 617 |  | 
 | 618 | 	sas_free_task(task); | 
 | 619 | out: | 
 | 620 | 	return res; | 
 | 621 | } | 
 | 622 |  | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 623 | #define ATA_IDENTIFY_DEV         0xEC | 
 | 624 | #define ATA_IDENTIFY_PACKET_DEV  0xA1 | 
 | 625 | #define ATA_SET_FEATURES         0xEF | 
 | 626 | #define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 | 
 | 627 |  | 
 | 628 | /** | 
 | 629 |  * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) | 
 | 630 |  * @dev: STP/SATA device of interest (ATA/ATAPI) | 
 | 631 |  * | 
 | 632 |  * The LLDD has already been notified of this device, so that we can | 
 | 633 |  * send FISes to it.  Here we try to get IDENTIFY DEVICE or IDENTIFY | 
 | 634 |  * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its | 
 | 635 |  * performance for this device. | 
 | 636 |  */ | 
 | 637 | static int sas_discover_sata_dev(struct domain_device *dev) | 
 | 638 | { | 
 | 639 | 	int     res; | 
 | 640 | 	__le16  *identify_x; | 
 | 641 | 	u8      command; | 
 | 642 |  | 
 | 643 | 	identify_x = kzalloc(512, GFP_KERNEL); | 
 | 644 | 	if (!identify_x) | 
 | 645 | 		return -ENOMEM; | 
 | 646 |  | 
 | 647 | 	if (dev->sata_dev.command_set == ATA_COMMAND_SET) { | 
 | 648 | 		dev->sata_dev.identify_device = identify_x; | 
 | 649 | 		command = ATA_IDENTIFY_DEV; | 
 | 650 | 	} else { | 
 | 651 | 		dev->sata_dev.identify_packet_device = identify_x; | 
 | 652 | 		command = ATA_IDENTIFY_PACKET_DEV; | 
 | 653 | 	} | 
 | 654 |  | 
 | 655 | 	res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 656 | 				DMA_FROM_DEVICE); | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 657 | 	if (res) | 
 | 658 | 		goto out_err; | 
 | 659 |  | 
 | 660 | 	/* lives on the media? */ | 
 | 661 | 	if (le16_to_cpu(identify_x[0]) & 4) { | 
 | 662 | 		/* incomplete response */ | 
 | 663 | 		SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " | 
 | 664 | 			    "dev %llx\n", SAS_ADDR(dev->sas_addr)); | 
| Al Viro | 17b7a8d | 2008-04-16 23:27:45 +0100 | [diff] [blame] | 665 | 		if (!(identify_x[83] & cpu_to_le16(1<<6))) | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 666 | 			goto cont1; | 
 | 667 | 		res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, | 
 | 668 | 					ATA_FEATURE_PUP_STBY_SPIN_UP, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 669 | 					NULL, 0, DMA_NONE); | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 670 | 		if (res) | 
 | 671 | 			goto cont1; | 
 | 672 |  | 
 | 673 | 		schedule_timeout_interruptible(5*HZ); /* More time? */ | 
 | 674 | 		res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, | 
| Jeff Garzik | 1d1bbee | 2007-07-26 09:28:37 -0400 | [diff] [blame] | 675 | 					DMA_FROM_DEVICE); | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 676 | 		if (res) | 
 | 677 | 			goto out_err; | 
 | 678 | 	} | 
 | 679 | cont1: | 
| James Bottomley | b914217 | 2007-07-22 13:15:55 -0500 | [diff] [blame] | 680 | 	/* XXX Hint: register this SATA device with SATL. | 
 | 681 | 	   When this returns, dev->sata_dev->lu is alive and | 
 | 682 | 	   present. | 
 | 683 | 	sas_satl_register_dev(dev); | 
 | 684 | 	*/ | 
 | 685 |  | 
 | 686 | 	sas_fill_in_rphy(dev, dev->rphy); | 
 | 687 |  | 
 | 688 | 	return 0; | 
 | 689 | out_err: | 
 | 690 | 	dev->sata_dev.identify_packet_device = NULL; | 
 | 691 | 	dev->sata_dev.identify_device = NULL; | 
 | 692 | 	kfree(identify_x); | 
 | 693 | 	return res; | 
 | 694 | } | 
 | 695 |  | 
 | 696 | static int sas_discover_sata_pm(struct domain_device *dev) | 
 | 697 | { | 
 | 698 | 	return -ENODEV; | 
 | 699 | } | 
 | 700 |  | 
 | 701 | /** | 
 | 702 |  * sas_discover_sata -- discover an STP/SATA domain device | 
 | 703 |  * @dev: pointer to struct domain_device of interest | 
 | 704 |  * | 
 | 705 |  * First we notify the LLDD of this device, so we can send frames to | 
 | 706 |  * it.  Then depending on the type of device we call the appropriate | 
 | 707 |  * discover functions.  Once device discover is done, we notify the | 
 | 708 |  * LLDD so that it can fine-tune its parameters for the device, by | 
 | 709 |  * removing it and then adding it.  That is, the second time around, | 
 | 710 |  * the driver would have certain fields, that it is looking at, set. | 
 | 711 |  * Finally we initialize the kobj so that the device can be added to | 
 | 712 |  * the system at registration time.  Devices directly attached to a HA | 
 | 713 |  * port, have no parents.  All other devices do, and should have their | 
 | 714 |  * "parent" pointer set appropriately before calling this function. | 
 | 715 |  */ | 
 | 716 | int sas_discover_sata(struct domain_device *dev) | 
 | 717 | { | 
 | 718 | 	int res; | 
 | 719 |  | 
 | 720 | 	sas_get_ata_command_set(dev); | 
 | 721 |  | 
 | 722 | 	res = sas_notify_lldd_dev_found(dev); | 
 | 723 | 	if (res) | 
 | 724 | 		return res; | 
 | 725 |  | 
 | 726 | 	switch (dev->dev_type) { | 
 | 727 | 	case SATA_DEV: | 
 | 728 | 		res = sas_discover_sata_dev(dev); | 
 | 729 | 		break; | 
 | 730 | 	case SATA_PM: | 
 | 731 | 		res = sas_discover_sata_pm(dev); | 
 | 732 | 		break; | 
 | 733 | 	default: | 
 | 734 | 		break; | 
 | 735 | 	} | 
 | 736 | 	sas_notify_lldd_dev_gone(dev); | 
 | 737 | 	if (!res) { | 
 | 738 | 		sas_notify_lldd_dev_found(dev); | 
 | 739 | 		res = sas_rphy_add(dev->rphy); | 
 | 740 | 	} | 
 | 741 |  | 
 | 742 | 	return res; | 
 | 743 | } | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 744 |  | 
 | 745 | void sas_ata_strategy_handler(struct Scsi_Host *shost) | 
 | 746 | { | 
 | 747 | 	struct scsi_device *sdev; | 
 | 748 |  | 
 | 749 | 	shost_for_each_device(sdev, shost) { | 
 | 750 | 		struct domain_device *ddev = sdev_to_domain_dev(sdev); | 
 | 751 | 		struct ata_port *ap = ddev->sata_dev.ap; | 
 | 752 |  | 
 | 753 | 		if (!dev_is_sata(ddev)) | 
 | 754 | 			continue; | 
| James Bottomley | c299190 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 755 |  | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 756 | 		ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); | 
 | 757 | 		ata_scsi_port_error_handler(shost, ap); | 
 | 758 | 	} | 
 | 759 | } | 
 | 760 |  | 
 | 761 | int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, | 
 | 762 | 		      enum blk_eh_timer_return *rtn) | 
 | 763 | { | 
 | 764 | 	struct domain_device *ddev = cmd_to_domain_dev(cmd); | 
 | 765 |  | 
 | 766 | 	if (!dev_is_sata(ddev) || task) | 
 | 767 | 		return 0; | 
 | 768 |  | 
 | 769 | 	/* we're a sata device with no task, so this must be a libata | 
 | 770 | 	 * eh timeout.  Ideally should hook into libata timeout | 
 | 771 | 	 * handling, but there's no point, it just wants to activate | 
 | 772 | 	 * the eh thread */ | 
 | 773 | 	*rtn = BLK_EH_NOT_HANDLED; | 
 | 774 | 	return 1; | 
 | 775 | } | 
 | 776 |  | 
 | 777 | int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, | 
 | 778 | 	       struct list_head *done_q) | 
 | 779 | { | 
 | 780 | 	int rtn = 0; | 
 | 781 | 	struct scsi_cmnd *cmd, *n; | 
 | 782 | 	struct ata_port *ap; | 
 | 783 |  | 
 | 784 | 	do { | 
 | 785 | 		LIST_HEAD(sata_q); | 
 | 786 |  | 
 | 787 | 		ap = NULL; | 
| James Bottomley | c299190 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 788 |  | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 789 | 		list_for_each_entry_safe(cmd, n, work_q, eh_entry) { | 
 | 790 | 			struct domain_device *ddev = cmd_to_domain_dev(cmd); | 
 | 791 |  | 
 | 792 | 			if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) | 
 | 793 | 				continue; | 
| James Bottomley | c299190 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 794 | 			if (ap && ap != ddev->sata_dev.ap) | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 795 | 				continue; | 
 | 796 | 			ap = ddev->sata_dev.ap; | 
 | 797 | 			rtn = 1; | 
 | 798 | 			list_move(&cmd->eh_entry, &sata_q); | 
 | 799 | 		} | 
 | 800 |  | 
 | 801 | 		if (!list_empty(&sata_q)) { | 
| James Bottomley | c299190 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 802 | 			ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n"); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 803 | 			ata_scsi_cmd_error_handler(shost, ap, &sata_q); | 
| James Bottomley | a82058a | 2011-03-10 17:13:18 -0600 | [diff] [blame] | 804 | 			/* | 
 | 805 | 			 * ata's error handler may leave the cmd on the list | 
 | 806 | 			 * so make sure they don't remain on a stack list | 
 | 807 | 			 * about to go out of scope. | 
 | 808 | 			 * | 
 | 809 | 			 * This looks strange, since the commands are | 
 | 810 | 			 * now part of no list, but the next error | 
 | 811 | 			 * action will be ata_port_error_handler() | 
 | 812 | 			 * which takes no list and sweeps them up | 
 | 813 | 			 * anyway from the ata tag array. | 
 | 814 | 			 */ | 
 | 815 | 			while (!list_empty(&sata_q)) | 
 | 816 | 				list_del_init(sata_q.next); | 
| James Bottomley | 00dd499 | 2011-01-23 09:44:12 -0600 | [diff] [blame] | 817 | 		} | 
 | 818 | 	} while (ap); | 
 | 819 |  | 
 | 820 | 	return rtn; | 
 | 821 | } |