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