xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_scsa.c (revision 601c90f161ff0319c1b4a2c3362b466043a65d8d)
14c06356bSdh142964 /*
24c06356bSdh142964  * CDDL HEADER START
34c06356bSdh142964  *
44c06356bSdh142964  * The contents of this file are subject to the terms of the
54c06356bSdh142964  * Common Development and Distribution License (the "License").
64c06356bSdh142964  * You may not use this file except in compliance with the License.
74c06356bSdh142964  *
84c06356bSdh142964  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh142964  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh142964  * See the License for the specific language governing permissions
114c06356bSdh142964  * and limitations under the License.
124c06356bSdh142964  *
134c06356bSdh142964  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh142964  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh142964  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh142964  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh142964  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh142964  *
194c06356bSdh142964  * CDDL HEADER END
204c06356bSdh142964  *
214c06356bSdh142964  *
224c06356bSdh142964  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234c06356bSdh142964  * Use is subject to license terms.
244c06356bSdh142964  */
254c06356bSdh142964 /*
264c06356bSdh142964  * SCSI (SCSA) midlayer interface for PMC drier.
274c06356bSdh142964  */
284c06356bSdh142964 
294c06356bSdh142964 #include <sys/scsi/adapters/pmcs/pmcs.h>
304c06356bSdh142964 
314c06356bSdh142964 extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
324c06356bSdh142964 
334c06356bSdh142964 static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
344c06356bSdh142964     scsi_hba_tran_t *, struct scsi_device *);
354c06356bSdh142964 static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
364c06356bSdh142964     scsi_hba_tran_t *, struct scsi_device *);
374c06356bSdh142964 static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
384c06356bSdh142964 static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
394c06356bSdh142964 static int pmcs_scsa_reset(struct scsi_address *, int);
404c06356bSdh142964 static int pmcs_scsi_reset_notify(struct scsi_address *, int,
414c06356bSdh142964     void (*)(caddr_t), caddr_t);
424c06356bSdh142964 static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
434c06356bSdh142964 static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
444c06356bSdh142964 static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
454c06356bSdh142964 static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
4696c4a178SChris Horne 
4796c4a178SChris Horne static int pmcs_smp_init(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4896c4a178SChris Horne     smp_device_t *);
4996c4a178SChris Horne static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
5096c4a178SChris Horne     smp_device_t *);
514c06356bSdh142964 static int pmcs_smp_start(struct smp_pkt *);
524c06356bSdh142964 
534c06356bSdh142964 static int pmcs_scsi_quiesce(dev_info_t *);
544c06356bSdh142964 static int pmcs_scsi_unquiesce(dev_info_t *);
554c06356bSdh142964 
564c06356bSdh142964 static int pmcs_cap(struct scsi_address *, char *, int, int, int);
574c06356bSdh142964 static pmcs_xscsi_t *
584c06356bSdh142964     pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
594c06356bSdh142964 static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
604c06356bSdh142964 static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
614c06356bSdh142964 
624c06356bSdh142964 static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
634c06356bSdh142964 static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
644c06356bSdh142964 static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
654c06356bSdh142964 
664c06356bSdh142964 static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
674c06356bSdh142964     pmcwork_t *, uint32_t *);
684c06356bSdh142964 
694c06356bSdh142964 
704c06356bSdh142964 int
714c06356bSdh142964 pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
724c06356bSdh142964 {
734c06356bSdh142964 	scsi_hba_tran_t *tran;
744c06356bSdh142964 	ddi_dma_attr_t pmcs_scsa_dattr;
754c06356bSdh142964 	int flags;
764c06356bSdh142964 
774c06356bSdh142964 	(void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
784c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_sgllen =
794c06356bSdh142964 	    ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
804c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
814c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
824c06356bSdh142964 
834c06356bSdh142964 	/*
844c06356bSdh142964 	 * Allocate a transport structure
854c06356bSdh142964 	 */
864c06356bSdh142964 	tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
874c06356bSdh142964 	if (tran == NULL) {
88c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
89c3bc407cSdh142964 		    "scsi_hba_tran_alloc failed");
904c06356bSdh142964 		return (DDI_FAILURE);
914c06356bSdh142964 	}
924c06356bSdh142964 
934c06356bSdh142964 	tran->tran_hba_private		= pwp;
944c06356bSdh142964 	tran->tran_tgt_init		= pmcs_scsa_tran_tgt_init;
954c06356bSdh142964 	tran->tran_tgt_free		= pmcs_scsa_tran_tgt_free;
964c06356bSdh142964 	tran->tran_start		= pmcs_scsa_start;
974c06356bSdh142964 	tran->tran_abort		= pmcs_scsa_abort;
984c06356bSdh142964 	tran->tran_reset		= pmcs_scsa_reset;
994c06356bSdh142964 	tran->tran_reset_notify		= pmcs_scsi_reset_notify;
1004c06356bSdh142964 	tran->tran_getcap		= pmcs_scsa_getcap;
1014c06356bSdh142964 	tran->tran_setcap		= pmcs_scsa_setcap;
1024c06356bSdh142964 	tran->tran_setup_pkt		= pmcs_scsa_setup_pkt;
1034c06356bSdh142964 	tran->tran_teardown_pkt		= pmcs_scsa_teardown_pkt;
1044c06356bSdh142964 	tran->tran_quiesce		= pmcs_scsi_quiesce;
1054c06356bSdh142964 	tran->tran_unquiesce		= pmcs_scsi_unquiesce;
1064c06356bSdh142964 	tran->tran_interconnect_type	= INTERCONNECT_SAS;
1074c06356bSdh142964 	tran->tran_hba_len		= sizeof (pmcs_cmd_t);
1084c06356bSdh142964 
1094c06356bSdh142964 	/*
1104c06356bSdh142964 	 * Attach this instance of the hba
1114c06356bSdh142964 	 */
1124c06356bSdh142964 
1134c06356bSdh142964 	flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
1144c06356bSdh142964 	    SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
1154c06356bSdh142964 
1164c06356bSdh142964 	if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
1174c06356bSdh142964 		scsi_hba_tran_free(tran);
118c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
119c3bc407cSdh142964 		    "scsi_hba_attach failed");
1204c06356bSdh142964 		return (DDI_FAILURE);
1214c06356bSdh142964 	}
1224c06356bSdh142964 	pwp->tran = tran;
1234c06356bSdh142964 
1244c06356bSdh142964 	/*
1254c06356bSdh142964 	 * Attach the SMP part of this hba
1264c06356bSdh142964 	 */
12796c4a178SChris Horne 	pwp->smp_tran = smp_hba_tran_alloc(pwp->dip);
1284c06356bSdh142964 	ASSERT(pwp->smp_tran != NULL);
12996c4a178SChris Horne 	pwp->smp_tran->smp_tran_hba_private = pwp;
13096c4a178SChris Horne 	pwp->smp_tran->smp_tran_init = pmcs_smp_init;
13196c4a178SChris Horne 	pwp->smp_tran->smp_tran_free = pmcs_smp_free;
13296c4a178SChris Horne 	pwp->smp_tran->smp_tran_start = pmcs_smp_start;
1334c06356bSdh142964 
13496c4a178SChris Horne 	if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
135c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13696c4a178SChris Horne 		    "smp_hba_attach failed");
13796c4a178SChris Horne 		smp_hba_tran_free(pwp->smp_tran);
1384c06356bSdh142964 		pwp->smp_tran = NULL;
1394c06356bSdh142964 		scsi_hba_tran_free(tran);
1404c06356bSdh142964 		return (DDI_FAILURE);
1414c06356bSdh142964 	}
1424c06356bSdh142964 
1434c06356bSdh142964 	return (DDI_SUCCESS);
1444c06356bSdh142964 }
1454c06356bSdh142964 
1464c06356bSdh142964 /*
1474c06356bSdh142964  * SCSA entry points
1484c06356bSdh142964  */
1494c06356bSdh142964 
1504c06356bSdh142964 static int
1514c06356bSdh142964 pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
1524c06356bSdh142964     scsi_hba_tran_t *tran, struct scsi_device *sd)
1534c06356bSdh142964 {
1544c06356bSdh142964 	pmcs_hw_t	*pwp = NULL;
1554c06356bSdh142964 	int		rval;
1564c06356bSdh142964 	char		*variant_prop = "sata";
1574c06356bSdh142964 	char		*tgt_port = NULL, *ua = NULL;
1584c06356bSdh142964 	pmcs_xscsi_t	*tgt = NULL;
1594c06356bSdh142964 	pmcs_iport_t	*iport;
1604c06356bSdh142964 	pmcs_lun_t	*lun = NULL;
1614c06356bSdh142964 	pmcs_phy_t	*phyp = NULL;
1624c06356bSdh142964 	uint64_t	lun_num;
1634c06356bSdh142964 	boolean_t	got_scratch = B_FALSE;
1644c06356bSdh142964 
1654c06356bSdh142964 	/*
1664c06356bSdh142964 	 * First, make sure we're an iport and get the pointer to the HBA
1674c06356bSdh142964 	 * node's softstate
1684c06356bSdh142964 	 */
1694c06356bSdh142964 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
170c3bc407cSdh142964 		pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
1714c06356bSdh142964 		    "%s: We don't enumerate devices on the HBA node", __func__);
1724c06356bSdh142964 		goto tgt_init_fail;
1734c06356bSdh142964 	}
1744c06356bSdh142964 
1754c06356bSdh142964 	pwp = ITRAN2PMC(tran);
1764c06356bSdh142964 	iport = ITRAN2IPORT(tran);
1774c06356bSdh142964 
1784c06356bSdh142964 	/*
1794c06356bSdh142964 	 * Get the target address
1804c06356bSdh142964 	 */
1814c06356bSdh142964 	rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
1824c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
1834c06356bSdh142964 	if (rval != DDI_PROP_SUCCESS) {
184c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
185c3bc407cSdh142964 		    "Couldn't get target UA");
1864c06356bSdh142964 		pwp = NULL;
1874c06356bSdh142964 		goto tgt_init_fail;
1884c06356bSdh142964 	}
189c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
190c3bc407cSdh142964 	    "got tgt_port '%s'", tgt_port);
1914c06356bSdh142964 
1924c06356bSdh142964 	/*
1934c06356bSdh142964 	 * Validate that this tran_tgt_init is for an active iport.
1944c06356bSdh142964 	 */
1954c06356bSdh142964 	if (iport->ua_state == UA_INACTIVE) {
196c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1974c06356bSdh142964 		    "%s: Got tran_tgt_init on inactive iport for '%s'",
1984c06356bSdh142964 		    __func__, tgt_port);
1994c06356bSdh142964 		pwp = NULL;
2004c06356bSdh142964 		goto tgt_init_fail;
2014c06356bSdh142964 	}
2024c06356bSdh142964 
2034c06356bSdh142964 	/*
2044c06356bSdh142964 	 * Since we're going to wait for scratch, be sure to acquire it while
2054c06356bSdh142964 	 * we're not holding any other locks
2064c06356bSdh142964 	 */
2074c06356bSdh142964 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
2084c06356bSdh142964 	got_scratch = B_TRUE;
2094c06356bSdh142964 
2104c06356bSdh142964 	mutex_enter(&pwp->lock);
2114c06356bSdh142964 
2124c06356bSdh142964 	/*
2134c06356bSdh142964 	 * See if there's already a target softstate.  If not, allocate one.
2144c06356bSdh142964 	 */
2154c06356bSdh142964 	tgt = pmcs_get_target(iport, tgt_port);
2164c06356bSdh142964 
2174c06356bSdh142964 	if (tgt == NULL) {
2184c06356bSdh142964 		goto tgt_init_fail;
2194c06356bSdh142964 	}
2204c06356bSdh142964 
2214c06356bSdh142964 	phyp = tgt->phy;
2224c06356bSdh142964 	if (!IS_ROOT_PHY(phyp)) {
2234c06356bSdh142964 		pmcs_inc_phy_ref_count(phyp);
2244c06356bSdh142964 	}
2254c06356bSdh142964 	ASSERT(mutex_owned(&phyp->phy_lock));
2264c06356bSdh142964 
227c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "tgt = 0x%p, dip = 0x%p",
2284c06356bSdh142964 	    (void *)tgt, (void *)tgt_dip);
2294c06356bSdh142964 
2304c06356bSdh142964 	/*
2314c06356bSdh142964 	 * Now get the full "w<WWN>,LUN" unit-address (including LU).
2324c06356bSdh142964 	 */
2334c06356bSdh142964 	ua = scsi_device_unit_address(sd);
2344c06356bSdh142964 	if (ua == NULL) {
235c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
2364c06356bSdh142964 		    "Couldn't get LU unit address");
2374c06356bSdh142964 		goto tgt_init_fail;
2384c06356bSdh142964 	}
239c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, tgt, "got lun ua '%s'", ua);
2404c06356bSdh142964 
2414c06356bSdh142964 	lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
2424c06356bSdh142964 	    SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
2434c06356bSdh142964 	if (lun_num == SCSI_LUN64_ILLEGAL) {
244c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
245c3bc407cSdh142964 		    "No LUN for tgt %p", (void *)tgt);
2464c06356bSdh142964 		goto tgt_init_fail;
2474c06356bSdh142964 	}
2484c06356bSdh142964 
249c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy "
250c3bc407cSdh142964 	    "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
2514c06356bSdh142964 
2524c06356bSdh142964 	mutex_enter(&tgt->statlock);
2534c06356bSdh142964 	tgt->dtype = phyp->dtype;
2544c06356bSdh142964 	if (tgt->dtype != SAS && tgt->dtype != SATA) {
255c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
256c3bc407cSdh142964 		    "PHY 0x%p went away?", (void *)phyp);
2574c06356bSdh142964 		goto tgt_init_fail;
2584c06356bSdh142964 	}
2594c06356bSdh142964 
2604c06356bSdh142964 	/* We don't support SATA devices at LUN > 0. */
2614c06356bSdh142964 	if ((tgt->dtype == SATA) && (lun_num > 0)) {
262c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
2634c06356bSdh142964 		    "%s: No support for SATA devices at LUN > 0 "
2644c06356bSdh142964 		    "(target = 0x%p)", __func__, (void *)tgt);
2654c06356bSdh142964 		goto tgt_init_fail;
2664c06356bSdh142964 	}
2674c06356bSdh142964 
2684c06356bSdh142964 	/*
2694c06356bSdh142964 	 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
2704c06356bSdh142964 	 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
2714c06356bSdh142964 	 * verify that the framework never tries to initialize two scsi_device
2724c06356bSdh142964 	 * structures with the same unit-address at the same time.
2734c06356bSdh142964 	 */
2744c06356bSdh142964 	if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
275c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
2764c06356bSdh142964 		    "Couldn't allocate LU soft state");
2774c06356bSdh142964 		goto tgt_init_fail;
2784c06356bSdh142964 	}
2794c06356bSdh142964 
2804c06356bSdh142964 	lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
2814c06356bSdh142964 	if (lun == NULL) {
282c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
283c3bc407cSdh142964 		    "Couldn't get LU soft state");
2844c06356bSdh142964 		goto tgt_init_fail;
2854c06356bSdh142964 	}
2864c06356bSdh142964 	scsi_device_hba_private_set(sd, lun);
2874c06356bSdh142964 	lun->lun_num = lun_num;
2884c06356bSdh142964 
2894c06356bSdh142964 	/* convert the scsi_lun64_t value to SCSI standard form */
2904c06356bSdh142964 	lun->scsi_lun = scsi_lun64_to_lun(lun_num);
2914c06356bSdh142964 
2924c06356bSdh142964 	ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
2934c06356bSdh142964 	bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
2944c06356bSdh142964 
2954c06356bSdh142964 	lun->target = tgt;
2964c06356bSdh142964 
2974c06356bSdh142964 	/*
2984c06356bSdh142964 	 * If this is the first tran_tgt_init, add this target to our list
2994c06356bSdh142964 	 */
3004c06356bSdh142964 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
3014c06356bSdh142964 		int target;
3024c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
3034c06356bSdh142964 			if (pwp->targets[target] != NULL) {
3044c06356bSdh142964 				continue;
3054c06356bSdh142964 			}
3064c06356bSdh142964 
3074c06356bSdh142964 			pwp->targets[target] = tgt;
3084c06356bSdh142964 			tgt->target_num = (uint16_t)target;
3094c06356bSdh142964 			break;
3104c06356bSdh142964 		}
3114c06356bSdh142964 
3124c06356bSdh142964 		if (target == pwp->max_dev) {
313c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3144c06356bSdh142964 			    "Target list full.");
3154c06356bSdh142964 			goto tgt_init_fail;
3164c06356bSdh142964 		}
3174c06356bSdh142964 	}
3184c06356bSdh142964 
3194c06356bSdh142964 	tgt->dip = sd->sd_dev;
320499cfd15SDavid Hollister 	tgt->sd = sd;
3214c06356bSdh142964 
3224c06356bSdh142964 	if (!pmcs_assign_device(pwp, tgt)) {
3234c06356bSdh142964 		pmcs_release_scratch(pwp);
3244c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
3254c06356bSdh142964 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
3264c06356bSdh142964 		tgt->phy = NULL;
327c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3284c06356bSdh142964 		    "%s: pmcs_assign_device failed for target 0x%p",
3294c06356bSdh142964 		    __func__, (void *)tgt);
3304c06356bSdh142964 		goto tgt_init_fail;
3314c06356bSdh142964 	}
3324c06356bSdh142964 
3334c06356bSdh142964 	pmcs_release_scratch(pwp);
3344c06356bSdh142964 	tgt->ref_count++;
3354c06356bSdh142964 
3364c06356bSdh142964 	(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3374c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
3384c06356bSdh142964 
3394c06356bSdh142964 	/* SM-HBA */
3404c06356bSdh142964 	if (tgt->dtype == SATA) {
3414c06356bSdh142964 		/* TCR in PSARC/1997/281 opinion */
3424c06356bSdh142964 		(void) scsi_device_prop_update_string(sd,
3434c06356bSdh142964 		    SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
3444c06356bSdh142964 	}
3454c06356bSdh142964 
3464c06356bSdh142964 	tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
3474c06356bSdh142964 
3484c06356bSdh142964 	if (tgt->phy_addressable) {
3494c06356bSdh142964 		(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3504c06356bSdh142964 		    SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
3514c06356bSdh142964 	}
3524c06356bSdh142964 
3534c06356bSdh142964 	/* SM-HBA */
3544c06356bSdh142964 	(void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd);
355499cfd15SDavid Hollister 	/*
356499cfd15SDavid Hollister 	 * Make sure attached port and target port pm props are updated
357499cfd15SDavid Hollister 	 * By passing in 0s, we're not actually updating any values, but
358499cfd15SDavid Hollister 	 * the properties should now get updated on the node.
359499cfd15SDavid Hollister 	 */
360499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(phyp, 0, 0, B_TRUE);
3614c06356bSdh142964 
3624c06356bSdh142964 	mutex_exit(&tgt->statlock);
3634c06356bSdh142964 	pmcs_unlock_phy(phyp);
3644c06356bSdh142964 	mutex_exit(&pwp->lock);
3654c06356bSdh142964 	scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
3664c06356bSdh142964 	return (DDI_SUCCESS);
3674c06356bSdh142964 
3684c06356bSdh142964 tgt_init_fail:
3694c06356bSdh142964 	if (got_scratch) {
3704c06356bSdh142964 		pmcs_release_scratch(pwp);
3714c06356bSdh142964 	}
3724c06356bSdh142964 	if (lun) {
3734c06356bSdh142964 		ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
3744c06356bSdh142964 	}
3754c06356bSdh142964 	if (phyp) {
3764c06356bSdh142964 		mutex_exit(&tgt->statlock);
3774c06356bSdh142964 		pmcs_unlock_phy(phyp);
3784c06356bSdh142964 		/*
3794c06356bSdh142964 		 * phyp's ref count was incremented in pmcs_new_tport.
3804c06356bSdh142964 		 * We're failing configuration, we now need to decrement it.
3814c06356bSdh142964 		 */
3824c06356bSdh142964 		if (!IS_ROOT_PHY(phyp)) {
3834c06356bSdh142964 			pmcs_dec_phy_ref_count(phyp);
3844c06356bSdh142964 		}
3854c06356bSdh142964 		phyp->target = NULL;
3864c06356bSdh142964 	}
3874c06356bSdh142964 	if (tgt && tgt->ref_count == 0) {
3884c06356bSdh142964 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
3894c06356bSdh142964 	}
3904c06356bSdh142964 	if (pwp) {
3914c06356bSdh142964 		mutex_exit(&pwp->lock);
3924c06356bSdh142964 	}
3934c06356bSdh142964 	if (tgt_port) {
3944c06356bSdh142964 		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
3954c06356bSdh142964 	}
3964c06356bSdh142964 	return (DDI_FAILURE);
3974c06356bSdh142964 }
3984c06356bSdh142964 
3994c06356bSdh142964 static void
4004c06356bSdh142964 pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
4014c06356bSdh142964     scsi_hba_tran_t *tran, struct scsi_device *sd)
4024c06356bSdh142964 {
4034c06356bSdh142964 	_NOTE(ARGUNUSED(hba_dip, tgt_dip));
4044c06356bSdh142964 	pmcs_hw_t	*pwp;
4054c06356bSdh142964 	pmcs_lun_t	*lun;
4064c06356bSdh142964 	pmcs_xscsi_t	*target;
4074c06356bSdh142964 	char		*unit_address;
4084c06356bSdh142964 	pmcs_phy_t	*phyp;
4094c06356bSdh142964 
4104c06356bSdh142964 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
4114c06356bSdh142964 		pwp = TRAN2PMC(tran);
412c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
4134c06356bSdh142964 		    "%s: We don't enumerate devices on the HBA node", __func__);
4144c06356bSdh142964 		return;
4154c06356bSdh142964 	}
4164c06356bSdh142964 
4174c06356bSdh142964 	lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
4184c06356bSdh142964 
4194c06356bSdh142964 	ASSERT((lun != NULL) && (lun->target != NULL));
4204c06356bSdh142964 	ASSERT(lun->target->ref_count > 0);
4214c06356bSdh142964 
4224c06356bSdh142964 	target = lun->target;
4234c06356bSdh142964 
4244c06356bSdh142964 	unit_address = lun->unit_address;
4254c06356bSdh142964 	ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address);
4264c06356bSdh142964 
4274c06356bSdh142964 	pwp = ITRAN2PMC(tran);
4284c06356bSdh142964 	mutex_enter(&pwp->lock);
4294c06356bSdh142964 	mutex_enter(&target->statlock);
4304c06356bSdh142964 	ASSERT(target->phy);
4314c06356bSdh142964 	phyp = target->phy;
4324c06356bSdh142964 
433af685682SSrikanth, Ramana 	if (target->recover_wait) {
434af685682SSrikanth, Ramana 		mutex_exit(&target->statlock);
435af685682SSrikanth, Ramana 		mutex_exit(&pwp->lock);
436af685682SSrikanth, Ramana 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, "%s: "
437af685682SSrikanth, Ramana 		    "Target 0x%p in device state recovery, fail tran_tgt_free",
438af685682SSrikanth, Ramana 		    __func__, (void *)target);
439af685682SSrikanth, Ramana 		return;
440af685682SSrikanth, Ramana 	}
441af685682SSrikanth, Ramana 
4424c06356bSdh142964 	/*
4434c06356bSdh142964 	 * If this target still has a PHY pointer and that PHY's target pointer
4444c06356bSdh142964 	 * has been cleared, then that PHY has been reaped. In that case, there
4454c06356bSdh142964 	 * would be no need to decrement the reference count
4464c06356bSdh142964 	 */
4474c06356bSdh142964 	if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
4484c06356bSdh142964 		pmcs_dec_phy_ref_count(phyp);
4494c06356bSdh142964 	}
4504c06356bSdh142964 
4514c06356bSdh142964 	if (--target->ref_count == 0) {
4524c06356bSdh142964 		/*
4534c06356bSdh142964 		 * Remove this target from our list.  The target soft
4544c06356bSdh142964 		 * state will remain, and the device will remain registered
4554c06356bSdh142964 		 * with the hardware unless/until we're told the device
4564c06356bSdh142964 		 * physically went away.
4574c06356bSdh142964 		 */
458c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
4594c06356bSdh142964 		    "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
4604c06356bSdh142964 		    target->target_num);
4614c06356bSdh142964 		pwp->targets[target->target_num] = NULL;
4624c06356bSdh142964 		target->target_num = PMCS_INVALID_TARGET_NUM;
4634c06356bSdh142964 		/*
4644c06356bSdh142964 		 * If the target still has a PHY pointer, break the linkage
4654c06356bSdh142964 		 */
4664c06356bSdh142964 		if (phyp) {
4674c06356bSdh142964 			phyp->target = NULL;
4684c06356bSdh142964 		}
4694c06356bSdh142964 		target->phy = NULL;
4704c06356bSdh142964 		pmcs_destroy_target(target);
4714c06356bSdh142964 	} else {
4724c06356bSdh142964 		mutex_exit(&target->statlock);
4734c06356bSdh142964 	}
4744c06356bSdh142964 
4754c06356bSdh142964 	mutex_exit(&pwp->lock);
4764c06356bSdh142964 }
4774c06356bSdh142964 
4784c06356bSdh142964 static int
4794c06356bSdh142964 pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
4804c06356bSdh142964 {
4814c06356bSdh142964 	pmcs_cmd_t *sp = PKT2CMD(pkt);
4824c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
4834c06356bSdh142964 	pmcs_xscsi_t *xp;
4844c06356bSdh142964 	boolean_t blocked;
4854c06356bSdh142964 	uint32_t hba_state;
4864c06356bSdh142964 
487c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
488c3bc407cSdh142964 	    "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt,
4894c06356bSdh142964 	    (void *)scsi_address_device(&pkt->pkt_address),
4904c06356bSdh142964 	    pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
4914c06356bSdh142964 
4924c06356bSdh142964 	if (pkt->pkt_flags & FLAG_NOINTR) {
493c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
494c3bc407cSdh142964 		    "%s: nointr pkt", __func__);
4954c06356bSdh142964 		return (TRAN_BADPKT);
4964c06356bSdh142964 	}
4974c06356bSdh142964 
4984c06356bSdh142964 	sp->cmd_tag = 0;
4994c06356bSdh142964 	pkt->pkt_state = pkt->pkt_statistics = 0;
5004c06356bSdh142964 	pkt->pkt_reason = CMD_INCOMPLETE;
5014c06356bSdh142964 
5024c06356bSdh142964 	mutex_enter(&pwp->lock);
5034c06356bSdh142964 	hba_state = pwp->state;
5044c06356bSdh142964 	blocked = pwp->blocked;
5054c06356bSdh142964 	mutex_exit(&pwp->lock);
5064c06356bSdh142964 
5074c06356bSdh142964 	if (hba_state != STATE_RUNNING) {
508c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
509c3bc407cSdh142964 		    "%s: hba dead", __func__);
5104c06356bSdh142964 		return (TRAN_FATAL_ERROR);
5114c06356bSdh142964 	}
5124c06356bSdh142964 
5134c06356bSdh142964 	xp = pmcs_addr2xp(ap, NULL, sp);
5144c06356bSdh142964 	if (xp == NULL) {
515c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
5164c06356bSdh142964 		    "%s: dropping due to null target", __func__);
517b18a19c2SJesse Butler 		goto dead_target;
5184c06356bSdh142964 	}
5194c06356bSdh142964 	ASSERT(mutex_owned(&xp->statlock));
5204c06356bSdh142964 
5214c06356bSdh142964 	/*
522b18a19c2SJesse Butler 	 * First, check to see if the device is gone.
5234c06356bSdh142964 	 */
524b18a19c2SJesse Butler 	if (xp->dev_gone) {
525*601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5264c06356bSdh142964 		mutex_exit(&xp->statlock);
527c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp,
528b18a19c2SJesse Butler 		    "%s: dropping due to dead target 0x%p",
5294c06356bSdh142964 		    __func__, (void *)xp);
530b18a19c2SJesse Butler 		goto dead_target;
5314c06356bSdh142964 	}
5324c06356bSdh142964 
5334c06356bSdh142964 	/*
5344c06356bSdh142964 	 * If we're blocked (quiesced) just return.
5354c06356bSdh142964 	 */
5364c06356bSdh142964 	if (blocked) {
537c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
538c3bc407cSdh142964 		    "%s: hba blocked", __func__);
539*601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5404c06356bSdh142964 		mutex_exit(&xp->statlock);
5414c06356bSdh142964 		mutex_enter(&xp->wqlock);
5424c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5434c06356bSdh142964 		mutex_exit(&xp->wqlock);
5444c06356bSdh142964 		return (TRAN_ACCEPT);
5454c06356bSdh142964 	}
5464c06356bSdh142964 
5474c06356bSdh142964 	/*
5484c06356bSdh142964 	 * If we're draining or resetting, queue and return.
5494c06356bSdh142964 	 */
5504c06356bSdh142964 	if (xp->draining || xp->resetting || xp->recover_wait) {
551*601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5524c06356bSdh142964 		mutex_exit(&xp->statlock);
5534c06356bSdh142964 		mutex_enter(&xp->wqlock);
5544c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5554c06356bSdh142964 		mutex_exit(&xp->wqlock);
556c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
5574c06356bSdh142964 		    "%s: draining/resetting/recovering (cnt %u)",
5584c06356bSdh142964 		    __func__, xp->actv_cnt);
5594c06356bSdh142964 		/*
5604c06356bSdh142964 		 * By the time we get here, draining or
5614c06356bSdh142964 		 * resetting may have come and gone, not
5624c06356bSdh142964 		 * yet noticing that we had put something
5634c06356bSdh142964 		 * on the wait queue, so schedule a worker
5644c06356bSdh142964 		 * to look at this later.
5654c06356bSdh142964 		 */
5664c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
5674c06356bSdh142964 		return (TRAN_ACCEPT);
5684c06356bSdh142964 	}
569*601c90f1SSrikanth, Ramana 
570*601c90f1SSrikanth, Ramana 	xp->actv_pkts++;
5714c06356bSdh142964 	mutex_exit(&xp->statlock);
5724c06356bSdh142964 
5734c06356bSdh142964 	/*
5744c06356bSdh142964 	 * Queue this command to the tail of the wait queue.
5754c06356bSdh142964 	 * This keeps us getting commands out of order.
5764c06356bSdh142964 	 */
5774c06356bSdh142964 	mutex_enter(&xp->wqlock);
5784c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5794c06356bSdh142964 	mutex_exit(&xp->wqlock);
5804c06356bSdh142964 
5814c06356bSdh142964 	/*
5824c06356bSdh142964 	 * Now run the queue for this device.
5834c06356bSdh142964 	 */
5844c06356bSdh142964 	(void) pmcs_scsa_wq_run_one(pwp, xp);
5854c06356bSdh142964 
5864c06356bSdh142964 	return (TRAN_ACCEPT);
5874c06356bSdh142964 
588b18a19c2SJesse Butler dead_target:
5894c06356bSdh142964 	pkt->pkt_state = STATE_GOT_BUS;
5904c06356bSdh142964 	pkt->pkt_reason = CMD_DEV_GONE;
5914c06356bSdh142964 	mutex_enter(&pwp->cq_lock);
5924c06356bSdh142964 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
5934c06356bSdh142964 	PMCS_CQ_RUN_LOCKED(pwp);
5944c06356bSdh142964 	mutex_exit(&pwp->cq_lock);
5954c06356bSdh142964 	return (TRAN_ACCEPT);
5964c06356bSdh142964 }
5974c06356bSdh142964 
5984c06356bSdh142964 static int
5994c06356bSdh142964 pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
6004c06356bSdh142964 {
6014c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
6024c06356bSdh142964 	pmcs_cmd_t *sp = PKT2CMD(pkt);
6034c06356bSdh142964 	pmcs_xscsi_t *xp = sp->cmd_target;
6044c06356bSdh142964 	pmcs_phy_t *pptr;
6054c06356bSdh142964 	uint32_t tag;
6064c06356bSdh142964 	uint64_t lun;
6074c06356bSdh142964 	pmcwork_t *pwrk;
6084c06356bSdh142964 
6094c06356bSdh142964 	mutex_enter(&pwp->lock);
6104c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
6114c06356bSdh142964 		mutex_exit(&pwp->lock);
612c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
613c3bc407cSdh142964 		    "%s: hba dead", __func__);
6144c06356bSdh142964 		return (0);
6154c06356bSdh142964 	}
6164c06356bSdh142964 	mutex_exit(&pwp->lock);
6174c06356bSdh142964 
6184c06356bSdh142964 	if (sp->cmd_lun) {
6194c06356bSdh142964 		lun = sp->cmd_lun->lun_num;
6204c06356bSdh142964 	} else {
6214c06356bSdh142964 		lun = 0;
6224c06356bSdh142964 	}
6234c06356bSdh142964 	if (xp == NULL) {
6244c06356bSdh142964 		return (0);
6254c06356bSdh142964 	}
6264c06356bSdh142964 
6274c06356bSdh142964 	/*
6284c06356bSdh142964 	 * See if we have a real work structure associated with this cmd.
6294c06356bSdh142964 	 */
6304c06356bSdh142964 	pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
6314c06356bSdh142964 	if (pwrk && pwrk->arg == sp) {
6324c06356bSdh142964 		tag = pwrk->htag;
6334c06356bSdh142964 		pptr = pwrk->phy;
6344c06356bSdh142964 		pwrk->timer = 0;	/* we don't time this here */
6354c06356bSdh142964 		ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
6364c06356bSdh142964 		mutex_exit(&pwrk->lock);
6374c06356bSdh142964 		pmcs_lock_phy(pptr);
6384c06356bSdh142964 		if (pptr->dtype == SAS) {
6394c06356bSdh142964 			if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
6404c06356bSdh142964 			    NULL)) {
6414c06356bSdh142964 				pptr->abort_pending = 1;
6424c06356bSdh142964 				pmcs_unlock_phy(pptr);
6434c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6444c06356bSdh142964 				return (0);
6454c06356bSdh142964 			}
6464c06356bSdh142964 		} else {
6474c06356bSdh142964 			/*
6484c06356bSdh142964 			 * XXX: Was the command that was active an
6494c06356bSdh142964 			 * NCQ I/O command?
6504c06356bSdh142964 			 */
6514c06356bSdh142964 			pptr->need_rl_ext = 1;
6524c06356bSdh142964 			if (pmcs_sata_abort_ncq(pwp, pptr)) {
6534c06356bSdh142964 				pptr->abort_pending = 1;
6544c06356bSdh142964 				pmcs_unlock_phy(pptr);
6554c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6564c06356bSdh142964 				return (0);
6574c06356bSdh142964 			}
6584c06356bSdh142964 		}
6594c06356bSdh142964 		pptr->abort_pending = 1;
6604c06356bSdh142964 		pmcs_unlock_phy(pptr);
6614c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6624c06356bSdh142964 		return (1);
6634c06356bSdh142964 	}
6644c06356bSdh142964 	if (pwrk) {
6654c06356bSdh142964 		mutex_exit(&pwrk->lock);
6664c06356bSdh142964 	}
6674c06356bSdh142964 	/*
6684c06356bSdh142964 	 * Okay, those weren't the droids we were looking for.
6694c06356bSdh142964 	 * See if the command is on any of the wait queues.
6704c06356bSdh142964 	 */
6714c06356bSdh142964 	mutex_enter(&xp->wqlock);
6724c06356bSdh142964 	sp = NULL;
6734c06356bSdh142964 	STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
6744c06356bSdh142964 		if (sp == PKT2CMD(pkt)) {
6754c06356bSdh142964 			STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
6764c06356bSdh142964 			break;
6774c06356bSdh142964 		}
6784c06356bSdh142964 	}
6794c06356bSdh142964 	mutex_exit(&xp->wqlock);
6804c06356bSdh142964 	if (sp) {
6814c06356bSdh142964 		pkt->pkt_reason = CMD_ABORTED;
6824c06356bSdh142964 		pkt->pkt_statistics |= STAT_ABORTED;
6834c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
6844c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6854c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
6864c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
6874c06356bSdh142964 		return (1);
6884c06356bSdh142964 	}
6894c06356bSdh142964 	return (0);
6904c06356bSdh142964 }
6914c06356bSdh142964 
6924c06356bSdh142964 /*
6934c06356bSdh142964  * SCSA reset functions
6944c06356bSdh142964  */
6954c06356bSdh142964 static int
6964c06356bSdh142964 pmcs_scsa_reset(struct scsi_address *ap, int level)
6974c06356bSdh142964 {
6984c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
6994c06356bSdh142964 	pmcs_phy_t *pptr;
7004c06356bSdh142964 	pmcs_xscsi_t *xp;
7014c06356bSdh142964 	uint64_t lun = (uint64_t)-1, *lp = NULL;
7024c06356bSdh142964 	int rval;
7034c06356bSdh142964 
7044c06356bSdh142964 	mutex_enter(&pwp->lock);
7054c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
7064c06356bSdh142964 		mutex_exit(&pwp->lock);
707c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
708c3bc407cSdh142964 		    "%s: hba dead", __func__);
7094c06356bSdh142964 		return (0);
7104c06356bSdh142964 	}
7114c06356bSdh142964 	mutex_exit(&pwp->lock);
7124c06356bSdh142964 
7134c06356bSdh142964 	switch (level)  {
7144c06356bSdh142964 	case RESET_ALL:
7154c06356bSdh142964 		rval = 0;
7164c06356bSdh142964 		break;
7174c06356bSdh142964 	case RESET_LUN:
7184c06356bSdh142964 		/*
7194c06356bSdh142964 		 * Point lp at lun so that pmcs_addr2xp
7204c06356bSdh142964 		 * will fill out the 64 bit lun number.
7214c06356bSdh142964 		 */
7224c06356bSdh142964 		lp = &lun;
7234c06356bSdh142964 		/* FALLTHROUGH */
7244c06356bSdh142964 	case RESET_TARGET:
7254c06356bSdh142964 		xp = pmcs_addr2xp(ap, lp, NULL);
7264c06356bSdh142964 		if (xp == NULL) {
727c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7284c06356bSdh142964 			    "%s: no xp found for this scsi address", __func__);
7294c06356bSdh142964 			return (0);
7304c06356bSdh142964 		}
7314c06356bSdh142964 
732b18a19c2SJesse Butler 		if (xp->dev_gone) {
7334c06356bSdh142964 			mutex_exit(&xp->statlock);
734c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
7354c06356bSdh142964 			    "%s: Target 0x%p has gone away", __func__,
7364c06356bSdh142964 			    (void *)xp);
7374c06356bSdh142964 			return (0);
7384c06356bSdh142964 		}
7394c06356bSdh142964 
7404c06356bSdh142964 		/*
7414c06356bSdh142964 		 * If we're already performing this action, or if device
7424c06356bSdh142964 		 * state recovery is already running, just return failure.
7434c06356bSdh142964 		 */
7444c06356bSdh142964 		if (xp->resetting || xp->recover_wait) {
7454c06356bSdh142964 			mutex_exit(&xp->statlock);
7464c06356bSdh142964 			return (0);
7474c06356bSdh142964 		}
7484c06356bSdh142964 		xp->reset_wait = 0;
7494c06356bSdh142964 		xp->reset_success = 0;
7504c06356bSdh142964 		xp->resetting = 1;
7514c06356bSdh142964 		pptr = xp->phy;
7524c06356bSdh142964 		mutex_exit(&xp->statlock);
7534c06356bSdh142964 
7544c06356bSdh142964 		if (pmcs_reset_dev(pwp, pptr, lun)) {
7554c06356bSdh142964 			rval = 0;
7564c06356bSdh142964 		} else {
7574c06356bSdh142964 			rval = 1;
7584c06356bSdh142964 		}
7594c06356bSdh142964 
7604c06356bSdh142964 		mutex_enter(&xp->statlock);
7614c06356bSdh142964 		if (rval == 1) {
7624c06356bSdh142964 			xp->reset_success = 1;
7634c06356bSdh142964 		}
7644c06356bSdh142964 		if (xp->reset_wait) {
7654c06356bSdh142964 			xp->reset_wait = 0;
7664c06356bSdh142964 			cv_signal(&xp->reset_cv);
7674c06356bSdh142964 		}
7684c06356bSdh142964 		xp->resetting = 0;
7694c06356bSdh142964 		mutex_exit(&xp->statlock);
7704c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
7714c06356bSdh142964 		break;
7724c06356bSdh142964 	default:
7734c06356bSdh142964 		rval = 0;
7744c06356bSdh142964 		break;
7754c06356bSdh142964 	}
7764c06356bSdh142964 
7774c06356bSdh142964 	return (rval);
7784c06356bSdh142964 }
7794c06356bSdh142964 
7804c06356bSdh142964 static int
7814c06356bSdh142964 pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
7824c06356bSdh142964     void (*callback)(caddr_t), caddr_t arg)
7834c06356bSdh142964 {
7844c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
7854c06356bSdh142964 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
7864c06356bSdh142964 	    &pwp->lock, &pwp->reset_notify_listf));
7874c06356bSdh142964 }
7884c06356bSdh142964 
7894c06356bSdh142964 
7904c06356bSdh142964 static int
7914c06356bSdh142964 pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
7924c06356bSdh142964 {
7934c06356bSdh142964 	_NOTE(ARGUNUSED(val, tonly));
7944c06356bSdh142964 	int cidx, rval = 0;
7954c06356bSdh142964 	pmcs_xscsi_t *xp;
7964c06356bSdh142964 
7974c06356bSdh142964 	cidx = scsi_hba_lookup_capstr(cap);
7984c06356bSdh142964 	if (cidx == -1) {
7994c06356bSdh142964 		return (-1);
8004c06356bSdh142964 	}
8014c06356bSdh142964 
8024c06356bSdh142964 	xp = pmcs_addr2xp(ap, NULL, NULL);
8034c06356bSdh142964 	if (xp == NULL) {
8044c06356bSdh142964 		return (-1);
8054c06356bSdh142964 	}
8064c06356bSdh142964 
8074c06356bSdh142964 	switch (cidx) {
8084c06356bSdh142964 	case SCSI_CAP_DMA_MAX:
8094c06356bSdh142964 	case SCSI_CAP_INITIATOR_ID:
8104c06356bSdh142964 		if (set == 0) {
8114c06356bSdh142964 			rval = INT_MAX;	/* argh */
8124c06356bSdh142964 		}
8134c06356bSdh142964 		break;
8144c06356bSdh142964 	case SCSI_CAP_DISCONNECT:
8154c06356bSdh142964 	case SCSI_CAP_SYNCHRONOUS:
8164c06356bSdh142964 	case SCSI_CAP_WIDE_XFER:
8174c06356bSdh142964 	case SCSI_CAP_PARITY:
8184c06356bSdh142964 	case SCSI_CAP_ARQ:
8194c06356bSdh142964 	case SCSI_CAP_UNTAGGED_QING:
8204c06356bSdh142964 		if (set == 0) {
8214c06356bSdh142964 			rval = 1;
8224c06356bSdh142964 		}
8234c06356bSdh142964 		break;
8244c06356bSdh142964 
8254c06356bSdh142964 	case SCSI_CAP_TAGGED_QING:
8264c06356bSdh142964 		rval = 1;
8274c06356bSdh142964 		break;
8284c06356bSdh142964 
8294c06356bSdh142964 	case SCSI_CAP_MSG_OUT:
8304c06356bSdh142964 	case SCSI_CAP_RESET_NOTIFICATION:
8314c06356bSdh142964 	case SCSI_CAP_QFULL_RETRIES:
8324c06356bSdh142964 	case SCSI_CAP_QFULL_RETRY_INTERVAL:
8334c06356bSdh142964 		break;
8344c06356bSdh142964 	case SCSI_CAP_SCSI_VERSION:
8354c06356bSdh142964 		if (set == 0) {
8364c06356bSdh142964 			rval = SCSI_VERSION_3;
8374c06356bSdh142964 		}
8384c06356bSdh142964 		break;
8394c06356bSdh142964 	case SCSI_CAP_INTERCONNECT_TYPE:
8404c06356bSdh142964 		if (set) {
8414c06356bSdh142964 			break;
8424c06356bSdh142964 		}
8434c06356bSdh142964 		if (xp->phy_addressable) {
8444c06356bSdh142964 			rval = INTERCONNECT_SATA;
8454c06356bSdh142964 		} else {
8464c06356bSdh142964 			rval = INTERCONNECT_SAS;
8474c06356bSdh142964 		}
8484c06356bSdh142964 		break;
8494c06356bSdh142964 	case SCSI_CAP_CDB_LEN:
8504c06356bSdh142964 		if (set == 0) {
8514c06356bSdh142964 			rval = 16;
8524c06356bSdh142964 		}
8534c06356bSdh142964 		break;
8544c06356bSdh142964 	case SCSI_CAP_LUN_RESET:
8554c06356bSdh142964 		if (set) {
8564c06356bSdh142964 			break;
8574c06356bSdh142964 		}
8584c06356bSdh142964 		if (xp->dtype == SATA) {
8594c06356bSdh142964 			rval = 0;
8604c06356bSdh142964 		} else {
8614c06356bSdh142964 			rval = 1;
8624c06356bSdh142964 		}
8634c06356bSdh142964 		break;
8644c06356bSdh142964 	default:
8654c06356bSdh142964 		rval = -1;
8664c06356bSdh142964 		break;
8674c06356bSdh142964 	}
8684c06356bSdh142964 	mutex_exit(&xp->statlock);
869c3bc407cSdh142964 	pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL,
8704c06356bSdh142964 	    "%s: cap %s val %d set %d rval %d",
8714c06356bSdh142964 	    __func__, cap, val, set, rval);
8724c06356bSdh142964 	return (rval);
8734c06356bSdh142964 }
8744c06356bSdh142964 
8754c06356bSdh142964 /*
8764c06356bSdh142964  * Returns with statlock held if the xp is found.
8774c06356bSdh142964  * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
8784c06356bSdh142964  */
8794c06356bSdh142964 static pmcs_xscsi_t *
8804c06356bSdh142964 pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
8814c06356bSdh142964 {
8824c06356bSdh142964 	pmcs_xscsi_t *xp;
8834c06356bSdh142964 	pmcs_lun_t *lun = (pmcs_lun_t *)
8844c06356bSdh142964 	    scsi_device_hba_private_get(scsi_address_device(ap));
8854c06356bSdh142964 
8864c06356bSdh142964 	if ((lun == NULL) || (lun->target == NULL)) {
8874c06356bSdh142964 		return (NULL);
8884c06356bSdh142964 	}
8894c06356bSdh142964 	xp = lun->target;
8904c06356bSdh142964 	mutex_enter(&xp->statlock);
8914c06356bSdh142964 
892b18a19c2SJesse Butler 	if (xp->dev_gone || (xp->phy == NULL)) {
893*601c90f1SSrikanth, Ramana 		/*
894*601c90f1SSrikanth, Ramana 		 * This may be a retried packet, so it's possible cmd_target
895*601c90f1SSrikanth, Ramana 		 * and cmd_lun may still be populated.  Clear them.
896*601c90f1SSrikanth, Ramana 		 */
897*601c90f1SSrikanth, Ramana 		if (sp != NULL) {
898*601c90f1SSrikanth, Ramana 			sp->cmd_target = NULL;
899*601c90f1SSrikanth, Ramana 			sp->cmd_lun = NULL;
900*601c90f1SSrikanth, Ramana 		}
9014c06356bSdh142964 		mutex_exit(&xp->statlock);
9024c06356bSdh142964 		return (NULL);
9034c06356bSdh142964 	}
9044c06356bSdh142964 
9054c06356bSdh142964 	if (sp != NULL) {
9064c06356bSdh142964 		sp->cmd_target = xp;
9074c06356bSdh142964 		sp->cmd_lun = lun;
9084c06356bSdh142964 	}
9094c06356bSdh142964 	if (lp) {
9104c06356bSdh142964 		*lp = lun->lun_num;
9114c06356bSdh142964 	}
9124c06356bSdh142964 	return (xp);
9134c06356bSdh142964 }
9144c06356bSdh142964 
9154c06356bSdh142964 static int
9164c06356bSdh142964 pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
9174c06356bSdh142964 {
9184c06356bSdh142964 	int r;
9194c06356bSdh142964 	if (cap == NULL) {
9204c06356bSdh142964 		return (-1);
9214c06356bSdh142964 	}
9224c06356bSdh142964 	r = pmcs_cap(ap, cap, 0, whom, 0);
9234c06356bSdh142964 	return (r);
9244c06356bSdh142964 }
9254c06356bSdh142964 
9264c06356bSdh142964 static int
9274c06356bSdh142964 pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
9284c06356bSdh142964 {
9294c06356bSdh142964 	int r;
9304c06356bSdh142964 	if (cap == NULL) {
9314c06356bSdh142964 		return (-1);
9324c06356bSdh142964 	}
9334c06356bSdh142964 	r = pmcs_cap(ap, cap, value, whom, 1);
9344c06356bSdh142964 	return (r);
9354c06356bSdh142964 }
9364c06356bSdh142964 
9374c06356bSdh142964 static int
9384c06356bSdh142964 pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
9394c06356bSdh142964     caddr_t cbarg)
9404c06356bSdh142964 {
9414c06356bSdh142964 	_NOTE(ARGUNUSED(callback, cbarg));
9424c06356bSdh142964 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
9434c06356bSdh142964 
9444c06356bSdh142964 	bzero(sp, sizeof (pmcs_cmd_t));
9454c06356bSdh142964 	sp->cmd_pkt = pkt;
9464c06356bSdh142964 	return (0);
9474c06356bSdh142964 }
9484c06356bSdh142964 
9494c06356bSdh142964 static void
9504c06356bSdh142964 pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
9514c06356bSdh142964 {
9524c06356bSdh142964 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
9534c06356bSdh142964 	sp->cmd_target = NULL;
9544c06356bSdh142964 	sp->cmd_lun = NULL;
9554c06356bSdh142964 }
9564c06356bSdh142964 
9574c06356bSdh142964 static int
95896c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt)
9594c06356bSdh142964 {
9604c06356bSdh142964 	struct pmcwork *pwrk;
9614c06356bSdh142964 	const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
9624c06356bSdh142964 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
9634c06356bSdh142964 	uint64_t wwn;
96496c4a178SChris Horne 	pmcs_hw_t *pwp;
9654c06356bSdh142964 	pmcs_phy_t *pptr;
9664c06356bSdh142964 	pmcs_xscsi_t *xp;
9674c06356bSdh142964 	uint_t reqsz, rspsz, will_retry;
9684c06356bSdh142964 	int result;
9694c06356bSdh142964 
97096c4a178SChris Horne 	pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private;
97196c4a178SChris Horne 	bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
9724c06356bSdh142964 
973c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
974c3bc407cSdh142964 	    "%s: starting for wwn 0x%" PRIx64, __func__, wwn);
9754c06356bSdh142964 
97696c4a178SChris Horne 	will_retry = smp_pkt->smp_pkt_will_retry;
9774c06356bSdh142964 
9784c06356bSdh142964 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
97996c4a178SChris Horne 	reqsz = smp_pkt->smp_pkt_reqsize;
9804c06356bSdh142964 	if (reqsz > SAS_SMP_MAX_PAYLOAD) {
9814c06356bSdh142964 		reqsz = SAS_SMP_MAX_PAYLOAD;
9824c06356bSdh142964 	}
98396c4a178SChris Horne 	(void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz);
9844c06356bSdh142964 
98596c4a178SChris Horne 	rspsz = smp_pkt->smp_pkt_rspsize;
9864c06356bSdh142964 	if (rspsz > SAS_SMP_MAX_PAYLOAD) {
9874c06356bSdh142964 		rspsz = SAS_SMP_MAX_PAYLOAD;
9884c06356bSdh142964 	}
9894c06356bSdh142964 
9904c06356bSdh142964 	/*
9914c06356bSdh142964 	 * The request size from the SMP driver always includes 4 bytes
9924c06356bSdh142964 	 * for the CRC. The PMCS chip, however, doesn't want to see those
9934c06356bSdh142964 	 * counts as part of the transfer size.
9944c06356bSdh142964 	 */
9954c06356bSdh142964 	reqsz -= 4;
9964c06356bSdh142964 
9974c06356bSdh142964 	pptr = pmcs_find_phy_by_wwn(pwp, wwn);
9984c06356bSdh142964 	/* PHY is now locked */
9994c06356bSdh142964 	if (pptr == NULL || pptr->dtype != EXPANDER) {
10004c06356bSdh142964 		if (pptr) {
10014c06356bSdh142964 			pmcs_unlock_phy(pptr);
10024c06356bSdh142964 		}
10034c06356bSdh142964 		pmcs_release_scratch(pwp);
1004c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1005c3bc407cSdh142964 		    "%s: could not find phy", __func__);
100696c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ENXIO;
10074c06356bSdh142964 		return (DDI_FAILURE);
10084c06356bSdh142964 	}
10094c06356bSdh142964 
10104c06356bSdh142964 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
10114c06356bSdh142964 	if (pwrk == NULL) {
10124c06356bSdh142964 		pmcs_unlock_phy(pptr);
10134c06356bSdh142964 		pmcs_release_scratch(pwp);
1014c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
10154c06356bSdh142964 		    "%s: could not get work structure", __func__);
101696c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY;
10174c06356bSdh142964 		return (DDI_FAILURE);
10184c06356bSdh142964 	}
10194c06356bSdh142964 
10204c06356bSdh142964 	pwrk->arg = msg;
10214c06356bSdh142964 	pwrk->dtype = EXPANDER;
10224c06356bSdh142964 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10234c06356bSdh142964 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10244c06356bSdh142964 	if (ptr == NULL) {
10254c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
10264c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10274c06356bSdh142964 		pmcs_unlock_phy(pptr);
10284c06356bSdh142964 		pmcs_release_scratch(pwp);
1029c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1030c3bc407cSdh142964 		    "%s: could not get IQ entry", __func__);
103196c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY;
10324c06356bSdh142964 		return (DDI_FAILURE);
10334c06356bSdh142964 	}
10344c06356bSdh142964 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
10354c06356bSdh142964 	msg[1] = LE_32(pwrk->htag);
10364c06356bSdh142964 	msg[2] = LE_32(pptr->device_id);
10374c06356bSdh142964 	msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
10384c06356bSdh142964 	msg[8] = LE_32(DWORD0(pwp->scratch_dma));
10394c06356bSdh142964 	msg[9] = LE_32(DWORD1(pwp->scratch_dma));
10404c06356bSdh142964 	msg[10] = LE_32(reqsz);
10414c06356bSdh142964 	msg[11] = 0;
10424c06356bSdh142964 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
10434c06356bSdh142964 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
10444c06356bSdh142964 	msg[14] = LE_32(rspsz);
10454c06356bSdh142964 	msg[15] = 0;
10464c06356bSdh142964 
10474c06356bSdh142964 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
10486745c559SJesse Butler 	/* SMP serialization */
10496745c559SJesse Butler 	pmcs_smp_acquire(pptr->iport);
10506745c559SJesse Butler 
10514c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
10524c06356bSdh142964 	htag = pwrk->htag;
10534c06356bSdh142964 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10544c06356bSdh142964 
10554c06356bSdh142964 	pmcs_unlock_phy(pptr);
105696c4a178SChris Horne 	WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result);
10574c06356bSdh142964 	pmcs_pwork(pwp, pwrk);
1058*601c90f1SSrikanth, Ramana 	/* Release SMP lock before reacquiring PHY lock */
10596745c559SJesse Butler 	pmcs_smp_release(pptr->iport);
1060*601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
10616745c559SJesse Butler 
10624c06356bSdh142964 	if (result) {
10634c06356bSdh142964 		pmcs_timed_out(pwp, htag, __func__);
10644c06356bSdh142964 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
1065c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
10664c06356bSdh142964 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
10674c06356bSdh142964 			    __func__, htag);
10684c06356bSdh142964 		} else {
1069c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
10704c06356bSdh142964 			    "%s: Issuing SMP ABORT for htag 0x%08x",
10714c06356bSdh142964 			    __func__, htag);
10724c06356bSdh142964 		}
10734c06356bSdh142964 		pmcs_unlock_phy(pptr);
10744c06356bSdh142964 		pmcs_release_scratch(pwp);
107596c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ETIMEDOUT;
10764c06356bSdh142964 		return (DDI_FAILURE);
10774c06356bSdh142964 	}
10784c06356bSdh142964 	status = LE_32(msg[2]);
10794c06356bSdh142964 	if (status == PMCOUT_STATUS_OVERFLOW) {
10804c06356bSdh142964 		status = PMCOUT_STATUS_OK;
108196c4a178SChris Horne 		smp_pkt->smp_pkt_reason = EOVERFLOW;
10824c06356bSdh142964 	}
10834c06356bSdh142964 	if (status != PMCOUT_STATUS_OK) {
10844c06356bSdh142964 		const char *emsg = pmcs_status_str(status);
10854c06356bSdh142964 		if (emsg == NULL) {
1086c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
10874c06356bSdh142964 			    "SMP operation failed (0x%x)", status);
10884c06356bSdh142964 		} else {
1089c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
10904c06356bSdh142964 			    "SMP operation failed (%s)", emsg);
10914c06356bSdh142964 		}
10924c06356bSdh142964 
10934c06356bSdh142964 		if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
10944c06356bSdh142964 		    (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
109596c4a178SChris Horne 			smp_pkt->smp_pkt_reason =
109696c4a178SChris Horne 			    will_retry ? EAGAIN : ETIMEDOUT;
10974c06356bSdh142964 			result = DDI_FAILURE;
10984c06356bSdh142964 		} else if (status ==
10994c06356bSdh142964 		    PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
11004c06356bSdh142964 			xp = pptr->target;
11014c06356bSdh142964 			if (xp == NULL) {
110296c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11034c06356bSdh142964 				result = DDI_FAILURE;
11044c06356bSdh142964 				goto out;
11054c06356bSdh142964 			}
11064c06356bSdh142964 			if (xp->dev_state !=
11074c06356bSdh142964 			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
11084c06356bSdh142964 				xp->dev_state =
11094c06356bSdh142964 				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
1110c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, xp,
11114c06356bSdh142964 				    "%s: Got _IT_NEXUS_LOSS SMP status. "
11124c06356bSdh142964 				    "Tgt(0x%p) dev_state set to "
11134c06356bSdh142964 				    "_NON_OPERATIONAL", __func__,
11144c06356bSdh142964 				    (void *)xp);
11154c06356bSdh142964 			}
11164c06356bSdh142964 			/* ABORT any pending commands related to this device */
11174c06356bSdh142964 			if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
11184c06356bSdh142964 				pptr->abort_pending = 1;
111996c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11204c06356bSdh142964 				result = DDI_FAILURE;
11214c06356bSdh142964 			}
11224c06356bSdh142964 		} else {
112396c4a178SChris Horne 			smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO;
11244c06356bSdh142964 			result = DDI_FAILURE;
11254c06356bSdh142964 		}
11264c06356bSdh142964 	} else {
112796c4a178SChris Horne 		(void) memcpy(smp_pkt->smp_pkt_rsp,
11284c06356bSdh142964 		    &((uint8_t *)pwp->scratch)[rdoff], rspsz);
112996c4a178SChris Horne 		if (smp_pkt->smp_pkt_reason == EOVERFLOW) {
11304c06356bSdh142964 			result = DDI_FAILURE;
11314c06356bSdh142964 		} else {
11324c06356bSdh142964 			result = DDI_SUCCESS;
11334c06356bSdh142964 		}
11344c06356bSdh142964 	}
11354c06356bSdh142964 out:
11364c06356bSdh142964 	pmcs_unlock_phy(pptr);
11374c06356bSdh142964 	pmcs_release_scratch(pwp);
11384c06356bSdh142964 	return (result);
11394c06356bSdh142964 }
11404c06356bSdh142964 
11414c06356bSdh142964 static int
11424c06356bSdh142964 pmcs_smp_init(dev_info_t *self, dev_info_t *child,
114396c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp_sd)
11444c06356bSdh142964 {
114596c4a178SChris Horne 	_NOTE(ARGUNUSED(tran, smp_sd));
11464c06356bSdh142964 	pmcs_iport_t *iport;
11474c06356bSdh142964 	pmcs_hw_t *pwp;
11484c06356bSdh142964 	pmcs_xscsi_t *tgt;
11494c06356bSdh142964 	pmcs_phy_t *phy, *pphy;
11504c06356bSdh142964 	uint64_t wwn;
11514c06356bSdh142964 	char *addr, *tgt_port;
11524c06356bSdh142964 	int ua_form = 1;
11534c06356bSdh142964 
11544c06356bSdh142964 	iport = ddi_get_soft_state(pmcs_iport_softstate,
11554c06356bSdh142964 	    ddi_get_instance(self));
11564c06356bSdh142964 	ASSERT(iport);
11574c06356bSdh142964 	if (iport == NULL)
11584c06356bSdh142964 		return (DDI_FAILURE);
11594c06356bSdh142964 	pwp = iport->pwp;
11604c06356bSdh142964 	ASSERT(pwp);
11614c06356bSdh142964 	if (pwp == NULL)
11624c06356bSdh142964 		return (DDI_FAILURE);
1163c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: %s", __func__,
11644c06356bSdh142964 	    ddi_get_name(child));
11654c06356bSdh142964 
11664c06356bSdh142964 	/* Get "target-port" prop from devinfo node */
11674c06356bSdh142964 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
11684c06356bSdh142964 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
11694c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1170c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1171c3bc407cSdh142964 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
11724c06356bSdh142964 		/* Dont fail _smp_init() because we couldnt get/set a prop */
11734c06356bSdh142964 		return (DDI_SUCCESS);
11744c06356bSdh142964 	}
11754c06356bSdh142964 
11764c06356bSdh142964 	/*
11774c06356bSdh142964 	 * Validate that this tran_tgt_init is for an active iport.
11784c06356bSdh142964 	 */
11794c06356bSdh142964 	if (iport->ua_state == UA_INACTIVE) {
1180c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1181c3bc407cSdh142964 		    "%s: Init on inactive iport for '%s'", __func__, tgt_port);
11824c06356bSdh142964 		ddi_prop_free(tgt_port);
11834c06356bSdh142964 		return (DDI_FAILURE);
11844c06356bSdh142964 	}
11854c06356bSdh142964 
11864c06356bSdh142964 	mutex_enter(&pwp->lock);
11874c06356bSdh142964 
11884c06356bSdh142964 	/* Retrieve softstate using unit-address */
11894c06356bSdh142964 	tgt = pmcs_get_target(iport, tgt_port);
11904c06356bSdh142964 	if (tgt == NULL) {
1191c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1192c3bc407cSdh142964 		    "%s: tgt softstate not found", __func__);
11934c06356bSdh142964 		ddi_prop_free(tgt_port);
11944c06356bSdh142964 		mutex_exit(&pwp->lock);
11954c06356bSdh142964 		return (DDI_FAILURE);
11964c06356bSdh142964 	}
11974c06356bSdh142964 
11984c06356bSdh142964 	phy = tgt->phy;
11994c06356bSdh142964 	ASSERT(mutex_owned(&phy->phy_lock));
12004c06356bSdh142964 
12014c06356bSdh142964 	if (IS_ROOT_PHY(phy)) {
12024c06356bSdh142964 		/* Expander attached to HBA - don't ref_count it */
12034c06356bSdh142964 		wwn = pwp->sas_wwns[0];
12044c06356bSdh142964 	} else {
12054c06356bSdh142964 		pmcs_inc_phy_ref_count(phy);
12064c06356bSdh142964 
12074c06356bSdh142964 		/*
12084c06356bSdh142964 		 * Parent (in topology) is also an expander
12094c06356bSdh142964 		 * Now that we've increased the ref count on phy, it's OK
12104c06356bSdh142964 		 * to drop the lock so we can acquire the parent's lock.
12114c06356bSdh142964 		 */
12124c06356bSdh142964 		pphy = phy->parent;
12134c06356bSdh142964 		pmcs_unlock_phy(phy);
12144c06356bSdh142964 		pmcs_lock_phy(pphy);
12154c06356bSdh142964 		wwn = pmcs_barray2wwn(pphy->sas_address);
12164c06356bSdh142964 		pmcs_unlock_phy(pphy);
12174c06356bSdh142964 		pmcs_lock_phy(phy);
12184c06356bSdh142964 	}
12194c06356bSdh142964 
12204c06356bSdh142964 	/*
12214c06356bSdh142964 	 * If this is the 1st smp_init, add this to our list.
12224c06356bSdh142964 	 */
12234c06356bSdh142964 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
12244c06356bSdh142964 		int target;
12254c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
12264c06356bSdh142964 			if (pwp->targets[target] != NULL) {
12274c06356bSdh142964 				continue;
12284c06356bSdh142964 			}
12294c06356bSdh142964 
12304c06356bSdh142964 			pwp->targets[target] = tgt;
12314c06356bSdh142964 			tgt->target_num = (uint16_t)target;
12324c06356bSdh142964 			tgt->assigned = 1;
12334c06356bSdh142964 			tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
12344c06356bSdh142964 			break;
12354c06356bSdh142964 		}
12364c06356bSdh142964 
12374c06356bSdh142964 		if (target == pwp->max_dev) {
1238c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
12394c06356bSdh142964 			    "Target list full.");
12404c06356bSdh142964 			goto smp_init_fail;
12414c06356bSdh142964 		}
12424c06356bSdh142964 	}
12434c06356bSdh142964 
12444c06356bSdh142964 	if (!pmcs_assign_device(pwp, tgt)) {
12454c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
1246c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
12474c06356bSdh142964 		    "%s: pmcs_assign_device failed for target 0x%p",
12484c06356bSdh142964 		    __func__, (void *)tgt);
12494c06356bSdh142964 		goto smp_init_fail;
12504c06356bSdh142964 	}
12514c06356bSdh142964 
1252499cfd15SDavid Hollister 	/*
1253499cfd15SDavid Hollister 	 * Update the attached port and target port pm properties
1254499cfd15SDavid Hollister 	 */
1255499cfd15SDavid Hollister 	tgt->smpd = smp_sd;
1256499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE);
1257499cfd15SDavid Hollister 
12584c06356bSdh142964 	pmcs_unlock_phy(phy);
12594c06356bSdh142964 	mutex_exit(&pwp->lock);
12604c06356bSdh142964 
12614c06356bSdh142964 	tgt->ref_count++;
12624c06356bSdh142964 	tgt->dtype = phy->dtype;
12634c06356bSdh142964 
12644c06356bSdh142964 	addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
1265499cfd15SDavid Hollister 	if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT,
1266499cfd15SDavid Hollister 	    addr) != DDI_SUCCESS) {
1267c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set "
1268c3bc407cSdh142964 		    "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
12694c06356bSdh142964 	}
12704c06356bSdh142964 	(void) scsi_free_wwnstr(addr);
12714c06356bSdh142964 	ddi_prop_free(tgt_port);
12724c06356bSdh142964 	return (DDI_SUCCESS);
12734c06356bSdh142964 
12744c06356bSdh142964 smp_init_fail:
12754c06356bSdh142964 	tgt->phy = NULL;
12764c06356bSdh142964 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
12774c06356bSdh142964 	phy->target = NULL;
12784c06356bSdh142964 	if (!IS_ROOT_PHY(phy)) {
12794c06356bSdh142964 		pmcs_dec_phy_ref_count(phy);
12804c06356bSdh142964 	}
12814c06356bSdh142964 	pmcs_unlock_phy(phy);
12824c06356bSdh142964 	mutex_exit(&pwp->lock);
12834c06356bSdh142964 	ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
12844c06356bSdh142964 	ddi_prop_free(tgt_port);
12854c06356bSdh142964 	return (DDI_FAILURE);
12864c06356bSdh142964 }
12874c06356bSdh142964 
12884c06356bSdh142964 static void
12894c06356bSdh142964 pmcs_smp_free(dev_info_t *self, dev_info_t *child,
129096c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp)
12914c06356bSdh142964 {
12924c06356bSdh142964 	_NOTE(ARGUNUSED(tran, smp));
12934c06356bSdh142964 	pmcs_iport_t *iport;
12944c06356bSdh142964 	pmcs_hw_t *pwp;
12954c06356bSdh142964 	pmcs_xscsi_t *tgt;
12964c06356bSdh142964 	char *tgt_port;
12974c06356bSdh142964 
12984c06356bSdh142964 	iport = ddi_get_soft_state(pmcs_iport_softstate,
12994c06356bSdh142964 	    ddi_get_instance(self));
13004c06356bSdh142964 	ASSERT(iport);
13014c06356bSdh142964 	if (iport == NULL)
13024c06356bSdh142964 		return;
13034c06356bSdh142964 
13044c06356bSdh142964 	pwp = iport->pwp;
13054c06356bSdh142964 	if (pwp == NULL)
13064c06356bSdh142964 		return;
13074c06356bSdh142964 	ASSERT(pwp);
1308c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: %s", __func__,
13094c06356bSdh142964 	    ddi_get_name(child));
13104c06356bSdh142964 
13114c06356bSdh142964 	/* Get "target-port" prop from devinfo node */
13124c06356bSdh142964 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
13134c06356bSdh142964 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
13144c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1315c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1316c3bc407cSdh142964 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
13174c06356bSdh142964 		return;
13184c06356bSdh142964 	}
13194c06356bSdh142964 	/* Retrieve softstate using unit-address */
13204c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
13214c06356bSdh142964 	ddi_prop_free(tgt_port);
13224c06356bSdh142964 
13234c06356bSdh142964 	if (tgt == NULL) {
1324c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1325c3bc407cSdh142964 		    "%s: tgt softstate not found", __func__);
13264c06356bSdh142964 		return;
13274c06356bSdh142964 	}
13284c06356bSdh142964 
13294c06356bSdh142964 	mutex_enter(&pwp->lock);
13304c06356bSdh142964 	mutex_enter(&tgt->statlock);
13314c06356bSdh142964 	if (tgt->phy) {
13324c06356bSdh142964 		if (!IS_ROOT_PHY(tgt->phy)) {
13334c06356bSdh142964 			pmcs_dec_phy_ref_count(tgt->phy);
13344c06356bSdh142964 		}
13354c06356bSdh142964 	}
13364c06356bSdh142964 
13374c06356bSdh142964 	if (--tgt->ref_count == 0) {
13384c06356bSdh142964 		/*
13394c06356bSdh142964 		 * Remove this target from our list. The softstate
13404c06356bSdh142964 		 * will remain, and the device will remain registered
13414c06356bSdh142964 		 * with the hardware unless/until we're told that the
13424c06356bSdh142964 		 * device physically went away.
13434c06356bSdh142964 		 */
1344c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
13454c06356bSdh142964 		    "Removing target 0x%p (vtgt %d) from target list",
13464c06356bSdh142964 		    (void *)tgt, tgt->target_num);
13474c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
13484c06356bSdh142964 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
13494c06356bSdh142964 		tgt->phy->target = NULL;
13504c06356bSdh142964 		tgt->phy = NULL;
13514c06356bSdh142964 	}
13524c06356bSdh142964 
13534c06356bSdh142964 	mutex_exit(&tgt->statlock);
13544c06356bSdh142964 	mutex_exit(&pwp->lock);
13554c06356bSdh142964 }
13564c06356bSdh142964 
13574c06356bSdh142964 static int
13584c06356bSdh142964 pmcs_scsi_quiesce(dev_info_t *dip)
13594c06356bSdh142964 {
13604c06356bSdh142964 	pmcs_hw_t *pwp;
13614c06356bSdh142964 	int totactive = -1;
13624c06356bSdh142964 	pmcs_xscsi_t *xp;
13634c06356bSdh142964 	uint16_t target;
13644c06356bSdh142964 
13654c06356bSdh142964 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
13664c06356bSdh142964 		return (0);		/* iport */
13674c06356bSdh142964 
13684c06356bSdh142964 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
13694c06356bSdh142964 	if (pwp == NULL) {
13704c06356bSdh142964 		return (-1);
13714c06356bSdh142964 	}
13724c06356bSdh142964 	mutex_enter(&pwp->lock);
13734c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
13744c06356bSdh142964 		mutex_exit(&pwp->lock);
13754c06356bSdh142964 		return (-1);
13764c06356bSdh142964 	}
13774c06356bSdh142964 
1378c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
13794c06356bSdh142964 	pwp->blocked = 1;
13804c06356bSdh142964 	while (totactive) {
13814c06356bSdh142964 		totactive = 0;
13824c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
13834c06356bSdh142964 			xp = pwp->targets[target];
13844c06356bSdh142964 			if (xp == NULL) {
13854c06356bSdh142964 				continue;
13864c06356bSdh142964 			}
13874c06356bSdh142964 			mutex_enter(&xp->statlock);
13884c06356bSdh142964 			if (xp->actv_cnt) {
13894c06356bSdh142964 				totactive += xp->actv_cnt;
13904c06356bSdh142964 				xp->draining = 1;
13914c06356bSdh142964 			}
13924c06356bSdh142964 			mutex_exit(&xp->statlock);
13934c06356bSdh142964 		}
13944c06356bSdh142964 		if (totactive) {
13954c06356bSdh142964 			cv_wait(&pwp->drain_cv, &pwp->lock);
13964c06356bSdh142964 		}
13974c06356bSdh142964 		/*
13984c06356bSdh142964 		 * The pwp->blocked may have been reset. e.g a SCSI bus reset
13994c06356bSdh142964 		 */
14004c06356bSdh142964 		pwp->blocked = 1;
14014c06356bSdh142964 	}
14024c06356bSdh142964 
14034c06356bSdh142964 	for (target = 0; target < pwp->max_dev; target++) {
14044c06356bSdh142964 		xp = pwp->targets[target];
14054c06356bSdh142964 		if (xp == NULL) {
14064c06356bSdh142964 			continue;
14074c06356bSdh142964 		}
14084c06356bSdh142964 		mutex_enter(&xp->statlock);
14094c06356bSdh142964 		xp->draining = 0;
14104c06356bSdh142964 		mutex_exit(&xp->statlock);
14114c06356bSdh142964 	}
14124c06356bSdh142964 
14134c06356bSdh142964 	mutex_exit(&pwp->lock);
14144c06356bSdh142964 	if (totactive == 0) {
1415c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1416c3bc407cSdh142964 		    "%s drain complete", __func__);
14174c06356bSdh142964 	}
14184c06356bSdh142964 	return (0);
14194c06356bSdh142964 }
14204c06356bSdh142964 
14214c06356bSdh142964 static int
14224c06356bSdh142964 pmcs_scsi_unquiesce(dev_info_t *dip)
14234c06356bSdh142964 {
14244c06356bSdh142964 	pmcs_hw_t *pwp;
14254c06356bSdh142964 
14264c06356bSdh142964 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
14274c06356bSdh142964 		return (0);		/* iport */
14284c06356bSdh142964 
14294c06356bSdh142964 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
14304c06356bSdh142964 	if (pwp == NULL) {
14314c06356bSdh142964 		return (-1);
14324c06356bSdh142964 	}
14334c06356bSdh142964 	mutex_enter(&pwp->lock);
14344c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
14354c06356bSdh142964 		mutex_exit(&pwp->lock);
14364c06356bSdh142964 		return (-1);
14374c06356bSdh142964 	}
1438c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
14394c06356bSdh142964 	pwp->blocked = 0;
14404c06356bSdh142964 	mutex_exit(&pwp->lock);
14414c06356bSdh142964 
14424c06356bSdh142964 	/*
14434c06356bSdh142964 	 * Run all pending commands.
14444c06356bSdh142964 	 */
14454c06356bSdh142964 	pmcs_scsa_wq_run(pwp);
14464c06356bSdh142964 
14474c06356bSdh142964 	/*
14484c06356bSdh142964 	 * Complete all completed commands.
14494c06356bSdh142964 	 * This also unlocks us.
14504c06356bSdh142964 	 */
14514c06356bSdh142964 	PMCS_CQ_RUN(pwp);
14524c06356bSdh142964 	return (0);
14534c06356bSdh142964 }
14544c06356bSdh142964 
14554c06356bSdh142964 /*
14564c06356bSdh142964  * Start commands for a particular device
14574c06356bSdh142964  * If the actual start of a command fails, return B_FALSE.  Any other result
14584c06356bSdh142964  * is a B_TRUE return.
14594c06356bSdh142964  */
14604c06356bSdh142964 boolean_t
14614c06356bSdh142964 pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
14624c06356bSdh142964 {
14634c06356bSdh142964 	pmcs_cmd_t *sp;
14644c06356bSdh142964 	pmcs_phy_t *phyp;
14654c06356bSdh142964 	pmcwork_t *pwrk;
14664c06356bSdh142964 	boolean_t run_one, blocked;
14674c06356bSdh142964 	int rval;
14684c06356bSdh142964 
14694c06356bSdh142964 	/*
14704c06356bSdh142964 	 * First, check to see if we're blocked or resource limited
14714c06356bSdh142964 	 */
14724c06356bSdh142964 	mutex_enter(&pwp->lock);
14734c06356bSdh142964 	blocked = pwp->blocked;
14744c06356bSdh142964 	/*
14754c06356bSdh142964 	 * If resource_limited is set, we're resource constrained and
14764c06356bSdh142964 	 * we will run only one work request for this target.
14774c06356bSdh142964 	 */
14784c06356bSdh142964 	run_one = pwp->resource_limited;
14794c06356bSdh142964 	mutex_exit(&pwp->lock);
14804c06356bSdh142964 
14814c06356bSdh142964 	if (blocked) {
14824c06356bSdh142964 		/* Queues will get restarted when we get unblocked */
14834c06356bSdh142964 		return (B_TRUE);
14844c06356bSdh142964 	}
14854c06356bSdh142964 
14864c06356bSdh142964 	/*
14874c06356bSdh142964 	 * Might as well verify the queue is not empty before moving on
14884c06356bSdh142964 	 */
14894c06356bSdh142964 	mutex_enter(&xp->wqlock);
14904c06356bSdh142964 	if (STAILQ_EMPTY(&xp->wq)) {
14914c06356bSdh142964 		mutex_exit(&xp->wqlock);
14924c06356bSdh142964 		return (B_TRUE);
14934c06356bSdh142964 	}
14944c06356bSdh142964 	mutex_exit(&xp->wqlock);
14954c06356bSdh142964 
14964c06356bSdh142964 	/*
14974c06356bSdh142964 	 * If we're draining or resetting, just reschedule work queue and bail.
14984c06356bSdh142964 	 */
14994c06356bSdh142964 	mutex_enter(&xp->statlock);
15004c06356bSdh142964 	if (xp->draining || xp->resetting || xp->special_running ||
15014c06356bSdh142964 	    xp->special_needed) {
15024c06356bSdh142964 		mutex_exit(&xp->statlock);
15034c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15044c06356bSdh142964 		return (B_TRUE);
15054c06356bSdh142964 	}
15064c06356bSdh142964 
15074c06356bSdh142964 	/*
1508b18a19c2SJesse Butler 	 * Next, check to see if the target is gone.
15094c06356bSdh142964 	 */
1510b18a19c2SJesse Butler 	if (xp->dev_gone) {
1511c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1512b18a19c2SJesse Butler 		    "%s: Flushing wait queue for dead tgt 0x%p", __func__,
15134c06356bSdh142964 		    (void *)xp);
15144c06356bSdh142964 		pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
15154c06356bSdh142964 		mutex_exit(&xp->statlock);
15164c06356bSdh142964 		return (B_TRUE);
15174c06356bSdh142964 	}
15184c06356bSdh142964 
15194c06356bSdh142964 	/*
15204c06356bSdh142964 	 * Increment the PHY's ref_count now so we know it won't go away
15214c06356bSdh142964 	 * after we drop the target lock.  Drop it before returning.  If the
15224c06356bSdh142964 	 * PHY dies, the commands we attempt to send will fail, but at least
15234c06356bSdh142964 	 * we know we have a real PHY pointer.
15244c06356bSdh142964 	 */
15254c06356bSdh142964 	phyp = xp->phy;
15264c06356bSdh142964 	pmcs_inc_phy_ref_count(phyp);
15274c06356bSdh142964 	mutex_exit(&xp->statlock);
15284c06356bSdh142964 
15294c06356bSdh142964 	mutex_enter(&xp->wqlock);
15304c06356bSdh142964 	while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
15314c06356bSdh142964 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
15324c06356bSdh142964 		if (pwrk == NULL) {
1533c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
15344c06356bSdh142964 			    "%s: out of work structures", __func__);
15354c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15364c06356bSdh142964 			break;
15374c06356bSdh142964 		}
15384c06356bSdh142964 		STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
15394c06356bSdh142964 		mutex_exit(&xp->wqlock);
15404c06356bSdh142964 
15414c06356bSdh142964 		pwrk->xp = xp;
15424c06356bSdh142964 		pwrk->arg = sp;
15434c06356bSdh142964 		sp->cmd_tag = pwrk->htag;
15444c06356bSdh142964 		pwrk->timer = US2WT(CMD2PKT(sp)->pkt_time * 1000000);
15454c06356bSdh142964 		if (pwrk->timer == 0) {
15464c06356bSdh142964 			pwrk->timer = US2WT(1000000);
15474c06356bSdh142964 		}
15484c06356bSdh142964 
15494c06356bSdh142964 		pwrk->dtype = xp->dtype;
15504c06356bSdh142964 
15514c06356bSdh142964 		if (xp->dtype == SAS) {
15524c06356bSdh142964 			pwrk->ptr = (void *) pmcs_SAS_done;
15534c06356bSdh142964 			if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
15544c06356bSdh142964 				sp->cmd_tag = NULL;
15554c06356bSdh142964 				pmcs_dec_phy_ref_count(phyp);
15564c06356bSdh142964 				pmcs_pwork(pwp, pwrk);
15574c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15584c06356bSdh142964 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
15594c06356bSdh142964 					return (B_FALSE);
15604c06356bSdh142964 				} else {
15614c06356bSdh142964 					return (B_TRUE);
15624c06356bSdh142964 				}
15634c06356bSdh142964 			}
15644c06356bSdh142964 		} else {
15654c06356bSdh142964 			ASSERT(xp->dtype == SATA);
15664c06356bSdh142964 			pwrk->ptr = (void *) pmcs_SATA_done;
15674c06356bSdh142964 			if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
15684c06356bSdh142964 				sp->cmd_tag = NULL;
15694c06356bSdh142964 				pmcs_dec_phy_ref_count(phyp);
15704c06356bSdh142964 				pmcs_pwork(pwp, pwrk);
15714c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15724c06356bSdh142964 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
15734c06356bSdh142964 					return (B_FALSE);
15744c06356bSdh142964 				} else {
15754c06356bSdh142964 					return (B_TRUE);
15764c06356bSdh142964 				}
15774c06356bSdh142964 			}
15784c06356bSdh142964 		}
15794c06356bSdh142964 
15804c06356bSdh142964 		if (run_one) {
15814c06356bSdh142964 			goto wq_out;
15824c06356bSdh142964 		}
15834c06356bSdh142964 		mutex_enter(&xp->wqlock);
15844c06356bSdh142964 	}
15854c06356bSdh142964 
15864c06356bSdh142964 	mutex_exit(&xp->wqlock);
15874c06356bSdh142964 
15884c06356bSdh142964 wq_out:
15894c06356bSdh142964 	pmcs_dec_phy_ref_count(phyp);
15904c06356bSdh142964 	return (B_TRUE);
15914c06356bSdh142964 }
15924c06356bSdh142964 
15934c06356bSdh142964 /*
15944c06356bSdh142964  * Start commands for all devices.
15954c06356bSdh142964  */
15964c06356bSdh142964 void
15974c06356bSdh142964 pmcs_scsa_wq_run(pmcs_hw_t *pwp)
15984c06356bSdh142964 {
15994c06356bSdh142964 	pmcs_xscsi_t *xp;
16004c06356bSdh142964 	uint16_t target_start, target;
16014c06356bSdh142964 	boolean_t	rval = B_TRUE;
16024c06356bSdh142964 
16034c06356bSdh142964 	mutex_enter(&pwp->lock);
16044c06356bSdh142964 	target_start = pwp->last_wq_dev;
16054c06356bSdh142964 	target = target_start;
16064c06356bSdh142964 
16074c06356bSdh142964 	do {
16084c06356bSdh142964 		xp = pwp->targets[target];
16094c06356bSdh142964 		if (xp == NULL) {
16104c06356bSdh142964 			if (++target == pwp->max_dev) {
16114c06356bSdh142964 				target = 0;
16124c06356bSdh142964 			}
16134c06356bSdh142964 			continue;
16144c06356bSdh142964 		}
16154c06356bSdh142964 
16164c06356bSdh142964 		mutex_exit(&pwp->lock);
16174c06356bSdh142964 		rval = pmcs_scsa_wq_run_one(pwp, xp);
16184c06356bSdh142964 		if (rval == B_FALSE) {
16194c06356bSdh142964 			mutex_enter(&pwp->lock);
16204c06356bSdh142964 			break;
16214c06356bSdh142964 		}
16224c06356bSdh142964 		mutex_enter(&pwp->lock);
16234c06356bSdh142964 		if (++target == pwp->max_dev) {
16244c06356bSdh142964 			target = 0;
16254c06356bSdh142964 		}
16264c06356bSdh142964 	} while (target != target_start);
16274c06356bSdh142964 
16284c06356bSdh142964 	if (rval) {
16294c06356bSdh142964 		pwp->resource_limited = 0; /* Not resource-constrained */
16304c06356bSdh142964 	} else {
16314c06356bSdh142964 		pwp->resource_limited = 1; /* Give others a chance */
16324c06356bSdh142964 	}
16334c06356bSdh142964 
16344c06356bSdh142964 	pwp->last_wq_dev = target;
16354c06356bSdh142964 	mutex_exit(&pwp->lock);
16364c06356bSdh142964 }
16374c06356bSdh142964 
16384c06356bSdh142964 /*
16394c06356bSdh142964  * Pull the completion queue, drop the lock and complete all elements.
16404c06356bSdh142964  */
16414c06356bSdh142964 
16424c06356bSdh142964 void
16434c06356bSdh142964 pmcs_scsa_cq_run(void *arg)
16444c06356bSdh142964 {
16454c06356bSdh142964 	pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
16464c06356bSdh142964 	pmcs_hw_t *pwp = cqti->cq_pwp;
16474c06356bSdh142964 	pmcs_cmd_t *sp, *nxt;
16484c06356bSdh142964 	struct scsi_pkt *pkt;
1649*601c90f1SSrikanth, Ramana 	pmcs_xscsi_t *tgt;
16504c06356bSdh142964 	pmcs_iocomp_cb_t *ioccb, *ioccb_next;
16514c06356bSdh142964 	pmcs_cb_t callback;
16524c06356bSdh142964 	uint32_t niodone;
16534c06356bSdh142964 
16544c06356bSdh142964 	DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
16554c06356bSdh142964 
16564c06356bSdh142964 	mutex_enter(&pwp->cq_lock);
16574c06356bSdh142964 
16584c06356bSdh142964 	while (!pwp->cq_info.cq_stop) {
16594c06356bSdh142964 		/*
16604c06356bSdh142964 		 * First, check the I/O completion callback queue.
16614c06356bSdh142964 		 */
16624c06356bSdh142964 
16634c06356bSdh142964 		ioccb = pwp->iocomp_cb_head;
16644c06356bSdh142964 		pwp->iocomp_cb_head = NULL;
16654c06356bSdh142964 		pwp->iocomp_cb_tail = NULL;
16664c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
16674c06356bSdh142964 
16684c06356bSdh142964 		niodone = 0;
16694c06356bSdh142964 
16704c06356bSdh142964 		while (ioccb) {
16714c06356bSdh142964 			niodone++;
16724c06356bSdh142964 			/*
16734c06356bSdh142964 			 * Grab the lock on the work structure. The callback
16744c06356bSdh142964 			 * routine is responsible for clearing it.
16754c06356bSdh142964 			 */
16764c06356bSdh142964 			mutex_enter(&ioccb->pwrk->lock);
16774c06356bSdh142964 			ioccb_next = ioccb->next;
16784c06356bSdh142964 			callback = (pmcs_cb_t)ioccb->pwrk->ptr;
16794c06356bSdh142964 			(*callback)(pwp, ioccb->pwrk,
16804c06356bSdh142964 			    (uint32_t *)((void *)ioccb->iomb));
16814c06356bSdh142964 			kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
16824c06356bSdh142964 			ioccb = ioccb_next;
16834c06356bSdh142964 		}
16844c06356bSdh142964 
16854c06356bSdh142964 		/*
16864c06356bSdh142964 		 * Next, run the completion queue
16874c06356bSdh142964 		 */
16884c06356bSdh142964 
16894c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
16904c06356bSdh142964 		sp = STAILQ_FIRST(&pwp->cq);
16914c06356bSdh142964 		STAILQ_INIT(&pwp->cq);
16924c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
16934c06356bSdh142964 
16944c06356bSdh142964 		DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
16954c06356bSdh142964 		    pmcs_cq_thr_info_t *, cqti);
16964c06356bSdh142964 
16974c06356bSdh142964 		if (sp && pmcs_check_acc_dma_handle(pwp)) {
16984c06356bSdh142964 			ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
16994c06356bSdh142964 		}
17004c06356bSdh142964 
17014c06356bSdh142964 		while (sp) {
17024c06356bSdh142964 			nxt = STAILQ_NEXT(sp, cmd_next);
17034c06356bSdh142964 			pkt = CMD2PKT(sp);
1704*601c90f1SSrikanth, Ramana 			tgt = sp->cmd_target;
1705*601c90f1SSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt,
17064c06356bSdh142964 			    "%s: calling completion on %p for tgt %p", __func__,
1707*601c90f1SSrikanth, Ramana 			    (void *)sp, (void *)tgt);
1708*601c90f1SSrikanth, Ramana 			if (tgt) {
1709*601c90f1SSrikanth, Ramana 				mutex_enter(&tgt->statlock);
1710*601c90f1SSrikanth, Ramana 				ASSERT(tgt->actv_pkts != 0);
1711*601c90f1SSrikanth, Ramana 				tgt->actv_pkts--;
1712*601c90f1SSrikanth, Ramana 				mutex_exit(&tgt->statlock);
1713*601c90f1SSrikanth, Ramana 			}
17144c06356bSdh142964 			scsi_hba_pkt_comp(pkt);
17154c06356bSdh142964 			sp = nxt;
17164c06356bSdh142964 		}
17174c06356bSdh142964 
17184c06356bSdh142964 		DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
17194c06356bSdh142964 		    pmcs_cq_thr_info_t *, cqti);
17204c06356bSdh142964 
17214c06356bSdh142964 		mutex_enter(&cqti->cq_thr_lock);
17224c06356bSdh142964 		cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
17234c06356bSdh142964 		mutex_exit(&cqti->cq_thr_lock);
17244c06356bSdh142964 
17254c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
17264c06356bSdh142964 	}
17274c06356bSdh142964 
17284c06356bSdh142964 	mutex_exit(&pwp->cq_lock);
17294c06356bSdh142964 	DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
17304c06356bSdh142964 	thread_exit();
17314c06356bSdh142964 }
17324c06356bSdh142964 
17334c06356bSdh142964 /*
17344c06356bSdh142964  * Run a SAS command.  Called with pwrk->lock held, returns unlocked.
17354c06356bSdh142964  */
17364c06356bSdh142964 static int
17374c06356bSdh142964 pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
17384c06356bSdh142964 {
17394c06356bSdh142964 	pmcs_hw_t *pwp = CMD2PMC(sp);
17404c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
17414c06356bSdh142964 	pmcs_xscsi_t *xp = pwrk->xp;
17424c06356bSdh142964 	uint32_t iq, *ptr;
17434c06356bSdh142964 	sas_ssp_cmd_iu_t sc;
17444c06356bSdh142964 
17454c06356bSdh142964 	mutex_enter(&xp->statlock);
1746b18a19c2SJesse Butler 	if (!xp->assigned) {
17474c06356bSdh142964 		mutex_exit(&xp->statlock);
17484c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
17494c06356bSdh142964 	}
17504c06356bSdh142964 	if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
17514c06356bSdh142964 		mutex_exit(&xp->statlock);
17524c06356bSdh142964 		mutex_enter(&xp->wqlock);
17534c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
17544c06356bSdh142964 		mutex_exit(&xp->wqlock);
17554c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
17564c06356bSdh142964 	}
17574c06356bSdh142964 	GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
17584c06356bSdh142964 	if (ptr == NULL) {
17594c06356bSdh142964 		mutex_exit(&xp->statlock);
17604c06356bSdh142964 		/*
17614c06356bSdh142964 		 * This is a temporary failure not likely to unblocked by
17624c06356bSdh142964 		 * commands completing as the test for scheduling the
17634c06356bSdh142964 		 * restart of work is a per-device test.
17644c06356bSdh142964 		 */
17654c06356bSdh142964 		mutex_enter(&xp->wqlock);
17664c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
17674c06356bSdh142964 		mutex_exit(&xp->wqlock);
1768c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
17694c06356bSdh142964 		    "%s: Failed to get IO IQ entry for tgt %d",
17704c06356bSdh142964 		    __func__, xp->target_num);
17714c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
17724c06356bSdh142964 
17734c06356bSdh142964 	}
17744c06356bSdh142964 
17754c06356bSdh142964 	ptr[0] =
17764c06356bSdh142964 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
17774c06356bSdh142964 	ptr[1] = LE_32(pwrk->htag);
17784c06356bSdh142964 	ptr[2] = LE_32(pwrk->phy->device_id);
17794c06356bSdh142964 	ptr[3] = LE_32(pkt->pkt_dma_len);
17804c06356bSdh142964 	if (ptr[3]) {
17814c06356bSdh142964 		ASSERT(pkt->pkt_numcookies);
17824c06356bSdh142964 		if (pkt->pkt_dma_flags & DDI_DMA_READ) {
17834c06356bSdh142964 			ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
17844c06356bSdh142964 		} else {
17854c06356bSdh142964 			ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
17864c06356bSdh142964 		}
17874c06356bSdh142964 		if (pmcs_dma_load(pwp, sp, ptr)) {
17884c06356bSdh142964 			mutex_exit(&pwp->iqp_lock[iq]);
17894c06356bSdh142964 			mutex_exit(&xp->statlock);
17904c06356bSdh142964 			mutex_enter(&xp->wqlock);
17914c06356bSdh142964 			if (STAILQ_EMPTY(&xp->wq)) {
17924c06356bSdh142964 				STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
17934c06356bSdh142964 				mutex_exit(&xp->wqlock);
17944c06356bSdh142964 			} else {
17954c06356bSdh142964 				mutex_exit(&xp->wqlock);
17964c06356bSdh142964 				CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
17974c06356bSdh142964 				CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
17984c06356bSdh142964 				CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
17994c06356bSdh142964 				    STATE_GOT_TARGET | STATE_SENT_CMD |
18004c06356bSdh142964 				    STATE_GOT_STATUS;
18014c06356bSdh142964 				mutex_enter(&pwp->cq_lock);
18024c06356bSdh142964 				STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
18034c06356bSdh142964 				mutex_exit(&pwp->cq_lock);
1804c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
18054c06356bSdh142964 				    "%s: Failed to dma_load for tgt %d (QF)",
18064c06356bSdh142964 				    __func__, xp->target_num);
18074c06356bSdh142964 			}
18084c06356bSdh142964 			return (PMCS_WQ_RUN_FAIL_RES);
18094c06356bSdh142964 		}
18104c06356bSdh142964 	} else {
18114c06356bSdh142964 		ptr[4] = LE_32(PMCIN_DATADIR_NONE);
18124c06356bSdh142964 		CLEAN_MESSAGE(ptr, 12);
18134c06356bSdh142964 	}
18144c06356bSdh142964 	xp->actv_cnt++;
18154c06356bSdh142964 	if (xp->actv_cnt > xp->maxdepth) {
18164c06356bSdh142964 		xp->maxdepth = xp->actv_cnt;
1817c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth "
1818c3bc407cSdh142964 		    "now %u", pwrk->phy->path, xp->maxdepth);
18194c06356bSdh142964 	}
18204c06356bSdh142964 	mutex_exit(&xp->statlock);
18214c06356bSdh142964 
18224c06356bSdh142964 
18234c06356bSdh142964 #ifdef	DEBUG
18244c06356bSdh142964 	/*
18254c06356bSdh142964 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
18264c06356bSdh142964 	 * event when this goes out on the wire.
18274c06356bSdh142964 	 */
18284c06356bSdh142964 	ptr[4] |= PMCIN_MESSAGE_REPORT;
18294c06356bSdh142964 #endif
18304c06356bSdh142964 	/*
18314c06356bSdh142964 	 * Fill in the SSP IU
18324c06356bSdh142964 	 */
18334c06356bSdh142964 
18344c06356bSdh142964 	bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
18354c06356bSdh142964 	bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
18364c06356bSdh142964 
18374c06356bSdh142964 	switch (pkt->pkt_flags & FLAG_TAGMASK) {
18384c06356bSdh142964 	case FLAG_HTAG:
18394c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
18404c06356bSdh142964 		break;
18414c06356bSdh142964 	case FLAG_OTAG:
18424c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
18434c06356bSdh142964 		break;
18444c06356bSdh142964 	case FLAG_STAG:
18454c06356bSdh142964 	default:
18464c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
18474c06356bSdh142964 		break;
18484c06356bSdh142964 	}
18494c06356bSdh142964 	(void) memcpy(sc.cdb, pkt->pkt_cdbp,
18504c06356bSdh142964 	    min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
18514c06356bSdh142964 	(void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
18524c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
18534c06356bSdh142964 	mutex_exit(&pwrk->lock);
1854c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
18554c06356bSdh142964 	    "%s: giving pkt %p (tag %x) to the hardware", __func__,
18564c06356bSdh142964 	    (void *)pkt, pwrk->htag);
18574c06356bSdh142964 #ifdef DEBUG
18584c06356bSdh142964 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
18594c06356bSdh142964 #endif
18604c06356bSdh142964 	mutex_enter(&xp->aqlock);
18614c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
18624c06356bSdh142964 	mutex_exit(&xp->aqlock);
18634c06356bSdh142964 	INC_IQ_ENTRY(pwp, iq);
18644c06356bSdh142964 
18654c06356bSdh142964 	/*
18664c06356bSdh142964 	 * If we just submitted the last command queued from device state
18674c06356bSdh142964 	 * recovery, clear the wq_recovery_tail pointer.
18684c06356bSdh142964 	 */
18694c06356bSdh142964 	mutex_enter(&xp->wqlock);
18704c06356bSdh142964 	if (xp->wq_recovery_tail == sp) {
18714c06356bSdh142964 		xp->wq_recovery_tail = NULL;
18724c06356bSdh142964 	}
18734c06356bSdh142964 	mutex_exit(&xp->wqlock);
18744c06356bSdh142964 
18754c06356bSdh142964 	return (PMCS_WQ_RUN_SUCCESS);
18764c06356bSdh142964 }
18774c06356bSdh142964 
18784c06356bSdh142964 /*
18794c06356bSdh142964  * Complete a SAS command
18804c06356bSdh142964  *
18814c06356bSdh142964  * Called with pwrk lock held.
18824c06356bSdh142964  * The free of pwrk releases the lock.
18834c06356bSdh142964  */
18844c06356bSdh142964 
18854c06356bSdh142964 static void
18864c06356bSdh142964 pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
18874c06356bSdh142964 {
18884c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
18894c06356bSdh142964 	pmcs_phy_t *pptr = pwrk->phy;
18904c06356bSdh142964 	pmcs_xscsi_t *xp = pwrk->xp;
18914c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
18924c06356bSdh142964 	int dead;
18934c06356bSdh142964 	uint32_t sts;
18944c06356bSdh142964 	boolean_t aborted = B_FALSE;
18954c06356bSdh142964 	boolean_t do_ds_recovery = B_FALSE;
18964c06356bSdh142964 
18974c06356bSdh142964 	ASSERT(xp != NULL);
18984c06356bSdh142964 	ASSERT(sp != NULL);
18994c06356bSdh142964 	ASSERT(pptr != NULL);
19004c06356bSdh142964 
19014c06356bSdh142964 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
19024c06356bSdh142964 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
19034c06356bSdh142964 	    hrtime_t, gethrtime());
19044c06356bSdh142964 
19054c06356bSdh142964 	dead = pwrk->dead;
19064c06356bSdh142964 
19074c06356bSdh142964 	if (msg) {
19084c06356bSdh142964 		sts = LE_32(msg[2]);
19094c06356bSdh142964 	} else {
19104c06356bSdh142964 		sts = 0;
19114c06356bSdh142964 	}
19124c06356bSdh142964 
19134c06356bSdh142964 	if (dead != 0) {
1914c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
1915c3bc407cSdh142964 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
19164c06356bSdh142964 		goto out;
19174c06356bSdh142964 	}
19184c06356bSdh142964 
19194c06356bSdh142964 	if (sts == PMCOUT_STATUS_ABORTED) {
19204c06356bSdh142964 		aborted = B_TRUE;
19214c06356bSdh142964 	}
19224c06356bSdh142964 
19234c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
1924c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
19254c06356bSdh142964 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
19264c06356bSdh142964 		    __func__, (void *)sp, pwrk->htag, pptr->path);
1927*601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
1928*601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
1929*601c90f1SSrikanth, Ramana 		    STATE_SENT_CMD;
1930*601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
19314c06356bSdh142964 		goto out;
19324c06356bSdh142964 	}
19334c06356bSdh142964 
19344c06356bSdh142964 	/*
19354c06356bSdh142964 	 * If the status isn't okay but not underflow,
19364c06356bSdh142964 	 * step to the side and parse the (possible) error.
19374c06356bSdh142964 	 */
19384c06356bSdh142964 #ifdef DEBUG
19394c06356bSdh142964 	if (msg) {
19404c06356bSdh142964 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
19414c06356bSdh142964 	}
19424c06356bSdh142964 #endif
19434c06356bSdh142964 	if (!msg) {
19444c06356bSdh142964 		goto out;
19454c06356bSdh142964 	}
19464c06356bSdh142964 
19474c06356bSdh142964 	switch (sts) {
19484c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
19494c06356bSdh142964 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
19504c06356bSdh142964 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
1951c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
1952*601c90f1SSrikanth, Ramana 		    "%s: PHY %s requires DS recovery (status=%d)",
19534c06356bSdh142964 		    __func__, pptr->path, sts);
19544c06356bSdh142964 		do_ds_recovery = B_TRUE;
19554c06356bSdh142964 		break;
19564c06356bSdh142964 	case PMCOUT_STATUS_UNDERFLOW:
19574c06356bSdh142964 		(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
1958c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL,
19594c06356bSdh142964 		    "%s: underflow %u for cdb 0x%x",
19604c06356bSdh142964 		    __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
19614c06356bSdh142964 		sts = PMCOUT_STATUS_OK;
19624c06356bSdh142964 		msg[3] = 0;
19634c06356bSdh142964 		break;
19644c06356bSdh142964 	case PMCOUT_STATUS_OK:
19654c06356bSdh142964 		pkt->pkt_resid = 0;
19664c06356bSdh142964 		break;
19674c06356bSdh142964 	}
19684c06356bSdh142964 
19694c06356bSdh142964 	if (sts != PMCOUT_STATUS_OK) {
19704c06356bSdh142964 		pmcs_ioerror(pwp, SAS, pwrk, msg);
19714c06356bSdh142964 	} else {
19724c06356bSdh142964 		if (msg[3]) {
19734c06356bSdh142964 			uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
19744c06356bSdh142964 			sas_ssp_rsp_iu_t *rptr = (void *)local;
19754c06356bSdh142964 			const int lim =
19764c06356bSdh142964 			    (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
19774c06356bSdh142964 			static const uint8_t ssp_rsp_evec[] = {
19784c06356bSdh142964 				0x58, 0x61, 0x56, 0x72, 0x00
19794c06356bSdh142964 			};
19804c06356bSdh142964 
19814c06356bSdh142964 			/*
19824c06356bSdh142964 			 * Transform the the first part of the response
19834c06356bSdh142964 			 * to host canonical form. This gives us enough
19844c06356bSdh142964 			 * information to figure out what to do with the
19854c06356bSdh142964 			 * rest (which remains unchanged in the incoming
19864c06356bSdh142964 			 * message which can be up to two queue entries
19874c06356bSdh142964 			 * in length).
19884c06356bSdh142964 			 */
19894c06356bSdh142964 			pmcs_endian_transform(pwp, local, &msg[5],
19904c06356bSdh142964 			    ssp_rsp_evec);
19914c06356bSdh142964 			xd = (uint8_t *)(&msg[5]);
19924c06356bSdh142964 			xd += SAS_RSP_HDR_SIZE;
19934c06356bSdh142964 
19944c06356bSdh142964 			if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
19954c06356bSdh142964 				if (rptr->response_data_length != 4) {
19964c06356bSdh142964 					pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
19974c06356bSdh142964 					    "Bad SAS RESPONSE DATA LENGTH",
19984c06356bSdh142964 					    msg);
19994c06356bSdh142964 					pkt->pkt_reason = CMD_TRAN_ERR;
20004c06356bSdh142964 					goto out;
20014c06356bSdh142964 				}
20024c06356bSdh142964 				(void) memcpy(&sts, xd, sizeof (uint32_t));
20034c06356bSdh142964 				sts = BE_32(sts);
20044c06356bSdh142964 				/*
20054c06356bSdh142964 				 * The only response code we should legally get
20064c06356bSdh142964 				 * here is an INVALID FRAME response code.
20074c06356bSdh142964 				 */
20084c06356bSdh142964 				if (sts == SAS_RSP_INVALID_FRAME) {
2009c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20104c06356bSdh142964 					    "%s: pkt %p tgt %u path %s "
20114c06356bSdh142964 					    "completed: INVALID FRAME response",
20124c06356bSdh142964 					    __func__, (void *)pkt,
20134c06356bSdh142964 					    xp->target_num, pptr->path);
20144c06356bSdh142964 				} else {
2015c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20164c06356bSdh142964 					    "%s: pkt %p tgt %u path %s "
20174c06356bSdh142964 					    "completed: illegal response 0x%x",
20184c06356bSdh142964 					    __func__, (void *)pkt,
20194c06356bSdh142964 					    xp->target_num, pptr->path, sts);
20204c06356bSdh142964 				}
20214c06356bSdh142964 				pkt->pkt_reason = CMD_TRAN_ERR;
20224c06356bSdh142964 				goto out;
20234c06356bSdh142964 			}
20244c06356bSdh142964 			if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
20254c06356bSdh142964 				uint32_t slen;
20264c06356bSdh142964 				slen = rptr->sense_data_length;
20274c06356bSdh142964 				if (slen > lim) {
20284c06356bSdh142964 					slen = lim;
20294c06356bSdh142964 				}
20304c06356bSdh142964 				pmcs_latch_status(pwp, sp, rptr->status, xd,
20314c06356bSdh142964 				    slen, pptr->path);
20324c06356bSdh142964 			} else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
20334b456463SDavid Hollister 				pmcout_ssp_comp_t *sspcp;
20344b456463SDavid Hollister 				sspcp = (pmcout_ssp_comp_t *)msg;
20354b456463SDavid Hollister 				uint32_t *residp;
20364c06356bSdh142964 				/*
20374c06356bSdh142964 				 * This is the case for a plain SCSI status.
20384b456463SDavid Hollister 				 * Note: If RESC_V is set and we're here, there
20394b456463SDavid Hollister 				 * is a residual.  We need to find it and update
20404b456463SDavid Hollister 				 * the packet accordingly.
20414c06356bSdh142964 				 */
20424c06356bSdh142964 				pmcs_latch_status(pwp, sp, rptr->status, NULL,
20434c06356bSdh142964 				    0, pptr->path);
20444b456463SDavid Hollister 
20454b456463SDavid Hollister 				if (sspcp->resc_v) {
20464b456463SDavid Hollister 					/*
20474b456463SDavid Hollister 					 * Point residual to the SSP_RESP_IU
20484b456463SDavid Hollister 					 */
20494b456463SDavid Hollister 					residp = (uint32_t *)(sspcp + 1);
20504b456463SDavid Hollister 					/*
20514b456463SDavid Hollister 					 * param contains the number of bytes
20524b456463SDavid Hollister 					 * between where the SSP_RESP_IU may
20534b456463SDavid Hollister 					 * or may not be and the residual.
20544b456463SDavid Hollister 					 * Increment residp by the appropriate
20554b456463SDavid Hollister 					 * number of words: (param+resc_pad)/4).
20564b456463SDavid Hollister 					 */
20574b456463SDavid Hollister 					residp += (LE_32(sspcp->param) +
20584b456463SDavid Hollister 					    sspcp->resc_pad) /
20594b456463SDavid Hollister 					    sizeof (uint32_t);
20604b456463SDavid Hollister 					pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
20614b456463SDavid Hollister 					    pptr, xp, "%s: tgt 0x%p "
20624b456463SDavid Hollister 					    "residual %d for pkt 0x%p",
20634b456463SDavid Hollister 					    __func__, (void *) xp, *residp,
20644b456463SDavid Hollister 					    (void *) pkt);
20654b456463SDavid Hollister 					ASSERT(LE_32(*residp) <=
20664b456463SDavid Hollister 					    pkt->pkt_dma_len);
20674b456463SDavid Hollister 					(void) pmcs_set_resid(pkt,
20684b456463SDavid Hollister 					    pkt->pkt_dma_len, LE_32(*residp));
20694b456463SDavid Hollister 				}
20704c06356bSdh142964 			} else {
20714c06356bSdh142964 				pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
20724c06356bSdh142964 				    "illegal SAS response", msg);
20734c06356bSdh142964 				pkt->pkt_reason = CMD_TRAN_ERR;
20744c06356bSdh142964 				goto out;
20754c06356bSdh142964 			}
20764c06356bSdh142964 		} else {
20774c06356bSdh142964 			pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
20784c06356bSdh142964 			    pptr->path);
20794c06356bSdh142964 		}
20804c06356bSdh142964 		if (pkt->pkt_dma_len) {
20814c06356bSdh142964 			pkt->pkt_state |= STATE_XFERRED_DATA;
20824c06356bSdh142964 		}
20834c06356bSdh142964 	}
2084c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
20854c06356bSdh142964 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
20864c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
20874c06356bSdh142964 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
20884c06356bSdh142964 
20894c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2090c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20914c06356bSdh142964 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
20924c06356bSdh142964 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
20934c06356bSdh142964 		aborted = B_TRUE;
20944c06356bSdh142964 	}
20954c06356bSdh142964 
20964c06356bSdh142964 out:
20974c06356bSdh142964 	pmcs_pwork(pwp, pwrk);
20984c06356bSdh142964 	pmcs_dma_unload(pwp, sp);
20994c06356bSdh142964 
21004c06356bSdh142964 	mutex_enter(&xp->statlock);
21014c06356bSdh142964 	if (xp->dev_gone) {
21024c06356bSdh142964 		mutex_exit(&xp->statlock);
2103c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
21044c06356bSdh142964 		    "%s: Completing command for dead target 0x%p", __func__,
21054c06356bSdh142964 		    (void *)xp);
21064c06356bSdh142964 		return;
21074c06356bSdh142964 	}
21084c06356bSdh142964 
21094c06356bSdh142964 	ASSERT(xp->actv_cnt > 0);
21104c06356bSdh142964 	if (--(xp->actv_cnt) == 0) {
21114c06356bSdh142964 		if (xp->draining) {
2112c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
21134c06356bSdh142964 			    "%s: waking up drain waiters", __func__);
21144c06356bSdh142964 			cv_signal(&pwp->drain_cv);
21154c06356bSdh142964 		}
21164c06356bSdh142964 	}
21174c06356bSdh142964 	mutex_exit(&xp->statlock);
21184c06356bSdh142964 	if (dead == 0) {
21194c06356bSdh142964 #ifdef	DEBUG
21204c06356bSdh142964 		pmcs_cmd_t *wp;
21214c06356bSdh142964 		mutex_enter(&xp->aqlock);
21224c06356bSdh142964 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
21234c06356bSdh142964 			if (wp == sp) {
21244c06356bSdh142964 				break;
21254c06356bSdh142964 			}
21264c06356bSdh142964 		}
21274c06356bSdh142964 		ASSERT(wp != NULL);
21284c06356bSdh142964 #else
21294c06356bSdh142964 		mutex_enter(&xp->aqlock);
21304c06356bSdh142964 #endif
21314c06356bSdh142964 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
21324c06356bSdh142964 		if (aborted) {
2133c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21344c06356bSdh142964 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
21354c06356bSdh142964 			    __func__, (void *)xp);
21364c06356bSdh142964 			cv_signal(&xp->abort_cv);
21374c06356bSdh142964 		}
21384c06356bSdh142964 		mutex_exit(&xp->aqlock);
21394c06356bSdh142964 	}
21404c06356bSdh142964 
21414c06356bSdh142964 	/*
21424c06356bSdh142964 	 * If do_ds_recovery is set, we need to initiate device state
21434c06356bSdh142964 	 * recovery.  In this case, we put this I/O back on the head of
21444c06356bSdh142964 	 * the wait queue to run again after recovery is complete
21454c06356bSdh142964 	 */
21464c06356bSdh142964 	if (do_ds_recovery) {
21474c06356bSdh142964 		mutex_enter(&xp->statlock);
21484c06356bSdh142964 		pmcs_start_dev_state_recovery(xp, pptr);
21494c06356bSdh142964 		mutex_exit(&xp->statlock);
2150c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p "
2151c3bc407cSdh142964 		    "back on wq during recovery for tgt 0x%p", __func__,
2152c3bc407cSdh142964 		    (void *)sp, (void *)xp);
21534c06356bSdh142964 		mutex_enter(&xp->wqlock);
21544c06356bSdh142964 		if (xp->wq_recovery_tail == NULL) {
21554c06356bSdh142964 			STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
21564c06356bSdh142964 		} else {
21574c06356bSdh142964 			/*
21584c06356bSdh142964 			 * If there are other I/Os waiting at the head due to
21594c06356bSdh142964 			 * device state recovery, add this one in the right spot
21604c06356bSdh142964 			 * to maintain proper order.
21614c06356bSdh142964 			 */
21624c06356bSdh142964 			STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
21634c06356bSdh142964 			    cmd_next);
21644c06356bSdh142964 		}
21654c06356bSdh142964 		xp->wq_recovery_tail = sp;
21664c06356bSdh142964 		mutex_exit(&xp->wqlock);
21674c06356bSdh142964 	} else {
21684c06356bSdh142964 		/*
21694c06356bSdh142964 		 * If we're not initiating device state recovery and this
21704c06356bSdh142964 		 * command was not "dead", put it on the completion queue
21714c06356bSdh142964 		 */
21724c06356bSdh142964 		if (!dead) {
21734c06356bSdh142964 			mutex_enter(&pwp->cq_lock);
21744c06356bSdh142964 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
21754c06356bSdh142964 			mutex_exit(&pwp->cq_lock);
21764c06356bSdh142964 		}
21774c06356bSdh142964 	}
21784c06356bSdh142964 }
21794c06356bSdh142964 
21804c06356bSdh142964 /*
21814c06356bSdh142964  * Run a SATA command (normal reads and writes),
21824c06356bSdh142964  * or block and schedule a SATL interpretation
21834c06356bSdh142964  * of the command.
21844c06356bSdh142964  *
21854c06356bSdh142964  * Called with pwrk lock held, returns unlocked.
21864c06356bSdh142964  */
21874c06356bSdh142964 
21884c06356bSdh142964 static int
21894c06356bSdh142964 pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
21904c06356bSdh142964 {
21914c06356bSdh142964 	pmcs_hw_t *pwp = CMD2PMC(sp);
21924c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
21934c06356bSdh142964 	pmcs_xscsi_t *xp;
21944c06356bSdh142964 	uint8_t cdb_base, asc, tag;
21954c06356bSdh142964 	uint32_t *ptr, iq, nblk, i, mtype;
21964c06356bSdh142964 	fis_t fis;
21974c06356bSdh142964 	size_t amt;
21984c06356bSdh142964 	uint64_t lba;
21994c06356bSdh142964 
22004c06356bSdh142964 	xp = pwrk->xp;
22014c06356bSdh142964 
22024c06356bSdh142964 	/*
22034c06356bSdh142964 	 * First, see if this is just a plain read/write command.
22044c06356bSdh142964 	 * If not, we have to queue it up for processing, block
22054c06356bSdh142964 	 * any additional commands from coming in, and wake up
22064c06356bSdh142964 	 * the thread that will process this command.
22074c06356bSdh142964 	 */
22084c06356bSdh142964 	cdb_base = pkt->pkt_cdbp[0] & 0x1f;
22094c06356bSdh142964 	if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
2210c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
2211c3bc407cSdh142964 		    "%s: special SATA cmd %p", __func__, (void *)sp);
22124c06356bSdh142964 
22134c06356bSdh142964 		ASSERT(xp->phy != NULL);
22144c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
22154c06356bSdh142964 		pmcs_lock_phy(xp->phy);
22164c06356bSdh142964 		mutex_enter(&xp->statlock);
22174c06356bSdh142964 		xp->special_needed = 1; /* Set the special_needed flag */
22184c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
22194c06356bSdh142964 		if (pmcs_run_sata_special(pwp, xp)) {
22204c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
22214c06356bSdh142964 		}
22224c06356bSdh142964 		mutex_exit(&xp->statlock);
22234c06356bSdh142964 		pmcs_unlock_phy(xp->phy);
22244c06356bSdh142964 
22254c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
22264c06356bSdh142964 	}
22274c06356bSdh142964 
2228c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__);
22294c06356bSdh142964 
22304c06356bSdh142964 	mutex_enter(&xp->statlock);
2231b18a19c2SJesse Butler 	if (!xp->assigned) {
22324c06356bSdh142964 		mutex_exit(&xp->statlock);
22334c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
22344c06356bSdh142964 	}
22354c06356bSdh142964 	if (xp->special_running || xp->special_needed || xp->recover_wait) {
22364c06356bSdh142964 		mutex_exit(&xp->statlock);
22374c06356bSdh142964 		mutex_enter(&xp->wqlock);
22384c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
22394c06356bSdh142964 		mutex_exit(&xp->wqlock);
22404c06356bSdh142964 		/*
22414c06356bSdh142964 		 * By the time we get here the special
22424c06356bSdh142964 		 * commands running or waiting to be run
22434c06356bSdh142964 		 * may have come and gone, so kick our
22444c06356bSdh142964 		 * worker to run the waiting queues
22454c06356bSdh142964 		 * just in case.
22464c06356bSdh142964 		 */
22474c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
22484c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
22494c06356bSdh142964 	}
22504c06356bSdh142964 	lba = xp->capacity;
22514c06356bSdh142964 	mutex_exit(&xp->statlock);
22524c06356bSdh142964 
22534c06356bSdh142964 	/*
22544c06356bSdh142964 	 * Extract data length and lba parameters out of the command. The
22554c06356bSdh142964 	 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
22564c06356bSdh142964 	 * values are considered illegal.
22574c06356bSdh142964 	 */
22584c06356bSdh142964 	asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
22594c06356bSdh142964 	if (asc) {
22604c06356bSdh142964 		uint8_t sns[18];
22614c06356bSdh142964 		bzero(sns, sizeof (sns));
22624c06356bSdh142964 		sns[0] = 0xf0;
22634c06356bSdh142964 		sns[2] = 0x5;
22644c06356bSdh142964 		sns[12] = asc;
22654c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
22664c06356bSdh142964 		    pwrk->phy->path);
22674c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
22684c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
22694c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
22704c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
22714c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
22724c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
22734c06356bSdh142964 	}
22744c06356bSdh142964 
22754c06356bSdh142964 	/*
22764c06356bSdh142964 	 * If the command decodes as not moving any data, complete it here.
22774c06356bSdh142964 	 */
22784c06356bSdh142964 	amt = nblk;
22794c06356bSdh142964 	amt <<= 9;
22804c06356bSdh142964 	amt = pmcs_set_resid(pkt, amt, nblk << 9);
22814c06356bSdh142964 	if (amt == 0) {
22824c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
22834c06356bSdh142964 		    pwrk->phy->path);
22844c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
22854c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
22864c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
22874c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
22884c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
22894c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
22904c06356bSdh142964 	}
22914c06356bSdh142964 
22924c06356bSdh142964 	/*
22934c06356bSdh142964 	 * Get an inbound queue entry for this I/O
22944c06356bSdh142964 	 */
22954c06356bSdh142964 	GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
22964c06356bSdh142964 	if (ptr == NULL) {
22974c06356bSdh142964 		/*
22984c06356bSdh142964 		 * This is a temporary failure not likely to unblocked by
22994c06356bSdh142964 		 * commands completing as the test for scheduling the
23004c06356bSdh142964 		 * restart of work is a per-device test.
23014c06356bSdh142964 		 */
23024c06356bSdh142964 		mutex_enter(&xp->wqlock);
23034c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23044c06356bSdh142964 		mutex_exit(&xp->wqlock);
23054c06356bSdh142964 		pmcs_dma_unload(pwp, sp);
23064c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
2307c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
23084c06356bSdh142964 		    "%s: Failed to get IO IQ entry for tgt %d",
23094c06356bSdh142964 		    __func__, xp->target_num);
23104c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
23114c06356bSdh142964 	}
23124c06356bSdh142964 
23134c06356bSdh142964 	/*
23144c06356bSdh142964 	 * Get a tag.  At this point, hold statlock until the tagmap is
23154c06356bSdh142964 	 * updated (just prior to sending the cmd to the hardware).
23164c06356bSdh142964 	 */
23174c06356bSdh142964 	mutex_enter(&xp->statlock);
23184c06356bSdh142964 	for (tag = 0; tag < xp->qdepth; tag++) {
23194c06356bSdh142964 		if ((xp->tagmap & (1 << tag)) == 0) {
23204c06356bSdh142964 			break;
23214c06356bSdh142964 		}
23224c06356bSdh142964 	}
23234c06356bSdh142964 
23244c06356bSdh142964 	if (tag == xp->qdepth) {
23254c06356bSdh142964 		mutex_exit(&xp->statlock);
23264c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[iq]);
23274c06356bSdh142964 		mutex_enter(&xp->wqlock);
23284c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23294c06356bSdh142964 		mutex_exit(&xp->wqlock);
23304c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
23314c06356bSdh142964 	}
23324c06356bSdh142964 
23334c06356bSdh142964 	sp->cmd_satltag = (uint8_t)tag;
23344c06356bSdh142964 
23354c06356bSdh142964 	/*
23364c06356bSdh142964 	 * Set up the command
23374c06356bSdh142964 	 */
23384c06356bSdh142964 	bzero(fis, sizeof (fis));
23394c06356bSdh142964 	ptr[0] =
23404c06356bSdh142964 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
23414c06356bSdh142964 	ptr[1] = LE_32(pwrk->htag);
23424c06356bSdh142964 	ptr[2] = LE_32(pwrk->phy->device_id);
23434c06356bSdh142964 	ptr[3] = LE_32(amt);
23444c06356bSdh142964 
23454c06356bSdh142964 	if (xp->ncq) {
23464c06356bSdh142964 		mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
23474c06356bSdh142964 		fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
23484c06356bSdh142964 		if (cdb_base == SCMD_READ) {
23494c06356bSdh142964 			fis[0] |= (READ_FPDMA_QUEUED << 16);
23504c06356bSdh142964 		} else {
23514c06356bSdh142964 			fis[0] |= (WRITE_FPDMA_QUEUED << 16);
23524c06356bSdh142964 		}
23534c06356bSdh142964 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
23544c06356bSdh142964 		fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
23554c06356bSdh142964 		fis[3] = tag << 3;
23564c06356bSdh142964 	} else {
23574c06356bSdh142964 		int op;
23584c06356bSdh142964 		fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
23594c06356bSdh142964 		if (xp->pio) {
23604c06356bSdh142964 			mtype = SATA_PROTOCOL_PIO;
23614c06356bSdh142964 			if (cdb_base == SCMD_READ) {
23624c06356bSdh142964 				op = READ_SECTORS_EXT;
23634c06356bSdh142964 			} else {
23644c06356bSdh142964 				op = WRITE_SECTORS_EXT;
23654c06356bSdh142964 			}
23664c06356bSdh142964 		} else {
23674c06356bSdh142964 			mtype = SATA_PROTOCOL_DMA;
23684c06356bSdh142964 			if (cdb_base == SCMD_READ) {
23694c06356bSdh142964 				op = READ_DMA_EXT;
23704c06356bSdh142964 			} else {
23714c06356bSdh142964 				op = WRITE_DMA_EXT;
23724c06356bSdh142964 			}
23734c06356bSdh142964 		}
23744c06356bSdh142964 		fis[0] |= (op << 16);
23754c06356bSdh142964 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
23764c06356bSdh142964 		fis[2] = (lba >> 24) & 0xffffff;
23774c06356bSdh142964 		fis[3] = nblk;
23784c06356bSdh142964 	}
23794c06356bSdh142964 
23804c06356bSdh142964 	if (cdb_base == SCMD_READ) {
23814c06356bSdh142964 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
23824c06356bSdh142964 	} else {
23834c06356bSdh142964 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
23844c06356bSdh142964 	}
23854c06356bSdh142964 #ifdef	DEBUG
23864c06356bSdh142964 	/*
23874c06356bSdh142964 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
23884c06356bSdh142964 	 * event when this goes out on the wire.
23894c06356bSdh142964 	 */
23904c06356bSdh142964 	ptr[4] |= PMCIN_MESSAGE_REPORT;
23914c06356bSdh142964 #endif
23924c06356bSdh142964 	for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
23934c06356bSdh142964 		ptr[i+5] = LE_32(fis[i]);
23944c06356bSdh142964 	}
23954c06356bSdh142964 	if (pmcs_dma_load(pwp, sp, ptr)) {
23964c06356bSdh142964 		mutex_exit(&xp->statlock);
23974c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[iq]);
23984c06356bSdh142964 		mutex_enter(&xp->wqlock);
23994c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24004c06356bSdh142964 		mutex_exit(&xp->wqlock);
2401c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
24024c06356bSdh142964 		    "%s: Failed to dma_load for tgt %d",
24034c06356bSdh142964 		    __func__, xp->target_num);
24044c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
24054c06356bSdh142964 
24064c06356bSdh142964 	}
24074c06356bSdh142964 
24084c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
24094c06356bSdh142964 	mutex_exit(&pwrk->lock);
24104c06356bSdh142964 	xp->tagmap |= (1 << tag);
24114c06356bSdh142964 	xp->actv_cnt++;
24124c06356bSdh142964 	if (xp->actv_cnt > xp->maxdepth) {
24134c06356bSdh142964 		xp->maxdepth = xp->actv_cnt;
2414c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp,
2415c3bc407cSdh142964 		    "%s: max depth now %u", pwrk->phy->path, xp->maxdepth);
24164c06356bSdh142964 	}
24174c06356bSdh142964 	mutex_exit(&xp->statlock);
24184c06356bSdh142964 	mutex_enter(&xp->aqlock);
24194c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
24204c06356bSdh142964 	mutex_exit(&xp->aqlock);
2421c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
2422c3bc407cSdh142964 	    "%s: giving pkt %p to hardware", __func__, (void *)pkt);
24234c06356bSdh142964 #ifdef DEBUG
24244c06356bSdh142964 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
24254c06356bSdh142964 #endif
24264c06356bSdh142964 	INC_IQ_ENTRY(pwp, iq);
24274c06356bSdh142964 
24284c06356bSdh142964 	return (PMCS_WQ_RUN_SUCCESS);
24294c06356bSdh142964 }
24304c06356bSdh142964 
24314c06356bSdh142964 /*
24324c06356bSdh142964  * Complete a SATA command.  Called with pwrk lock held.
24334c06356bSdh142964  */
24344c06356bSdh142964 void
24354c06356bSdh142964 pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
24364c06356bSdh142964 {
24374c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
24384c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
24394c06356bSdh142964 	pmcs_phy_t *pptr = pwrk->phy;
24404c06356bSdh142964 	int dead;
24414c06356bSdh142964 	uint32_t sts;
24424c06356bSdh142964 	pmcs_xscsi_t *xp;
24434c06356bSdh142964 	boolean_t aborted = B_FALSE;
24444c06356bSdh142964 
24454c06356bSdh142964 	xp = pwrk->xp;
24464c06356bSdh142964 	ASSERT(xp != NULL);
24474c06356bSdh142964 
24484c06356bSdh142964 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
24494c06356bSdh142964 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
24504c06356bSdh142964 	    hrtime_t, gethrtime());
24514c06356bSdh142964 
24524c06356bSdh142964 	dead = pwrk->dead;
24534c06356bSdh142964 
24544c06356bSdh142964 	if (msg) {
24554c06356bSdh142964 		sts = LE_32(msg[2]);
24564c06356bSdh142964 	} else {
24574c06356bSdh142964 		sts = 0;
24584c06356bSdh142964 	}
24594c06356bSdh142964 
24604c06356bSdh142964 	if (dead != 0) {
2461c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2462c3bc407cSdh142964 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
24634c06356bSdh142964 		goto out;
24644c06356bSdh142964 	}
24654c06356bSdh142964 	if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
24664c06356bSdh142964 	    (sts != PMCOUT_STATUS_ABORTED)) {
2467c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
24684c06356bSdh142964 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
24694c06356bSdh142964 		    __func__, (void *)sp, pwrk->htag, pptr->path);
24704c06356bSdh142964 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
24714c06356bSdh142964 		/* pkt_reason already set to CMD_TIMEOUT */
24724c06356bSdh142964 		ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
24734c06356bSdh142964 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
24744c06356bSdh142964 		    STATE_SENT_CMD;
24754c06356bSdh142964 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
24764c06356bSdh142964 		goto out;
24774c06356bSdh142964 	}
24784c06356bSdh142964 
2479c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done",
24804c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num);
24814c06356bSdh142964 
24824c06356bSdh142964 	/*
24834c06356bSdh142964 	 * If the status isn't okay but not underflow,
24844c06356bSdh142964 	 * step to the side and parse the (possible) error.
24854c06356bSdh142964 	 */
24864c06356bSdh142964 #ifdef DEBUG
24874c06356bSdh142964 	if (msg) {
24884c06356bSdh142964 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
24894c06356bSdh142964 	}
24904c06356bSdh142964 #endif
24914c06356bSdh142964 	if (!msg) {
24924c06356bSdh142964 		goto out;
24934c06356bSdh142964 	}
24944c06356bSdh142964 
24954c06356bSdh142964 	/*
24964c06356bSdh142964 	 * If the status isn't okay or we got a FIS response of some kind,
24974c06356bSdh142964 	 * step to the side and parse the (possible) error.
24984c06356bSdh142964 	 */
24994c06356bSdh142964 	if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
25004c06356bSdh142964 		if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
25014c06356bSdh142964 			mutex_exit(&pwrk->lock);
25024c06356bSdh142964 			pmcs_lock_phy(pptr);
25034c06356bSdh142964 			mutex_enter(&xp->statlock);
25044c06356bSdh142964 			if ((xp->resetting == 0) && (xp->reset_success != 0) &&
25054c06356bSdh142964 			    (xp->reset_wait == 0)) {
25064c06356bSdh142964 				mutex_exit(&xp->statlock);
25074c06356bSdh142964 				if (pmcs_reset_phy(pwp, pptr,
25084c06356bSdh142964 				    PMCS_PHYOP_LINK_RESET) != 0) {
2509c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2510c3bc407cSdh142964 					    "%s: PHY (%s) Local Control/Link "
2511c3bc407cSdh142964 					    "Reset FAILED as part of error "
2512c3bc407cSdh142964 					    "recovery", __func__, pptr->path);
25134c06356bSdh142964 				}
25144c06356bSdh142964 				mutex_enter(&xp->statlock);
25154c06356bSdh142964 			}
25164c06356bSdh142964 			mutex_exit(&xp->statlock);
25174c06356bSdh142964 			pmcs_unlock_phy(pptr);
25184c06356bSdh142964 			mutex_enter(&pwrk->lock);
25194c06356bSdh142964 		}
25204c06356bSdh142964 		pmcs_ioerror(pwp, SATA, pwrk, msg);
25214c06356bSdh142964 	} else {
25224c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
25234c06356bSdh142964 		    pwrk->phy->path);
25244c06356bSdh142964 		pkt->pkt_state |= STATE_XFERRED_DATA;
25254c06356bSdh142964 		pkt->pkt_resid = 0;
25264c06356bSdh142964 	}
25274c06356bSdh142964 
2528c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
25294c06356bSdh142964 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
25304c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
25314c06356bSdh142964 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
25324c06356bSdh142964 
25334c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2534c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
25354c06356bSdh142964 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
25364c06356bSdh142964 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
25374c06356bSdh142964 		aborted = B_TRUE;
25384c06356bSdh142964 	}
25394c06356bSdh142964 
25404c06356bSdh142964 out:
25414c06356bSdh142964 	pmcs_pwork(pwp, pwrk);
25424c06356bSdh142964 	pmcs_dma_unload(pwp, sp);
25434c06356bSdh142964 
25444c06356bSdh142964 	mutex_enter(&xp->statlock);
25454c06356bSdh142964 	xp->tagmap &= ~(1 << sp->cmd_satltag);
25464c06356bSdh142964 
25474c06356bSdh142964 	if (xp->dev_gone) {
25484c06356bSdh142964 		mutex_exit(&xp->statlock);
2549c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
25504c06356bSdh142964 		    "%s: Completing command for dead target 0x%p", __func__,
25514c06356bSdh142964 		    (void *)xp);
25524c06356bSdh142964 		return;
25534c06356bSdh142964 	}
25544c06356bSdh142964 
25554c06356bSdh142964 	ASSERT(xp->actv_cnt > 0);
25564c06356bSdh142964 	if (--(xp->actv_cnt) == 0) {
25574c06356bSdh142964 		if (xp->draining) {
2558c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
25594c06356bSdh142964 			    "%s: waking up drain waiters", __func__);
25604c06356bSdh142964 			cv_signal(&pwp->drain_cv);
25614c06356bSdh142964 		} else if (xp->special_needed) {
25624c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
25634c06356bSdh142964 		}
25644c06356bSdh142964 	}
25654c06356bSdh142964 	mutex_exit(&xp->statlock);
25664c06356bSdh142964 
25674c06356bSdh142964 	if (dead == 0) {
25684c06356bSdh142964 #ifdef	DEBUG
25694c06356bSdh142964 		pmcs_cmd_t *wp;
25704c06356bSdh142964 		mutex_enter(&xp->aqlock);
25714c06356bSdh142964 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
25724c06356bSdh142964 			if (wp == sp) {
25734c06356bSdh142964 				break;
25744c06356bSdh142964 			}
25754c06356bSdh142964 		}
25764c06356bSdh142964 		ASSERT(wp != NULL);
25774c06356bSdh142964 #else
25784c06356bSdh142964 		mutex_enter(&xp->aqlock);
25794c06356bSdh142964 #endif
25804c06356bSdh142964 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
25814c06356bSdh142964 		if (aborted) {
2582c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
25834c06356bSdh142964 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
25844c06356bSdh142964 			    __func__, (void *)xp);
25854c06356bSdh142964 			cv_signal(&xp->abort_cv);
25864c06356bSdh142964 		}
25874c06356bSdh142964 		mutex_exit(&xp->aqlock);
25884c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
25894c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
25904c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
25914c06356bSdh142964 	}
25924c06356bSdh142964 }
25934c06356bSdh142964 
25944c06356bSdh142964 static uint8_t
25954c06356bSdh142964 pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
25964c06356bSdh142964 {
25974c06356bSdh142964 	uint8_t asc = 0;
25984c06356bSdh142964 	switch (cdb[0]) {
25994c06356bSdh142964 	case SCMD_READ_G5:
26004c06356bSdh142964 	case SCMD_WRITE_G5:
26014c06356bSdh142964 		*xfr =
26024c06356bSdh142964 		    (((uint32_t)cdb[10]) <<  24) |
26034c06356bSdh142964 		    (((uint32_t)cdb[11]) <<  16) |
26044c06356bSdh142964 		    (((uint32_t)cdb[12]) <<   8) |
26054c06356bSdh142964 		    ((uint32_t)cdb[13]);
26064c06356bSdh142964 		*lba =
26074c06356bSdh142964 		    (((uint64_t)cdb[2]) << 56) |
26084c06356bSdh142964 		    (((uint64_t)cdb[3]) << 48) |
26094c06356bSdh142964 		    (((uint64_t)cdb[4]) << 40) |
26104c06356bSdh142964 		    (((uint64_t)cdb[5]) << 32) |
26114c06356bSdh142964 		    (((uint64_t)cdb[6]) << 24) |
26124c06356bSdh142964 		    (((uint64_t)cdb[7]) << 16) |
26134c06356bSdh142964 		    (((uint64_t)cdb[8]) <<  8) |
26144c06356bSdh142964 		    ((uint64_t)cdb[9]);
26154c06356bSdh142964 		/* Check for illegal bits */
26164c06356bSdh142964 		if (cdb[15]) {
26174c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
26184c06356bSdh142964 		}
26194c06356bSdh142964 		break;
26204c06356bSdh142964 	case SCMD_READ_G4:
26214c06356bSdh142964 	case SCMD_WRITE_G4:
26224c06356bSdh142964 		*xfr =
26234c06356bSdh142964 		    (((uint32_t)cdb[6]) <<  16) |
26244c06356bSdh142964 		    (((uint32_t)cdb[7]) <<   8) |
26254c06356bSdh142964 		    ((uint32_t)cdb[8]);
26264c06356bSdh142964 		*lba =
26274c06356bSdh142964 		    (((uint32_t)cdb[2]) << 24) |
26284c06356bSdh142964 		    (((uint32_t)cdb[3]) << 16) |
26294c06356bSdh142964 		    (((uint32_t)cdb[4]) <<  8) |
26304c06356bSdh142964 		    ((uint32_t)cdb[5]);
26314c06356bSdh142964 		/* Check for illegal bits */
26324c06356bSdh142964 		if (cdb[11]) {
26334c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
26344c06356bSdh142964 		}
26354c06356bSdh142964 		break;
26364c06356bSdh142964 	case SCMD_READ_G1:
26374c06356bSdh142964 	case SCMD_WRITE_G1:
26384c06356bSdh142964 		*xfr = (((uint32_t)cdb[7]) <<  8) | ((uint32_t)cdb[8]);
26394c06356bSdh142964 		*lba =
26404c06356bSdh142964 		    (((uint32_t)cdb[2]) << 24) |
26414c06356bSdh142964 		    (((uint32_t)cdb[3]) << 16) |
26424c06356bSdh142964 		    (((uint32_t)cdb[4]) <<  8) |
26434c06356bSdh142964 		    ((uint32_t)cdb[5]);
26444c06356bSdh142964 		/* Check for illegal bits */
26454c06356bSdh142964 		if (cdb[9]) {
26464c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
26474c06356bSdh142964 		}
26484c06356bSdh142964 		break;
26494c06356bSdh142964 	case SCMD_READ:
26504c06356bSdh142964 	case SCMD_WRITE:
26514c06356bSdh142964 		*xfr = cdb[4];
26524c06356bSdh142964 		if (*xfr == 0) {
26534c06356bSdh142964 			*xfr = 256;
26544c06356bSdh142964 		}
26554c06356bSdh142964 		*lba =
26564c06356bSdh142964 		    (((uint32_t)cdb[1] & 0x1f) << 16) |
26574c06356bSdh142964 		    (((uint32_t)cdb[2]) << 8) |
26584c06356bSdh142964 		    ((uint32_t)cdb[3]);
26594c06356bSdh142964 		/* Check for illegal bits */
26604c06356bSdh142964 		if (cdb[5]) {
26614c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
26624c06356bSdh142964 		}
26634c06356bSdh142964 		break;
26644c06356bSdh142964 	}
26654c06356bSdh142964 
26664c06356bSdh142964 	if (asc == 0) {
26674c06356bSdh142964 		if ((*lba + *xfr) > lbamax) {
26684c06356bSdh142964 			asc = 0x21;	/* logical block out of range */
26694c06356bSdh142964 		}
26704c06356bSdh142964 	}
26714c06356bSdh142964 	return (asc);
26724c06356bSdh142964 }
26734c06356bSdh142964 
26744c06356bSdh142964 /*
26754c06356bSdh142964  * Called with pwrk lock held.
26764c06356bSdh142964  */
26774c06356bSdh142964 static void
26784c06356bSdh142964 pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w)
26794c06356bSdh142964 {
26804c06356bSdh142964 	static uint8_t por[] = {
26814c06356bSdh142964 	    0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
26824c06356bSdh142964 	};
26834c06356bSdh142964 	static uint8_t parity[] = {
26844c06356bSdh142964 	    0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
26854c06356bSdh142964 	};
26864c06356bSdh142964 	const char *msg;
26874c06356bSdh142964 	char buf[20];
26884c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
26894c06356bSdh142964 	pmcs_phy_t *phyp = pwrk->phy;
26904c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
26914c06356bSdh142964 	uint32_t status;
26924c06356bSdh142964 	uint32_t resid;
26934c06356bSdh142964 
26944c06356bSdh142964 	ASSERT(w != NULL);
26954c06356bSdh142964 	status = LE_32(w[2]);
26964c06356bSdh142964 	resid = LE_32(w[3]);
26974c06356bSdh142964 
26984c06356bSdh142964 	msg = pmcs_status_str(status);
26994c06356bSdh142964 	if (msg == NULL) {
27004c06356bSdh142964 		(void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
27014c06356bSdh142964 		msg = buf;
27024c06356bSdh142964 	}
27034c06356bSdh142964 
27044c06356bSdh142964 	if (status != PMCOUT_STATUS_OK) {
2705c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, NULL,
27064c06356bSdh142964 		    "%s: device %s tag 0x%x status %s @ %llu", __func__,
27074c06356bSdh142964 		    phyp->path, pwrk->htag, msg,
27084c06356bSdh142964 		    (unsigned long long)gethrtime());
27094c06356bSdh142964 	}
27104c06356bSdh142964 
27114c06356bSdh142964 	pkt->pkt_reason = CMD_CMPLT;		/* default reason */
27124c06356bSdh142964 
27134c06356bSdh142964 	switch (status) {
27144c06356bSdh142964 	case PMCOUT_STATUS_OK:
27154c06356bSdh142964 		if (t == SATA) {
27164c06356bSdh142964 			int i;
27174c06356bSdh142964 			fis_t fis;
27184c06356bSdh142964 			for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
27194c06356bSdh142964 				fis[i] = LE_32(w[4+i]);
27204c06356bSdh142964 			}
27214c06356bSdh142964 			if ((fis[0] & 0xff) != FIS_REG_D2H) {
2722c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
27234c06356bSdh142964 				    "unexpected fis code 0x%x", fis[0] & 0xff);
27244c06356bSdh142964 			} else {
2725c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
2726c3bc407cSdh142964 				    "FIS ERROR");
27274c06356bSdh142964 				pmcs_fis_dump(pwp, fis);
27284c06356bSdh142964 			}
27294c06356bSdh142964 			pkt->pkt_reason = CMD_TRAN_ERR;
27304c06356bSdh142964 			break;
27314c06356bSdh142964 		}
27324c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
27334c06356bSdh142964 		break;
27344c06356bSdh142964 
27354c06356bSdh142964 	case PMCOUT_STATUS_ABORTED:
27364c06356bSdh142964 		/*
27374c06356bSdh142964 		 * Command successfully aborted.
27384c06356bSdh142964 		 */
27394c06356bSdh142964 		if (phyp->dead) {
27404c06356bSdh142964 			pkt->pkt_reason = CMD_DEV_GONE;
27414c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
27424c06356bSdh142964 		} else if (pwrk->ssp_event != 0) {
27434c06356bSdh142964 			pkt->pkt_reason = CMD_TRAN_ERR;
27444c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
27454c06356bSdh142964 		} else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
27464c06356bSdh142964 			pkt->pkt_reason = CMD_TIMEOUT;
27474c06356bSdh142964 			pkt->pkt_statistics |= STAT_TIMEOUT;
27484c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
27494c06356bSdh142964 			    STATE_SENT_CMD;
27504c06356bSdh142964 		} else {
27514c06356bSdh142964 			pkt->pkt_reason = CMD_ABORTED;
27524c06356bSdh142964 			pkt->pkt_statistics |= STAT_ABORTED;
27534c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
27544c06356bSdh142964 			    STATE_SENT_CMD;
27554c06356bSdh142964 		}
27564c06356bSdh142964 
27574c06356bSdh142964 		/*
27584c06356bSdh142964 		 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
27594c06356bSdh142964 		 * this point, so go ahead and mark it as aborted.
27604c06356bSdh142964 		 */
27614c06356bSdh142964 		pwrk->state = PMCS_WORK_STATE_ABORTED;
27624c06356bSdh142964 		break;
27634c06356bSdh142964 
27644c06356bSdh142964 	case PMCOUT_STATUS_UNDERFLOW:
27654c06356bSdh142964 		/*
27664c06356bSdh142964 		 * This will only get called for SATA
27674c06356bSdh142964 		 */
27684c06356bSdh142964 		pkt->pkt_resid = resid;
27694c06356bSdh142964 		if (pkt->pkt_dma_len < pkt->pkt_resid) {
27704c06356bSdh142964 			(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
27714c06356bSdh142964 		}
27724c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
27734c06356bSdh142964 		break;
27744c06356bSdh142964 
27754c06356bSdh142964 	case PMCOUT_STATUS_NO_DEVICE:
27764c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
27774c06356bSdh142964 		pkt->pkt_reason = CMD_DEV_GONE;
27784c06356bSdh142964 		break;
27794c06356bSdh142964 
27804c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
27814c06356bSdh142964 		/*
27824c06356bSdh142964 		 * Need to do rediscovery. We probably have
27834c06356bSdh142964 		 * the wrong device (disk swap), so kill
27844c06356bSdh142964 		 * this one.
27854c06356bSdh142964 		 */
27864c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
27874c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
27884c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
27894c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR:
27904c06356bSdh142964 		/*
27914c06356bSdh142964 		 * Need to do rediscovery.
27924c06356bSdh142964 		 */
27934c06356bSdh142964 		if (!phyp->dead) {
27944c06356bSdh142964 			mutex_exit(&pwrk->lock);
27954c06356bSdh142964 			pmcs_lock_phy(pwrk->phy);
27964c06356bSdh142964 			pmcs_kill_changed(pwp, pwrk->phy, 0);
27974c06356bSdh142964 			pmcs_unlock_phy(pwrk->phy);
27984c06356bSdh142964 			mutex_enter(&pwrk->lock);
27994c06356bSdh142964 			pkt->pkt_reason = CMD_INCOMPLETE;
28004c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
28014c06356bSdh142964 		} else {
28024c06356bSdh142964 			pkt->pkt_reason = CMD_DEV_GONE;
28034c06356bSdh142964 		}
28044c06356bSdh142964 		break;
28054c06356bSdh142964 
28064c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
28074c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
28084c06356bSdh142964 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
28094c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
28104c06356bSdh142964 		/* cmd is pending on the target */
28114c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
28124c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
28134c06356bSdh142964 		/* transitory - commands sent while in NCQ failure mode */
28144c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
28154c06356bSdh142964 		/* NCQ failure */
28164c06356bSdh142964 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
28174c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERR_BREAK:
28184c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
28194c06356bSdh142964 		pkt->pkt_reason = CMD_INCOMPLETE;
28204c06356bSdh142964 		pkt->pkt_state = STATE_GOT_BUS;
28214c06356bSdh142964 		break;
28224c06356bSdh142964 
28234c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
28244c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
28254c06356bSdh142964 		break;
28264c06356bSdh142964 
28274c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
28284c06356bSdh142964 		/* synthesize a RESERVATION CONFLICT */
2829499cfd15SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
2830499cfd15SDavid Hollister 		    "%s: Potential affiliation active on 0x%" PRIx64, __func__,
2831499cfd15SDavid Hollister 		    pmcs_barray2wwn(phyp->sas_address));
28324c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
28334c06356bSdh142964 		    0, phyp->path);
28344c06356bSdh142964 		break;
28354c06356bSdh142964 
28364c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
28374c06356bSdh142964 		/* synthesize a power-on/reset */
28384c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
28394c06356bSdh142964 		    phyp->path);
28404c06356bSdh142964 		break;
28414c06356bSdh142964 
28424c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
28434c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
28444c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
28454c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
28464c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
28474c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
28484c06356bSdh142964 		/* synthesize a PARITY ERROR */
28494c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
28504c06356bSdh142964 		    sizeof (parity), phyp->path);
28514c06356bSdh142964 		break;
28524c06356bSdh142964 
28534c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
28544c06356bSdh142964 	case PMCOUT_STATUS_IO_NOT_VALID:
28554c06356bSdh142964 	case PMCOUT_STATUS_PROG_ERROR:
28564c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
28574c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
28584c06356bSdh142964 	default:
28594c06356bSdh142964 		pkt->pkt_reason = CMD_TRAN_ERR;
28604c06356bSdh142964 		break;
28614c06356bSdh142964 	}
28624c06356bSdh142964 }
28634c06356bSdh142964 
28644c06356bSdh142964 /*
28654c06356bSdh142964  * Latch up SCSI status
28664c06356bSdh142964  */
28674c06356bSdh142964 
28684c06356bSdh142964 void
28694c06356bSdh142964 pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
28704c06356bSdh142964     uint8_t *snsp, size_t snslen, char *path)
28714c06356bSdh142964 {
28724c06356bSdh142964 	static const char c1[] =
28734c06356bSdh142964 	    "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
28744c06356bSdh142964 	    "HTAG 0x%x @ %llu";
28754c06356bSdh142964 	static const char c2[] =
28764c06356bSdh142964 	    "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
28774c06356bSdh142964 
28784c06356bSdh142964 	CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
28794c06356bSdh142964 	    STATE_SENT_CMD | STATE_GOT_STATUS;
28804c06356bSdh142964 	CMD2PKT(sp)->pkt_scbp[0] = status;
28814c06356bSdh142964 
28824c06356bSdh142964 	if (status == STATUS_CHECK && snsp &&
28834c06356bSdh142964 	    (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
28844c06356bSdh142964 		struct scsi_arq_status *aqp =
28854c06356bSdh142964 		    (void *) CMD2PKT(sp)->pkt_scbp;
28864c06356bSdh142964 		size_t amt = sizeof (struct scsi_extended_sense);
28874c06356bSdh142964 		uint8_t key = scsi_sense_key(snsp);
28884c06356bSdh142964 		uint8_t asc = scsi_sense_asc(snsp);
28894c06356bSdh142964 		uint8_t ascq = scsi_sense_ascq(snsp);
28904c06356bSdh142964 		if (amt > snslen) {
28914c06356bSdh142964 			amt = snslen;
28924c06356bSdh142964 		}
2893c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path,
2894c3bc407cSdh142964 		    status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
28954c06356bSdh142964 		    sp->cmd_tag, (unsigned long long)gethrtime());
28964c06356bSdh142964 		CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
28974c06356bSdh142964 		(*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
28984c06356bSdh142964 		aqp->sts_rqpkt_statistics = 0;
28994c06356bSdh142964 		aqp->sts_rqpkt_reason = CMD_CMPLT;
29004c06356bSdh142964 		aqp->sts_rqpkt_state = STATE_GOT_BUS |
29014c06356bSdh142964 		    STATE_GOT_TARGET | STATE_SENT_CMD |
29024c06356bSdh142964 		    STATE_XFERRED_DATA | STATE_GOT_STATUS;
29034c06356bSdh142964 		(void) memcpy(&aqp->sts_sensedata, snsp, amt);
29044c06356bSdh142964 		if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
29054c06356bSdh142964 			aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
29064c06356bSdh142964 			aqp->sts_rqpkt_state = 0;
29074c06356bSdh142964 			aqp->sts_rqpkt_resid =
29084c06356bSdh142964 			    sizeof (struct scsi_extended_sense);
29094c06356bSdh142964 		} else {
29104c06356bSdh142964 			aqp->sts_rqpkt_resid =
29114c06356bSdh142964 			    sizeof (struct scsi_extended_sense) - amt;
29124c06356bSdh142964 		}
29134c06356bSdh142964 	} else if (status) {
2914c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2,
29154c06356bSdh142964 		    path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
29164c06356bSdh142964 		    sp->cmd_tag, (unsigned long long)gethrtime());
29174c06356bSdh142964 	}
29184c06356bSdh142964 
29194c06356bSdh142964 	CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
29204c06356bSdh142964 }
29214c06356bSdh142964 
29224c06356bSdh142964 /*
29234c06356bSdh142964  * Calculate and set packet residual and return the amount
29244c06356bSdh142964  * left over after applying various filters.
29254c06356bSdh142964  */
29264c06356bSdh142964 size_t
29274c06356bSdh142964 pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
29284c06356bSdh142964 {
29294c06356bSdh142964 	pkt->pkt_resid = cdbamt;
29304c06356bSdh142964 	if (amt > pkt->pkt_resid) {
29314c06356bSdh142964 		amt = pkt->pkt_resid;
29324c06356bSdh142964 	}
29334c06356bSdh142964 	if (amt > pkt->pkt_dma_len) {
29344c06356bSdh142964 		amt = pkt->pkt_dma_len;
29354c06356bSdh142964 	}
29364c06356bSdh142964 	return (amt);
29374c06356bSdh142964 }
29384c06356bSdh142964 
29394c06356bSdh142964 /*
29404c06356bSdh142964  * Return the existing target softstate if there is one.  If there is,
29414c06356bSdh142964  * the PHY is locked as well and that lock must be freed by the caller
29424c06356bSdh142964  * after the target/PHY linkage is established.
29434c06356bSdh142964  */
29444c06356bSdh142964 pmcs_xscsi_t *
29454c06356bSdh142964 pmcs_get_target(pmcs_iport_t *iport, char *tgt_port)
29464c06356bSdh142964 {
29474c06356bSdh142964 	pmcs_hw_t *pwp = iport->pwp;
29484c06356bSdh142964 	pmcs_phy_t *phyp;
29494c06356bSdh142964 	pmcs_xscsi_t *tgt;
29504c06356bSdh142964 	uint64_t wwn;
29514c06356bSdh142964 	char unit_address[PMCS_MAX_UA_SIZE];
29524c06356bSdh142964 	int ua_form = 1;
29534c06356bSdh142964 
29544c06356bSdh142964 	/*
29554c06356bSdh142964 	 * Find the PHY for this target
29564c06356bSdh142964 	 */
29574c06356bSdh142964 	phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
29584c06356bSdh142964 	if (phyp == NULL) {
2959c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
2960c3bc407cSdh142964 		    "%s: No PHY for target @ %s", __func__, tgt_port);
29614c06356bSdh142964 		return (NULL);
29624c06356bSdh142964 	}
29634c06356bSdh142964 
29644c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
29654c06356bSdh142964 
29664c06356bSdh142964 	if (tgt) {
29674c06356bSdh142964 		/*
29684c06356bSdh142964 		 * There's already a target.  Check its PHY pointer to see
29694c06356bSdh142964 		 * if we need to clear the old linkages
29704c06356bSdh142964 		 */
29714c06356bSdh142964 		if (tgt->phy && (tgt->phy != phyp)) {
2972c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
29734c06356bSdh142964 			    "%s: Target PHY updated from %p to %p", __func__,
29744c06356bSdh142964 			    (void *)tgt->phy, (void *)phyp);
29754c06356bSdh142964 			if (!IS_ROOT_PHY(tgt->phy)) {
29764c06356bSdh142964 				pmcs_dec_phy_ref_count(tgt->phy);
29774c06356bSdh142964 				pmcs_inc_phy_ref_count(phyp);
29784c06356bSdh142964 			}
29794c06356bSdh142964 			tgt->phy->target = NULL;
29804c06356bSdh142964 		}
29814c06356bSdh142964 
29824c06356bSdh142964 		tgt->phy = phyp;
29834c06356bSdh142964 		phyp->target = tgt;
29844c06356bSdh142964 		return (tgt);
29854c06356bSdh142964 	}
29864c06356bSdh142964 
29874c06356bSdh142964 	/*
29884c06356bSdh142964 	 * Make sure the PHY we found is on the correct iport
29894c06356bSdh142964 	 */
29904c06356bSdh142964 	if (phyp->iport != iport) {
2991c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
29924c06356bSdh142964 		    "%s: No target at %s on this iport", __func__, tgt_port);
29934c06356bSdh142964 		pmcs_unlock_phy(phyp);
29944c06356bSdh142964 		return (NULL);
29954c06356bSdh142964 	}
29964c06356bSdh142964 
29974c06356bSdh142964 	/*
29984c06356bSdh142964 	 * Allocate the new softstate
29994c06356bSdh142964 	 */
30004c06356bSdh142964 	wwn = pmcs_barray2wwn(phyp->sas_address);
30014c06356bSdh142964 	(void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
30024c06356bSdh142964 
30034c06356bSdh142964 	if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
30044c06356bSdh142964 	    DDI_SUCCESS) {
3005c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
30064c06356bSdh142964 		    "%s: Couldn't alloc softstate for device at %s",
30074c06356bSdh142964 		    __func__, unit_address);
30084c06356bSdh142964 		pmcs_unlock_phy(phyp);
30094c06356bSdh142964 		return (NULL);
30104c06356bSdh142964 	}
30114c06356bSdh142964 
30124c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
30134c06356bSdh142964 	STAILQ_INIT(&tgt->wq);
30144c06356bSdh142964 	STAILQ_INIT(&tgt->aq);
30154c06356bSdh142964 	STAILQ_INIT(&tgt->sq);
30164c06356bSdh142964 	mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
30174c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
30184c06356bSdh142964 	mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
30194c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
30204c06356bSdh142964 	mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
30214c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
30224c06356bSdh142964 	cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
30234c06356bSdh142964 	cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
30244c06356bSdh142964 	tgt->qdepth = 1;
30254c06356bSdh142964 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
30264c06356bSdh142964 	bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
30274c06356bSdh142964 	tgt->pwp = pwp;
30284c06356bSdh142964 	tgt->ua = strdup(iport->ua);
30294c06356bSdh142964 	tgt->phy = phyp;
30304c06356bSdh142964 	ASSERT((phyp->target == NULL) || (phyp->target == tgt));
30314c06356bSdh142964 	if (phyp->target == NULL) {
30324c06356bSdh142964 		phyp->target = tgt;
30334c06356bSdh142964 	}
30344c06356bSdh142964 
30354c06356bSdh142964 	/*
30364c06356bSdh142964 	 * Don't allocate LUN softstate for SMP targets
30374c06356bSdh142964 	 */
30384c06356bSdh142964 	if (phyp->dtype == EXPANDER) {
30394c06356bSdh142964 		return (tgt);
30404c06356bSdh142964 	}
30414c06356bSdh142964 
30424c06356bSdh142964 	if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
30434c06356bSdh142964 	    sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
3044c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
30454c06356bSdh142964 		    "%s: LUN soft_state_bystr_init failed", __func__);
30464c06356bSdh142964 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
30474c06356bSdh142964 		pmcs_unlock_phy(phyp);
30484c06356bSdh142964 		return (NULL);
30494c06356bSdh142964 	}
30504c06356bSdh142964 
30514c06356bSdh142964 	return (tgt);
30524c06356bSdh142964 }
3053