14c06356bSdh /*
24c06356bSdh  * CDDL HEADER START
34c06356bSdh  *
44c06356bSdh  * The contents of this file are subject to the terms of the
54c06356bSdh  * Common Development and Distribution License (the "License").
64c06356bSdh  * You may not use this file except in compliance with the License.
74c06356bSdh  *
84c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh  * See the License for the specific language governing permissions
114c06356bSdh  * and limitations under the License.
124c06356bSdh  *
134c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh  *
194c06356bSdh  * CDDL HEADER END
204c06356bSdh  *
214c06356bSdh  *
224c06356bSdh  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234c06356bSdh  * Use is subject to license terms.
244c06356bSdh  */
254c06356bSdh /*
26*33f457d3SToomas Soome  * SATA midlayer interface for PMC driver.
274c06356bSdh  */
284c06356bSdh 
294c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
304c06356bSdh 
314c06356bSdh static void
SATAcopy(pmcs_cmd_t * sp,void * kbuf,uint32_t amt)324c06356bSdh SATAcopy(pmcs_cmd_t *sp, void *kbuf, uint32_t amt)
334c06356bSdh {
344c06356bSdh 	struct buf *bp = scsi_pkt2bp(CMD2PKT(sp));
354c06356bSdh 
364c06356bSdh 	bp_mapin(scsi_pkt2bp(CMD2PKT(sp)));
374c06356bSdh 	/* There is only one direction currently */
384c06356bSdh 	(void) memcpy(bp->b_un.b_addr, kbuf, amt);
394c06356bSdh 	CMD2PKT(sp)->pkt_resid -= amt;
404c06356bSdh 	CMD2PKT(sp)->pkt_state |= STATE_XFERRED_DATA;
414c06356bSdh 	bp_mapout(scsi_pkt2bp(CMD2PKT(sp)));
424c06356bSdh }
434c06356bSdh 
444c06356bSdh /*
454c06356bSdh  * Run a non block-io command. Some commands are interpreted
464c06356bSdh  * out of extant data. Some imply actually running a SATA command.
474c06356bSdh  *
484c06356bSdh  * Returns zero if we were able to run.
494c06356bSdh  *
504c06356bSdh  * Returns -1 only if other commands are active, either another
514c06356bSdh  * command here or regular I/O active.
524c06356bSdh  *
534c06356bSdh  * Called with PHY lock and xp statlock held.
544c06356bSdh  */
55*33f457d3SToomas Soome #define	SRESPSZ	132
56*33f457d3SToomas Soome CTASSERT(SRESPSZ == sizeof (struct scsi_inquiry));
574c06356bSdh 
584c06356bSdh static int
pmcs_sata_special_work(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)594c06356bSdh pmcs_sata_special_work(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
604c06356bSdh {
614c06356bSdh 	int i;
624c06356bSdh 	int saq;
634c06356bSdh 	pmcs_cmd_t *sp;
644c06356bSdh 	struct scsi_pkt *pkt;
654c06356bSdh 	pmcs_phy_t *pptr;
664c06356bSdh 	uint8_t rp[SRESPSZ];
674c06356bSdh 	ata_identify_t *id;
684c06356bSdh 	uint32_t amt = 0;
694c06356bSdh 	uint8_t key = 0x05;	/* illegal command */
704c06356bSdh 	uint8_t asc = 0;
714c06356bSdh 	uint8_t ascq = 0;
724c06356bSdh 	uint8_t status = STATUS_GOOD;
734c06356bSdh 
744c06356bSdh 	if (xp->actv_cnt) {
75c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
76c3bc407cSdh 		    "%s: target %p actv count %u",
774c06356bSdh 		    __func__, (void *)xp, xp->actv_cnt);
784c06356bSdh 		return (-1);
794c06356bSdh 	}
804c06356bSdh 	if (xp->special_running) {
81c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
824c06356bSdh 		    "%s: target %p special running already",
834c06356bSdh 		    __func__, (void *)xp);
844c06356bSdh 		return (-1);
854c06356bSdh 	}
864c06356bSdh 	xp->special_needed = 0;
874c06356bSdh 
884c06356bSdh 	/*
894c06356bSdh 	 * We're now running special.
904c06356bSdh 	 */
914c06356bSdh 	xp->special_running = 1;
924c06356bSdh 	pptr = xp->phy;
934c06356bSdh 
944c06356bSdh 	sp = STAILQ_FIRST(&xp->sq);
954c06356bSdh 	if (sp == NULL) {
964c06356bSdh 		xp->special_running = 0;
974c06356bSdh 		return (0);
984c06356bSdh 	}
994c06356bSdh 
1004c06356bSdh 	pkt = CMD2PKT(sp);
101c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
1024c06356bSdh 	    "%s: target %p cmd %p cdb0 %x with actv_cnt %u",
1034c06356bSdh 	    __func__, (void *)xp, (void *)sp, pkt->pkt_cdbp[0], xp->actv_cnt);
1044c06356bSdh 
1054c06356bSdh 	if (pkt->pkt_cdbp[0] == SCMD_INQUIRY ||
1064c06356bSdh 	    pkt->pkt_cdbp[0] == SCMD_READ_CAPACITY) {
1074c06356bSdh 		int retval;
1084c06356bSdh 
1094c06356bSdh 		if (pmcs_acquire_scratch(pwp, B_FALSE)) {
1104c06356bSdh 			xp->special_running = 0;
1114c06356bSdh 			return (-1);
1124c06356bSdh 		}
1134c06356bSdh 		saq = 1;
1144c06356bSdh 
1154c06356bSdh 		mutex_exit(&xp->statlock);
1164c06356bSdh 		retval = pmcs_sata_identify(pwp, pptr);
1174c06356bSdh 		mutex_enter(&xp->statlock);
1184c06356bSdh 
1194c06356bSdh 		if (retval) {
1204c06356bSdh 			pmcs_release_scratch(pwp);
1214c06356bSdh 			xp->special_running = 0;
1224c06356bSdh 
123c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
1244c06356bSdh 			    "%s: target %p identify failed %x",
1254c06356bSdh 			    __func__, (void *)xp, retval);
1264c06356bSdh 			/*
1274c06356bSdh 			 * If the failure is due to not being
1284c06356bSdh 			 * able to get resources, return such
1294c06356bSdh 			 * that we'll try later. Otherwise,
1304c06356bSdh 			 * fail current command.
1314c06356bSdh 			 */
1324c06356bSdh 			if (retval == ENOMEM) {
133c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
1344c06356bSdh 				    "%s: sata identify failed (ENOMEM) for "
1354c06356bSdh 				    "cmd %p", __func__, (void *)sp);
1364c06356bSdh 				return (-1);
1374c06356bSdh 			}
1384c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
1394c06356bSdh 			    STATE_SENT_CMD;
1404c06356bSdh 			if (retval == ETIMEDOUT) {
1414c06356bSdh 				pkt->pkt_reason = CMD_TIMEOUT;
1424c06356bSdh 				pkt->pkt_statistics |= STAT_TIMEOUT;
1434c06356bSdh 			} else {
1444c06356bSdh 				pkt->pkt_reason = CMD_TRAN_ERR;
1454c06356bSdh 			}
1464c06356bSdh 			goto out;
1474c06356bSdh 		}
1484c06356bSdh 
1494c06356bSdh 		id = pwp->scratch;
1504c06356bSdh 
1514c06356bSdh 		/*
1524c06356bSdh 		 * Check to see if this device is an NCQ capable device.
1534c06356bSdh 		 * Yes, we'll end up doing this check for every INQUIRY
1544c06356bSdh 		 * if indeed we *are* only a pio device, but this is so
1554c06356bSdh 		 * infrequent that it's not really worth an extra bitfield.
1564c06356bSdh 		 *
1574c06356bSdh 		 * Note that PIO mode here means that the PMCS firmware
1584c06356bSdh 		 * performs PIO- not us.
1594c06356bSdh 		 */
1604c06356bSdh 		if (xp->ncq == 0) {
1614c06356bSdh 			/*
1624c06356bSdh 			 * Reset existing stuff.
1634c06356bSdh 			 */
1644c06356bSdh 			xp->pio = 0;
1654c06356bSdh 			xp->qdepth = 1;
1664c06356bSdh 			xp->tagmap = 0;
1674c06356bSdh 
1684c06356bSdh 			if (id->word76 != 0 && id->word76 != 0xffff &&
1694c06356bSdh 			    (LE_16(id->word76) & (1 << 8))) {
1704c06356bSdh 				xp->ncq = 1;
1714c06356bSdh 				xp->qdepth = (LE_16(id->word75) & 0x1f) + 1;
172c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
1734c06356bSdh 				    "%s: device %s supports NCQ %u deep",
1744c06356bSdh 				    __func__, xp->phy->path, xp->qdepth);
1754c06356bSdh 			} else {
1764c06356bSdh 				/*
1774c06356bSdh 				 * Default back to PIO.
1784c06356bSdh 				 *
1794c06356bSdh 				 * Note that non-FPDMA would still be possible,
1804c06356bSdh 				 * but for this specific configuration, if it's
1814c06356bSdh 				 * not NCQ it's safest to assume PIO.
1824c06356bSdh 				 */
1834c06356bSdh 				xp->pio = 1;
184c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
1854c06356bSdh 				    "%s: device %s assumed PIO",
1864c06356bSdh 				    __func__, xp->phy->path);
1874c06356bSdh 			}
1884c06356bSdh 		}
1894c06356bSdh 	} else {
1904c06356bSdh 		saq = 0;
1914c06356bSdh 		id = NULL;
1924c06356bSdh 	}
1934c06356bSdh 
1944c06356bSdh 	bzero(rp, SRESPSZ);
1954c06356bSdh 
1964c06356bSdh 	switch (pkt->pkt_cdbp[0]) {
1974c06356bSdh 	case SCMD_INQUIRY:
1984c06356bSdh 	{
1994c06356bSdh 		struct scsi_inquiry *inqp;
2004c06356bSdh 		uint16_t *a, *b;
2014c06356bSdh 
2024c06356bSdh 		/* Check for illegal bits */
2034c06356bSdh 		if ((pkt->pkt_cdbp[1] & 0xfc) || pkt->pkt_cdbp[5]) {
2044c06356bSdh 			status = STATUS_CHECK;
2054c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
2064c06356bSdh 			break;
2074c06356bSdh 		}
2084c06356bSdh 		if (pkt->pkt_cdbp[1] & 0x1) {
2094c06356bSdh 			switch (pkt->pkt_cdbp[2]) {
2104c06356bSdh 			case 0x0:
2114c06356bSdh 				rp[3] = 3;
2124c06356bSdh 				rp[5] = 0x80;
2134c06356bSdh 				rp[6] = 0x83;
2144c06356bSdh 				amt = 7;
2154c06356bSdh 				break;
2164c06356bSdh 			case 0x80:
2174c06356bSdh 				rp[1] = 0x80;
2184c06356bSdh 				rp[3] = 0x14;
2194c06356bSdh 				a = (void *) &rp[4];
2204c06356bSdh 				b = id->model_number;
2214c06356bSdh 				for (i = 0; i < 5; i++) {
2224c06356bSdh 					*a = ddi_swap16(*b);
2234c06356bSdh 					a++;
2244c06356bSdh 					b++;
2254c06356bSdh 				}
2264c06356bSdh 				amt = 24;
2274c06356bSdh 				break;
2284c06356bSdh 			case 0x83:
2294c06356bSdh 				rp[1] = 0x83;
2304c06356bSdh 				if ((LE_16(id->word87) & 0x100) &&
2314c06356bSdh 				    (LE_16(id->word108) >> 12) == 5)  {
2324c06356bSdh 					rp[3] = 12;
2334c06356bSdh 					rp[4] = 1;
2344c06356bSdh 					rp[5] = 3;
2354c06356bSdh 					rp[7] = 8;
2364c06356bSdh 					rp[8] = LE_16(id->word108) >> 8;
2374c06356bSdh 					rp[9] = LE_16(id->word108);
2384c06356bSdh 					rp[10] = LE_16(id->word109) >> 8;
2394c06356bSdh 					rp[11] = LE_16(id->word109);
2404c06356bSdh 					rp[12] = LE_16(id->word110) >> 8;
2414c06356bSdh 					rp[13] = LE_16(id->word110);
2424c06356bSdh 					rp[14] = LE_16(id->word111) >> 8;
2434c06356bSdh 					rp[15] = LE_16(id->word111);
2444c06356bSdh 					amt = 16;
2454c06356bSdh 				} else {
2464c06356bSdh 					rp[3] = 64;
2474c06356bSdh 					rp[4] = 2;
2484c06356bSdh 					rp[5] = 1;
2494c06356bSdh 					rp[7] = 60;
2504c06356bSdh 					rp[8] = 'A';
2514c06356bSdh 					rp[9] = 'T';
2524c06356bSdh 					rp[10] = 'A';
2534c06356bSdh 					rp[11] = ' ';
2544c06356bSdh 					rp[12] = ' ';
2554c06356bSdh 					rp[13] = ' ';
2564c06356bSdh 					rp[14] = ' ';
2574c06356bSdh 					rp[15] = ' ';
2584c06356bSdh 					a = (void *) &rp[16];
2594c06356bSdh 					b = id->model_number;
2604c06356bSdh 					for (i = 0; i < 20; i++) {
2614c06356bSdh 						*a = ddi_swap16(*b);
2624c06356bSdh 						a++;
2634c06356bSdh 						b++;
2644c06356bSdh 					}
2654c06356bSdh 					a = (void *) &rp[40];
2664c06356bSdh 					b = id->serial_number;
2674c06356bSdh 					for (i = 0; i < 10; i++) {
2684c06356bSdh 						*a = ddi_swap16(*b);
2694c06356bSdh 						a++;
2704c06356bSdh 						b++;
2714c06356bSdh 					}
2724c06356bSdh 					amt = 68;
2734c06356bSdh 				}
2744c06356bSdh 				break;
2754c06356bSdh 			default:
2764c06356bSdh 				status = STATUS_CHECK;
2774c06356bSdh 				asc = 0x24;	/* invalid field in cdb */
2784c06356bSdh 				break;
2794c06356bSdh 			}
2804c06356bSdh 		} else {
2814c06356bSdh 			inqp = (struct scsi_inquiry *)rp;
282a02e8111SRichard Lowe 			inqp->inq_qual = 0;
2834c06356bSdh 			inqp->inq_ansi = 5;	/* spc3 */
2844c06356bSdh 			inqp->inq_rdf = 2;	/* response format 2 */
2854c06356bSdh 			inqp->inq_len = 32;
2864c06356bSdh 
2874c06356bSdh 			if (xp->ncq && (xp->qdepth > 1)) {
2884c06356bSdh 				inqp->inq_cmdque = 1;
2894c06356bSdh 			}
2904c06356bSdh 
2914c06356bSdh 			(void) memcpy(inqp->inq_vid, "ATA     ", 8);
2924c06356bSdh 
2934c06356bSdh 			a = (void *)inqp->inq_pid;
2944c06356bSdh 			b = id->model_number;
2954c06356bSdh 			for (i = 0; i < 8; i++) {
2964c06356bSdh 				*a = ddi_swap16(*b);
2974c06356bSdh 				a++;
2984c06356bSdh 				b++;
2994c06356bSdh 			}
3004c06356bSdh 			if (id->firmware_revision[2] == 0x2020 &&
3014c06356bSdh 			    id->firmware_revision[3] == 0x2020) {
3024c06356bSdh 				inqp->inq_revision[0] =
3034c06356bSdh 				    ddi_swap16(id->firmware_revision[0]) >> 8;
3044c06356bSdh 				inqp->inq_revision[1] =
3054c06356bSdh 				    ddi_swap16(id->firmware_revision[0]);
3064c06356bSdh 				inqp->inq_revision[2] =
3074c06356bSdh 				    ddi_swap16(id->firmware_revision[1]) >> 8;
3084c06356bSdh 				inqp->inq_revision[3] =
3094c06356bSdh 				    ddi_swap16(id->firmware_revision[1]);
3104c06356bSdh 			} else {
3114c06356bSdh 				inqp->inq_revision[0] =
3124c06356bSdh 				    ddi_swap16(id->firmware_revision[2]) >> 8;
3134c06356bSdh 				inqp->inq_revision[1] =
3144c06356bSdh 				    ddi_swap16(id->firmware_revision[2]);
3154c06356bSdh 				inqp->inq_revision[2] =
3164c06356bSdh 				    ddi_swap16(id->firmware_revision[3]) >> 8;
3174c06356bSdh 				inqp->inq_revision[3] =
3184c06356bSdh 				    ddi_swap16(id->firmware_revision[3]);
3194c06356bSdh 			}
3204c06356bSdh 			amt = 36;
3214c06356bSdh 		}
3224c06356bSdh 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
3234c06356bSdh 		if (amt) {
3244c06356bSdh 			if (xp->actv_cnt) {
3254c06356bSdh 				xp->special_needed = 1;
3264c06356bSdh 				xp->special_running = 0;
327c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
328c3bc407cSdh 				    "%s: @ line %d", __func__, __LINE__);
3294c06356bSdh 				if (saq) {
3304c06356bSdh 					pmcs_release_scratch(pwp);
3314c06356bSdh 				}
3324c06356bSdh 				return (-1);
3334c06356bSdh 			}
3344c06356bSdh 			SATAcopy(sp, rp, amt);
3354c06356bSdh 		}
3364c06356bSdh 		break;
3374c06356bSdh 	}
3384c06356bSdh 	case SCMD_READ_CAPACITY:
3394c06356bSdh 	{
3404c06356bSdh 		uint64_t last_block;
3414c06356bSdh 		uint32_t block_size = 512;	/* XXXX */
3424c06356bSdh 
3434c06356bSdh 		xp->capacity = LBA_CAPACITY(id);
3444c06356bSdh 		last_block = xp->capacity - 1;
3454c06356bSdh 		/* Check for illegal bits */
3464c06356bSdh 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[6] ||
3474c06356bSdh 		    (pkt->pkt_cdbp[8] & 0xfe) || pkt->pkt_cdbp[7] ||
3484c06356bSdh 		    pkt->pkt_cdbp[9]) {
3494c06356bSdh 			status = STATUS_CHECK;
3504c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
3514c06356bSdh 			break;
3524c06356bSdh 		}
3534c06356bSdh 		for (i = 1; i < 10; i++) {
3544c06356bSdh 			if (pkt->pkt_cdbp[i]) {
3554c06356bSdh 				status = STATUS_CHECK;
3564c06356bSdh 				asc = 0x24;	/* invalid field in cdb */
3574c06356bSdh 				break;
3584c06356bSdh 			}
3594c06356bSdh 		}
3604c06356bSdh 		if (status != STATUS_GOOD) {
3614c06356bSdh 			break;
3624c06356bSdh 		}
3634c06356bSdh 		if (last_block > 0xffffffffULL) {
3644c06356bSdh 			last_block = 0xffffffffULL;
3654c06356bSdh 		}
3664c06356bSdh 		rp[0] = (last_block >> 24) & 0xff;
3674c06356bSdh 		rp[1] = (last_block >> 16) & 0xff;
3684c06356bSdh 		rp[2] = (last_block >>  8) & 0xff;
3694c06356bSdh 		rp[3] = (last_block) & 0xff;
3704c06356bSdh 		rp[4] = (block_size >> 24) & 0xff;
3714c06356bSdh 		rp[5] = (block_size >> 16) & 0xff;
3724c06356bSdh 		rp[6] = (block_size >>  8) & 0xff;
3734c06356bSdh 		rp[7] = (block_size) & 0xff;
3744c06356bSdh 		amt = 8;
3754c06356bSdh 		amt = pmcs_set_resid(pkt, amt, 8);
3764c06356bSdh 		if (amt) {
3774c06356bSdh 			if (xp->actv_cnt) {
3784c06356bSdh 				xp->special_needed = 1;
3794c06356bSdh 				xp->special_running = 0;
380c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
381c3bc407cSdh 				    "%s: @ line %d", __func__, __LINE__);
3824c06356bSdh 				if (saq) {
3834c06356bSdh 					pmcs_release_scratch(pwp);
3844c06356bSdh 				}
3854c06356bSdh 				return (-1);
3864c06356bSdh 			}
3874c06356bSdh 			SATAcopy(sp, rp, amt);
3884c06356bSdh 		}
3894c06356bSdh 		break;
3904c06356bSdh 	}
3914c06356bSdh 	case SCMD_REPORT_LUNS: {
3924c06356bSdh 		int rl_len;
3934c06356bSdh 
3944c06356bSdh 		/* Check for illegal bits */
3954c06356bSdh 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[3] || pkt->pkt_cdbp[4] ||
3964c06356bSdh 		    pkt->pkt_cdbp[5] || pkt->pkt_cdbp[10] ||
3974c06356bSdh 		    pkt->pkt_cdbp[11]) {
3984c06356bSdh 			status = STATUS_CHECK;
3994c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
4004c06356bSdh 			break;
4014c06356bSdh 		}
4024c06356bSdh 
4034c06356bSdh 		rp[3] = 8;
4044c06356bSdh 		rl_len = 16;	/* list length (4) + reserved (4) + 1 LUN (8) */
4054c06356bSdh 		amt = rl_len;
4064c06356bSdh 		amt = pmcs_set_resid(pkt, amt, rl_len);
4074c06356bSdh 
4084c06356bSdh 		if (amt) {
4094c06356bSdh 			if (xp->actv_cnt) {
4104c06356bSdh 				xp->special_needed = 1;
4114c06356bSdh 				xp->special_running = 0;
412c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
413c3bc407cSdh 				    "%s: @ line %d", __func__, __LINE__);
4144c06356bSdh 				if (saq) {
4154c06356bSdh 					pmcs_release_scratch(pwp);
4164c06356bSdh 				}
4174c06356bSdh 				return (-1);
4184c06356bSdh 			}
4194c06356bSdh 			SATAcopy(sp, rp, rl_len);
4204c06356bSdh 		}
4214c06356bSdh 		break;
4224c06356bSdh 	}
4234c06356bSdh 
4244c06356bSdh 	case SCMD_REQUEST_SENSE:
4254c06356bSdh 		/* Check for illegal bits */
4264c06356bSdh 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
4274c06356bSdh 		    pkt->pkt_cdbp[3] || pkt->pkt_cdbp[5]) {
4284c06356bSdh 			status = STATUS_CHECK;
4294c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
4304c06356bSdh 			break;
4314c06356bSdh 		}
4324c06356bSdh 		rp[0] = 0xf0;
4334c06356bSdh 		amt = 18;
4344c06356bSdh 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
4354c06356bSdh 		if (amt) {
4364c06356bSdh 			if (xp->actv_cnt) {
4374c06356bSdh 				xp->special_needed = 1;
4384c06356bSdh 				xp->special_running = 0;
439c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
440c3bc407cSdh 				    "%s: @ line %d", __func__, __LINE__);
4414c06356bSdh 				if (saq) {
4424c06356bSdh 					pmcs_release_scratch(pwp);
4434c06356bSdh 				}
4444c06356bSdh 				return (-1);
4454c06356bSdh 			}
4464c06356bSdh 			SATAcopy(sp, rp, 18);
4474c06356bSdh 		}
4484c06356bSdh 		break;
4494c06356bSdh 	case SCMD_START_STOP:
4504c06356bSdh 		/* Check for illegal bits */
4514c06356bSdh 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
4524c06356bSdh 		    (pkt->pkt_cdbp[3] & 0xf0) || (pkt->pkt_cdbp[4] & 0x08) ||
4534c06356bSdh 		    pkt->pkt_cdbp[5]) {
4544c06356bSdh 			status = STATUS_CHECK;
4554c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
4564c06356bSdh 			break;
4574c06356bSdh 		}
4584c06356bSdh 		break;
4594c06356bSdh 	case SCMD_SYNCHRONIZE_CACHE:
4604c06356bSdh 		/* Check for illegal bits */
4614c06356bSdh 		if ((pkt->pkt_cdbp[1] & 0xf8) || (pkt->pkt_cdbp[6] & 0xe0) ||
4624c06356bSdh 		    pkt->pkt_cdbp[9]) {
4634c06356bSdh 			status = STATUS_CHECK;
4644c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
4654c06356bSdh 			break;
4664c06356bSdh 		}
4674c06356bSdh 		break;
4684c06356bSdh 	case SCMD_TEST_UNIT_READY:
4694c06356bSdh 		/* Check for illegal bits */
4704c06356bSdh 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[2] || pkt->pkt_cdbp[3] ||
4714c06356bSdh 		    pkt->pkt_cdbp[4] || pkt->pkt_cdbp[5]) {
4724c06356bSdh 			status = STATUS_CHECK;
4734c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
4744c06356bSdh 			break;
4754c06356bSdh 		}
4764c06356bSdh 		if (xp->ca) {
4774c06356bSdh 			status = STATUS_CHECK;
4784c06356bSdh 			key = 0x6;
4794c06356bSdh 			asc = 0x28;
4804c06356bSdh 			xp->ca = 0;
4814c06356bSdh 		}
4824c06356bSdh 		break;
4834c06356bSdh 	default:
4844c06356bSdh 		asc = 0x20;	/* invalid operation command code */
4854c06356bSdh 		status = STATUS_CHECK;
4864c06356bSdh 		break;
4874c06356bSdh 	}
4884c06356bSdh 	if (status != STATUS_GOOD) {
4894c06356bSdh 		bzero(rp, 18);
4904c06356bSdh 		rp[0] = 0xf0;
4914c06356bSdh 		rp[2] = key;
4924c06356bSdh 		rp[12] = asc;
4934c06356bSdh 		rp[13] = ascq;
4944c06356bSdh 		pmcs_latch_status(pwp, sp, status, rp, 18, pptr->path);
4954c06356bSdh 	} else {
4964c06356bSdh 		pmcs_latch_status(pwp, sp, status, NULL, 0, pptr->path);
4974c06356bSdh 	}
4984c06356bSdh 
4994c06356bSdh out:
5004c06356bSdh 	STAILQ_REMOVE_HEAD(&xp->sq, cmd_next);
501c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
5024c06356bSdh 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
5034c06356bSdh 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
5044c06356bSdh 	    pkt->pkt_state, pkt->pkt_resid, status);
5054c06356bSdh 
5064c06356bSdh 	if (saq) {
5074c06356bSdh 		pmcs_release_scratch(pwp);
5084c06356bSdh 	}
5094c06356bSdh 
5104c06356bSdh 	if (xp->draining) {
511c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5124c06356bSdh 		    "%s: waking up drain waiters", __func__);
5134c06356bSdh 		cv_signal(&pwp->drain_cv);
5144c06356bSdh 	}
5154c06356bSdh 
5164c06356bSdh 	mutex_exit(&xp->statlock);
5174c06356bSdh 	mutex_enter(&pwp->cq_lock);
5184c06356bSdh 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
5194c06356bSdh 	PMCS_CQ_RUN_LOCKED(pwp);
5204c06356bSdh 	mutex_exit(&pwp->cq_lock);
5214c06356bSdh 	mutex_enter(&xp->statlock);
5224c06356bSdh 	xp->special_running = 0;
5234c06356bSdh 	return (0);
5244c06356bSdh }
5254c06356bSdh 
5264c06356bSdh /*
5274c06356bSdh  * Run all special commands queued up for a SATA device.
5284c06356bSdh  * We're only called if the caller knows we have work to do.
5294c06356bSdh  *
5304c06356bSdh  * We can't run them if things are still active for the device,
5314c06356bSdh  * return saying we didn't run anything.
5324c06356bSdh  *
5334c06356bSdh  * When we finish, wake up anyone waiting for active commands
5344c06356bSdh  * to go to zero.
5354c06356bSdh  *
5364c06356bSdh  * Called with PHY lock and xp statlock held.
5374c06356bSdh  */
5384c06356bSdh int
pmcs_run_sata_special(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)5394c06356bSdh pmcs_run_sata_special(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
5404c06356bSdh {
5414c06356bSdh 	while (!STAILQ_EMPTY(&xp->sq)) {
5424c06356bSdh 		if (pmcs_sata_special_work(pwp, xp)) {
5434c06356bSdh 			return (-1);
5444c06356bSdh 		}
5454c06356bSdh 	}
5464c06356bSdh 	return (0);
5474c06356bSdh }
5484c06356bSdh 
5494c06356bSdh /*
5504c06356bSdh  * Search for SATA special commands to run and run them.
5514c06356bSdh  * If we succeed in running the special command(s), kick
5524c06356bSdh  * the normal commands into operation again. Call completion
5534c06356bSdh  * for any commands that were completed while we were here.
5544c06356bSdh  *
5554c06356bSdh  * Called unlocked.
5564c06356bSdh  */
5574c06356bSdh void
pmcs_sata_work(pmcs_hw_t * pwp)5584c06356bSdh pmcs_sata_work(pmcs_hw_t *pwp)
5594c06356bSdh {
5604c06356bSdh 	pmcs_xscsi_t *xp;
5614c06356bSdh 	int spinagain = 0;
5624c06356bSdh 	uint16_t target;
5634c06356bSdh 
5644c06356bSdh 	for (target = 0; target < pwp->max_dev; target++) {
5654c06356bSdh 		xp = pwp->targets[target];
5664c06356bSdh 		if ((xp == NULL) || (xp->phy == NULL)) {
5674c06356bSdh 			continue;
5684c06356bSdh 		}
5694c06356bSdh 		pmcs_lock_phy(xp->phy);
5704c06356bSdh 		mutex_enter(&xp->statlock);
5714c06356bSdh 		if (STAILQ_EMPTY(&xp->sq)) {
5724c06356bSdh 			mutex_exit(&xp->statlock);
5734c06356bSdh 			pmcs_unlock_phy(xp->phy);
5744c06356bSdh 			continue;
5754c06356bSdh 		}
5764c06356bSdh 		if (xp->actv_cnt) {
5774c06356bSdh 			xp->special_needed = 1;
578c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
5794c06356bSdh 			    "%s: deferring until drained", __func__);
5804c06356bSdh 			spinagain++;
5814c06356bSdh 		} else {
5824c06356bSdh 			if (pmcs_run_sata_special(pwp, xp)) {
5834c06356bSdh 				spinagain++;
5844c06356bSdh 			}
5854c06356bSdh 		}
5864c06356bSdh 		mutex_exit(&xp->statlock);
5874c06356bSdh 		pmcs_unlock_phy(xp->phy);
5884c06356bSdh 	}
5894c06356bSdh 
5904c06356bSdh 	if (spinagain) {
5914c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
5924c06356bSdh 	} else {
5934c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
5944c06356bSdh 	}
5954c06356bSdh 
5964c06356bSdh 	/*
5974c06356bSdh 	 * Run completion on any commands ready for it.
5984c06356bSdh 	 */
5994c06356bSdh 	PMCS_CQ_RUN(pwp);
6004c06356bSdh }
6014c06356bSdh 
6024c06356bSdh /*
6034c06356bSdh  * Called with PHY lock held and scratch acquired
6044c06356bSdh  */
6054c06356bSdh int
pmcs_sata_identify(pmcs_hw_t * pwp,pmcs_phy_t * pptr)6064c06356bSdh pmcs_sata_identify(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
6074c06356bSdh {
6084c06356bSdh 	fis_t fis;
6094c06356bSdh 	fis[0] = (IDENTIFY_DEVICE << 16) | (1 << 15) | FIS_REG_H2DEV;
6104c06356bSdh 	fis[1] = 0;
6114c06356bSdh 	fis[2] = 0;
6124c06356bSdh 	fis[3] = 0;
6134c06356bSdh 	fis[4] = 0;
6144c06356bSdh 	return (pmcs_run_sata_cmd(pwp, pptr, fis, SATA_PROTOCOL_PIO,
6154c06356bSdh 	    PMCIN_DATADIR_2_INI, sizeof (ata_identify_t)));
6164c06356bSdh }
6174c06356bSdh 
6184c06356bSdh /*
6194c06356bSdh  * Called with PHY lock held and scratch held
6204c06356bSdh  */
6214c06356bSdh 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)6224c06356bSdh pmcs_run_sata_cmd(pmcs_hw_t *pwp, pmcs_phy_t *pptr, fis_t fis, uint32_t mode,
6234c06356bSdh     uint32_t ddir, uint32_t dlen)
6244c06356bSdh {
6254c06356bSdh 	struct pmcwork *pwrk;
6264c06356bSdh 	uint32_t *ptr, msg[PMCS_MSG_SIZE];
627499cfd15SDavid Hollister 	uint32_t iq, htag, status;
6284c06356bSdh 	int i, result = 0;
6294c06356bSdh 
6304c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
6314c06356bSdh 	if (pwrk == NULL) {
6324c06356bSdh 		return (ENOMEM);
6334c06356bSdh 	}
6344c06356bSdh 
6354c06356bSdh 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
6364c06356bSdh 	    PMCIN_SATA_HOST_IO_START));
6374c06356bSdh 	htag = pwrk->htag;
6384c06356bSdh 	pwrk->arg = msg;
6394c06356bSdh 	pwrk->dtype = SATA;
6404c06356bSdh 	msg[1] = LE_32(pwrk->htag);
6414c06356bSdh 	msg[2] = LE_32(pptr->device_id);
6424c06356bSdh 	msg[3] = LE_32(dlen);
6434c06356bSdh 	msg[4] = LE_32(mode | ddir);
6444c06356bSdh 	if (dlen) {
6454c06356bSdh 		if (ddir == PMCIN_DATADIR_2_DEV) {
6464c06356bSdh 			if (ddi_dma_sync(pwp->cip_handles, 0, 0,
6474c06356bSdh 			    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
648c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
649c3bc407cSdh 				    "Condition check failed at %s():%d",
650c3bc407cSdh 				    __func__, __LINE__);
6514c06356bSdh 			}
6524c06356bSdh 		}
6534c06356bSdh 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
6544c06356bSdh 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
6554c06356bSdh 		msg[14] = LE_32(dlen);
6564c06356bSdh 		msg[15] = 0;
6574c06356bSdh 	} else {
6584c06356bSdh 		msg[12] = 0;
6594c06356bSdh 		msg[13] = 0;
6604c06356bSdh 		msg[14] = 0;
6614c06356bSdh 		msg[15] = 0;
6624c06356bSdh 	}
6634c06356bSdh 	for (i = 0; i < 5; i++) {
6644c06356bSdh 		msg[5+i] = LE_32(fis[i]);
6654c06356bSdh 	}
6664c06356bSdh 	msg[10] = 0;
6674c06356bSdh 	msg[11] = 0;
6684c06356bSdh 	GET_IO_IQ_ENTRY(pwp, ptr, pptr->device_id, iq);
6694c06356bSdh 	if (ptr == NULL) {
6704c06356bSdh 		pmcs_pwork(pwp, pwrk);
6714c06356bSdh 		return (ENOMEM);
6724c06356bSdh 	}
6734c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
6744c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6754c06356bSdh 	INC_IQ_ENTRY(pwp, iq);
6764c06356bSdh 
6774c06356bSdh 	pmcs_unlock_phy(pptr);
6784c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
6794c06356bSdh 	pmcs_pwork(pwp, pwrk);
6804c06356bSdh 	pmcs_lock_phy(pptr);
6814c06356bSdh 
6824c06356bSdh 	if (result) {
6834c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
6844c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
6854c06356bSdh 			pptr->abort_pending = 1;
6864c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6874c06356bSdh 		}
6884c06356bSdh 		return (ETIMEDOUT);
6894c06356bSdh 	}
6904c06356bSdh 
691499cfd15SDavid Hollister 	status = LE_32(msg[2]);
692499cfd15SDavid Hollister 
693499cfd15SDavid Hollister 	if (status != PMCOUT_STATUS_OK) {
694499cfd15SDavid Hollister 		if (status == PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY) {
695499cfd15SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
696499cfd15SDavid Hollister 			    "%s: Potential affiliation active on 0x%" PRIx64,
697499cfd15SDavid Hollister 			    __func__, pmcs_barray2wwn(pptr->sas_address));
698499cfd15SDavid Hollister 		} else {
699499cfd15SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, pptr->target,
700499cfd15SDavid Hollister 			    "%s: SATA I/O returned with IOMB status 0x%x",
701499cfd15SDavid Hollister 			    __func__, status);
702499cfd15SDavid Hollister 		}
7034c06356bSdh 		return (EIO);
7044c06356bSdh 	}
705499cfd15SDavid Hollister 
7064c06356bSdh 	if (LE_32(ptr[3]) != 0) {
7074c06356bSdh 		size_t j, amt = LE_32(ptr[3]);
7084c06356bSdh 		if (amt > sizeof (fis_t)) {
7094c06356bSdh 			amt = sizeof (fis_t);
7104c06356bSdh 		}
7114c06356bSdh 		amt >>= 2;
7124c06356bSdh 		for (j = 0; j < amt; j++) {
7134c06356bSdh 			fis[j] = LE_32(msg[4 + j]);
7144c06356bSdh 		}
7154c06356bSdh 	}
7164c06356bSdh 	if (dlen && ddir == PMCIN_DATADIR_2_INI) {
7174c06356bSdh 		if (ddi_dma_sync(pwp->cip_handles, 0, 0,
7184c06356bSdh 		    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
719c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
720c3bc407cSdh 			    "Condition check failed at %s():%d",
721c3bc407cSdh 			    __func__, __LINE__);
7224c06356bSdh 		}
7234c06356bSdh 	}
7244c06356bSdh 	return (0);
7254c06356bSdh }
726