xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_sata.c (revision 508a0e8cf1600b06c1f7361ad76e736710d3fdf8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  *
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * SATA midlayer interface for PMC drier.
27  */
28 
29 #include <sys/scsi/adapters/pmcs/pmcs.h>
30 
31 static void
32 SATAcopy(pmcs_cmd_t *sp, void *kbuf, uint32_t amt)
33 {
34 	struct buf *bp = scsi_pkt2bp(CMD2PKT(sp));
35 
36 	bp_mapin(scsi_pkt2bp(CMD2PKT(sp)));
37 	/* There is only one direction currently */
38 	(void) memcpy(bp->b_un.b_addr, kbuf, amt);
39 	CMD2PKT(sp)->pkt_resid -= amt;
40 	CMD2PKT(sp)->pkt_state |= STATE_XFERRED_DATA;
41 	bp_mapout(scsi_pkt2bp(CMD2PKT(sp)));
42 }
43 
44 /*
45  * Run a non block-io command. Some commands are interpreted
46  * out of extant data. Some imply actually running a SATA command.
47  *
48  * Returns zero if we were able to run.
49  *
50  * Returns -1 only if other commands are active, either another
51  * command here or regular I/O active.
52  *
53  * Called with PHY lock and xp statlock held.
54  */
55 #define	SRESPSZ	128
56 
57 static int
58 pmcs_sata_special_work(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
59 {
60 	int i;
61 	int saq;
62 	pmcs_cmd_t *sp;
63 	struct scsi_pkt *pkt;
64 	pmcs_phy_t *pptr;
65 	uint8_t rp[SRESPSZ];
66 	ata_identify_t *id;
67 	uint32_t amt = 0;
68 	uint8_t key = 0x05;	/* illegal command */
69 	uint8_t asc = 0;
70 	uint8_t ascq = 0;
71 	uint8_t status = STATUS_GOOD;
72 
73 	if (xp->actv_cnt) {
74 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
75 		    "%s: target %p actv count %u",
76 		    __func__, (void *)xp, xp->actv_cnt);
77 		return (-1);
78 	}
79 	if (xp->special_running) {
80 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
81 		    "%s: target %p special running already",
82 		    __func__, (void *)xp);
83 		return (-1);
84 	}
85 	xp->special_needed = 0;
86 
87 	/*
88 	 * We're now running special.
89 	 */
90 	xp->special_running = 1;
91 	pptr = xp->phy;
92 
93 	sp = STAILQ_FIRST(&xp->sq);
94 	if (sp == NULL) {
95 		xp->special_running = 0;
96 		return (0);
97 	}
98 
99 	pkt = CMD2PKT(sp);
100 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
101 	    "%s: target %p cmd %p cdb0 %x with actv_cnt %u",
102 	    __func__, (void *)xp, (void *)sp, pkt->pkt_cdbp[0], xp->actv_cnt);
103 
104 	if (pkt->pkt_cdbp[0] == SCMD_INQUIRY ||
105 	    pkt->pkt_cdbp[0] == SCMD_READ_CAPACITY) {
106 		int retval;
107 
108 		if (pmcs_acquire_scratch(pwp, B_FALSE)) {
109 			xp->special_running = 0;
110 			return (-1);
111 		}
112 		saq = 1;
113 
114 		mutex_exit(&xp->statlock);
115 		retval = pmcs_sata_identify(pwp, pptr);
116 		mutex_enter(&xp->statlock);
117 
118 		if (retval) {
119 			pmcs_release_scratch(pwp);
120 			xp->special_running = 0;
121 
122 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
123 			    "%s: target %p identify failed %x",
124 			    __func__, (void *)xp, retval);
125 			/*
126 			 * If the failure is due to not being
127 			 * able to get resources, return such
128 			 * that we'll try later. Otherwise,
129 			 * fail current command.
130 			 */
131 			if (retval == ENOMEM) {
132 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
133 				    "%s: sata identify failed (ENOMEM) for "
134 				    "cmd %p", __func__, (void *)sp);
135 				return (-1);
136 			}
137 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
138 			    STATE_SENT_CMD;
139 			if (retval == ETIMEDOUT) {
140 				pkt->pkt_reason = CMD_TIMEOUT;
141 				pkt->pkt_statistics |= STAT_TIMEOUT;
142 			} else {
143 				pkt->pkt_reason = CMD_TRAN_ERR;
144 			}
145 			goto out;
146 		}
147 
148 		id = pwp->scratch;
149 
150 		/*
151 		 * Check to see if this device is an NCQ capable device.
152 		 * Yes, we'll end up doing this check for every INQUIRY
153 		 * if indeed we *are* only a pio device, but this is so
154 		 * infrequent that it's not really worth an extra bitfield.
155 		 *
156 		 * Note that PIO mode here means that the PMCS firmware
157 		 * performs PIO- not us.
158 		 */
159 		if (xp->ncq == 0) {
160 			/*
161 			 * Reset existing stuff.
162 			 */
163 			xp->pio = 0;
164 			xp->qdepth = 1;
165 			xp->tagmap = 0;
166 
167 			if (id->word76 != 0 && id->word76 != 0xffff &&
168 			    (LE_16(id->word76) & (1 << 8))) {
169 				xp->ncq = 1;
170 				xp->qdepth = (LE_16(id->word75) & 0x1f) + 1;
171 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
172 				    "%s: device %s supports NCQ %u deep",
173 				    __func__, xp->phy->path, xp->qdepth);
174 			} else {
175 				/*
176 				 * Default back to PIO.
177 				 *
178 				 * Note that non-FPDMA would still be possible,
179 				 * but for this specific configuration, if it's
180 				 * not NCQ it's safest to assume PIO.
181 				 */
182 				xp->pio = 1;
183 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
184 				    "%s: device %s assumed PIO",
185 				    __func__, xp->phy->path);
186 			}
187 		}
188 	} else {
189 		saq = 0;
190 		id = NULL;
191 	}
192 
193 	bzero(rp, SRESPSZ);
194 
195 	switch (pkt->pkt_cdbp[0]) {
196 	case SCMD_INQUIRY:
197 	{
198 		struct scsi_inquiry *inqp;
199 		uint16_t *a, *b;
200 
201 		/* Check for illegal bits */
202 		if ((pkt->pkt_cdbp[1] & 0xfc) || pkt->pkt_cdbp[5]) {
203 			status = STATUS_CHECK;
204 			asc = 0x24;	/* invalid field in cdb */
205 			break;
206 		}
207 		if (pkt->pkt_cdbp[1] & 0x1) {
208 			switch (pkt->pkt_cdbp[2]) {
209 			case 0x0:
210 				rp[3] = 3;
211 				rp[5] = 0x80;
212 				rp[6] = 0x83;
213 				amt = 7;
214 				break;
215 			case 0x80:
216 				rp[1] = 0x80;
217 				rp[3] = 0x14;
218 				a = (void *) &rp[4];
219 				b = id->model_number;
220 				for (i = 0; i < 5; i++) {
221 					*a = ddi_swap16(*b);
222 					a++;
223 					b++;
224 				}
225 				amt = 24;
226 				break;
227 			case 0x83:
228 				rp[1] = 0x83;
229 				if ((LE_16(id->word87) & 0x100) &&
230 				    (LE_16(id->word108) >> 12) == 5)  {
231 					rp[3] = 12;
232 					rp[4] = 1;
233 					rp[5] = 3;
234 					rp[7] = 8;
235 					rp[8] = LE_16(id->word108) >> 8;
236 					rp[9] = LE_16(id->word108);
237 					rp[10] = LE_16(id->word109) >> 8;
238 					rp[11] = LE_16(id->word109);
239 					rp[12] = LE_16(id->word110) >> 8;
240 					rp[13] = LE_16(id->word110);
241 					rp[14] = LE_16(id->word111) >> 8;
242 					rp[15] = LE_16(id->word111);
243 					amt = 16;
244 				} else {
245 					rp[3] = 64;
246 					rp[4] = 2;
247 					rp[5] = 1;
248 					rp[7] = 60;
249 					rp[8] = 'A';
250 					rp[9] = 'T';
251 					rp[10] = 'A';
252 					rp[11] = ' ';
253 					rp[12] = ' ';
254 					rp[13] = ' ';
255 					rp[14] = ' ';
256 					rp[15] = ' ';
257 					a = (void *) &rp[16];
258 					b = id->model_number;
259 					for (i = 0; i < 20; i++) {
260 						*a = ddi_swap16(*b);
261 						a++;
262 						b++;
263 					}
264 					a = (void *) &rp[40];
265 					b = id->serial_number;
266 					for (i = 0; i < 10; i++) {
267 						*a = ddi_swap16(*b);
268 						a++;
269 						b++;
270 					}
271 					amt = 68;
272 				}
273 				break;
274 			default:
275 				status = STATUS_CHECK;
276 				asc = 0x24;	/* invalid field in cdb */
277 				break;
278 			}
279 		} else {
280 			inqp = (struct scsi_inquiry *)rp;
281 			inqp->inq_qual = 0;
282 			inqp->inq_ansi = 5;	/* spc3 */
283 			inqp->inq_rdf = 2;	/* response format 2 */
284 			inqp->inq_len = 32;
285 
286 			if (xp->ncq && (xp->qdepth > 1)) {
287 				inqp->inq_cmdque = 1;
288 			}
289 
290 			(void) memcpy(inqp->inq_vid, "ATA     ", 8);
291 
292 			a = (void *)inqp->inq_pid;
293 			b = id->model_number;
294 			for (i = 0; i < 8; i++) {
295 				*a = ddi_swap16(*b);
296 				a++;
297 				b++;
298 			}
299 			if (id->firmware_revision[2] == 0x2020 &&
300 			    id->firmware_revision[3] == 0x2020) {
301 				inqp->inq_revision[0] =
302 				    ddi_swap16(id->firmware_revision[0]) >> 8;
303 				inqp->inq_revision[1] =
304 				    ddi_swap16(id->firmware_revision[0]);
305 				inqp->inq_revision[2] =
306 				    ddi_swap16(id->firmware_revision[1]) >> 8;
307 				inqp->inq_revision[3] =
308 				    ddi_swap16(id->firmware_revision[1]);
309 			} else {
310 				inqp->inq_revision[0] =
311 				    ddi_swap16(id->firmware_revision[2]) >> 8;
312 				inqp->inq_revision[1] =
313 				    ddi_swap16(id->firmware_revision[2]);
314 				inqp->inq_revision[2] =
315 				    ddi_swap16(id->firmware_revision[3]) >> 8;
316 				inqp->inq_revision[3] =
317 				    ddi_swap16(id->firmware_revision[3]);
318 			}
319 			amt = 36;
320 		}
321 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
322 		if (amt) {
323 			if (xp->actv_cnt) {
324 				xp->special_needed = 1;
325 				xp->special_running = 0;
326 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
327 				    "%s: @ line %d", __func__, __LINE__);
328 				if (saq) {
329 					pmcs_release_scratch(pwp);
330 				}
331 				return (-1);
332 			}
333 			SATAcopy(sp, rp, amt);
334 		}
335 		break;
336 	}
337 	case SCMD_READ_CAPACITY:
338 	{
339 		uint64_t last_block;
340 		uint32_t block_size = 512;	/* XXXX */
341 
342 		xp->capacity = LBA_CAPACITY(id);
343 		last_block = xp->capacity - 1;
344 		/* Check for illegal bits */
345 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[6] ||
346 		    (pkt->pkt_cdbp[8] & 0xfe) || pkt->pkt_cdbp[7] ||
347 		    pkt->pkt_cdbp[9]) {
348 			status = STATUS_CHECK;
349 			asc = 0x24;	/* invalid field in cdb */
350 			break;
351 		}
352 		for (i = 1; i < 10; i++) {
353 			if (pkt->pkt_cdbp[i]) {
354 				status = STATUS_CHECK;
355 				asc = 0x24;	/* invalid field in cdb */
356 				break;
357 			}
358 		}
359 		if (status != STATUS_GOOD) {
360 			break;
361 		}
362 		if (last_block > 0xffffffffULL) {
363 			last_block = 0xffffffffULL;
364 		}
365 		rp[0] = (last_block >> 24) & 0xff;
366 		rp[1] = (last_block >> 16) & 0xff;
367 		rp[2] = (last_block >>  8) & 0xff;
368 		rp[3] = (last_block) & 0xff;
369 		rp[4] = (block_size >> 24) & 0xff;
370 		rp[5] = (block_size >> 16) & 0xff;
371 		rp[6] = (block_size >>  8) & 0xff;
372 		rp[7] = (block_size) & 0xff;
373 		amt = 8;
374 		amt = pmcs_set_resid(pkt, amt, 8);
375 		if (amt) {
376 			if (xp->actv_cnt) {
377 				xp->special_needed = 1;
378 				xp->special_running = 0;
379 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
380 				    "%s: @ line %d", __func__, __LINE__);
381 				if (saq) {
382 					pmcs_release_scratch(pwp);
383 				}
384 				return (-1);
385 			}
386 			SATAcopy(sp, rp, amt);
387 		}
388 		break;
389 	}
390 	case SCMD_REPORT_LUNS: {
391 		int rl_len;
392 
393 		/* Check for illegal bits */
394 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[3] || pkt->pkt_cdbp[4] ||
395 		    pkt->pkt_cdbp[5] || pkt->pkt_cdbp[10] ||
396 		    pkt->pkt_cdbp[11]) {
397 			status = STATUS_CHECK;
398 			asc = 0x24;	/* invalid field in cdb */
399 			break;
400 		}
401 
402 		rp[3] = 8;
403 		rl_len = 16;	/* list length (4) + reserved (4) + 1 LUN (8) */
404 		amt = rl_len;
405 		amt = pmcs_set_resid(pkt, amt, rl_len);
406 
407 		if (amt) {
408 			if (xp->actv_cnt) {
409 				xp->special_needed = 1;
410 				xp->special_running = 0;
411 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
412 				    "%s: @ line %d", __func__, __LINE__);
413 				if (saq) {
414 					pmcs_release_scratch(pwp);
415 				}
416 				return (-1);
417 			}
418 			SATAcopy(sp, rp, rl_len);
419 		}
420 		break;
421 	}
422 
423 	case SCMD_REQUEST_SENSE:
424 		/* Check for illegal bits */
425 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
426 		    pkt->pkt_cdbp[3] || pkt->pkt_cdbp[5]) {
427 			status = STATUS_CHECK;
428 			asc = 0x24;	/* invalid field in cdb */
429 			break;
430 		}
431 		rp[0] = 0xf0;
432 		amt = 18;
433 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
434 		if (amt) {
435 			if (xp->actv_cnt) {
436 				xp->special_needed = 1;
437 				xp->special_running = 0;
438 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
439 				    "%s: @ line %d", __func__, __LINE__);
440 				if (saq) {
441 					pmcs_release_scratch(pwp);
442 				}
443 				return (-1);
444 			}
445 			SATAcopy(sp, rp, 18);
446 		}
447 		break;
448 	case SCMD_START_STOP:
449 		/* Check for illegal bits */
450 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
451 		    (pkt->pkt_cdbp[3] & 0xf0) || (pkt->pkt_cdbp[4] & 0x08) ||
452 		    pkt->pkt_cdbp[5]) {
453 			status = STATUS_CHECK;
454 			asc = 0x24;	/* invalid field in cdb */
455 			break;
456 		}
457 		break;
458 	case SCMD_SYNCHRONIZE_CACHE:
459 		/* Check for illegal bits */
460 		if ((pkt->pkt_cdbp[1] & 0xf8) || (pkt->pkt_cdbp[6] & 0xe0) ||
461 		    pkt->pkt_cdbp[9]) {
462 			status = STATUS_CHECK;
463 			asc = 0x24;	/* invalid field in cdb */
464 			break;
465 		}
466 		break;
467 	case SCMD_TEST_UNIT_READY:
468 		/* Check for illegal bits */
469 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[2] || pkt->pkt_cdbp[3] ||
470 		    pkt->pkt_cdbp[4] || pkt->pkt_cdbp[5]) {
471 			status = STATUS_CHECK;
472 			asc = 0x24;	/* invalid field in cdb */
473 			break;
474 		}
475 		if (xp->ca) {
476 			status = STATUS_CHECK;
477 			key = 0x6;
478 			asc = 0x28;
479 			xp->ca = 0;
480 		}
481 		break;
482 	default:
483 		asc = 0x20;	/* invalid operation command code */
484 		status = STATUS_CHECK;
485 		break;
486 	}
487 	if (status != STATUS_GOOD) {
488 		bzero(rp, 18);
489 		rp[0] = 0xf0;
490 		rp[2] = key;
491 		rp[12] = asc;
492 		rp[13] = ascq;
493 		pmcs_latch_status(pwp, sp, status, rp, 18, pptr->path);
494 	} else {
495 		pmcs_latch_status(pwp, sp, status, NULL, 0, pptr->path);
496 	}
497 
498 out:
499 	STAILQ_REMOVE_HEAD(&xp->sq, cmd_next);
500 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
501 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
502 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
503 	    pkt->pkt_state, pkt->pkt_resid, status);
504 
505 	if (saq) {
506 		pmcs_release_scratch(pwp);
507 	}
508 
509 	if (xp->draining) {
510 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
511 		    "%s: waking up drain waiters", __func__);
512 		cv_signal(&pwp->drain_cv);
513 	}
514 
515 	mutex_exit(&xp->statlock);
516 	mutex_enter(&pwp->cq_lock);
517 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
518 	PMCS_CQ_RUN_LOCKED(pwp);
519 	mutex_exit(&pwp->cq_lock);
520 	mutex_enter(&xp->statlock);
521 	xp->special_running = 0;
522 	return (0);
523 }
524 
525 /*
526  * Run all special commands queued up for a SATA device.
527  * We're only called if the caller knows we have work to do.
528  *
529  * We can't run them if things are still active for the device,
530  * return saying we didn't run anything.
531  *
532  * When we finish, wake up anyone waiting for active commands
533  * to go to zero.
534  *
535  * Called with PHY lock and xp statlock held.
536  */
537 int
538 pmcs_run_sata_special(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
539 {
540 	while (!STAILQ_EMPTY(&xp->sq)) {
541 		if (pmcs_sata_special_work(pwp, xp)) {
542 			return (-1);
543 		}
544 	}
545 	return (0);
546 }
547 
548 /*
549  * Search for SATA special commands to run and run them.
550  * If we succeed in running the special command(s), kick
551  * the normal commands into operation again. Call completion
552  * for any commands that were completed while we were here.
553  *
554  * Called unlocked.
555  */
556 void
557 pmcs_sata_work(pmcs_hw_t *pwp)
558 {
559 	pmcs_xscsi_t *xp;
560 	int spinagain = 0;
561 	uint16_t target;
562 
563 	for (target = 0; target < pwp->max_dev; target++) {
564 		xp = pwp->targets[target];
565 		if ((xp == NULL) || (xp->phy == NULL)) {
566 			continue;
567 		}
568 		pmcs_lock_phy(xp->phy);
569 		mutex_enter(&xp->statlock);
570 		if (STAILQ_EMPTY(&xp->sq)) {
571 			mutex_exit(&xp->statlock);
572 			pmcs_unlock_phy(xp->phy);
573 			continue;
574 		}
575 		if (xp->actv_cnt) {
576 			xp->special_needed = 1;
577 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
578 			    "%s: deferring until drained", __func__);
579 			spinagain++;
580 		} else {
581 			if (pmcs_run_sata_special(pwp, xp)) {
582 				spinagain++;
583 			}
584 		}
585 		mutex_exit(&xp->statlock);
586 		pmcs_unlock_phy(xp->phy);
587 	}
588 
589 	if (spinagain) {
590 		SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
591 	} else {
592 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
593 	}
594 
595 	/*
596 	 * Run completion on any commands ready for it.
597 	 */
598 	PMCS_CQ_RUN(pwp);
599 }
600 
601 /*
602  * Called with PHY lock held and scratch acquired
603  */
604 int
605 pmcs_sata_identify(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
606 {
607 	fis_t fis;
608 	fis[0] = (IDENTIFY_DEVICE << 16) | (1 << 15) | FIS_REG_H2DEV;
609 	fis[1] = 0;
610 	fis[2] = 0;
611 	fis[3] = 0;
612 	fis[4] = 0;
613 	return (pmcs_run_sata_cmd(pwp, pptr, fis, SATA_PROTOCOL_PIO,
614 	    PMCIN_DATADIR_2_INI, sizeof (ata_identify_t)));
615 }
616 
617 /*
618  * Called with PHY lock held and scratch held
619  */
620 int
621 pmcs_run_sata_cmd(pmcs_hw_t *pwp, pmcs_phy_t *pptr, fis_t fis, uint32_t mode,
622     uint32_t ddir, uint32_t dlen)
623 {
624 	struct pmcwork *pwrk;
625 	uint32_t *ptr, msg[PMCS_MSG_SIZE];
626 	uint32_t iq, htag, status;
627 	int i, result = 0;
628 
629 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
630 	if (pwrk == NULL) {
631 		return (ENOMEM);
632 	}
633 
634 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
635 	    PMCIN_SATA_HOST_IO_START));
636 	htag = pwrk->htag;
637 	pwrk->arg = msg;
638 	pwrk->dtype = SATA;
639 	msg[1] = LE_32(pwrk->htag);
640 	msg[2] = LE_32(pptr->device_id);
641 	msg[3] = LE_32(dlen);
642 	msg[4] = LE_32(mode | ddir);
643 	if (dlen) {
644 		if (ddir == PMCIN_DATADIR_2_DEV) {
645 			if (ddi_dma_sync(pwp->cip_handles, 0, 0,
646 			    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
647 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
648 				    "Condition check failed at %s():%d",
649 				    __func__, __LINE__);
650 			}
651 		}
652 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
653 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
654 		msg[14] = LE_32(dlen);
655 		msg[15] = 0;
656 	} else {
657 		msg[12] = 0;
658 		msg[13] = 0;
659 		msg[14] = 0;
660 		msg[15] = 0;
661 	}
662 	for (i = 0; i < 5; i++) {
663 		msg[5+i] = LE_32(fis[i]);
664 	}
665 	msg[10] = 0;
666 	msg[11] = 0;
667 	GET_IO_IQ_ENTRY(pwp, ptr, pptr->device_id, iq);
668 	if (ptr == NULL) {
669 		pmcs_pwork(pwp, pwrk);
670 		return (ENOMEM);
671 	}
672 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
673 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
674 	INC_IQ_ENTRY(pwp, iq);
675 
676 	pmcs_unlock_phy(pptr);
677 	WAIT_FOR(pwrk, 1000, result);
678 	pmcs_pwork(pwp, pwrk);
679 	pmcs_lock_phy(pptr);
680 
681 	if (result) {
682 		pmcs_timed_out(pwp, htag, __func__);
683 		if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
684 			pptr->abort_pending = 1;
685 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
686 		}
687 		return (ETIMEDOUT);
688 	}
689 
690 	status = LE_32(msg[2]);
691 
692 	if (status != PMCOUT_STATUS_OK) {
693 		if (status == PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY) {
694 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
695 			    "%s: Potential affiliation active on 0x%" PRIx64,
696 			    __func__, pmcs_barray2wwn(pptr->sas_address));
697 		} else {
698 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, pptr->target,
699 			    "%s: SATA I/O returned with IOMB status 0x%x",
700 			    __func__, status);
701 		}
702 		return (EIO);
703 	}
704 
705 	if (LE_32(ptr[3]) != 0) {
706 		size_t j, amt = LE_32(ptr[3]);
707 		if (amt > sizeof (fis_t)) {
708 			amt = sizeof (fis_t);
709 		}
710 		amt >>= 2;
711 		for (j = 0; j < amt; j++) {
712 			fis[j] = LE_32(msg[4 + j]);
713 		}
714 	}
715 	if (dlen && ddir == PMCIN_DATADIR_2_INI) {
716 		if (ddi_dma_sync(pwp->cip_handles, 0, 0,
717 		    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
718 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
719 			    "Condition check failed at %s():%d",
720 			    __func__, __LINE__);
721 		}
722 	}
723 	return (0);
724 }
725