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