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 | |
| 24 | #include <scsi/sas_ata.h> |
| 25 | #include "sas_internal.h" |
| 26 | #include <scsi/scsi_host.h> |
| 27 | #include <scsi/scsi_device.h> |
| 28 | #include <scsi/scsi_tcq.h> |
| 29 | #include <scsi/scsi.h> |
| 30 | #include <scsi/scsi_transport.h> |
| 31 | #include <scsi/scsi_transport_sas.h> |
| 32 | #include "../scsi_sas_internal.h" |
| 33 | |
| 34 | static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) |
| 35 | { |
| 36 | /* Cheesy attempt to translate SAS errors into ATA. Hah! */ |
| 37 | |
| 38 | /* transport error */ |
| 39 | if (ts->resp == SAS_TASK_UNDELIVERED) |
| 40 | return AC_ERR_ATA_BUS; |
| 41 | |
| 42 | /* ts->resp == SAS_TASK_COMPLETE */ |
| 43 | /* task delivered, what happened afterwards? */ |
| 44 | switch (ts->stat) { |
| 45 | case SAS_DEV_NO_RESPONSE: |
| 46 | return AC_ERR_TIMEOUT; |
| 47 | |
| 48 | case SAS_INTERRUPTED: |
| 49 | case SAS_PHY_DOWN: |
| 50 | case SAS_NAK_R_ERR: |
| 51 | return AC_ERR_ATA_BUS; |
| 52 | |
| 53 | |
| 54 | case SAS_DATA_UNDERRUN: |
| 55 | /* |
| 56 | * Some programs that use the taskfile interface |
| 57 | * (smartctl in particular) can cause underrun |
| 58 | * problems. Ignore these errors, perhaps at our |
| 59 | * peril. |
| 60 | */ |
| 61 | return 0; |
| 62 | |
| 63 | case SAS_DATA_OVERRUN: |
| 64 | case SAS_QUEUE_FULL: |
| 65 | case SAS_DEVICE_UNKNOWN: |
| 66 | case SAS_SG_ERR: |
| 67 | return AC_ERR_INVALID; |
| 68 | |
| 69 | case SAM_CHECK_COND: |
| 70 | case SAS_OPEN_TO: |
| 71 | case SAS_OPEN_REJECT: |
| 72 | SAS_DPRINTK("%s: Saw error %d. What to do?\n", |
| 73 | __FUNCTION__, ts->stat); |
| 74 | return AC_ERR_OTHER; |
| 75 | |
| 76 | case SAS_ABORTED_TASK: |
| 77 | return AC_ERR_DEV; |
| 78 | |
| 79 | case SAS_PROTO_RESPONSE: |
| 80 | /* This means the ending_fis has the error |
| 81 | * value; return 0 here to collect it */ |
| 82 | return 0; |
| 83 | default: |
| 84 | return 0; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | static void sas_ata_task_done(struct sas_task *task) |
| 89 | { |
| 90 | struct ata_queued_cmd *qc = task->uldd_task; |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 91 | struct domain_device *dev; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 92 | struct task_status_struct *stat = &task->task_status; |
| 93 | struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; |
| 94 | enum ata_completion_errors ac; |
Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 95 | unsigned long flags; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 96 | |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 97 | if (!qc) |
| 98 | goto qc_already_gone; |
| 99 | |
| 100 | dev = qc->ap->private_data; |
| 101 | |
Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 102 | spin_lock_irqsave(dev->sata_dev.ap->lock, flags); |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 103 | if (stat->stat == SAS_PROTO_RESPONSE) { |
| 104 | ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); |
| 105 | qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); |
| 106 | dev->sata_dev.sstatus = resp->sstatus; |
| 107 | dev->sata_dev.serror = resp->serror; |
| 108 | dev->sata_dev.scontrol = resp->scontrol; |
| 109 | dev->sata_dev.ap->sactive = resp->sactive; |
| 110 | } else if (stat->stat != SAM_STAT_GOOD) { |
| 111 | ac = sas_to_ata_err(stat); |
| 112 | if (ac) { |
| 113 | SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, |
| 114 | stat->stat); |
| 115 | /* We saw a SAS error. Send a vague error. */ |
| 116 | qc->err_mask = ac; |
| 117 | dev->sata_dev.tf.feature = 0x04; /* status err */ |
| 118 | dev->sata_dev.tf.command = ATA_ERR; |
| 119 | } |
| 120 | } |
| 121 | |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 122 | qc->lldd_task = NULL; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 123 | ata_qc_complete(qc); |
Darrick J. Wong | 3eb7a51 | 2007-01-30 01:18:35 -0800 | [diff] [blame] | 124 | spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); |
| 125 | |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 126 | qc_already_gone: |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 127 | list_del_init(&task->list); |
| 128 | sas_free_task(task); |
| 129 | } |
| 130 | |
| 131 | static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) |
| 132 | { |
Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 133 | int res; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 134 | struct sas_task *task; |
| 135 | struct domain_device *dev = qc->ap->private_data; |
| 136 | struct sas_ha_struct *sas_ha = dev->port->ha; |
| 137 | struct Scsi_Host *host = sas_ha->core.shost; |
| 138 | struct sas_internal *i = to_sas_internal(host->transportt); |
| 139 | struct scatterlist *sg; |
| 140 | unsigned int num = 0; |
| 141 | unsigned int xfer = 0; |
| 142 | |
| 143 | task = sas_alloc_task(GFP_ATOMIC); |
| 144 | if (!task) |
Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 145 | return AC_ERR_SYSTEM; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 146 | task->dev = dev; |
| 147 | task->task_proto = SAS_PROTOCOL_STP; |
| 148 | task->task_done = sas_ata_task_done; |
| 149 | |
| 150 | if (qc->tf.command == ATA_CMD_FPDMA_WRITE || |
| 151 | qc->tf.command == ATA_CMD_FPDMA_READ) { |
| 152 | /* Need to zero out the tag libata assigned us */ |
| 153 | qc->tf.nsect = 0; |
| 154 | } |
| 155 | |
| 156 | ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); |
| 157 | task->uldd_task = qc; |
| 158 | if (is_atapi_taskfile(&qc->tf)) { |
| 159 | memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); |
| 160 | task->total_xfer_len = qc->nbytes + qc->pad_len; |
| 161 | task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; |
| 162 | } else { |
| 163 | ata_for_each_sg(sg, qc) { |
| 164 | num++; |
| 165 | xfer += sg->length; |
| 166 | } |
| 167 | |
| 168 | task->total_xfer_len = xfer; |
| 169 | task->num_scatter = num; |
| 170 | } |
| 171 | |
| 172 | task->data_dir = qc->dma_dir; |
| 173 | task->scatter = qc->__sg; |
| 174 | task->ata_task.retry_count = 1; |
| 175 | task->task_state_flags = SAS_TASK_STATE_PENDING; |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 176 | qc->lldd_task = task; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 177 | |
| 178 | switch (qc->tf.protocol) { |
| 179 | case ATA_PROT_NCQ: |
| 180 | task->ata_task.use_ncq = 1; |
| 181 | /* fall through */ |
| 182 | case ATA_PROT_ATAPI_DMA: |
| 183 | case ATA_PROT_DMA: |
| 184 | task->ata_task.dma_xfer = 1; |
| 185 | break; |
| 186 | } |
| 187 | |
| 188 | if (sas_ha->lldd_max_execute_num < 2) |
| 189 | res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); |
| 190 | else |
| 191 | res = sas_queue_up(task); |
| 192 | |
| 193 | /* Examine */ |
| 194 | if (res) { |
| 195 | SAS_DPRINTK("lldd_execute_task returned: %d\n", res); |
| 196 | |
| 197 | sas_free_task(task); |
Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 198 | return AC_ERR_SYSTEM; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 199 | } |
| 200 | |
Darrick J. Wong | 35a7f2f | 2007-01-30 01:18:38 -0800 | [diff] [blame] | 201 | return 0; |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | static u8 sas_ata_check_status(struct ata_port *ap) |
| 205 | { |
| 206 | struct domain_device *dev = ap->private_data; |
| 207 | return dev->sata_dev.tf.command; |
| 208 | } |
| 209 | |
| 210 | static void sas_ata_phy_reset(struct ata_port *ap) |
| 211 | { |
| 212 | struct domain_device *dev = ap->private_data; |
| 213 | struct sas_internal *i = |
| 214 | to_sas_internal(dev->port->ha->core.shost->transportt); |
| 215 | int res = 0; |
| 216 | |
| 217 | if (i->dft->lldd_I_T_nexus_reset) |
| 218 | res = i->dft->lldd_I_T_nexus_reset(dev); |
| 219 | |
| 220 | if (res) |
| 221 | SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); |
| 222 | |
| 223 | switch (dev->sata_dev.command_set) { |
| 224 | case ATA_COMMAND_SET: |
| 225 | SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); |
| 226 | ap->device[0].class = ATA_DEV_ATA; |
| 227 | break; |
| 228 | case ATAPI_COMMAND_SET: |
| 229 | SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); |
| 230 | ap->device[0].class = ATA_DEV_ATAPI; |
| 231 | break; |
| 232 | default: |
| 233 | SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", |
| 234 | __FUNCTION__, |
| 235 | dev->sata_dev.command_set); |
| 236 | ap->device[0].class = ATA_DEV_ATA; |
| 237 | break; |
| 238 | } |
| 239 | |
| 240 | ap->cbl = ATA_CBL_SATA; |
| 241 | } |
| 242 | |
| 243 | static void sas_ata_post_internal(struct ata_queued_cmd *qc) |
| 244 | { |
| 245 | if (qc->flags & ATA_QCFLAG_FAILED) |
| 246 | qc->err_mask |= AC_ERR_OTHER; |
| 247 | |
Darrick J. Wong | 1c50dc8 | 2007-01-30 01:18:41 -0800 | [diff] [blame^] | 248 | if (qc->err_mask) { |
| 249 | /* |
| 250 | * Find the sas_task and kill it. By this point, |
| 251 | * libata has decided to kill the qc, so we needn't |
| 252 | * bother with sas_ata_task_done. But we still |
| 253 | * ought to abort the task. |
| 254 | */ |
| 255 | struct sas_task *task = qc->lldd_task; |
| 256 | struct domain_device *dev = qc->ap->private_data; |
| 257 | |
| 258 | qc->lldd_task = NULL; |
| 259 | if (task) { |
| 260 | task->uldd_task = NULL; |
| 261 | __sas_task_abort(task); |
| 262 | } |
| 263 | |
| 264 | sas_phy_reset(dev->port->phy, 1); |
| 265 | } |
Darrick J. Wong | 338ec57 | 2006-10-18 14:43:37 -0700 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) |
| 269 | { |
| 270 | struct domain_device *dev = ap->private_data; |
| 271 | memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); |
| 272 | } |
| 273 | |
| 274 | static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, |
| 275 | u32 val) |
| 276 | { |
| 277 | struct domain_device *dev = ap->private_data; |
| 278 | |
| 279 | SAS_DPRINTK("STUB %s\n", __FUNCTION__); |
| 280 | switch (sc_reg_in) { |
| 281 | case SCR_STATUS: |
| 282 | dev->sata_dev.sstatus = val; |
| 283 | break; |
| 284 | case SCR_CONTROL: |
| 285 | dev->sata_dev.scontrol = val; |
| 286 | break; |
| 287 | case SCR_ERROR: |
| 288 | dev->sata_dev.serror = val; |
| 289 | break; |
| 290 | case SCR_ACTIVE: |
| 291 | dev->sata_dev.ap->sactive = val; |
| 292 | break; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | static u32 sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in) |
| 297 | { |
| 298 | struct domain_device *dev = ap->private_data; |
| 299 | |
| 300 | SAS_DPRINTK("STUB %s\n", __FUNCTION__); |
| 301 | switch (sc_reg_in) { |
| 302 | case SCR_STATUS: |
| 303 | return dev->sata_dev.sstatus; |
| 304 | case SCR_CONTROL: |
| 305 | return dev->sata_dev.scontrol; |
| 306 | case SCR_ERROR: |
| 307 | return dev->sata_dev.serror; |
| 308 | case SCR_ACTIVE: |
| 309 | return dev->sata_dev.ap->sactive; |
| 310 | default: |
| 311 | return 0xffffffffU; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | static struct ata_port_operations sas_sata_ops = { |
| 316 | .port_disable = ata_port_disable, |
| 317 | .check_status = sas_ata_check_status, |
| 318 | .check_altstatus = sas_ata_check_status, |
| 319 | .dev_select = ata_noop_dev_select, |
| 320 | .phy_reset = sas_ata_phy_reset, |
| 321 | .post_internal_cmd = sas_ata_post_internal, |
| 322 | .tf_read = sas_ata_tf_read, |
| 323 | .qc_prep = ata_noop_qc_prep, |
| 324 | .qc_issue = sas_ata_qc_issue, |
| 325 | .port_start = ata_sas_port_start, |
| 326 | .port_stop = ata_sas_port_stop, |
| 327 | .scr_read = sas_ata_scr_read, |
| 328 | .scr_write = sas_ata_scr_write |
| 329 | }; |
| 330 | |
| 331 | static struct ata_port_info sata_port_info = { |
| 332 | .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | |
| 333 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, |
| 334 | .pio_mask = 0x1f, /* PIO0-4 */ |
| 335 | .mwdma_mask = 0x07, /* MWDMA0-2 */ |
| 336 | .udma_mask = ATA_UDMA6, |
| 337 | .port_ops = &sas_sata_ops |
| 338 | }; |
| 339 | |
| 340 | int sas_ata_init_host_and_port(struct domain_device *found_dev, |
| 341 | struct scsi_target *starget) |
| 342 | { |
| 343 | struct Scsi_Host *shost = dev_to_shost(&starget->dev); |
| 344 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); |
| 345 | struct ata_port *ap; |
| 346 | |
| 347 | ata_host_init(&found_dev->sata_dev.ata_host, |
| 348 | &ha->pcidev->dev, |
| 349 | sata_port_info.flags, |
| 350 | &sas_sata_ops); |
| 351 | ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, |
| 352 | &sata_port_info, |
| 353 | shost); |
| 354 | if (!ap) { |
| 355 | SAS_DPRINTK("ata_sas_port_alloc failed.\n"); |
| 356 | return -ENODEV; |
| 357 | } |
| 358 | |
| 359 | ap->private_data = found_dev; |
| 360 | ap->cbl = ATA_CBL_SATA; |
| 361 | ap->scsi_host = shost; |
| 362 | found_dev->sata_dev.ap = ap; |
| 363 | |
| 364 | return 0; |
| 365 | } |