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