xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_sata.c (revision 11307:94401ad61b17)
110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM  * CDDL HEADER START
310696SDavid.Hollister@Sun.COM  *
410696SDavid.Hollister@Sun.COM  * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM  * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM  * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM  *
810696SDavid.Hollister@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM  * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM  * and limitations under the License.
1210696SDavid.Hollister@Sun.COM  *
1310696SDavid.Hollister@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM  *
1910696SDavid.Hollister@Sun.COM  * CDDL HEADER END
2010696SDavid.Hollister@Sun.COM  *
2110696SDavid.Hollister@Sun.COM  *
2210696SDavid.Hollister@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2310696SDavid.Hollister@Sun.COM  * Use is subject to license terms.
2410696SDavid.Hollister@Sun.COM  */
2510696SDavid.Hollister@Sun.COM /*
2610696SDavid.Hollister@Sun.COM  * SATA midlayer interface for PMC drier.
2710696SDavid.Hollister@Sun.COM  */
2810696SDavid.Hollister@Sun.COM 
2910696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
3010696SDavid.Hollister@Sun.COM 
3110696SDavid.Hollister@Sun.COM static void
SATAcopy(pmcs_cmd_t * sp,void * kbuf,uint32_t amt)3210696SDavid.Hollister@Sun.COM SATAcopy(pmcs_cmd_t *sp, void *kbuf, uint32_t amt)
3310696SDavid.Hollister@Sun.COM {
3410696SDavid.Hollister@Sun.COM 	struct buf *bp = scsi_pkt2bp(CMD2PKT(sp));
3510696SDavid.Hollister@Sun.COM 
3610696SDavid.Hollister@Sun.COM 	bp_mapin(scsi_pkt2bp(CMD2PKT(sp)));
3710696SDavid.Hollister@Sun.COM 	/* There is only one direction currently */
3810696SDavid.Hollister@Sun.COM 	(void) memcpy(bp->b_un.b_addr, kbuf, amt);
3910696SDavid.Hollister@Sun.COM 	CMD2PKT(sp)->pkt_resid -= amt;
4010696SDavid.Hollister@Sun.COM 	CMD2PKT(sp)->pkt_state |= STATE_XFERRED_DATA;
4110696SDavid.Hollister@Sun.COM 	bp_mapout(scsi_pkt2bp(CMD2PKT(sp)));
4210696SDavid.Hollister@Sun.COM }
4310696SDavid.Hollister@Sun.COM 
4410696SDavid.Hollister@Sun.COM /*
4510696SDavid.Hollister@Sun.COM  * Run a non block-io command. Some commands are interpreted
4610696SDavid.Hollister@Sun.COM  * out of extant data. Some imply actually running a SATA command.
4710696SDavid.Hollister@Sun.COM  *
4810696SDavid.Hollister@Sun.COM  * Returns zero if we were able to run.
4910696SDavid.Hollister@Sun.COM  *
5010696SDavid.Hollister@Sun.COM  * Returns -1 only if other commands are active, either another
5110696SDavid.Hollister@Sun.COM  * command here or regular I/O active.
5210696SDavid.Hollister@Sun.COM  *
5310696SDavid.Hollister@Sun.COM  * Called with PHY lock and xp statlock held.
5410696SDavid.Hollister@Sun.COM  */
5510696SDavid.Hollister@Sun.COM #define	SRESPSZ	128
5610696SDavid.Hollister@Sun.COM 
5710696SDavid.Hollister@Sun.COM static int
pmcs_sata_special_work(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)5810696SDavid.Hollister@Sun.COM pmcs_sata_special_work(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
5910696SDavid.Hollister@Sun.COM {
6010696SDavid.Hollister@Sun.COM 	int i;
6110696SDavid.Hollister@Sun.COM 	int saq;
6210696SDavid.Hollister@Sun.COM 	pmcs_cmd_t *sp;
6310696SDavid.Hollister@Sun.COM 	struct scsi_pkt *pkt;
6410696SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr;
6510696SDavid.Hollister@Sun.COM 	uint8_t rp[SRESPSZ];
6610696SDavid.Hollister@Sun.COM 	ata_identify_t *id;
6710696SDavid.Hollister@Sun.COM 	uint32_t amt = 0;
6810696SDavid.Hollister@Sun.COM 	uint8_t key = 0x05;	/* illegal command */
6910696SDavid.Hollister@Sun.COM 	uint8_t asc = 0;
7010696SDavid.Hollister@Sun.COM 	uint8_t ascq = 0;
7110696SDavid.Hollister@Sun.COM 	uint8_t status = STATUS_GOOD;
7210696SDavid.Hollister@Sun.COM 
7310696SDavid.Hollister@Sun.COM 	if (xp->actv_cnt) {
7411048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
7511048SDavid.Hollister@Sun.COM 		    "%s: target %p actv count %u",
7610696SDavid.Hollister@Sun.COM 		    __func__, (void *)xp, xp->actv_cnt);
7710696SDavid.Hollister@Sun.COM 		return (-1);
7810696SDavid.Hollister@Sun.COM 	}
7910696SDavid.Hollister@Sun.COM 	if (xp->special_running) {
8011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
8110696SDavid.Hollister@Sun.COM 		    "%s: target %p special running already",
8210696SDavid.Hollister@Sun.COM 		    __func__, (void *)xp);
8310696SDavid.Hollister@Sun.COM 		return (-1);
8410696SDavid.Hollister@Sun.COM 	}
8510696SDavid.Hollister@Sun.COM 	xp->special_needed = 0;
8610696SDavid.Hollister@Sun.COM 
8710696SDavid.Hollister@Sun.COM 	/*
8810696SDavid.Hollister@Sun.COM 	 * We're now running special.
8910696SDavid.Hollister@Sun.COM 	 */
9010696SDavid.Hollister@Sun.COM 	xp->special_running = 1;
9110696SDavid.Hollister@Sun.COM 	pptr = xp->phy;
9210696SDavid.Hollister@Sun.COM 
9310696SDavid.Hollister@Sun.COM 	sp = STAILQ_FIRST(&xp->sq);
9410696SDavid.Hollister@Sun.COM 	if (sp == NULL) {
9510696SDavid.Hollister@Sun.COM 		xp->special_running = 0;
9610696SDavid.Hollister@Sun.COM 		return (0);
9710696SDavid.Hollister@Sun.COM 	}
9810696SDavid.Hollister@Sun.COM 
9910696SDavid.Hollister@Sun.COM 	pkt = CMD2PKT(sp);
10011048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
10110696SDavid.Hollister@Sun.COM 	    "%s: target %p cmd %p cdb0 %x with actv_cnt %u",
10210696SDavid.Hollister@Sun.COM 	    __func__, (void *)xp, (void *)sp, pkt->pkt_cdbp[0], xp->actv_cnt);
10310696SDavid.Hollister@Sun.COM 
10410696SDavid.Hollister@Sun.COM 	if (pkt->pkt_cdbp[0] == SCMD_INQUIRY ||
10510696SDavid.Hollister@Sun.COM 	    pkt->pkt_cdbp[0] == SCMD_READ_CAPACITY) {
10610696SDavid.Hollister@Sun.COM 		int retval;
10710696SDavid.Hollister@Sun.COM 
10810696SDavid.Hollister@Sun.COM 		if (pmcs_acquire_scratch(pwp, B_FALSE)) {
10910696SDavid.Hollister@Sun.COM 			xp->special_running = 0;
11010696SDavid.Hollister@Sun.COM 			return (-1);
11110696SDavid.Hollister@Sun.COM 		}
11210696SDavid.Hollister@Sun.COM 		saq = 1;
11310696SDavid.Hollister@Sun.COM 
11410696SDavid.Hollister@Sun.COM 		mutex_exit(&xp->statlock);
11510696SDavid.Hollister@Sun.COM 		retval = pmcs_sata_identify(pwp, pptr);
11610696SDavid.Hollister@Sun.COM 		mutex_enter(&xp->statlock);
11710696SDavid.Hollister@Sun.COM 
11810696SDavid.Hollister@Sun.COM 		if (retval) {
11910696SDavid.Hollister@Sun.COM 			pmcs_release_scratch(pwp);
12010696SDavid.Hollister@Sun.COM 			xp->special_running = 0;
12110696SDavid.Hollister@Sun.COM 
12211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
12310696SDavid.Hollister@Sun.COM 			    "%s: target %p identify failed %x",
12410696SDavid.Hollister@Sun.COM 			    __func__, (void *)xp, retval);
12510696SDavid.Hollister@Sun.COM 			/*
12610696SDavid.Hollister@Sun.COM 			 * If the failure is due to not being
12710696SDavid.Hollister@Sun.COM 			 * able to get resources, return such
12810696SDavid.Hollister@Sun.COM 			 * that we'll try later. Otherwise,
12910696SDavid.Hollister@Sun.COM 			 * fail current command.
13010696SDavid.Hollister@Sun.COM 			 */
13110696SDavid.Hollister@Sun.COM 			if (retval == ENOMEM) {
13211048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
13310696SDavid.Hollister@Sun.COM 				    "%s: sata identify failed (ENOMEM) for "
13410696SDavid.Hollister@Sun.COM 				    "cmd %p", __func__, (void *)sp);
13510696SDavid.Hollister@Sun.COM 				return (-1);
13610696SDavid.Hollister@Sun.COM 			}
13710696SDavid.Hollister@Sun.COM 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
13810696SDavid.Hollister@Sun.COM 			    STATE_SENT_CMD;
13910696SDavid.Hollister@Sun.COM 			if (retval == ETIMEDOUT) {
14010696SDavid.Hollister@Sun.COM 				pkt->pkt_reason = CMD_TIMEOUT;
14110696SDavid.Hollister@Sun.COM 				pkt->pkt_statistics |= STAT_TIMEOUT;
14210696SDavid.Hollister@Sun.COM 			} else {
14310696SDavid.Hollister@Sun.COM 				pkt->pkt_reason = CMD_TRAN_ERR;
14410696SDavid.Hollister@Sun.COM 			}
14510696SDavid.Hollister@Sun.COM 			goto out;
14610696SDavid.Hollister@Sun.COM 		}
14710696SDavid.Hollister@Sun.COM 
14810696SDavid.Hollister@Sun.COM 		id = pwp->scratch;
14910696SDavid.Hollister@Sun.COM 
15010696SDavid.Hollister@Sun.COM 		/*
15110696SDavid.Hollister@Sun.COM 		 * Check to see if this device is an NCQ capable device.
15210696SDavid.Hollister@Sun.COM 		 * Yes, we'll end up doing this check for every INQUIRY
15310696SDavid.Hollister@Sun.COM 		 * if indeed we *are* only a pio device, but this is so
15410696SDavid.Hollister@Sun.COM 		 * infrequent that it's not really worth an extra bitfield.
15510696SDavid.Hollister@Sun.COM 		 *
15610696SDavid.Hollister@Sun.COM 		 * Note that PIO mode here means that the PMCS firmware
15710696SDavid.Hollister@Sun.COM 		 * performs PIO- not us.
15810696SDavid.Hollister@Sun.COM 		 */
15910696SDavid.Hollister@Sun.COM 		if (xp->ncq == 0) {
16010696SDavid.Hollister@Sun.COM 			/*
16110696SDavid.Hollister@Sun.COM 			 * Reset existing stuff.
16210696SDavid.Hollister@Sun.COM 			 */
16310696SDavid.Hollister@Sun.COM 			xp->pio = 0;
16410696SDavid.Hollister@Sun.COM 			xp->qdepth = 1;
16510696SDavid.Hollister@Sun.COM 			xp->tagmap = 0;
16610696SDavid.Hollister@Sun.COM 
16710696SDavid.Hollister@Sun.COM 			if (id->word76 != 0 && id->word76 != 0xffff &&
16810696SDavid.Hollister@Sun.COM 			    (LE_16(id->word76) & (1 << 8))) {
16910696SDavid.Hollister@Sun.COM 				xp->ncq = 1;
17010696SDavid.Hollister@Sun.COM 				xp->qdepth = (LE_16(id->word75) & 0x1f) + 1;
17111048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
17210696SDavid.Hollister@Sun.COM 				    "%s: device %s supports NCQ %u deep",
17310696SDavid.Hollister@Sun.COM 				    __func__, xp->phy->path, xp->qdepth);
17410696SDavid.Hollister@Sun.COM 			} else {
17510696SDavid.Hollister@Sun.COM 				/*
17610696SDavid.Hollister@Sun.COM 				 * Default back to PIO.
17710696SDavid.Hollister@Sun.COM 				 *
17810696SDavid.Hollister@Sun.COM 				 * Note that non-FPDMA would still be possible,
17910696SDavid.Hollister@Sun.COM 				 * but for this specific configuration, if it's
18010696SDavid.Hollister@Sun.COM 				 * not NCQ it's safest to assume PIO.
18110696SDavid.Hollister@Sun.COM 				 */
18210696SDavid.Hollister@Sun.COM 				xp->pio = 1;
18311048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
18410696SDavid.Hollister@Sun.COM 				    "%s: device %s assumed PIO",
18510696SDavid.Hollister@Sun.COM 				    __func__, xp->phy->path);
18610696SDavid.Hollister@Sun.COM 			}
18710696SDavid.Hollister@Sun.COM 		}
18810696SDavid.Hollister@Sun.COM 	} else {
18910696SDavid.Hollister@Sun.COM 		saq = 0;
19010696SDavid.Hollister@Sun.COM 		id = NULL;
19110696SDavid.Hollister@Sun.COM 	}
19210696SDavid.Hollister@Sun.COM 
19310696SDavid.Hollister@Sun.COM 	bzero(rp, SRESPSZ);
19410696SDavid.Hollister@Sun.COM 
19510696SDavid.Hollister@Sun.COM 	switch (pkt->pkt_cdbp[0]) {
19610696SDavid.Hollister@Sun.COM 	case SCMD_INQUIRY:
19710696SDavid.Hollister@Sun.COM 	{
19810696SDavid.Hollister@Sun.COM 		struct scsi_inquiry *inqp;
19910696SDavid.Hollister@Sun.COM 		uint16_t *a, *b;
20010696SDavid.Hollister@Sun.COM 
20110696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
20210696SDavid.Hollister@Sun.COM 		if ((pkt->pkt_cdbp[1] & 0xfc) || pkt->pkt_cdbp[5]) {
20310696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
20410696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
20510696SDavid.Hollister@Sun.COM 			break;
20610696SDavid.Hollister@Sun.COM 		}
20710696SDavid.Hollister@Sun.COM 		if (pkt->pkt_cdbp[1] & 0x1) {
20810696SDavid.Hollister@Sun.COM 			switch (pkt->pkt_cdbp[2]) {
20910696SDavid.Hollister@Sun.COM 			case 0x0:
21010696SDavid.Hollister@Sun.COM 				rp[3] = 3;
21110696SDavid.Hollister@Sun.COM 				rp[5] = 0x80;
21210696SDavid.Hollister@Sun.COM 				rp[6] = 0x83;
21310696SDavid.Hollister@Sun.COM 				amt = 7;
21410696SDavid.Hollister@Sun.COM 				break;
21510696SDavid.Hollister@Sun.COM 			case 0x80:
21610696SDavid.Hollister@Sun.COM 				rp[1] = 0x80;
21710696SDavid.Hollister@Sun.COM 				rp[3] = 0x14;
21810696SDavid.Hollister@Sun.COM 				a = (void *) &rp[4];
21910696SDavid.Hollister@Sun.COM 				b = id->model_number;
22010696SDavid.Hollister@Sun.COM 				for (i = 0; i < 5; i++) {
22110696SDavid.Hollister@Sun.COM 					*a = ddi_swap16(*b);
22210696SDavid.Hollister@Sun.COM 					a++;
22310696SDavid.Hollister@Sun.COM 					b++;
22410696SDavid.Hollister@Sun.COM 				}
22510696SDavid.Hollister@Sun.COM 				amt = 24;
22610696SDavid.Hollister@Sun.COM 				break;
22710696SDavid.Hollister@Sun.COM 			case 0x83:
22810696SDavid.Hollister@Sun.COM 				rp[1] = 0x83;
22910696SDavid.Hollister@Sun.COM 				if ((LE_16(id->word87) & 0x100) &&
23010696SDavid.Hollister@Sun.COM 				    (LE_16(id->word108) >> 12) == 5)  {
23110696SDavid.Hollister@Sun.COM 					rp[3] = 12;
23210696SDavid.Hollister@Sun.COM 					rp[4] = 1;
23310696SDavid.Hollister@Sun.COM 					rp[5] = 3;
23410696SDavid.Hollister@Sun.COM 					rp[7] = 8;
23510696SDavid.Hollister@Sun.COM 					rp[8] = LE_16(id->word108) >> 8;
23610696SDavid.Hollister@Sun.COM 					rp[9] = LE_16(id->word108);
23710696SDavid.Hollister@Sun.COM 					rp[10] = LE_16(id->word109) >> 8;
23810696SDavid.Hollister@Sun.COM 					rp[11] = LE_16(id->word109);
23910696SDavid.Hollister@Sun.COM 					rp[12] = LE_16(id->word110) >> 8;
24010696SDavid.Hollister@Sun.COM 					rp[13] = LE_16(id->word110);
24110696SDavid.Hollister@Sun.COM 					rp[14] = LE_16(id->word111) >> 8;
24210696SDavid.Hollister@Sun.COM 					rp[15] = LE_16(id->word111);
24310696SDavid.Hollister@Sun.COM 					amt = 16;
24410696SDavid.Hollister@Sun.COM 				} else {
24510696SDavid.Hollister@Sun.COM 					rp[3] = 64;
24610696SDavid.Hollister@Sun.COM 					rp[4] = 2;
24710696SDavid.Hollister@Sun.COM 					rp[5] = 1;
24810696SDavid.Hollister@Sun.COM 					rp[7] = 60;
24910696SDavid.Hollister@Sun.COM 					rp[8] = 'A';
25010696SDavid.Hollister@Sun.COM 					rp[9] = 'T';
25110696SDavid.Hollister@Sun.COM 					rp[10] = 'A';
25210696SDavid.Hollister@Sun.COM 					rp[11] = ' ';
25310696SDavid.Hollister@Sun.COM 					rp[12] = ' ';
25410696SDavid.Hollister@Sun.COM 					rp[13] = ' ';
25510696SDavid.Hollister@Sun.COM 					rp[14] = ' ';
25610696SDavid.Hollister@Sun.COM 					rp[15] = ' ';
25710696SDavid.Hollister@Sun.COM 					a = (void *) &rp[16];
25810696SDavid.Hollister@Sun.COM 					b = id->model_number;
25910696SDavid.Hollister@Sun.COM 					for (i = 0; i < 20; i++) {
26010696SDavid.Hollister@Sun.COM 						*a = ddi_swap16(*b);
26110696SDavid.Hollister@Sun.COM 						a++;
26210696SDavid.Hollister@Sun.COM 						b++;
26310696SDavid.Hollister@Sun.COM 					}
26410696SDavid.Hollister@Sun.COM 					a = (void *) &rp[40];
26510696SDavid.Hollister@Sun.COM 					b = id->serial_number;
26610696SDavid.Hollister@Sun.COM 					for (i = 0; i < 10; i++) {
26710696SDavid.Hollister@Sun.COM 						*a = ddi_swap16(*b);
26810696SDavid.Hollister@Sun.COM 						a++;
26910696SDavid.Hollister@Sun.COM 						b++;
27010696SDavid.Hollister@Sun.COM 					}
27110696SDavid.Hollister@Sun.COM 					amt = 68;
27210696SDavid.Hollister@Sun.COM 				}
27310696SDavid.Hollister@Sun.COM 				break;
27410696SDavid.Hollister@Sun.COM 			default:
27510696SDavid.Hollister@Sun.COM 				status = STATUS_CHECK;
27610696SDavid.Hollister@Sun.COM 				asc = 0x24;	/* invalid field in cdb */
27710696SDavid.Hollister@Sun.COM 				break;
27810696SDavid.Hollister@Sun.COM 			}
27910696SDavid.Hollister@Sun.COM 		} else {
28010696SDavid.Hollister@Sun.COM 			inqp = (struct scsi_inquiry *)rp;
28110696SDavid.Hollister@Sun.COM 			inqp->inq_qual = (LE_16(id->word0) & 0x80) ? 0x80 : 0;
28210696SDavid.Hollister@Sun.COM 			inqp->inq_ansi = 5;	/* spc3 */
28310696SDavid.Hollister@Sun.COM 			inqp->inq_rdf = 2;	/* response format 2 */
28410696SDavid.Hollister@Sun.COM 			inqp->inq_len = 32;
28510696SDavid.Hollister@Sun.COM 
28610696SDavid.Hollister@Sun.COM 			if (xp->ncq && (xp->qdepth > 1)) {
28710696SDavid.Hollister@Sun.COM 				inqp->inq_cmdque = 1;
28810696SDavid.Hollister@Sun.COM 			}
28910696SDavid.Hollister@Sun.COM 
29010696SDavid.Hollister@Sun.COM 			(void) memcpy(inqp->inq_vid, "ATA     ", 8);
29110696SDavid.Hollister@Sun.COM 
29210696SDavid.Hollister@Sun.COM 			a = (void *)inqp->inq_pid;
29310696SDavid.Hollister@Sun.COM 			b = id->model_number;
29410696SDavid.Hollister@Sun.COM 			for (i = 0; i < 8; i++) {
29510696SDavid.Hollister@Sun.COM 				*a = ddi_swap16(*b);
29610696SDavid.Hollister@Sun.COM 				a++;
29710696SDavid.Hollister@Sun.COM 				b++;
29810696SDavid.Hollister@Sun.COM 			}
29910696SDavid.Hollister@Sun.COM 			if (id->firmware_revision[2] == 0x2020 &&
30010696SDavid.Hollister@Sun.COM 			    id->firmware_revision[3] == 0x2020) {
30110696SDavid.Hollister@Sun.COM 				inqp->inq_revision[0] =
30210696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[0]) >> 8;
30310696SDavid.Hollister@Sun.COM 				inqp->inq_revision[1] =
30410696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[0]);
30510696SDavid.Hollister@Sun.COM 				inqp->inq_revision[2] =
30610696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[1]) >> 8;
30710696SDavid.Hollister@Sun.COM 				inqp->inq_revision[3] =
30810696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[1]);
30910696SDavid.Hollister@Sun.COM 			} else {
31010696SDavid.Hollister@Sun.COM 				inqp->inq_revision[0] =
31110696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[2]) >> 8;
31210696SDavid.Hollister@Sun.COM 				inqp->inq_revision[1] =
31310696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[2]);
31410696SDavid.Hollister@Sun.COM 				inqp->inq_revision[2] =
31510696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[3]) >> 8;
31610696SDavid.Hollister@Sun.COM 				inqp->inq_revision[3] =
31710696SDavid.Hollister@Sun.COM 				    ddi_swap16(id->firmware_revision[3]);
31810696SDavid.Hollister@Sun.COM 			}
31910696SDavid.Hollister@Sun.COM 			amt = 36;
32010696SDavid.Hollister@Sun.COM 		}
32110696SDavid.Hollister@Sun.COM 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
32210696SDavid.Hollister@Sun.COM 		if (amt) {
32310696SDavid.Hollister@Sun.COM 			if (xp->actv_cnt) {
32410696SDavid.Hollister@Sun.COM 				xp->special_needed = 1;
32510696SDavid.Hollister@Sun.COM 				xp->special_running = 0;
32611048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
32711048SDavid.Hollister@Sun.COM 				    "%s: @ line %d", __func__, __LINE__);
32810696SDavid.Hollister@Sun.COM 				if (saq) {
32910696SDavid.Hollister@Sun.COM 					pmcs_release_scratch(pwp);
33010696SDavid.Hollister@Sun.COM 				}
33110696SDavid.Hollister@Sun.COM 				return (-1);
33210696SDavid.Hollister@Sun.COM 			}
33310696SDavid.Hollister@Sun.COM 			SATAcopy(sp, rp, amt);
33410696SDavid.Hollister@Sun.COM 		}
33510696SDavid.Hollister@Sun.COM 		break;
33610696SDavid.Hollister@Sun.COM 	}
33710696SDavid.Hollister@Sun.COM 	case SCMD_READ_CAPACITY:
33810696SDavid.Hollister@Sun.COM 	{
33910696SDavid.Hollister@Sun.COM 		uint64_t last_block;
34010696SDavid.Hollister@Sun.COM 		uint32_t block_size = 512;	/* XXXX */
34110696SDavid.Hollister@Sun.COM 
34210696SDavid.Hollister@Sun.COM 		xp->capacity = LBA_CAPACITY(id);
34310696SDavid.Hollister@Sun.COM 		last_block = xp->capacity - 1;
34410696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
34510696SDavid.Hollister@Sun.COM 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[6] ||
34610696SDavid.Hollister@Sun.COM 		    (pkt->pkt_cdbp[8] & 0xfe) || pkt->pkt_cdbp[7] ||
34710696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[9]) {
34810696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
34910696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
35010696SDavid.Hollister@Sun.COM 			break;
35110696SDavid.Hollister@Sun.COM 		}
35210696SDavid.Hollister@Sun.COM 		for (i = 1; i < 10; i++) {
35310696SDavid.Hollister@Sun.COM 			if (pkt->pkt_cdbp[i]) {
35410696SDavid.Hollister@Sun.COM 				status = STATUS_CHECK;
35510696SDavid.Hollister@Sun.COM 				asc = 0x24;	/* invalid field in cdb */
35610696SDavid.Hollister@Sun.COM 				break;
35710696SDavid.Hollister@Sun.COM 			}
35810696SDavid.Hollister@Sun.COM 		}
35910696SDavid.Hollister@Sun.COM 		if (status != STATUS_GOOD) {
36010696SDavid.Hollister@Sun.COM 			break;
36110696SDavid.Hollister@Sun.COM 		}
36210696SDavid.Hollister@Sun.COM 		if (last_block > 0xffffffffULL) {
36310696SDavid.Hollister@Sun.COM 			last_block = 0xffffffffULL;
36410696SDavid.Hollister@Sun.COM 		}
36510696SDavid.Hollister@Sun.COM 		rp[0] = (last_block >> 24) & 0xff;
36610696SDavid.Hollister@Sun.COM 		rp[1] = (last_block >> 16) & 0xff;
36710696SDavid.Hollister@Sun.COM 		rp[2] = (last_block >>  8) & 0xff;
36810696SDavid.Hollister@Sun.COM 		rp[3] = (last_block) & 0xff;
36910696SDavid.Hollister@Sun.COM 		rp[4] = (block_size >> 24) & 0xff;
37010696SDavid.Hollister@Sun.COM 		rp[5] = (block_size >> 16) & 0xff;
37110696SDavid.Hollister@Sun.COM 		rp[6] = (block_size >>  8) & 0xff;
37210696SDavid.Hollister@Sun.COM 		rp[7] = (block_size) & 0xff;
37310696SDavid.Hollister@Sun.COM 		amt = 8;
37410696SDavid.Hollister@Sun.COM 		amt = pmcs_set_resid(pkt, amt, 8);
37510696SDavid.Hollister@Sun.COM 		if (amt) {
37610696SDavid.Hollister@Sun.COM 			if (xp->actv_cnt) {
37710696SDavid.Hollister@Sun.COM 				xp->special_needed = 1;
37810696SDavid.Hollister@Sun.COM 				xp->special_running = 0;
37911048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
38011048SDavid.Hollister@Sun.COM 				    "%s: @ line %d", __func__, __LINE__);
38110696SDavid.Hollister@Sun.COM 				if (saq) {
38210696SDavid.Hollister@Sun.COM 					pmcs_release_scratch(pwp);
38310696SDavid.Hollister@Sun.COM 				}
38410696SDavid.Hollister@Sun.COM 				return (-1);
38510696SDavid.Hollister@Sun.COM 			}
38610696SDavid.Hollister@Sun.COM 			SATAcopy(sp, rp, amt);
38710696SDavid.Hollister@Sun.COM 		}
38810696SDavid.Hollister@Sun.COM 		break;
38910696SDavid.Hollister@Sun.COM 	}
39010696SDavid.Hollister@Sun.COM 	case SCMD_REPORT_LUNS: {
39110696SDavid.Hollister@Sun.COM 		int rl_len;
39210696SDavid.Hollister@Sun.COM 
39310696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
39410696SDavid.Hollister@Sun.COM 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[3] || pkt->pkt_cdbp[4] ||
39510696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[5] || pkt->pkt_cdbp[10] ||
39610696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[11]) {
39710696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
39810696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
39910696SDavid.Hollister@Sun.COM 			break;
40010696SDavid.Hollister@Sun.COM 		}
40110696SDavid.Hollister@Sun.COM 
40210696SDavid.Hollister@Sun.COM 		rp[3] = 8;
40310696SDavid.Hollister@Sun.COM 		rl_len = 16;	/* list length (4) + reserved (4) + 1 LUN (8) */
40410696SDavid.Hollister@Sun.COM 		amt = rl_len;
40510696SDavid.Hollister@Sun.COM 		amt = pmcs_set_resid(pkt, amt, rl_len);
40610696SDavid.Hollister@Sun.COM 
40710696SDavid.Hollister@Sun.COM 		if (amt) {
40810696SDavid.Hollister@Sun.COM 			if (xp->actv_cnt) {
40910696SDavid.Hollister@Sun.COM 				xp->special_needed = 1;
41010696SDavid.Hollister@Sun.COM 				xp->special_running = 0;
41111048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
41211048SDavid.Hollister@Sun.COM 				    "%s: @ line %d", __func__, __LINE__);
41310696SDavid.Hollister@Sun.COM 				if (saq) {
41410696SDavid.Hollister@Sun.COM 					pmcs_release_scratch(pwp);
41510696SDavid.Hollister@Sun.COM 				}
41610696SDavid.Hollister@Sun.COM 				return (-1);
41710696SDavid.Hollister@Sun.COM 			}
41810696SDavid.Hollister@Sun.COM 			SATAcopy(sp, rp, rl_len);
41910696SDavid.Hollister@Sun.COM 		}
42010696SDavid.Hollister@Sun.COM 		break;
42110696SDavid.Hollister@Sun.COM 	}
42210696SDavid.Hollister@Sun.COM 
42310696SDavid.Hollister@Sun.COM 	case SCMD_REQUEST_SENSE:
42410696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
42510696SDavid.Hollister@Sun.COM 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
42610696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[3] || pkt->pkt_cdbp[5]) {
42710696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
42810696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
42910696SDavid.Hollister@Sun.COM 			break;
43010696SDavid.Hollister@Sun.COM 		}
43110696SDavid.Hollister@Sun.COM 		rp[0] = 0xf0;
43210696SDavid.Hollister@Sun.COM 		amt = 18;
43310696SDavid.Hollister@Sun.COM 		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
43410696SDavid.Hollister@Sun.COM 		if (amt) {
43510696SDavid.Hollister@Sun.COM 			if (xp->actv_cnt) {
43610696SDavid.Hollister@Sun.COM 				xp->special_needed = 1;
43710696SDavid.Hollister@Sun.COM 				xp->special_running = 0;
43811048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
43911048SDavid.Hollister@Sun.COM 				    "%s: @ line %d", __func__, __LINE__);
44010696SDavid.Hollister@Sun.COM 				if (saq) {
44110696SDavid.Hollister@Sun.COM 					pmcs_release_scratch(pwp);
44210696SDavid.Hollister@Sun.COM 				}
44310696SDavid.Hollister@Sun.COM 				return (-1);
44410696SDavid.Hollister@Sun.COM 			}
44510696SDavid.Hollister@Sun.COM 			SATAcopy(sp, rp, 18);
44610696SDavid.Hollister@Sun.COM 		}
44710696SDavid.Hollister@Sun.COM 		break;
44810696SDavid.Hollister@Sun.COM 	case SCMD_START_STOP:
44910696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
45010696SDavid.Hollister@Sun.COM 		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
45110696SDavid.Hollister@Sun.COM 		    (pkt->pkt_cdbp[3] & 0xf0) || (pkt->pkt_cdbp[4] & 0x08) ||
45210696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[5]) {
45310696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
45410696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
45510696SDavid.Hollister@Sun.COM 			break;
45610696SDavid.Hollister@Sun.COM 		}
45710696SDavid.Hollister@Sun.COM 		break;
45810696SDavid.Hollister@Sun.COM 	case SCMD_SYNCHRONIZE_CACHE:
45910696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
46010696SDavid.Hollister@Sun.COM 		if ((pkt->pkt_cdbp[1] & 0xf8) || (pkt->pkt_cdbp[6] & 0xe0) ||
46110696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[9]) {
46210696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
46310696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
46410696SDavid.Hollister@Sun.COM 			break;
46510696SDavid.Hollister@Sun.COM 		}
46610696SDavid.Hollister@Sun.COM 		break;
46710696SDavid.Hollister@Sun.COM 	case SCMD_TEST_UNIT_READY:
46810696SDavid.Hollister@Sun.COM 		/* Check for illegal bits */
46910696SDavid.Hollister@Sun.COM 		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[2] || pkt->pkt_cdbp[3] ||
47010696SDavid.Hollister@Sun.COM 		    pkt->pkt_cdbp[4] || pkt->pkt_cdbp[5]) {
47110696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
47210696SDavid.Hollister@Sun.COM 			asc = 0x24;	/* invalid field in cdb */
47310696SDavid.Hollister@Sun.COM 			break;
47410696SDavid.Hollister@Sun.COM 		}
47510696SDavid.Hollister@Sun.COM 		if (xp->ca) {
47610696SDavid.Hollister@Sun.COM 			status = STATUS_CHECK;
47710696SDavid.Hollister@Sun.COM 			key = 0x6;
47810696SDavid.Hollister@Sun.COM 			asc = 0x28;
47910696SDavid.Hollister@Sun.COM 			xp->ca = 0;
48010696SDavid.Hollister@Sun.COM 		}
48110696SDavid.Hollister@Sun.COM 		break;
48210696SDavid.Hollister@Sun.COM 	default:
48310696SDavid.Hollister@Sun.COM 		asc = 0x20;	/* invalid operation command code */
48410696SDavid.Hollister@Sun.COM 		status = STATUS_CHECK;
48510696SDavid.Hollister@Sun.COM 		break;
48610696SDavid.Hollister@Sun.COM 	}
48710696SDavid.Hollister@Sun.COM 	if (status != STATUS_GOOD) {
48810696SDavid.Hollister@Sun.COM 		bzero(rp, 18);
48910696SDavid.Hollister@Sun.COM 		rp[0] = 0xf0;
49010696SDavid.Hollister@Sun.COM 		rp[2] = key;
49110696SDavid.Hollister@Sun.COM 		rp[12] = asc;
49210696SDavid.Hollister@Sun.COM 		rp[13] = ascq;
49310696SDavid.Hollister@Sun.COM 		pmcs_latch_status(pwp, sp, status, rp, 18, pptr->path);
49410696SDavid.Hollister@Sun.COM 	} else {
49510696SDavid.Hollister@Sun.COM 		pmcs_latch_status(pwp, sp, status, NULL, 0, pptr->path);
49610696SDavid.Hollister@Sun.COM 	}
49710696SDavid.Hollister@Sun.COM 
49810696SDavid.Hollister@Sun.COM out:
49910696SDavid.Hollister@Sun.COM 	STAILQ_REMOVE_HEAD(&xp->sq, cmd_next);
50011048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
50110696SDavid.Hollister@Sun.COM 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
50210696SDavid.Hollister@Sun.COM 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
50310696SDavid.Hollister@Sun.COM 	    pkt->pkt_state, pkt->pkt_resid, status);
50410696SDavid.Hollister@Sun.COM 
50510696SDavid.Hollister@Sun.COM 	if (saq) {
50610696SDavid.Hollister@Sun.COM 		pmcs_release_scratch(pwp);
50710696SDavid.Hollister@Sun.COM 	}
50810696SDavid.Hollister@Sun.COM 
50910696SDavid.Hollister@Sun.COM 	if (xp->draining) {
51011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51110696SDavid.Hollister@Sun.COM 		    "%s: waking up drain waiters", __func__);
51210696SDavid.Hollister@Sun.COM 		cv_signal(&pwp->drain_cv);
51310696SDavid.Hollister@Sun.COM 	}
51410696SDavid.Hollister@Sun.COM 
51510696SDavid.Hollister@Sun.COM 	mutex_exit(&xp->statlock);
51610696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->cq_lock);
51710696SDavid.Hollister@Sun.COM 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
51810696SDavid.Hollister@Sun.COM 	PMCS_CQ_RUN_LOCKED(pwp);
51910696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->cq_lock);
52010696SDavid.Hollister@Sun.COM 	mutex_enter(&xp->statlock);
52110696SDavid.Hollister@Sun.COM 	xp->special_running = 0;
52210696SDavid.Hollister@Sun.COM 	return (0);
52310696SDavid.Hollister@Sun.COM }
52410696SDavid.Hollister@Sun.COM 
52510696SDavid.Hollister@Sun.COM /*
52610696SDavid.Hollister@Sun.COM  * Run all special commands queued up for a SATA device.
52710696SDavid.Hollister@Sun.COM  * We're only called if the caller knows we have work to do.
52810696SDavid.Hollister@Sun.COM  *
52910696SDavid.Hollister@Sun.COM  * We can't run them if things are still active for the device,
53010696SDavid.Hollister@Sun.COM  * return saying we didn't run anything.
53110696SDavid.Hollister@Sun.COM  *
53210696SDavid.Hollister@Sun.COM  * When we finish, wake up anyone waiting for active commands
53310696SDavid.Hollister@Sun.COM  * to go to zero.
53410696SDavid.Hollister@Sun.COM  *
53510696SDavid.Hollister@Sun.COM  * Called with PHY lock and xp statlock held.
53610696SDavid.Hollister@Sun.COM  */
53710696SDavid.Hollister@Sun.COM int
pmcs_run_sata_special(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)53810696SDavid.Hollister@Sun.COM pmcs_run_sata_special(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
53910696SDavid.Hollister@Sun.COM {
54010696SDavid.Hollister@Sun.COM 	while (!STAILQ_EMPTY(&xp->sq)) {
54110696SDavid.Hollister@Sun.COM 		if (pmcs_sata_special_work(pwp, xp)) {
54210696SDavid.Hollister@Sun.COM 			return (-1);
54310696SDavid.Hollister@Sun.COM 		}
54410696SDavid.Hollister@Sun.COM 	}
54510696SDavid.Hollister@Sun.COM 	return (0);
54610696SDavid.Hollister@Sun.COM }
54710696SDavid.Hollister@Sun.COM 
54810696SDavid.Hollister@Sun.COM /*
54910696SDavid.Hollister@Sun.COM  * Search for SATA special commands to run and run them.
55010696SDavid.Hollister@Sun.COM  * If we succeed in running the special command(s), kick
55110696SDavid.Hollister@Sun.COM  * the normal commands into operation again. Call completion
55210696SDavid.Hollister@Sun.COM  * for any commands that were completed while we were here.
55310696SDavid.Hollister@Sun.COM  *
55410696SDavid.Hollister@Sun.COM  * Called unlocked.
55510696SDavid.Hollister@Sun.COM  */
55610696SDavid.Hollister@Sun.COM void
pmcs_sata_work(pmcs_hw_t * pwp)55710696SDavid.Hollister@Sun.COM pmcs_sata_work(pmcs_hw_t *pwp)
55810696SDavid.Hollister@Sun.COM {
55910696SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *xp;
56010696SDavid.Hollister@Sun.COM 	int spinagain = 0;
56110696SDavid.Hollister@Sun.COM 	uint16_t target;
56210696SDavid.Hollister@Sun.COM 
56310696SDavid.Hollister@Sun.COM 	for (target = 0; target < pwp->max_dev; target++) {
56410696SDavid.Hollister@Sun.COM 		xp = pwp->targets[target];
56510696SDavid.Hollister@Sun.COM 		if ((xp == NULL) || (xp->phy == NULL)) {
56610696SDavid.Hollister@Sun.COM 			continue;
56710696SDavid.Hollister@Sun.COM 		}
56810696SDavid.Hollister@Sun.COM 		pmcs_lock_phy(xp->phy);
56910696SDavid.Hollister@Sun.COM 		mutex_enter(&xp->statlock);
57010696SDavid.Hollister@Sun.COM 		if (STAILQ_EMPTY(&xp->sq)) {
57110696SDavid.Hollister@Sun.COM 			mutex_exit(&xp->statlock);
57210696SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(xp->phy);
57310696SDavid.Hollister@Sun.COM 			continue;
57410696SDavid.Hollister@Sun.COM 		}
57510696SDavid.Hollister@Sun.COM 		if (xp->actv_cnt) {
57610696SDavid.Hollister@Sun.COM 			xp->special_needed = 1;
57711048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
57810696SDavid.Hollister@Sun.COM 			    "%s: deferring until drained", __func__);
57910696SDavid.Hollister@Sun.COM 			spinagain++;
58010696SDavid.Hollister@Sun.COM 		} else {
58110696SDavid.Hollister@Sun.COM 			if (pmcs_run_sata_special(pwp, xp)) {
58210696SDavid.Hollister@Sun.COM 				spinagain++;
58310696SDavid.Hollister@Sun.COM 			}
58410696SDavid.Hollister@Sun.COM 		}
58510696SDavid.Hollister@Sun.COM 		mutex_exit(&xp->statlock);
58610696SDavid.Hollister@Sun.COM 		pmcs_unlock_phy(xp->phy);
58710696SDavid.Hollister@Sun.COM 	}
58810696SDavid.Hollister@Sun.COM 
58910696SDavid.Hollister@Sun.COM 	if (spinagain) {
59010696SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
59110696SDavid.Hollister@Sun.COM 	} else {
59210696SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
59310696SDavid.Hollister@Sun.COM 	}
59410696SDavid.Hollister@Sun.COM 
59510696SDavid.Hollister@Sun.COM 	/*
59610696SDavid.Hollister@Sun.COM 	 * Run completion on any commands ready for it.
59710696SDavid.Hollister@Sun.COM 	 */
59810696SDavid.Hollister@Sun.COM 	PMCS_CQ_RUN(pwp);
59910696SDavid.Hollister@Sun.COM }
60010696SDavid.Hollister@Sun.COM 
60110696SDavid.Hollister@Sun.COM /*
60210696SDavid.Hollister@Sun.COM  * Called with PHY lock held and scratch acquired
60310696SDavid.Hollister@Sun.COM  */
60410696SDavid.Hollister@Sun.COM int
pmcs_sata_identify(pmcs_hw_t * pwp,pmcs_phy_t * pptr)60510696SDavid.Hollister@Sun.COM pmcs_sata_identify(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
60610696SDavid.Hollister@Sun.COM {
60710696SDavid.Hollister@Sun.COM 	fis_t fis;
60810696SDavid.Hollister@Sun.COM 	fis[0] = (IDENTIFY_DEVICE << 16) | (1 << 15) | FIS_REG_H2DEV;
60910696SDavid.Hollister@Sun.COM 	fis[1] = 0;
61010696SDavid.Hollister@Sun.COM 	fis[2] = 0;
61110696SDavid.Hollister@Sun.COM 	fis[3] = 0;
61210696SDavid.Hollister@Sun.COM 	fis[4] = 0;
61310696SDavid.Hollister@Sun.COM 	return (pmcs_run_sata_cmd(pwp, pptr, fis, SATA_PROTOCOL_PIO,
61410696SDavid.Hollister@Sun.COM 	    PMCIN_DATADIR_2_INI, sizeof (ata_identify_t)));
61510696SDavid.Hollister@Sun.COM }
61610696SDavid.Hollister@Sun.COM 
61710696SDavid.Hollister@Sun.COM /*
61810696SDavid.Hollister@Sun.COM  * Called with PHY lock held and scratch held
61910696SDavid.Hollister@Sun.COM  */
62010696SDavid.Hollister@Sun.COM 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)62110696SDavid.Hollister@Sun.COM pmcs_run_sata_cmd(pmcs_hw_t *pwp, pmcs_phy_t *pptr, fis_t fis, uint32_t mode,
62210696SDavid.Hollister@Sun.COM     uint32_t ddir, uint32_t dlen)
62310696SDavid.Hollister@Sun.COM {
62410696SDavid.Hollister@Sun.COM 	struct pmcwork *pwrk;
62510696SDavid.Hollister@Sun.COM 	uint32_t *ptr, msg[PMCS_MSG_SIZE];
626*11307SDavid.Hollister@Sun.COM 	uint32_t iq, htag, status;
62710696SDavid.Hollister@Sun.COM 	int i, result = 0;
62810696SDavid.Hollister@Sun.COM 
62910696SDavid.Hollister@Sun.COM 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
63010696SDavid.Hollister@Sun.COM 	if (pwrk == NULL) {
63110696SDavid.Hollister@Sun.COM 		return (ENOMEM);
63210696SDavid.Hollister@Sun.COM 	}
63310696SDavid.Hollister@Sun.COM 
63410696SDavid.Hollister@Sun.COM 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
63510696SDavid.Hollister@Sun.COM 	    PMCIN_SATA_HOST_IO_START));
63610696SDavid.Hollister@Sun.COM 	htag = pwrk->htag;
63710696SDavid.Hollister@Sun.COM 	pwrk->arg = msg;
63810696SDavid.Hollister@Sun.COM 	pwrk->dtype = SATA;
63910696SDavid.Hollister@Sun.COM 	msg[1] = LE_32(pwrk->htag);
64010696SDavid.Hollister@Sun.COM 	msg[2] = LE_32(pptr->device_id);
64110696SDavid.Hollister@Sun.COM 	msg[3] = LE_32(dlen);
64210696SDavid.Hollister@Sun.COM 	msg[4] = LE_32(mode | ddir);
64310696SDavid.Hollister@Sun.COM 	if (dlen) {
64410696SDavid.Hollister@Sun.COM 		if (ddir == PMCIN_DATADIR_2_DEV) {
64510696SDavid.Hollister@Sun.COM 			if (ddi_dma_sync(pwp->cip_handles, 0, 0,
64610696SDavid.Hollister@Sun.COM 			    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
64711048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
64811048SDavid.Hollister@Sun.COM 				    "Condition check failed at %s():%d",
64911048SDavid.Hollister@Sun.COM 				    __func__, __LINE__);
65010696SDavid.Hollister@Sun.COM 			}
65110696SDavid.Hollister@Sun.COM 		}
65210696SDavid.Hollister@Sun.COM 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
65310696SDavid.Hollister@Sun.COM 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
65410696SDavid.Hollister@Sun.COM 		msg[14] = LE_32(dlen);
65510696SDavid.Hollister@Sun.COM 		msg[15] = 0;
65610696SDavid.Hollister@Sun.COM 	} else {
65710696SDavid.Hollister@Sun.COM 		msg[12] = 0;
65810696SDavid.Hollister@Sun.COM 		msg[13] = 0;
65910696SDavid.Hollister@Sun.COM 		msg[14] = 0;
66010696SDavid.Hollister@Sun.COM 		msg[15] = 0;
66110696SDavid.Hollister@Sun.COM 	}
66210696SDavid.Hollister@Sun.COM 	for (i = 0; i < 5; i++) {
66310696SDavid.Hollister@Sun.COM 		msg[5+i] = LE_32(fis[i]);
66410696SDavid.Hollister@Sun.COM 	}
66510696SDavid.Hollister@Sun.COM 	msg[10] = 0;
66610696SDavid.Hollister@Sun.COM 	msg[11] = 0;
66710696SDavid.Hollister@Sun.COM 	GET_IO_IQ_ENTRY(pwp, ptr, pptr->device_id, iq);
66810696SDavid.Hollister@Sun.COM 	if (ptr == NULL) {
66910696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
67010696SDavid.Hollister@Sun.COM 		return (ENOMEM);
67110696SDavid.Hollister@Sun.COM 	}
67210696SDavid.Hollister@Sun.COM 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
67310696SDavid.Hollister@Sun.COM 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
67410696SDavid.Hollister@Sun.COM 	INC_IQ_ENTRY(pwp, iq);
67510696SDavid.Hollister@Sun.COM 
67610696SDavid.Hollister@Sun.COM 	pmcs_unlock_phy(pptr);
67710696SDavid.Hollister@Sun.COM 	WAIT_FOR(pwrk, 1000, result);
67810696SDavid.Hollister@Sun.COM 	pmcs_pwork(pwp, pwrk);
67910696SDavid.Hollister@Sun.COM 	pmcs_lock_phy(pptr);
68010696SDavid.Hollister@Sun.COM 
68110696SDavid.Hollister@Sun.COM 	if (result) {
68210696SDavid.Hollister@Sun.COM 		pmcs_timed_out(pwp, htag, __func__);
68310696SDavid.Hollister@Sun.COM 		if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
68410696SDavid.Hollister@Sun.COM 			pptr->abort_pending = 1;
68510696SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
68610696SDavid.Hollister@Sun.COM 		}
68710696SDavid.Hollister@Sun.COM 		return (ETIMEDOUT);
68810696SDavid.Hollister@Sun.COM 	}
68910696SDavid.Hollister@Sun.COM 
690*11307SDavid.Hollister@Sun.COM 	status = LE_32(msg[2]);
691*11307SDavid.Hollister@Sun.COM 
692*11307SDavid.Hollister@Sun.COM 	if (status != PMCOUT_STATUS_OK) {
693*11307SDavid.Hollister@Sun.COM 		if (status == PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY) {
694*11307SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
695*11307SDavid.Hollister@Sun.COM 			    "%s: Potential affiliation active on 0x%" PRIx64,
696*11307SDavid.Hollister@Sun.COM 			    __func__, pmcs_barray2wwn(pptr->sas_address));
697*11307SDavid.Hollister@Sun.COM 		} else {
698*11307SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, pptr->target,
699*11307SDavid.Hollister@Sun.COM 			    "%s: SATA I/O returned with IOMB status 0x%x",
700*11307SDavid.Hollister@Sun.COM 			    __func__, status);
701*11307SDavid.Hollister@Sun.COM 		}
70210696SDavid.Hollister@Sun.COM 		return (EIO);
70310696SDavid.Hollister@Sun.COM 	}
704*11307SDavid.Hollister@Sun.COM 
70510696SDavid.Hollister@Sun.COM 	if (LE_32(ptr[3]) != 0) {
70610696SDavid.Hollister@Sun.COM 		size_t j, amt = LE_32(ptr[3]);
70710696SDavid.Hollister@Sun.COM 		if (amt > sizeof (fis_t)) {
70810696SDavid.Hollister@Sun.COM 			amt = sizeof (fis_t);
70910696SDavid.Hollister@Sun.COM 		}
71010696SDavid.Hollister@Sun.COM 		amt >>= 2;
71110696SDavid.Hollister@Sun.COM 		for (j = 0; j < amt; j++) {
71210696SDavid.Hollister@Sun.COM 			fis[j] = LE_32(msg[4 + j]);
71310696SDavid.Hollister@Sun.COM 		}
71410696SDavid.Hollister@Sun.COM 	}
71510696SDavid.Hollister@Sun.COM 	if (dlen && ddir == PMCIN_DATADIR_2_INI) {
71610696SDavid.Hollister@Sun.COM 		if (ddi_dma_sync(pwp->cip_handles, 0, 0,
71710696SDavid.Hollister@Sun.COM 		    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
71811048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
71911048SDavid.Hollister@Sun.COM 			    "Condition check failed at %s():%d",
72011048SDavid.Hollister@Sun.COM 			    __func__, __LINE__);
72110696SDavid.Hollister@Sun.COM 		}
72210696SDavid.Hollister@Sun.COM 	}
72310696SDavid.Hollister@Sun.COM 	return (0);
72410696SDavid.Hollister@Sun.COM }
725