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
20658280b6SDavid Hollister  */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh142964  */
244c06356bSdh142964 /*
254c06356bSdh142964  * SCSI (SCSA) midlayer interface for PMC drier.
264c06356bSdh142964  */
274c06356bSdh142964 
284c06356bSdh142964 #include <sys/scsi/adapters/pmcs/pmcs.h>
294c06356bSdh142964 
304c06356bSdh142964 extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
314c06356bSdh142964 
324c06356bSdh142964 static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
334c06356bSdh142964     scsi_hba_tran_t *, struct scsi_device *);
344c06356bSdh142964 static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
354c06356bSdh142964     scsi_hba_tran_t *, struct scsi_device *);
364c06356bSdh142964 static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
374c06356bSdh142964 static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
384c06356bSdh142964 static int pmcs_scsa_reset(struct scsi_address *, int);
394c06356bSdh142964 static int pmcs_scsi_reset_notify(struct scsi_address *, int,
404c06356bSdh142964     void (*)(caddr_t), caddr_t);
414c06356bSdh142964 static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
424c06356bSdh142964 static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
434c06356bSdh142964 static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
444c06356bSdh142964 static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
4596c4a178SChris Horne 
4696c4a178SChris Horne static int pmcs_smp_init(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4796c4a178SChris Horne     smp_device_t *);
4896c4a178SChris Horne static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4996c4a178SChris Horne     smp_device_t *);
504c06356bSdh142964 static int pmcs_smp_start(struct smp_pkt *);
514c06356bSdh142964 
524c06356bSdh142964 static int pmcs_scsi_quiesce(dev_info_t *);
534c06356bSdh142964 static int pmcs_scsi_unquiesce(dev_info_t *);
544c06356bSdh142964 
554c06356bSdh142964 static int pmcs_cap(struct scsi_address *, char *, int, int, int);
564c06356bSdh142964 static pmcs_xscsi_t *
574c06356bSdh142964     pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
584c06356bSdh142964 static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
594c06356bSdh142964 static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
604c06356bSdh142964 
614c06356bSdh142964 static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
624c06356bSdh142964 static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
634c06356bSdh142964 static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
644c06356bSdh142964 
654c06356bSdh142964 static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
66658280b6SDavid Hollister     pmcwork_t *, uint32_t *, uint32_t);
674c06356bSdh142964 
684c06356bSdh142964 
694c06356bSdh142964 int
704c06356bSdh142964 pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
714c06356bSdh142964 {
724c06356bSdh142964 	scsi_hba_tran_t *tran;
734c06356bSdh142964 	ddi_dma_attr_t pmcs_scsa_dattr;
744c06356bSdh142964 	int flags;
754c06356bSdh142964 
764c06356bSdh142964 	(void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
774c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_sgllen =
784c06356bSdh142964 	    ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
794c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
804c06356bSdh142964 	pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
814c06356bSdh142964 
824c06356bSdh142964 	/*
834c06356bSdh142964 	 * Allocate a transport structure
844c06356bSdh142964 	 */
854c06356bSdh142964 	tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
864c06356bSdh142964 	if (tran == NULL) {
87c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
88c3bc407cSdh142964 		    "scsi_hba_tran_alloc failed");
894c06356bSdh142964 		return (DDI_FAILURE);
904c06356bSdh142964 	}
914c06356bSdh142964 
924c06356bSdh142964 	tran->tran_hba_private		= pwp;
934c06356bSdh142964 	tran->tran_tgt_init		= pmcs_scsa_tran_tgt_init;
944c06356bSdh142964 	tran->tran_tgt_free		= pmcs_scsa_tran_tgt_free;
954c06356bSdh142964 	tran->tran_start		= pmcs_scsa_start;
964c06356bSdh142964 	tran->tran_abort		= pmcs_scsa_abort;
974c06356bSdh142964 	tran->tran_reset		= pmcs_scsa_reset;
984c06356bSdh142964 	tran->tran_reset_notify		= pmcs_scsi_reset_notify;
994c06356bSdh142964 	tran->tran_getcap		= pmcs_scsa_getcap;
1004c06356bSdh142964 	tran->tran_setcap		= pmcs_scsa_setcap;
1014c06356bSdh142964 	tran->tran_setup_pkt		= pmcs_scsa_setup_pkt;
1024c06356bSdh142964 	tran->tran_teardown_pkt		= pmcs_scsa_teardown_pkt;
1034c06356bSdh142964 	tran->tran_quiesce		= pmcs_scsi_quiesce;
1044c06356bSdh142964 	tran->tran_unquiesce		= pmcs_scsi_unquiesce;
1054c06356bSdh142964 	tran->tran_interconnect_type	= INTERCONNECT_SAS;
1064c06356bSdh142964 	tran->tran_hba_len		= sizeof (pmcs_cmd_t);
1074c06356bSdh142964 
1084c06356bSdh142964 	/*
1094c06356bSdh142964 	 * Attach this instance of the hba
1104c06356bSdh142964 	 */
1114c06356bSdh142964 
1124c06356bSdh142964 	flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
1134c06356bSdh142964 	    SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
1144c06356bSdh142964 
1154c06356bSdh142964 	if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
1164c06356bSdh142964 		scsi_hba_tran_free(tran);
117c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
118c3bc407cSdh142964 		    "scsi_hba_attach failed");
1194c06356bSdh142964 		return (DDI_FAILURE);
1204c06356bSdh142964 	}
1214c06356bSdh142964 	pwp->tran = tran;
1224c06356bSdh142964 
1234c06356bSdh142964 	/*
1244c06356bSdh142964 	 * Attach the SMP part of this hba
1254c06356bSdh142964 	 */
12696c4a178SChris Horne 	pwp->smp_tran = smp_hba_tran_alloc(pwp->dip);
1274c06356bSdh142964 	ASSERT(pwp->smp_tran != NULL);
12896c4a178SChris Horne 	pwp->smp_tran->smp_tran_hba_private = pwp;
12996c4a178SChris Horne 	pwp->smp_tran->smp_tran_init = pmcs_smp_init;
13096c4a178SChris Horne 	pwp->smp_tran->smp_tran_free = pmcs_smp_free;
13196c4a178SChris Horne 	pwp->smp_tran->smp_tran_start = pmcs_smp_start;
1324c06356bSdh142964 
13396c4a178SChris Horne 	if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
134c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13596c4a178SChris Horne 		    "smp_hba_attach failed");
13696c4a178SChris Horne 		smp_hba_tran_free(pwp->smp_tran);
1374c06356bSdh142964 		pwp->smp_tran = NULL;
1384c06356bSdh142964 		scsi_hba_tran_free(tran);
1394c06356bSdh142964 		return (DDI_FAILURE);
1404c06356bSdh142964 	}
1414c06356bSdh142964 
1424c06356bSdh142964 	return (DDI_SUCCESS);
1434c06356bSdh142964 }
1444c06356bSdh142964 
1454c06356bSdh142964 /*
1464c06356bSdh142964  * SCSA entry points
1474c06356bSdh142964  */
1484c06356bSdh142964 
1494c06356bSdh142964 static int
1504c06356bSdh142964 pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
1514c06356bSdh142964     scsi_hba_tran_t *tran, struct scsi_device *sd)
1524c06356bSdh142964 {
1534c06356bSdh142964 	pmcs_hw_t	*pwp = NULL;
1544c06356bSdh142964 	int		rval;
1554c06356bSdh142964 	char		*variant_prop = "sata";
1564c06356bSdh142964 	char		*tgt_port = NULL, *ua = NULL;
1574c06356bSdh142964 	pmcs_xscsi_t	*tgt = NULL;
1584c06356bSdh142964 	pmcs_iport_t	*iport;
1594c06356bSdh142964 	pmcs_lun_t	*lun = NULL;
1604c06356bSdh142964 	pmcs_phy_t	*phyp = NULL;
1614c06356bSdh142964 	uint64_t	lun_num;
1624c06356bSdh142964 	boolean_t	got_scratch = B_FALSE;
1634c06356bSdh142964 
1644c06356bSdh142964 	/*
1654c06356bSdh142964 	 * First, make sure we're an iport and get the pointer to the HBA
1664c06356bSdh142964 	 * node's softstate
1674c06356bSdh142964 	 */
1684c06356bSdh142964 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
169c3bc407cSdh142964 		pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
1704c06356bSdh142964 		    "%s: We don't enumerate devices on the HBA node", __func__);
1714c06356bSdh142964 		goto tgt_init_fail;
1724c06356bSdh142964 	}
1734c06356bSdh142964 
1744c06356bSdh142964 	pwp = ITRAN2PMC(tran);
1754c06356bSdh142964 	iport = ITRAN2IPORT(tran);
1764c06356bSdh142964 
1774c06356bSdh142964 	/*
178c40ba10dSReed 	 * Get the unit-address
179c40ba10dSReed 	 */
180c40ba10dSReed 	ua = scsi_device_unit_address(sd);
181c40ba10dSReed 	if (ua == NULL) {
182c40ba10dSReed 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
183c40ba10dSReed 		    "%s: Couldn't get UA", __func__);
184c40ba10dSReed 		pwp = NULL;
185c40ba10dSReed 		goto tgt_init_fail;
186c40ba10dSReed 	}
187c40ba10dSReed 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
188c40ba10dSReed 	    "got ua '%s'", ua);
189c40ba10dSReed 
190c40ba10dSReed 	/*
1914c06356bSdh142964 	 * Get the target address
1924c06356bSdh142964 	 */
1934c06356bSdh142964 	rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
1944c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
1954c06356bSdh142964 	if (rval != DDI_PROP_SUCCESS) {
196c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
197c3bc407cSdh142964 		    "Couldn't get target UA");
1984c06356bSdh142964 		pwp = NULL;
1994c06356bSdh142964 		goto tgt_init_fail;
2004c06356bSdh142964 	}
201c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
202c3bc407cSdh142964 	    "got tgt_port '%s'", tgt_port);
2034c06356bSdh142964 
2044c06356bSdh142964 	/*
2054c06356bSdh142964 	 * Validate that this tran_tgt_init is for an active iport.
2064c06356bSdh142964 	 */
2074c06356bSdh142964 	if (iport->ua_state == UA_INACTIVE) {
208c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2094c06356bSdh142964 		    "%s: Got tran_tgt_init on inactive iport for '%s'",
2104c06356bSdh142964 		    __func__, tgt_port);
2114c06356bSdh142964 		pwp = NULL;
2124c06356bSdh142964 		goto tgt_init_fail;
2134c06356bSdh142964 	}
2144c06356bSdh142964 
2154c06356bSdh142964 	/*
2164c06356bSdh142964 	 * Since we're going to wait for scratch, be sure to acquire it while
2174c06356bSdh142964 	 * we're not holding any other locks
2184c06356bSdh142964 	 */
2194c06356bSdh142964 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
2204c06356bSdh142964 	got_scratch = B_TRUE;
2214c06356bSdh142964 
2224c06356bSdh142964 	mutex_enter(&pwp->lock);
2234c06356bSdh142964 
2244c06356bSdh142964 	/*
2254c06356bSdh142964 	 * See if there's already a target softstate.  If not, allocate one.
2264c06356bSdh142964 	 */
2275c45adf0SJesse Butler 	tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
2284c06356bSdh142964 
2294c06356bSdh142964 	if (tgt == NULL) {
230188eaed9SSrikanth Suravajhala 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: "
231188eaed9SSrikanth Suravajhala 		    "No tgt for tgt_port (%s)", __func__, tgt_port);
2324c06356bSdh142964 		goto tgt_init_fail;
2334c06356bSdh142964 	}
2344c06356bSdh142964 
2354c06356bSdh142964 	phyp = tgt->phy;
2364c06356bSdh142964 	if (!IS_ROOT_PHY(phyp)) {
2374c06356bSdh142964 		pmcs_inc_phy_ref_count(phyp);
2384c06356bSdh142964 	}
2394c06356bSdh142964 	ASSERT(mutex_owned(&phyp->phy_lock));
2404c06356bSdh142964 
241c40ba10dSReed 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "@%s tgt = 0x%p, dip = 0x%p",
242c40ba10dSReed 	    ua, (void *)tgt, (void *)tgt_dip);
2434c06356bSdh142964 
244c40ba10dSReed 	/* Now get the lun */
2454c06356bSdh142964 	lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
2464c06356bSdh142964 	    SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
2474c06356bSdh142964 	if (lun_num == SCSI_LUN64_ILLEGAL) {
248c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
249c3bc407cSdh142964 		    "No LUN for tgt %p", (void *)tgt);
2504c06356bSdh142964 		goto tgt_init_fail;
2514c06356bSdh142964 	}
2524c06356bSdh142964 
253c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy "
254c3bc407cSdh142964 	    "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
2554c06356bSdh142964 
2564c06356bSdh142964 	mutex_enter(&tgt->statlock);
2574c06356bSdh142964 	tgt->dtype = phyp->dtype;
2584c06356bSdh142964 	if (tgt->dtype != SAS && tgt->dtype != SATA) {
259c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
260c3bc407cSdh142964 		    "PHY 0x%p went away?", (void *)phyp);
2614c06356bSdh142964 		goto tgt_init_fail;
2624c06356bSdh142964 	}
2634c06356bSdh142964 
2644c06356bSdh142964 	/* We don't support SATA devices at LUN > 0. */
2654c06356bSdh142964 	if ((tgt->dtype == SATA) && (lun_num > 0)) {
266c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
2674c06356bSdh142964 		    "%s: No support for SATA devices at LUN > 0 "
2684c06356bSdh142964 		    "(target = 0x%p)", __func__, (void *)tgt);
2694c06356bSdh142964 		goto tgt_init_fail;
2704c06356bSdh142964 	}
2714c06356bSdh142964 
2724c06356bSdh142964 	/*
2734c06356bSdh142964 	 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
2744c06356bSdh142964 	 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
2754c06356bSdh142964 	 * verify that the framework never tries to initialize two scsi_device
2764c06356bSdh142964 	 * structures with the same unit-address at the same time.
2774c06356bSdh142964 	 */
2784c06356bSdh142964 	if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
279c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
2804c06356bSdh142964 		    "Couldn't allocate LU soft state");
2814c06356bSdh142964 		goto tgt_init_fail;
2824c06356bSdh142964 	}
2834c06356bSdh142964 
2844c06356bSdh142964 	lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
2854c06356bSdh142964 	if (lun == NULL) {
286c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
287c3bc407cSdh142964 		    "Couldn't get LU soft state");
2884c06356bSdh142964 		goto tgt_init_fail;
2894c06356bSdh142964 	}
2904c06356bSdh142964 	scsi_device_hba_private_set(sd, lun);
2914c06356bSdh142964 	lun->lun_num = lun_num;
2924c06356bSdh142964 
2934c06356bSdh142964 	/* convert the scsi_lun64_t value to SCSI standard form */
2944c06356bSdh142964 	lun->scsi_lun = scsi_lun64_to_lun(lun_num);
2954c06356bSdh142964 
2964c06356bSdh142964 	ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
2974c06356bSdh142964 	bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
2984c06356bSdh142964 
2994c06356bSdh142964 	lun->target = tgt;
3004c06356bSdh142964 
3014c06356bSdh142964 	/*
3024c06356bSdh142964 	 * If this is the first tran_tgt_init, add this target to our list
3034c06356bSdh142964 	 */
3044c06356bSdh142964 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
3054c06356bSdh142964 		int target;
3064c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
3074c06356bSdh142964 			if (pwp->targets[target] != NULL) {
3084c06356bSdh142964 				continue;
3094c06356bSdh142964 			}
3104c06356bSdh142964 
3114c06356bSdh142964 			pwp->targets[target] = tgt;
3124c06356bSdh142964 			tgt->target_num = (uint16_t)target;
3134c06356bSdh142964 			break;
3144c06356bSdh142964 		}
3154c06356bSdh142964 
3164c06356bSdh142964 		if (target == pwp->max_dev) {
317c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3184c06356bSdh142964 			    "Target list full.");
3194c06356bSdh142964 			goto tgt_init_fail;
3204c06356bSdh142964 		}
3214c06356bSdh142964 	}
3224c06356bSdh142964 
3234c06356bSdh142964 	tgt->dip = sd->sd_dev;
32473a3eccdSDavid Hollister 	lun->sd = sd;
32573a3eccdSDavid Hollister 	list_insert_tail(&tgt->lun_list, lun);
3264c06356bSdh142964 
3274c06356bSdh142964 	if (!pmcs_assign_device(pwp, tgt)) {
3284c06356bSdh142964 		pmcs_release_scratch(pwp);
3294c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
3304c06356bSdh142964 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
3314c06356bSdh142964 		tgt->phy = NULL;
332c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3334c06356bSdh142964 		    "%s: pmcs_assign_device failed for target 0x%p",
3344c06356bSdh142964 		    __func__, (void *)tgt);
3354c06356bSdh142964 		goto tgt_init_fail;
3364c06356bSdh142964 	}
3374c06356bSdh142964 
3384c06356bSdh142964 	pmcs_release_scratch(pwp);
3394c06356bSdh142964 	tgt->ref_count++;
3404c06356bSdh142964 
3414c06356bSdh142964 	(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3424c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
3434c06356bSdh142964 
3444c06356bSdh142964 	/* SM-HBA */
3454c06356bSdh142964 	if (tgt->dtype == SATA) {
3464c06356bSdh142964 		/* TCR in PSARC/1997/281 opinion */
3474c06356bSdh142964 		(void) scsi_device_prop_update_string(sd,
3484c06356bSdh142964 		    SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
3494c06356bSdh142964 	}
3504c06356bSdh142964 
3514c06356bSdh142964 	tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
3524c06356bSdh142964 
3534c06356bSdh142964 	if (tgt->phy_addressable) {
3544c06356bSdh142964 		(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3554c06356bSdh142964 		    SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
3564c06356bSdh142964 	}
3574c06356bSdh142964 
3584c06356bSdh142964 	/* SM-HBA */
3594c06356bSdh142964 	(void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd);
360499cfd15SDavid Hollister 	/*
361499cfd15SDavid Hollister 	 * Make sure attached port and target port pm props are updated
362499cfd15SDavid Hollister 	 * By passing in 0s, we're not actually updating any values, but
363499cfd15SDavid Hollister 	 * the properties should now get updated on the node.
364499cfd15SDavid Hollister 	 */
3654c06356bSdh142964 
3664c06356bSdh142964 	mutex_exit(&tgt->statlock);
36773a3eccdSDavid Hollister 	pmcs_update_phy_pm_props(phyp, 0, 0, B_TRUE);
3684c06356bSdh142964 	pmcs_unlock_phy(phyp);
3694c06356bSdh142964 	mutex_exit(&pwp->lock);
3704c06356bSdh142964 	scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
3714c06356bSdh142964 	return (DDI_SUCCESS);
3724c06356bSdh142964 
3734c06356bSdh142964 tgt_init_fail:
374616875b4SDavid Hollister 	scsi_device_hba_private_set(sd, NULL);
3754c06356bSdh142964 	if (got_scratch) {
3764c06356bSdh142964 		pmcs_release_scratch(pwp);
3774c06356bSdh142964 	}
3784c06356bSdh142964 	if (lun) {
37973a3eccdSDavid Hollister 		list_remove(&tgt->lun_list, lun);
3804c06356bSdh142964 		ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
3814c06356bSdh142964 	}
3824c06356bSdh142964 	if (phyp) {
3834c06356bSdh142964 		mutex_exit(&tgt->statlock);
3844c06356bSdh142964 		pmcs_unlock_phy(phyp);
3854c06356bSdh142964 		/*
3864c06356bSdh142964 		 * phyp's ref count was incremented in pmcs_new_tport.
3874c06356bSdh142964 		 * We're failing configuration, we now need to decrement it.
3884c06356bSdh142964 		 */
3894c06356bSdh142964 		if (!IS_ROOT_PHY(phyp)) {
3904c06356bSdh142964 			pmcs_dec_phy_ref_count(phyp);
3914c06356bSdh142964 		}
3924c06356bSdh142964 		phyp->target = NULL;
3934c06356bSdh142964 	}
3944c06356bSdh142964 	if (tgt && tgt->ref_count == 0) {
3954c06356bSdh142964 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
3964c06356bSdh142964 	}
3974c06356bSdh142964 	if (pwp) {
3984c06356bSdh142964 		mutex_exit(&pwp->lock);
399c40ba10dSReed 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
400c40ba10dSReed 		    "%s: failed for @%s tgt 0x%p phy 0x%p", __func__, ua,
401c40ba10dSReed 		    (void *)tgt, (void *)phyp);
4024c06356bSdh142964 	}
4034c06356bSdh142964 	if (tgt_port) {
4044c06356bSdh142964 		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
4054c06356bSdh142964 	}
4064c06356bSdh142964 	return (DDI_FAILURE);
4074c06356bSdh142964 }
4084c06356bSdh142964 
4094c06356bSdh142964 static void
4104c06356bSdh142964 pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
4114c06356bSdh142964     scsi_hba_tran_t *tran, struct scsi_device *sd)
4124c06356bSdh142964 {
4134c06356bSdh142964 	_NOTE(ARGUNUSED(hba_dip, tgt_dip));
4144c06356bSdh142964 	pmcs_hw_t	*pwp;
4154c06356bSdh142964 	pmcs_lun_t	*lun;
4164c06356bSdh142964 	pmcs_xscsi_t	*target;
4174c06356bSdh142964 	char		*unit_address;
4184c06356bSdh142964 	pmcs_phy_t	*phyp;
4194c06356bSdh142964 
4204c06356bSdh142964 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
4214c06356bSdh142964 		pwp = TRAN2PMC(tran);
422c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
4234c06356bSdh142964 		    "%s: We don't enumerate devices on the HBA node", __func__);
4244c06356bSdh142964 		return;
4254c06356bSdh142964 	}
4264c06356bSdh142964 
4274c06356bSdh142964 	lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
4284c06356bSdh142964 
4294c06356bSdh142964 	ASSERT((lun != NULL) && (lun->target != NULL));
4304c06356bSdh142964 	ASSERT(lun->target->ref_count > 0);
4314c06356bSdh142964 
4324c06356bSdh142964 	target = lun->target;
4334c06356bSdh142964 	unit_address = lun->unit_address;
43473a3eccdSDavid Hollister 	list_remove(&target->lun_list, lun);
4354c06356bSdh142964 
4364c06356bSdh142964 	pwp = ITRAN2PMC(tran);
4374c06356bSdh142964 	mutex_enter(&pwp->lock);
4384c06356bSdh142964 	phyp = target->phy;
439ee13933aSSrikanth Suravajhala 	if (phyp) {
440ee13933aSSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
441ee13933aSSrikanth Suravajhala 	}
442ee13933aSSrikanth Suravajhala 	mutex_enter(&target->statlock);
4434c06356bSdh142964 
44473a3eccdSDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
44573a3eccdSDavid Hollister 	    "%s: for @%s tgt 0x%p phy 0x%p", __func__, unit_address,
44673a3eccdSDavid Hollister 	    (void *)target, (void *)phyp);
44773a3eccdSDavid Hollister 	ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address);
44873a3eccdSDavid Hollister 
449af685682SSrikanth, Ramana 	if (target->recover_wait) {
450af685682SSrikanth, Ramana 		mutex_exit(&target->statlock);
451ee13933aSSrikanth Suravajhala 		if (phyp) {
452ee13933aSSrikanth Suravajhala 			mutex_exit(&phyp->phy_lock);
453ee13933aSSrikanth Suravajhala 		}
454af685682SSrikanth, Ramana 		mutex_exit(&pwp->lock);
455af685682SSrikanth, Ramana 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, "%s: "
456af685682SSrikanth, Ramana 		    "Target 0x%p in device state recovery, fail tran_tgt_free",
457af685682SSrikanth, Ramana 		    __func__, (void *)target);
458af685682SSrikanth, Ramana 		return;
459af685682SSrikanth, Ramana 	}
460af685682SSrikanth, Ramana 
4614c06356bSdh142964 	/*
4624c06356bSdh142964 	 * If this target still has a PHY pointer and that PHY's target pointer
4634c06356bSdh142964 	 * has been cleared, then that PHY has been reaped. In that case, there
4644c06356bSdh142964 	 * would be no need to decrement the reference count
4654c06356bSdh142964 	 */
4664c06356bSdh142964 	if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
4674c06356bSdh142964 		pmcs_dec_phy_ref_count(phyp);
4684c06356bSdh142964 	}
4694c06356bSdh142964 
4704c06356bSdh142964 	if (--target->ref_count == 0) {
4714c06356bSdh142964 		/*
4724c06356bSdh142964 		 * Remove this target from our list.  The target soft
4734c06356bSdh142964 		 * state will remain, and the device will remain registered
4744c06356bSdh142964 		 * with the hardware unless/until we're told the device
4754c06356bSdh142964 		 * physically went away.
4764c06356bSdh142964 		 */
477c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
4784c06356bSdh142964 		    "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
4794c06356bSdh142964 		    target->target_num);
4804c06356bSdh142964 		pwp->targets[target->target_num] = NULL;
4814c06356bSdh142964 		target->target_num = PMCS_INVALID_TARGET_NUM;
4824c06356bSdh142964 		/*
4834c06356bSdh142964 		 * If the target still has a PHY pointer, break the linkage
4844c06356bSdh142964 		 */
4854c06356bSdh142964 		if (phyp) {
4864c06356bSdh142964 			phyp->target = NULL;
4874c06356bSdh142964 		}
4884c06356bSdh142964 		target->phy = NULL;
489ee13933aSSrikanth Suravajhala 		if (phyp) {
490ee13933aSSrikanth Suravajhala 			mutex_exit(&phyp->phy_lock);
491ee13933aSSrikanth Suravajhala 		}
492*654ea33cSSrikanth Suravajhala 		pmcs_destroy_target(target);
493*654ea33cSSrikanth Suravajhala 	} else {
494*654ea33cSSrikanth Suravajhala 		mutex_exit(&target->statlock);
495*654ea33cSSrikanth Suravajhala 		if (phyp) {
496*654ea33cSSrikanth Suravajhala 			mutex_exit(&phyp->phy_lock);
497*654ea33cSSrikanth Suravajhala 		}
498*654ea33cSSrikanth Suravajhala 	}
499*654ea33cSSrikanth Suravajhala 
5004c06356bSdh142964 	mutex_exit(&pwp->lock);
5014c06356bSdh142964 }
5024c06356bSdh142964 
5034c06356bSdh142964 static int
5044c06356bSdh142964 pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
5054c06356bSdh142964 {
5064c06356bSdh142964 	pmcs_cmd_t *sp = PKT2CMD(pkt);
5074c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
5084c06356bSdh142964 	pmcs_xscsi_t *xp;
5094c06356bSdh142964 	boolean_t blocked;
5104c06356bSdh142964 	uint32_t hba_state;
5114c06356bSdh142964 
512c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
513c3bc407cSdh142964 	    "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt,
5144c06356bSdh142964 	    (void *)scsi_address_device(&pkt->pkt_address),
5154c06356bSdh142964 	    pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
5164c06356bSdh142964 
5174c06356bSdh142964 	if (pkt->pkt_flags & FLAG_NOINTR) {
518c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
519c3bc407cSdh142964 		    "%s: nointr pkt", __func__);
5204c06356bSdh142964 		return (TRAN_BADPKT);
5214c06356bSdh142964 	}
5224c06356bSdh142964 
5234c06356bSdh142964 	sp->cmd_tag = 0;
5244c06356bSdh142964 	pkt->pkt_state = pkt->pkt_statistics = 0;
5254c06356bSdh142964 	pkt->pkt_reason = CMD_INCOMPLETE;
5264c06356bSdh142964 
5274c06356bSdh142964 	mutex_enter(&pwp->lock);
5284c06356bSdh142964 	hba_state = pwp->state;
5294c06356bSdh142964 	blocked = pwp->blocked;
5304c06356bSdh142964 	mutex_exit(&pwp->lock);
5314c06356bSdh142964 
5324c06356bSdh142964 	if (hba_state != STATE_RUNNING) {
533c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
534c3bc407cSdh142964 		    "%s: hba dead", __func__);
5354c06356bSdh142964 		return (TRAN_FATAL_ERROR);
5364c06356bSdh142964 	}
5374c06356bSdh142964 
5384c06356bSdh142964 	xp = pmcs_addr2xp(ap, NULL, sp);
5394c06356bSdh142964 	if (xp == NULL) {
540c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
5414c06356bSdh142964 		    "%s: dropping due to null target", __func__);
542b18a19c2SJesse Butler 		goto dead_target;
5434c06356bSdh142964 	}
5444c06356bSdh142964 	ASSERT(mutex_owned(&xp->statlock));
5454c06356bSdh142964 
5464c06356bSdh142964 	/*
547b18a19c2SJesse Butler 	 * First, check to see if the device is gone.
5484c06356bSdh142964 	 */
549b18a19c2SJesse Butler 	if (xp->dev_gone) {
550601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5514c06356bSdh142964 		mutex_exit(&xp->statlock);
552c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp,
553b18a19c2SJesse Butler 		    "%s: dropping due to dead target 0x%p",
5544c06356bSdh142964 		    __func__, (void *)xp);
555b18a19c2SJesse Butler 		goto dead_target;
5564c06356bSdh142964 	}
5574c06356bSdh142964 
5584c06356bSdh142964 	/*
5594c06356bSdh142964 	 * If we're blocked (quiesced) just return.
5604c06356bSdh142964 	 */
5614c06356bSdh142964 	if (blocked) {
562c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
563c3bc407cSdh142964 		    "%s: hba blocked", __func__);
564601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5654c06356bSdh142964 		mutex_exit(&xp->statlock);
5664c06356bSdh142964 		mutex_enter(&xp->wqlock);
5674c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5684c06356bSdh142964 		mutex_exit(&xp->wqlock);
5694c06356bSdh142964 		return (TRAN_ACCEPT);
5704c06356bSdh142964 	}
5714c06356bSdh142964 
5724c06356bSdh142964 	/*
5734c06356bSdh142964 	 * If we're draining or resetting, queue and return.
5744c06356bSdh142964 	 */
5754c06356bSdh142964 	if (xp->draining || xp->resetting || xp->recover_wait) {
576601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5774c06356bSdh142964 		mutex_exit(&xp->statlock);
5784c06356bSdh142964 		mutex_enter(&xp->wqlock);
5794c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5804c06356bSdh142964 		mutex_exit(&xp->wqlock);
581c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
5824c06356bSdh142964 		    "%s: draining/resetting/recovering (cnt %u)",
5834c06356bSdh142964 		    __func__, xp->actv_cnt);
5844c06356bSdh142964 		/*
5854c06356bSdh142964 		 * By the time we get here, draining or
5864c06356bSdh142964 		 * resetting may have come and gone, not
5874c06356bSdh142964 		 * yet noticing that we had put something
5884c06356bSdh142964 		 * on the wait queue, so schedule a worker
5894c06356bSdh142964 		 * to look at this later.
5904c06356bSdh142964 		 */
5914c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
5924c06356bSdh142964 		return (TRAN_ACCEPT);
5934c06356bSdh142964 	}
594601c90f1SSrikanth, Ramana 
595601c90f1SSrikanth, Ramana 	xp->actv_pkts++;
5964c06356bSdh142964 	mutex_exit(&xp->statlock);
5974c06356bSdh142964 
5984c06356bSdh142964 	/*
5994c06356bSdh142964 	 * Queue this command to the tail of the wait queue.
6004c06356bSdh142964 	 * This keeps us getting commands out of order.
6014c06356bSdh142964 	 */
6024c06356bSdh142964 	mutex_enter(&xp->wqlock);
6034c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
6044c06356bSdh142964 	mutex_exit(&xp->wqlock);
6054c06356bSdh142964 
6064c06356bSdh142964 	/*
6074c06356bSdh142964 	 * Now run the queue for this device.
6084c06356bSdh142964 	 */
6094c06356bSdh142964 	(void) pmcs_scsa_wq_run_one(pwp, xp);
6104c06356bSdh142964 
6114c06356bSdh142964 	return (TRAN_ACCEPT);
6124c06356bSdh142964 
613b18a19c2SJesse Butler dead_target:
6144c06356bSdh142964 	pkt->pkt_state = STATE_GOT_BUS;
6154c06356bSdh142964 	pkt->pkt_reason = CMD_DEV_GONE;
6164c06356bSdh142964 	mutex_enter(&pwp->cq_lock);
6174c06356bSdh142964 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6184c06356bSdh142964 	PMCS_CQ_RUN_LOCKED(pwp);
6194c06356bSdh142964 	mutex_exit(&pwp->cq_lock);
6204c06356bSdh142964 	return (TRAN_ACCEPT);
6214c06356bSdh142964 }
6224c06356bSdh142964 
62302b04f6eSSrikanth, Ramana /* Return code 1 = Success */
6244c06356bSdh142964 static int
6254c06356bSdh142964 pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
6264c06356bSdh142964 {
6274c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
62802b04f6eSSrikanth, Ramana 	pmcs_cmd_t *sp = NULL;
62902b04f6eSSrikanth, Ramana 	pmcs_xscsi_t *xp = NULL;
63002b04f6eSSrikanth, Ramana 	pmcs_phy_t *pptr = NULL;
63102b04f6eSSrikanth, Ramana 	pmcs_lun_t *pmcs_lun = (pmcs_lun_t *)
63202b04f6eSSrikanth, Ramana 	    scsi_device_hba_private_get(scsi_address_device(ap));
6334c06356bSdh142964 	uint32_t tag;
6344c06356bSdh142964 	uint64_t lun;
6354c06356bSdh142964 	pmcwork_t *pwrk;
6364c06356bSdh142964 
6374c06356bSdh142964 	mutex_enter(&pwp->lock);
6384c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
6394c06356bSdh142964 		mutex_exit(&pwp->lock);
640c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
641c3bc407cSdh142964 		    "%s: hba dead", __func__);
6424c06356bSdh142964 		return (0);
6434c06356bSdh142964 	}
6444c06356bSdh142964 	mutex_exit(&pwp->lock);
6454c06356bSdh142964 
64602b04f6eSSrikanth, Ramana 	if (pkt == NULL) {
64702b04f6eSSrikanth, Ramana 		if (pmcs_lun == NULL) {
64802b04f6eSSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
64902b04f6eSSrikanth, Ramana 			    "No pmcs_lun_t struct to do ABORT_ALL", __func__);
65002b04f6eSSrikanth, Ramana 			return (0);
65102b04f6eSSrikanth, Ramana 		}
65202b04f6eSSrikanth, Ramana 		xp = pmcs_lun->target;
65302b04f6eSSrikanth, Ramana 		if (xp != NULL) {
65402b04f6eSSrikanth, Ramana 			pptr = xp->phy;
65502b04f6eSSrikanth, Ramana 		}
65602b04f6eSSrikanth, Ramana 		if (pptr == NULL) {
65702b04f6eSSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: pkt is "
65802b04f6eSSrikanth, Ramana 			    "NULL. No tgt/phy to do ABORT_ALL", __func__);
65902b04f6eSSrikanth, Ramana 			return (0);
66002b04f6eSSrikanth, Ramana 		}
66102b04f6eSSrikanth, Ramana 		pmcs_lock_phy(pptr);
66202b04f6eSSrikanth, Ramana 		if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
66302b04f6eSSrikanth, Ramana 			pptr->abort_pending = 1;
66402b04f6eSSrikanth, Ramana 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
66502b04f6eSSrikanth, Ramana 		}
66602b04f6eSSrikanth, Ramana 		pmcs_unlock_phy(pptr);
66702b04f6eSSrikanth, Ramana 		return (1);
66802b04f6eSSrikanth, Ramana 	}
66902b04f6eSSrikanth, Ramana 
67002b04f6eSSrikanth, Ramana 	sp = PKT2CMD(pkt);
67102b04f6eSSrikanth, Ramana 	xp = sp->cmd_target;
67202b04f6eSSrikanth, Ramana 
6734c06356bSdh142964 	if (sp->cmd_lun) {
6744c06356bSdh142964 		lun = sp->cmd_lun->lun_num;
6754c06356bSdh142964 	} else {
6764c06356bSdh142964 		lun = 0;
6774c06356bSdh142964 	}
6784c06356bSdh142964 	if (xp == NULL) {
6794c06356bSdh142964 		return (0);
6804c06356bSdh142964 	}
6814c06356bSdh142964 
6824c06356bSdh142964 	/*
6834c06356bSdh142964 	 * See if we have a real work structure associated with this cmd.
6844c06356bSdh142964 	 */
685978d7443SSrikanth Suravajhala 	pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
6864c06356bSdh142964 	if (pwrk && pwrk->arg == sp) {
6874c06356bSdh142964 		tag = pwrk->htag;
6884c06356bSdh142964 		pptr = pwrk->phy;
6894c06356bSdh142964 		pwrk->timer = 0;	/* we don't time this here */
6904c06356bSdh142964 		ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
6914c06356bSdh142964 		mutex_exit(&pwrk->lock);
6924c06356bSdh142964 		pmcs_lock_phy(pptr);
6934c06356bSdh142964 		if (pptr->dtype == SAS) {
6944c06356bSdh142964 			if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
6954c06356bSdh142964 			    NULL)) {
6964c06356bSdh142964 				pptr->abort_pending = 1;
6974c06356bSdh142964 				pmcs_unlock_phy(pptr);
6984c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6994c06356bSdh142964 				return (0);
7004c06356bSdh142964 			}
7014c06356bSdh142964 		} else {
7024c06356bSdh142964 			/*
7034c06356bSdh142964 			 * XXX: Was the command that was active an
7044c06356bSdh142964 			 * NCQ I/O command?
7054c06356bSdh142964 			 */
7064c06356bSdh142964 			pptr->need_rl_ext = 1;
7074c06356bSdh142964 			if (pmcs_sata_abort_ncq(pwp, pptr)) {
7084c06356bSdh142964 				pptr->abort_pending = 1;
7094c06356bSdh142964 				pmcs_unlock_phy(pptr);
7104c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7114c06356bSdh142964 				return (0);
7124c06356bSdh142964 			}
7134c06356bSdh142964 		}
7144c06356bSdh142964 		pptr->abort_pending = 1;
7154c06356bSdh142964 		pmcs_unlock_phy(pptr);
7164c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7174c06356bSdh142964 		return (1);
7184c06356bSdh142964 	}
7194c06356bSdh142964 	if (pwrk) {
7204c06356bSdh142964 		mutex_exit(&pwrk->lock);
7214c06356bSdh142964 	}
7224c06356bSdh142964 	/*
7234c06356bSdh142964 	 * Okay, those weren't the droids we were looking for.
7244c06356bSdh142964 	 * See if the command is on any of the wait queues.
7254c06356bSdh142964 	 */
7264c06356bSdh142964 	mutex_enter(&xp->wqlock);
7274c06356bSdh142964 	sp = NULL;
7284c06356bSdh142964 	STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
7294c06356bSdh142964 		if (sp == PKT2CMD(pkt)) {
7304c06356bSdh142964 			STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
7314c06356bSdh142964 			break;
7324c06356bSdh142964 		}
7334c06356bSdh142964 	}
7344c06356bSdh142964 	mutex_exit(&xp->wqlock);
7354c06356bSdh142964 	if (sp) {
7364c06356bSdh142964 		pkt->pkt_reason = CMD_ABORTED;
7374c06356bSdh142964 		pkt->pkt_statistics |= STAT_ABORTED;
7384c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
7394c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
7404c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
7414c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
7424c06356bSdh142964 		return (1);
7434c06356bSdh142964 	}
7444c06356bSdh142964 	return (0);
7454c06356bSdh142964 }
7464c06356bSdh142964 
7474c06356bSdh142964 /*
7484c06356bSdh142964  * SCSA reset functions
7494c06356bSdh142964  */
7504c06356bSdh142964 static int
7514c06356bSdh142964 pmcs_scsa_reset(struct scsi_address *ap, int level)
7524c06356bSdh142964 {
7534c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
7544c06356bSdh142964 	pmcs_phy_t *pptr;
7554c06356bSdh142964 	pmcs_xscsi_t *xp;
7564c06356bSdh142964 	uint64_t lun = (uint64_t)-1, *lp = NULL;
7574c06356bSdh142964 	int rval;
7584c06356bSdh142964 
7594c06356bSdh142964 	mutex_enter(&pwp->lock);
7604c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
7614c06356bSdh142964 		mutex_exit(&pwp->lock);
762c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
763c3bc407cSdh142964 		    "%s: hba dead", __func__);
7644c06356bSdh142964 		return (0);
7654c06356bSdh142964 	}
7664c06356bSdh142964 	mutex_exit(&pwp->lock);
7674c06356bSdh142964 
7684c06356bSdh142964 	switch (level)  {
7694c06356bSdh142964 	case RESET_ALL:
7704c06356bSdh142964 		rval = 0;
7714c06356bSdh142964 		break;
7724c06356bSdh142964 	case RESET_LUN:
7734c06356bSdh142964 		/*
7744c06356bSdh142964 		 * Point lp at lun so that pmcs_addr2xp
7754c06356bSdh142964 		 * will fill out the 64 bit lun number.
7764c06356bSdh142964 		 */
7774c06356bSdh142964 		lp = &lun;
7784c06356bSdh142964 		/* FALLTHROUGH */
7794c06356bSdh142964 	case RESET_TARGET:
7804c06356bSdh142964 		xp = pmcs_addr2xp(ap, lp, NULL);
7814c06356bSdh142964 		if (xp == NULL) {
782c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7834c06356bSdh142964 			    "%s: no xp found for this scsi address", __func__);
7844c06356bSdh142964 			return (0);
7854c06356bSdh142964 		}
7864c06356bSdh142964 
787b18a19c2SJesse Butler 		if (xp->dev_gone) {
7884c06356bSdh142964 			mutex_exit(&xp->statlock);
789c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
7904c06356bSdh142964 			    "%s: Target 0x%p has gone away", __func__,
7914c06356bSdh142964 			    (void *)xp);
7924c06356bSdh142964 			return (0);
7934c06356bSdh142964 		}
7944c06356bSdh142964 
7954c06356bSdh142964 		/*
7964c06356bSdh142964 		 * If we're already performing this action, or if device
7974c06356bSdh142964 		 * state recovery is already running, just return failure.
7984c06356bSdh142964 		 */
7994c06356bSdh142964 		if (xp->resetting || xp->recover_wait) {
8004c06356bSdh142964 			mutex_exit(&xp->statlock);
8014c06356bSdh142964 			return (0);
8024c06356bSdh142964 		}
8034c06356bSdh142964 		xp->reset_wait = 0;
8044c06356bSdh142964 		xp->reset_success = 0;
8054c06356bSdh142964 		xp->resetting = 1;
8064c06356bSdh142964 		pptr = xp->phy;
8074c06356bSdh142964 		mutex_exit(&xp->statlock);
8084c06356bSdh142964 
8094c06356bSdh142964 		if (pmcs_reset_dev(pwp, pptr, lun)) {
8104c06356bSdh142964 			rval = 0;
8114c06356bSdh142964 		} else {
8124c06356bSdh142964 			rval = 1;
8134c06356bSdh142964 		}
8144c06356bSdh142964 
8154c06356bSdh142964 		mutex_enter(&xp->statlock);
8164c06356bSdh142964 		if (rval == 1) {
8174c06356bSdh142964 			xp->reset_success = 1;
8184c06356bSdh142964 		}
8194c06356bSdh142964 		if (xp->reset_wait) {
8204c06356bSdh142964 			xp->reset_wait = 0;
8214c06356bSdh142964 			cv_signal(&xp->reset_cv);
8224c06356bSdh142964 		}
8234c06356bSdh142964 		xp->resetting = 0;
8244c06356bSdh142964 		mutex_exit(&xp->statlock);
8254c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
8264c06356bSdh142964 		break;
8274c06356bSdh142964 	default:
8284c06356bSdh142964 		rval = 0;
8294c06356bSdh142964 		break;
8304c06356bSdh142964 	}
8314c06356bSdh142964 
8324c06356bSdh142964 	return (rval);
8334c06356bSdh142964 }
8344c06356bSdh142964 
8354c06356bSdh142964 static int
8364c06356bSdh142964 pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
8374c06356bSdh142964     void (*callback)(caddr_t), caddr_t arg)
8384c06356bSdh142964 {
8394c06356bSdh142964 	pmcs_hw_t *pwp = ADDR2PMC(ap);
8404c06356bSdh142964 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
8414c06356bSdh142964 	    &pwp->lock, &pwp->reset_notify_listf));
8424c06356bSdh142964 }
8434c06356bSdh142964 
8444c06356bSdh142964 
8454c06356bSdh142964 static int
8464c06356bSdh142964 pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
8474c06356bSdh142964 {
8484c06356bSdh142964 	_NOTE(ARGUNUSED(val, tonly));
8494c06356bSdh142964 	int cidx, rval = 0;
8504c06356bSdh142964 	pmcs_xscsi_t *xp;
8514c06356bSdh142964 
8524c06356bSdh142964 	cidx = scsi_hba_lookup_capstr(cap);
8534c06356bSdh142964 	if (cidx == -1) {
8544c06356bSdh142964 		return (-1);
8554c06356bSdh142964 	}
8564c06356bSdh142964 
8574c06356bSdh142964 	xp = pmcs_addr2xp(ap, NULL, NULL);
8584c06356bSdh142964 	if (xp == NULL) {
8594c06356bSdh142964 		return (-1);
8604c06356bSdh142964 	}
8614c06356bSdh142964 
8624c06356bSdh142964 	switch (cidx) {
8634c06356bSdh142964 	case SCSI_CAP_DMA_MAX:
8644c06356bSdh142964 	case SCSI_CAP_INITIATOR_ID:
8654c06356bSdh142964 		if (set == 0) {
8664c06356bSdh142964 			rval = INT_MAX;	/* argh */
8674c06356bSdh142964 		}
8684c06356bSdh142964 		break;
8694c06356bSdh142964 	case SCSI_CAP_DISCONNECT:
8704c06356bSdh142964 	case SCSI_CAP_SYNCHRONOUS:
8714c06356bSdh142964 	case SCSI_CAP_WIDE_XFER:
8724c06356bSdh142964 	case SCSI_CAP_PARITY:
8734c06356bSdh142964 	case SCSI_CAP_ARQ:
8744c06356bSdh142964 	case SCSI_CAP_UNTAGGED_QING:
8754c06356bSdh142964 		if (set == 0) {
8764c06356bSdh142964 			rval = 1;
8774c06356bSdh142964 		}
8784c06356bSdh142964 		break;
8794c06356bSdh142964 
8804c06356bSdh142964 	case SCSI_CAP_TAGGED_QING:
8814c06356bSdh142964 		rval = 1;
8824c06356bSdh142964 		break;
8834c06356bSdh142964 
8844c06356bSdh142964 	case SCSI_CAP_MSG_OUT:
8854c06356bSdh142964 	case SCSI_CAP_RESET_NOTIFICATION:
8864c06356bSdh142964 	case SCSI_CAP_QFULL_RETRIES:
8874c06356bSdh142964 	case SCSI_CAP_QFULL_RETRY_INTERVAL:
8884c06356bSdh142964 		break;
8894c06356bSdh142964 	case SCSI_CAP_SCSI_VERSION:
8904c06356bSdh142964 		if (set == 0) {
8914c06356bSdh142964 			rval = SCSI_VERSION_3;
8924c06356bSdh142964 		}
8934c06356bSdh142964 		break;
8944c06356bSdh142964 	case SCSI_CAP_INTERCONNECT_TYPE:
8954c06356bSdh142964 		if (set) {
8964c06356bSdh142964 			break;
8974c06356bSdh142964 		}
8984c06356bSdh142964 		if (xp->phy_addressable) {
8994c06356bSdh142964 			rval = INTERCONNECT_SATA;
9004c06356bSdh142964 		} else {
9014c06356bSdh142964 			rval = INTERCONNECT_SAS;
9024c06356bSdh142964 		}
9034c06356bSdh142964 		break;
9044c06356bSdh142964 	case SCSI_CAP_CDB_LEN:
9054c06356bSdh142964 		if (set == 0) {
9064c06356bSdh142964 			rval = 16;
9074c06356bSdh142964 		}
9084c06356bSdh142964 		break;
9094c06356bSdh142964 	case SCSI_CAP_LUN_RESET:
9104c06356bSdh142964 		if (set) {
9114c06356bSdh142964 			break;
9124c06356bSdh142964 		}
9134c06356bSdh142964 		if (xp->dtype == SATA) {
9144c06356bSdh142964 			rval = 0;
9154c06356bSdh142964 		} else {
9164c06356bSdh142964 			rval = 1;
9174c06356bSdh142964 		}
9184c06356bSdh142964 		break;
9194c06356bSdh142964 	default:
9204c06356bSdh142964 		rval = -1;
9214c06356bSdh142964 		break;
9224c06356bSdh142964 	}
9234c06356bSdh142964 	mutex_exit(&xp->statlock);
924c3bc407cSdh142964 	pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL,
9254c06356bSdh142964 	    "%s: cap %s val %d set %d rval %d",
9264c06356bSdh142964 	    __func__, cap, val, set, rval);
9274c06356bSdh142964 	return (rval);
9284c06356bSdh142964 }
9294c06356bSdh142964 
9304c06356bSdh142964 /*
9314c06356bSdh142964  * Returns with statlock held if the xp is found.
9324c06356bSdh142964  * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
9334c06356bSdh142964  */
9344c06356bSdh142964 static pmcs_xscsi_t *
9354c06356bSdh142964 pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
9364c06356bSdh142964 {
9374c06356bSdh142964 	pmcs_xscsi_t *xp;
9384c06356bSdh142964 	pmcs_lun_t *lun = (pmcs_lun_t *)
9394c06356bSdh142964 	    scsi_device_hba_private_get(scsi_address_device(ap));
9404c06356bSdh142964 
9414c06356bSdh142964 	if ((lun == NULL) || (lun->target == NULL)) {
9424c06356bSdh142964 		return (NULL);
9434c06356bSdh142964 	}
9444c06356bSdh142964 	xp = lun->target;
9454c06356bSdh142964 	mutex_enter(&xp->statlock);
9464c06356bSdh142964 
947b18a19c2SJesse Butler 	if (xp->dev_gone || (xp->phy == NULL)) {
948601c90f1SSrikanth, Ramana 		/*
949601c90f1SSrikanth, Ramana 		 * This may be a retried packet, so it's possible cmd_target
950601c90f1SSrikanth, Ramana 		 * and cmd_lun may still be populated.  Clear them.
951601c90f1SSrikanth, Ramana 		 */
952601c90f1SSrikanth, Ramana 		if (sp != NULL) {
953601c90f1SSrikanth, Ramana 			sp->cmd_target = NULL;
954601c90f1SSrikanth, Ramana 			sp->cmd_lun = NULL;
955601c90f1SSrikanth, Ramana 		}
9564c06356bSdh142964 		mutex_exit(&xp->statlock);
9574c06356bSdh142964 		return (NULL);
9584c06356bSdh142964 	}
9594c06356bSdh142964 
9604c06356bSdh142964 	if (sp != NULL) {
9614c06356bSdh142964 		sp->cmd_target = xp;
9624c06356bSdh142964 		sp->cmd_lun = lun;
9634c06356bSdh142964 	}
9644c06356bSdh142964 	if (lp) {
9654c06356bSdh142964 		*lp = lun->lun_num;
9664c06356bSdh142964 	}
9674c06356bSdh142964 	return (xp);
9684c06356bSdh142964 }
9694c06356bSdh142964 
9704c06356bSdh142964 static int
9714c06356bSdh142964 pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
9724c06356bSdh142964 {
9734c06356bSdh142964 	int r;
9744c06356bSdh142964 	if (cap == NULL) {
9754c06356bSdh142964 		return (-1);
9764c06356bSdh142964 	}
9774c06356bSdh142964 	r = pmcs_cap(ap, cap, 0, whom, 0);
9784c06356bSdh142964 	return (r);
9794c06356bSdh142964 }
9804c06356bSdh142964 
9814c06356bSdh142964 static int
9824c06356bSdh142964 pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
9834c06356bSdh142964 {
9844c06356bSdh142964 	int r;
9854c06356bSdh142964 	if (cap == NULL) {
9864c06356bSdh142964 		return (-1);
9874c06356bSdh142964 	}
9884c06356bSdh142964 	r = pmcs_cap(ap, cap, value, whom, 1);
9894c06356bSdh142964 	return (r);
9904c06356bSdh142964 }
9914c06356bSdh142964 
9924c06356bSdh142964 static int
9934c06356bSdh142964 pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
9944c06356bSdh142964     caddr_t cbarg)
9954c06356bSdh142964 {
9964c06356bSdh142964 	_NOTE(ARGUNUSED(callback, cbarg));
9974c06356bSdh142964 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
9984c06356bSdh142964 
9994c06356bSdh142964 	bzero(sp, sizeof (pmcs_cmd_t));
10004c06356bSdh142964 	sp->cmd_pkt = pkt;
10014c06356bSdh142964 	return (0);
10024c06356bSdh142964 }
10034c06356bSdh142964 
10044c06356bSdh142964 static void
10054c06356bSdh142964 pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
10064c06356bSdh142964 {
10074c06356bSdh142964 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
10084c06356bSdh142964 	sp->cmd_target = NULL;
10094c06356bSdh142964 	sp->cmd_lun = NULL;
10104c06356bSdh142964 }
10114c06356bSdh142964 
10124c06356bSdh142964 static int
101396c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt)
10144c06356bSdh142964 {
10154c06356bSdh142964 	struct pmcwork *pwrk;
10163be32c0fSJesse Butler 	pmcs_iport_t *iport;
10174c06356bSdh142964 	const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
10184c06356bSdh142964 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
10194c06356bSdh142964 	uint64_t wwn;
102096c4a178SChris Horne 	pmcs_hw_t *pwp;
10214c06356bSdh142964 	pmcs_phy_t *pptr;
10224c06356bSdh142964 	pmcs_xscsi_t *xp;
10234c06356bSdh142964 	uint_t reqsz, rspsz, will_retry;
10244c06356bSdh142964 	int result;
10254c06356bSdh142964 
102696c4a178SChris Horne 	pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private;
102796c4a178SChris Horne 	bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
10284c06356bSdh142964 
1029c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
1030c3bc407cSdh142964 	    "%s: starting for wwn 0x%" PRIx64, __func__, wwn);
10314c06356bSdh142964 
103296c4a178SChris Horne 	will_retry = smp_pkt->smp_pkt_will_retry;
10334c06356bSdh142964 
10344c06356bSdh142964 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
103596c4a178SChris Horne 	reqsz = smp_pkt->smp_pkt_reqsize;
10364c06356bSdh142964 	if (reqsz > SAS_SMP_MAX_PAYLOAD) {
10374c06356bSdh142964 		reqsz = SAS_SMP_MAX_PAYLOAD;
10384c06356bSdh142964 	}
103996c4a178SChris Horne 	(void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz);
10404c06356bSdh142964 
104196c4a178SChris Horne 	rspsz = smp_pkt->smp_pkt_rspsize;
10424c06356bSdh142964 	if (rspsz > SAS_SMP_MAX_PAYLOAD) {
10434c06356bSdh142964 		rspsz = SAS_SMP_MAX_PAYLOAD;
10444c06356bSdh142964 	}
10454c06356bSdh142964 
10464c06356bSdh142964 	/*
10474c06356bSdh142964 	 * The request size from the SMP driver always includes 4 bytes
10484c06356bSdh142964 	 * for the CRC. The PMCS chip, however, doesn't want to see those
10494c06356bSdh142964 	 * counts as part of the transfer size.
10504c06356bSdh142964 	 */
10514c06356bSdh142964 	reqsz -= 4;
10524c06356bSdh142964 
10534c06356bSdh142964 	pptr = pmcs_find_phy_by_wwn(pwp, wwn);
10544c06356bSdh142964 	/* PHY is now locked */
10554c06356bSdh142964 	if (pptr == NULL || pptr->dtype != EXPANDER) {
10564c06356bSdh142964 		if (pptr) {
10574c06356bSdh142964 			pmcs_unlock_phy(pptr);
10584c06356bSdh142964 		}
10594c06356bSdh142964 		pmcs_release_scratch(pwp);
10609aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1061c3bc407cSdh142964 		    "%s: could not find phy", __func__);
106296c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ENXIO;
10634c06356bSdh142964 		return (DDI_FAILURE);
10644c06356bSdh142964 	}
10654c06356bSdh142964 
10669aed1621SDavid Hollister 	if ((pptr->iport == NULL) || !pptr->valid_device_id) {
10679aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
10689aed1621SDavid Hollister 		pmcs_release_scratch(pwp);
10699aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
10709aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
10719aed1621SDavid Hollister 		smp_pkt->smp_pkt_reason = ENXIO;
10729aed1621SDavid Hollister 		return (DDI_FAILURE);
10739aed1621SDavid Hollister 	}
10749aed1621SDavid Hollister 
10754c06356bSdh142964 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
10764c06356bSdh142964 	if (pwrk == NULL) {
10774c06356bSdh142964 		pmcs_unlock_phy(pptr);
10784c06356bSdh142964 		pmcs_release_scratch(pwp);
1079c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
10804c06356bSdh142964 		    "%s: could not get work structure", __func__);
108196c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY;
10824c06356bSdh142964 		return (DDI_FAILURE);
10834c06356bSdh142964 	}
10844c06356bSdh142964 
10854c06356bSdh142964 	pwrk->arg = msg;
10864c06356bSdh142964 	pwrk->dtype = EXPANDER;
10874c06356bSdh142964 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10884c06356bSdh142964 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10894c06356bSdh142964 	if (ptr == NULL) {
10904c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
10914c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10924c06356bSdh142964 		pmcs_unlock_phy(pptr);
10934c06356bSdh142964 		pmcs_release_scratch(pwp);
1094c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1095c3bc407cSdh142964 		    "%s: could not get IQ entry", __func__);
109696c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY;
10974c06356bSdh142964 		return (DDI_FAILURE);
10984c06356bSdh142964 	}
10994c06356bSdh142964 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
11004c06356bSdh142964 	msg[1] = LE_32(pwrk->htag);
11014c06356bSdh142964 	msg[2] = LE_32(pptr->device_id);
11024c06356bSdh142964 	msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
11034c06356bSdh142964 	msg[8] = LE_32(DWORD0(pwp->scratch_dma));
11044c06356bSdh142964 	msg[9] = LE_32(DWORD1(pwp->scratch_dma));
11054c06356bSdh142964 	msg[10] = LE_32(reqsz);
11064c06356bSdh142964 	msg[11] = 0;
11074c06356bSdh142964 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
11084c06356bSdh142964 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
11094c06356bSdh142964 	msg[14] = LE_32(rspsz);
11104c06356bSdh142964 	msg[15] = 0;
11114c06356bSdh142964 
11124c06356bSdh142964 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
11136745c559SJesse Butler 
11143be32c0fSJesse Butler 	pmcs_hold_iport(pptr->iport);
11153be32c0fSJesse Butler 	iport = pptr->iport;
11163be32c0fSJesse Butler 	pmcs_smp_acquire(iport);
11174c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
11184c06356bSdh142964 	htag = pwrk->htag;
11194c06356bSdh142964 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
11204c06356bSdh142964 	pmcs_unlock_phy(pptr);
112196c4a178SChris Horne 	WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result);
11224c06356bSdh142964 	pmcs_pwork(pwp, pwrk);
11233be32c0fSJesse Butler 	pmcs_smp_release(iport);
11243be32c0fSJesse Butler 	pmcs_rele_iport(iport);
1125601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
11266745c559SJesse Butler 
11274c06356bSdh142964 	if (result) {
11284c06356bSdh142964 		pmcs_timed_out(pwp, htag, __func__);
11294c06356bSdh142964 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
11309aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11314c06356bSdh142964 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
11324c06356bSdh142964 			    __func__, htag);
11334c06356bSdh142964 		} else {
11349aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11354c06356bSdh142964 			    "%s: Issuing SMP ABORT for htag 0x%08x",
11364c06356bSdh142964 			    __func__, htag);
11374c06356bSdh142964 		}
11384c06356bSdh142964 		pmcs_unlock_phy(pptr);
11394c06356bSdh142964 		pmcs_release_scratch(pwp);
114096c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ETIMEDOUT;
11414c06356bSdh142964 		return (DDI_FAILURE);
11424c06356bSdh142964 	}
11434c06356bSdh142964 	status = LE_32(msg[2]);
11444c06356bSdh142964 	if (status == PMCOUT_STATUS_OVERFLOW) {
11454c06356bSdh142964 		status = PMCOUT_STATUS_OK;
114696c4a178SChris Horne 		smp_pkt->smp_pkt_reason = EOVERFLOW;
11474c06356bSdh142964 	}
11484c06356bSdh142964 	if (status != PMCOUT_STATUS_OK) {
11494c06356bSdh142964 		const char *emsg = pmcs_status_str(status);
11504c06356bSdh142964 		if (emsg == NULL) {
11519aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11524c06356bSdh142964 			    "SMP operation failed (0x%x)", status);
11534c06356bSdh142964 		} else {
11549aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11554c06356bSdh142964 			    "SMP operation failed (%s)", emsg);
11564c06356bSdh142964 		}
11574c06356bSdh142964 
11584c06356bSdh142964 		if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
11594c06356bSdh142964 		    (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
116096c4a178SChris Horne 			smp_pkt->smp_pkt_reason =
116196c4a178SChris Horne 			    will_retry ? EAGAIN : ETIMEDOUT;
11624c06356bSdh142964 			result = DDI_FAILURE;
11634c06356bSdh142964 		} else if (status ==
11644c06356bSdh142964 		    PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
11654c06356bSdh142964 			xp = pptr->target;
11664c06356bSdh142964 			if (xp == NULL) {
116796c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11684c06356bSdh142964 				result = DDI_FAILURE;
11694c06356bSdh142964 				goto out;
11704c06356bSdh142964 			}
11714c06356bSdh142964 			if (xp->dev_state !=
11724c06356bSdh142964 			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
11734c06356bSdh142964 				xp->dev_state =
11744c06356bSdh142964 				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
11759aed1621SDavid Hollister 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, xp->phy,
11769aed1621SDavid Hollister 				    xp, "%s: Got _IT_NEXUS_LOSS SMP status. "
11774c06356bSdh142964 				    "Tgt(0x%p) dev_state set to "
11784c06356bSdh142964 				    "_NON_OPERATIONAL", __func__,
11794c06356bSdh142964 				    (void *)xp);
11804c06356bSdh142964 			}
11814c06356bSdh142964 			/* ABORT any pending commands related to this device */
11824c06356bSdh142964 			if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
11834c06356bSdh142964 				pptr->abort_pending = 1;
118496c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11854c06356bSdh142964 				result = DDI_FAILURE;
11864c06356bSdh142964 			}
11874c06356bSdh142964 		} else {
118896c4a178SChris Horne 			smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO;
11894c06356bSdh142964 			result = DDI_FAILURE;
11904c06356bSdh142964 		}
11914c06356bSdh142964 	} else {
119296c4a178SChris Horne 		(void) memcpy(smp_pkt->smp_pkt_rsp,
11934c06356bSdh142964 		    &((uint8_t *)pwp->scratch)[rdoff], rspsz);
119496c4a178SChris Horne 		if (smp_pkt->smp_pkt_reason == EOVERFLOW) {
11954c06356bSdh142964 			result = DDI_FAILURE;
11964c06356bSdh142964 		} else {
11974c06356bSdh142964 			result = DDI_SUCCESS;
11984c06356bSdh142964 		}
11994c06356bSdh142964 	}
12004c06356bSdh142964 out:
12019aed1621SDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, pptr->target,
12029aed1621SDavid Hollister 	    "%s: done for wwn 0x%" PRIx64, __func__, wwn);
12039aed1621SDavid Hollister 
12044c06356bSdh142964 	pmcs_unlock_phy(pptr);
12054c06356bSdh142964 	pmcs_release_scratch(pwp);
12064c06356bSdh142964 	return (result);
12074c06356bSdh142964 }
12084c06356bSdh142964 
12094c06356bSdh142964 static int
12104c06356bSdh142964 pmcs_smp_init(dev_info_t *self, dev_info_t *child,
121196c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp_sd)
12124c06356bSdh142964 {
121396c4a178SChris Horne 	_NOTE(ARGUNUSED(tran, smp_sd));
12144c06356bSdh142964 	pmcs_iport_t *iport;
12154c06356bSdh142964 	pmcs_hw_t *pwp;
12164c06356bSdh142964 	pmcs_xscsi_t *tgt;
12174c06356bSdh142964 	pmcs_phy_t *phy, *pphy;
12184c06356bSdh142964 	uint64_t wwn;
12194c06356bSdh142964 	char *addr, *tgt_port;
12204c06356bSdh142964 	int ua_form = 1;
12214c06356bSdh142964 
12224c06356bSdh142964 	iport = ddi_get_soft_state(pmcs_iport_softstate,
12234c06356bSdh142964 	    ddi_get_instance(self));
12244c06356bSdh142964 	ASSERT(iport);
12254c06356bSdh142964 	if (iport == NULL)
12264c06356bSdh142964 		return (DDI_FAILURE);
12274c06356bSdh142964 	pwp = iport->pwp;
12284c06356bSdh142964 	ASSERT(pwp);
12294c06356bSdh142964 	if (pwp == NULL)
12304c06356bSdh142964 		return (DDI_FAILURE);
12314c06356bSdh142964 
12324c06356bSdh142964 	/* Get "target-port" prop from devinfo node */
12334c06356bSdh142964 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
12344c06356bSdh142964 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
12354c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1236c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1237c3bc407cSdh142964 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
12384c06356bSdh142964 		/* Dont fail _smp_init() because we couldnt get/set a prop */
12394c06356bSdh142964 		return (DDI_SUCCESS);
12404c06356bSdh142964 	}
12414c06356bSdh142964 
12424c06356bSdh142964 	/*
12434c06356bSdh142964 	 * Validate that this tran_tgt_init is for an active iport.
12444c06356bSdh142964 	 */
12454c06356bSdh142964 	if (iport->ua_state == UA_INACTIVE) {
1246c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1247c3bc407cSdh142964 		    "%s: Init on inactive iport for '%s'", __func__, tgt_port);
12484c06356bSdh142964 		ddi_prop_free(tgt_port);
12494c06356bSdh142964 		return (DDI_FAILURE);
12504c06356bSdh142964 	}
12514c06356bSdh142964 
12524c06356bSdh142964 	mutex_enter(&pwp->lock);
12534c06356bSdh142964 
12544c06356bSdh142964 	/* Retrieve softstate using unit-address */
12555c45adf0SJesse Butler 	tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
12564c06356bSdh142964 	if (tgt == NULL) {
1257c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1258c3bc407cSdh142964 		    "%s: tgt softstate not found", __func__);
12594c06356bSdh142964 		ddi_prop_free(tgt_port);
12604c06356bSdh142964 		mutex_exit(&pwp->lock);
12614c06356bSdh142964 		return (DDI_FAILURE);
12624c06356bSdh142964 	}
12634c06356bSdh142964 
126473a3eccdSDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)",
126573a3eccdSDavid Hollister 	    __func__, ddi_get_name(child), tgt_port);
126673a3eccdSDavid Hollister 
126773a3eccdSDavid Hollister 	mutex_enter(&tgt->statlock);
12684c06356bSdh142964 	phy = tgt->phy;
12694c06356bSdh142964 	ASSERT(mutex_owned(&phy->phy_lock));
12704c06356bSdh142964 
12714c06356bSdh142964 	if (IS_ROOT_PHY(phy)) {
12724c06356bSdh142964 		/* Expander attached to HBA - don't ref_count it */
12734c06356bSdh142964 		wwn = pwp->sas_wwns[0];
12744c06356bSdh142964 	} else {
12754c06356bSdh142964 		pmcs_inc_phy_ref_count(phy);
12764c06356bSdh142964 
12774c06356bSdh142964 		/*
12784c06356bSdh142964 		 * Parent (in topology) is also an expander
12794c06356bSdh142964 		 * Now that we've increased the ref count on phy, it's OK
12804c06356bSdh142964 		 * to drop the lock so we can acquire the parent's lock.
12814c06356bSdh142964 		 */
12824c06356bSdh142964 		pphy = phy->parent;
128373a3eccdSDavid Hollister 		mutex_exit(&tgt->statlock);
12844c06356bSdh142964 		pmcs_unlock_phy(phy);
12854c06356bSdh142964 		pmcs_lock_phy(pphy);
12864c06356bSdh142964 		wwn = pmcs_barray2wwn(pphy->sas_address);
12874c06356bSdh142964 		pmcs_unlock_phy(pphy);
12884c06356bSdh142964 		pmcs_lock_phy(phy);
128973a3eccdSDavid Hollister 		mutex_enter(&tgt->statlock);
12904c06356bSdh142964 	}
12914c06356bSdh142964 
12924c06356bSdh142964 	/*
12934c06356bSdh142964 	 * If this is the 1st smp_init, add this to our list.
12944c06356bSdh142964 	 */
12954c06356bSdh142964 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
12964c06356bSdh142964 		int target;
12974c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
12984c06356bSdh142964 			if (pwp->targets[target] != NULL) {
12994c06356bSdh142964 				continue;
13004c06356bSdh142964 			}
13014c06356bSdh142964 
13024c06356bSdh142964 			pwp->targets[target] = tgt;
13034c06356bSdh142964 			tgt->target_num = (uint16_t)target;
13044c06356bSdh142964 			tgt->assigned = 1;
13054c06356bSdh142964 			tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
13064c06356bSdh142964 			break;
13074c06356bSdh142964 		}
13084c06356bSdh142964 
13094c06356bSdh142964 		if (target == pwp->max_dev) {
1310c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
13114c06356bSdh142964 			    "Target list full.");
13124c06356bSdh142964 			goto smp_init_fail;
13134c06356bSdh142964 		}
13144c06356bSdh142964 	}
13154c06356bSdh142964 
13164c06356bSdh142964 	if (!pmcs_assign_device(pwp, tgt)) {
13174c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
1318c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
13194c06356bSdh142964 		    "%s: pmcs_assign_device failed for target 0x%p",
13204c06356bSdh142964 		    __func__, (void *)tgt);
13214c06356bSdh142964 		goto smp_init_fail;
13224c06356bSdh142964 	}
13234c06356bSdh142964 
1324499cfd15SDavid Hollister 	/*
1325499cfd15SDavid Hollister 	 * Update the attached port and target port pm properties
1326499cfd15SDavid Hollister 	 */
1327499cfd15SDavid Hollister 	tgt->smpd = smp_sd;
1328499cfd15SDavid Hollister 
13294c06356bSdh142964 	pmcs_unlock_phy(phy);
13304c06356bSdh142964 	mutex_exit(&pwp->lock);
13314c06356bSdh142964 
13324c06356bSdh142964 	tgt->ref_count++;
13334c06356bSdh142964 	tgt->dtype = phy->dtype;
133473a3eccdSDavid Hollister 	mutex_exit(&tgt->statlock);
133573a3eccdSDavid Hollister 
133673a3eccdSDavid Hollister 	pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE);
13374c06356bSdh142964 
13384c06356bSdh142964 	addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
1339499cfd15SDavid Hollister 	if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT,
1340499cfd15SDavid Hollister 	    addr) != DDI_SUCCESS) {
1341c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set "
1342c3bc407cSdh142964 		    "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
13434c06356bSdh142964 	}
13444c06356bSdh142964 	(void) scsi_free_wwnstr(addr);
13454c06356bSdh142964 	ddi_prop_free(tgt_port);
13464c06356bSdh142964 	return (DDI_SUCCESS);
13474c06356bSdh142964 
13484c06356bSdh142964 smp_init_fail:
13494c06356bSdh142964 	tgt->phy = NULL;
13504c06356bSdh142964 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
13514c06356bSdh142964 	phy->target = NULL;
13524c06356bSdh142964 	if (!IS_ROOT_PHY(phy)) {
13534c06356bSdh142964 		pmcs_dec_phy_ref_count(phy);
13544c06356bSdh142964 	}
135573a3eccdSDavid Hollister 	mutex_exit(&tgt->statlock);
13564c06356bSdh142964 	pmcs_unlock_phy(phy);
13574c06356bSdh142964 	mutex_exit(&pwp->lock);
13584c06356bSdh142964 	ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
13594c06356bSdh142964 	ddi_prop_free(tgt_port);
13604c06356bSdh142964 	return (DDI_FAILURE);
13614c06356bSdh142964 }
13624c06356bSdh142964 
13634c06356bSdh142964 static void
13644c06356bSdh142964 pmcs_smp_free(dev_info_t *self, dev_info_t *child,
136596c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp)
13664c06356bSdh142964 {
13674c06356bSdh142964 	_NOTE(ARGUNUSED(tran, smp));
13684c06356bSdh142964 	pmcs_iport_t *iport;
13694c06356bSdh142964 	pmcs_hw_t *pwp;
13704c06356bSdh142964 	pmcs_xscsi_t *tgt;
1371ee13933aSSrikanth Suravajhala 	pmcs_phy_t *phyp;
13724c06356bSdh142964 	char *tgt_port;
13734c06356bSdh142964 
13744c06356bSdh142964 	iport = ddi_get_soft_state(pmcs_iport_softstate,
13754c06356bSdh142964 	    ddi_get_instance(self));
13764c06356bSdh142964 	ASSERT(iport);
13774c06356bSdh142964 	if (iport == NULL)
13784c06356bSdh142964 		return;
13794c06356bSdh142964 
13804c06356bSdh142964 	pwp = iport->pwp;
13814c06356bSdh142964 	if (pwp == NULL)
13824c06356bSdh142964 		return;
13834c06356bSdh142964 	ASSERT(pwp);
13844c06356bSdh142964 
13854c06356bSdh142964 	/* Get "target-port" prop from devinfo node */
13864c06356bSdh142964 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
13874c06356bSdh142964 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
13884c06356bSdh142964 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1389c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1390c3bc407cSdh142964 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
13914c06356bSdh142964 		return;
13924c06356bSdh142964 	}
13939aed1621SDavid Hollister 
13944c06356bSdh142964 	/* Retrieve softstate using unit-address */
13959aed1621SDavid Hollister 	mutex_enter(&pwp->lock);
13964c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
13979aed1621SDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", __func__,
13989aed1621SDavid Hollister 	    ddi_get_name(child), tgt_port);
13994c06356bSdh142964 	ddi_prop_free(tgt_port);
14004c06356bSdh142964 
14014c06356bSdh142964 	if (tgt == NULL) {
1402c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1403c3bc407cSdh142964 		    "%s: tgt softstate not found", __func__);
14049aed1621SDavid Hollister 		mutex_exit(&pwp->lock);
14054c06356bSdh142964 		return;
14064c06356bSdh142964 	}
14074c06356bSdh142964 
1408ee13933aSSrikanth Suravajhala 	phyp = tgt->phy;
1409ee13933aSSrikanth Suravajhala 	if (phyp) {
1410ee13933aSSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
1411ee13933aSSrikanth Suravajhala 		if (!IS_ROOT_PHY(phyp)) {
1412ee13933aSSrikanth Suravajhala 			pmcs_dec_phy_ref_count(phyp);
1413ee13933aSSrikanth Suravajhala 		}
1414ee13933aSSrikanth Suravajhala 	}
14154c06356bSdh142964 	mutex_enter(&tgt->statlock);
14164c06356bSdh142964 
14174c06356bSdh142964 	if (--tgt->ref_count == 0) {
14184c06356bSdh142964 		/*
14194c06356bSdh142964 		 * Remove this target from our list. The softstate
14204c06356bSdh142964 		 * will remain, and the device will remain registered
14214c06356bSdh142964 		 * with the hardware unless/until we're told that the
14224c06356bSdh142964 		 * device physically went away.
14234c06356bSdh142964 		 */
1424c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
14254c06356bSdh142964 		    "Removing target 0x%p (vtgt %d) from target list",
14264c06356bSdh142964 		    (void *)tgt, tgt->target_num);
14274c06356bSdh142964 		pwp->targets[tgt->target_num] = NULL;
14284c06356bSdh142964 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
1429ee13933aSSrikanth Suravajhala 		if (phyp) {
1430ee13933aSSrikanth Suravajhala 			phyp->target = NULL;
14315c45adf0SJesse Butler 		}
1432ee13933aSSrikanth Suravajhala 		tgt->phy = NULL;
14339aed1621SDavid Hollister 		pmcs_destroy_target(tgt);
14349aed1621SDavid Hollister 	} else {
14359aed1621SDavid Hollister 		mutex_exit(&tgt->statlock);
14364c06356bSdh142964 	}
14374c06356bSdh142964 
1438ee13933aSSrikanth Suravajhala 	if (phyp) {
1439ee13933aSSrikanth Suravajhala 		mutex_exit(&phyp->phy_lock);
1440ee13933aSSrikanth Suravajhala 	}
14414c06356bSdh142964 	mutex_exit(&pwp->lock);
14424c06356bSdh142964 }
14434c06356bSdh142964 
14444c06356bSdh142964 static int
14454c06356bSdh142964 pmcs_scsi_quiesce(dev_info_t *dip)
14464c06356bSdh142964 {
14474c06356bSdh142964 	pmcs_hw_t *pwp;
14484c06356bSdh142964 	int totactive = -1;
14494c06356bSdh142964 	pmcs_xscsi_t *xp;
14504c06356bSdh142964 	uint16_t target;
14514c06356bSdh142964 
14524c06356bSdh142964 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
14534c06356bSdh142964 		return (0);		/* iport */
14544c06356bSdh142964 
14554c06356bSdh142964 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
14564c06356bSdh142964 	if (pwp == NULL) {
14574c06356bSdh142964 		return (-1);
14584c06356bSdh142964 	}
14594c06356bSdh142964 	mutex_enter(&pwp->lock);
14604c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
14614c06356bSdh142964 		mutex_exit(&pwp->lock);
14624c06356bSdh142964 		return (-1);
14634c06356bSdh142964 	}
14644c06356bSdh142964 
1465c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
14665c45adf0SJesse Butler 	pwp->quiesced = pwp->blocked = 1;
14674c06356bSdh142964 	while (totactive) {
14684c06356bSdh142964 		totactive = 0;
14694c06356bSdh142964 		for (target = 0; target < pwp->max_dev; target++) {
14704c06356bSdh142964 			xp = pwp->targets[target];
14714c06356bSdh142964 			if (xp == NULL) {
14724c06356bSdh142964 				continue;
14734c06356bSdh142964 			}
14744c06356bSdh142964 			mutex_enter(&xp->statlock);
14754c06356bSdh142964 			if (xp->actv_cnt) {
14764c06356bSdh142964 				totactive += xp->actv_cnt;
14774c06356bSdh142964 				xp->draining = 1;
14784c06356bSdh142964 			}
14794c06356bSdh142964 			mutex_exit(&xp->statlock);
14804c06356bSdh142964 		}
14814c06356bSdh142964 		if (totactive) {
14824c06356bSdh142964 			cv_wait(&pwp->drain_cv, &pwp->lock);
14834c06356bSdh142964 		}
14844c06356bSdh142964 		/*
14854c06356bSdh142964 		 * The pwp->blocked may have been reset. e.g a SCSI bus reset
14864c06356bSdh142964 		 */
14874c06356bSdh142964 		pwp->blocked = 1;
14884c06356bSdh142964 	}
14894c06356bSdh142964 
14904c06356bSdh142964 	for (target = 0; target < pwp->max_dev; target++) {
14914c06356bSdh142964 		xp = pwp->targets[target];
14924c06356bSdh142964 		if (xp == NULL) {
14934c06356bSdh142964 			continue;
14944c06356bSdh142964 		}
14954c06356bSdh142964 		mutex_enter(&xp->statlock);
14964c06356bSdh142964 		xp->draining = 0;
14974c06356bSdh142964 		mutex_exit(&xp->statlock);
14984c06356bSdh142964 	}
14994c06356bSdh142964 
15004c06356bSdh142964 	mutex_exit(&pwp->lock);
15014c06356bSdh142964 	if (totactive == 0) {
1502c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1503c3bc407cSdh142964 		    "%s drain complete", __func__);
15044c06356bSdh142964 	}
15054c06356bSdh142964 	return (0);
15064c06356bSdh142964 }
15074c06356bSdh142964 
15084c06356bSdh142964 static int
15094c06356bSdh142964 pmcs_scsi_unquiesce(dev_info_t *dip)
15104c06356bSdh142964 {
15114c06356bSdh142964 	pmcs_hw_t *pwp;
15124c06356bSdh142964 
15134c06356bSdh142964 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
15144c06356bSdh142964 		return (0);		/* iport */
15154c06356bSdh142964 
15164c06356bSdh142964 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
15174c06356bSdh142964 	if (pwp == NULL) {
15184c06356bSdh142964 		return (-1);
15194c06356bSdh142964 	}
15204c06356bSdh142964 	mutex_enter(&pwp->lock);
15214c06356bSdh142964 	if (pwp->state != STATE_RUNNING) {
15224c06356bSdh142964 		mutex_exit(&pwp->lock);
15234c06356bSdh142964 		return (-1);
15244c06356bSdh142964 	}
1525c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
15265c45adf0SJesse Butler 	pwp->blocked = pwp->quiesced = 0;
15274c06356bSdh142964 	mutex_exit(&pwp->lock);
15284c06356bSdh142964 
15294c06356bSdh142964 	/*
15304c06356bSdh142964 	 * Run all pending commands.
15314c06356bSdh142964 	 */
15324c06356bSdh142964 	pmcs_scsa_wq_run(pwp);
15334c06356bSdh142964 
15344c06356bSdh142964 	/*
15354c06356bSdh142964 	 * Complete all completed commands.
15364c06356bSdh142964 	 * This also unlocks us.
15374c06356bSdh142964 	 */
15384c06356bSdh142964 	PMCS_CQ_RUN(pwp);
15394c06356bSdh142964 	return (0);
15404c06356bSdh142964 }
15414c06356bSdh142964 
15424c06356bSdh142964 /*
15434c06356bSdh142964  * Start commands for a particular device
15444c06356bSdh142964  * If the actual start of a command fails, return B_FALSE.  Any other result
15454c06356bSdh142964  * is a B_TRUE return.
15464c06356bSdh142964  */
15474c06356bSdh142964 boolean_t
15484c06356bSdh142964 pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
15494c06356bSdh142964 {
15504c06356bSdh142964 	pmcs_cmd_t *sp;
15514c06356bSdh142964 	pmcs_phy_t *phyp;
15524c06356bSdh142964 	pmcwork_t *pwrk;
15534c06356bSdh142964 	boolean_t run_one, blocked;
15544c06356bSdh142964 	int rval;
15554c06356bSdh142964 
15564c06356bSdh142964 	/*
15574c06356bSdh142964 	 * First, check to see if we're blocked or resource limited
15584c06356bSdh142964 	 */
15594c06356bSdh142964 	mutex_enter(&pwp->lock);
15604c06356bSdh142964 	blocked = pwp->blocked;
15614c06356bSdh142964 	/*
15624c06356bSdh142964 	 * If resource_limited is set, we're resource constrained and
15634c06356bSdh142964 	 * we will run only one work request for this target.
15644c06356bSdh142964 	 */
15654c06356bSdh142964 	run_one = pwp->resource_limited;
15664c06356bSdh142964 	mutex_exit(&pwp->lock);
15674c06356bSdh142964 
15684c06356bSdh142964 	if (blocked) {
15694c06356bSdh142964 		/* Queues will get restarted when we get unblocked */
15704c06356bSdh142964 		return (B_TRUE);
15714c06356bSdh142964 	}
15724c06356bSdh142964 
15734c06356bSdh142964 	/*
15744c06356bSdh142964 	 * Might as well verify the queue is not empty before moving on
15754c06356bSdh142964 	 */
15764c06356bSdh142964 	mutex_enter(&xp->wqlock);
15774c06356bSdh142964 	if (STAILQ_EMPTY(&xp->wq)) {
15784c06356bSdh142964 		mutex_exit(&xp->wqlock);
15794c06356bSdh142964 		return (B_TRUE);
15804c06356bSdh142964 	}
15814c06356bSdh142964 	mutex_exit(&xp->wqlock);
15824c06356bSdh142964 
15834c06356bSdh142964 	/*
15844c06356bSdh142964 	 * If we're draining or resetting, just reschedule work queue and bail.
15854c06356bSdh142964 	 */
15864c06356bSdh142964 	mutex_enter(&xp->statlock);
15874c06356bSdh142964 	if (xp->draining || xp->resetting || xp->special_running ||
15884c06356bSdh142964 	    xp->special_needed) {
15894c06356bSdh142964 		mutex_exit(&xp->statlock);
15904c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15914c06356bSdh142964 		return (B_TRUE);
15924c06356bSdh142964 	}
15934c06356bSdh142964 
15944c06356bSdh142964 	/*
1595b18a19c2SJesse Butler 	 * Next, check to see if the target is gone.
15964c06356bSdh142964 	 */
1597b18a19c2SJesse Butler 	if (xp->dev_gone) {
1598c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1599b18a19c2SJesse Butler 		    "%s: Flushing wait queue for dead tgt 0x%p", __func__,
16004c06356bSdh142964 		    (void *)xp);
16014c06356bSdh142964 		pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
16024c06356bSdh142964 		mutex_exit(&xp->statlock);
16034c06356bSdh142964 		return (B_TRUE);
16044c06356bSdh142964 	}
16054c06356bSdh142964 
16064c06356bSdh142964 	/*
16074c06356bSdh142964 	 * Increment the PHY's ref_count now so we know it won't go away
16084c06356bSdh142964 	 * after we drop the target lock.  Drop it before returning.  If the
16094c06356bSdh142964 	 * PHY dies, the commands we attempt to send will fail, but at least
16104c06356bSdh142964 	 * we know we have a real PHY pointer.
16114c06356bSdh142964 	 */
16124c06356bSdh142964 	phyp = xp->phy;
16134c06356bSdh142964 	pmcs_inc_phy_ref_count(phyp);
16144c06356bSdh142964 	mutex_exit(&xp->statlock);
16154c06356bSdh142964 
16164c06356bSdh142964 	mutex_enter(&xp->wqlock);
16174c06356bSdh142964 	while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
16184c06356bSdh142964 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
16194c06356bSdh142964 		if (pwrk == NULL) {
1620c280a92bSDavid Hollister 			mutex_exit(&xp->wqlock);
1621c280a92bSDavid Hollister 			mutex_enter(&pwp->lock);
1622c280a92bSDavid Hollister 			if (pwp->resource_limited == 0) {
1623c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16244c06356bSdh142964 				    "%s: out of work structures", __func__);
1625c280a92bSDavid Hollister 			}
1626c280a92bSDavid Hollister 			pwp->resource_limited = 1;
16274c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1628c280a92bSDavid Hollister 			mutex_exit(&pwp->lock);
1629c280a92bSDavid Hollister 			return (B_FALSE);
16304c06356bSdh142964 		}
16314c06356bSdh142964 		STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
16324c06356bSdh142964 		mutex_exit(&xp->wqlock);
16334c06356bSdh142964 
16344c06356bSdh142964 		pwrk->xp = xp;
16354c06356bSdh142964 		pwrk->arg = sp;
16364c06356bSdh142964 		sp->cmd_tag = pwrk->htag;
16374c06356bSdh142964 		pwrk->timer = US2WT(CMD2PKT(sp)->pkt_time * 1000000);
16384c06356bSdh142964 		if (pwrk->timer == 0) {
16394c06356bSdh142964 			pwrk->timer = US2WT(1000000);
16404c06356bSdh142964 		}
16414c06356bSdh142964 
16424c06356bSdh142964 		pwrk->dtype = xp->dtype;
16434c06356bSdh142964 
16444c06356bSdh142964 		if (xp->dtype == SAS) {
16454c06356bSdh142964 			pwrk->ptr = (void *) pmcs_SAS_done;
16464c06356bSdh142964 			if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
16474c06356bSdh142964 				sp->cmd_tag = NULL;
16484c06356bSdh142964 				pmcs_dec_phy_ref_count(phyp);
16494c06356bSdh142964 				pmcs_pwork(pwp, pwrk);
16504c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16514c06356bSdh142964 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
16524c06356bSdh142964 					return (B_FALSE);
16534c06356bSdh142964 				} else {
16544c06356bSdh142964 					return (B_TRUE);
16554c06356bSdh142964 				}
16564c06356bSdh142964 			}
16574c06356bSdh142964 		} else {
16584c06356bSdh142964 			ASSERT(xp->dtype == SATA);
16594c06356bSdh142964 			pwrk->ptr = (void *) pmcs_SATA_done;
16604c06356bSdh142964 			if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
16614c06356bSdh142964 				sp->cmd_tag = NULL;
16624c06356bSdh142964 				pmcs_dec_phy_ref_count(phyp);
16634c06356bSdh142964 				pmcs_pwork(pwp, pwrk);
16644c06356bSdh142964 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16654c06356bSdh142964 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
16664c06356bSdh142964 					return (B_FALSE);
16674c06356bSdh142964 				} else {
16684c06356bSdh142964 					return (B_TRUE);
16694c06356bSdh142964 				}
16704c06356bSdh142964 			}
16714c06356bSdh142964 		}
16724c06356bSdh142964 
16734c06356bSdh142964 		if (run_one) {
16744c06356bSdh142964 			goto wq_out;
16754c06356bSdh142964 		}
16764c06356bSdh142964 		mutex_enter(&xp->wqlock);
16774c06356bSdh142964 	}
16784c06356bSdh142964 
16794c06356bSdh142964 	mutex_exit(&xp->wqlock);
16804c06356bSdh142964 
16814c06356bSdh142964 wq_out:
16824c06356bSdh142964 	pmcs_dec_phy_ref_count(phyp);
16834c06356bSdh142964 	return (B_TRUE);
16844c06356bSdh142964 }
16854c06356bSdh142964 
16864c06356bSdh142964 /*
16874c06356bSdh142964  * Start commands for all devices.
16884c06356bSdh142964  */
16894c06356bSdh142964 void
16904c06356bSdh142964 pmcs_scsa_wq_run(pmcs_hw_t *pwp)
16914c06356bSdh142964 {
16924c06356bSdh142964 	pmcs_xscsi_t *xp;
16934c06356bSdh142964 	uint16_t target_start, target;
16944c06356bSdh142964 	boolean_t	rval = B_TRUE;
16954c06356bSdh142964 
16964c06356bSdh142964 	mutex_enter(&pwp->lock);
16974c06356bSdh142964 	target_start = pwp->last_wq_dev;
16984c06356bSdh142964 	target = target_start;
16994c06356bSdh142964 
17004c06356bSdh142964 	do {
17014c06356bSdh142964 		xp = pwp->targets[target];
1702429adc13SSrikanth, Ramana 		if ((xp == NULL) || (STAILQ_EMPTY(&xp->wq))) {
17034c06356bSdh142964 			if (++target == pwp->max_dev) {
17044c06356bSdh142964 				target = 0;
17054c06356bSdh142964 			}
17064c06356bSdh142964 			continue;
17074c06356bSdh142964 		}
17084c06356bSdh142964 
17094c06356bSdh142964 		mutex_exit(&pwp->lock);
17104c06356bSdh142964 		rval = pmcs_scsa_wq_run_one(pwp, xp);
17114c06356bSdh142964 		mutex_enter(&pwp->lock);
1712429adc13SSrikanth, Ramana 
1713429adc13SSrikanth, Ramana 		if (rval == B_FALSE) {
17144c06356bSdh142964 			break;
17154c06356bSdh142964 		}
1716429adc13SSrikanth, Ramana 
17174c06356bSdh142964 		if (++target == pwp->max_dev) {
17184c06356bSdh142964 			target = 0;
17194c06356bSdh142964 		}
17204c06356bSdh142964 	} while (target != target_start);
17214c06356bSdh142964 
17224c06356bSdh142964 	if (rval) {
1723c280a92bSDavid Hollister 		/*
1724c280a92bSDavid Hollister 		 * If we were resource limited, but apparently are not now,
1725c280a92bSDavid Hollister 		 * reschedule the work queues anyway.
1726c280a92bSDavid Hollister 		 */
1727c280a92bSDavid Hollister 		if (pwp->resource_limited) {
1728c280a92bSDavid Hollister 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1729c280a92bSDavid Hollister 		}
17304c06356bSdh142964 		pwp->resource_limited = 0; /* Not resource-constrained */
17314c06356bSdh142964 	} else {
1732c280a92bSDavid Hollister 		/*
1733c280a92bSDavid Hollister 		 * Give everybody a chance, and reschedule to run the queues
1734c280a92bSDavid Hollister 		 * again as long as we're limited.
1735c280a92bSDavid Hollister 		 */
1736c280a92bSDavid Hollister 		pwp->resource_limited = 1;
1737c280a92bSDavid Hollister 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
17384c06356bSdh142964 	}
17394c06356bSdh142964 
17404c06356bSdh142964 	pwp->last_wq_dev = target;
17414c06356bSdh142964 	mutex_exit(&pwp->lock);
17424c06356bSdh142964 }
17434c06356bSdh142964 
17444c06356bSdh142964 /*
17454c06356bSdh142964  * Pull the completion queue, drop the lock and complete all elements.
17464c06356bSdh142964  */
17474c06356bSdh142964 
17484c06356bSdh142964 void
17494c06356bSdh142964 pmcs_scsa_cq_run(void *arg)
17504c06356bSdh142964 {
17514c06356bSdh142964 	pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
17524c06356bSdh142964 	pmcs_hw_t *pwp = cqti->cq_pwp;
17534c06356bSdh142964 	pmcs_cmd_t *sp, *nxt;
17544c06356bSdh142964 	struct scsi_pkt *pkt;
1755601c90f1SSrikanth, Ramana 	pmcs_xscsi_t *tgt;
17564c06356bSdh142964 	pmcs_iocomp_cb_t *ioccb, *ioccb_next;
17574c06356bSdh142964 	pmcs_cb_t callback;
17584c06356bSdh142964 
17594c06356bSdh142964 	DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
17604c06356bSdh142964 
17614c06356bSdh142964 	mutex_enter(&pwp->cq_lock);
17624c06356bSdh142964 
17634c06356bSdh142964 	while (!pwp->cq_info.cq_stop) {
17644c06356bSdh142964 		/*
17654c06356bSdh142964 		 * First, check the I/O completion callback queue.
17664c06356bSdh142964 		 */
17674c06356bSdh142964 		ioccb = pwp->iocomp_cb_head;
17684c06356bSdh142964 		pwp->iocomp_cb_head = NULL;
17694c06356bSdh142964 		pwp->iocomp_cb_tail = NULL;
17704c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
17714c06356bSdh142964 
17724c06356bSdh142964 		while (ioccb) {
17734c06356bSdh142964 			/*
17744c06356bSdh142964 			 * Grab the lock on the work structure. The callback
17754c06356bSdh142964 			 * routine is responsible for clearing it.
17764c06356bSdh142964 			 */
17774c06356bSdh142964 			mutex_enter(&ioccb->pwrk->lock);
17784c06356bSdh142964 			ioccb_next = ioccb->next;
17794c06356bSdh142964 			callback = (pmcs_cb_t)ioccb->pwrk->ptr;
17804c06356bSdh142964 			(*callback)(pwp, ioccb->pwrk,
17814c06356bSdh142964 			    (uint32_t *)((void *)ioccb->iomb));
17824c06356bSdh142964 			kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
17834c06356bSdh142964 			ioccb = ioccb_next;
17844c06356bSdh142964 		}
17854c06356bSdh142964 
17864c06356bSdh142964 		/*
17874c06356bSdh142964 		 * Next, run the completion queue
17884c06356bSdh142964 		 */
17894c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
17904c06356bSdh142964 		sp = STAILQ_FIRST(&pwp->cq);
17914c06356bSdh142964 		STAILQ_INIT(&pwp->cq);
17924c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
17934c06356bSdh142964 
17944c06356bSdh142964 		DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
17954c06356bSdh142964 		    pmcs_cq_thr_info_t *, cqti);
17964c06356bSdh142964 
17974c06356bSdh142964 		if (sp && pmcs_check_acc_dma_handle(pwp)) {
17984c06356bSdh142964 			ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
17994c06356bSdh142964 		}
18004c06356bSdh142964 
18014c06356bSdh142964 		while (sp) {
18024c06356bSdh142964 			nxt = STAILQ_NEXT(sp, cmd_next);
18034c06356bSdh142964 			pkt = CMD2PKT(sp);
1804601c90f1SSrikanth, Ramana 			tgt = sp->cmd_target;
1805601c90f1SSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt,
18064c06356bSdh142964 			    "%s: calling completion on %p for tgt %p", __func__,
1807601c90f1SSrikanth, Ramana 			    (void *)sp, (void *)tgt);
1808601c90f1SSrikanth, Ramana 			if (tgt) {
1809601c90f1SSrikanth, Ramana 				mutex_enter(&tgt->statlock);
1810601c90f1SSrikanth, Ramana 				ASSERT(tgt->actv_pkts != 0);
1811601c90f1SSrikanth, Ramana 				tgt->actv_pkts--;
1812601c90f1SSrikanth, Ramana 				mutex_exit(&tgt->statlock);
1813601c90f1SSrikanth, Ramana 			}
18144c06356bSdh142964 			scsi_hba_pkt_comp(pkt);
18154c06356bSdh142964 			sp = nxt;
18164c06356bSdh142964 		}
18174c06356bSdh142964 
18184c06356bSdh142964 		DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
18194c06356bSdh142964 		    pmcs_cq_thr_info_t *, cqti);
18204c06356bSdh142964 
18218f514e74SDavid Hollister 		/*
18228f514e74SDavid Hollister 		 * Check if there are more completions to do.  If so, and we've
18238f514e74SDavid Hollister 		 * not been told to stop, skip the wait and cycle through again.
18248f514e74SDavid Hollister 		 */
18258f514e74SDavid Hollister 		mutex_enter(&pwp->cq_lock);
18268f514e74SDavid Hollister 		if ((pwp->iocomp_cb_head == NULL) && STAILQ_EMPTY(&pwp->cq) &&
18278f514e74SDavid Hollister 		    !pwp->cq_info.cq_stop) {
18288f514e74SDavid Hollister 			mutex_exit(&pwp->cq_lock);
18294c06356bSdh142964 			mutex_enter(&cqti->cq_thr_lock);
18304c06356bSdh142964 			cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
18314c06356bSdh142964 			mutex_exit(&cqti->cq_thr_lock);
18324c06356bSdh142964 			mutex_enter(&pwp->cq_lock);
18334c06356bSdh142964 		}
18348f514e74SDavid Hollister 	}
18354c06356bSdh142964 
18364c06356bSdh142964 	mutex_exit(&pwp->cq_lock);
18374c06356bSdh142964 	DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
18384c06356bSdh142964 	thread_exit();
18394c06356bSdh142964 }
18404c06356bSdh142964 
18414c06356bSdh142964 /*
18424c06356bSdh142964  * Run a SAS command.  Called with pwrk->lock held, returns unlocked.
18434c06356bSdh142964  */
18444c06356bSdh142964 static int
18454c06356bSdh142964 pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
18464c06356bSdh142964 {
18474c06356bSdh142964 	pmcs_hw_t *pwp = CMD2PMC(sp);
18484c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
18494c06356bSdh142964 	pmcs_xscsi_t *xp = pwrk->xp;
18504c06356bSdh142964 	uint32_t iq, *ptr;
18514c06356bSdh142964 	sas_ssp_cmd_iu_t sc;
18524c06356bSdh142964 
18533be32c0fSJesse Butler 	ASSERT(xp != NULL);
18544c06356bSdh142964 	mutex_enter(&xp->statlock);
1855b18a19c2SJesse Butler 	if (!xp->assigned) {
18564c06356bSdh142964 		mutex_exit(&xp->statlock);
18574c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
18584c06356bSdh142964 	}
18594c06356bSdh142964 	if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
18604c06356bSdh142964 		mutex_exit(&xp->statlock);
18614c06356bSdh142964 		mutex_enter(&xp->wqlock);
18624c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18634c06356bSdh142964 		mutex_exit(&xp->wqlock);
18644c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
18654c06356bSdh142964 	}
18664c06356bSdh142964 	GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
18674c06356bSdh142964 	if (ptr == NULL) {
18684c06356bSdh142964 		mutex_exit(&xp->statlock);
18694c06356bSdh142964 		/*
18704c06356bSdh142964 		 * This is a temporary failure not likely to unblocked by
18714c06356bSdh142964 		 * commands completing as the test for scheduling the
18724c06356bSdh142964 		 * restart of work is a per-device test.
18734c06356bSdh142964 		 */
18744c06356bSdh142964 		mutex_enter(&xp->wqlock);
18754c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18764c06356bSdh142964 		mutex_exit(&xp->wqlock);
1877c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
18784c06356bSdh142964 		    "%s: Failed to get IO IQ entry for tgt %d",
18794c06356bSdh142964 		    __func__, xp->target_num);
18804c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
18814c06356bSdh142964 
18824c06356bSdh142964 	}
18834c06356bSdh142964 
18844c06356bSdh142964 	ptr[0] =
18854c06356bSdh142964 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
18864c06356bSdh142964 	ptr[1] = LE_32(pwrk->htag);
18874c06356bSdh142964 	ptr[2] = LE_32(pwrk->phy->device_id);
18884c06356bSdh142964 	ptr[3] = LE_32(pkt->pkt_dma_len);
18894c06356bSdh142964 	if (ptr[3]) {
18904c06356bSdh142964 		ASSERT(pkt->pkt_numcookies);
18914c06356bSdh142964 		if (pkt->pkt_dma_flags & DDI_DMA_READ) {
18924c06356bSdh142964 			ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
18934c06356bSdh142964 		} else {
18944c06356bSdh142964 			ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
18954c06356bSdh142964 		}
18964c06356bSdh142964 		if (pmcs_dma_load(pwp, sp, ptr)) {
18974c06356bSdh142964 			mutex_exit(&pwp->iqp_lock[iq]);
18984c06356bSdh142964 			mutex_exit(&xp->statlock);
18994c06356bSdh142964 			mutex_enter(&xp->wqlock);
19004c06356bSdh142964 			if (STAILQ_EMPTY(&xp->wq)) {
19014c06356bSdh142964 				STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
19024c06356bSdh142964 				mutex_exit(&xp->wqlock);
19034c06356bSdh142964 			} else {
19044c06356bSdh142964 				mutex_exit(&xp->wqlock);
19054c06356bSdh142964 				CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
19064c06356bSdh142964 				CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
19074c06356bSdh142964 				CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
19084c06356bSdh142964 				    STATE_GOT_TARGET | STATE_SENT_CMD |
19094c06356bSdh142964 				    STATE_GOT_STATUS;
19104c06356bSdh142964 				mutex_enter(&pwp->cq_lock);
19114c06356bSdh142964 				STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
19128f514e74SDavid Hollister 				PMCS_CQ_RUN_LOCKED(pwp);
19134c06356bSdh142964 				mutex_exit(&pwp->cq_lock);
1914c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
19154c06356bSdh142964 				    "%s: Failed to dma_load for tgt %d (QF)",
19164c06356bSdh142964 				    __func__, xp->target_num);
19174c06356bSdh142964 			}
19184c06356bSdh142964 			return (PMCS_WQ_RUN_FAIL_RES);
19194c06356bSdh142964 		}
19204c06356bSdh142964 	} else {
19214c06356bSdh142964 		ptr[4] = LE_32(PMCIN_DATADIR_NONE);
19224c06356bSdh142964 		CLEAN_MESSAGE(ptr, 12);
19234c06356bSdh142964 	}
19244c06356bSdh142964 	xp->actv_cnt++;
19254c06356bSdh142964 	if (xp->actv_cnt > xp->maxdepth) {
19264c06356bSdh142964 		xp->maxdepth = xp->actv_cnt;
1927c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth "
1928c3bc407cSdh142964 		    "now %u", pwrk->phy->path, xp->maxdepth);
19294c06356bSdh142964 	}
19304c06356bSdh142964 	mutex_exit(&xp->statlock);
19314c06356bSdh142964 
19324c06356bSdh142964 
19334c06356bSdh142964 #ifdef	DEBUG
19344c06356bSdh142964 	/*
19354c06356bSdh142964 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
19364c06356bSdh142964 	 * event when this goes out on the wire.
19374c06356bSdh142964 	 */
19384c06356bSdh142964 	ptr[4] |= PMCIN_MESSAGE_REPORT;
19394c06356bSdh142964 #endif
19404c06356bSdh142964 	/*
19414c06356bSdh142964 	 * Fill in the SSP IU
19424c06356bSdh142964 	 */
19434c06356bSdh142964 
19444c06356bSdh142964 	bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
19454c06356bSdh142964 	bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
19464c06356bSdh142964 
19474c06356bSdh142964 	switch (pkt->pkt_flags & FLAG_TAGMASK) {
19484c06356bSdh142964 	case FLAG_HTAG:
19494c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
19504c06356bSdh142964 		break;
19514c06356bSdh142964 	case FLAG_OTAG:
19524c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
19534c06356bSdh142964 		break;
19544c06356bSdh142964 	case FLAG_STAG:
19554c06356bSdh142964 	default:
19564c06356bSdh142964 		sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
19574c06356bSdh142964 		break;
19584c06356bSdh142964 	}
19594c06356bSdh142964 	(void) memcpy(sc.cdb, pkt->pkt_cdbp,
19604c06356bSdh142964 	    min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
19614c06356bSdh142964 	(void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
19624c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
19634c06356bSdh142964 	mutex_exit(&pwrk->lock);
1964c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
19654c06356bSdh142964 	    "%s: giving pkt %p (tag %x) to the hardware", __func__,
19664c06356bSdh142964 	    (void *)pkt, pwrk->htag);
19674c06356bSdh142964 #ifdef DEBUG
19684c06356bSdh142964 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
19694c06356bSdh142964 #endif
19704c06356bSdh142964 	mutex_enter(&xp->aqlock);
19714c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
19724c06356bSdh142964 	mutex_exit(&xp->aqlock);
19734c06356bSdh142964 	INC_IQ_ENTRY(pwp, iq);
19744c06356bSdh142964 
19754c06356bSdh142964 	/*
19764c06356bSdh142964 	 * If we just submitted the last command queued from device state
19774c06356bSdh142964 	 * recovery, clear the wq_recovery_tail pointer.
19784c06356bSdh142964 	 */
19794c06356bSdh142964 	mutex_enter(&xp->wqlock);
19804c06356bSdh142964 	if (xp->wq_recovery_tail == sp) {
19814c06356bSdh142964 		xp->wq_recovery_tail = NULL;
19824c06356bSdh142964 	}
19834c06356bSdh142964 	mutex_exit(&xp->wqlock);
19844c06356bSdh142964 
19854c06356bSdh142964 	return (PMCS_WQ_RUN_SUCCESS);
19864c06356bSdh142964 }
19874c06356bSdh142964 
19884c06356bSdh142964 /*
19894c06356bSdh142964  * Complete a SAS command
19904c06356bSdh142964  *
19914c06356bSdh142964  * Called with pwrk lock held.
19924c06356bSdh142964  * The free of pwrk releases the lock.
19934c06356bSdh142964  */
19944c06356bSdh142964 
19954c06356bSdh142964 static void
19964c06356bSdh142964 pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
19974c06356bSdh142964 {
19984c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
19994c06356bSdh142964 	pmcs_phy_t *pptr = pwrk->phy;
20004c06356bSdh142964 	pmcs_xscsi_t *xp = pwrk->xp;
20014c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
20024c06356bSdh142964 	int dead;
20034c06356bSdh142964 	uint32_t sts;
20044c06356bSdh142964 	boolean_t aborted = B_FALSE;
20054c06356bSdh142964 	boolean_t do_ds_recovery = B_FALSE;
20064c06356bSdh142964 
20074c06356bSdh142964 	ASSERT(xp != NULL);
20084c06356bSdh142964 	ASSERT(sp != NULL);
20094c06356bSdh142964 	ASSERT(pptr != NULL);
20104c06356bSdh142964 
20114c06356bSdh142964 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
20124c06356bSdh142964 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
20134c06356bSdh142964 	    hrtime_t, gethrtime());
20144c06356bSdh142964 
20154c06356bSdh142964 	dead = pwrk->dead;
20164c06356bSdh142964 
20174c06356bSdh142964 	if (msg) {
20184c06356bSdh142964 		sts = LE_32(msg[2]);
20194c06356bSdh142964 	} else {
20204c06356bSdh142964 		sts = 0;
20214c06356bSdh142964 	}
20224c06356bSdh142964 
20234c06356bSdh142964 	if (dead != 0) {
2024c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2025c3bc407cSdh142964 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
20264c06356bSdh142964 		goto out;
20274c06356bSdh142964 	}
20284c06356bSdh142964 
20294c06356bSdh142964 	if (sts == PMCOUT_STATUS_ABORTED) {
20304c06356bSdh142964 		aborted = B_TRUE;
20314c06356bSdh142964 	}
20324c06356bSdh142964 
20334c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
2034c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20354c06356bSdh142964 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
20364c06356bSdh142964 		    __func__, (void *)sp, pwrk->htag, pptr->path);
2037601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
2038601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
2039601c90f1SSrikanth, Ramana 		    STATE_SENT_CMD;
2040601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
20414c06356bSdh142964 		goto out;
20424c06356bSdh142964 	}
20434c06356bSdh142964 
20444c06356bSdh142964 	/*
20454c06356bSdh142964 	 * If the status isn't okay but not underflow,
20464c06356bSdh142964 	 * step to the side and parse the (possible) error.
20474c06356bSdh142964 	 */
20484c06356bSdh142964 #ifdef DEBUG
20494c06356bSdh142964 	if (msg) {
20504c06356bSdh142964 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
20514c06356bSdh142964 	}
20524c06356bSdh142964 #endif
20534c06356bSdh142964 	if (!msg) {
20544c06356bSdh142964 		goto out;
20554c06356bSdh142964 	}
20564c06356bSdh142964 
20574c06356bSdh142964 	switch (sts) {
20584c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
20594c06356bSdh142964 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
20604c06356bSdh142964 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
2061c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2062601c90f1SSrikanth, Ramana 		    "%s: PHY %s requires DS recovery (status=%d)",
20634c06356bSdh142964 		    __func__, pptr->path, sts);
20644c06356bSdh142964 		do_ds_recovery = B_TRUE;
20654c06356bSdh142964 		break;
20664c06356bSdh142964 	case PMCOUT_STATUS_UNDERFLOW:
20674c06356bSdh142964 		(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
2068c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL,
20694c06356bSdh142964 		    "%s: underflow %u for cdb 0x%x",
20704c06356bSdh142964 		    __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
20714c06356bSdh142964 		sts = PMCOUT_STATUS_OK;
20724c06356bSdh142964 		msg[3] = 0;
20734c06356bSdh142964 		break;
20744c06356bSdh142964 	case PMCOUT_STATUS_OK:
20754c06356bSdh142964 		pkt->pkt_resid = 0;
20764c06356bSdh142964 		break;
20774c06356bSdh142964 	}
20784c06356bSdh142964 
20794c06356bSdh142964 	if (sts != PMCOUT_STATUS_OK) {
2080658280b6SDavid Hollister 		pmcs_ioerror(pwp, SAS, pwrk, msg, sts);
20814c06356bSdh142964 	} else {
20824c06356bSdh142964 		if (msg[3]) {
20834c06356bSdh142964 			uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
20844c06356bSdh142964 			sas_ssp_rsp_iu_t *rptr = (void *)local;
20854c06356bSdh142964 			const int lim =
20864c06356bSdh142964 			    (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
20874c06356bSdh142964 			static const uint8_t ssp_rsp_evec[] = {
20884c06356bSdh142964 				0x58, 0x61, 0x56, 0x72, 0x00
20894c06356bSdh142964 			};
20904c06356bSdh142964 
20914c06356bSdh142964 			/*
20924c06356bSdh142964 			 * Transform the the first part of the response
20934c06356bSdh142964 			 * to host canonical form. This gives us enough
20944c06356bSdh142964 			 * information to figure out what to do with the
20954c06356bSdh142964 			 * rest (which remains unchanged in the incoming
20964c06356bSdh142964 			 * message which can be up to two queue entries
20974c06356bSdh142964 			 * in length).
20984c06356bSdh142964 			 */
20994c06356bSdh142964 			pmcs_endian_transform(pwp, local, &msg[5],
21004c06356bSdh142964 			    ssp_rsp_evec);
21014c06356bSdh142964 			xd = (uint8_t *)(&msg[5]);
21024c06356bSdh142964 			xd += SAS_RSP_HDR_SIZE;
21034c06356bSdh142964 
21044c06356bSdh142964 			if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
21054c06356bSdh142964 				if (rptr->response_data_length != 4) {
21064c06356bSdh142964 					pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21074c06356bSdh142964 					    "Bad SAS RESPONSE DATA LENGTH",
21084c06356bSdh142964 					    msg);
21094c06356bSdh142964 					pkt->pkt_reason = CMD_TRAN_ERR;
21104c06356bSdh142964 					goto out;
21114c06356bSdh142964 				}
21124c06356bSdh142964 				(void) memcpy(&sts, xd, sizeof (uint32_t));
21134c06356bSdh142964 				sts = BE_32(sts);
21144c06356bSdh142964 				/*
21154c06356bSdh142964 				 * The only response code we should legally get
21164c06356bSdh142964 				 * here is an INVALID FRAME response code.
21174c06356bSdh142964 				 */
21184c06356bSdh142964 				if (sts == SAS_RSP_INVALID_FRAME) {
2119c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21204c06356bSdh142964 					    "%s: pkt %p tgt %u path %s "
21214c06356bSdh142964 					    "completed: INVALID FRAME response",
21224c06356bSdh142964 					    __func__, (void *)pkt,
21234c06356bSdh142964 					    xp->target_num, pptr->path);
21244c06356bSdh142964 				} else {
2125c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21264c06356bSdh142964 					    "%s: pkt %p tgt %u path %s "
21274c06356bSdh142964 					    "completed: illegal response 0x%x",
21284c06356bSdh142964 					    __func__, (void *)pkt,
21294c06356bSdh142964 					    xp->target_num, pptr->path, sts);
21304c06356bSdh142964 				}
21314c06356bSdh142964 				pkt->pkt_reason = CMD_TRAN_ERR;
21324c06356bSdh142964 				goto out;
21334c06356bSdh142964 			}
21344c06356bSdh142964 			if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
21354c06356bSdh142964 				uint32_t slen;
21364c06356bSdh142964 				slen = rptr->sense_data_length;
21374c06356bSdh142964 				if (slen > lim) {
21384c06356bSdh142964 					slen = lim;
21394c06356bSdh142964 				}
21404c06356bSdh142964 				pmcs_latch_status(pwp, sp, rptr->status, xd,
21414c06356bSdh142964 				    slen, pptr->path);
21424c06356bSdh142964 			} else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
21434b456463SDavid Hollister 				pmcout_ssp_comp_t *sspcp;
21444b456463SDavid Hollister 				sspcp = (pmcout_ssp_comp_t *)msg;
21454b456463SDavid Hollister 				uint32_t *residp;
21464c06356bSdh142964 				/*
21474c06356bSdh142964 				 * This is the case for a plain SCSI status.
21484b456463SDavid Hollister 				 * Note: If RESC_V is set and we're here, there
21494b456463SDavid Hollister 				 * is a residual.  We need to find it and update
21504b456463SDavid Hollister 				 * the packet accordingly.
21514c06356bSdh142964 				 */
21524c06356bSdh142964 				pmcs_latch_status(pwp, sp, rptr->status, NULL,
21534c06356bSdh142964 				    0, pptr->path);
21544b456463SDavid Hollister 
21554b456463SDavid Hollister 				if (sspcp->resc_v) {
21564b456463SDavid Hollister 					/*
21574b456463SDavid Hollister 					 * Point residual to the SSP_RESP_IU
21584b456463SDavid Hollister 					 */
21594b456463SDavid Hollister 					residp = (uint32_t *)(sspcp + 1);
21604b456463SDavid Hollister 					/*
21614b456463SDavid Hollister 					 * param contains the number of bytes
21624b456463SDavid Hollister 					 * between where the SSP_RESP_IU may
21634b456463SDavid Hollister 					 * or may not be and the residual.
21644b456463SDavid Hollister 					 * Increment residp by the appropriate
21654b456463SDavid Hollister 					 * number of words: (param+resc_pad)/4).
21664b456463SDavid Hollister 					 */
21674b456463SDavid Hollister 					residp += (LE_32(sspcp->param) +
21684b456463SDavid Hollister 					    sspcp->resc_pad) /
21694b456463SDavid Hollister 					    sizeof (uint32_t);
21704b456463SDavid Hollister 					pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
21714b456463SDavid Hollister 					    pptr, xp, "%s: tgt 0x%p "
21724b456463SDavid Hollister 					    "residual %d for pkt 0x%p",
21734b456463SDavid Hollister 					    __func__, (void *) xp, *residp,
21744b456463SDavid Hollister 					    (void *) pkt);
21754b456463SDavid Hollister 					ASSERT(LE_32(*residp) <=
21764b456463SDavid Hollister 					    pkt->pkt_dma_len);
21774b456463SDavid Hollister 					(void) pmcs_set_resid(pkt,
21784b456463SDavid Hollister 					    pkt->pkt_dma_len, LE_32(*residp));
21794b456463SDavid Hollister 				}
21804c06356bSdh142964 			} else {
21814c06356bSdh142964 				pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21824c06356bSdh142964 				    "illegal SAS response", msg);
21834c06356bSdh142964 				pkt->pkt_reason = CMD_TRAN_ERR;
21844c06356bSdh142964 				goto out;
21854c06356bSdh142964 			}
21864c06356bSdh142964 		} else {
21874c06356bSdh142964 			pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
21884c06356bSdh142964 			    pptr->path);
21894c06356bSdh142964 		}
21904c06356bSdh142964 		if (pkt->pkt_dma_len) {
21914c06356bSdh142964 			pkt->pkt_state |= STATE_XFERRED_DATA;
21924c06356bSdh142964 		}
21934c06356bSdh142964 	}
2194c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
21954c06356bSdh142964 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
21964c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
21974c06356bSdh142964 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
21984c06356bSdh142964 
21994c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2200c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
22014c06356bSdh142964 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
22024c06356bSdh142964 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
22034c06356bSdh142964 		aborted = B_TRUE;
22044c06356bSdh142964 	}
22054c06356bSdh142964 
22064c06356bSdh142964 out:
22074c06356bSdh142964 	pmcs_dma_unload(pwp, sp);
22084c06356bSdh142964 	mutex_enter(&xp->statlock);
22099aed1621SDavid Hollister 
22109aed1621SDavid Hollister 	/*
22115c45adf0SJesse Butler 	 * If the device no longer has a PHY pointer, clear the PHY pointer
22125c45adf0SJesse Butler 	 * from the work structure before we free it.  Otherwise, pmcs_pwork
22135c45adf0SJesse Butler 	 * may decrement the ref_count on a PHY that's been freed.
22145c45adf0SJesse Butler 	 */
22155c45adf0SJesse Butler 	if (xp->phy == NULL) {
22165c45adf0SJesse Butler 		pwrk->phy = NULL;
22175c45adf0SJesse Butler 	}
22185c45adf0SJesse Butler 
221939cd77a0SJesse Butler 	/*
222039cd77a0SJesse Butler 	 * We may arrive here due to a command timing out, which in turn
222139cd77a0SJesse Butler 	 * could be addressed in a different context.  So, free the work
222239cd77a0SJesse Butler 	 * back, but only after confirming it's not already been freed
222339cd77a0SJesse Butler 	 * elsewhere.
222439cd77a0SJesse Butler 	 */
22253492a3feSJesse Butler 	if (pwrk->htag != PMCS_TAG_FREE) {
22265c45adf0SJesse Butler 		pmcs_pwork(pwp, pwrk);
222739cd77a0SJesse Butler 	}
22285c45adf0SJesse Butler 
22295c45adf0SJesse Butler 	/*
22309aed1621SDavid Hollister 	 * If the device is gone, we only put this command on the completion
22319aed1621SDavid Hollister 	 * queue if the work structure is not marked dead.  If it's marked
22329aed1621SDavid Hollister 	 * dead, it will already have been put there.
22339aed1621SDavid Hollister 	 */
22344c06356bSdh142964 	if (xp->dev_gone) {
22354c06356bSdh142964 		mutex_exit(&xp->statlock);
22369aed1621SDavid Hollister 		if (!dead) {
2237429adc13SSrikanth, Ramana 			mutex_enter(&xp->aqlock);
2238429adc13SSrikanth, Ramana 			STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2239429adc13SSrikanth, Ramana 			mutex_exit(&xp->aqlock);
22405c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2241429adc13SSrikanth, Ramana 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2242429adc13SSrikanth, Ramana 			    __func__, (void *)sp, sp->cmd_tag);
22439aed1621SDavid Hollister 			mutex_enter(&pwp->cq_lock);
22449aed1621SDavid Hollister 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
22458f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
22469aed1621SDavid Hollister 			mutex_exit(&pwp->cq_lock);
2247c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
22489aed1621SDavid Hollister 			    "%s: Completing command for dead target 0x%p",
22499aed1621SDavid Hollister 			    __func__, (void *)xp);
22509aed1621SDavid Hollister 		}
22514c06356bSdh142964 		return;
22524c06356bSdh142964 	}
22534c06356bSdh142964 
22544c06356bSdh142964 	ASSERT(xp->actv_cnt > 0);
22554c06356bSdh142964 	if (--(xp->actv_cnt) == 0) {
22564c06356bSdh142964 		if (xp->draining) {
2257c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
22584c06356bSdh142964 			    "%s: waking up drain waiters", __func__);
22594c06356bSdh142964 			cv_signal(&pwp->drain_cv);
22604c06356bSdh142964 		}
22614c06356bSdh142964 	}
22624c06356bSdh142964 	mutex_exit(&xp->statlock);
2263c280a92bSDavid Hollister 
2264c280a92bSDavid Hollister 	/*
2265c280a92bSDavid Hollister 	 * If the status is other than OK, determine if it's something that
2266c280a92bSDavid Hollister 	 * is worth re-attempting enumeration.  If so, mark the PHY.
2267c280a92bSDavid Hollister 	 */
2268c280a92bSDavid Hollister 	if (sts != PMCOUT_STATUS_OK) {
2269c280a92bSDavid Hollister 		pmcs_status_disposition(pptr, sts);
2270c280a92bSDavid Hollister 	}
2271c280a92bSDavid Hollister 
22724c06356bSdh142964 	if (dead == 0) {
22734c06356bSdh142964 #ifdef	DEBUG
22744c06356bSdh142964 		pmcs_cmd_t *wp;
22754c06356bSdh142964 		mutex_enter(&xp->aqlock);
22764c06356bSdh142964 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
22774c06356bSdh142964 			if (wp == sp) {
22784c06356bSdh142964 				break;
22794c06356bSdh142964 			}
22804c06356bSdh142964 		}
22814c06356bSdh142964 		ASSERT(wp != NULL);
22824c06356bSdh142964 #else
22834c06356bSdh142964 		mutex_enter(&xp->aqlock);
22844c06356bSdh142964 #endif
22855c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
22869aed1621SDavid Hollister 		    "%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__,
22879aed1621SDavid Hollister 		    (void *)sp, sp->cmd_tag);
22884c06356bSdh142964 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
22894c06356bSdh142964 		if (aborted) {
2290c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
22914c06356bSdh142964 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
22924c06356bSdh142964 			    __func__, (void *)xp);
22934c06356bSdh142964 			cv_signal(&xp->abort_cv);
22944c06356bSdh142964 		}
22954c06356bSdh142964 		mutex_exit(&xp->aqlock);
22964c06356bSdh142964 	}
22974c06356bSdh142964 
22984c06356bSdh142964 	/*
22994c06356bSdh142964 	 * If do_ds_recovery is set, we need to initiate device state
23004c06356bSdh142964 	 * recovery.  In this case, we put this I/O back on the head of
23014c06356bSdh142964 	 * the wait queue to run again after recovery is complete
23024c06356bSdh142964 	 */
23034c06356bSdh142964 	if (do_ds_recovery) {
23044c06356bSdh142964 		mutex_enter(&xp->statlock);
23054c06356bSdh142964 		pmcs_start_dev_state_recovery(xp, pptr);
23064c06356bSdh142964 		mutex_exit(&xp->statlock);
2307c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p "
2308c3bc407cSdh142964 		    "back on wq during recovery for tgt 0x%p", __func__,
2309c3bc407cSdh142964 		    (void *)sp, (void *)xp);
23104c06356bSdh142964 		mutex_enter(&xp->wqlock);
23114c06356bSdh142964 		if (xp->wq_recovery_tail == NULL) {
23124c06356bSdh142964 			STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23134c06356bSdh142964 		} else {
23144c06356bSdh142964 			/*
23154c06356bSdh142964 			 * If there are other I/Os waiting at the head due to
23164c06356bSdh142964 			 * device state recovery, add this one in the right spot
23174c06356bSdh142964 			 * to maintain proper order.
23184c06356bSdh142964 			 */
23194c06356bSdh142964 			STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
23204c06356bSdh142964 			    cmd_next);
23214c06356bSdh142964 		}
23224c06356bSdh142964 		xp->wq_recovery_tail = sp;
23234c06356bSdh142964 		mutex_exit(&xp->wqlock);
23244c06356bSdh142964 	} else {
23254c06356bSdh142964 		/*
23264c06356bSdh142964 		 * If we're not initiating device state recovery and this
23274c06356bSdh142964 		 * command was not "dead", put it on the completion queue
23284c06356bSdh142964 		 */
23294c06356bSdh142964 		if (!dead) {
23304c06356bSdh142964 			mutex_enter(&pwp->cq_lock);
23314c06356bSdh142964 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
23328f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
23334c06356bSdh142964 			mutex_exit(&pwp->cq_lock);
23344c06356bSdh142964 		}
23354c06356bSdh142964 	}
23364c06356bSdh142964 }
23374c06356bSdh142964 
23384c06356bSdh142964 /*
23394c06356bSdh142964  * Run a SATA command (normal reads and writes),
23404c06356bSdh142964  * or block and schedule a SATL interpretation
23414c06356bSdh142964  * of the command.
23424c06356bSdh142964  *
23434c06356bSdh142964  * Called with pwrk lock held, returns unlocked.
23444c06356bSdh142964  */
23454c06356bSdh142964 
23464c06356bSdh142964 static int
23474c06356bSdh142964 pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
23484c06356bSdh142964 {
23494c06356bSdh142964 	pmcs_hw_t *pwp = CMD2PMC(sp);
23504c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
23514c06356bSdh142964 	pmcs_xscsi_t *xp;
23524c06356bSdh142964 	uint8_t cdb_base, asc, tag;
23534c06356bSdh142964 	uint32_t *ptr, iq, nblk, i, mtype;
23544c06356bSdh142964 	fis_t fis;
23554c06356bSdh142964 	size_t amt;
23564c06356bSdh142964 	uint64_t lba;
23574c06356bSdh142964 
23584c06356bSdh142964 	xp = pwrk->xp;
23593be32c0fSJesse Butler 	ASSERT(xp != NULL);
23604c06356bSdh142964 
23614c06356bSdh142964 	/*
23624c06356bSdh142964 	 * First, see if this is just a plain read/write command.
23634c06356bSdh142964 	 * If not, we have to queue it up for processing, block
23644c06356bSdh142964 	 * any additional commands from coming in, and wake up
23654c06356bSdh142964 	 * the thread that will process this command.
23664c06356bSdh142964 	 */
23674c06356bSdh142964 	cdb_base = pkt->pkt_cdbp[0] & 0x1f;
23684c06356bSdh142964 	if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
2369c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
2370c3bc407cSdh142964 		    "%s: special SATA cmd %p", __func__, (void *)sp);
23714c06356bSdh142964 
23724c06356bSdh142964 		ASSERT(xp->phy != NULL);
23734c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
23744c06356bSdh142964 		pmcs_lock_phy(xp->phy);
23754c06356bSdh142964 		mutex_enter(&xp->statlock);
23764c06356bSdh142964 		xp->special_needed = 1; /* Set the special_needed flag */
23774c06356bSdh142964 		STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
23784c06356bSdh142964 		if (pmcs_run_sata_special(pwp, xp)) {
23794c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
23804c06356bSdh142964 		}
23814c06356bSdh142964 		mutex_exit(&xp->statlock);
23824c06356bSdh142964 		pmcs_unlock_phy(xp->phy);
23834c06356bSdh142964 
23844c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
23854c06356bSdh142964 	}
23864c06356bSdh142964 
2387c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__);
23884c06356bSdh142964 
23894c06356bSdh142964 	mutex_enter(&xp->statlock);
2390b18a19c2SJesse Butler 	if (!xp->assigned) {
23914c06356bSdh142964 		mutex_exit(&xp->statlock);
23924c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
23934c06356bSdh142964 	}
23944c06356bSdh142964 	if (xp->special_running || xp->special_needed || xp->recover_wait) {
23954c06356bSdh142964 		mutex_exit(&xp->statlock);
23964c06356bSdh142964 		mutex_enter(&xp->wqlock);
23974c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23984c06356bSdh142964 		mutex_exit(&xp->wqlock);
23994c06356bSdh142964 		/*
24004c06356bSdh142964 		 * By the time we get here the special
24014c06356bSdh142964 		 * commands running or waiting to be run
24024c06356bSdh142964 		 * may have come and gone, so kick our
24034c06356bSdh142964 		 * worker to run the waiting queues
24044c06356bSdh142964 		 * just in case.
24054c06356bSdh142964 		 */
24064c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
24074c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
24084c06356bSdh142964 	}
24094c06356bSdh142964 	lba = xp->capacity;
24104c06356bSdh142964 	mutex_exit(&xp->statlock);
24114c06356bSdh142964 
24124c06356bSdh142964 	/*
24134c06356bSdh142964 	 * Extract data length and lba parameters out of the command. The
24144c06356bSdh142964 	 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
24154c06356bSdh142964 	 * values are considered illegal.
24164c06356bSdh142964 	 */
24174c06356bSdh142964 	asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
24184c06356bSdh142964 	if (asc) {
24194c06356bSdh142964 		uint8_t sns[18];
24204c06356bSdh142964 		bzero(sns, sizeof (sns));
24214c06356bSdh142964 		sns[0] = 0xf0;
24224c06356bSdh142964 		sns[2] = 0x5;
24234c06356bSdh142964 		sns[12] = asc;
24244c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
24254c06356bSdh142964 		    pwrk->phy->path);
24264c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
24274c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
24284c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24294c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
24304c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
24314c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
24324c06356bSdh142964 	}
24334c06356bSdh142964 
24344c06356bSdh142964 	/*
24354c06356bSdh142964 	 * If the command decodes as not moving any data, complete it here.
24364c06356bSdh142964 	 */
24374c06356bSdh142964 	amt = nblk;
24384c06356bSdh142964 	amt <<= 9;
24394c06356bSdh142964 	amt = pmcs_set_resid(pkt, amt, nblk << 9);
24404c06356bSdh142964 	if (amt == 0) {
24414c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
24424c06356bSdh142964 		    pwrk->phy->path);
24434c06356bSdh142964 		pmcs_pwork(pwp, pwrk);
24444c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
24454c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24464c06356bSdh142964 		PMCS_CQ_RUN_LOCKED(pwp);
24474c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
24484c06356bSdh142964 		return (PMCS_WQ_RUN_SUCCESS);
24494c06356bSdh142964 	}
24504c06356bSdh142964 
24514c06356bSdh142964 	/*
24524c06356bSdh142964 	 * Get an inbound queue entry for this I/O
24534c06356bSdh142964 	 */
24544c06356bSdh142964 	GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
24554c06356bSdh142964 	if (ptr == NULL) {
24564c06356bSdh142964 		/*
24574c06356bSdh142964 		 * This is a temporary failure not likely to unblocked by
24584c06356bSdh142964 		 * commands completing as the test for scheduling the
24594c06356bSdh142964 		 * restart of work is a per-device test.
24604c06356bSdh142964 		 */
24614c06356bSdh142964 		mutex_enter(&xp->wqlock);
24624c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24634c06356bSdh142964 		mutex_exit(&xp->wqlock);
24644c06356bSdh142964 		pmcs_dma_unload(pwp, sp);
24654c06356bSdh142964 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
2466c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
24674c06356bSdh142964 		    "%s: Failed to get IO IQ entry for tgt %d",
24684c06356bSdh142964 		    __func__, xp->target_num);
24694c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
24704c06356bSdh142964 	}
24714c06356bSdh142964 
24724c06356bSdh142964 	/*
24734c06356bSdh142964 	 * Get a tag.  At this point, hold statlock until the tagmap is
24744c06356bSdh142964 	 * updated (just prior to sending the cmd to the hardware).
24754c06356bSdh142964 	 */
24764c06356bSdh142964 	mutex_enter(&xp->statlock);
24774c06356bSdh142964 	for (tag = 0; tag < xp->qdepth; tag++) {
24784c06356bSdh142964 		if ((xp->tagmap & (1 << tag)) == 0) {
24794c06356bSdh142964 			break;
24804c06356bSdh142964 		}
24814c06356bSdh142964 	}
24824c06356bSdh142964 
24834c06356bSdh142964 	if (tag == xp->qdepth) {
24844c06356bSdh142964 		mutex_exit(&xp->statlock);
24854c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[iq]);
24864c06356bSdh142964 		mutex_enter(&xp->wqlock);
24874c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24884c06356bSdh142964 		mutex_exit(&xp->wqlock);
24894c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_OTHER);
24904c06356bSdh142964 	}
24914c06356bSdh142964 
24924c06356bSdh142964 	sp->cmd_satltag = (uint8_t)tag;
24934c06356bSdh142964 
24944c06356bSdh142964 	/*
24954c06356bSdh142964 	 * Set up the command
24964c06356bSdh142964 	 */
24974c06356bSdh142964 	bzero(fis, sizeof (fis));
24984c06356bSdh142964 	ptr[0] =
24994c06356bSdh142964 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
25004c06356bSdh142964 	ptr[1] = LE_32(pwrk->htag);
25014c06356bSdh142964 	ptr[2] = LE_32(pwrk->phy->device_id);
25024c06356bSdh142964 	ptr[3] = LE_32(amt);
25034c06356bSdh142964 
25044c06356bSdh142964 	if (xp->ncq) {
25054c06356bSdh142964 		mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
25064c06356bSdh142964 		fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
25074c06356bSdh142964 		if (cdb_base == SCMD_READ) {
25084c06356bSdh142964 			fis[0] |= (READ_FPDMA_QUEUED << 16);
25094c06356bSdh142964 		} else {
25104c06356bSdh142964 			fis[0] |= (WRITE_FPDMA_QUEUED << 16);
25114c06356bSdh142964 		}
25124c06356bSdh142964 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25134c06356bSdh142964 		fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
25144c06356bSdh142964 		fis[3] = tag << 3;
25154c06356bSdh142964 	} else {
25164c06356bSdh142964 		int op;
25174c06356bSdh142964 		fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
25184c06356bSdh142964 		if (xp->pio) {
25194c06356bSdh142964 			mtype = SATA_PROTOCOL_PIO;
25204c06356bSdh142964 			if (cdb_base == SCMD_READ) {
25214c06356bSdh142964 				op = READ_SECTORS_EXT;
25224c06356bSdh142964 			} else {
25234c06356bSdh142964 				op = WRITE_SECTORS_EXT;
25244c06356bSdh142964 			}
25254c06356bSdh142964 		} else {
25264c06356bSdh142964 			mtype = SATA_PROTOCOL_DMA;
25274c06356bSdh142964 			if (cdb_base == SCMD_READ) {
25284c06356bSdh142964 				op = READ_DMA_EXT;
25294c06356bSdh142964 			} else {
25304c06356bSdh142964 				op = WRITE_DMA_EXT;
25314c06356bSdh142964 			}
25324c06356bSdh142964 		}
25334c06356bSdh142964 		fis[0] |= (op << 16);
25344c06356bSdh142964 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25354c06356bSdh142964 		fis[2] = (lba >> 24) & 0xffffff;
25364c06356bSdh142964 		fis[3] = nblk;
25374c06356bSdh142964 	}
25384c06356bSdh142964 
25394c06356bSdh142964 	if (cdb_base == SCMD_READ) {
25404c06356bSdh142964 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
25414c06356bSdh142964 	} else {
25424c06356bSdh142964 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
25434c06356bSdh142964 	}
25444c06356bSdh142964 #ifdef	DEBUG
25454c06356bSdh142964 	/*
25464c06356bSdh142964 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
25474c06356bSdh142964 	 * event when this goes out on the wire.
25484c06356bSdh142964 	 */
25494c06356bSdh142964 	ptr[4] |= PMCIN_MESSAGE_REPORT;
25504c06356bSdh142964 #endif
25514c06356bSdh142964 	for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
25524c06356bSdh142964 		ptr[i+5] = LE_32(fis[i]);
25534c06356bSdh142964 	}
25544c06356bSdh142964 	if (pmcs_dma_load(pwp, sp, ptr)) {
25554c06356bSdh142964 		mutex_exit(&xp->statlock);
25564c06356bSdh142964 		mutex_exit(&pwp->iqp_lock[iq]);
25574c06356bSdh142964 		mutex_enter(&xp->wqlock);
25584c06356bSdh142964 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
25594c06356bSdh142964 		mutex_exit(&xp->wqlock);
2560c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
25614c06356bSdh142964 		    "%s: Failed to dma_load for tgt %d",
25624c06356bSdh142964 		    __func__, xp->target_num);
25634c06356bSdh142964 		return (PMCS_WQ_RUN_FAIL_RES);
25644c06356bSdh142964 
25654c06356bSdh142964 	}
25664c06356bSdh142964 
25674c06356bSdh142964 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
25684c06356bSdh142964 	mutex_exit(&pwrk->lock);
25694c06356bSdh142964 	xp->tagmap |= (1 << tag);
25704c06356bSdh142964 	xp->actv_cnt++;
25714c06356bSdh142964 	if (xp->actv_cnt > xp->maxdepth) {
25724c06356bSdh142964 		xp->maxdepth = xp->actv_cnt;
2573c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp,
2574c3bc407cSdh142964 		    "%s: max depth now %u", pwrk->phy->path, xp->maxdepth);
25754c06356bSdh142964 	}
25764c06356bSdh142964 	mutex_exit(&xp->statlock);
25774c06356bSdh142964 	mutex_enter(&xp->aqlock);
25784c06356bSdh142964 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
25794c06356bSdh142964 	mutex_exit(&xp->aqlock);
2580c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
2581c3bc407cSdh142964 	    "%s: giving pkt %p to hardware", __func__, (void *)pkt);
25824c06356bSdh142964 #ifdef DEBUG
25834c06356bSdh142964 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
25844c06356bSdh142964 #endif
25854c06356bSdh142964 	INC_IQ_ENTRY(pwp, iq);
25864c06356bSdh142964 
25874c06356bSdh142964 	return (PMCS_WQ_RUN_SUCCESS);
25884c06356bSdh142964 }
25894c06356bSdh142964 
25904c06356bSdh142964 /*
25914c06356bSdh142964  * Complete a SATA command.  Called with pwrk lock held.
25924c06356bSdh142964  */
25934c06356bSdh142964 void
25944c06356bSdh142964 pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
25954c06356bSdh142964 {
25964c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
25974c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
25984c06356bSdh142964 	pmcs_phy_t *pptr = pwrk->phy;
25994c06356bSdh142964 	int dead;
26004c06356bSdh142964 	uint32_t sts;
26014c06356bSdh142964 	pmcs_xscsi_t *xp;
26024c06356bSdh142964 	boolean_t aborted = B_FALSE;
26034c06356bSdh142964 
26044c06356bSdh142964 	xp = pwrk->xp;
26054c06356bSdh142964 	ASSERT(xp != NULL);
26064c06356bSdh142964 
26074c06356bSdh142964 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
26084c06356bSdh142964 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
26094c06356bSdh142964 	    hrtime_t, gethrtime());
26104c06356bSdh142964 
26114c06356bSdh142964 	dead = pwrk->dead;
26124c06356bSdh142964 
26134c06356bSdh142964 	if (msg) {
26144c06356bSdh142964 		sts = LE_32(msg[2]);
26154c06356bSdh142964 	} else {
26164c06356bSdh142964 		sts = 0;
26174c06356bSdh142964 	}
26184c06356bSdh142964 
26194c06356bSdh142964 	if (dead != 0) {
2620c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2621c3bc407cSdh142964 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
26224c06356bSdh142964 		goto out;
26234c06356bSdh142964 	}
26244c06356bSdh142964 	if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
26254c06356bSdh142964 	    (sts != PMCOUT_STATUS_ABORTED)) {
2626c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
26274c06356bSdh142964 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
26284c06356bSdh142964 		    __func__, (void *)sp, pwrk->htag, pptr->path);
26294c06356bSdh142964 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
26304c06356bSdh142964 		/* pkt_reason already set to CMD_TIMEOUT */
26314c06356bSdh142964 		ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
26324c06356bSdh142964 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
26334c06356bSdh142964 		    STATE_SENT_CMD;
26344c06356bSdh142964 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
26354c06356bSdh142964 		goto out;
26364c06356bSdh142964 	}
26374c06356bSdh142964 
2638c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done",
26394c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num);
26404c06356bSdh142964 
26414c06356bSdh142964 	/*
26424c06356bSdh142964 	 * If the status isn't okay but not underflow,
26434c06356bSdh142964 	 * step to the side and parse the (possible) error.
26444c06356bSdh142964 	 */
26454c06356bSdh142964 #ifdef DEBUG
26464c06356bSdh142964 	if (msg) {
26474c06356bSdh142964 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
26484c06356bSdh142964 	}
26494c06356bSdh142964 #endif
26504c06356bSdh142964 	if (!msg) {
26514c06356bSdh142964 		goto out;
26524c06356bSdh142964 	}
26534c06356bSdh142964 
26544c06356bSdh142964 	/*
26554c06356bSdh142964 	 * If the status isn't okay or we got a FIS response of some kind,
26564c06356bSdh142964 	 * step to the side and parse the (possible) error.
26574c06356bSdh142964 	 */
26584c06356bSdh142964 	if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
26594c06356bSdh142964 		if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
26604c06356bSdh142964 			mutex_exit(&pwrk->lock);
26614c06356bSdh142964 			pmcs_lock_phy(pptr);
26624c06356bSdh142964 			mutex_enter(&xp->statlock);
26634c06356bSdh142964 			if ((xp->resetting == 0) && (xp->reset_success != 0) &&
26644c06356bSdh142964 			    (xp->reset_wait == 0)) {
26654c06356bSdh142964 				mutex_exit(&xp->statlock);
26664c06356bSdh142964 				if (pmcs_reset_phy(pwp, pptr,
26674c06356bSdh142964 				    PMCS_PHYOP_LINK_RESET) != 0) {
2668c3bc407cSdh142964 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2669c3bc407cSdh142964 					    "%s: PHY (%s) Local Control/Link "
2670c3bc407cSdh142964 					    "Reset FAILED as part of error "
2671c3bc407cSdh142964 					    "recovery", __func__, pptr->path);
26724c06356bSdh142964 				}
26734c06356bSdh142964 				mutex_enter(&xp->statlock);
26744c06356bSdh142964 			}
26754c06356bSdh142964 			mutex_exit(&xp->statlock);
26764c06356bSdh142964 			pmcs_unlock_phy(pptr);
26774c06356bSdh142964 			mutex_enter(&pwrk->lock);
26784c06356bSdh142964 		}
2679658280b6SDavid Hollister 		pmcs_ioerror(pwp, SATA, pwrk, msg, sts);
26804c06356bSdh142964 	} else {
26814c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
26824c06356bSdh142964 		    pwrk->phy->path);
26834c06356bSdh142964 		pkt->pkt_state |= STATE_XFERRED_DATA;
26844c06356bSdh142964 		pkt->pkt_resid = 0;
26854c06356bSdh142964 	}
26864c06356bSdh142964 
2687c3bc407cSdh142964 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
26884c06356bSdh142964 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
26894c06356bSdh142964 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
26904c06356bSdh142964 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
26914c06356bSdh142964 
26924c06356bSdh142964 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2693c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
26944c06356bSdh142964 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
26954c06356bSdh142964 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
26964c06356bSdh142964 		aborted = B_TRUE;
26974c06356bSdh142964 	}
26984c06356bSdh142964 
26994c06356bSdh142964 out:
27004c06356bSdh142964 	pmcs_dma_unload(pwp, sp);
27014c06356bSdh142964 	mutex_enter(&xp->statlock);
27024c06356bSdh142964 	xp->tagmap &= ~(1 << sp->cmd_satltag);
27034c06356bSdh142964 
27045c45adf0SJesse Butler 	/*
27055c45adf0SJesse Butler 	 * If the device no longer has a PHY pointer, clear the PHY pointer
27065c45adf0SJesse Butler 	 * from the work structure before we free it.  Otherwise, pmcs_pwork
27075c45adf0SJesse Butler 	 * may decrement the ref_count on a PHY that's been freed.
27085c45adf0SJesse Butler 	 */
27095c45adf0SJesse Butler 	if (xp->phy == NULL) {
27105c45adf0SJesse Butler 		pwrk->phy = NULL;
27115c45adf0SJesse Butler 	}
27125c45adf0SJesse Butler 
271339cd77a0SJesse Butler 	/*
271439cd77a0SJesse Butler 	 * We may arrive here due to a command timing out, which in turn
271539cd77a0SJesse Butler 	 * could be addressed in a different context.  So, free the work
271639cd77a0SJesse Butler 	 * back, but only after confirming it's not already been freed
271739cd77a0SJesse Butler 	 * elsewhere.
271839cd77a0SJesse Butler 	 */
27193492a3feSJesse Butler 	if (pwrk->htag != PMCS_TAG_FREE) {
27205c45adf0SJesse Butler 		pmcs_pwork(pwp, pwrk);
272139cd77a0SJesse Butler 	}
27225c45adf0SJesse Butler 
27234c06356bSdh142964 	if (xp->dev_gone) {
27244c06356bSdh142964 		mutex_exit(&xp->statlock);
27259aed1621SDavid Hollister 		if (!dead) {
2726429adc13SSrikanth, Ramana 			mutex_enter(&xp->aqlock);
2727429adc13SSrikanth, Ramana 			STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2728429adc13SSrikanth, Ramana 			mutex_exit(&xp->aqlock);
27295c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2730429adc13SSrikanth, Ramana 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2731429adc13SSrikanth, Ramana 			    __func__, (void *)sp, sp->cmd_tag);
27329aed1621SDavid Hollister 			mutex_enter(&pwp->cq_lock);
27339aed1621SDavid Hollister 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
27348f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
27359aed1621SDavid Hollister 			mutex_exit(&pwp->cq_lock);
2736c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
27379aed1621SDavid Hollister 			    "%s: Completing command for dead target 0x%p",
27389aed1621SDavid Hollister 			    __func__, (void *)xp);
27399aed1621SDavid Hollister 		}
27404c06356bSdh142964 		return;
27414c06356bSdh142964 	}
27424c06356bSdh142964 
27434c06356bSdh142964 	ASSERT(xp->actv_cnt > 0);
27444c06356bSdh142964 	if (--(xp->actv_cnt) == 0) {
27454c06356bSdh142964 		if (xp->draining) {
2746c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
27474c06356bSdh142964 			    "%s: waking up drain waiters", __func__);
27484c06356bSdh142964 			cv_signal(&pwp->drain_cv);
27494c06356bSdh142964 		} else if (xp->special_needed) {
27504c06356bSdh142964 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
27514c06356bSdh142964 		}
27524c06356bSdh142964 	}
27534c06356bSdh142964 	mutex_exit(&xp->statlock);
27544c06356bSdh142964 
2755c280a92bSDavid Hollister 	/*
2756c280a92bSDavid Hollister 	 * If the status is other than OK, determine if it's something that
2757c280a92bSDavid Hollister 	 * is worth re-attempting enumeration.  If so, mark the PHY.
2758c280a92bSDavid Hollister 	 */
2759c280a92bSDavid Hollister 	if (sts != PMCOUT_STATUS_OK) {
2760c280a92bSDavid Hollister 		pmcs_status_disposition(pptr, sts);
2761c280a92bSDavid Hollister 	}
2762c280a92bSDavid Hollister 
27634c06356bSdh142964 	if (dead == 0) {
27644c06356bSdh142964 #ifdef	DEBUG
27654c06356bSdh142964 		pmcs_cmd_t *wp;
27664c06356bSdh142964 		mutex_enter(&xp->aqlock);
27674c06356bSdh142964 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
27684c06356bSdh142964 			if (wp == sp) {
27694c06356bSdh142964 				break;
27704c06356bSdh142964 			}
27714c06356bSdh142964 		}
27724c06356bSdh142964 		ASSERT(wp != NULL);
27734c06356bSdh142964 #else
27744c06356bSdh142964 		mutex_enter(&xp->aqlock);
27754c06356bSdh142964 #endif
27764c06356bSdh142964 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
27774c06356bSdh142964 		if (aborted) {
2778c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
27794c06356bSdh142964 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
27804c06356bSdh142964 			    __func__, (void *)xp);
27814c06356bSdh142964 			cv_signal(&xp->abort_cv);
27824c06356bSdh142964 		}
27834c06356bSdh142964 		mutex_exit(&xp->aqlock);
27844c06356bSdh142964 		mutex_enter(&pwp->cq_lock);
27854c06356bSdh142964 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
27868f514e74SDavid Hollister 		PMCS_CQ_RUN_LOCKED(pwp);
27874c06356bSdh142964 		mutex_exit(&pwp->cq_lock);
27884c06356bSdh142964 	}
27894c06356bSdh142964 }
27904c06356bSdh142964 
27914c06356bSdh142964 static uint8_t
27924c06356bSdh142964 pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
27934c06356bSdh142964 {
27944c06356bSdh142964 	uint8_t asc = 0;
27954c06356bSdh142964 	switch (cdb[0]) {
27964c06356bSdh142964 	case SCMD_READ_G5:
27974c06356bSdh142964 	case SCMD_WRITE_G5:
27984c06356bSdh142964 		*xfr =
27994c06356bSdh142964 		    (((uint32_t)cdb[10]) <<  24) |
28004c06356bSdh142964 		    (((uint32_t)cdb[11]) <<  16) |
28014c06356bSdh142964 		    (((uint32_t)cdb[12]) <<   8) |
28024c06356bSdh142964 		    ((uint32_t)cdb[13]);
28034c06356bSdh142964 		*lba =
28044c06356bSdh142964 		    (((uint64_t)cdb[2]) << 56) |
28054c06356bSdh142964 		    (((uint64_t)cdb[3]) << 48) |
28064c06356bSdh142964 		    (((uint64_t)cdb[4]) << 40) |
28074c06356bSdh142964 		    (((uint64_t)cdb[5]) << 32) |
28084c06356bSdh142964 		    (((uint64_t)cdb[6]) << 24) |
28094c06356bSdh142964 		    (((uint64_t)cdb[7]) << 16) |
28104c06356bSdh142964 		    (((uint64_t)cdb[8]) <<  8) |
28114c06356bSdh142964 		    ((uint64_t)cdb[9]);
28124c06356bSdh142964 		/* Check for illegal bits */
28134c06356bSdh142964 		if (cdb[15]) {
28144c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
28154c06356bSdh142964 		}
28164c06356bSdh142964 		break;
28174c06356bSdh142964 	case SCMD_READ_G4:
28184c06356bSdh142964 	case SCMD_WRITE_G4:
28194c06356bSdh142964 		*xfr =
28204c06356bSdh142964 		    (((uint32_t)cdb[6]) <<  16) |
28214c06356bSdh142964 		    (((uint32_t)cdb[7]) <<   8) |
28224c06356bSdh142964 		    ((uint32_t)cdb[8]);
28234c06356bSdh142964 		*lba =
28244c06356bSdh142964 		    (((uint32_t)cdb[2]) << 24) |
28254c06356bSdh142964 		    (((uint32_t)cdb[3]) << 16) |
28264c06356bSdh142964 		    (((uint32_t)cdb[4]) <<  8) |
28274c06356bSdh142964 		    ((uint32_t)cdb[5]);
28284c06356bSdh142964 		/* Check for illegal bits */
28294c06356bSdh142964 		if (cdb[11]) {
28304c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
28314c06356bSdh142964 		}
28324c06356bSdh142964 		break;
28334c06356bSdh142964 	case SCMD_READ_G1:
28344c06356bSdh142964 	case SCMD_WRITE_G1:
28354c06356bSdh142964 		*xfr = (((uint32_t)cdb[7]) <<  8) | ((uint32_t)cdb[8]);
28364c06356bSdh142964 		*lba =
28374c06356bSdh142964 		    (((uint32_t)cdb[2]) << 24) |
28384c06356bSdh142964 		    (((uint32_t)cdb[3]) << 16) |
28394c06356bSdh142964 		    (((uint32_t)cdb[4]) <<  8) |
28404c06356bSdh142964 		    ((uint32_t)cdb[5]);
28414c06356bSdh142964 		/* Check for illegal bits */
28424c06356bSdh142964 		if (cdb[9]) {
28434c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
28444c06356bSdh142964 		}
28454c06356bSdh142964 		break;
28464c06356bSdh142964 	case SCMD_READ:
28474c06356bSdh142964 	case SCMD_WRITE:
28484c06356bSdh142964 		*xfr = cdb[4];
28494c06356bSdh142964 		if (*xfr == 0) {
28504c06356bSdh142964 			*xfr = 256;
28514c06356bSdh142964 		}
28524c06356bSdh142964 		*lba =
28534c06356bSdh142964 		    (((uint32_t)cdb[1] & 0x1f) << 16) |
28544c06356bSdh142964 		    (((uint32_t)cdb[2]) << 8) |
28554c06356bSdh142964 		    ((uint32_t)cdb[3]);
28564c06356bSdh142964 		/* Check for illegal bits */
28574c06356bSdh142964 		if (cdb[5]) {
28584c06356bSdh142964 			asc = 0x24;	/* invalid field in cdb */
28594c06356bSdh142964 		}
28604c06356bSdh142964 		break;
28614c06356bSdh142964 	}
28624c06356bSdh142964 
28634c06356bSdh142964 	if (asc == 0) {
28644c06356bSdh142964 		if ((*lba + *xfr) > lbamax) {
28654c06356bSdh142964 			asc = 0x21;	/* logical block out of range */
28664c06356bSdh142964 		}
28674c06356bSdh142964 	}
28684c06356bSdh142964 	return (asc);
28694c06356bSdh142964 }
28704c06356bSdh142964 
28714c06356bSdh142964 /*
28724c06356bSdh142964  * Called with pwrk lock held.
28734c06356bSdh142964  */
28744c06356bSdh142964 static void
2875658280b6SDavid Hollister pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w,
2876658280b6SDavid Hollister     uint32_t status)
28774c06356bSdh142964 {
28784c06356bSdh142964 	static uint8_t por[] = {
28794c06356bSdh142964 	    0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
28804c06356bSdh142964 	};
28814c06356bSdh142964 	static uint8_t parity[] = {
28824c06356bSdh142964 	    0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
28834c06356bSdh142964 	};
28844c06356bSdh142964 	const char *msg;
28854c06356bSdh142964 	char buf[20];
28864c06356bSdh142964 	pmcs_cmd_t *sp = pwrk->arg;
28874c06356bSdh142964 	pmcs_phy_t *phyp = pwrk->phy;
28884c06356bSdh142964 	struct scsi_pkt *pkt = CMD2PKT(sp);
28894c06356bSdh142964 	uint32_t resid;
28904c06356bSdh142964 
28914c06356bSdh142964 	ASSERT(w != NULL);
28924c06356bSdh142964 	resid = LE_32(w[3]);
28934c06356bSdh142964 
28944c06356bSdh142964 	msg = pmcs_status_str(status);
28954c06356bSdh142964 	if (msg == NULL) {
28964c06356bSdh142964 		(void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
28974c06356bSdh142964 		msg = buf;
28984c06356bSdh142964 	}
28994c06356bSdh142964 
29004c06356bSdh142964 	if (status != PMCOUT_STATUS_OK) {
29019aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, NULL,
29024c06356bSdh142964 		    "%s: device %s tag 0x%x status %s @ %llu", __func__,
29034c06356bSdh142964 		    phyp->path, pwrk->htag, msg,
29044c06356bSdh142964 		    (unsigned long long)gethrtime());
29054c06356bSdh142964 	}
29064c06356bSdh142964 
29074c06356bSdh142964 	pkt->pkt_reason = CMD_CMPLT;		/* default reason */
29084c06356bSdh142964 
29094c06356bSdh142964 	switch (status) {
29104c06356bSdh142964 	case PMCOUT_STATUS_OK:
29114c06356bSdh142964 		if (t == SATA) {
29124c06356bSdh142964 			int i;
29134c06356bSdh142964 			fis_t fis;
29144c06356bSdh142964 			for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
29154c06356bSdh142964 				fis[i] = LE_32(w[4+i]);
29164c06356bSdh142964 			}
29174c06356bSdh142964 			if ((fis[0] & 0xff) != FIS_REG_D2H) {
2918c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
29194c06356bSdh142964 				    "unexpected fis code 0x%x", fis[0] & 0xff);
29204c06356bSdh142964 			} else {
2921c3bc407cSdh142964 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
2922c3bc407cSdh142964 				    "FIS ERROR");
29234c06356bSdh142964 				pmcs_fis_dump(pwp, fis);
29244c06356bSdh142964 			}
29254c06356bSdh142964 			pkt->pkt_reason = CMD_TRAN_ERR;
29264c06356bSdh142964 			break;
29274c06356bSdh142964 		}
29284c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29294c06356bSdh142964 		break;
29304c06356bSdh142964 
29314c06356bSdh142964 	case PMCOUT_STATUS_ABORTED:
29324c06356bSdh142964 		/*
29334c06356bSdh142964 		 * Command successfully aborted.
29344c06356bSdh142964 		 */
29354c06356bSdh142964 		if (phyp->dead) {
29364c06356bSdh142964 			pkt->pkt_reason = CMD_DEV_GONE;
29374c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
29384c06356bSdh142964 		} else if (pwrk->ssp_event != 0) {
29394c06356bSdh142964 			pkt->pkt_reason = CMD_TRAN_ERR;
29404c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
29414c06356bSdh142964 		} else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
29424c06356bSdh142964 			pkt->pkt_reason = CMD_TIMEOUT;
29434c06356bSdh142964 			pkt->pkt_statistics |= STAT_TIMEOUT;
29444c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29454c06356bSdh142964 			    STATE_SENT_CMD;
29464c06356bSdh142964 		} else {
29474c06356bSdh142964 			pkt->pkt_reason = CMD_ABORTED;
29484c06356bSdh142964 			pkt->pkt_statistics |= STAT_ABORTED;
29494c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29504c06356bSdh142964 			    STATE_SENT_CMD;
29514c06356bSdh142964 		}
29524c06356bSdh142964 
29534c06356bSdh142964 		/*
29544c06356bSdh142964 		 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
29554c06356bSdh142964 		 * this point, so go ahead and mark it as aborted.
29564c06356bSdh142964 		 */
29574c06356bSdh142964 		pwrk->state = PMCS_WORK_STATE_ABORTED;
29584c06356bSdh142964 		break;
29594c06356bSdh142964 
29604c06356bSdh142964 	case PMCOUT_STATUS_UNDERFLOW:
29614c06356bSdh142964 		/*
29624c06356bSdh142964 		 * This will only get called for SATA
29634c06356bSdh142964 		 */
29644c06356bSdh142964 		pkt->pkt_resid = resid;
29654c06356bSdh142964 		if (pkt->pkt_dma_len < pkt->pkt_resid) {
29664c06356bSdh142964 			(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
29674c06356bSdh142964 		}
29684c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29694c06356bSdh142964 		break;
29704c06356bSdh142964 
29714c06356bSdh142964 	case PMCOUT_STATUS_NO_DEVICE:
29724c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
29734c06356bSdh142964 		pkt->pkt_reason = CMD_DEV_GONE;
29744c06356bSdh142964 		break;
29754c06356bSdh142964 
29764c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
29774c06356bSdh142964 		/*
29784c06356bSdh142964 		 * Need to do rediscovery. We probably have
29794c06356bSdh142964 		 * the wrong device (disk swap), so kill
29804c06356bSdh142964 		 * this one.
29814c06356bSdh142964 		 */
29824c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
29834c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
29844c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
29859aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
29864c06356bSdh142964 		/*
29874c06356bSdh142964 		 * Need to do rediscovery.
29884c06356bSdh142964 		 */
29894c06356bSdh142964 		if (!phyp->dead) {
29904c06356bSdh142964 			mutex_exit(&pwrk->lock);
29914c06356bSdh142964 			pmcs_lock_phy(pwrk->phy);
29924c06356bSdh142964 			pmcs_kill_changed(pwp, pwrk->phy, 0);
29934c06356bSdh142964 			pmcs_unlock_phy(pwrk->phy);
29944c06356bSdh142964 			mutex_enter(&pwrk->lock);
29954c06356bSdh142964 			pkt->pkt_reason = CMD_INCOMPLETE;
29964c06356bSdh142964 			pkt->pkt_state = STATE_GOT_BUS;
29974c06356bSdh142964 		} else {
29984c06356bSdh142964 			pkt->pkt_reason = CMD_DEV_GONE;
29994c06356bSdh142964 		}
30004c06356bSdh142964 		break;
30014c06356bSdh142964 
30024c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
30034c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
30044c06356bSdh142964 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
30054c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
30064c06356bSdh142964 		/* cmd is pending on the target */
30074c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
30084c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
30094c06356bSdh142964 		/* transitory - commands sent while in NCQ failure mode */
30104c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
30114c06356bSdh142964 		/* NCQ failure */
30124c06356bSdh142964 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
30134c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERR_BREAK:
30144c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
30154c06356bSdh142964 		pkt->pkt_reason = CMD_INCOMPLETE;
30164c06356bSdh142964 		pkt->pkt_state = STATE_GOT_BUS;
30174c06356bSdh142964 		break;
30184c06356bSdh142964 
30194c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
3020658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3021658280b6SDavid Hollister 		    "STATUS_BUSY for htag 0x%08x", sp->cmd_tag);
30224c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
30234c06356bSdh142964 		break;
30244c06356bSdh142964 
30254c06356bSdh142964 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
30264c06356bSdh142964 		/* synthesize a RESERVATION CONFLICT */
3027499cfd15SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3028499cfd15SDavid Hollister 		    "%s: Potential affiliation active on 0x%" PRIx64, __func__,
3029499cfd15SDavid Hollister 		    pmcs_barray2wwn(phyp->sas_address));
30304c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
30314c06356bSdh142964 		    0, phyp->path);
30324c06356bSdh142964 		break;
30334c06356bSdh142964 
30344c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
30354c06356bSdh142964 		/* synthesize a power-on/reset */
30364c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
30374c06356bSdh142964 		    phyp->path);
30384c06356bSdh142964 		break;
30394c06356bSdh142964 
30404c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
30414c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
30424c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
30434c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
30444c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
30454c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
30464c06356bSdh142964 		/* synthesize a PARITY ERROR */
30474c06356bSdh142964 		pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
30484c06356bSdh142964 		    sizeof (parity), phyp->path);
30494c06356bSdh142964 		break;
30504c06356bSdh142964 
30514c06356bSdh142964 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
30524c06356bSdh142964 	case PMCOUT_STATUS_IO_NOT_VALID:
30534c06356bSdh142964 	case PMCOUT_STATUS_PROG_ERROR:
30544c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
30554c06356bSdh142964 	case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
30564c06356bSdh142964 	default:
30574c06356bSdh142964 		pkt->pkt_reason = CMD_TRAN_ERR;
30584c06356bSdh142964 		break;
30594c06356bSdh142964 	}
30604c06356bSdh142964 }
30614c06356bSdh142964 
30624c06356bSdh142964 /*
30634c06356bSdh142964  * Latch up SCSI status
30644c06356bSdh142964  */
30654c06356bSdh142964 
30664c06356bSdh142964 void
30674c06356bSdh142964 pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
30684c06356bSdh142964     uint8_t *snsp, size_t snslen, char *path)
30694c06356bSdh142964 {
30704c06356bSdh142964 	static const char c1[] =
30714c06356bSdh142964 	    "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
30724c06356bSdh142964 	    "HTAG 0x%x @ %llu";
30734c06356bSdh142964 	static const char c2[] =
30744c06356bSdh142964 	    "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
30754c06356bSdh142964 
30764c06356bSdh142964 	CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
30774c06356bSdh142964 	    STATE_SENT_CMD | STATE_GOT_STATUS;
30784c06356bSdh142964 	CMD2PKT(sp)->pkt_scbp[0] = status;
30794c06356bSdh142964 
30804c06356bSdh142964 	if (status == STATUS_CHECK && snsp &&
30814c06356bSdh142964 	    (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
30824c06356bSdh142964 		struct scsi_arq_status *aqp =
30834c06356bSdh142964 		    (void *) CMD2PKT(sp)->pkt_scbp;
30844c06356bSdh142964 		size_t amt = sizeof (struct scsi_extended_sense);
30854c06356bSdh142964 		uint8_t key = scsi_sense_key(snsp);
30864c06356bSdh142964 		uint8_t asc = scsi_sense_asc(snsp);
30874c06356bSdh142964 		uint8_t ascq = scsi_sense_ascq(snsp);
30884c06356bSdh142964 		if (amt > snslen) {
30894c06356bSdh142964 			amt = snslen;
30904c06356bSdh142964 		}
3091c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path,
3092c3bc407cSdh142964 		    status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
30934c06356bSdh142964 		    sp->cmd_tag, (unsigned long long)gethrtime());
30944c06356bSdh142964 		CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
30954c06356bSdh142964 		(*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
30964c06356bSdh142964 		aqp->sts_rqpkt_statistics = 0;
30974c06356bSdh142964 		aqp->sts_rqpkt_reason = CMD_CMPLT;
30984c06356bSdh142964 		aqp->sts_rqpkt_state = STATE_GOT_BUS |
30994c06356bSdh142964 		    STATE_GOT_TARGET | STATE_SENT_CMD |
31004c06356bSdh142964 		    STATE_XFERRED_DATA | STATE_GOT_STATUS;
31014c06356bSdh142964 		(void) memcpy(&aqp->sts_sensedata, snsp, amt);
31024c06356bSdh142964 		if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
31034c06356bSdh142964 			aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
31044c06356bSdh142964 			aqp->sts_rqpkt_state = 0;
31054c06356bSdh142964 			aqp->sts_rqpkt_resid =
31064c06356bSdh142964 			    sizeof (struct scsi_extended_sense);
31074c06356bSdh142964 		} else {
31084c06356bSdh142964 			aqp->sts_rqpkt_resid =
31094c06356bSdh142964 			    sizeof (struct scsi_extended_sense) - amt;
31104c06356bSdh142964 		}
31114c06356bSdh142964 	} else if (status) {
3112c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2,
31134c06356bSdh142964 		    path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
31144c06356bSdh142964 		    sp->cmd_tag, (unsigned long long)gethrtime());
31154c06356bSdh142964 	}
31164c06356bSdh142964 
31174c06356bSdh142964 	CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
31184c06356bSdh142964 }
31194c06356bSdh142964 
31204c06356bSdh142964 /*
31214c06356bSdh142964  * Calculate and set packet residual and return the amount
31224c06356bSdh142964  * left over after applying various filters.
31234c06356bSdh142964  */
31244c06356bSdh142964 size_t
31254c06356bSdh142964 pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
31264c06356bSdh142964 {
31274c06356bSdh142964 	pkt->pkt_resid = cdbamt;
31284c06356bSdh142964 	if (amt > pkt->pkt_resid) {
31294c06356bSdh142964 		amt = pkt->pkt_resid;
31304c06356bSdh142964 	}
31314c06356bSdh142964 	if (amt > pkt->pkt_dma_len) {
31324c06356bSdh142964 		amt = pkt->pkt_dma_len;
31334c06356bSdh142964 	}
31344c06356bSdh142964 	return (amt);
31354c06356bSdh142964 }
31364c06356bSdh142964 
31374c06356bSdh142964 /*
3138c280a92bSDavid Hollister  * Return the existing target softstate (unlocked) if there is one.  If so,
3139c280a92bSDavid Hollister  * the PHY is locked and that lock must be freed by the caller after the
3140c280a92bSDavid Hollister  * target/PHY linkage is established.  If there isn't one, and alloc_tgt is
3141c280a92bSDavid Hollister  * TRUE, then allocate one.
31424c06356bSdh142964  */
31434c06356bSdh142964 pmcs_xscsi_t *
31445c45adf0SJesse Butler pmcs_get_target(pmcs_iport_t *iport, char *tgt_port, boolean_t alloc_tgt)
31454c06356bSdh142964 {
31464c06356bSdh142964 	pmcs_hw_t *pwp = iport->pwp;
31474c06356bSdh142964 	pmcs_phy_t *phyp;
31484c06356bSdh142964 	pmcs_xscsi_t *tgt;
31494c06356bSdh142964 	uint64_t wwn;
31504c06356bSdh142964 	char unit_address[PMCS_MAX_UA_SIZE];
31514c06356bSdh142964 	int ua_form = 1;
31524c06356bSdh142964 
31534c06356bSdh142964 	/*
31544c06356bSdh142964 	 * Find the PHY for this target
31554c06356bSdh142964 	 */
31564c06356bSdh142964 	phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
31574c06356bSdh142964 	if (phyp == NULL) {
3158c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
3159c3bc407cSdh142964 		    "%s: No PHY for target @ %s", __func__, tgt_port);
31604c06356bSdh142964 		return (NULL);
31614c06356bSdh142964 	}
31624c06356bSdh142964 
31634c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
31644c06356bSdh142964 
31654c06356bSdh142964 	if (tgt) {
3166c280a92bSDavid Hollister 		mutex_enter(&tgt->statlock);
31674c06356bSdh142964 		/*
31684c06356bSdh142964 		 * There's already a target.  Check its PHY pointer to see
31694c06356bSdh142964 		 * if we need to clear the old linkages
31704c06356bSdh142964 		 */
31714c06356bSdh142964 		if (tgt->phy && (tgt->phy != phyp)) {
3172c3bc407cSdh142964 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
31734c06356bSdh142964 			    "%s: Target PHY updated from %p to %p", __func__,
31744c06356bSdh142964 			    (void *)tgt->phy, (void *)phyp);
31754c06356bSdh142964 			if (!IS_ROOT_PHY(tgt->phy)) {
31764c06356bSdh142964 				pmcs_dec_phy_ref_count(tgt->phy);
31774c06356bSdh142964 				pmcs_inc_phy_ref_count(phyp);
31784c06356bSdh142964 			}
31794c06356bSdh142964 			tgt->phy->target = NULL;
31804c06356bSdh142964 		}
31814c06356bSdh142964 
3182c280a92bSDavid Hollister 		/*
3183c280a92bSDavid Hollister 		 * If this target has no PHY pointer and alloc_tgt is FALSE,
3184c280a92bSDavid Hollister 		 * that implies we expect the target to already exist.  This
3185c280a92bSDavid Hollister 		 * implies that there has already been a tran_tgt_init on at
3186c280a92bSDavid Hollister 		 * least one LU.
3187c280a92bSDavid Hollister 		 */
3188c280a92bSDavid Hollister 		if ((tgt->phy == NULL) && !alloc_tgt) {
3189c280a92bSDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt,
3190c280a92bSDavid Hollister 			    "%s: Establish linkage from new PHY to old target @"
3191c280a92bSDavid Hollister 			    "%s", __func__, tgt->unit_address);
3192c280a92bSDavid Hollister 			for (int idx = 0; idx < tgt->ref_count; idx++) {
3193c280a92bSDavid Hollister 				pmcs_inc_phy_ref_count(phyp);
3194c280a92bSDavid Hollister 			}
3195c280a92bSDavid Hollister 		}
3196c280a92bSDavid Hollister 
31974c06356bSdh142964 		tgt->phy = phyp;
31984c06356bSdh142964 		phyp->target = tgt;
3199c280a92bSDavid Hollister 
3200c280a92bSDavid Hollister 		mutex_exit(&tgt->statlock);
32014c06356bSdh142964 		return (tgt);
32024c06356bSdh142964 	}
32034c06356bSdh142964 
32044c06356bSdh142964 	/*
32054c06356bSdh142964 	 * Make sure the PHY we found is on the correct iport
32064c06356bSdh142964 	 */
32074c06356bSdh142964 	if (phyp->iport != iport) {
3208c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
32094c06356bSdh142964 		    "%s: No target at %s on this iport", __func__, tgt_port);
32104c06356bSdh142964 		pmcs_unlock_phy(phyp);
32114c06356bSdh142964 		return (NULL);
32124c06356bSdh142964 	}
32134c06356bSdh142964 
32144c06356bSdh142964 	/*
32155c45adf0SJesse Butler 	 * If this was just a lookup (i.e. alloc_tgt is false), return now.
32165c45adf0SJesse Butler 	 */
32175c45adf0SJesse Butler 	if (alloc_tgt == B_FALSE) {
32185c45adf0SJesse Butler 		pmcs_unlock_phy(phyp);
32195c45adf0SJesse Butler 		return (NULL);
32205c45adf0SJesse Butler 	}
32215c45adf0SJesse Butler 
32225c45adf0SJesse Butler 	/*
32234c06356bSdh142964 	 * Allocate the new softstate
32244c06356bSdh142964 	 */
32254c06356bSdh142964 	wwn = pmcs_barray2wwn(phyp->sas_address);
32264c06356bSdh142964 	(void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
32274c06356bSdh142964 
32284c06356bSdh142964 	if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
32294c06356bSdh142964 	    DDI_SUCCESS) {
3230c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
32314c06356bSdh142964 		    "%s: Couldn't alloc softstate for device at %s",
32324c06356bSdh142964 		    __func__, unit_address);
32334c06356bSdh142964 		pmcs_unlock_phy(phyp);
32344c06356bSdh142964 		return (NULL);
32354c06356bSdh142964 	}
32364c06356bSdh142964 
32374c06356bSdh142964 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
3238a25672a1SDavid Hollister 	ASSERT(tgt != NULL);
32394c06356bSdh142964 	STAILQ_INIT(&tgt->wq);
32404c06356bSdh142964 	STAILQ_INIT(&tgt->aq);
32414c06356bSdh142964 	STAILQ_INIT(&tgt->sq);
32424c06356bSdh142964 	mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
32434c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
32444c06356bSdh142964 	mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
32454c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
32464c06356bSdh142964 	mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
32474c06356bSdh142964 	    DDI_INTR_PRI(pwp->intr_pri));
32484c06356bSdh142964 	cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
32494c06356bSdh142964 	cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
325073a3eccdSDavid Hollister 	list_create(&tgt->lun_list, sizeof (pmcs_lun_t),
325173a3eccdSDavid Hollister 	    offsetof(pmcs_lun_t, lun_list_next));
32524c06356bSdh142964 	tgt->qdepth = 1;
32534c06356bSdh142964 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
32544c06356bSdh142964 	bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
32554c06356bSdh142964 	tgt->pwp = pwp;
32564c06356bSdh142964 	tgt->ua = strdup(iport->ua);
32574c06356bSdh142964 	tgt->phy = phyp;
32584c06356bSdh142964 	ASSERT((phyp->target == NULL) || (phyp->target == tgt));
32594c06356bSdh142964 	if (phyp->target == NULL) {
32604c06356bSdh142964 		phyp->target = tgt;
32614c06356bSdh142964 	}
32624c06356bSdh142964 
32634c06356bSdh142964 	/*
32644c06356bSdh142964 	 * Don't allocate LUN softstate for SMP targets
32654c06356bSdh142964 	 */
32664c06356bSdh142964 	if (phyp->dtype == EXPANDER) {
32674c06356bSdh142964 		return (tgt);
32684c06356bSdh142964 	}
32694c06356bSdh142964 
32704c06356bSdh142964 	if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
32714c06356bSdh142964 	    sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
3272c3bc407cSdh142964 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
32734c06356bSdh142964 		    "%s: LUN soft_state_bystr_init failed", __func__);
32744c06356bSdh142964 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
32754c06356bSdh142964 		pmcs_unlock_phy(phyp);
32764c06356bSdh142964 		return (NULL);
32774c06356bSdh142964 	}
32784c06356bSdh142964 
32794c06356bSdh142964 	return (tgt);
32804c06356bSdh142964 }
3281