14c06356bSdh /*
24c06356bSdh  * CDDL HEADER START
34c06356bSdh  *
44c06356bSdh  * The contents of this file are subject to the terms of the
54c06356bSdh  * Common Development and Distribution License (the "License").
64c06356bSdh  * You may not use this file except in compliance with the License.
74c06356bSdh  *
84c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh  * See the License for the specific language governing permissions
114c06356bSdh  * and limitations under the License.
124c06356bSdh  *
134c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh  *
194c06356bSdh  * CDDL HEADER END
20658280b6SDavid Hollister  */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh  */
244c06356bSdh /*
254c06356bSdh  * SCSI (SCSA) midlayer interface for PMC drier.
264c06356bSdh  */
274c06356bSdh 
284c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
294c06356bSdh 
304c06356bSdh extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
314c06356bSdh 
324c06356bSdh static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
334c06356bSdh     scsi_hba_tran_t *, struct scsi_device *);
344c06356bSdh static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
354c06356bSdh     scsi_hba_tran_t *, struct scsi_device *);
364c06356bSdh static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
374c06356bSdh static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
384c06356bSdh static int pmcs_scsa_reset(struct scsi_address *, int);
394c06356bSdh static int pmcs_scsi_reset_notify(struct scsi_address *, int,
404c06356bSdh     void (*)(caddr_t), caddr_t);
414c06356bSdh static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
424c06356bSdh static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
434c06356bSdh static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
444c06356bSdh 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 *,
474c06356bSdh     smp_device_t *);
4896c4a178SChris Horne static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
494c06356bSdh     smp_device_t *);
5096c4a178SChris Horne static int pmcs_smp_start(struct smp_pkt *);
514c06356bSdh 
524c06356bSdh static int pmcs_scsi_quiesce(dev_info_t *);
534c06356bSdh static int pmcs_scsi_unquiesce(dev_info_t *);
544c06356bSdh 
554c06356bSdh static int pmcs_cap(struct scsi_address *, char *, int, int, int);
564c06356bSdh static pmcs_xscsi_t *
574c06356bSdh     pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
584c06356bSdh static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
594c06356bSdh static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
604c06356bSdh 
614c06356bSdh static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
624c06356bSdh static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
634c06356bSdh static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
644c06356bSdh 
654c06356bSdh static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
66658280b6SDavid Hollister     pmcwork_t *, uint32_t *, uint32_t);
674c06356bSdh 
684c06356bSdh 
694c06356bSdh int
pmcs_scsa_init(pmcs_hw_t * pwp,const ddi_dma_attr_t * ap)704c06356bSdh pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
714c06356bSdh {
724c06356bSdh 	scsi_hba_tran_t *tran;
734c06356bSdh 	ddi_dma_attr_t pmcs_scsa_dattr;
744c06356bSdh 	int flags;
754c06356bSdh 
764c06356bSdh 	(void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
774c06356bSdh 	pmcs_scsa_dattr.dma_attr_sgllen =
784c06356bSdh 	    ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
794c06356bSdh 	pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
804c06356bSdh 	pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
814c06356bSdh 
824c06356bSdh 	/*
834c06356bSdh 	 * Allocate a transport structure
844c06356bSdh 	 */
854c06356bSdh 	tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
864c06356bSdh 	if (tran == NULL) {
87c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
88c3bc407cSdh 		    "scsi_hba_tran_alloc failed");
894c06356bSdh 		return (DDI_FAILURE);
904c06356bSdh 	}
914c06356bSdh 
924c06356bSdh 	tran->tran_hba_private		= pwp;
934c06356bSdh 	tran->tran_tgt_init		= pmcs_scsa_tran_tgt_init;
944c06356bSdh 	tran->tran_tgt_free		= pmcs_scsa_tran_tgt_free;
954c06356bSdh 	tran->tran_start		= pmcs_scsa_start;
964c06356bSdh 	tran->tran_abort		= pmcs_scsa_abort;
974c06356bSdh 	tran->tran_reset		= pmcs_scsa_reset;
984c06356bSdh 	tran->tran_reset_notify		= pmcs_scsi_reset_notify;
994c06356bSdh 	tran->tran_getcap		= pmcs_scsa_getcap;
1004c06356bSdh 	tran->tran_setcap		= pmcs_scsa_setcap;
1014c06356bSdh 	tran->tran_setup_pkt		= pmcs_scsa_setup_pkt;
1024c06356bSdh 	tran->tran_teardown_pkt		= pmcs_scsa_teardown_pkt;
1034c06356bSdh 	tran->tran_quiesce		= pmcs_scsi_quiesce;
1044c06356bSdh 	tran->tran_unquiesce		= pmcs_scsi_unquiesce;
1054c06356bSdh 	tran->tran_interconnect_type	= INTERCONNECT_SAS;
1064c06356bSdh 	tran->tran_hba_len		= sizeof (pmcs_cmd_t);
1074c06356bSdh 
1084c06356bSdh 	/*
1094c06356bSdh 	 * Attach this instance of the hba
1104c06356bSdh 	 */
1114c06356bSdh 
1124c06356bSdh 	flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
1134c06356bSdh 	    SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
1144c06356bSdh 
1154c06356bSdh 	if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
1164c06356bSdh 		scsi_hba_tran_free(tran);
117c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
118c3bc407cSdh 		    "scsi_hba_attach failed");
1194c06356bSdh 		return (DDI_FAILURE);
1204c06356bSdh 	}
1214c06356bSdh 	pwp->tran = tran;
1224c06356bSdh 
1234c06356bSdh 	/*
1244c06356bSdh 	 * Attach the SMP part of this hba
1254c06356bSdh 	 */
12696c4a178SChris Horne 	pwp->smp_tran = smp_hba_tran_alloc(pwp->dip);
1274c06356bSdh 	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;
1324c06356bSdh 
13396c4a178SChris Horne 	if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
134c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13596c4a178SChris Horne 		    "smp_hba_attach failed");
13696c4a178SChris Horne 		smp_hba_tran_free(pwp->smp_tran);
1374c06356bSdh 		pwp->smp_tran = NULL;
1384c06356bSdh 		scsi_hba_tran_free(tran);
1394c06356bSdh 		return (DDI_FAILURE);
1404c06356bSdh 	}
1414c06356bSdh 
1424c06356bSdh 	return (DDI_SUCCESS);
1434c06356bSdh }
1444c06356bSdh 
1454c06356bSdh /*
1464c06356bSdh  * SCSA entry points
1474c06356bSdh  */
1484c06356bSdh 
1494c06356bSdh static int
pmcs_scsa_tran_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)1504c06356bSdh pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
1514c06356bSdh     scsi_hba_tran_t *tran, struct scsi_device *sd)
1524c06356bSdh {
1534c06356bSdh 	pmcs_hw_t	*pwp = NULL;
1544c06356bSdh 	int		rval;
1554c06356bSdh 	char		*variant_prop = "sata";
1564c06356bSdh 	char		*tgt_port = NULL, *ua = NULL;
1574c06356bSdh 	pmcs_xscsi_t	*tgt = NULL;
1584c06356bSdh 	pmcs_iport_t	*iport;
1594c06356bSdh 	pmcs_lun_t	*lun = NULL;
1604c06356bSdh 	pmcs_phy_t	*phyp = NULL;
1614c06356bSdh 	uint64_t	lun_num;
1624c06356bSdh 	boolean_t	got_scratch = B_FALSE;
1634c06356bSdh 
1644c06356bSdh 	/*
1654c06356bSdh 	 * First, make sure we're an iport and get the pointer to the HBA
1664c06356bSdh 	 * node's softstate
1674c06356bSdh 	 */
1684c06356bSdh 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
169c3bc407cSdh 		pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
1704c06356bSdh 		    "%s: We don't enumerate devices on the HBA node", __func__);
1714c06356bSdh 		goto tgt_init_fail;
1724c06356bSdh 	}
1734c06356bSdh 
1744c06356bSdh 	pwp = ITRAN2PMC(tran);
1754c06356bSdh 	iport = ITRAN2IPORT(tran);
1764c06356bSdh 
177c40ba10dSReed 	/*
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 
1904c06356bSdh 	/*
1914c06356bSdh 	 * Get the target address
1924c06356bSdh 	 */
1934c06356bSdh 	rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
1944c06356bSdh 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
1954c06356bSdh 	if (rval != DDI_PROP_SUCCESS) {
196c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
197c3bc407cSdh 		    "Couldn't get target UA");
1984c06356bSdh 		pwp = NULL;
1994c06356bSdh 		goto tgt_init_fail;
2004c06356bSdh 	}
201c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
202c3bc407cSdh 	    "got tgt_port '%s'", tgt_port);
2034c06356bSdh 
2044c06356bSdh 	/*
2054c06356bSdh 	 * Validate that this tran_tgt_init is for an active iport.
2064c06356bSdh 	 */
2074c06356bSdh 	if (iport->ua_state == UA_INACTIVE) {
208c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2094c06356bSdh 		    "%s: Got tran_tgt_init on inactive iport for '%s'",
2104c06356bSdh 		    __func__, tgt_port);
2114c06356bSdh 		pwp = NULL;
2124c06356bSdh 		goto tgt_init_fail;
2134c06356bSdh 	}
2144c06356bSdh 
2154c06356bSdh 	/*
2164c06356bSdh 	 * Since we're going to wait for scratch, be sure to acquire it while
2174c06356bSdh 	 * we're not holding any other locks
2184c06356bSdh 	 */
2194c06356bSdh 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
2204c06356bSdh 	got_scratch = B_TRUE;
2214c06356bSdh 
2224c06356bSdh 	mutex_enter(&pwp->lock);
2234c06356bSdh 
2244c06356bSdh 	/*
2254c06356bSdh 	 * See if there's already a target softstate.  If not, allocate one.
2264c06356bSdh 	 */
2275c45adf0SJesse Butler 	tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
2284c06356bSdh 
2294c06356bSdh 	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);
2324c06356bSdh 		goto tgt_init_fail;
2334c06356bSdh 	}
2344c06356bSdh 
2354c06356bSdh 	phyp = tgt->phy;
2364c06356bSdh 	if (!IS_ROOT_PHY(phyp)) {
2374c06356bSdh 		pmcs_inc_phy_ref_count(phyp);
2384c06356bSdh 	}
2394c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
2404c06356bSdh 
241c40ba10dSReed 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "@%s tgt = 0x%p, dip = 0x%p",
242c40ba10dSReed 	    ua, (void *)tgt, (void *)tgt_dip);
2434c06356bSdh 
244c40ba10dSReed 	/* Now get the lun */
2454c06356bSdh 	lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
2464c06356bSdh 	    SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
2474c06356bSdh 	if (lun_num == SCSI_LUN64_ILLEGAL) {
248c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
249c3bc407cSdh 		    "No LUN for tgt %p", (void *)tgt);
2504c06356bSdh 		goto tgt_init_fail;
2514c06356bSdh 	}
2524c06356bSdh 
253c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy "
254c3bc407cSdh 	    "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
2554c06356bSdh 
2564c06356bSdh 	mutex_enter(&tgt->statlock);
2574c06356bSdh 	tgt->dtype = phyp->dtype;
2584c06356bSdh 	if (tgt->dtype != SAS && tgt->dtype != SATA) {
259c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
260c3bc407cSdh 		    "PHY 0x%p went away?", (void *)phyp);
2614c06356bSdh 		goto tgt_init_fail;
2624c06356bSdh 	}
2634c06356bSdh 
2644c06356bSdh 	/* We don't support SATA devices at LUN > 0. */
2654c06356bSdh 	if ((tgt->dtype == SATA) && (lun_num > 0)) {
266c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
2674c06356bSdh 		    "%s: No support for SATA devices at LUN > 0 "
2684c06356bSdh 		    "(target = 0x%p)", __func__, (void *)tgt);
2694c06356bSdh 		goto tgt_init_fail;
2704c06356bSdh 	}
2714c06356bSdh 
2724c06356bSdh 	/*
2734c06356bSdh 	 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
2744c06356bSdh 	 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
2754c06356bSdh 	 * verify that the framework never tries to initialize two scsi_device
2764c06356bSdh 	 * structures with the same unit-address at the same time.
2774c06356bSdh 	 */
2784c06356bSdh 	if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
279c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
2804c06356bSdh 		    "Couldn't allocate LU soft state");
2814c06356bSdh 		goto tgt_init_fail;
2824c06356bSdh 	}
2834c06356bSdh 
2844c06356bSdh 	lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
2854c06356bSdh 	if (lun == NULL) {
286c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
287c3bc407cSdh 		    "Couldn't get LU soft state");
2884c06356bSdh 		goto tgt_init_fail;
2894c06356bSdh 	}
2904c06356bSdh 	scsi_device_hba_private_set(sd, lun);
2914c06356bSdh 	lun->lun_num = lun_num;
2924c06356bSdh 
2934c06356bSdh 	/* convert the scsi_lun64_t value to SCSI standard form */
2944c06356bSdh 	lun->scsi_lun = scsi_lun64_to_lun(lun_num);
2954c06356bSdh 
2964c06356bSdh 	ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
2974c06356bSdh 	bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
2984c06356bSdh 
2994c06356bSdh 	lun->target = tgt;
3004c06356bSdh 
3014c06356bSdh 	/*
3024c06356bSdh 	 * If this is the first tran_tgt_init, add this target to our list
3034c06356bSdh 	 */
3044c06356bSdh 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
3054c06356bSdh 		int target;
3064c06356bSdh 		for (target = 0; target < pwp->max_dev; target++) {
3074c06356bSdh 			if (pwp->targets[target] != NULL) {
3084c06356bSdh 				continue;
3094c06356bSdh 			}
3104c06356bSdh 
3114c06356bSdh 			pwp->targets[target] = tgt;
3124c06356bSdh 			tgt->target_num = (uint16_t)target;
3134c06356bSdh 			break;
3144c06356bSdh 		}
3154c06356bSdh 
3164c06356bSdh 		if (target == pwp->max_dev) {
317c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3184c06356bSdh 			    "Target list full.");
3194c06356bSdh 			goto tgt_init_fail;
3204c06356bSdh 		}
3214c06356bSdh 	}
3224c06356bSdh 
3234c06356bSdh 	tgt->dip = sd->sd_dev;
32473a3eccdSDavid Hollister 	lun->sd = sd;
32573a3eccdSDavid Hollister 	list_insert_tail(&tgt->lun_list, lun);
3264c06356bSdh 
3274c06356bSdh 	if (!pmcs_assign_device(pwp, tgt)) {
3284c06356bSdh 		pmcs_release_scratch(pwp);
3294c06356bSdh 		pwp->targets[tgt->target_num] = NULL;
3304c06356bSdh 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
3314c06356bSdh 		tgt->phy = NULL;
332c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3334c06356bSdh 		    "%s: pmcs_assign_device failed for target 0x%p",
3344c06356bSdh 		    __func__, (void *)tgt);
3354c06356bSdh 		goto tgt_init_fail;
3364c06356bSdh 	}
3374c06356bSdh 
3384c06356bSdh 	pmcs_release_scratch(pwp);
3394c06356bSdh 	tgt->ref_count++;
3404c06356bSdh 
3414c06356bSdh 	(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3424c06356bSdh 	    SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
3434c06356bSdh 
3444c06356bSdh 	/* SM-HBA */
3454c06356bSdh 	if (tgt->dtype == SATA) {
3464c06356bSdh 		/* TCR in PSARC/1997/281 opinion */
3474c06356bSdh 		(void) scsi_device_prop_update_string(sd,
3484c06356bSdh 		    SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
3494c06356bSdh 	}
3504c06356bSdh 
3514c06356bSdh 	tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
3524c06356bSdh 
3534c06356bSdh 	if (tgt->phy_addressable) {
3544c06356bSdh 		(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3554c06356bSdh 		    SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
3564c06356bSdh 	}
3574c06356bSdh 
3584c06356bSdh 	/* SM-HBA */
3594c06356bSdh 	(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 	 */
3654c06356bSdh 
3664c06356bSdh 	mutex_exit(&tgt->statlock);
36773a3eccdSDavid Hollister 	pmcs_update_phy_pm_props(phyp, 0, 0, B_TRUE);
3684c06356bSdh 	pmcs_unlock_phy(phyp);
3694c06356bSdh 	mutex_exit(&pwp->lock);
3704c06356bSdh 	scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
3714c06356bSdh 	return (DDI_SUCCESS);
3724c06356bSdh 
3734c06356bSdh tgt_init_fail:
374616875b4SDavid Hollister 	scsi_device_hba_private_set(sd, NULL);
3754c06356bSdh 	if (got_scratch) {
3764c06356bSdh 		pmcs_release_scratch(pwp);
3774c06356bSdh 	}
3784c06356bSdh 	if (lun) {
37973a3eccdSDavid Hollister 		list_remove(&tgt->lun_list, lun);
3804c06356bSdh 		ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
3814c06356bSdh 	}
3824c06356bSdh 	if (phyp) {
3834c06356bSdh 		mutex_exit(&tgt->statlock);
3844c06356bSdh 		pmcs_unlock_phy(phyp);
3854c06356bSdh 		/*
3864c06356bSdh 		 * phyp's ref count was incremented in pmcs_new_tport.
3874c06356bSdh 		 * We're failing configuration, we now need to decrement it.
3884c06356bSdh 		 */
3894c06356bSdh 		if (!IS_ROOT_PHY(phyp)) {
3904c06356bSdh 			pmcs_dec_phy_ref_count(phyp);
3914c06356bSdh 		}
3924c06356bSdh 		phyp->target = NULL;
3934c06356bSdh 	}
3944c06356bSdh 	if (tgt && tgt->ref_count == 0) {
3954c06356bSdh 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
3964c06356bSdh 	}
3974c06356bSdh 	if (pwp) {
3984c06356bSdh 		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);
4024c06356bSdh 	}
4034c06356bSdh 	if (tgt_port) {
4044c06356bSdh 		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
4054c06356bSdh 	}
4064c06356bSdh 	return (DDI_FAILURE);
4074c06356bSdh }
4084c06356bSdh 
4094c06356bSdh static void
pmcs_scsa_tran_tgt_free(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)4104c06356bSdh pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
4114c06356bSdh     scsi_hba_tran_t *tran, struct scsi_device *sd)
4124c06356bSdh {
4134c06356bSdh 	_NOTE(ARGUNUSED(hba_dip, tgt_dip));
4144c06356bSdh 	pmcs_hw_t	*pwp;
4154c06356bSdh 	pmcs_lun_t	*lun;
4164c06356bSdh 	pmcs_xscsi_t	*target;
4174c06356bSdh 	char		*unit_address;
4184c06356bSdh 	pmcs_phy_t	*phyp;
4194c06356bSdh 
4204c06356bSdh 	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
4214c06356bSdh 		pwp = TRAN2PMC(tran);
422c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
4234c06356bSdh 		    "%s: We don't enumerate devices on the HBA node", __func__);
4244c06356bSdh 		return;
4254c06356bSdh 	}
4264c06356bSdh 
4274c06356bSdh 	lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
4284c06356bSdh 
4294c06356bSdh 	ASSERT((lun != NULL) && (lun->target != NULL));
4304c06356bSdh 	ASSERT(lun->target->ref_count > 0);
4314c06356bSdh 
4324c06356bSdh 	target = lun->target;
4334c06356bSdh 	unit_address = lun->unit_address;
43473a3eccdSDavid Hollister 	list_remove(&target->lun_list, lun);
4354c06356bSdh 
4364c06356bSdh 	pwp = ITRAN2PMC(tran);
4374c06356bSdh 	mutex_enter(&pwp->lock);
4384c06356bSdh 	phyp = target->phy;
439ee13933aSSrikanth Suravajhala 	if (phyp) {
440ee13933aSSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
441ee13933aSSrikanth Suravajhala 	}
442ee13933aSSrikanth Suravajhala 	mutex_enter(&target->statlock);
4434c06356bSdh 
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 
4614c06356bSdh 	/*
4624c06356bSdh 	 * If this target still has a PHY pointer and that PHY's target pointer
4634c06356bSdh 	 * has been cleared, then that PHY has been reaped. In that case, there
4644c06356bSdh 	 * would be no need to decrement the reference count
4654c06356bSdh 	 */
4664c06356bSdh 	if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
4674c06356bSdh 		pmcs_dec_phy_ref_count(phyp);
4684c06356bSdh 	}
4694c06356bSdh 
4704c06356bSdh 	if (--target->ref_count == 0) {
4714c06356bSdh 		/*
4724c06356bSdh 		 * Remove this target from our list.  The target soft
4734c06356bSdh 		 * state will remain, and the device will remain registered
4744c06356bSdh 		 * with the hardware unless/until we're told the device
4754c06356bSdh 		 * physically went away.
4764c06356bSdh 		 */
477c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
4784c06356bSdh 		    "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
4794c06356bSdh 		    target->target_num);
4804c06356bSdh 		pwp->targets[target->target_num] = NULL;
4814c06356bSdh 		target->target_num = PMCS_INVALID_TARGET_NUM;
4826c87a171SJesse Butler 		/* If the PHY has a pointer to this target, clear it */
4836c87a171SJesse Butler 		if (phyp && (phyp->target == target)) {
4844c06356bSdh 			phyp->target = NULL;
4854c06356bSdh 		}
4864c06356bSdh 		target->phy = NULL;
487654ea33cSSrikanth Suravajhala 		if (phyp) {
488654ea33cSSrikanth Suravajhala 			mutex_exit(&phyp->phy_lock);
489654ea33cSSrikanth Suravajhala 		}
4904c06356bSdh 		pmcs_destroy_target(target);
4914c06356bSdh 	} else {
4924c06356bSdh 		mutex_exit(&target->statlock);
493654ea33cSSrikanth Suravajhala 		if (phyp) {
494654ea33cSSrikanth Suravajhala 			mutex_exit(&phyp->phy_lock);
495654ea33cSSrikanth Suravajhala 		}
4964c06356bSdh 	}
4974c06356bSdh 
4984c06356bSdh 	mutex_exit(&pwp->lock);
4994c06356bSdh }
5004c06356bSdh 
5014c06356bSdh static int
pmcs_scsa_start(struct scsi_address * ap,struct scsi_pkt * pkt)5024c06356bSdh pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
5034c06356bSdh {
5044c06356bSdh 	pmcs_cmd_t *sp = PKT2CMD(pkt);
5054c06356bSdh 	pmcs_hw_t *pwp = ADDR2PMC(ap);
5064c06356bSdh 	pmcs_xscsi_t *xp;
5074c06356bSdh 	boolean_t blocked;
5084c06356bSdh 	uint32_t hba_state;
5094c06356bSdh 
510c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
511c3bc407cSdh 	    "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt,
5124c06356bSdh 	    (void *)scsi_address_device(&pkt->pkt_address),
5134c06356bSdh 	    pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
5144c06356bSdh 
5154c06356bSdh 	if (pkt->pkt_flags & FLAG_NOINTR) {
516c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
517c3bc407cSdh 		    "%s: nointr pkt", __func__);
5184c06356bSdh 		return (TRAN_BADPKT);
5194c06356bSdh 	}
5204c06356bSdh 
5214c06356bSdh 	sp->cmd_tag = 0;
5224c06356bSdh 	pkt->pkt_state = pkt->pkt_statistics = 0;
5234c06356bSdh 	pkt->pkt_reason = CMD_INCOMPLETE;
5244c06356bSdh 
5254c06356bSdh 	mutex_enter(&pwp->lock);
5264c06356bSdh 	hba_state = pwp->state;
5274c06356bSdh 	blocked = pwp->blocked;
5284c06356bSdh 	mutex_exit(&pwp->lock);
5294c06356bSdh 
5304c06356bSdh 	if (hba_state != STATE_RUNNING) {
531c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
532c3bc407cSdh 		    "%s: hba dead", __func__);
5334c06356bSdh 		return (TRAN_FATAL_ERROR);
5344c06356bSdh 	}
5354c06356bSdh 
5364c06356bSdh 	xp = pmcs_addr2xp(ap, NULL, sp);
5374c06356bSdh 	if (xp == NULL) {
538c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
5394c06356bSdh 		    "%s: dropping due to null target", __func__);
540b18a19c2SJesse Butler 		goto dead_target;
5414c06356bSdh 	}
5424c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
5434c06356bSdh 
5444c06356bSdh 	/*
545b18a19c2SJesse Butler 	 * First, check to see if the device is gone.
5464c06356bSdh 	 */
547b18a19c2SJesse Butler 	if (xp->dev_gone) {
548601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5494c06356bSdh 		mutex_exit(&xp->statlock);
550c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp,
551b18a19c2SJesse Butler 		    "%s: dropping due to dead target 0x%p",
5524c06356bSdh 		    __func__, (void *)xp);
553b18a19c2SJesse Butler 		goto dead_target;
5544c06356bSdh 	}
5554c06356bSdh 
5564c06356bSdh 	/*
5574c06356bSdh 	 * If we're blocked (quiesced) just return.
5584c06356bSdh 	 */
5594c06356bSdh 	if (blocked) {
560c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
561c3bc407cSdh 		    "%s: hba blocked", __func__);
562601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5634c06356bSdh 		mutex_exit(&xp->statlock);
5644c06356bSdh 		mutex_enter(&xp->wqlock);
5654c06356bSdh 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5664c06356bSdh 		mutex_exit(&xp->wqlock);
5674c06356bSdh 		return (TRAN_ACCEPT);
5684c06356bSdh 	}
5694c06356bSdh 
5704c06356bSdh 	/*
5714c06356bSdh 	 * If we're draining or resetting, queue and return.
5724c06356bSdh 	 */
5734c06356bSdh 	if (xp->draining || xp->resetting || xp->recover_wait) {
574601c90f1SSrikanth, Ramana 		xp->actv_pkts++;
5754c06356bSdh 		mutex_exit(&xp->statlock);
5764c06356bSdh 		mutex_enter(&xp->wqlock);
5774c06356bSdh 		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5784c06356bSdh 		mutex_exit(&xp->wqlock);
579c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
5804c06356bSdh 		    "%s: draining/resetting/recovering (cnt %u)",
5814c06356bSdh 		    __func__, xp->actv_cnt);
5824c06356bSdh 		/*
5834c06356bSdh 		 * By the time we get here, draining or
5844c06356bSdh 		 * resetting may have come and gone, not
5854c06356bSdh 		 * yet noticing that we had put something
5864c06356bSdh 		 * on the wait queue, so schedule a worker
5874c06356bSdh 		 * to look at this later.
5884c06356bSdh 		 */
5894c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
5904c06356bSdh 		return (TRAN_ACCEPT);
5914c06356bSdh 	}
592601c90f1SSrikanth, Ramana 
593601c90f1SSrikanth, Ramana 	xp->actv_pkts++;
5944c06356bSdh 	mutex_exit(&xp->statlock);
5954c06356bSdh 
5964c06356bSdh 	/*
5974c06356bSdh 	 * Queue this command to the tail of the wait queue.
5984c06356bSdh 	 * This keeps us getting commands out of order.
5994c06356bSdh 	 */
6004c06356bSdh 	mutex_enter(&xp->wqlock);
6014c06356bSdh 	STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
6024c06356bSdh 	mutex_exit(&xp->wqlock);
6034c06356bSdh 
6044c06356bSdh 	/*
6054c06356bSdh 	 * Now run the queue for this device.
6064c06356bSdh 	 */
6074c06356bSdh 	(void) pmcs_scsa_wq_run_one(pwp, xp);
6084c06356bSdh 
6094c06356bSdh 	return (TRAN_ACCEPT);
6104c06356bSdh 
611b18a19c2SJesse Butler dead_target:
6124c06356bSdh 	pkt->pkt_state = STATE_GOT_BUS;
6134c06356bSdh 	pkt->pkt_reason = CMD_DEV_GONE;
6144c06356bSdh 	mutex_enter(&pwp->cq_lock);
6154c06356bSdh 	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6164c06356bSdh 	PMCS_CQ_RUN_LOCKED(pwp);
6174c06356bSdh 	mutex_exit(&pwp->cq_lock);
6184c06356bSdh 	return (TRAN_ACCEPT);
6194c06356bSdh }
6204c06356bSdh 
62102b04f6eSSrikanth, Ramana /* Return code 1 = Success */
6224c06356bSdh static int
pmcs_scsa_abort(struct scsi_address * ap,struct scsi_pkt * pkt)6234c06356bSdh pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
6244c06356bSdh {
6254c06356bSdh 	pmcs_hw_t *pwp = ADDR2PMC(ap);
62602b04f6eSSrikanth, Ramana 	pmcs_cmd_t *sp = NULL;
62702b04f6eSSrikanth, Ramana 	pmcs_xscsi_t *xp = NULL;
62802b04f6eSSrikanth, Ramana 	pmcs_phy_t *pptr = NULL;
62902b04f6eSSrikanth, Ramana 	pmcs_lun_t *pmcs_lun = (pmcs_lun_t *)
63002b04f6eSSrikanth, Ramana 	    scsi_device_hba_private_get(scsi_address_device(ap));
6314c06356bSdh 	uint32_t tag;
6324c06356bSdh 	uint64_t lun;
6334c06356bSdh 	pmcwork_t *pwrk;
6344c06356bSdh 
6354c06356bSdh 	mutex_enter(&pwp->lock);
6364c06356bSdh 	if (pwp->state != STATE_RUNNING) {
6374c06356bSdh 		mutex_exit(&pwp->lock);
638c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
639c3bc407cSdh 		    "%s: hba dead", __func__);
6404c06356bSdh 		return (0);
6414c06356bSdh 	}
6424c06356bSdh 	mutex_exit(&pwp->lock);
6434c06356bSdh 
64402b04f6eSSrikanth, Ramana 	if (pkt == NULL) {
64502b04f6eSSrikanth, Ramana 		if (pmcs_lun == NULL) {
64602b04f6eSSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
64702b04f6eSSrikanth, Ramana 			    "No pmcs_lun_t struct to do ABORT_ALL", __func__);
64802b04f6eSSrikanth, Ramana 			return (0);
64902b04f6eSSrikanth, Ramana 		}
65002b04f6eSSrikanth, Ramana 		xp = pmcs_lun->target;
65102b04f6eSSrikanth, Ramana 		if (xp != NULL) {
65202b04f6eSSrikanth, Ramana 			pptr = xp->phy;
65302b04f6eSSrikanth, Ramana 		}
65402b04f6eSSrikanth, Ramana 		if (pptr == NULL) {
65502b04f6eSSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: pkt is "
65602b04f6eSSrikanth, Ramana 			    "NULL. No tgt/phy to do ABORT_ALL", __func__);
65702b04f6eSSrikanth, Ramana 			return (0);
65802b04f6eSSrikanth, Ramana 		}
65902b04f6eSSrikanth, Ramana 		pmcs_lock_phy(pptr);
66002b04f6eSSrikanth, Ramana 		if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
66102b04f6eSSrikanth, Ramana 			pptr->abort_pending = 1;
66202b04f6eSSrikanth, Ramana 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
66302b04f6eSSrikanth, Ramana 		}
66402b04f6eSSrikanth, Ramana 		pmcs_unlock_phy(pptr);
66502b04f6eSSrikanth, Ramana 		return (1);
66602b04f6eSSrikanth, Ramana 	}
66702b04f6eSSrikanth, Ramana 
66802b04f6eSSrikanth, Ramana 	sp = PKT2CMD(pkt);
66902b04f6eSSrikanth, Ramana 	xp = sp->cmd_target;
67002b04f6eSSrikanth, Ramana 
6714c06356bSdh 	if (sp->cmd_lun) {
6724c06356bSdh 		lun = sp->cmd_lun->lun_num;
6734c06356bSdh 	} else {
6744c06356bSdh 		lun = 0;
6754c06356bSdh 	}
6764c06356bSdh 	if (xp == NULL) {
6774c06356bSdh 		return (0);
6784c06356bSdh 	}
6794c06356bSdh 
6804c06356bSdh 	/*
6814c06356bSdh 	 * See if we have a real work structure associated with this cmd.
6824c06356bSdh 	 */
683978d7443SSrikanth Suravajhala 	pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
6844c06356bSdh 	if (pwrk && pwrk->arg == sp) {
6854c06356bSdh 		tag = pwrk->htag;
6864c06356bSdh 		pptr = pwrk->phy;
6874c06356bSdh 		pwrk->timer = 0;	/* we don't time this here */
6884c06356bSdh 		ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
6894c06356bSdh 		mutex_exit(&pwrk->lock);
6904c06356bSdh 		pmcs_lock_phy(pptr);
6914c06356bSdh 		if (pptr->dtype == SAS) {
6924c06356bSdh 			if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
6934c06356bSdh 			    NULL)) {
6944c06356bSdh 				pptr->abort_pending = 1;
6954c06356bSdh 				pmcs_unlock_phy(pptr);
6964c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6974c06356bSdh 				return (0);
6984c06356bSdh 			}
6994c06356bSdh 		} else {
7004c06356bSdh 			/*
7014c06356bSdh 			 * XXX: Was the command that was active an
7024c06356bSdh 			 * NCQ I/O command?
7034c06356bSdh 			 */
7044c06356bSdh 			pptr->need_rl_ext = 1;
7054c06356bSdh 			if (pmcs_sata_abort_ncq(pwp, pptr)) {
7064c06356bSdh 				pptr->abort_pending = 1;
7074c06356bSdh 				pmcs_unlock_phy(pptr);
7084c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7094c06356bSdh 				return (0);
7104c06356bSdh 			}
7114c06356bSdh 		}
7124c06356bSdh 		pptr->abort_pending = 1;
7134c06356bSdh 		pmcs_unlock_phy(pptr);
7144c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7154c06356bSdh 		return (1);
7164c06356bSdh 	}
7174c06356bSdh 	if (pwrk) {
7184c06356bSdh 		mutex_exit(&pwrk->lock);
7194c06356bSdh 	}
7204c06356bSdh 	/*
7214c06356bSdh 	 * Okay, those weren't the droids we were looking for.
7224c06356bSdh 	 * See if the command is on any of the wait queues.
7234c06356bSdh 	 */
7244c06356bSdh 	mutex_enter(&xp->wqlock);
7254c06356bSdh 	sp = NULL;
7264c06356bSdh 	STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
7274c06356bSdh 		if (sp == PKT2CMD(pkt)) {
7284c06356bSdh 			STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
7294c06356bSdh 			break;
7304c06356bSdh 		}
7314c06356bSdh 	}
7324c06356bSdh 	mutex_exit(&xp->wqlock);
7334c06356bSdh 	if (sp) {
7344c06356bSdh 		pkt->pkt_reason = CMD_ABORTED;
7354c06356bSdh 		pkt->pkt_statistics |= STAT_ABORTED;
7364c06356bSdh 		mutex_enter(&pwp->cq_lock);
7374c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
7384c06356bSdh 		PMCS_CQ_RUN_LOCKED(pwp);
7394c06356bSdh 		mutex_exit(&pwp->cq_lock);
7404c06356bSdh 		return (1);
7414c06356bSdh 	}
7424c06356bSdh 	return (0);
7434c06356bSdh }
7444c06356bSdh 
7454c06356bSdh /*
7464c06356bSdh  * SCSA reset functions
7474c06356bSdh  */
7484c06356bSdh static int
pmcs_scsa_reset(struct scsi_address * ap,int level)7494c06356bSdh pmcs_scsa_reset(struct scsi_address *ap, int level)
7504c06356bSdh {
7514c06356bSdh 	pmcs_hw_t *pwp = ADDR2PMC(ap);
7524c06356bSdh 	pmcs_phy_t *pptr;
7534c06356bSdh 	pmcs_xscsi_t *xp;
7544c06356bSdh 	uint64_t lun = (uint64_t)-1, *lp = NULL;
7554c06356bSdh 	int rval;
7564c06356bSdh 
7574c06356bSdh 	mutex_enter(&pwp->lock);
7584c06356bSdh 	if (pwp->state != STATE_RUNNING) {
7594c06356bSdh 		mutex_exit(&pwp->lock);
760c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
761c3bc407cSdh 		    "%s: hba dead", __func__);
7624c06356bSdh 		return (0);
7634c06356bSdh 	}
7644c06356bSdh 	mutex_exit(&pwp->lock);
7654c06356bSdh 
7664c06356bSdh 	switch (level)  {
7674c06356bSdh 	case RESET_ALL:
7684c06356bSdh 		rval = 0;
7694c06356bSdh 		break;
7704c06356bSdh 	case RESET_LUN:
7714c06356bSdh 		/*
7724c06356bSdh 		 * Point lp at lun so that pmcs_addr2xp
7734c06356bSdh 		 * will fill out the 64 bit lun number.
7744c06356bSdh 		 */
7754c06356bSdh 		lp = &lun;
7764c06356bSdh 		/* FALLTHROUGH */
7774c06356bSdh 	case RESET_TARGET:
7784c06356bSdh 		xp = pmcs_addr2xp(ap, lp, NULL);
7794c06356bSdh 		if (xp == NULL) {
780c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7814c06356bSdh 			    "%s: no xp found for this scsi address", __func__);
7824c06356bSdh 			return (0);
7834c06356bSdh 		}
7844c06356bSdh 
785b18a19c2SJesse Butler 		if (xp->dev_gone) {
7864c06356bSdh 			mutex_exit(&xp->statlock);
787c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
7884c06356bSdh 			    "%s: Target 0x%p has gone away", __func__,
7894c06356bSdh 			    (void *)xp);
7904c06356bSdh 			return (0);
7914c06356bSdh 		}
7924c06356bSdh 
7934c06356bSdh 		/*
7944c06356bSdh 		 * If we're already performing this action, or if device
7954c06356bSdh 		 * state recovery is already running, just return failure.
7964c06356bSdh 		 */
7974c06356bSdh 		if (xp->resetting || xp->recover_wait) {
7984c06356bSdh 			mutex_exit(&xp->statlock);
7994c06356bSdh 			return (0);
8004c06356bSdh 		}
8014c06356bSdh 		xp->reset_wait = 0;
8024c06356bSdh 		xp->reset_success = 0;
8034c06356bSdh 		xp->resetting = 1;
8044c06356bSdh 		pptr = xp->phy;
8054c06356bSdh 		mutex_exit(&xp->statlock);
8064c06356bSdh 
8074c06356bSdh 		if (pmcs_reset_dev(pwp, pptr, lun)) {
8084c06356bSdh 			rval = 0;
8094c06356bSdh 		} else {
8104c06356bSdh 			rval = 1;
8114c06356bSdh 		}
8124c06356bSdh 
8134c06356bSdh 		mutex_enter(&xp->statlock);
8144c06356bSdh 		if (rval == 1) {
8154c06356bSdh 			xp->reset_success = 1;
8164c06356bSdh 		}
8174c06356bSdh 		if (xp->reset_wait) {
8184c06356bSdh 			xp->reset_wait = 0;
8194c06356bSdh 			cv_signal(&xp->reset_cv);
8204c06356bSdh 		}
8214c06356bSdh 		xp->resetting = 0;
8224c06356bSdh 		mutex_exit(&xp->statlock);
8234c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
8244c06356bSdh 		break;
8254c06356bSdh 	default:
8264c06356bSdh 		rval = 0;
8274c06356bSdh 		break;
8284c06356bSdh 	}
8294c06356bSdh 
8304c06356bSdh 	return (rval);
8314c06356bSdh }
8324c06356bSdh 
8334c06356bSdh static int
pmcs_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)8344c06356bSdh pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
8354c06356bSdh     void (*callback)(caddr_t), caddr_t arg)
8364c06356bSdh {
8374c06356bSdh 	pmcs_hw_t *pwp = ADDR2PMC(ap);
8384c06356bSdh 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
8394c06356bSdh 	    &pwp->lock, &pwp->reset_notify_listf));
8404c06356bSdh }
8414c06356bSdh 
8424c06356bSdh 
8434c06356bSdh static int
pmcs_cap(struct scsi_address * ap,char * cap,int val,int tonly,int set)8444c06356bSdh pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
8454c06356bSdh {
8464c06356bSdh 	_NOTE(ARGUNUSED(val, tonly));
8474c06356bSdh 	int cidx, rval = 0;
8484c06356bSdh 	pmcs_xscsi_t *xp;
8494c06356bSdh 
8504c06356bSdh 	cidx = scsi_hba_lookup_capstr(cap);
8514c06356bSdh 	if (cidx == -1) {
8524c06356bSdh 		return (-1);
8534c06356bSdh 	}
8544c06356bSdh 
8554c06356bSdh 	xp = pmcs_addr2xp(ap, NULL, NULL);
8564c06356bSdh 	if (xp == NULL) {
8574c06356bSdh 		return (-1);
8584c06356bSdh 	}
8594c06356bSdh 
8604c06356bSdh 	switch (cidx) {
8614c06356bSdh 	case SCSI_CAP_DMA_MAX:
8624c06356bSdh 	case SCSI_CAP_INITIATOR_ID:
8634c06356bSdh 		if (set == 0) {
8644c06356bSdh 			rval = INT_MAX;	/* argh */
8654c06356bSdh 		}
8664c06356bSdh 		break;
8674c06356bSdh 	case SCSI_CAP_DISCONNECT:
8684c06356bSdh 	case SCSI_CAP_SYNCHRONOUS:
8694c06356bSdh 	case SCSI_CAP_WIDE_XFER:
8704c06356bSdh 	case SCSI_CAP_PARITY:
8714c06356bSdh 	case SCSI_CAP_ARQ:
8724c06356bSdh 	case SCSI_CAP_UNTAGGED_QING:
8734c06356bSdh 		if (set == 0) {
8744c06356bSdh 			rval = 1;
8754c06356bSdh 		}
8764c06356bSdh 		break;
8774c06356bSdh 
8784c06356bSdh 	case SCSI_CAP_TAGGED_QING:
8794c06356bSdh 		rval = 1;
8804c06356bSdh 		break;
8814c06356bSdh 
8824c06356bSdh 	case SCSI_CAP_MSG_OUT:
8834c06356bSdh 	case SCSI_CAP_RESET_NOTIFICATION:
8844c06356bSdh 	case SCSI_CAP_QFULL_RETRIES:
8854c06356bSdh 	case SCSI_CAP_QFULL_RETRY_INTERVAL:
8864c06356bSdh 		break;
8874c06356bSdh 	case SCSI_CAP_SCSI_VERSION:
8884c06356bSdh 		if (set == 0) {
8894c06356bSdh 			rval = SCSI_VERSION_3;
8904c06356bSdh 		}
8914c06356bSdh 		break;
8924c06356bSdh 	case SCSI_CAP_INTERCONNECT_TYPE:
8934c06356bSdh 		if (set) {
8944c06356bSdh 			break;
8954c06356bSdh 		}
8964c06356bSdh 		if (xp->phy_addressable) {
8974c06356bSdh 			rval = INTERCONNECT_SATA;
8984c06356bSdh 		} else {
8994c06356bSdh 			rval = INTERCONNECT_SAS;
9004c06356bSdh 		}
9014c06356bSdh 		break;
9024c06356bSdh 	case SCSI_CAP_CDB_LEN:
9034c06356bSdh 		if (set == 0) {
9044c06356bSdh 			rval = 16;
9054c06356bSdh 		}
9064c06356bSdh 		break;
9074c06356bSdh 	case SCSI_CAP_LUN_RESET:
9084c06356bSdh 		if (set) {
9094c06356bSdh 			break;
9104c06356bSdh 		}
9114c06356bSdh 		if (xp->dtype == SATA) {
9124c06356bSdh 			rval = 0;
9134c06356bSdh 		} else {
9144c06356bSdh 			rval = 1;
9154c06356bSdh 		}
9164c06356bSdh 		break;
9174c06356bSdh 	default:
9184c06356bSdh 		rval = -1;
9194c06356bSdh 		break;
9204c06356bSdh 	}
9214c06356bSdh 	mutex_exit(&xp->statlock);
922c3bc407cSdh 	pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL,
9234c06356bSdh 	    "%s: cap %s val %d set %d rval %d",
9244c06356bSdh 	    __func__, cap, val, set, rval);
9254c06356bSdh 	return (rval);
9264c06356bSdh }
9274c06356bSdh 
9284c06356bSdh /*
9294c06356bSdh  * Returns with statlock held if the xp is found.
9304c06356bSdh  * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
9314c06356bSdh  */
9324c06356bSdh static pmcs_xscsi_t *
pmcs_addr2xp(struct scsi_address * ap,uint64_t * lp,pmcs_cmd_t * sp)9334c06356bSdh pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
9344c06356bSdh {
9354c06356bSdh 	pmcs_xscsi_t *xp;
9364c06356bSdh 	pmcs_lun_t *lun = (pmcs_lun_t *)
9374c06356bSdh 	    scsi_device_hba_private_get(scsi_address_device(ap));
9384c06356bSdh 
9394c06356bSdh 	if ((lun == NULL) || (lun->target == NULL)) {
9404c06356bSdh 		return (NULL);
9414c06356bSdh 	}
9424c06356bSdh 	xp = lun->target;
9434c06356bSdh 	mutex_enter(&xp->statlock);
9444c06356bSdh 
945b18a19c2SJesse Butler 	if (xp->dev_gone || (xp->phy == NULL)) {
946601c90f1SSrikanth, Ramana 		/*
947601c90f1SSrikanth, Ramana 		 * This may be a retried packet, so it's possible cmd_target
948601c90f1SSrikanth, Ramana 		 * and cmd_lun may still be populated.  Clear them.
949601c90f1SSrikanth, Ramana 		 */
950601c90f1SSrikanth, Ramana 		if (sp != NULL) {
951601c90f1SSrikanth, Ramana 			sp->cmd_target = NULL;
952601c90f1SSrikanth, Ramana 			sp->cmd_lun = NULL;
953601c90f1SSrikanth, Ramana 		}
9544c06356bSdh 		mutex_exit(&xp->statlock);
9554c06356bSdh 		return (NULL);
9564c06356bSdh 	}
9574c06356bSdh 
9584c06356bSdh 	if (sp != NULL) {
9594c06356bSdh 		sp->cmd_target = xp;
9604c06356bSdh 		sp->cmd_lun = lun;
9614c06356bSdh 	}
9624c06356bSdh 	if (lp) {
9634c06356bSdh 		*lp = lun->lun_num;
9644c06356bSdh 	}
9654c06356bSdh 	return (xp);
9664c06356bSdh }
9674c06356bSdh 
9684c06356bSdh static int
pmcs_scsa_getcap(struct scsi_address * ap,char * cap,int whom)9694c06356bSdh pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
9704c06356bSdh {
9714c06356bSdh 	int r;
9724c06356bSdh 	if (cap == NULL) {
9734c06356bSdh 		return (-1);
9744c06356bSdh 	}
9754c06356bSdh 	r = pmcs_cap(ap, cap, 0, whom, 0);
9764c06356bSdh 	return (r);
9774c06356bSdh }
9784c06356bSdh 
9794c06356bSdh static int
pmcs_scsa_setcap(struct scsi_address * ap,char * cap,int value,int whom)9804c06356bSdh pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
9814c06356bSdh {
9824c06356bSdh 	int r;
9834c06356bSdh 	if (cap == NULL) {
9844c06356bSdh 		return (-1);
9854c06356bSdh 	}
9864c06356bSdh 	r = pmcs_cap(ap, cap, value, whom, 1);
9874c06356bSdh 	return (r);
9884c06356bSdh }
9894c06356bSdh 
9904c06356bSdh static int
pmcs_scsa_setup_pkt(struct scsi_pkt * pkt,int (* callback)(caddr_t),caddr_t cbarg)9914c06356bSdh pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
9924c06356bSdh     caddr_t cbarg)
9934c06356bSdh {
9944c06356bSdh 	_NOTE(ARGUNUSED(callback, cbarg));
9954c06356bSdh 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
9964c06356bSdh 
9974c06356bSdh 	bzero(sp, sizeof (pmcs_cmd_t));
9984c06356bSdh 	sp->cmd_pkt = pkt;
9994c06356bSdh 	return (0);
10004c06356bSdh }
10014c06356bSdh 
10024c06356bSdh static void
pmcs_scsa_teardown_pkt(struct scsi_pkt * pkt)10034c06356bSdh pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
10044c06356bSdh {
10054c06356bSdh 	pmcs_cmd_t *sp = pkt->pkt_ha_private;
10064c06356bSdh 	sp->cmd_target = NULL;
10074c06356bSdh 	sp->cmd_lun = NULL;
10084c06356bSdh }
10094c06356bSdh 
10104c06356bSdh static int
pmcs_smp_start(struct smp_pkt * smp_pkt)101196c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt)
10124c06356bSdh {
10134c06356bSdh 	struct pmcwork *pwrk;
10143be32c0fSJesse Butler 	pmcs_iport_t *iport;
10154c06356bSdh 	const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
10164c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
10174c06356bSdh 	uint64_t wwn;
101896c4a178SChris Horne 	pmcs_hw_t *pwp;
10194c06356bSdh 	pmcs_phy_t *pptr;
10204c06356bSdh 	pmcs_xscsi_t *xp;
10214c06356bSdh 	uint_t reqsz, rspsz, will_retry;
10224c06356bSdh 	int result;
10234c06356bSdh 
102496c4a178SChris Horne 	pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private;
102596c4a178SChris Horne 	bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
10264c06356bSdh 
1027c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
1028c3bc407cSdh 	    "%s: starting for wwn 0x%" PRIx64, __func__, wwn);
10294c06356bSdh 
103096c4a178SChris Horne 	will_retry = smp_pkt->smp_pkt_will_retry;
10314c06356bSdh 
10324c06356bSdh 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
103396c4a178SChris Horne 	reqsz = smp_pkt->smp_pkt_reqsize;
10344c06356bSdh 	if (reqsz > SAS_SMP_MAX_PAYLOAD) {
10354c06356bSdh 		reqsz = SAS_SMP_MAX_PAYLOAD;
10364c06356bSdh 	}
103796c4a178SChris Horne 	(void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz);
10384c06356bSdh 
103996c4a178SChris Horne 	rspsz = smp_pkt->smp_pkt_rspsize;
10404c06356bSdh 	if (rspsz > SAS_SMP_MAX_PAYLOAD) {
10414c06356bSdh 		rspsz = SAS_SMP_MAX_PAYLOAD;
10424c06356bSdh 	}
10434c06356bSdh 
10444c06356bSdh 	/*
10454c06356bSdh 	 * The request size from the SMP driver always includes 4 bytes
10464c06356bSdh 	 * for the CRC. The PMCS chip, however, doesn't want to see those
10474c06356bSdh 	 * counts as part of the transfer size.
10484c06356bSdh 	 */
10494c06356bSdh 	reqsz -= 4;
10504c06356bSdh 
10514c06356bSdh 	pptr = pmcs_find_phy_by_wwn(pwp, wwn);
10524c06356bSdh 	/* PHY is now locked */
10534c06356bSdh 	if (pptr == NULL || pptr->dtype != EXPANDER) {
10544c06356bSdh 		if (pptr) {
10554c06356bSdh 			pmcs_unlock_phy(pptr);
10564c06356bSdh 		}
10574c06356bSdh 		pmcs_release_scratch(pwp);
10589aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1059c3bc407cSdh 		    "%s: could not find phy", __func__);
106096c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ENXIO;
10614c06356bSdh 		return (DDI_FAILURE);
10624c06356bSdh 	}
10634c06356bSdh 
10649aed1621SDavid Hollister 	if ((pptr->iport == NULL) || !pptr->valid_device_id) {
10659aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
10669aed1621SDavid Hollister 		pmcs_release_scratch(pwp);
10679aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
10689aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
10699aed1621SDavid Hollister 		smp_pkt->smp_pkt_reason = ENXIO;
10709aed1621SDavid Hollister 		return (DDI_FAILURE);
10719aed1621SDavid Hollister 	}
10729aed1621SDavid Hollister 
10734c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
10744c06356bSdh 	if (pwrk == NULL) {
10754c06356bSdh 		pmcs_unlock_phy(pptr);
10764c06356bSdh 		pmcs_release_scratch(pwp);
1077c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
10784c06356bSdh 		    "%s: could not get work structure", __func__);
107996c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY;
10804c06356bSdh 		return (DDI_FAILURE);
10814c06356bSdh 	}
10824c06356bSdh 
10834c06356bSdh 	pwrk->arg = msg;
10844c06356bSdh 	pwrk->dtype = EXPANDER;
10854c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10864c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10874c06356bSdh 	if (ptr == NULL) {
10884c06356bSdh 		pmcs_pwork(pwp, pwrk);
10894c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10904c06356bSdh 		pmcs_unlock_phy(pptr);
10914c06356bSdh 		pmcs_release_scratch(pwp);
1092c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1093c3bc407cSdh 		    "%s: could not get IQ entry", __func__);
109496c4a178SChris Horne 		smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY;
10954c06356bSdh 		return (DDI_FAILURE);
10964c06356bSdh 	}
10974c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
10984c06356bSdh 	msg[1] = LE_32(pwrk->htag);
10994c06356bSdh 	msg[2] = LE_32(pptr->device_id);
11004c06356bSdh 	msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
11014c06356bSdh 	msg[8] = LE_32(DWORD0(pwp->scratch_dma));
11024c06356bSdh 	msg[9] = LE_32(DWORD1(pwp->scratch_dma));
11034c06356bSdh 	msg[10] = LE_32(reqsz);
11044c06356bSdh 	msg[11] = 0;
11054c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
11064c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
11074c06356bSdh 	msg[14] = LE_32(rspsz);
11084c06356bSdh 	msg[15] = 0;
11094c06356bSdh 
11104c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
11116745c559SJesse Butler 
11123be32c0fSJesse Butler 	pmcs_hold_iport(pptr->iport);
11133be32c0fSJesse Butler 	iport = pptr->iport;
11143be32c0fSJesse Butler 	pmcs_smp_acquire(iport);
11154c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
11164c06356bSdh 	htag = pwrk->htag;
11174c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
11184c06356bSdh 	pmcs_unlock_phy(pptr);
111996c4a178SChris Horne 	WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result);
11204c06356bSdh 	pmcs_pwork(pwp, pwrk);
1121827ab345SJesse Butler 	pmcs_smp_release(iport);
1122827ab345SJesse Butler 	pmcs_rele_iport(iport);
1123601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
11244c06356bSdh 	if (result) {
11254c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
11264c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
11279aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11284c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
11294c06356bSdh 			    __func__, htag);
11304c06356bSdh 		} else {
11319aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11324c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
11334c06356bSdh 			    __func__, htag);
11344c06356bSdh 		}
11354c06356bSdh 		pmcs_unlock_phy(pptr);
11364c06356bSdh 		pmcs_release_scratch(pwp);
113796c4a178SChris Horne 		smp_pkt->smp_pkt_reason = ETIMEDOUT;
11384c06356bSdh 		return (DDI_FAILURE);
11394c06356bSdh 	}
11404c06356bSdh 	status = LE_32(msg[2]);
11414c06356bSdh 	if (status == PMCOUT_STATUS_OVERFLOW) {
11424c06356bSdh 		status = PMCOUT_STATUS_OK;
114396c4a178SChris Horne 		smp_pkt->smp_pkt_reason = EOVERFLOW;
11444c06356bSdh 	}
11454c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
11464c06356bSdh 		const char *emsg = pmcs_status_str(status);
11474c06356bSdh 		if (emsg == NULL) {
11489aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11494c06356bSdh 			    "SMP operation failed (0x%x)", status);
11504c06356bSdh 		} else {
11519aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11524c06356bSdh 			    "SMP operation failed (%s)", emsg);
11534c06356bSdh 		}
11544c06356bSdh 
11554c06356bSdh 		if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
11564c06356bSdh 		    (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
115796c4a178SChris Horne 			smp_pkt->smp_pkt_reason =
115896c4a178SChris Horne 			    will_retry ? EAGAIN : ETIMEDOUT;
11594c06356bSdh 			result = DDI_FAILURE;
11604c06356bSdh 		} else if (status ==
11614c06356bSdh 		    PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
11624c06356bSdh 			xp = pptr->target;
11634c06356bSdh 			if (xp == NULL) {
116496c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11654c06356bSdh 				result = DDI_FAILURE;
11664c06356bSdh 				goto out;
11674c06356bSdh 			}
11684c06356bSdh 			if (xp->dev_state !=
11694c06356bSdh 			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
11704c06356bSdh 				xp->dev_state =
11714c06356bSdh 				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
11729aed1621SDavid Hollister 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, xp->phy,
11739aed1621SDavid Hollister 				    xp, "%s: Got _IT_NEXUS_LOSS SMP status. "
11744c06356bSdh 				    "Tgt(0x%p) dev_state set to "
11754c06356bSdh 				    "_NON_OPERATIONAL", __func__,
11764c06356bSdh 				    (void *)xp);
11774c06356bSdh 			}
11784c06356bSdh 			/* ABORT any pending commands related to this device */
11794c06356bSdh 			if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
11804c06356bSdh 				pptr->abort_pending = 1;
118196c4a178SChris Horne 				smp_pkt->smp_pkt_reason = EIO;
11824c06356bSdh 				result = DDI_FAILURE;
11834c06356bSdh 			}
11844c06356bSdh 		} else {
118596c4a178SChris Horne 			smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO;
11864c06356bSdh 			result = DDI_FAILURE;
11874c06356bSdh 		}
11884c06356bSdh 	} else {
118996c4a178SChris Horne 		(void) memcpy(smp_pkt->smp_pkt_rsp,
11904c06356bSdh 		    &((uint8_t *)pwp->scratch)[rdoff], rspsz);
119196c4a178SChris Horne 		if (smp_pkt->smp_pkt_reason == EOVERFLOW) {
11924c06356bSdh 			result = DDI_FAILURE;
11934c06356bSdh 		} else {
11944c06356bSdh 			result = DDI_SUCCESS;
11954c06356bSdh 		}
11964c06356bSdh 	}
11974c06356bSdh out:
11989aed1621SDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, pptr->target,
11999aed1621SDavid Hollister 	    "%s: done for wwn 0x%" PRIx64, __func__, wwn);
12009aed1621SDavid Hollister 
12014c06356bSdh 	pmcs_unlock_phy(pptr);
12024c06356bSdh 	pmcs_release_scratch(pwp);
12034c06356bSdh 	return (result);
12044c06356bSdh }
12054c06356bSdh 
12064c06356bSdh static int
pmcs_smp_init(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp_sd)12074c06356bSdh pmcs_smp_init(dev_info_t *self, dev_info_t *child,
120896c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp_sd)
12094c06356bSdh {
121096c4a178SChris Horne 	_NOTE(ARGUNUSED(tran, smp_sd));
12114c06356bSdh 	pmcs_iport_t *iport;
12124c06356bSdh 	pmcs_hw_t *pwp;
12134c06356bSdh 	pmcs_xscsi_t *tgt;
12144c06356bSdh 	pmcs_phy_t *phy, *pphy;
12154c06356bSdh 	uint64_t wwn;
12164c06356bSdh 	char *addr, *tgt_port;
12174c06356bSdh 	int ua_form = 1;
12184c06356bSdh 
12194c06356bSdh 	iport = ddi_get_soft_state(pmcs_iport_softstate,
12204c06356bSdh 	    ddi_get_instance(self));
12214c06356bSdh 	ASSERT(iport);
12224c06356bSdh 	if (iport == NULL)
12234c06356bSdh 		return (DDI_FAILURE);
12244c06356bSdh 	pwp = iport->pwp;
12254c06356bSdh 	ASSERT(pwp);
12264c06356bSdh 	if (pwp == NULL)
12274c06356bSdh 		return (DDI_FAILURE);
12284c06356bSdh 
12294c06356bSdh 	/* Get "target-port" prop from devinfo node */
12304c06356bSdh 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
12314c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
12324c06356bSdh 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1233c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1234c3bc407cSdh 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
12354c06356bSdh 		/* Dont fail _smp_init() because we couldnt get/set a prop */
12364c06356bSdh 		return (DDI_SUCCESS);
12374c06356bSdh 	}
12384c06356bSdh 
12394c06356bSdh 	/*
12404c06356bSdh 	 * Validate that this tran_tgt_init is for an active iport.
12414c06356bSdh 	 */
12424c06356bSdh 	if (iport->ua_state == UA_INACTIVE) {
1243c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1244c3bc407cSdh 		    "%s: Init on inactive iport for '%s'", __func__, tgt_port);
12454c06356bSdh 		ddi_prop_free(tgt_port);
12464c06356bSdh 		return (DDI_FAILURE);
12474c06356bSdh 	}
12484c06356bSdh 
12494c06356bSdh 	mutex_enter(&pwp->lock);
12504c06356bSdh 
12514c06356bSdh 	/* Retrieve softstate using unit-address */
12525c45adf0SJesse Butler 	tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
12534c06356bSdh 	if (tgt == NULL) {
1254c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1255c3bc407cSdh 		    "%s: tgt softstate not found", __func__);
12564c06356bSdh 		ddi_prop_free(tgt_port);
12574c06356bSdh 		mutex_exit(&pwp->lock);
12584c06356bSdh 		return (DDI_FAILURE);
12594c06356bSdh 	}
12604c06356bSdh 
126173a3eccdSDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)",
126273a3eccdSDavid Hollister 	    __func__, ddi_get_name(child), tgt_port);
126373a3eccdSDavid Hollister 
126473a3eccdSDavid Hollister 	mutex_enter(&tgt->statlock);
12654c06356bSdh 	phy = tgt->phy;
12664c06356bSdh 	ASSERT(mutex_owned(&phy->phy_lock));
12674c06356bSdh 
12684c06356bSdh 	if (IS_ROOT_PHY(phy)) {
12694c06356bSdh 		/* Expander attached to HBA - don't ref_count it */
12704c06356bSdh 		wwn = pwp->sas_wwns[0];
12714c06356bSdh 	} else {
12724c06356bSdh 		pmcs_inc_phy_ref_count(phy);
12734c06356bSdh 
12744c06356bSdh 		/*
12754c06356bSdh 		 * Parent (in topology) is also an expander
12764c06356bSdh 		 * Now that we've increased the ref count on phy, it's OK
12774c06356bSdh 		 * to drop the lock so we can acquire the parent's lock.
12784c06356bSdh 		 */
12794c06356bSdh 		pphy = phy->parent;
128073a3eccdSDavid Hollister 		mutex_exit(&tgt->statlock);
12814c06356bSdh 		pmcs_unlock_phy(phy);
12824c06356bSdh 		pmcs_lock_phy(pphy);
12834c06356bSdh 		wwn = pmcs_barray2wwn(pphy->sas_address);
12844c06356bSdh 		pmcs_unlock_phy(pphy);
12854c06356bSdh 		pmcs_lock_phy(phy);
128673a3eccdSDavid Hollister 		mutex_enter(&tgt->statlock);
12874c06356bSdh 	}
12884c06356bSdh 
12894c06356bSdh 	/*
12904c06356bSdh 	 * If this is the 1st smp_init, add this to our list.
12914c06356bSdh 	 */
12924c06356bSdh 	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
12934c06356bSdh 		int target;
12944c06356bSdh 		for (target = 0; target < pwp->max_dev; target++) {
12954c06356bSdh 			if (pwp->targets[target] != NULL) {
12964c06356bSdh 				continue;
12974c06356bSdh 			}
12984c06356bSdh 
12994c06356bSdh 			pwp->targets[target] = tgt;
13004c06356bSdh 			tgt->target_num = (uint16_t)target;
13014c06356bSdh 			tgt->assigned = 1;
13024c06356bSdh 			tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
13034c06356bSdh 			break;
13044c06356bSdh 		}
13054c06356bSdh 
13064c06356bSdh 		if (target == pwp->max_dev) {
1307c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
13084c06356bSdh 			    "Target list full.");
13094c06356bSdh 			goto smp_init_fail;
13104c06356bSdh 		}
13114c06356bSdh 	}
13124c06356bSdh 
13134c06356bSdh 	if (!pmcs_assign_device(pwp, tgt)) {
13144c06356bSdh 		pwp->targets[tgt->target_num] = NULL;
1315c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
13164c06356bSdh 		    "%s: pmcs_assign_device failed for target 0x%p",
13174c06356bSdh 		    __func__, (void *)tgt);
13184c06356bSdh 		goto smp_init_fail;
13194c06356bSdh 	}
13204c06356bSdh 
1321499cfd15SDavid Hollister 	/*
1322499cfd15SDavid Hollister 	 * Update the attached port and target port pm properties
1323499cfd15SDavid Hollister 	 */
1324499cfd15SDavid Hollister 	tgt->smpd = smp_sd;
1325499cfd15SDavid Hollister 
13264c06356bSdh 	pmcs_unlock_phy(phy);
13274c06356bSdh 	mutex_exit(&pwp->lock);
13284c06356bSdh 
13294c06356bSdh 	tgt->ref_count++;
13304c06356bSdh 	tgt->dtype = phy->dtype;
133173a3eccdSDavid Hollister 	mutex_exit(&tgt->statlock);
133273a3eccdSDavid Hollister 
133373a3eccdSDavid Hollister 	pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE);
13344c06356bSdh 
13354c06356bSdh 	addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
1336499cfd15SDavid Hollister 	if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT,
1337499cfd15SDavid Hollister 	    addr) != DDI_SUCCESS) {
1338c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set "
1339c3bc407cSdh 		    "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
13404c06356bSdh 	}
13414c06356bSdh 	(void) scsi_free_wwnstr(addr);
13424c06356bSdh 	ddi_prop_free(tgt_port);
13434c06356bSdh 	return (DDI_SUCCESS);
13444c06356bSdh 
13454c06356bSdh smp_init_fail:
13464c06356bSdh 	tgt->phy = NULL;
13474c06356bSdh 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
13484c06356bSdh 	phy->target = NULL;
13494c06356bSdh 	if (!IS_ROOT_PHY(phy)) {
13504c06356bSdh 		pmcs_dec_phy_ref_count(phy);
13514c06356bSdh 	}
135273a3eccdSDavid Hollister 	mutex_exit(&tgt->statlock);
13534c06356bSdh 	pmcs_unlock_phy(phy);
13544c06356bSdh 	mutex_exit(&pwp->lock);
13554c06356bSdh 	ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
13564c06356bSdh 	ddi_prop_free(tgt_port);
13574c06356bSdh 	return (DDI_FAILURE);
13584c06356bSdh }
13594c06356bSdh 
13604c06356bSdh static void
pmcs_smp_free(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp)13614c06356bSdh pmcs_smp_free(dev_info_t *self, dev_info_t *child,
136296c4a178SChris Horne     smp_hba_tran_t *tran, smp_device_t *smp)
13634c06356bSdh {
13644c06356bSdh 	_NOTE(ARGUNUSED(tran, smp));
13654c06356bSdh 	pmcs_iport_t *iport;
13664c06356bSdh 	pmcs_hw_t *pwp;
13674c06356bSdh 	pmcs_xscsi_t *tgt;
1368ee13933aSSrikanth Suravajhala 	pmcs_phy_t *phyp;
13694c06356bSdh 	char *tgt_port;
13704c06356bSdh 
13714c06356bSdh 	iport = ddi_get_soft_state(pmcs_iport_softstate,
13724c06356bSdh 	    ddi_get_instance(self));
13734c06356bSdh 	ASSERT(iport);
13744c06356bSdh 	if (iport == NULL)
13754c06356bSdh 		return;
13764c06356bSdh 
13774c06356bSdh 	pwp = iport->pwp;
13784c06356bSdh 	if (pwp == NULL)
13794c06356bSdh 		return;
13804c06356bSdh 	ASSERT(pwp);
13814c06356bSdh 
13824c06356bSdh 	/* Get "target-port" prop from devinfo node */
13834c06356bSdh 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
13844c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
13854c06356bSdh 	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1386c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1387c3bc407cSdh 		    "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
13884c06356bSdh 		return;
13894c06356bSdh 	}
13909aed1621SDavid Hollister 
13914c06356bSdh 	/* Retrieve softstate using unit-address */
13929aed1621SDavid Hollister 	mutex_enter(&pwp->lock);
13934c06356bSdh 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
13949aed1621SDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", __func__,
13959aed1621SDavid Hollister 	    ddi_get_name(child), tgt_port);
13964c06356bSdh 	ddi_prop_free(tgt_port);
13974c06356bSdh 
13984c06356bSdh 	if (tgt == NULL) {
1399c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1400c3bc407cSdh 		    "%s: tgt softstate not found", __func__);
14019aed1621SDavid Hollister 		mutex_exit(&pwp->lock);
14024c06356bSdh 		return;
14034c06356bSdh 	}
14044c06356bSdh 
1405ee13933aSSrikanth Suravajhala 	phyp = tgt->phy;
1406ee13933aSSrikanth Suravajhala 	if (phyp) {
1407ee13933aSSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
1408ee13933aSSrikanth Suravajhala 		if (!IS_ROOT_PHY(phyp)) {
1409ee13933aSSrikanth Suravajhala 			pmcs_dec_phy_ref_count(phyp);
14104c06356bSdh 		}
14114c06356bSdh 	}
1412ee13933aSSrikanth Suravajhala 	mutex_enter(&tgt->statlock);
14134c06356bSdh 
14144c06356bSdh 	if (--tgt->ref_count == 0) {
14154c06356bSdh 		/*
14164c06356bSdh 		 * Remove this target from our list. The softstate
14174c06356bSdh 		 * will remain, and the device will remain registered
14184c06356bSdh 		 * with the hardware unless/until we're told that the
14194c06356bSdh 		 * device physically went away.
14204c06356bSdh 		 */
1421c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
14224c06356bSdh 		    "Removing target 0x%p (vtgt %d) from target list",
14234c06356bSdh 		    (void *)tgt, tgt->target_num);
14244c06356bSdh 		pwp->targets[tgt->target_num] = NULL;
14254c06356bSdh 		tgt->target_num = PMCS_INVALID_TARGET_NUM;
14266c87a171SJesse Butler 		/* If the PHY has a pointer to this target, clear it */
14276c87a171SJesse Butler 		if (phyp && (phyp->target == tgt)) {
1428ee13933aSSrikanth Suravajhala 			phyp->target = NULL;
14295c45adf0SJesse Butler 		}
1430ee13933aSSrikanth Suravajhala 		tgt->phy = NULL;
14319aed1621SDavid Hollister 		pmcs_destroy_target(tgt);
14329aed1621SDavid Hollister 	} else {
14339aed1621SDavid Hollister 		mutex_exit(&tgt->statlock);
14344c06356bSdh 	}
14354c06356bSdh 
1436ee13933aSSrikanth Suravajhala 	if (phyp) {
1437ee13933aSSrikanth Suravajhala 		mutex_exit(&phyp->phy_lock);
1438ee13933aSSrikanth Suravajhala 	}
14394c06356bSdh 	mutex_exit(&pwp->lock);
14404c06356bSdh }
14414c06356bSdh 
14424c06356bSdh static int
pmcs_scsi_quiesce(dev_info_t * dip)14434c06356bSdh pmcs_scsi_quiesce(dev_info_t *dip)
14444c06356bSdh {
14454c06356bSdh 	pmcs_hw_t *pwp;
14464c06356bSdh 	int totactive = -1;
14474c06356bSdh 	pmcs_xscsi_t *xp;
14484c06356bSdh 	uint16_t target;
14494c06356bSdh 
14504c06356bSdh 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
14514c06356bSdh 		return (0);		/* iport */
14524c06356bSdh 
14534c06356bSdh 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
14544c06356bSdh 	if (pwp == NULL) {
14554c06356bSdh 		return (-1);
14564c06356bSdh 	}
14574c06356bSdh 	mutex_enter(&pwp->lock);
14584c06356bSdh 	if (pwp->state != STATE_RUNNING) {
14594c06356bSdh 		mutex_exit(&pwp->lock);
14604c06356bSdh 		return (-1);
14614c06356bSdh 	}
14624c06356bSdh 
1463c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
14645c45adf0SJesse Butler 	pwp->quiesced = pwp->blocked = 1;
14654c06356bSdh 	while (totactive) {
14664c06356bSdh 		totactive = 0;
14674c06356bSdh 		for (target = 0; target < pwp->max_dev; target++) {
14684c06356bSdh 			xp = pwp->targets[target];
14694c06356bSdh 			if (xp == NULL) {
14704c06356bSdh 				continue;
14714c06356bSdh 			}
14724c06356bSdh 			mutex_enter(&xp->statlock);
14734c06356bSdh 			if (xp->actv_cnt) {
14744c06356bSdh 				totactive += xp->actv_cnt;
14754c06356bSdh 				xp->draining = 1;
14764c06356bSdh 			}
14774c06356bSdh 			mutex_exit(&xp->statlock);
14784c06356bSdh 		}
14794c06356bSdh 		if (totactive) {
14804c06356bSdh 			cv_wait(&pwp->drain_cv, &pwp->lock);
14814c06356bSdh 		}
14824c06356bSdh 		/*
14834c06356bSdh 		 * The pwp->blocked may have been reset. e.g a SCSI bus reset
14844c06356bSdh 		 */
14854c06356bSdh 		pwp->blocked = 1;
14864c06356bSdh 	}
14874c06356bSdh 
14884c06356bSdh 	for (target = 0; target < pwp->max_dev; target++) {
14894c06356bSdh 		xp = pwp->targets[target];
14904c06356bSdh 		if (xp == NULL) {
14914c06356bSdh 			continue;
14924c06356bSdh 		}
14934c06356bSdh 		mutex_enter(&xp->statlock);
14944c06356bSdh 		xp->draining = 0;
14954c06356bSdh 		mutex_exit(&xp->statlock);
14964c06356bSdh 	}
14974c06356bSdh 
14984c06356bSdh 	mutex_exit(&pwp->lock);
14994c06356bSdh 	if (totactive == 0) {
1500c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1501c3bc407cSdh 		    "%s drain complete", __func__);
15024c06356bSdh 	}
15034c06356bSdh 	return (0);
15044c06356bSdh }
15054c06356bSdh 
15064c06356bSdh static int
pmcs_scsi_unquiesce(dev_info_t * dip)15074c06356bSdh pmcs_scsi_unquiesce(dev_info_t *dip)
15084c06356bSdh {
15094c06356bSdh 	pmcs_hw_t *pwp;
15104c06356bSdh 
15114c06356bSdh 	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
15124c06356bSdh 		return (0);		/* iport */
15134c06356bSdh 
15144c06356bSdh 	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
15154c06356bSdh 	if (pwp == NULL) {
15164c06356bSdh 		return (-1);
15174c06356bSdh 	}
15184c06356bSdh 	mutex_enter(&pwp->lock);
15194c06356bSdh 	if (pwp->state != STATE_RUNNING) {
15204c06356bSdh 		mutex_exit(&pwp->lock);
15214c06356bSdh 		return (-1);
15224c06356bSdh 	}
1523c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
15245c45adf0SJesse Butler 	pwp->blocked = pwp->quiesced = 0;
15254c06356bSdh 	mutex_exit(&pwp->lock);
15264c06356bSdh 
15274c06356bSdh 	/*
15284c06356bSdh 	 * Run all pending commands.
15294c06356bSdh 	 */
15304c06356bSdh 	pmcs_scsa_wq_run(pwp);
15314c06356bSdh 
15324c06356bSdh 	/*
15334c06356bSdh 	 * Complete all completed commands.
15344c06356bSdh 	 * This also unlocks us.
15354c06356bSdh 	 */
15364c06356bSdh 	PMCS_CQ_RUN(pwp);
15374c06356bSdh 	return (0);
15384c06356bSdh }
15394c06356bSdh 
15404c06356bSdh /*
15414c06356bSdh  * Start commands for a particular device
15424c06356bSdh  * If the actual start of a command fails, return B_FALSE.  Any other result
15434c06356bSdh  * is a B_TRUE return.
15444c06356bSdh  */
15454c06356bSdh boolean_t
pmcs_scsa_wq_run_one(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)15464c06356bSdh pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
15474c06356bSdh {
15484c06356bSdh 	pmcs_cmd_t *sp;
15494c06356bSdh 	pmcs_phy_t *phyp;
15504c06356bSdh 	pmcwork_t *pwrk;
15514c06356bSdh 	boolean_t run_one, blocked;
15524c06356bSdh 	int rval;
15534c06356bSdh 
15544c06356bSdh 	/*
15554c06356bSdh 	 * First, check to see if we're blocked or resource limited
15564c06356bSdh 	 */
15574c06356bSdh 	mutex_enter(&pwp->lock);
15584c06356bSdh 	blocked = pwp->blocked;
15594c06356bSdh 	/*
15604c06356bSdh 	 * If resource_limited is set, we're resource constrained and
15614c06356bSdh 	 * we will run only one work request for this target.
15624c06356bSdh 	 */
15634c06356bSdh 	run_one = pwp->resource_limited;
15644c06356bSdh 	mutex_exit(&pwp->lock);
15654c06356bSdh 
15664c06356bSdh 	if (blocked) {
15674c06356bSdh 		/* Queues will get restarted when we get unblocked */
15684c06356bSdh 		return (B_TRUE);
15694c06356bSdh 	}
15704c06356bSdh 
15714c06356bSdh 	/*
15724c06356bSdh 	 * Might as well verify the queue is not empty before moving on
15734c06356bSdh 	 */
15744c06356bSdh 	mutex_enter(&xp->wqlock);
15754c06356bSdh 	if (STAILQ_EMPTY(&xp->wq)) {
15764c06356bSdh 		mutex_exit(&xp->wqlock);
15774c06356bSdh 		return (B_TRUE);
15784c06356bSdh 	}
15794c06356bSdh 	mutex_exit(&xp->wqlock);
15804c06356bSdh 
15814c06356bSdh 	/*
15824c06356bSdh 	 * If we're draining or resetting, just reschedule work queue and bail.
15834c06356bSdh 	 */
15844c06356bSdh 	mutex_enter(&xp->statlock);
15854c06356bSdh 	if (xp->draining || xp->resetting || xp->special_running ||
15864c06356bSdh 	    xp->special_needed) {
15874c06356bSdh 		mutex_exit(&xp->statlock);
15884c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15894c06356bSdh 		return (B_TRUE);
15904c06356bSdh 	}
15914c06356bSdh 
15924c06356bSdh 	/*
1593b18a19c2SJesse Butler 	 * Next, check to see if the target is gone.
15944c06356bSdh 	 */
1595b18a19c2SJesse Butler 	if (xp->dev_gone) {
1596c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1597b18a19c2SJesse Butler 		    "%s: Flushing wait queue for dead tgt 0x%p", __func__,
15984c06356bSdh 		    (void *)xp);
15994c06356bSdh 		pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
16004c06356bSdh 		mutex_exit(&xp->statlock);
16014c06356bSdh 		return (B_TRUE);
16024c06356bSdh 	}
16034c06356bSdh 
16044c06356bSdh 	/*
16054c06356bSdh 	 * Increment the PHY's ref_count now so we know it won't go away
16064c06356bSdh 	 * after we drop the target lock.  Drop it before returning.  If the
16074c06356bSdh 	 * PHY dies, the commands we attempt to send will fail, but at least
16084c06356bSdh 	 * we know we have a real PHY pointer.
16094c06356bSdh 	 */
16104c06356bSdh 	phyp = xp->phy;
16114c06356bSdh 	pmcs_inc_phy_ref_count(phyp);
16124c06356bSdh 	mutex_exit(&xp->statlock);
16134c06356bSdh 
16144c06356bSdh 	mutex_enter(&xp->wqlock);
16154c06356bSdh 	while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
16164c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
16174c06356bSdh 		if (pwrk == NULL) {
1618c280a92bSDavid Hollister 			mutex_exit(&xp->wqlock);
1619c280a92bSDavid Hollister 			mutex_enter(&pwp->lock);
1620c280a92bSDavid Hollister 			if (pwp->resource_limited == 0) {
1621c280a92bSDavid Hollister 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1622c280a92bSDavid Hollister 				    "%s: out of work structures", __func__);
1623c280a92bSDavid Hollister 			}
1624c280a92bSDavid Hollister 			pwp->resource_limited = 1;
16254c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1626c280a92bSDavid Hollister 			mutex_exit(&pwp->lock);
1627c280a92bSDavid Hollister 			return (B_FALSE);
16284c06356bSdh 		}
16294c06356bSdh 		STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
16304c06356bSdh 		mutex_exit(&xp->wqlock);
16314c06356bSdh 
16324c06356bSdh 		pwrk->xp = xp;
16334c06356bSdh 		pwrk->arg = sp;
1634b53bcbf6SSrikanth Suravajhala 		pwrk->timer = 0;
16354c06356bSdh 		sp->cmd_tag = pwrk->htag;
16364c06356bSdh 
16374c06356bSdh 		pwrk->dtype = xp->dtype;
16384c06356bSdh 
16394c06356bSdh 		if (xp->dtype == SAS) {
16404c06356bSdh 			pwrk->ptr = (void *) pmcs_SAS_done;
16414c06356bSdh 			if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
16425a7797ceSJesse Butler 				if (rval != PMCS_WQ_RUN_FAIL_RES_CMP) {
1643*b46556d0SToomas Soome 					sp->cmd_tag = 0;
16445a7797ceSJesse Butler 				}
16454c06356bSdh 				pmcs_dec_phy_ref_count(phyp);
16464c06356bSdh 				pmcs_pwork(pwp, pwrk);
16474c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16484c06356bSdh 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
16494c06356bSdh 					return (B_FALSE);
16504c06356bSdh 				} else {
16514c06356bSdh 					return (B_TRUE);
16524c06356bSdh 				}
16534c06356bSdh 			}
16544c06356bSdh 		} else {
16554c06356bSdh 			ASSERT(xp->dtype == SATA);
16564c06356bSdh 			pwrk->ptr = (void *) pmcs_SATA_done;
16574c06356bSdh 			if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
1658*b46556d0SToomas Soome 				sp->cmd_tag = 0;
16594c06356bSdh 				pmcs_dec_phy_ref_count(phyp);
16604c06356bSdh 				pmcs_pwork(pwp, pwrk);
16614c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16624c06356bSdh 				if (rval == PMCS_WQ_RUN_FAIL_RES) {
16634c06356bSdh 					return (B_FALSE);
16644c06356bSdh 				} else {
16654c06356bSdh 					return (B_TRUE);
16664c06356bSdh 				}
16674c06356bSdh 			}
16684c06356bSdh 		}
16694c06356bSdh 
16704c06356bSdh 		if (run_one) {
16714c06356bSdh 			goto wq_out;
16724c06356bSdh 		}
16734c06356bSdh 		mutex_enter(&xp->wqlock);
16744c06356bSdh 	}
16754c06356bSdh 
16764c06356bSdh 	mutex_exit(&xp->wqlock);
16774c06356bSdh 
16784c06356bSdh wq_out:
16794c06356bSdh 	pmcs_dec_phy_ref_count(phyp);
16804c06356bSdh 	return (B_TRUE);
16814c06356bSdh }
16824c06356bSdh 
16834c06356bSdh /*
16844c06356bSdh  * Start commands for all devices.
16854c06356bSdh  */
16864c06356bSdh void
pmcs_scsa_wq_run(pmcs_hw_t * pwp)16874c06356bSdh pmcs_scsa_wq_run(pmcs_hw_t *pwp)
16884c06356bSdh {
16894c06356bSdh 	pmcs_xscsi_t *xp;
16904c06356bSdh 	uint16_t target_start, target;
16914c06356bSdh 	boolean_t	rval = B_TRUE;
16924c06356bSdh 
16934c06356bSdh 	mutex_enter(&pwp->lock);
16944c06356bSdh 	target_start = pwp->last_wq_dev;
16954c06356bSdh 	target = target_start;
16964c06356bSdh 
16974c06356bSdh 	do {
16984c06356bSdh 		xp = pwp->targets[target];
1699429adc13SSrikanth, Ramana 		if ((xp == NULL) || (STAILQ_EMPTY(&xp->wq))) {
17004c06356bSdh 			if (++target == pwp->max_dev) {
17014c06356bSdh 				target = 0;
17024c06356bSdh 			}
17034c06356bSdh 			continue;
17044c06356bSdh 		}
17054c06356bSdh 
17064c06356bSdh 		mutex_exit(&pwp->lock);
17074c06356bSdh 		rval = pmcs_scsa_wq_run_one(pwp, xp);
1708429adc13SSrikanth, Ramana 		mutex_enter(&pwp->lock);
1709429adc13SSrikanth, Ramana 
17104c06356bSdh 		if (rval == B_FALSE) {
17114c06356bSdh 			break;
17124c06356bSdh 		}
1713429adc13SSrikanth, Ramana 
17144c06356bSdh 		if (++target == pwp->max_dev) {
17154c06356bSdh 			target = 0;
17164c06356bSdh 		}
17174c06356bSdh 	} while (target != target_start);
17184c06356bSdh 
17194c06356bSdh 	if (rval) {
1720c280a92bSDavid Hollister 		/*
1721c280a92bSDavid Hollister 		 * If we were resource limited, but apparently are not now,
1722c280a92bSDavid Hollister 		 * reschedule the work queues anyway.
1723c280a92bSDavid Hollister 		 */
1724c280a92bSDavid Hollister 		if (pwp->resource_limited) {
1725c280a92bSDavid Hollister 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1726c280a92bSDavid Hollister 		}
17274c06356bSdh 		pwp->resource_limited = 0; /* Not resource-constrained */
17284c06356bSdh 	} else {
1729c280a92bSDavid Hollister 		/*
1730c280a92bSDavid Hollister 		 * Give everybody a chance, and reschedule to run the queues
1731c280a92bSDavid Hollister 		 * again as long as we're limited.
1732c280a92bSDavid Hollister 		 */
1733c280a92bSDavid Hollister 		pwp->resource_limited = 1;
1734c280a92bSDavid Hollister 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
17354c06356bSdh 	}
17364c06356bSdh 
17374c06356bSdh 	pwp->last_wq_dev = target;
17384c06356bSdh 	mutex_exit(&pwp->lock);
17394c06356bSdh }
17404c06356bSdh 
17414c06356bSdh /*
17424c06356bSdh  * Pull the completion queue, drop the lock and complete all elements.
17434c06356bSdh  */
17444c06356bSdh 
17454c06356bSdh void
pmcs_scsa_cq_run(void * arg)17464c06356bSdh pmcs_scsa_cq_run(void *arg)
17474c06356bSdh {
17484c06356bSdh 	pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
17494c06356bSdh 	pmcs_hw_t *pwp = cqti->cq_pwp;
17504c06356bSdh 	pmcs_cmd_t *sp, *nxt;
17514c06356bSdh 	struct scsi_pkt *pkt;
1752601c90f1SSrikanth, Ramana 	pmcs_xscsi_t *tgt;
17534c06356bSdh 	pmcs_iocomp_cb_t *ioccb, *ioccb_next;
17544c06356bSdh 	pmcs_cb_t callback;
17554c06356bSdh 
17564c06356bSdh 	DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
17574c06356bSdh 
17584c06356bSdh 	mutex_enter(&pwp->cq_lock);
17594c06356bSdh 
17604c06356bSdh 	while (!pwp->cq_info.cq_stop) {
17614c06356bSdh 		/*
17624c06356bSdh 		 * First, check the I/O completion callback queue.
17634c06356bSdh 		 */
17644c06356bSdh 		ioccb = pwp->iocomp_cb_head;
17654c06356bSdh 		pwp->iocomp_cb_head = NULL;
17664c06356bSdh 		pwp->iocomp_cb_tail = NULL;
17674c06356bSdh 		mutex_exit(&pwp->cq_lock);
17684c06356bSdh 
17694c06356bSdh 		while (ioccb) {
17704c06356bSdh 			/*
17714c06356bSdh 			 * Grab the lock on the work structure. The callback
17724c06356bSdh 			 * routine is responsible for clearing it.
17734c06356bSdh 			 */
17744c06356bSdh 			mutex_enter(&ioccb->pwrk->lock);
17754c06356bSdh 			ioccb_next = ioccb->next;
17764c06356bSdh 			callback = (pmcs_cb_t)ioccb->pwrk->ptr;
17774c06356bSdh 			(*callback)(pwp, ioccb->pwrk,
17784c06356bSdh 			    (uint32_t *)((void *)ioccb->iomb));
17794c06356bSdh 			kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
17804c06356bSdh 			ioccb = ioccb_next;
17814c06356bSdh 		}
17824c06356bSdh 
17834c06356bSdh 		/*
17844c06356bSdh 		 * Next, run the completion queue
17854c06356bSdh 		 */
17864c06356bSdh 		mutex_enter(&pwp->cq_lock);
17874c06356bSdh 		sp = STAILQ_FIRST(&pwp->cq);
17884c06356bSdh 		STAILQ_INIT(&pwp->cq);
17894c06356bSdh 		mutex_exit(&pwp->cq_lock);
17904c06356bSdh 
17914c06356bSdh 		DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
17924c06356bSdh 		    pmcs_cq_thr_info_t *, cqti);
17934c06356bSdh 
17944c06356bSdh 		if (sp && pmcs_check_acc_dma_handle(pwp)) {
17954c06356bSdh 			ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
17964c06356bSdh 		}
17974c06356bSdh 
17984c06356bSdh 		while (sp) {
17994c06356bSdh 			nxt = STAILQ_NEXT(sp, cmd_next);
18004c06356bSdh 			pkt = CMD2PKT(sp);
1801601c90f1SSrikanth, Ramana 			tgt = sp->cmd_target;
1802601c90f1SSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt,
18034c06356bSdh 			    "%s: calling completion on %p for tgt %p", __func__,
1804601c90f1SSrikanth, Ramana 			    (void *)sp, (void *)tgt);
1805601c90f1SSrikanth, Ramana 			if (tgt) {
1806601c90f1SSrikanth, Ramana 				mutex_enter(&tgt->statlock);
1807601c90f1SSrikanth, Ramana 				ASSERT(tgt->actv_pkts != 0);
1808601c90f1SSrikanth, Ramana 				tgt->actv_pkts--;
1809601c90f1SSrikanth, Ramana 				mutex_exit(&tgt->statlock);
1810601c90f1SSrikanth, Ramana 			}
18114c06356bSdh 			scsi_hba_pkt_comp(pkt);
18124c06356bSdh 			sp = nxt;
18134c06356bSdh 		}
18144c06356bSdh 
18154c06356bSdh 		DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
18164c06356bSdh 		    pmcs_cq_thr_info_t *, cqti);
18174c06356bSdh 
18188f514e74SDavid Hollister 		/*
18198f514e74SDavid Hollister 		 * Check if there are more completions to do.  If so, and we've
18208f514e74SDavid Hollister 		 * not been told to stop, skip the wait and cycle through again.
18218f514e74SDavid Hollister 		 */
18224c06356bSdh 		mutex_enter(&pwp->cq_lock);
18238f514e74SDavid Hollister 		if ((pwp->iocomp_cb_head == NULL) && STAILQ_EMPTY(&pwp->cq) &&
18248f514e74SDavid Hollister 		    !pwp->cq_info.cq_stop) {
18258f514e74SDavid Hollister 			mutex_exit(&pwp->cq_lock);
18268f514e74SDavid Hollister 			mutex_enter(&cqti->cq_thr_lock);
18278f514e74SDavid Hollister 			cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
18288f514e74SDavid Hollister 			mutex_exit(&cqti->cq_thr_lock);
18298f514e74SDavid Hollister 			mutex_enter(&pwp->cq_lock);
18308f514e74SDavid Hollister 		}
18314c06356bSdh 	}
18324c06356bSdh 
18334c06356bSdh 	mutex_exit(&pwp->cq_lock);
18344c06356bSdh 	DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
18354c06356bSdh 	thread_exit();
18364c06356bSdh }
18374c06356bSdh 
18384c06356bSdh /*
18394c06356bSdh  * Run a SAS command.  Called with pwrk->lock held, returns unlocked.
18404c06356bSdh  */
18414c06356bSdh static int
pmcs_SAS_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)18424c06356bSdh pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
18434c06356bSdh {
18444c06356bSdh 	pmcs_hw_t *pwp = CMD2PMC(sp);
18454c06356bSdh 	struct scsi_pkt *pkt = CMD2PKT(sp);
18464c06356bSdh 	pmcs_xscsi_t *xp = pwrk->xp;
184769b2e231SSrikanth Suravajhala 	uint32_t iq, lhtag, *ptr;
18484c06356bSdh 	sas_ssp_cmd_iu_t sc;
184969b2e231SSrikanth Suravajhala 	int sp_pkt_time = 0;
18504c06356bSdh 
18513be32c0fSJesse Butler 	ASSERT(xp != NULL);
18524c06356bSdh 	mutex_enter(&xp->statlock);
1853b18a19c2SJesse Butler 	if (!xp->assigned) {
18544c06356bSdh 		mutex_exit(&xp->statlock);
18554c06356bSdh 		return (PMCS_WQ_RUN_FAIL_OTHER);
18564c06356bSdh 	}
18574c06356bSdh 	if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
18584c06356bSdh 		mutex_exit(&xp->statlock);
18594c06356bSdh 		mutex_enter(&xp->wqlock);
18604c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18614c06356bSdh 		mutex_exit(&xp->wqlock);
18624c06356bSdh 		return (PMCS_WQ_RUN_FAIL_OTHER);
18634c06356bSdh 	}
18644c06356bSdh 	GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
18654c06356bSdh 	if (ptr == NULL) {
18664c06356bSdh 		mutex_exit(&xp->statlock);
18674c06356bSdh 		/*
18684c06356bSdh 		 * This is a temporary failure not likely to unblocked by
18694c06356bSdh 		 * commands completing as the test for scheduling the
18704c06356bSdh 		 * restart of work is a per-device test.
18714c06356bSdh 		 */
18724c06356bSdh 		mutex_enter(&xp->wqlock);
18734c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18744c06356bSdh 		mutex_exit(&xp->wqlock);
1875c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
18764c06356bSdh 		    "%s: Failed to get IO IQ entry for tgt %d",
18774c06356bSdh 		    __func__, xp->target_num);
18784c06356bSdh 		return (PMCS_WQ_RUN_FAIL_RES);
18794c06356bSdh 
18804c06356bSdh 	}
18814c06356bSdh 
18824c06356bSdh 	ptr[0] =
18834c06356bSdh 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
18844c06356bSdh 	ptr[1] = LE_32(pwrk->htag);
18854c06356bSdh 	ptr[2] = LE_32(pwrk->phy->device_id);
18864c06356bSdh 	ptr[3] = LE_32(pkt->pkt_dma_len);
18874c06356bSdh 	if (ptr[3]) {
18884c06356bSdh 		ASSERT(pkt->pkt_numcookies);
18894c06356bSdh 		if (pkt->pkt_dma_flags & DDI_DMA_READ) {
18904c06356bSdh 			ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
18914c06356bSdh 		} else {
18924c06356bSdh 			ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
18934c06356bSdh 		}
18944c06356bSdh 		if (pmcs_dma_load(pwp, sp, ptr)) {
18954c06356bSdh 			mutex_exit(&pwp->iqp_lock[iq]);
18964c06356bSdh 			mutex_exit(&xp->statlock);
18974c06356bSdh 			mutex_enter(&xp->wqlock);
18984c06356bSdh 			if (STAILQ_EMPTY(&xp->wq)) {
18994c06356bSdh 				STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
19004c06356bSdh 				mutex_exit(&xp->wqlock);
19015a7797ceSJesse Butler 				return (PMCS_WQ_RUN_FAIL_RES);
19024c06356bSdh 			} else {
19034c06356bSdh 				mutex_exit(&xp->wqlock);
19044c06356bSdh 				CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
19054c06356bSdh 				CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
19064c06356bSdh 				CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
19074c06356bSdh 				    STATE_GOT_TARGET | STATE_SENT_CMD |
19084c06356bSdh 				    STATE_GOT_STATUS;
1909*b46556d0SToomas Soome 				sp->cmd_tag = 0;
19104c06356bSdh 				mutex_enter(&pwp->cq_lock);
19114c06356bSdh 				STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
19128f514e74SDavid Hollister 				PMCS_CQ_RUN_LOCKED(pwp);
19134c06356bSdh 				mutex_exit(&pwp->cq_lock);
1914c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
19154c06356bSdh 				    "%s: Failed to dma_load for tgt %d (QF)",
19164c06356bSdh 				    __func__, xp->target_num);
19175a7797ceSJesse Butler 				return (PMCS_WQ_RUN_FAIL_RES_CMP);
19184c06356bSdh 			}
19194c06356bSdh 		}
19204c06356bSdh 	} else {
19214c06356bSdh 		ptr[4] = LE_32(PMCIN_DATADIR_NONE);
19224c06356bSdh 		CLEAN_MESSAGE(ptr, 12);
19234c06356bSdh 	}
19244c06356bSdh 	xp->actv_cnt++;
19254c06356bSdh 	if (xp->actv_cnt > xp->maxdepth) {
19264c06356bSdh 		xp->maxdepth = xp->actv_cnt;
1927c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth "
1928c3bc407cSdh 		    "now %u", pwrk->phy->path, xp->maxdepth);
19294c06356bSdh 	}
19304c06356bSdh 	mutex_exit(&xp->statlock);
19314c06356bSdh 
19324c06356bSdh 
19334c06356bSdh #ifdef	DEBUG
19344c06356bSdh 	/*
19354c06356bSdh 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
19364c06356bSdh 	 * event when this goes out on the wire.
19374c06356bSdh 	 */
19384c06356bSdh 	ptr[4] |= PMCIN_MESSAGE_REPORT;
19394c06356bSdh #endif
19404c06356bSdh 	/*
19414c06356bSdh 	 * Fill in the SSP IU
19424c06356bSdh 	 */
19434c06356bSdh 
19444c06356bSdh 	bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
19454c06356bSdh 	bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
19464c06356bSdh 
19474c06356bSdh 	switch (pkt->pkt_flags & FLAG_TAGMASK) {
19484c06356bSdh 	case FLAG_HTAG:
19494c06356bSdh 		sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
19504c06356bSdh 		break;
19514c06356bSdh 	case FLAG_OTAG:
19524c06356bSdh 		sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
19534c06356bSdh 		break;
19544c06356bSdh 	case FLAG_STAG:
19554c06356bSdh 	default:
19564c06356bSdh 		sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
19574c06356bSdh 		break;
19584c06356bSdh 	}
19594c06356bSdh 	(void) memcpy(sc.cdb, pkt->pkt_cdbp,
19604c06356bSdh 	    min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
19614c06356bSdh 	(void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
19624c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
196369b2e231SSrikanth Suravajhala 	lhtag = pwrk->htag;
19644c06356bSdh 	mutex_exit(&pwrk->lock);
1965c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
19664c06356bSdh 	    "%s: giving pkt %p (tag %x) to the hardware", __func__,
19674c06356bSdh 	    (void *)pkt, pwrk->htag);
19684c06356bSdh #ifdef DEBUG
19694c06356bSdh 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
19704c06356bSdh #endif
19714c06356bSdh 	mutex_enter(&xp->aqlock);
19724c06356bSdh 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
19734c06356bSdh 	mutex_exit(&xp->aqlock);
197469b2e231SSrikanth Suravajhala 	sp_pkt_time = CMD2PKT(sp)->pkt_time;
19754c06356bSdh 	INC_IQ_ENTRY(pwp, iq);
1976b53bcbf6SSrikanth Suravajhala 	mutex_enter(&pwrk->lock);
197769b2e231SSrikanth Suravajhala 	if (lhtag == pwrk->htag) {
197869b2e231SSrikanth Suravajhala 		pwrk->timer = US2WT(sp_pkt_time * 1000000);
197969b2e231SSrikanth Suravajhala 		if (pwrk->timer == 0) {
198069b2e231SSrikanth Suravajhala 			pwrk->timer = US2WT(1000000);
198169b2e231SSrikanth Suravajhala 		}
1982b53bcbf6SSrikanth Suravajhala 	}
1983b53bcbf6SSrikanth Suravajhala 	mutex_exit(&pwrk->lock);
19844c06356bSdh 
19854c06356bSdh 	/*
19864c06356bSdh 	 * If we just submitted the last command queued from device state
19874c06356bSdh 	 * recovery, clear the wq_recovery_tail pointer.
19884c06356bSdh 	 */
19894c06356bSdh 	mutex_enter(&xp->wqlock);
19904c06356bSdh 	if (xp->wq_recovery_tail == sp) {
19914c06356bSdh 		xp->wq_recovery_tail = NULL;
19924c06356bSdh 	}
19934c06356bSdh 	mutex_exit(&xp->wqlock);
19944c06356bSdh 
19954c06356bSdh 	return (PMCS_WQ_RUN_SUCCESS);
19964c06356bSdh }
19974c06356bSdh 
19984c06356bSdh /*
19994c06356bSdh  * Complete a SAS command
20004c06356bSdh  *
20014c06356bSdh  * Called with pwrk lock held.
20024c06356bSdh  * The free of pwrk releases the lock.
20034c06356bSdh  */
20044c06356bSdh 
20054c06356bSdh static void
pmcs_SAS_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)20064c06356bSdh pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
20074c06356bSdh {
20084c06356bSdh 	pmcs_cmd_t *sp = pwrk->arg;
20094c06356bSdh 	pmcs_phy_t *pptr = pwrk->phy;
20104c06356bSdh 	pmcs_xscsi_t *xp = pwrk->xp;
20114c06356bSdh 	struct scsi_pkt *pkt = CMD2PKT(sp);
20124c06356bSdh 	int dead;
20134c06356bSdh 	uint32_t sts;
20144c06356bSdh 	boolean_t aborted = B_FALSE;
20154c06356bSdh 	boolean_t do_ds_recovery = B_FALSE;
20164c06356bSdh 
20174c06356bSdh 	ASSERT(xp != NULL);
20184c06356bSdh 	ASSERT(sp != NULL);
20194c06356bSdh 	ASSERT(pptr != NULL);
20204c06356bSdh 
20214c06356bSdh 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
20224c06356bSdh 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
20234c06356bSdh 	    hrtime_t, gethrtime());
20244c06356bSdh 
20254c06356bSdh 	dead = pwrk->dead;
20264c06356bSdh 
20274c06356bSdh 	if (msg) {
20284c06356bSdh 		sts = LE_32(msg[2]);
20294c06356bSdh 	} else {
20304c06356bSdh 		sts = 0;
20314c06356bSdh 	}
20324c06356bSdh 
20334c06356bSdh 	if (dead != 0) {
2034c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2035c3bc407cSdh 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
20364c06356bSdh 		goto out;
20374c06356bSdh 	}
20384c06356bSdh 
20394c06356bSdh 	if (sts == PMCOUT_STATUS_ABORTED) {
20404c06356bSdh 		aborted = B_TRUE;
20414c06356bSdh 	}
20424c06356bSdh 
20434c06356bSdh 	if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
2044c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20454c06356bSdh 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
20464c06356bSdh 		    __func__, (void *)sp, pwrk->htag, pptr->path);
2047601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
2048601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
2049601c90f1SSrikanth, Ramana 		    STATE_SENT_CMD;
2050601c90f1SSrikanth, Ramana 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
20514c06356bSdh 		goto out;
20524c06356bSdh 	}
20534c06356bSdh 
20544c06356bSdh 	/*
20554c06356bSdh 	 * If the status isn't okay but not underflow,
20564c06356bSdh 	 * step to the side and parse the (possible) error.
20574c06356bSdh 	 */
20584c06356bSdh #ifdef DEBUG
20594c06356bSdh 	if (msg) {
20604c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
20614c06356bSdh 	}
20624c06356bSdh #endif
20634c06356bSdh 	if (!msg) {
20644c06356bSdh 		goto out;
20654c06356bSdh 	}
20664c06356bSdh 
20674c06356bSdh 	switch (sts) {
20684c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
20694c06356bSdh 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
20704c06356bSdh 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
2071c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2072601c90f1SSrikanth, Ramana 		    "%s: PHY %s requires DS recovery (status=%d)",
20734c06356bSdh 		    __func__, pptr->path, sts);
20744c06356bSdh 		do_ds_recovery = B_TRUE;
20754c06356bSdh 		break;
20764c06356bSdh 	case PMCOUT_STATUS_UNDERFLOW:
20774c06356bSdh 		(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
2078c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL,
20794c06356bSdh 		    "%s: underflow %u for cdb 0x%x",
20804c06356bSdh 		    __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
20814c06356bSdh 		sts = PMCOUT_STATUS_OK;
20824c06356bSdh 		msg[3] = 0;
20834c06356bSdh 		break;
20844c06356bSdh 	case PMCOUT_STATUS_OK:
20854c06356bSdh 		pkt->pkt_resid = 0;
20864c06356bSdh 		break;
20874c06356bSdh 	}
20884c06356bSdh 
20894c06356bSdh 	if (sts != PMCOUT_STATUS_OK) {
2090658280b6SDavid Hollister 		pmcs_ioerror(pwp, SAS, pwrk, msg, sts);
20914c06356bSdh 	} else {
20924c06356bSdh 		if (msg[3]) {
20934c06356bSdh 			uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
20944c06356bSdh 			sas_ssp_rsp_iu_t *rptr = (void *)local;
20954c06356bSdh 			const int lim =
20964c06356bSdh 			    (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
20974c06356bSdh 			static const uint8_t ssp_rsp_evec[] = {
20984c06356bSdh 				0x58, 0x61, 0x56, 0x72, 0x00
20994c06356bSdh 			};
21004c06356bSdh 
21014c06356bSdh 			/*
21024c06356bSdh 			 * Transform the the first part of the response
21034c06356bSdh 			 * to host canonical form. This gives us enough
21044c06356bSdh 			 * information to figure out what to do with the
21054c06356bSdh 			 * rest (which remains unchanged in the incoming
21064c06356bSdh 			 * message which can be up to two queue entries
21074c06356bSdh 			 * in length).
21084c06356bSdh 			 */
21094c06356bSdh 			pmcs_endian_transform(pwp, local, &msg[5],
21104c06356bSdh 			    ssp_rsp_evec);
21114c06356bSdh 			xd = (uint8_t *)(&msg[5]);
21124c06356bSdh 			xd += SAS_RSP_HDR_SIZE;
21134c06356bSdh 
21144c06356bSdh 			if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
21154c06356bSdh 				if (rptr->response_data_length != 4) {
21164c06356bSdh 					pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21174c06356bSdh 					    "Bad SAS RESPONSE DATA LENGTH",
21184c06356bSdh 					    msg);
21194c06356bSdh 					pkt->pkt_reason = CMD_TRAN_ERR;
21204c06356bSdh 					goto out;
21214c06356bSdh 				}
21224c06356bSdh 				(void) memcpy(&sts, xd, sizeof (uint32_t));
21234c06356bSdh 				sts = BE_32(sts);
21244c06356bSdh 				/*
21254c06356bSdh 				 * The only response code we should legally get
21264c06356bSdh 				 * here is an INVALID FRAME response code.
21274c06356bSdh 				 */
21284c06356bSdh 				if (sts == SAS_RSP_INVALID_FRAME) {
2129c3bc407cSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21304c06356bSdh 					    "%s: pkt %p tgt %u path %s "
21314c06356bSdh 					    "completed: INVALID FRAME response",
21324c06356bSdh 					    __func__, (void *)pkt,
21334c06356bSdh 					    xp->target_num, pptr->path);
21344c06356bSdh 				} else {
2135c3bc407cSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21364c06356bSdh 					    "%s: pkt %p tgt %u path %s "
21374c06356bSdh 					    "completed: illegal response 0x%x",
21384c06356bSdh 					    __func__, (void *)pkt,
21394c06356bSdh 					    xp->target_num, pptr->path, sts);
21404c06356bSdh 				}
21414c06356bSdh 				pkt->pkt_reason = CMD_TRAN_ERR;
21424c06356bSdh 				goto out;
21434c06356bSdh 			}
21444c06356bSdh 			if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
21454c06356bSdh 				uint32_t slen;
21464c06356bSdh 				slen = rptr->sense_data_length;
21474c06356bSdh 				if (slen > lim) {
21484c06356bSdh 					slen = lim;
21494c06356bSdh 				}
21504c06356bSdh 				pmcs_latch_status(pwp, sp, rptr->status, xd,
21514c06356bSdh 				    slen, pptr->path);
21524c06356bSdh 			} else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
21534b456463SDavid Hollister 				pmcout_ssp_comp_t *sspcp;
21544b456463SDavid Hollister 				sspcp = (pmcout_ssp_comp_t *)msg;
21554b456463SDavid Hollister 				uint32_t *residp;
21564c06356bSdh 				/*
21574c06356bSdh 				 * This is the case for a plain SCSI status.
21584b456463SDavid Hollister 				 * Note: If RESC_V is set and we're here, there
21594b456463SDavid Hollister 				 * is a residual.  We need to find it and update
21604b456463SDavid Hollister 				 * the packet accordingly.
21614c06356bSdh 				 */
21624c06356bSdh 				pmcs_latch_status(pwp, sp, rptr->status, NULL,
21634c06356bSdh 				    0, pptr->path);
21644b456463SDavid Hollister 
21654b456463SDavid Hollister 				if (sspcp->resc_v) {
21664b456463SDavid Hollister 					/*
21674b456463SDavid Hollister 					 * Point residual to the SSP_RESP_IU
21684b456463SDavid Hollister 					 */
21694b456463SDavid Hollister 					residp = (uint32_t *)(sspcp + 1);
21704b456463SDavid Hollister 					/*
21714b456463SDavid Hollister 					 * param contains the number of bytes
21724b456463SDavid Hollister 					 * between where the SSP_RESP_IU may
21734b456463SDavid Hollister 					 * or may not be and the residual.
21744b456463SDavid Hollister 					 * Increment residp by the appropriate
21754b456463SDavid Hollister 					 * number of words: (param+resc_pad)/4).
21764b456463SDavid Hollister 					 */
21774b456463SDavid Hollister 					residp += (LE_32(sspcp->param) +
21784b456463SDavid Hollister 					    sspcp->resc_pad) /
21794b456463SDavid Hollister 					    sizeof (uint32_t);
21804b456463SDavid Hollister 					pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
21814b456463SDavid Hollister 					    pptr, xp, "%s: tgt 0x%p "
21824b456463SDavid Hollister 					    "residual %d for pkt 0x%p",
21834b456463SDavid Hollister 					    __func__, (void *) xp, *residp,
21844b456463SDavid Hollister 					    (void *) pkt);
21854b456463SDavid Hollister 					ASSERT(LE_32(*residp) <=
21864b456463SDavid Hollister 					    pkt->pkt_dma_len);
21874b456463SDavid Hollister 					(void) pmcs_set_resid(pkt,
21884b456463SDavid Hollister 					    pkt->pkt_dma_len, LE_32(*residp));
21894b456463SDavid Hollister 				}
21904c06356bSdh 			} else {
21914c06356bSdh 				pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21924c06356bSdh 				    "illegal SAS response", msg);
21934c06356bSdh 				pkt->pkt_reason = CMD_TRAN_ERR;
21944c06356bSdh 				goto out;
21954c06356bSdh 			}
21964c06356bSdh 		} else {
21974c06356bSdh 			pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
21984c06356bSdh 			    pptr->path);
21994c06356bSdh 		}
22004c06356bSdh 		if (pkt->pkt_dma_len) {
22014c06356bSdh 			pkt->pkt_state |= STATE_XFERRED_DATA;
22024c06356bSdh 		}
22034c06356bSdh 	}
2204c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
22054c06356bSdh 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
22064c06356bSdh 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
22074c06356bSdh 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
22084c06356bSdh 
22094c06356bSdh 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2210c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
22114c06356bSdh 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
22124c06356bSdh 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
22134c06356bSdh 		aborted = B_TRUE;
22144c06356bSdh 	}
22154c06356bSdh 
22164c06356bSdh out:
22174c06356bSdh 	pmcs_dma_unload(pwp, sp);
22184c06356bSdh 	mutex_enter(&xp->statlock);
22199aed1621SDavid Hollister 
22205c45adf0SJesse Butler 	/*
22215c45adf0SJesse Butler 	 * If the device no longer has a PHY pointer, clear the PHY pointer
22225c45adf0SJesse Butler 	 * from the work structure before we free it.  Otherwise, pmcs_pwork
22235c45adf0SJesse Butler 	 * may decrement the ref_count on a PHY that's been freed.
22245c45adf0SJesse Butler 	 */
22255c45adf0SJesse Butler 	if (xp->phy == NULL) {
22265c45adf0SJesse Butler 		pwrk->phy = NULL;
22275c45adf0SJesse Butler 	}
22285c45adf0SJesse Butler 
222939cd77a0SJesse Butler 	/*
223039cd77a0SJesse Butler 	 * We may arrive here due to a command timing out, which in turn
223139cd77a0SJesse Butler 	 * could be addressed in a different context.  So, free the work
223239cd77a0SJesse Butler 	 * back, but only after confirming it's not already been freed
223339cd77a0SJesse Butler 	 * elsewhere.
223439cd77a0SJesse Butler 	 */
22353492a3feSJesse Butler 	if (pwrk->htag != PMCS_TAG_FREE) {
223639cd77a0SJesse Butler 		pmcs_pwork(pwp, pwrk);
223739cd77a0SJesse Butler 	}
22385c45adf0SJesse Butler 
22399aed1621SDavid Hollister 	/*
22409aed1621SDavid Hollister 	 * If the device is gone, we only put this command on the completion
22419aed1621SDavid Hollister 	 * queue if the work structure is not marked dead.  If it's marked
22429aed1621SDavid Hollister 	 * dead, it will already have been put there.
22439aed1621SDavid Hollister 	 */
22444c06356bSdh 	if (xp->dev_gone) {
22454c06356bSdh 		mutex_exit(&xp->statlock);
22469aed1621SDavid Hollister 		if (!dead) {
2247429adc13SSrikanth, Ramana 			mutex_enter(&xp->aqlock);
2248429adc13SSrikanth, Ramana 			STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2249429adc13SSrikanth, Ramana 			mutex_exit(&xp->aqlock);
22505c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2251429adc13SSrikanth, Ramana 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2252429adc13SSrikanth, Ramana 			    __func__, (void *)sp, sp->cmd_tag);
22539aed1621SDavid Hollister 			mutex_enter(&pwp->cq_lock);
22549aed1621SDavid Hollister 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
22558f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
22569aed1621SDavid Hollister 			mutex_exit(&pwp->cq_lock);
22579aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
22589aed1621SDavid Hollister 			    "%s: Completing command for dead target 0x%p",
22599aed1621SDavid Hollister 			    __func__, (void *)xp);
22609aed1621SDavid Hollister 		}
22614c06356bSdh 		return;
22624c06356bSdh 	}
22634c06356bSdh 
22644c06356bSdh 	ASSERT(xp->actv_cnt > 0);
22654c06356bSdh 	if (--(xp->actv_cnt) == 0) {
22664c06356bSdh 		if (xp->draining) {
2267c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
22684c06356bSdh 			    "%s: waking up drain waiters", __func__);
22694c06356bSdh 			cv_signal(&pwp->drain_cv);
22704c06356bSdh 		}
22714c06356bSdh 	}
22724c06356bSdh 	mutex_exit(&xp->statlock);
2273c280a92bSDavid Hollister 
2274c280a92bSDavid Hollister 	/*
2275c280a92bSDavid Hollister 	 * If the status is other than OK, determine if it's something that
2276c280a92bSDavid Hollister 	 * is worth re-attempting enumeration.  If so, mark the PHY.
2277c280a92bSDavid Hollister 	 */
2278c280a92bSDavid Hollister 	if (sts != PMCOUT_STATUS_OK) {
2279c280a92bSDavid Hollister 		pmcs_status_disposition(pptr, sts);
2280c280a92bSDavid Hollister 	}
2281c280a92bSDavid Hollister 
22824c06356bSdh 	if (dead == 0) {
22834c06356bSdh #ifdef	DEBUG
22844c06356bSdh 		pmcs_cmd_t *wp;
22854c06356bSdh 		mutex_enter(&xp->aqlock);
22864c06356bSdh 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
22874c06356bSdh 			if (wp == sp) {
22884c06356bSdh 				break;
22894c06356bSdh 			}
22904c06356bSdh 		}
22914c06356bSdh 		ASSERT(wp != NULL);
22924c06356bSdh #else
22934c06356bSdh 		mutex_enter(&xp->aqlock);
22944c06356bSdh #endif
22955c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
22969aed1621SDavid Hollister 		    "%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__,
22979aed1621SDavid Hollister 		    (void *)sp, sp->cmd_tag);
22984c06356bSdh 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
22994c06356bSdh 		if (aborted) {
2300c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
23014c06356bSdh 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
23024c06356bSdh 			    __func__, (void *)xp);
23034c06356bSdh 			cv_signal(&xp->abort_cv);
23044c06356bSdh 		}
23054c06356bSdh 		mutex_exit(&xp->aqlock);
23064c06356bSdh 	}
23074c06356bSdh 
23084c06356bSdh 	/*
23094c06356bSdh 	 * If do_ds_recovery is set, we need to initiate device state
23104c06356bSdh 	 * recovery.  In this case, we put this I/O back on the head of
23114c06356bSdh 	 * the wait queue to run again after recovery is complete
23124c06356bSdh 	 */
23134c06356bSdh 	if (do_ds_recovery) {
23144c06356bSdh 		mutex_enter(&xp->statlock);
23154c06356bSdh 		pmcs_start_dev_state_recovery(xp, pptr);
23164c06356bSdh 		mutex_exit(&xp->statlock);
2317c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p "
2318c3bc407cSdh 		    "back on wq during recovery for tgt 0x%p", __func__,
2319c3bc407cSdh 		    (void *)sp, (void *)xp);
23204c06356bSdh 		mutex_enter(&xp->wqlock);
23214c06356bSdh 		if (xp->wq_recovery_tail == NULL) {
23224c06356bSdh 			STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23234c06356bSdh 		} else {
23244c06356bSdh 			/*
23254c06356bSdh 			 * If there are other I/Os waiting at the head due to
23264c06356bSdh 			 * device state recovery, add this one in the right spot
23274c06356bSdh 			 * to maintain proper order.
23284c06356bSdh 			 */
23294c06356bSdh 			STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
23304c06356bSdh 			    cmd_next);
23314c06356bSdh 		}
23324c06356bSdh 		xp->wq_recovery_tail = sp;
23334c06356bSdh 		mutex_exit(&xp->wqlock);
23344c06356bSdh 	} else {
23354c06356bSdh 		/*
23364c06356bSdh 		 * If we're not initiating device state recovery and this
23374c06356bSdh 		 * command was not "dead", put it on the completion queue
23384c06356bSdh 		 */
23394c06356bSdh 		if (!dead) {
23404c06356bSdh 			mutex_enter(&pwp->cq_lock);
23414c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
23428f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
23434c06356bSdh 			mutex_exit(&pwp->cq_lock);
23444c06356bSdh 		}
23454c06356bSdh 	}
23464c06356bSdh }
23474c06356bSdh 
23484c06356bSdh /*
23494c06356bSdh  * Run a SATA command (normal reads and writes),
23504c06356bSdh  * or block and schedule a SATL interpretation
23514c06356bSdh  * of the command.
23524c06356bSdh  *
23534c06356bSdh  * Called with pwrk lock held, returns unlocked.
23544c06356bSdh  */
23554c06356bSdh 
23564c06356bSdh static int
pmcs_SATA_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)23574c06356bSdh pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
23584c06356bSdh {
23594c06356bSdh 	pmcs_hw_t *pwp = CMD2PMC(sp);
23604c06356bSdh 	struct scsi_pkt *pkt = CMD2PKT(sp);
23614c06356bSdh 	pmcs_xscsi_t *xp;
23624c06356bSdh 	uint8_t cdb_base, asc, tag;
236369b2e231SSrikanth Suravajhala 	uint32_t *ptr, lhtag, iq, nblk, i, mtype;
23644c06356bSdh 	fis_t fis;
23654c06356bSdh 	size_t amt;
23664c06356bSdh 	uint64_t lba;
236769b2e231SSrikanth Suravajhala 	int sp_pkt_time = 0;
23684c06356bSdh 
23694c06356bSdh 	xp = pwrk->xp;
23703be32c0fSJesse Butler 	ASSERT(xp != NULL);
23714c06356bSdh 
23724c06356bSdh 	/*
23734c06356bSdh 	 * First, see if this is just a plain read/write command.
23744c06356bSdh 	 * If not, we have to queue it up for processing, block
23754c06356bSdh 	 * any additional commands from coming in, and wake up
23764c06356bSdh 	 * the thread that will process this command.
23774c06356bSdh 	 */
23784c06356bSdh 	cdb_base = pkt->pkt_cdbp[0] & 0x1f;
23794c06356bSdh 	if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
2380c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
2381c3bc407cSdh 		    "%s: special SATA cmd %p", __func__, (void *)sp);
23824c06356bSdh 
23834c06356bSdh 		ASSERT(xp->phy != NULL);
23844c06356bSdh 		pmcs_pwork(pwp, pwrk);
23854c06356bSdh 		pmcs_lock_phy(xp->phy);
23864c06356bSdh 		mutex_enter(&xp->statlock);
23874c06356bSdh 		xp->special_needed = 1; /* Set the special_needed flag */
23884c06356bSdh 		STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
23894c06356bSdh 		if (pmcs_run_sata_special(pwp, xp)) {
23904c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
23914c06356bSdh 		}
23924c06356bSdh 		mutex_exit(&xp->statlock);
23934c06356bSdh 		pmcs_unlock_phy(xp->phy);
23944c06356bSdh 
23954c06356bSdh 		return (PMCS_WQ_RUN_SUCCESS);
23964c06356bSdh 	}
23974c06356bSdh 
2398c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__);
23994c06356bSdh 
24004c06356bSdh 	mutex_enter(&xp->statlock);
2401b18a19c2SJesse Butler 	if (!xp->assigned) {
24024c06356bSdh 		mutex_exit(&xp->statlock);
24034c06356bSdh 		return (PMCS_WQ_RUN_FAIL_OTHER);
24044c06356bSdh 	}
24054c06356bSdh 	if (xp->special_running || xp->special_needed || xp->recover_wait) {
24064c06356bSdh 		mutex_exit(&xp->statlock);
24074c06356bSdh 		mutex_enter(&xp->wqlock);
24084c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24094c06356bSdh 		mutex_exit(&xp->wqlock);
24104c06356bSdh 		/*
24114c06356bSdh 		 * By the time we get here the special
24124c06356bSdh 		 * commands running or waiting to be run
24134c06356bSdh 		 * may have come and gone, so kick our
24144c06356bSdh 		 * worker to run the waiting queues
24154c06356bSdh 		 * just in case.
24164c06356bSdh 		 */
24174c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
24184c06356bSdh 		return (PMCS_WQ_RUN_FAIL_OTHER);
24194c06356bSdh 	}
24204c06356bSdh 	lba = xp->capacity;
24214c06356bSdh 	mutex_exit(&xp->statlock);
24224c06356bSdh 
24234c06356bSdh 	/*
24244c06356bSdh 	 * Extract data length and lba parameters out of the command. The
24254c06356bSdh 	 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
24264c06356bSdh 	 * values are considered illegal.
24274c06356bSdh 	 */
24284c06356bSdh 	asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
24294c06356bSdh 	if (asc) {
24304c06356bSdh 		uint8_t sns[18];
24314c06356bSdh 		bzero(sns, sizeof (sns));
24324c06356bSdh 		sns[0] = 0xf0;
24334c06356bSdh 		sns[2] = 0x5;
24344c06356bSdh 		sns[12] = asc;
24354c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
24364c06356bSdh 		    pwrk->phy->path);
24374c06356bSdh 		pmcs_pwork(pwp, pwrk);
24384c06356bSdh 		mutex_enter(&pwp->cq_lock);
24394c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24404c06356bSdh 		PMCS_CQ_RUN_LOCKED(pwp);
24414c06356bSdh 		mutex_exit(&pwp->cq_lock);
24424c06356bSdh 		return (PMCS_WQ_RUN_SUCCESS);
24434c06356bSdh 	}
24444c06356bSdh 
24454c06356bSdh 	/*
24464c06356bSdh 	 * If the command decodes as not moving any data, complete it here.
24474c06356bSdh 	 */
24484c06356bSdh 	amt = nblk;
24494c06356bSdh 	amt <<= 9;
24504c06356bSdh 	amt = pmcs_set_resid(pkt, amt, nblk << 9);
24514c06356bSdh 	if (amt == 0) {
24524c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
24534c06356bSdh 		    pwrk->phy->path);
24544c06356bSdh 		pmcs_pwork(pwp, pwrk);
24554c06356bSdh 		mutex_enter(&pwp->cq_lock);
24564c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24574c06356bSdh 		PMCS_CQ_RUN_LOCKED(pwp);
24584c06356bSdh 		mutex_exit(&pwp->cq_lock);
24594c06356bSdh 		return (PMCS_WQ_RUN_SUCCESS);
24604c06356bSdh 	}
24614c06356bSdh 
24624c06356bSdh 	/*
24634c06356bSdh 	 * Get an inbound queue entry for this I/O
24644c06356bSdh 	 */
24654c06356bSdh 	GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
24664c06356bSdh 	if (ptr == NULL) {
24674c06356bSdh 		/*
24684c06356bSdh 		 * This is a temporary failure not likely to unblocked by
24694c06356bSdh 		 * commands completing as the test for scheduling the
24704c06356bSdh 		 * restart of work is a per-device test.
24714c06356bSdh 		 */
24724c06356bSdh 		mutex_enter(&xp->wqlock);
24734c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24744c06356bSdh 		mutex_exit(&xp->wqlock);
24754c06356bSdh 		pmcs_dma_unload(pwp, sp);
24764c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
2477c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
24784c06356bSdh 		    "%s: Failed to get IO IQ entry for tgt %d",
24794c06356bSdh 		    __func__, xp->target_num);
24804c06356bSdh 		return (PMCS_WQ_RUN_FAIL_RES);
24814c06356bSdh 	}
24824c06356bSdh 
24834c06356bSdh 	/*
24844c06356bSdh 	 * Get a tag.  At this point, hold statlock until the tagmap is
24854c06356bSdh 	 * updated (just prior to sending the cmd to the hardware).
24864c06356bSdh 	 */
24874c06356bSdh 	mutex_enter(&xp->statlock);
24884c06356bSdh 	for (tag = 0; tag < xp->qdepth; tag++) {
24894c06356bSdh 		if ((xp->tagmap & (1 << tag)) == 0) {
24904c06356bSdh 			break;
24914c06356bSdh 		}
24924c06356bSdh 	}
24934c06356bSdh 
24944c06356bSdh 	if (tag == xp->qdepth) {
24954c06356bSdh 		mutex_exit(&xp->statlock);
24964c06356bSdh 		mutex_exit(&pwp->iqp_lock[iq]);
24974c06356bSdh 		mutex_enter(&xp->wqlock);
24984c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24994c06356bSdh 		mutex_exit(&xp->wqlock);
25004c06356bSdh 		return (PMCS_WQ_RUN_FAIL_OTHER);
25014c06356bSdh 	}
25024c06356bSdh 
25034c06356bSdh 	sp->cmd_satltag = (uint8_t)tag;
25044c06356bSdh 
25054c06356bSdh 	/*
25064c06356bSdh 	 * Set up the command
25074c06356bSdh 	 */
25084c06356bSdh 	bzero(fis, sizeof (fis));
25094c06356bSdh 	ptr[0] =
25104c06356bSdh 	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
25114c06356bSdh 	ptr[1] = LE_32(pwrk->htag);
25124c06356bSdh 	ptr[2] = LE_32(pwrk->phy->device_id);
25134c06356bSdh 	ptr[3] = LE_32(amt);
25144c06356bSdh 
25154c06356bSdh 	if (xp->ncq) {
25164c06356bSdh 		mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
25174c06356bSdh 		fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
25184c06356bSdh 		if (cdb_base == SCMD_READ) {
25194c06356bSdh 			fis[0] |= (READ_FPDMA_QUEUED << 16);
25204c06356bSdh 		} else {
25214c06356bSdh 			fis[0] |= (WRITE_FPDMA_QUEUED << 16);
25224c06356bSdh 		}
25234c06356bSdh 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25244c06356bSdh 		fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
25254c06356bSdh 		fis[3] = tag << 3;
25264c06356bSdh 	} else {
25274c06356bSdh 		int op;
25284c06356bSdh 		fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
25294c06356bSdh 		if (xp->pio) {
25304c06356bSdh 			mtype = SATA_PROTOCOL_PIO;
25314c06356bSdh 			if (cdb_base == SCMD_READ) {
25324c06356bSdh 				op = READ_SECTORS_EXT;
25334c06356bSdh 			} else {
25344c06356bSdh 				op = WRITE_SECTORS_EXT;
25354c06356bSdh 			}
25364c06356bSdh 		} else {
25374c06356bSdh 			mtype = SATA_PROTOCOL_DMA;
25384c06356bSdh 			if (cdb_base == SCMD_READ) {
25394c06356bSdh 				op = READ_DMA_EXT;
25404c06356bSdh 			} else {
25414c06356bSdh 				op = WRITE_DMA_EXT;
25424c06356bSdh 			}
25434c06356bSdh 		}
25444c06356bSdh 		fis[0] |= (op << 16);
25454c06356bSdh 		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25464c06356bSdh 		fis[2] = (lba >> 24) & 0xffffff;
25474c06356bSdh 		fis[3] = nblk;
25484c06356bSdh 	}
25494c06356bSdh 
25504c06356bSdh 	if (cdb_base == SCMD_READ) {
25514c06356bSdh 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
25524c06356bSdh 	} else {
25534c06356bSdh 		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
25544c06356bSdh 	}
25554c06356bSdh #ifdef	DEBUG
25564c06356bSdh 	/*
25574c06356bSdh 	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
25584c06356bSdh 	 * event when this goes out on the wire.
25594c06356bSdh 	 */
25604c06356bSdh 	ptr[4] |= PMCIN_MESSAGE_REPORT;
25614c06356bSdh #endif
25624c06356bSdh 	for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
25634c06356bSdh 		ptr[i+5] = LE_32(fis[i]);
25644c06356bSdh 	}
25654c06356bSdh 	if (pmcs_dma_load(pwp, sp, ptr)) {
25664c06356bSdh 		mutex_exit(&xp->statlock);
25674c06356bSdh 		mutex_exit(&pwp->iqp_lock[iq]);
25684c06356bSdh 		mutex_enter(&xp->wqlock);
25694c06356bSdh 		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
25704c06356bSdh 		mutex_exit(&xp->wqlock);
2571c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
25724c06356bSdh 		    "%s: Failed to dma_load for tgt %d",
25734c06356bSdh 		    __func__, xp->target_num);
25744c06356bSdh 		return (PMCS_WQ_RUN_FAIL_RES);
25754c06356bSdh 
25764c06356bSdh 	}
25774c06356bSdh 
25784c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
257969b2e231SSrikanth Suravajhala 	lhtag = pwrk->htag;
25804c06356bSdh 	mutex_exit(&pwrk->lock);
25814c06356bSdh 	xp->tagmap |= (1 << tag);
25824c06356bSdh 	xp->actv_cnt++;
25834c06356bSdh 	if (xp->actv_cnt > xp->maxdepth) {
25844c06356bSdh 		xp->maxdepth = xp->actv_cnt;
2585c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp,
2586c3bc407cSdh 		    "%s: max depth now %u", pwrk->phy->path, xp->maxdepth);
25874c06356bSdh 	}
25884c06356bSdh 	mutex_exit(&xp->statlock);
25894c06356bSdh 	mutex_enter(&xp->aqlock);
25904c06356bSdh 	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
25914c06356bSdh 	mutex_exit(&xp->aqlock);
2592c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
2593c3bc407cSdh 	    "%s: giving pkt %p to hardware", __func__, (void *)pkt);
25944c06356bSdh #ifdef DEBUG
25954c06356bSdh 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
25964c06356bSdh #endif
259769b2e231SSrikanth Suravajhala 	sp_pkt_time = CMD2PKT(sp)->pkt_time;
25984c06356bSdh 	INC_IQ_ENTRY(pwp, iq);
2599b53bcbf6SSrikanth Suravajhala 	mutex_enter(&pwrk->lock);
260069b2e231SSrikanth Suravajhala 	if (lhtag == pwrk->htag) {
260169b2e231SSrikanth Suravajhala 		pwrk->timer = US2WT(sp_pkt_time * 1000000);
260269b2e231SSrikanth Suravajhala 		if (pwrk->timer == 0) {
260369b2e231SSrikanth Suravajhala 			pwrk->timer = US2WT(1000000);
260469b2e231SSrikanth Suravajhala 		}
2605b53bcbf6SSrikanth Suravajhala 	}
2606b53bcbf6SSrikanth Suravajhala 	mutex_exit(&pwrk->lock);
26074c06356bSdh 
26084c06356bSdh 	return (PMCS_WQ_RUN_SUCCESS);
26094c06356bSdh }
26104c06356bSdh 
26114c06356bSdh /*
26124c06356bSdh  * Complete a SATA command.  Called with pwrk lock held.
26134c06356bSdh  */
26144c06356bSdh void
pmcs_SATA_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)26154c06356bSdh pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
26164c06356bSdh {
26174c06356bSdh 	pmcs_cmd_t *sp = pwrk->arg;
26184c06356bSdh 	struct scsi_pkt *pkt = CMD2PKT(sp);
26194c06356bSdh 	pmcs_phy_t *pptr = pwrk->phy;
26204c06356bSdh 	int dead;
26214c06356bSdh 	uint32_t sts;
26224c06356bSdh 	pmcs_xscsi_t *xp;
26234c06356bSdh 	boolean_t aborted = B_FALSE;
26244c06356bSdh 
26254c06356bSdh 	xp = pwrk->xp;
26264c06356bSdh 	ASSERT(xp != NULL);
26274c06356bSdh 
26284c06356bSdh 	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
26294c06356bSdh 	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
26304c06356bSdh 	    hrtime_t, gethrtime());
26314c06356bSdh 
26324c06356bSdh 	dead = pwrk->dead;
26334c06356bSdh 
26344c06356bSdh 	if (msg) {
26354c06356bSdh 		sts = LE_32(msg[2]);
26364c06356bSdh 	} else {
26374c06356bSdh 		sts = 0;
26384c06356bSdh 	}
26394c06356bSdh 
26404c06356bSdh 	if (dead != 0) {
2641c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2642c3bc407cSdh 		    "0x%x for %s", __func__, pwrk->htag, pptr->path);
26434c06356bSdh 		goto out;
26444c06356bSdh 	}
26454c06356bSdh 	if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
26464c06356bSdh 	    (sts != PMCOUT_STATUS_ABORTED)) {
2647c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
26484c06356bSdh 		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
26494c06356bSdh 		    __func__, (void *)sp, pwrk->htag, pptr->path);
26504c06356bSdh 		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
26514c06356bSdh 		/* pkt_reason already set to CMD_TIMEOUT */
26524c06356bSdh 		ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
26534c06356bSdh 		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
26544c06356bSdh 		    STATE_SENT_CMD;
26554c06356bSdh 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
26564c06356bSdh 		goto out;
26574c06356bSdh 	}
26584c06356bSdh 
2659c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done",
26604c06356bSdh 	    __func__, (void *)pkt, xp->target_num);
26614c06356bSdh 
26624c06356bSdh 	/*
26634c06356bSdh 	 * If the status isn't okay but not underflow,
26644c06356bSdh 	 * step to the side and parse the (possible) error.
26654c06356bSdh 	 */
26664c06356bSdh #ifdef DEBUG
26674c06356bSdh 	if (msg) {
26684c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
26694c06356bSdh 	}
26704c06356bSdh #endif
26714c06356bSdh 	if (!msg) {
26724c06356bSdh 		goto out;
26734c06356bSdh 	}
26744c06356bSdh 
26754c06356bSdh 	/*
26764c06356bSdh 	 * If the status isn't okay or we got a FIS response of some kind,
26774c06356bSdh 	 * step to the side and parse the (possible) error.
26784c06356bSdh 	 */
26794c06356bSdh 	if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
26804c06356bSdh 		if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
26814c06356bSdh 			mutex_exit(&pwrk->lock);
26824c06356bSdh 			pmcs_lock_phy(pptr);
26834c06356bSdh 			mutex_enter(&xp->statlock);
26844c06356bSdh 			if ((xp->resetting == 0) && (xp->reset_success != 0) &&
26854c06356bSdh 			    (xp->reset_wait == 0)) {
26864c06356bSdh 				mutex_exit(&xp->statlock);
26874c06356bSdh 				if (pmcs_reset_phy(pwp, pptr,
26884c06356bSdh 				    PMCS_PHYOP_LINK_RESET) != 0) {
2689c3bc407cSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2690c3bc407cSdh 					    "%s: PHY (%s) Local Control/Link "
2691c3bc407cSdh 					    "Reset FAILED as part of error "
2692c3bc407cSdh 					    "recovery", __func__, pptr->path);
26934c06356bSdh 				}
26944c06356bSdh 				mutex_enter(&xp->statlock);
26954c06356bSdh 			}
26964c06356bSdh 			mutex_exit(&xp->statlock);
26974c06356bSdh 			pmcs_unlock_phy(pptr);
26984c06356bSdh 			mutex_enter(&pwrk->lock);
26994c06356bSdh 		}
2700658280b6SDavid Hollister 		pmcs_ioerror(pwp, SATA, pwrk, msg, sts);
27014c06356bSdh 	} else {
27024c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
27034c06356bSdh 		    pwrk->phy->path);
27044c06356bSdh 		pkt->pkt_state |= STATE_XFERRED_DATA;
27054c06356bSdh 		pkt->pkt_resid = 0;
27064c06356bSdh 	}
27074c06356bSdh 
2708c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
27094c06356bSdh 	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
27104c06356bSdh 	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
27114c06356bSdh 	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
27124c06356bSdh 
27134c06356bSdh 	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2714c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
27154c06356bSdh 		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
27164c06356bSdh 		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
27174c06356bSdh 		aborted = B_TRUE;
27184c06356bSdh 	}
27194c06356bSdh 
27204c06356bSdh out:
27214c06356bSdh 	pmcs_dma_unload(pwp, sp);
27224c06356bSdh 	mutex_enter(&xp->statlock);
27234c06356bSdh 	xp->tagmap &= ~(1 << sp->cmd_satltag);
27244c06356bSdh 
27255c45adf0SJesse Butler 	/*
27265c45adf0SJesse Butler 	 * If the device no longer has a PHY pointer, clear the PHY pointer
27275c45adf0SJesse Butler 	 * from the work structure before we free it.  Otherwise, pmcs_pwork
27285c45adf0SJesse Butler 	 * may decrement the ref_count on a PHY that's been freed.
27295c45adf0SJesse Butler 	 */
27305c45adf0SJesse Butler 	if (xp->phy == NULL) {
27315c45adf0SJesse Butler 		pwrk->phy = NULL;
27325c45adf0SJesse Butler 	}
27335c45adf0SJesse Butler 
273439cd77a0SJesse Butler 	/*
273539cd77a0SJesse Butler 	 * We may arrive here due to a command timing out, which in turn
273639cd77a0SJesse Butler 	 * could be addressed in a different context.  So, free the work
273739cd77a0SJesse Butler 	 * back, but only after confirming it's not already been freed
273839cd77a0SJesse Butler 	 * elsewhere.
273939cd77a0SJesse Butler 	 */
27403492a3feSJesse Butler 	if (pwrk->htag != PMCS_TAG_FREE) {
274139cd77a0SJesse Butler 		pmcs_pwork(pwp, pwrk);
274239cd77a0SJesse Butler 	}
27435c45adf0SJesse Butler 
27444c06356bSdh 	if (xp->dev_gone) {
27454c06356bSdh 		mutex_exit(&xp->statlock);
27469aed1621SDavid Hollister 		if (!dead) {
2747429adc13SSrikanth, Ramana 			mutex_enter(&xp->aqlock);
2748429adc13SSrikanth, Ramana 			STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2749429adc13SSrikanth, Ramana 			mutex_exit(&xp->aqlock);
27505c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2751429adc13SSrikanth, Ramana 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2752429adc13SSrikanth, Ramana 			    __func__, (void *)sp, sp->cmd_tag);
27539aed1621SDavid Hollister 			mutex_enter(&pwp->cq_lock);
27549aed1621SDavid Hollister 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
27558f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
27569aed1621SDavid Hollister 			mutex_exit(&pwp->cq_lock);
27579aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
27589aed1621SDavid Hollister 			    "%s: Completing command for dead target 0x%p",
27599aed1621SDavid Hollister 			    __func__, (void *)xp);
27609aed1621SDavid Hollister 		}
27614c06356bSdh 		return;
27624c06356bSdh 	}
27634c06356bSdh 
27644c06356bSdh 	ASSERT(xp->actv_cnt > 0);
27654c06356bSdh 	if (--(xp->actv_cnt) == 0) {
27664c06356bSdh 		if (xp->draining) {
2767c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
27684c06356bSdh 			    "%s: waking up drain waiters", __func__);
27694c06356bSdh 			cv_signal(&pwp->drain_cv);
27704c06356bSdh 		} else if (xp->special_needed) {
27714c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
27724c06356bSdh 		}
27734c06356bSdh 	}
27744c06356bSdh 	mutex_exit(&xp->statlock);
27754c06356bSdh 
2776c280a92bSDavid Hollister 	/*
2777c280a92bSDavid Hollister 	 * If the status is other than OK, determine if it's something that
2778c280a92bSDavid Hollister 	 * is worth re-attempting enumeration.  If so, mark the PHY.
2779c280a92bSDavid Hollister 	 */
2780c280a92bSDavid Hollister 	if (sts != PMCOUT_STATUS_OK) {
2781c280a92bSDavid Hollister 		pmcs_status_disposition(pptr, sts);
2782c280a92bSDavid Hollister 	}
2783c280a92bSDavid Hollister 
27844c06356bSdh 	if (dead == 0) {
27854c06356bSdh #ifdef	DEBUG
27864c06356bSdh 		pmcs_cmd_t *wp;
27874c06356bSdh 		mutex_enter(&xp->aqlock);
27884c06356bSdh 		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
27894c06356bSdh 			if (wp == sp) {
27904c06356bSdh 				break;
27914c06356bSdh 			}
27924c06356bSdh 		}
27934c06356bSdh 		ASSERT(wp != NULL);
27944c06356bSdh #else
27954c06356bSdh 		mutex_enter(&xp->aqlock);
27964c06356bSdh #endif
27974c06356bSdh 		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
27984c06356bSdh 		if (aborted) {
2799c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
28004c06356bSdh 			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
28014c06356bSdh 			    __func__, (void *)xp);
28024c06356bSdh 			cv_signal(&xp->abort_cv);
28034c06356bSdh 		}
28044c06356bSdh 		mutex_exit(&xp->aqlock);
28054c06356bSdh 		mutex_enter(&pwp->cq_lock);
28064c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
28078f514e74SDavid Hollister 		PMCS_CQ_RUN_LOCKED(pwp);
28084c06356bSdh 		mutex_exit(&pwp->cq_lock);
28094c06356bSdh 	}
28104c06356bSdh }
28114c06356bSdh 
28124c06356bSdh static uint8_t
pmcs_SATA_rwparm(uint8_t * cdb,uint32_t * xfr,uint64_t * lba,uint64_t lbamax)28134c06356bSdh pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
28144c06356bSdh {
28154c06356bSdh 	uint8_t asc = 0;
28164c06356bSdh 	switch (cdb[0]) {
28174c06356bSdh 	case SCMD_READ_G5:
28184c06356bSdh 	case SCMD_WRITE_G5:
28194c06356bSdh 		*xfr =
28204c06356bSdh 		    (((uint32_t)cdb[10]) <<  24) |
28214c06356bSdh 		    (((uint32_t)cdb[11]) <<  16) |
28224c06356bSdh 		    (((uint32_t)cdb[12]) <<   8) |
28234c06356bSdh 		    ((uint32_t)cdb[13]);
28244c06356bSdh 		*lba =
28254c06356bSdh 		    (((uint64_t)cdb[2]) << 56) |
28264c06356bSdh 		    (((uint64_t)cdb[3]) << 48) |
28274c06356bSdh 		    (((uint64_t)cdb[4]) << 40) |
28284c06356bSdh 		    (((uint64_t)cdb[5]) << 32) |
28294c06356bSdh 		    (((uint64_t)cdb[6]) << 24) |
28304c06356bSdh 		    (((uint64_t)cdb[7]) << 16) |
28314c06356bSdh 		    (((uint64_t)cdb[8]) <<  8) |
28324c06356bSdh 		    ((uint64_t)cdb[9]);
28334c06356bSdh 		/* Check for illegal bits */
28344c06356bSdh 		if (cdb[15]) {
28354c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
28364c06356bSdh 		}
28374c06356bSdh 		break;
28384c06356bSdh 	case SCMD_READ_G4:
28394c06356bSdh 	case SCMD_WRITE_G4:
28404c06356bSdh 		*xfr =
28414c06356bSdh 		    (((uint32_t)cdb[6]) <<  16) |
28424c06356bSdh 		    (((uint32_t)cdb[7]) <<   8) |
28434c06356bSdh 		    ((uint32_t)cdb[8]);
28444c06356bSdh 		*lba =
28454c06356bSdh 		    (((uint32_t)cdb[2]) << 24) |
28464c06356bSdh 		    (((uint32_t)cdb[3]) << 16) |
28474c06356bSdh 		    (((uint32_t)cdb[4]) <<  8) |
28484c06356bSdh 		    ((uint32_t)cdb[5]);
28494c06356bSdh 		/* Check for illegal bits */
28504c06356bSdh 		if (cdb[11]) {
28514c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
28524c06356bSdh 		}
28534c06356bSdh 		break;
28544c06356bSdh 	case SCMD_READ_G1:
28554c06356bSdh 	case SCMD_WRITE_G1:
28564c06356bSdh 		*xfr = (((uint32_t)cdb[7]) <<  8) | ((uint32_t)cdb[8]);
28574c06356bSdh 		*lba =
28584c06356bSdh 		    (((uint32_t)cdb[2]) << 24) |
28594c06356bSdh 		    (((uint32_t)cdb[3]) << 16) |
28604c06356bSdh 		    (((uint32_t)cdb[4]) <<  8) |
28614c06356bSdh 		    ((uint32_t)cdb[5]);
28624c06356bSdh 		/* Check for illegal bits */
28634c06356bSdh 		if (cdb[9]) {
28644c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
28654c06356bSdh 		}
28664c06356bSdh 		break;
28674c06356bSdh 	case SCMD_READ:
28684c06356bSdh 	case SCMD_WRITE:
28694c06356bSdh 		*xfr = cdb[4];
28704c06356bSdh 		if (*xfr == 0) {
28714c06356bSdh 			*xfr = 256;
28724c06356bSdh 		}
28734c06356bSdh 		*lba =
28744c06356bSdh 		    (((uint32_t)cdb[1] & 0x1f) << 16) |
28754c06356bSdh 		    (((uint32_t)cdb[2]) << 8) |
28764c06356bSdh 		    ((uint32_t)cdb[3]);
28774c06356bSdh 		/* Check for illegal bits */
28784c06356bSdh 		if (cdb[5]) {
28794c06356bSdh 			asc = 0x24;	/* invalid field in cdb */
28804c06356bSdh 		}
28814c06356bSdh 		break;
28824c06356bSdh 	}
28834c06356bSdh 
28844c06356bSdh 	if (asc == 0) {
28854c06356bSdh 		if ((*lba + *xfr) > lbamax) {
28864c06356bSdh 			asc = 0x21;	/* logical block out of range */
28874c06356bSdh 		}
28884c06356bSdh 	}
28894c06356bSdh 	return (asc);
28904c06356bSdh }
28914c06356bSdh 
28924c06356bSdh /*
28934c06356bSdh  * Called with pwrk lock held.
28944c06356bSdh  */
28954c06356bSdh static void
pmcs_ioerror(pmcs_hw_t * pwp,pmcs_dtype_t t,pmcwork_t * pwrk,uint32_t * w,uint32_t status)2896658280b6SDavid Hollister pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w,
2897658280b6SDavid Hollister     uint32_t status)
28984c06356bSdh {
28994c06356bSdh 	static uint8_t por[] = {
29004c06356bSdh 	    0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
29014c06356bSdh 	};
29024c06356bSdh 	static uint8_t parity[] = {
29034c06356bSdh 	    0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
29044c06356bSdh 	};
29054c06356bSdh 	const char *msg;
29064c06356bSdh 	char buf[20];
29074c06356bSdh 	pmcs_cmd_t *sp = pwrk->arg;
29084c06356bSdh 	pmcs_phy_t *phyp = pwrk->phy;
29094c06356bSdh 	struct scsi_pkt *pkt = CMD2PKT(sp);
29104c06356bSdh 	uint32_t resid;
29114c06356bSdh 
29124c06356bSdh 	ASSERT(w != NULL);
29134c06356bSdh 	resid = LE_32(w[3]);
29144c06356bSdh 
29154c06356bSdh 	msg = pmcs_status_str(status);
29164c06356bSdh 	if (msg == NULL) {
29174c06356bSdh 		(void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
29184c06356bSdh 		msg = buf;
29194c06356bSdh 	}
29204c06356bSdh 
29214c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
29229aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, NULL,
29234c06356bSdh 		    "%s: device %s tag 0x%x status %s @ %llu", __func__,
29244c06356bSdh 		    phyp->path, pwrk->htag, msg,
29254c06356bSdh 		    (unsigned long long)gethrtime());
29264c06356bSdh 	}
29274c06356bSdh 
29284c06356bSdh 	pkt->pkt_reason = CMD_CMPLT;		/* default reason */
29294c06356bSdh 
29304c06356bSdh 	switch (status) {
29314c06356bSdh 	case PMCOUT_STATUS_OK:
29324c06356bSdh 		if (t == SATA) {
29334c06356bSdh 			int i;
29344c06356bSdh 			fis_t fis;
29354c06356bSdh 			for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
29364c06356bSdh 				fis[i] = LE_32(w[4+i]);
29374c06356bSdh 			}
29384c06356bSdh 			if ((fis[0] & 0xff) != FIS_REG_D2H) {
2939c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
29404c06356bSdh 				    "unexpected fis code 0x%x", fis[0] & 0xff);
29414c06356bSdh 			} else {
2942c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
2943c3bc407cSdh 				    "FIS ERROR");
29444c06356bSdh 				pmcs_fis_dump(pwp, fis);
29454c06356bSdh 			}
29464c06356bSdh 			pkt->pkt_reason = CMD_TRAN_ERR;
29474c06356bSdh 			break;
29484c06356bSdh 		}
29494c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29504c06356bSdh 		break;
29514c06356bSdh 
29524c06356bSdh 	case PMCOUT_STATUS_ABORTED:
29534c06356bSdh 		/*
29544c06356bSdh 		 * Command successfully aborted.
29554c06356bSdh 		 */
29564c06356bSdh 		if (phyp->dead) {
29574c06356bSdh 			pkt->pkt_reason = CMD_DEV_GONE;
29584c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS;
29594c06356bSdh 		} else if (pwrk->ssp_event != 0) {
29604c06356bSdh 			pkt->pkt_reason = CMD_TRAN_ERR;
29614c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS;
29624c06356bSdh 		} else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
29634c06356bSdh 			pkt->pkt_reason = CMD_TIMEOUT;
29644c06356bSdh 			pkt->pkt_statistics |= STAT_TIMEOUT;
29654c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29664c06356bSdh 			    STATE_SENT_CMD;
29674c06356bSdh 		} else {
29684c06356bSdh 			pkt->pkt_reason = CMD_ABORTED;
29694c06356bSdh 			pkt->pkt_statistics |= STAT_ABORTED;
29704c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29714c06356bSdh 			    STATE_SENT_CMD;
29724c06356bSdh 		}
29734c06356bSdh 
29744c06356bSdh 		/*
29754c06356bSdh 		 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
29764c06356bSdh 		 * this point, so go ahead and mark it as aborted.
29774c06356bSdh 		 */
29784c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ABORTED;
29794c06356bSdh 		break;
29804c06356bSdh 
29814c06356bSdh 	case PMCOUT_STATUS_UNDERFLOW:
29824c06356bSdh 		/*
29834c06356bSdh 		 * This will only get called for SATA
29844c06356bSdh 		 */
29854c06356bSdh 		pkt->pkt_resid = resid;
29864c06356bSdh 		if (pkt->pkt_dma_len < pkt->pkt_resid) {
29874c06356bSdh 			(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
29884c06356bSdh 		}
29894c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29904c06356bSdh 		break;
29914c06356bSdh 
29924c06356bSdh 	case PMCOUT_STATUS_NO_DEVICE:
29934c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
29944c06356bSdh 		pkt->pkt_reason = CMD_DEV_GONE;
29954c06356bSdh 		break;
29964c06356bSdh 
29974c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
29984c06356bSdh 		/*
29994c06356bSdh 		 * Need to do rediscovery. We probably have
30004c06356bSdh 		 * the wrong device (disk swap), so kill
30014c06356bSdh 		 * this one.
30024c06356bSdh 		 */
30034c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
30044c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
30054c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
30069aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
30074c06356bSdh 		/*
30084c06356bSdh 		 * Need to do rediscovery.
30094c06356bSdh 		 */
30104c06356bSdh 		if (!phyp->dead) {
30114c06356bSdh 			mutex_exit(&pwrk->lock);
30124c06356bSdh 			pmcs_lock_phy(pwrk->phy);
30134c06356bSdh 			pmcs_kill_changed(pwp, pwrk->phy, 0);
30144c06356bSdh 			pmcs_unlock_phy(pwrk->phy);
30154c06356bSdh 			mutex_enter(&pwrk->lock);
30164c06356bSdh 			pkt->pkt_reason = CMD_INCOMPLETE;
30174c06356bSdh 			pkt->pkt_state = STATE_GOT_BUS;
30184c06356bSdh 		} else {
30194c06356bSdh 			pkt->pkt_reason = CMD_DEV_GONE;
30204c06356bSdh 		}
30214c06356bSdh 		break;
30224c06356bSdh 
30234c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
30244c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
30254c06356bSdh 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
30264c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
30274c06356bSdh 		/* cmd is pending on the target */
30284c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
30294c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
30304c06356bSdh 		/* transitory - commands sent while in NCQ failure mode */
30314c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
30324c06356bSdh 		/* NCQ failure */
30334c06356bSdh 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
30344c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_BREAK:
30354c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
30364c06356bSdh 		pkt->pkt_reason = CMD_INCOMPLETE;
30374c06356bSdh 		pkt->pkt_state = STATE_GOT_BUS;
30384c06356bSdh 		break;
30394c06356bSdh 
30404c06356bSdh 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
3041658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3042658280b6SDavid Hollister 		    "STATUS_BUSY for htag 0x%08x", sp->cmd_tag);
30434c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
30444c06356bSdh 		break;
30454c06356bSdh 
30464c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
30474c06356bSdh 		/* synthesize a RESERVATION CONFLICT */
3048499cfd15SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3049499cfd15SDavid Hollister 		    "%s: Potential affiliation active on 0x%" PRIx64, __func__,
3050499cfd15SDavid Hollister 		    pmcs_barray2wwn(phyp->sas_address));
30514c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
30524c06356bSdh 		    0, phyp->path);
30534c06356bSdh 		break;
30544c06356bSdh 
30554c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
30564c06356bSdh 		/* synthesize a power-on/reset */
30574c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
30584c06356bSdh 		    phyp->path);
30594c06356bSdh 		break;
30604c06356bSdh 
30614c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
30624c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
30634c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
30644c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
30654c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
30664c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
30674c06356bSdh 		/* synthesize a PARITY ERROR */
30684c06356bSdh 		pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
30694c06356bSdh 		    sizeof (parity), phyp->path);
30704c06356bSdh 		break;
30714c06356bSdh 
30724c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
30734c06356bSdh 	case PMCOUT_STATUS_IO_NOT_VALID:
30744c06356bSdh 	case PMCOUT_STATUS_PROG_ERROR:
30754c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
30764c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
30774c06356bSdh 	default:
30784c06356bSdh 		pkt->pkt_reason = CMD_TRAN_ERR;
30794c06356bSdh 		break;
30804c06356bSdh 	}
30814c06356bSdh }
30824c06356bSdh 
30834c06356bSdh /*
30844c06356bSdh  * Latch up SCSI status
30854c06356bSdh  */
30864c06356bSdh 
30874c06356bSdh void
pmcs_latch_status(pmcs_hw_t * pwp,pmcs_cmd_t * sp,uint8_t status,uint8_t * snsp,size_t snslen,char * path)30884c06356bSdh pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
30894c06356bSdh     uint8_t *snsp, size_t snslen, char *path)
30904c06356bSdh {
30914c06356bSdh 	static const char c1[] =
30924c06356bSdh 	    "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
30934c06356bSdh 	    "HTAG 0x%x @ %llu";
30944c06356bSdh 	static const char c2[] =
30954c06356bSdh 	    "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
30964c06356bSdh 
30974c06356bSdh 	CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
30984c06356bSdh 	    STATE_SENT_CMD | STATE_GOT_STATUS;
30994c06356bSdh 	CMD2PKT(sp)->pkt_scbp[0] = status;
31004c06356bSdh 
31014c06356bSdh 	if (status == STATUS_CHECK && snsp &&
31024c06356bSdh 	    (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
31034c06356bSdh 		struct scsi_arq_status *aqp =
31044c06356bSdh 		    (void *) CMD2PKT(sp)->pkt_scbp;
31054c06356bSdh 		size_t amt = sizeof (struct scsi_extended_sense);
31064c06356bSdh 		uint8_t key = scsi_sense_key(snsp);
31074c06356bSdh 		uint8_t asc = scsi_sense_asc(snsp);
31084c06356bSdh 		uint8_t ascq = scsi_sense_ascq(snsp);
31094c06356bSdh 		if (amt > snslen) {
31104c06356bSdh 			amt = snslen;
31114c06356bSdh 		}
3112c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path,
3113c3bc407cSdh 		    status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
31144c06356bSdh 		    sp->cmd_tag, (unsigned long long)gethrtime());
31154c06356bSdh 		CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
31164c06356bSdh 		(*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
31174c06356bSdh 		aqp->sts_rqpkt_statistics = 0;
31184c06356bSdh 		aqp->sts_rqpkt_reason = CMD_CMPLT;
31194c06356bSdh 		aqp->sts_rqpkt_state = STATE_GOT_BUS |
31204c06356bSdh 		    STATE_GOT_TARGET | STATE_SENT_CMD |
31214c06356bSdh 		    STATE_XFERRED_DATA | STATE_GOT_STATUS;
31224c06356bSdh 		(void) memcpy(&aqp->sts_sensedata, snsp, amt);
31234c06356bSdh 		if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
31244c06356bSdh 			aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
31254c06356bSdh 			aqp->sts_rqpkt_state = 0;
31264c06356bSdh 			aqp->sts_rqpkt_resid =
31274c06356bSdh 			    sizeof (struct scsi_extended_sense);
31284c06356bSdh 		} else {
31294c06356bSdh 			aqp->sts_rqpkt_resid =
31304c06356bSdh 			    sizeof (struct scsi_extended_sense) - amt;
31314c06356bSdh 		}
31324c06356bSdh 	} else if (status) {
3133c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2,
31344c06356bSdh 		    path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
31354c06356bSdh 		    sp->cmd_tag, (unsigned long long)gethrtime());
31364c06356bSdh 	}
31374c06356bSdh 
31384c06356bSdh 	CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
31394c06356bSdh }
31404c06356bSdh 
31414c06356bSdh /*
31424c06356bSdh  * Calculate and set packet residual and return the amount
31434c06356bSdh  * left over after applying various filters.
31444c06356bSdh  */
31454c06356bSdh size_t
pmcs_set_resid(struct scsi_pkt * pkt,size_t amt,uint32_t cdbamt)31464c06356bSdh pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
31474c06356bSdh {
31484c06356bSdh 	pkt->pkt_resid = cdbamt;
31494c06356bSdh 	if (amt > pkt->pkt_resid) {
31504c06356bSdh 		amt = pkt->pkt_resid;
31514c06356bSdh 	}
31524c06356bSdh 	if (amt > pkt->pkt_dma_len) {
31534c06356bSdh 		amt = pkt->pkt_dma_len;
31544c06356bSdh 	}
31554c06356bSdh 	return (amt);
31564c06356bSdh }
31574c06356bSdh 
31584c06356bSdh /*
3159c280a92bSDavid Hollister  * Return the existing target softstate (unlocked) if there is one.  If so,
3160c280a92bSDavid Hollister  * the PHY is locked and that lock must be freed by the caller after the
3161c280a92bSDavid Hollister  * target/PHY linkage is established.  If there isn't one, and alloc_tgt is
3162c280a92bSDavid Hollister  * TRUE, then allocate one.
31634c06356bSdh  */
31644c06356bSdh pmcs_xscsi_t *
pmcs_get_target(pmcs_iport_t * iport,char * tgt_port,boolean_t alloc_tgt)31655c45adf0SJesse Butler pmcs_get_target(pmcs_iport_t *iport, char *tgt_port, boolean_t alloc_tgt)
31664c06356bSdh {
31674c06356bSdh 	pmcs_hw_t *pwp = iport->pwp;
31684c06356bSdh 	pmcs_phy_t *phyp;
31694c06356bSdh 	pmcs_xscsi_t *tgt;
31704c06356bSdh 	uint64_t wwn;
31714c06356bSdh 	char unit_address[PMCS_MAX_UA_SIZE];
31724c06356bSdh 	int ua_form = 1;
31734c06356bSdh 
31744c06356bSdh 	/*
31754c06356bSdh 	 * Find the PHY for this target
31764c06356bSdh 	 */
31774c06356bSdh 	phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
31784c06356bSdh 	if (phyp == NULL) {
3179c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
3180c3bc407cSdh 		    "%s: No PHY for target @ %s", __func__, tgt_port);
31814c06356bSdh 		return (NULL);
31824c06356bSdh 	}
31834c06356bSdh 
31844c06356bSdh 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
31854c06356bSdh 
31864c06356bSdh 	if (tgt) {
3187c280a92bSDavid Hollister 		mutex_enter(&tgt->statlock);
31884c06356bSdh 		/*
31894c06356bSdh 		 * There's already a target.  Check its PHY pointer to see
31904c06356bSdh 		 * if we need to clear the old linkages
31914c06356bSdh 		 */
31924c06356bSdh 		if (tgt->phy && (tgt->phy != phyp)) {
3193c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
31944c06356bSdh 			    "%s: Target PHY updated from %p to %p", __func__,
31954c06356bSdh 			    (void *)tgt->phy, (void *)phyp);
31964c06356bSdh 			if (!IS_ROOT_PHY(tgt->phy)) {
31974c06356bSdh 				pmcs_dec_phy_ref_count(tgt->phy);
31984c06356bSdh 				pmcs_inc_phy_ref_count(phyp);
31994c06356bSdh 			}
32004c06356bSdh 			tgt->phy->target = NULL;
32014c06356bSdh 		}
32024c06356bSdh 
3203c280a92bSDavid Hollister 		/*
3204c280a92bSDavid Hollister 		 * If this target has no PHY pointer and alloc_tgt is FALSE,
3205c280a92bSDavid Hollister 		 * that implies we expect the target to already exist.  This
3206c280a92bSDavid Hollister 		 * implies that there has already been a tran_tgt_init on at
3207c280a92bSDavid Hollister 		 * least one LU.
3208c280a92bSDavid Hollister 		 */
3209c280a92bSDavid Hollister 		if ((tgt->phy == NULL) && !alloc_tgt) {
3210c280a92bSDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt,
3211c280a92bSDavid Hollister 			    "%s: Establish linkage from new PHY to old target @"
3212c280a92bSDavid Hollister 			    "%s", __func__, tgt->unit_address);
3213c280a92bSDavid Hollister 			for (int idx = 0; idx < tgt->ref_count; idx++) {
3214c280a92bSDavid Hollister 				pmcs_inc_phy_ref_count(phyp);
3215c280a92bSDavid Hollister 			}
3216c280a92bSDavid Hollister 		}
3217c280a92bSDavid Hollister 
32186c87a171SJesse Butler 		/*
32196c87a171SJesse Butler 		 * Set this target pointer back up, since it's been
32206c87a171SJesse Butler 		 * through pmcs_clear_xp().
32216c87a171SJesse Butler 		 */
32226c87a171SJesse Butler 		tgt->dev_gone = 0;
32236c87a171SJesse Butler 		tgt->assigned = 1;
32246c87a171SJesse Butler 		tgt->dtype = phyp->dtype;
32256c87a171SJesse Butler 		tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
32264c06356bSdh 		tgt->phy = phyp;
32274c06356bSdh 		phyp->target = tgt;
3228c280a92bSDavid Hollister 
3229c280a92bSDavid Hollister 		mutex_exit(&tgt->statlock);
32304c06356bSdh 		return (tgt);
32314c06356bSdh 	}
32324c06356bSdh 
32334c06356bSdh 	/*
32344c06356bSdh 	 * Make sure the PHY we found is on the correct iport
32354c06356bSdh 	 */
32364c06356bSdh 	if (phyp->iport != iport) {
3237c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
32384c06356bSdh 		    "%s: No target at %s on this iport", __func__, tgt_port);
32394c06356bSdh 		pmcs_unlock_phy(phyp);
32404c06356bSdh 		return (NULL);
32414c06356bSdh 	}
32424c06356bSdh 
32435c45adf0SJesse Butler 	/*
32445c45adf0SJesse Butler 	 * If this was just a lookup (i.e. alloc_tgt is false), return now.
32455c45adf0SJesse Butler 	 */
32465c45adf0SJesse Butler 	if (alloc_tgt == B_FALSE) {
32475c45adf0SJesse Butler 		pmcs_unlock_phy(phyp);
32485c45adf0SJesse Butler 		return (NULL);
32495c45adf0SJesse Butler 	}
32505c45adf0SJesse Butler 
32514c06356bSdh 	/*
32524c06356bSdh 	 * Allocate the new softstate
32534c06356bSdh 	 */
32544c06356bSdh 	wwn = pmcs_barray2wwn(phyp->sas_address);
32554c06356bSdh 	(void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
32564c06356bSdh 
32574c06356bSdh 	if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
32584c06356bSdh 	    DDI_SUCCESS) {
3259c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
32604c06356bSdh 		    "%s: Couldn't alloc softstate for device at %s",
32614c06356bSdh 		    __func__, unit_address);
32624c06356bSdh 		pmcs_unlock_phy(phyp);
32634c06356bSdh 		return (NULL);
32644c06356bSdh 	}
32654c06356bSdh 
32664c06356bSdh 	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
3267a25672a1SDavid Hollister 	ASSERT(tgt != NULL);
32684c06356bSdh 	STAILQ_INIT(&tgt->wq);
32694c06356bSdh 	STAILQ_INIT(&tgt->aq);
32704c06356bSdh 	STAILQ_INIT(&tgt->sq);
32714c06356bSdh 	mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
32724c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
32734c06356bSdh 	mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
32744c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
32754c06356bSdh 	mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
32764c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
32774c06356bSdh 	cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
32784c06356bSdh 	cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
327973a3eccdSDavid Hollister 	list_create(&tgt->lun_list, sizeof (pmcs_lun_t),
328073a3eccdSDavid Hollister 	    offsetof(pmcs_lun_t, lun_list_next));
32814c06356bSdh 	tgt->qdepth = 1;
32824c06356bSdh 	tgt->target_num = PMCS_INVALID_TARGET_NUM;
32834c06356bSdh 	bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
32844c06356bSdh 	tgt->pwp = pwp;
32854c06356bSdh 	tgt->ua = strdup(iport->ua);
32864c06356bSdh 	tgt->phy = phyp;
32874c06356bSdh 	ASSERT((phyp->target == NULL) || (phyp->target == tgt));
32884c06356bSdh 	if (phyp->target == NULL) {
32894c06356bSdh 		phyp->target = tgt;
32904c06356bSdh 	}
32914c06356bSdh 
32924c06356bSdh 	/*
32934c06356bSdh 	 * Don't allocate LUN softstate for SMP targets
32944c06356bSdh 	 */
32954c06356bSdh 	if (phyp->dtype == EXPANDER) {
32964c06356bSdh 		return (tgt);
32974c06356bSdh 	}
32984c06356bSdh 
32994c06356bSdh 	if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
33004c06356bSdh 	    sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
3301c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
33024c06356bSdh 		    "%s: LUN soft_state_bystr_init failed", __func__);
33034c06356bSdh 		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
33044c06356bSdh 		pmcs_unlock_phy(phyp);
33054c06356bSdh 		return (NULL);
33064c06356bSdh 	}
33074c06356bSdh 
33084c06356bSdh 	return (tgt);
33094c06356bSdh }
3310