xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c (revision 3be32c0f0acac4f6258b029f1a27a16a7ec65bb0)
14c06356bSdh /*
24c06356bSdh  * CDDL HEADER START
34c06356bSdh  *
44c06356bSdh  * The contents of this file are subject to the terms of the
54c06356bSdh  * Common Development and Distribution License (the "License").
64c06356bSdh  * You may not use this file except in compliance with the License.
74c06356bSdh  *
84c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh  * See the License for the specific language governing permissions
114c06356bSdh  * and limitations under the License.
124c06356bSdh  *
134c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh  *
194c06356bSdh  * CDDL HEADER END
20658280b6SDavid Hollister  */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh  */
244c06356bSdh 
254c06356bSdh /*
264c06356bSdh  * This file contains various support routines.
274c06356bSdh  */
284c06356bSdh 
294c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
304c06356bSdh 
314c06356bSdh /*
324c06356bSdh  * Local static data
334c06356bSdh  */
3460aabb4cSChris Horne static int tgtmap_stable_usec = MICROSEC;	/* 1 second */
3560aabb4cSChris Horne static int tgtmap_csync_usec = 10 * MICROSEC;	/* 10 seconds */
364c06356bSdh 
374c06356bSdh /*
384c06356bSdh  * SAS Topology Configuration
394c06356bSdh  */
404c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
414c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
424c06356bSdh 
4373a3eccdSDavid Hollister static void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
444c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
454c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
464c06356bSdh 
474c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
484c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
494c06356bSdh     pmcs_phy_t *);
504c06356bSdh 
514c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
52*3be32c0fSJesse Butler static void pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt);
534c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
544c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
554c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
56601c90f1SSrikanth, Ramana static void pmcs_begin_observations(pmcs_hw_t *);
570b53804eSReed static void pmcs_flush_observations(pmcs_hw_t *);
584c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *);
594c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
604c06356bSdh     pmcs_phy_t *);
614c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
624c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
634c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int);
644c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
654c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
664c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
674c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *);
684c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
694c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *);
709aed1621SDavid Hollister static void pmcs_iport_active(pmcs_iport_t *);
719aed1621SDavid Hollister static void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t,
729aed1621SDavid Hollister     void **);
739aed1621SDavid Hollister static boolean_t pmcs_tgtmap_deactivate_cb(void *, char *,
749aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t);
759aed1621SDavid Hollister static void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *);
762ac4abe8SDavid Hollister static void pmcs_get_fw_version(pmcs_hw_t *);
771f81b464SDavid Hollister static int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *);
784c06356bSdh 
794c06356bSdh /*
804c06356bSdh  * Often used strings
814c06356bSdh  */
824c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure";
834c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
846745c559SJesse Butler const char pmcs_timeo[] = "%s: command timed out";
854c06356bSdh 
864c06356bSdh extern const ddi_dma_attr_t pmcs_dattr;
871f81b464SDavid Hollister extern kmutex_t pmcs_trace_lock;
884c06356bSdh 
894c06356bSdh /*
904c06356bSdh  * Some Initial setup steps.
914c06356bSdh  */
924c06356bSdh 
934c06356bSdh int
944c06356bSdh pmcs_setup(pmcs_hw_t *pwp)
954c06356bSdh {
964c06356bSdh 	uint32_t barval = pwp->mpibar;
974c06356bSdh 	uint32_t i, scratch, regbar, regoff, barbar, baroff;
984c06356bSdh 	uint32_t new_ioq_depth, ferr = 0;
994c06356bSdh 
1004c06356bSdh 	/*
1014c06356bSdh 	 * Check current state. If we're not at READY state,
1024c06356bSdh 	 * we can't go further.
1034c06356bSdh 	 */
1044c06356bSdh 	scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
1054c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
106c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
107c3bc407cSdh 		    "%s: AAP Error State (0x%x)",
1084c06356bSdh 		    __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1094c06356bSdh 		    PMCS_MSGU_AAP_ERROR_MASK);
1104c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1114c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1124c06356bSdh 		return (-1);
1134c06356bSdh 	}
1144c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
115c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1164c06356bSdh 		    "%s: AAP unit not ready (state 0x%x)",
1174c06356bSdh 		    __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
1184c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1194c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1204c06356bSdh 		return (-1);
1214c06356bSdh 	}
1224c06356bSdh 
1234c06356bSdh 	/*
1244c06356bSdh 	 * Read the offset from the Message Unit scratchpad 0 register.
1254c06356bSdh 	 * This allows us to read the MPI Configuration table.
1264c06356bSdh 	 *
1274c06356bSdh 	 * Check its signature for validity.
1284c06356bSdh 	 */
1294c06356bSdh 	baroff = barval;
1304c06356bSdh 	barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
1314c06356bSdh 	baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
1324c06356bSdh 
1334c06356bSdh 	regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
1344c06356bSdh 	regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
1354c06356bSdh 	regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
1364c06356bSdh 
1374c06356bSdh 	if (regoff > baroff) {
138c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
139c3bc407cSdh 		    "%s: bad MPI Table Length (register offset=0x%08x, "
140c3bc407cSdh 		    "passed offset=0x%08x)", __func__, regoff, baroff);
1414c06356bSdh 		return (-1);
1424c06356bSdh 	}
1434c06356bSdh 	if (regbar != barbar) {
144c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
145c3bc407cSdh 		    "%s: bad MPI BAR (register BAROFF=0x%08x, "
146c3bc407cSdh 		    "passed BAROFF=0x%08x)", __func__, regbar, barbar);
1474c06356bSdh 		return (-1);
1484c06356bSdh 	}
1494c06356bSdh 	pwp->mpi_offset = regoff;
1504c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
151c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1524c06356bSdh 		    "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
1534c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
1544c06356bSdh 		return (-1);
1554c06356bSdh 	}
1564c06356bSdh 
1574c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
158c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1594c06356bSdh 		    "%s: Bad MPI Configuration Revision 0x%x", __func__,
1604c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
1614c06356bSdh 		return (-1);
1624c06356bSdh 	}
1634c06356bSdh 
1644c06356bSdh 	/*
1654c06356bSdh 	 * Generate offsets for the General System, Inbound Queue Configuration
1664c06356bSdh 	 * and Outbound Queue configuration tables. This way the macros to
1674c06356bSdh 	 * access those tables will work correctly.
1684c06356bSdh 	 */
1694c06356bSdh 	pwp->mpi_gst_offset =
1704c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
1714c06356bSdh 	pwp->mpi_iqc_offset =
1724c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
1734c06356bSdh 	pwp->mpi_oqc_offset =
1744c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
1754c06356bSdh 
1762ac4abe8SDavid Hollister 	pmcs_get_fw_version(pwp);
1774c06356bSdh 
1784c06356bSdh 	pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
1794c06356bSdh 	pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
1804c06356bSdh 
1814c06356bSdh 	pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1824c06356bSdh 	pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1834c06356bSdh 	pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1844c06356bSdh 	if (pwp->max_iq <= PMCS_NIQ) {
185c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
186c3bc407cSdh 		    "%s: not enough Inbound Queues supported "
187c3bc407cSdh 		    "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ);
1884c06356bSdh 		return (-1);
1894c06356bSdh 	}
1904c06356bSdh 	if (pwp->max_oq <= PMCS_NOQ) {
191c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
192c3bc407cSdh 		    "%s: not enough Outbound Queues supported "
193c3bc407cSdh 		    "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ);
1944c06356bSdh 		return (-1);
1954c06356bSdh 	}
1964c06356bSdh 	if (pwp->nphy == 0) {
197c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
198c3bc407cSdh 		    "%s: zero phys reported", __func__);
1994c06356bSdh 		return (-1);
2004c06356bSdh 	}
2014c06356bSdh 	if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
2024c06356bSdh 		pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
2034c06356bSdh 	}
2044c06356bSdh 
2054c06356bSdh 
2064c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
2074c06356bSdh 		PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
2084c06356bSdh 		PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
2094c06356bSdh 	}
2104c06356bSdh 
2114c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
2124c06356bSdh 	    (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
2134c06356bSdh 	    (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
2144c06356bSdh 
2154c06356bSdh 	/*
2164c06356bSdh 	 * Verify that ioq_depth is valid (> 0 and not so high that it
2174c06356bSdh 	 * would cause us to overrun the chip with commands).
2184c06356bSdh 	 */
2194c06356bSdh 	if (pwp->ioq_depth == 0) {
220c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2214c06356bSdh 		    "%s: I/O queue depth set to 0. Setting to %d",
2224c06356bSdh 		    __func__, PMCS_NQENTRY);
2234c06356bSdh 		pwp->ioq_depth = PMCS_NQENTRY;
2244c06356bSdh 	}
2254c06356bSdh 
2264c06356bSdh 	if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
227c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2284c06356bSdh 		    "%s: I/O queue depth set too low (%d). Setting to %d",
2294c06356bSdh 		    __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
2304c06356bSdh 		pwp->ioq_depth = PMCS_MIN_NQENTRY;
2314c06356bSdh 	}
2324c06356bSdh 
2334c06356bSdh 	if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
2344c06356bSdh 		new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
235c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2364c06356bSdh 		    "%s: I/O queue depth set too high (%d). Setting to %d",
2374c06356bSdh 		    __func__, pwp->ioq_depth, new_ioq_depth);
2384c06356bSdh 		pwp->ioq_depth = new_ioq_depth;
2394c06356bSdh 	}
2404c06356bSdh 
2414c06356bSdh 	/*
2424c06356bSdh 	 * Allocate consistent memory for OQs and IQs.
2434c06356bSdh 	 */
2444c06356bSdh 	pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
2454c06356bSdh 	pwp->iqp_dma_attr.dma_attr_align =
2464c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
2474c06356bSdh 
2484c06356bSdh 	/*
2494c06356bSdh 	 * The Rev C chip has the ability to do PIO to or from consistent
2504c06356bSdh 	 * memory anywhere in a 64 bit address space, but the firmware is
2514c06356bSdh 	 * not presently set up to do so.
2524c06356bSdh 	 */
2534c06356bSdh 	pwp->iqp_dma_attr.dma_attr_addr_hi =
2544c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
2554c06356bSdh 
2564c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
2574c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
2584c06356bSdh 		    &pwp->iqp_acchdls[i],
2594c06356bSdh 		    &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2604c06356bSdh 		    (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
261c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2624c06356bSdh 			    "Failed to setup DMA for iqp[%d]", i);
2634c06356bSdh 			return (-1);
2644c06356bSdh 		}
2654c06356bSdh 		bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2664c06356bSdh 	}
2674c06356bSdh 
2684c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
2694c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
2704c06356bSdh 		    &pwp->oqp_acchdls[i],
2714c06356bSdh 		    &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2724c06356bSdh 		    (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
273c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2744c06356bSdh 			    "Failed to setup DMA for oqp[%d]", i);
2754c06356bSdh 			return (-1);
2764c06356bSdh 		}
2774c06356bSdh 		bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2784c06356bSdh 	}
2794c06356bSdh 
2804c06356bSdh 	/*
2814c06356bSdh 	 * Install the IQ and OQ addresses (and null out the rest).
2824c06356bSdh 	 */
2834c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
2844c06356bSdh 		pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
2854c06356bSdh 		if (i < PMCS_NIQ) {
2864c06356bSdh 			if (i != PMCS_IQ_OTHER) {
2874c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2884c06356bSdh 				    pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
2894c06356bSdh 			} else {
2904c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2914c06356bSdh 				    (1 << 30) | pwp->ioq_depth |
2924c06356bSdh 				    (PMCS_QENTRY_SIZE << 16));
2934c06356bSdh 			}
2944c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
2954c06356bSdh 			    DWORD1(pwp->iqaddr[i]));
2964c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
2974c06356bSdh 			    DWORD0(pwp->iqaddr[i]));
2984c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
2994c06356bSdh 			    DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
3004c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
3014c06356bSdh 			    DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
3024c06356bSdh 		} else {
3034c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
3044c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
3054c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
3064c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
3074c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
3084c06356bSdh 		}
3094c06356bSdh 	}
3104c06356bSdh 
3114c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
3124c06356bSdh 		pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
3134c06356bSdh 		if (i < PMCS_NOQ) {
3144c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
3154c06356bSdh 			    (PMCS_QENTRY_SIZE << 16) | OQIEX);
3164c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
3174c06356bSdh 			    DWORD1(pwp->oqaddr[i]));
3184c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
3194c06356bSdh 			    DWORD0(pwp->oqaddr[i]));
3204c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
3214c06356bSdh 			    DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
3224c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
3234c06356bSdh 			    DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
3244c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
3254c06356bSdh 			    pwp->oqvec[i] << 24);
3264c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3274c06356bSdh 		} else {
3284c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
3294c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
3304c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
3314c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
3324c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
3334c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
3344c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3354c06356bSdh 		}
3364c06356bSdh 	}
3374c06356bSdh 
3384c06356bSdh 	/*
3394c06356bSdh 	 * Set up logging, if defined.
3404c06356bSdh 	 */
3414c06356bSdh 	if (pwp->fwlog) {
3424c06356bSdh 		uint64_t logdma = pwp->fwaddr;
3434c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
3444c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
3454c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
3464c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
3474c06356bSdh 		logdma += (PMCS_FWLOG_SIZE >> 1);
3484c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
3494c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
3504c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
3514c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
3524c06356bSdh 	}
3534c06356bSdh 
3544c06356bSdh 	/*
3554c06356bSdh 	 * Interrupt vectors, outbound queues, and odb_auto_clear
3564c06356bSdh 	 *
3574c06356bSdh 	 * MSI/MSI-X:
3584c06356bSdh 	 * If we got 4 interrupt vectors, we'll assign one to each outbound
3594c06356bSdh 	 * queue as well as the fatal interrupt, and auto clear can be set
3604c06356bSdh 	 * for each.
3614c06356bSdh 	 *
3624c06356bSdh 	 * If we only got 2 vectors, one will be used for I/O completions
3634c06356bSdh 	 * and the other for the other two vectors.  In this case, auto_
3644c06356bSdh 	 * clear can only be set for I/Os, which is fine.  The fatal
3654c06356bSdh 	 * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
3664c06356bSdh 	 * is not an interrupt vector.
3674c06356bSdh 	 *
3684c06356bSdh 	 * MSI/MSI-X/INT-X:
3694c06356bSdh 	 * If we only got 1 interrupt vector, auto_clear must be set to 0,
3704c06356bSdh 	 * and again the fatal interrupt will be mapped to the
3714c06356bSdh 	 * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
3724c06356bSdh 	 */
3734c06356bSdh 
3744c06356bSdh 	switch (pwp->int_type) {
3754c06356bSdh 	case PMCS_INT_MSIX:
3764c06356bSdh 	case PMCS_INT_MSI:
3774c06356bSdh 		switch (pwp->intr_cnt) {
3784c06356bSdh 		case 1:
3794c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3804c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3814c06356bSdh 			pwp->odb_auto_clear = 0;
3824c06356bSdh 			break;
3834c06356bSdh 		case 2:
3844c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3854c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3864c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
3874c06356bSdh 			    (1 << PMCS_MSIX_IODONE);
3884c06356bSdh 			break;
3894c06356bSdh 		case 4:
3904c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3914c06356bSdh 			    (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
3924c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
3934c06356bSdh 			    (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
3944c06356bSdh 			    (1 << PMCS_MSIX_EVENTS);
3954c06356bSdh 			break;
3964c06356bSdh 		}
3974c06356bSdh 		break;
3984c06356bSdh 
3994c06356bSdh 	case PMCS_INT_FIXED:
4004c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
4014c06356bSdh 		    PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
4024c06356bSdh 		pwp->odb_auto_clear = 0;
4034c06356bSdh 		break;
4044c06356bSdh 	}
4054c06356bSdh 
406658280b6SDavid Hollister 	/*
407658280b6SDavid Hollister 	 * If the open retry interval is non-zero, set it.
408658280b6SDavid Hollister 	 */
409658280b6SDavid Hollister 	if (pwp->open_retry_interval != 0) {
410658280b6SDavid Hollister 		int phynum;
411658280b6SDavid Hollister 
412658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
413658280b6SDavid Hollister 		    "%s: Setting open retry interval to %d usecs", __func__,
414658280b6SDavid Hollister 		    pwp->open_retry_interval);
415658280b6SDavid Hollister 		for (phynum = 0; phynum < pwp->nphy; phynum ++) {
416658280b6SDavid Hollister 			pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum),
417658280b6SDavid Hollister 			    pwp->open_retry_interval);
418658280b6SDavid Hollister 		}
419658280b6SDavid Hollister 	}
420658280b6SDavid Hollister 
4214c06356bSdh 	/*
4224c06356bSdh 	 * Enable Interrupt Reassertion
4234c06356bSdh 	 * Default Delay 1000us
4244c06356bSdh 	 */
4254c06356bSdh 	ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
4264c06356bSdh 	if ((ferr & PMCS_MPI_IRAE) == 0) {
4274c06356bSdh 		ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
4284c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
4294c06356bSdh 	}
4304c06356bSdh 
4314c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
4324c06356bSdh 	pwp->mpi_table_setup = 1;
4334c06356bSdh 	return (0);
4344c06356bSdh }
4354c06356bSdh 
4364c06356bSdh /*
4374c06356bSdh  * Start the Message Passing protocol with the PMC chip.
4384c06356bSdh  */
4394c06356bSdh int
4404c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp)
4414c06356bSdh {
4424c06356bSdh 	int i;
4434c06356bSdh 
4444c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
4454c06356bSdh 	for (i = 0; i < 1000; i++) {
4464c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4474c06356bSdh 		    PMCS_MSGU_IBDB_MPIINI) == 0) {
4484c06356bSdh 			break;
4494c06356bSdh 		}
4504c06356bSdh 		drv_usecwait(1000);
4514c06356bSdh 	}
4524c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
4534c06356bSdh 		return (-1);
4544c06356bSdh 	}
4554c06356bSdh 	drv_usecwait(500000);
4564c06356bSdh 
4574c06356bSdh 	/*
4584c06356bSdh 	 * Check to make sure we got to INIT state.
4594c06356bSdh 	 */
4604c06356bSdh 	if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
4614c06356bSdh 	    PMCS_MPI_STATE_INIT) {
462c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
463c3bc407cSdh 		    "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
4644c06356bSdh 		    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
4654c06356bSdh 		    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
4664c06356bSdh 		return (-1);
4674c06356bSdh 	}
4684c06356bSdh 	return (0);
4694c06356bSdh }
4704c06356bSdh 
4714c06356bSdh /*
4724c06356bSdh  * Stop the Message Passing protocol with the PMC chip.
4734c06356bSdh  */
4744c06356bSdh int
4754c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp)
4764c06356bSdh {
4774c06356bSdh 	int i;
4784c06356bSdh 
4794c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
4804c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
4814c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
4824c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
4834c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
4844c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
4854c06356bSdh 	}
4864c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
4874c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
4884c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
4894c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
4904c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
4914c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
4924c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
4934c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
4944c06356bSdh 	}
4954c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
4964c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
4974c06356bSdh 	for (i = 0; i < 2000; i++) {
4984c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4994c06356bSdh 		    PMCS_MSGU_IBDB_MPICTU) == 0) {
5004c06356bSdh 			break;
5014c06356bSdh 		}
5024c06356bSdh 		drv_usecwait(1000);
5034c06356bSdh 	}
5044c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
505c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
506c3bc407cSdh 		    "%s: MPI stop failed", __func__);
5074c06356bSdh 		return (-1);
5084c06356bSdh 	}
5094c06356bSdh 	return (0);
5104c06356bSdh }
5114c06356bSdh 
5124c06356bSdh /*
5134c06356bSdh  * Do a sequence of ECHO messages to test for MPI functionality,
5144c06356bSdh  * all inbound and outbound queue functionality and interrupts.
5154c06356bSdh  */
5164c06356bSdh int
5174c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp)
5184c06356bSdh {
5194c06356bSdh 	echo_test_t fred;
5204c06356bSdh 	struct pmcwork *pwrk;
5214c06356bSdh 	uint32_t *msg, count;
5224c06356bSdh 	int iqe = 0, iqo = 0, result, rval = 0;
5234c06356bSdh 	int iterations;
5244c06356bSdh 	hrtime_t echo_start, echo_end, echo_total;
5254c06356bSdh 
5264c06356bSdh 	ASSERT(pwp->max_cmd > 0);
5274c06356bSdh 
5284c06356bSdh 	/*
5294c06356bSdh 	 * We want iterations to be max_cmd * 3 to ensure that we run the
5304c06356bSdh 	 * echo test enough times to iterate through every inbound queue
5314c06356bSdh 	 * at least twice.
5324c06356bSdh 	 */
5334c06356bSdh 	iterations = pwp->max_cmd * 3;
5344c06356bSdh 
5354c06356bSdh 	echo_total = 0;
5364c06356bSdh 	count = 0;
5374c06356bSdh 
5384c06356bSdh 	while (count < iterations) {
5394c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
5404c06356bSdh 		if (pwrk == NULL) {
541c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
542c3bc407cSdh 			    pmcs_nowrk, __func__);
5434c06356bSdh 			rval = -1;
5444c06356bSdh 			break;
5454c06356bSdh 		}
5464c06356bSdh 
5474c06356bSdh 		mutex_enter(&pwp->iqp_lock[iqe]);
5484c06356bSdh 		msg = GET_IQ_ENTRY(pwp, iqe);
5494c06356bSdh 		if (msg == NULL) {
5504c06356bSdh 			mutex_exit(&pwp->iqp_lock[iqe]);
5514c06356bSdh 			pmcs_pwork(pwp, pwrk);
552c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
553c3bc407cSdh 			    pmcs_nomsg, __func__);
5544c06356bSdh 			rval = -1;
5554c06356bSdh 			break;
5564c06356bSdh 		}
5574c06356bSdh 
5584c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
5594c06356bSdh 
5604c06356bSdh 		if (iqe == PMCS_IQ_OTHER) {
5614c06356bSdh 			/* This is on the high priority queue */
5624c06356bSdh 			msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
5634c06356bSdh 		} else {
5644c06356bSdh 			msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
5654c06356bSdh 		}
5664c06356bSdh 		msg[1] = LE_32(pwrk->htag);
5674c06356bSdh 		fred.signature = 0xdeadbeef;
5684c06356bSdh 		fred.count = count;
5694c06356bSdh 		fred.ptr = &count;
5704c06356bSdh 		(void) memcpy(&msg[2], &fred, sizeof (fred));
5714c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5724c06356bSdh 
5734c06356bSdh 		INC_IQ_ENTRY(pwp, iqe);
5744c06356bSdh 
5754c06356bSdh 		echo_start = gethrtime();
5764c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__start,
5774c06356bSdh 		    hrtime_t, echo_start, uint32_t, pwrk->htag);
5784c06356bSdh 
5794c06356bSdh 		if (++iqe == PMCS_NIQ) {
5804c06356bSdh 			iqe = 0;
5814c06356bSdh 		}
5824c06356bSdh 		if (++iqo == PMCS_NOQ) {
5834c06356bSdh 			iqo = 0;
5844c06356bSdh 		}
5854c06356bSdh 
5864c06356bSdh 		WAIT_FOR(pwrk, 250, result);
587*3be32c0fSJesse Butler 		pmcs_pwork(pwp, pwrk);
5884c06356bSdh 
5894c06356bSdh 		echo_end = gethrtime();
5904c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__end,
5914c06356bSdh 		    hrtime_t, echo_end, int, result);
5924c06356bSdh 		echo_total += (echo_end - echo_start);
5934c06356bSdh 
5944c06356bSdh 		if (result) {
595c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5964c06356bSdh 			    "%s: command timed out on echo test #%d",
5974c06356bSdh 			    __func__, count);
5984c06356bSdh 			rval = -1;
5994c06356bSdh 			break;
6004c06356bSdh 		}
6014c06356bSdh 	}
6024c06356bSdh 
6034c06356bSdh 	/*
6044c06356bSdh 	 * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
6054c06356bSdh 	 * remove the overhead of things like the delay in getting signaled
6064c06356bSdh 	 * for completion.
6074c06356bSdh 	 */
6084c06356bSdh 	if (echo_total != 0) {
6094c06356bSdh 		pwp->io_intr_coal.intr_latency =
6104c06356bSdh 		    (echo_total / iterations) / 2;
6114c06356bSdh 		pwp->io_intr_coal.intr_threshold =
6124c06356bSdh 		    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
6134c06356bSdh 		    pwp->io_intr_coal.intr_latency);
6144c06356bSdh 	}
6154c06356bSdh 
6164c06356bSdh 	return (rval);
6174c06356bSdh }
6184c06356bSdh 
6194c06356bSdh /*
6204c06356bSdh  * Start the (real) phys
6214c06356bSdh  */
6224c06356bSdh int
6234c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
6244c06356bSdh {
6254c06356bSdh 	int result;
6264c06356bSdh 	uint32_t *msg;
6274c06356bSdh 	struct pmcwork *pwrk;
6284c06356bSdh 	pmcs_phy_t *pptr;
6294c06356bSdh 	sas_identify_af_t sap;
6304c06356bSdh 
6314c06356bSdh 	mutex_enter(&pwp->lock);
6324c06356bSdh 	pptr = pwp->root_phys + phynum;
6334c06356bSdh 	if (pptr == NULL) {
6344c06356bSdh 		mutex_exit(&pwp->lock);
635c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
636c3bc407cSdh 		    "%s: cannot find port %d", __func__, phynum);
6374c06356bSdh 		return (0);
6384c06356bSdh 	}
6394c06356bSdh 
6404c06356bSdh 	pmcs_lock_phy(pptr);
6414c06356bSdh 	mutex_exit(&pwp->lock);
6424c06356bSdh 
6434c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
6444c06356bSdh 	if (pwrk == NULL) {
6454c06356bSdh 		pmcs_unlock_phy(pptr);
646c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
6474c06356bSdh 		return (-1);
6484c06356bSdh 	}
6494c06356bSdh 
6504c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6514c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6524c06356bSdh 
6534c06356bSdh 	if (msg == NULL) {
6544c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6554c06356bSdh 		pmcs_unlock_phy(pptr);
6564c06356bSdh 		pmcs_pwork(pwp, pwrk);
657c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
6584c06356bSdh 		return (-1);
6594c06356bSdh 	}
6604c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
6614c06356bSdh 	msg[1] = LE_32(pwrk->htag);
6624c06356bSdh 	msg[2] = LE_32(linkmode | speed | phynum);
6634c06356bSdh 	bzero(&sap, sizeof (sap));
6644c06356bSdh 	sap.device_type = SAS_IF_DTYPE_ENDPOINT;
6654c06356bSdh 	sap.ssp_ini_port = 1;
6664c06356bSdh 
6674c06356bSdh 	if (pwp->separate_ports) {
6684c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
6694c06356bSdh 	} else {
6704c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
6714c06356bSdh 	}
6724c06356bSdh 
6734c06356bSdh 	ASSERT(phynum < SAS2_PHYNUM_MAX);
6744c06356bSdh 	sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
6754c06356bSdh 	(void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
6764c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6774c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6784c06356bSdh 
6794c06356bSdh 	pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
6804c06356bSdh 	pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
6814c06356bSdh 	pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
6824c06356bSdh 	pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
6834c06356bSdh 
6844c06356bSdh 	pmcs_unlock_phy(pptr);
6854c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
6864c06356bSdh 	pmcs_pwork(pwp, pwrk);
6874c06356bSdh 
6884c06356bSdh 	if (result) {
6896745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
6904c06356bSdh 	} else {
6914c06356bSdh 		mutex_enter(&pwp->lock);
6924c06356bSdh 		pwp->phys_started |= (1 << phynum);
6934c06356bSdh 		mutex_exit(&pwp->lock);
6944c06356bSdh 	}
6954c06356bSdh 
6964c06356bSdh 	return (0);
6974c06356bSdh }
6984c06356bSdh 
6994c06356bSdh int
7004c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp)
7014c06356bSdh {
7021f81b464SDavid Hollister 	int i, rval;
7034c06356bSdh 
7044c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
7054c06356bSdh 		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
7064c06356bSdh 			if (pmcs_start_phy(pwp, i,
7074c06356bSdh 			    (pwp->phymode << PHY_MODE_SHIFT),
7084c06356bSdh 			    pwp->physpeed << PHY_LINK_SHIFT)) {
7094c06356bSdh 				return (-1);
7104c06356bSdh 			}
7114c06356bSdh 			if (pmcs_clear_diag_counters(pwp, i)) {
712c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
713c3bc407cSdh 				    "%s: failed to reset counters on PHY (%d)",
714c3bc407cSdh 				    __func__, i);
7154c06356bSdh 			}
7164c06356bSdh 		}
7174c06356bSdh 	}
7181f81b464SDavid Hollister 
7191f81b464SDavid Hollister 	rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp);
7201f81b464SDavid Hollister 	if (rval) {
7211f81b464SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7221f81b464SDavid Hollister 		    "%s: Failed to obtain firmware timestamp", __func__);
7231f81b464SDavid Hollister 	} else {
7241f81b464SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7251f81b464SDavid Hollister 		    "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp);
7261f81b464SDavid Hollister 	}
7271f81b464SDavid Hollister 
7284c06356bSdh 	return (0);
7294c06356bSdh }
7304c06356bSdh 
7314c06356bSdh /*
7324c06356bSdh  * Called with PHY locked
7334c06356bSdh  */
7344c06356bSdh int
7354c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
7364c06356bSdh {
7374c06356bSdh 	uint32_t *msg;
7384c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
7394c06356bSdh 	const char *mbar;
7404c06356bSdh 	uint32_t amt;
7414c06356bSdh 	uint32_t pdevid;
7424c06356bSdh 	uint32_t stsoff;
7434c06356bSdh 	uint32_t status;
7444c06356bSdh 	int result, level, phynum;
7454c06356bSdh 	struct pmcwork *pwrk;
746*3be32c0fSJesse Butler 	pmcs_iport_t *iport;
7474c06356bSdh 	uint32_t htag;
7484c06356bSdh 
7494c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
7504c06356bSdh 
7514c06356bSdh 	bzero(iomb, PMCS_QENTRY_SIZE);
7524c06356bSdh 	phynum = pptr->phynum;
7534c06356bSdh 	level = pptr->level;
7544c06356bSdh 	if (level > 0) {
7554c06356bSdh 		pdevid = pptr->parent->device_id;
756601c90f1SSrikanth, Ramana 	} else if ((level == 0) && (pptr->dtype == EXPANDER)) {
757601c90f1SSrikanth, Ramana 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
758601c90f1SSrikanth, Ramana 		    "%s: Not resetting HBA PHY @ %s", __func__, pptr->path);
759601c90f1SSrikanth, Ramana 		return (0);
7604c06356bSdh 	}
7614c06356bSdh 
7629aed1621SDavid Hollister 	if (!pptr->iport || !pptr->valid_device_id) {
7639aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
7649aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
7659aed1621SDavid Hollister 		return (0);
7669aed1621SDavid Hollister 	}
7679aed1621SDavid Hollister 
7684c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
7694c06356bSdh 
7704c06356bSdh 	if (pwrk == NULL) {
771c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
7724c06356bSdh 		return (ENOMEM);
7734c06356bSdh 	}
7744c06356bSdh 
7754c06356bSdh 	pwrk->arg = iomb;
7764c06356bSdh 
7774c06356bSdh 	/*
7784c06356bSdh 	 * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
7794c06356bSdh 	 * function to do either a link reset or hard reset.  If level == 0,
7804c06356bSdh 	 * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
7814c06356bSdh 	 * root (local) PHY
7824c06356bSdh 	 */
7834c06356bSdh 	if (level) {
7844c06356bSdh 		stsoff = 2;
7854c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
7864c06356bSdh 		    PMCIN_SMP_REQUEST));
7874c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
7884c06356bSdh 		iomb[2] = LE_32(pdevid);
7894c06356bSdh 		iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
7904c06356bSdh 		/*
7914c06356bSdh 		 * Send SMP PHY CONTROL/HARD or LINK RESET
7924c06356bSdh 		 */
7934c06356bSdh 		iomb[4] = BE_32(0x40910000);
7944c06356bSdh 		iomb[5] = 0;
7954c06356bSdh 
7964c06356bSdh 		if (type == PMCS_PHYOP_HARD_RESET) {
7974c06356bSdh 			mbar = "SMP PHY CONTROL/HARD RESET";
798c80dec56SDavid Hollister 			iomb[6] = BE_32((phynum << 16) |
799c80dec56SDavid Hollister 			    (PMCS_PHYOP_HARD_RESET << 8));
8004c06356bSdh 		} else {
8014c06356bSdh 			mbar = "SMP PHY CONTROL/LINK RESET";
802c80dec56SDavid Hollister 			iomb[6] = BE_32((phynum << 16) |
803c80dec56SDavid Hollister 			    (PMCS_PHYOP_LINK_RESET << 8));
8044c06356bSdh 		}
805c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8064c06356bSdh 		    "%s: sending %s to %s for phy 0x%x",
8074c06356bSdh 		    __func__, mbar, pptr->parent->path, pptr->phynum);
8084c06356bSdh 		amt = 7;
8094c06356bSdh 	} else {
8104c06356bSdh 		/*
8114c06356bSdh 		 * Unlike most other Outbound messages, status for
8124c06356bSdh 		 * a local phy operation is in DWORD 3.
8134c06356bSdh 		 */
8144c06356bSdh 		stsoff = 3;
8154c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8164c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
8174c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
8184c06356bSdh 		if (type == PMCS_PHYOP_LINK_RESET) {
8194c06356bSdh 			mbar = "LOCAL PHY LINK RESET";
8204c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
8214c06356bSdh 		} else {
8224c06356bSdh 			mbar = "LOCAL PHY HARD RESET";
8234c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
8244c06356bSdh 		}
825c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8264c06356bSdh 		    "%s: sending %s to %s", __func__, mbar, pptr->path);
8274c06356bSdh 		amt = 3;
8284c06356bSdh 	}
8294c06356bSdh 
8304c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8314c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8324c06356bSdh 	if (msg == NULL) {
8334c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8344c06356bSdh 		pmcs_pwork(pwp, pwrk);
835c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8364c06356bSdh 		return (ENOMEM);
8374c06356bSdh 	}
8384c06356bSdh 	COPY_MESSAGE(msg, iomb, amt);
8394c06356bSdh 	htag = pwrk->htag;
8406745c559SJesse Butler 
841*3be32c0fSJesse Butler 	pmcs_hold_iport(pptr->iport);
842*3be32c0fSJesse Butler 	iport = pptr->iport;
843*3be32c0fSJesse Butler 	pmcs_smp_acquire(iport);
8444c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
8454c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8464c06356bSdh 	pmcs_unlock_phy(pptr);
8474c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
8484c06356bSdh 	pmcs_pwork(pwp, pwrk);
849*3be32c0fSJesse Butler 	pmcs_smp_release(iport);
850*3be32c0fSJesse Butler 	pmcs_rele_iport(iport);
851601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
8526745c559SJesse Butler 
8534c06356bSdh 	if (result) {
8546745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8554c06356bSdh 
8564c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
857c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8584c06356bSdh 			    "%s: Unable to issue SMP abort for htag 0x%08x",
8594c06356bSdh 			    __func__, htag);
8604c06356bSdh 		} else {
861c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8624c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
8634c06356bSdh 			    __func__, htag);
8644c06356bSdh 		}
8654c06356bSdh 		return (EIO);
8664c06356bSdh 	}
8674c06356bSdh 	status = LE_32(iomb[stsoff]);
8684c06356bSdh 
8694c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
8704c06356bSdh 		char buf[32];
8714c06356bSdh 		const char *es =  pmcs_status_str(status);
8724c06356bSdh 		if (es == NULL) {
8734c06356bSdh 			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
8744c06356bSdh 			    status);
8754c06356bSdh 			es = buf;
8764c06356bSdh 		}
877c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8784c06356bSdh 		    "%s: %s action returned %s for %s", __func__, mbar, es,
8794c06356bSdh 		    pptr->path);
880601c90f1SSrikanth, Ramana 		return (status);
8814c06356bSdh 	}
8824c06356bSdh 
8834c06356bSdh 	return (0);
8844c06356bSdh }
8854c06356bSdh 
8864c06356bSdh /*
8874c06356bSdh  * Stop the (real) phys.  No PHY or softstate locks are required as this only
8884c06356bSdh  * happens during detach.
8894c06356bSdh  */
8904c06356bSdh void
8914c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
8924c06356bSdh {
8934c06356bSdh 	int result;
8944c06356bSdh 	pmcs_phy_t *pptr;
8954c06356bSdh 	uint32_t *msg;
8964c06356bSdh 	struct pmcwork *pwrk;
8974c06356bSdh 
8984c06356bSdh 	pptr =  pwp->root_phys + phynum;
8994c06356bSdh 	if (pptr == NULL) {
900c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
9014c06356bSdh 		    "%s: unable to find port %d", __func__, phynum);
9024c06356bSdh 		return;
9034c06356bSdh 	}
9044c06356bSdh 
9054c06356bSdh 	if (pwp->phys_started & (1 << phynum)) {
9064c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
9074c06356bSdh 
9084c06356bSdh 		if (pwrk == NULL) {
909c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
910c3bc407cSdh 			    pmcs_nowrk, __func__);
9114c06356bSdh 			return;
9124c06356bSdh 		}
9134c06356bSdh 
9144c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9154c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9164c06356bSdh 
9174c06356bSdh 		if (msg == NULL) {
9184c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9194c06356bSdh 			pmcs_pwork(pwp, pwrk);
920c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
921c3bc407cSdh 			    pmcs_nomsg, __func__);
9224c06356bSdh 			return;
9234c06356bSdh 		}
9244c06356bSdh 
9254c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
9264c06356bSdh 		msg[1] = LE_32(pwrk->htag);
9274c06356bSdh 		msg[2] = LE_32(phynum);
9284c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
9294c06356bSdh 		/*
9304c06356bSdh 		 * Make this unconfigured now.
9314c06356bSdh 		 */
9324c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9334c06356bSdh 		WAIT_FOR(pwrk, 1000, result);
9344c06356bSdh 		pmcs_pwork(pwp, pwrk);
9354c06356bSdh 		if (result) {
9366745c559SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
937c3bc407cSdh 			    pptr, NULL, pmcs_timeo, __func__);
9384c06356bSdh 		}
9394c06356bSdh 
9404c06356bSdh 		pwp->phys_started &= ~(1 << phynum);
9414c06356bSdh 	}
9424c06356bSdh 
9434c06356bSdh 	pptr->configured = 0;
9444c06356bSdh }
9454c06356bSdh 
9464c06356bSdh /*
9474c06356bSdh  * No locks should be required as this is only called during detach
9484c06356bSdh  */
9494c06356bSdh void
9504c06356bSdh pmcs_stop_phys(pmcs_hw_t *pwp)
9514c06356bSdh {
9524c06356bSdh 	int i;
9534c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
9544c06356bSdh 		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
9554c06356bSdh 			pmcs_stop_phy(pwp, i);
9564c06356bSdh 		}
9574c06356bSdh 	}
9584c06356bSdh }
9594c06356bSdh 
9604c06356bSdh /*
9614c06356bSdh  * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed.
9624c06356bSdh  * 	ERR_CNT_RESET: return status of cmd
9634c06356bSdh  *	DIAG_REPORT_GET: return value of the counter
9644c06356bSdh  */
9654c06356bSdh int
9664c06356bSdh pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
9674c06356bSdh     uint8_t phynum)
9684c06356bSdh {
9694c06356bSdh 	uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
9704c06356bSdh 	int result;
9714c06356bSdh 	struct pmcwork *pwrk;
9724c06356bSdh 
9734c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
9744c06356bSdh 	if (pwrk == NULL) {
975c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
9764c06356bSdh 		return (DDI_FAILURE);
9774c06356bSdh 	}
9784c06356bSdh 	pwrk->arg = msg;
9794c06356bSdh 	htag = pwrk->htag;
9804c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
9814c06356bSdh 	msg[1] = LE_32(htag);
9824c06356bSdh 	msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) |
9834c06356bSdh 	    (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
9844c06356bSdh 
9854c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9864c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9874c06356bSdh 	if (ptr == NULL) {
9884c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9894c06356bSdh 		pmcs_pwork(pwp, pwrk);
990c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
9914c06356bSdh 		return (DDI_FAILURE);
9924c06356bSdh 	}
9934c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
9944c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
9954c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9964c06356bSdh 
9974c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
9984c06356bSdh 	pmcs_pwork(pwp, pwrk);
9994c06356bSdh 	if (result) {
10004c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
10014c06356bSdh 		return (DDI_FAILURE);
10024c06356bSdh 	}
10034c06356bSdh 
10044c06356bSdh 	status = LE_32(msg[3]);
10054c06356bSdh 
10064c06356bSdh 	/* Return for counter reset */
10074c06356bSdh 	if (cmd == PMCS_ERR_CNT_RESET)
10084c06356bSdh 		return (status);
10094c06356bSdh 
10104c06356bSdh 	/* Return for counter value */
10114c06356bSdh 	if (status) {
1012c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1013c3bc407cSdh 		    "%s: failed, status (0x%x)", __func__, status);
10144c06356bSdh 		return (DDI_FAILURE);
10154c06356bSdh 	}
10164c06356bSdh 	return (LE_32(msg[4]));
10174c06356bSdh }
10184c06356bSdh 
10194c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */
10204c06356bSdh int
10214c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
10224c06356bSdh {
10234c06356bSdh 	return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
10244c06356bSdh }
10254c06356bSdh 
10264c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */
10274c06356bSdh int
10284c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
10294c06356bSdh {
10304c06356bSdh 	uint32_t	cmd = PMCS_ERR_CNT_RESET;
10314c06356bSdh 	uint32_t	cmd_desc;
10324c06356bSdh 
10334c06356bSdh 	cmd_desc = PMCS_INVALID_DWORD_CNT;
10344c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10354c06356bSdh 		return (DDI_FAILURE);
10364c06356bSdh 
10374c06356bSdh 	cmd_desc = PMCS_DISPARITY_ERR_CNT;
10384c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10394c06356bSdh 		return (DDI_FAILURE);
10404c06356bSdh 
10414c06356bSdh 	cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
10424c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10434c06356bSdh 		return (DDI_FAILURE);
10444c06356bSdh 
10454c06356bSdh 	cmd_desc = PMCS_RESET_FAILED_CNT;
10464c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10474c06356bSdh 		return (DDI_FAILURE);
10484c06356bSdh 
10494c06356bSdh 	return (DDI_SUCCESS);
10504c06356bSdh }
10514c06356bSdh 
10524c06356bSdh /*
10534c06356bSdh  * Get firmware timestamp
10544c06356bSdh  */
10551f81b464SDavid Hollister static int
10561f81b464SDavid Hollister pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *fw_ts, hrtime_t *sys_hr_ts)
10574c06356bSdh {
10584c06356bSdh 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
10594c06356bSdh 	int result;
10604c06356bSdh 	struct pmcwork *pwrk;
10614c06356bSdh 
10624c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
10634c06356bSdh 	if (pwrk == NULL) {
1064c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
10654c06356bSdh 		return (-1);
10664c06356bSdh 	}
10674c06356bSdh 	pwrk->arg = msg;
10684c06356bSdh 	htag = pwrk->htag;
10694c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
10704c06356bSdh 	msg[1] = LE_32(pwrk->htag);
10714c06356bSdh 
10724c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10734c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10744c06356bSdh 	if (ptr == NULL) {
10754c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10764c06356bSdh 		pmcs_pwork(pwp, pwrk);
1077c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
10784c06356bSdh 		return (-1);
10794c06356bSdh 	}
10804c06356bSdh 	COPY_MESSAGE(ptr, msg, 2);
10814c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
10824c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10834c06356bSdh 
10844c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
10854c06356bSdh 	pmcs_pwork(pwp, pwrk);
10864c06356bSdh 	if (result) {
10874c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
10884c06356bSdh 		return (-1);
10894c06356bSdh 	}
10901f81b464SDavid Hollister 
10911f81b464SDavid Hollister 	mutex_enter(&pmcs_trace_lock);
10921f81b464SDavid Hollister 	*sys_hr_ts = gethrtime();
10931f81b464SDavid Hollister 	gethrestime(&pwp->sys_timestamp);
10941f81b464SDavid Hollister 	*fw_ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
10951f81b464SDavid Hollister 	mutex_exit(&pmcs_trace_lock);
10964c06356bSdh 	return (0);
10974c06356bSdh }
10984c06356bSdh 
10994c06356bSdh /*
11004c06356bSdh  * Dump all pertinent registers
11014c06356bSdh  */
11024c06356bSdh 
11034c06356bSdh void
11044c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp)
11054c06356bSdh {
11064c06356bSdh 	int i;
11074c06356bSdh 	uint32_t val;
11084c06356bSdh 
1109c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start",
11104c06356bSdh 	    ddi_get_instance(pwp->dip));
1111c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11124c06356bSdh 	    "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
11134c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
11144c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
11154c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
1116c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x",
11174c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
1118c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x",
11194c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
1120c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x",
11214c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
1122c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x",
11234c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
11244c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
1125c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u",
11264c06356bSdh 		    i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
11274c06356bSdh 	}
11284c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
1129c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u",
11304c06356bSdh 		    i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
11314c06356bSdh 	}
11324c06356bSdh 	val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
1133c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11344c06356bSdh 	    "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
11354c06356bSdh 	    val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
11364c06356bSdh 	    PMCS_HMI_ERR(val));
1137c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x",
11384c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
1139c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x",
11404c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
1141c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x",
11424c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
1143c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x",
11444c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
11454c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
11464c06356bSdh 		uint32_t rerrf, pinfo, started = 0, link = 0;
11474c06356bSdh 		pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
11484c06356bSdh 		if (pinfo & 1) {
11494c06356bSdh 			started = 1;
11504c06356bSdh 			link = pinfo & 2;
11514c06356bSdh 		}
11524c06356bSdh 		rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
1153c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11544c06356bSdh 		    "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
11554c06356bSdh 		    i, started, link, rerrf);
11564c06356bSdh 	}
1157c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end",
11584c06356bSdh 	    ddi_get_instance(pwp->dip));
11594c06356bSdh }
11604c06356bSdh 
11614c06356bSdh /*
11624c06356bSdh  * Handle SATA Abort and other error processing
11634c06356bSdh  */
11644c06356bSdh int
11654c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp)
11664c06356bSdh {
11674c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
1168601c90f1SSrikanth, Ramana 	pmcs_xscsi_t *tgt;
11694c06356bSdh 	int r, level = 0;
11704c06356bSdh 
1171c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__);
11724c06356bSdh 
11734c06356bSdh 	mutex_enter(&pwp->lock);
11744c06356bSdh 	pptr = pwp->root_phys;
11754c06356bSdh 	mutex_exit(&pwp->lock);
11764c06356bSdh 
11774c06356bSdh 	while (pptr) {
11784c06356bSdh 		/*
11794c06356bSdh 		 * XXX: Need to make sure this doesn't happen
11804c06356bSdh 		 * XXX: when non-NCQ commands are running.
11814c06356bSdh 		 */
11824c06356bSdh 		pmcs_lock_phy(pptr);
11834c06356bSdh 		if (pptr->need_rl_ext) {
11844c06356bSdh 			ASSERT(pptr->dtype == SATA);
11854c06356bSdh 			if (pmcs_acquire_scratch(pwp, B_FALSE)) {
11864c06356bSdh 				goto next_phy;
11874c06356bSdh 			}
11884c06356bSdh 			r = pmcs_sata_abort_ncq(pwp, pptr);
11894c06356bSdh 			pmcs_release_scratch(pwp);
11904c06356bSdh 			if (r == ENOMEM) {
11914c06356bSdh 				goto next_phy;
11924c06356bSdh 			}
11934c06356bSdh 			if (r) {
11944c06356bSdh 				r = pmcs_reset_phy(pwp, pptr,
11954c06356bSdh 				    PMCS_PHYOP_LINK_RESET);
11964c06356bSdh 				if (r == ENOMEM) {
11974c06356bSdh 					goto next_phy;
11984c06356bSdh 				}
11994c06356bSdh 				/* what if other failures happened? */
12004c06356bSdh 				pptr->abort_pending = 1;
12014c06356bSdh 				pptr->abort_sent = 0;
12024c06356bSdh 			}
12034c06356bSdh 		}
12044c06356bSdh 		if (pptr->abort_pending == 0 || pptr->abort_sent) {
12054c06356bSdh 			goto next_phy;
12064c06356bSdh 		}
12074c06356bSdh 		pptr->abort_pending = 0;
12084c06356bSdh 		if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
12094c06356bSdh 			pptr->abort_pending = 1;
12104c06356bSdh 			goto next_phy;
12114c06356bSdh 		}
12124c06356bSdh 		pptr->abort_sent = 1;
12134c06356bSdh 
1214601c90f1SSrikanth, Ramana 		/*
1215601c90f1SSrikanth, Ramana 		 * If the iport is no longer active, flush the queues
1216601c90f1SSrikanth, Ramana 		 */
1217601c90f1SSrikanth, Ramana 		if ((pptr->iport == NULL) ||
1218601c90f1SSrikanth, Ramana 		    (pptr->iport->ua_state != UA_ACTIVE)) {
1219601c90f1SSrikanth, Ramana 			tgt = pptr->target;
1220*3be32c0fSJesse Butler 			if (tgt != NULL) {
1221601c90f1SSrikanth, Ramana 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
1222601c90f1SSrikanth, Ramana 				    "%s: Clearing target 0x%p, inactive iport",
1223601c90f1SSrikanth, Ramana 				    __func__, (void *) tgt);
1224601c90f1SSrikanth, Ramana 				mutex_enter(&tgt->statlock);
1225601c90f1SSrikanth, Ramana 				pmcs_clear_xp(pwp, tgt);
1226601c90f1SSrikanth, Ramana 				mutex_exit(&tgt->statlock);
1227601c90f1SSrikanth, Ramana 			}
1228601c90f1SSrikanth, Ramana 		}
1229601c90f1SSrikanth, Ramana 
12304c06356bSdh next_phy:
12314c06356bSdh 		if (pptr->children) {
12324c06356bSdh 			pnext = pptr->children;
12334c06356bSdh 			pnext_uplevel[level++] = pptr->sibling;
12344c06356bSdh 		} else {
12354c06356bSdh 			pnext = pptr->sibling;
12364c06356bSdh 			while ((pnext == NULL) && (level > 0)) {
12374c06356bSdh 				pnext = pnext_uplevel[--level];
12384c06356bSdh 			}
12394c06356bSdh 		}
12404c06356bSdh 
12414c06356bSdh 		pmcs_unlock_phy(pptr);
12424c06356bSdh 		pptr = pnext;
12434c06356bSdh 	}
12444c06356bSdh 
12454c06356bSdh 	return (0);
12464c06356bSdh }
12474c06356bSdh 
12484c06356bSdh /*
12494c06356bSdh  * Register a device (get a device handle for it).
12504c06356bSdh  * Called with PHY lock held.
12514c06356bSdh  */
12524c06356bSdh int
12534c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
12544c06356bSdh {
12554c06356bSdh 	struct pmcwork *pwrk;
12564c06356bSdh 	int result = 0;
12574c06356bSdh 	uint32_t *msg;
12584c06356bSdh 	uint32_t tmp, status;
12594c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
12604c06356bSdh 
12614c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
12624c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
12634c06356bSdh 
12644c06356bSdh 	if (msg == NULL ||
12654c06356bSdh 	    (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
12664c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
12674c06356bSdh 		result = ENOMEM;
12684c06356bSdh 		goto out;
12694c06356bSdh 	}
12704c06356bSdh 
12714c06356bSdh 	pwrk->arg = iomb;
12724c06356bSdh 	pwrk->dtype = pptr->dtype;
12734c06356bSdh 
12744c06356bSdh 	msg[1] = LE_32(pwrk->htag);
12754c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
12764c06356bSdh 	tmp = PMCS_DEVREG_TLR |
12774c06356bSdh 	    (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
12784c06356bSdh 	if (IS_ROOT_PHY(pptr)) {
12794c06356bSdh 		msg[2] = LE_32(pptr->portid |
12804c06356bSdh 		    (pptr->phynum << PMCS_PHYID_SHIFT));
12814c06356bSdh 	} else {
12824c06356bSdh 		msg[2] = LE_32(pptr->portid);
12834c06356bSdh 	}
12844c06356bSdh 	if (pptr->dtype == SATA) {
12854c06356bSdh 		if (IS_ROOT_PHY(pptr)) {
12864c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
12874c06356bSdh 		} else {
12884c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA;
12894c06356bSdh 		}
12904c06356bSdh 	} else {
12914c06356bSdh 		tmp |= PMCS_DEVREG_TYPE_SAS;
12924c06356bSdh 	}
12934c06356bSdh 	msg[3] = LE_32(tmp);
12944c06356bSdh 	msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
12954c06356bSdh 	(void) memcpy(&msg[5], pptr->sas_address, 8);
12964c06356bSdh 
12974c06356bSdh 	CLEAN_MESSAGE(msg, 7);
12984c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
12994c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13004c06356bSdh 
13014c06356bSdh 	pmcs_unlock_phy(pptr);
13024c06356bSdh 	WAIT_FOR(pwrk, 250, result);
13034c06356bSdh 	pmcs_pwork(pwp, pwrk);
1304*3be32c0fSJesse Butler 	pmcs_lock_phy(pptr);
13054c06356bSdh 
13064c06356bSdh 	if (result) {
13076745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
13084c06356bSdh 		result = ETIMEDOUT;
13094c06356bSdh 		goto out;
13104c06356bSdh 	}
13114c06356bSdh 	status = LE_32(iomb[2]);
13124c06356bSdh 	tmp = LE_32(iomb[3]);
13134c06356bSdh 	switch (status) {
13144c06356bSdh 	case PMCS_DEVREG_OK:
13154c06356bSdh 	case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
13164c06356bSdh 	case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
13174c06356bSdh 		if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
13184c06356bSdh 			result = EEXIST;
13194c06356bSdh 			goto out;
13204c06356bSdh 		} else if (status != PMCS_DEVREG_OK) {
13214c06356bSdh 			if (tmp == 0xffffffff) {	/* F/W bug */
1322c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
13234c06356bSdh 				    "%s: phy %s already has bogus devid 0x%x",
13244c06356bSdh 				    __func__, pptr->path, tmp);
13254c06356bSdh 				result = EIO;
13264c06356bSdh 				goto out;
13274c06356bSdh 			} else {
1328c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
13294c06356bSdh 				    "%s: phy %s already has a device id 0x%x",
13304c06356bSdh 				    __func__, pptr->path, tmp);
13314c06356bSdh 			}
13324c06356bSdh 		}
13334c06356bSdh 		break;
13344c06356bSdh 	default:
1335c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1336c3bc407cSdh 		    "%s: status 0x%x when trying to register device %s",
1337c3bc407cSdh 		    __func__, status, pptr->path);
13384c06356bSdh 		result = EIO;
13394c06356bSdh 		goto out;
13404c06356bSdh 	}
13414c06356bSdh 	pptr->device_id = tmp;
13424c06356bSdh 	pptr->valid_device_id = 1;
1343c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT
13444c06356bSdh 	    " registered with device_id 0x%x (portid %d)", pptr->path,
13454c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
13464c06356bSdh out:
13474c06356bSdh 	return (result);
13484c06356bSdh }
13494c06356bSdh 
13504c06356bSdh /*
13514c06356bSdh  * Deregister a device (remove a device handle).
13524c06356bSdh  * Called with PHY locked.
13534c06356bSdh  */
13544c06356bSdh void
13554c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
13564c06356bSdh {
13574c06356bSdh 	struct pmcwork *pwrk;
13584c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
13594c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
13604c06356bSdh 	int result;
13614c06356bSdh 
13624c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
13634c06356bSdh 	if (pwrk == NULL) {
13644c06356bSdh 		return;
13654c06356bSdh 	}
13664c06356bSdh 
13674c06356bSdh 	pwrk->arg = iomb;
13684c06356bSdh 	pwrk->dtype = pptr->dtype;
13694c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
13704c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13714c06356bSdh 	if (ptr == NULL) {
13724c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
13734c06356bSdh 		pmcs_pwork(pwp, pwrk);
13744c06356bSdh 		return;
13754c06356bSdh 	}
13764c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
13774c06356bSdh 	    PMCIN_DEREGISTER_DEVICE_HANDLE));
13784c06356bSdh 	msg[1] = LE_32(pwrk->htag);
13794c06356bSdh 	msg[2] = LE_32(pptr->device_id);
13804c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
13814c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
13824c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13834c06356bSdh 
13844c06356bSdh 	pmcs_unlock_phy(pptr);
13854c06356bSdh 	WAIT_FOR(pwrk, 250, result);
13864c06356bSdh 	pmcs_pwork(pwp, pwrk);
13874c06356bSdh 	pmcs_lock_phy(pptr);
13884c06356bSdh 
13894c06356bSdh 	if (result) {
13906745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
13914c06356bSdh 		return;
13924c06356bSdh 	}
13934c06356bSdh 	status = LE_32(iomb[2]);
13944c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
1395c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1396c3bc407cSdh 		    "%s: status 0x%x when trying to deregister device %s",
1397c3bc407cSdh 		    __func__, status, pptr->path);
13984c06356bSdh 	} else {
1399c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1400c3bc407cSdh 		    "%s: device %s deregistered", __func__, pptr->path);
14014c06356bSdh 	}
1402*3be32c0fSJesse Butler 
1403*3be32c0fSJesse Butler 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
1404*3be32c0fSJesse Butler 	pptr->configured = 0;
1405*3be32c0fSJesse Butler 	pptr->deregister_wait = 0;
1406*3be32c0fSJesse Butler 	pptr->valid_device_id = 0;
14074c06356bSdh }
14084c06356bSdh 
14094c06356bSdh /*
14104c06356bSdh  * Deregister all registered devices.
14114c06356bSdh  */
14124c06356bSdh void
14134c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
14144c06356bSdh {
14154c06356bSdh 	/*
14164c06356bSdh 	 * Start at the maximum level and walk back to level 0.  This only
14174c06356bSdh 	 * gets done during detach after all threads and timers have been
14181b94a41bSChris Horne 	 * destroyed.
14194c06356bSdh 	 */
14204c06356bSdh 	while (phyp) {
14214c06356bSdh 		if (phyp->children) {
14224c06356bSdh 			pmcs_deregister_devices(pwp, phyp->children);
14234c06356bSdh 		}
14241b94a41bSChris Horne 		pmcs_lock_phy(phyp);
14254c06356bSdh 		if (phyp->valid_device_id) {
14264c06356bSdh 			pmcs_deregister_device(pwp, phyp);
14274c06356bSdh 		}
14281b94a41bSChris Horne 		pmcs_unlock_phy(phyp);
14294c06356bSdh 		phyp = phyp->sibling;
14304c06356bSdh 	}
14314c06356bSdh }
14324c06356bSdh 
14334c06356bSdh /*
14344c06356bSdh  * Perform a 'soft' reset on the PMC chip
14354c06356bSdh  */
14364c06356bSdh int
14374c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
14384c06356bSdh {
14394c06356bSdh 	uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
14404c06356bSdh 	pmcs_phy_t *pptr;
14414c06356bSdh 	char *msg = NULL;
14424c06356bSdh 	int i;
14434c06356bSdh 
14444c06356bSdh 	/*
14454c06356bSdh 	 * Disable interrupts
14464c06356bSdh 	 */
14474c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
14484c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
14494c06356bSdh 
1450c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__);
14514c06356bSdh 
14524c06356bSdh 	if (pwp->locks_initted) {
14534c06356bSdh 		mutex_enter(&pwp->lock);
14544c06356bSdh 	}
14554c06356bSdh 	pwp->blocked = 1;
14564c06356bSdh 
14575c45adf0SJesse Butler 	/*
14585c45adf0SJesse Butler 	 * Clear our softstate copies of the MSGU and IOP heartbeats.
14595c45adf0SJesse Butler 	 */
14605c45adf0SJesse Butler 	pwp->last_msgu_tick = pwp->last_iop_tick = 0;
14615c45adf0SJesse Butler 
14624c06356bSdh 	/*
14634c06356bSdh 	 * Step 1
14644c06356bSdh 	 */
14654c06356bSdh 	s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
14664c06356bSdh 	if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
14674c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
14684c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
14694c06356bSdh 		for (i = 0; i < 100; i++) {
14704c06356bSdh 			s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14714c06356bSdh 			    PMCS_MSGU_HOST_SOFT_RESET_READY;
14724c06356bSdh 			if (s2) {
14734c06356bSdh 				break;
14744c06356bSdh 			}
14754c06356bSdh 			drv_usecwait(10000);
14764c06356bSdh 		}
14774c06356bSdh 		s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14784c06356bSdh 		    PMCS_MSGU_HOST_SOFT_RESET_READY;
14794c06356bSdh 		if (s2 == 0) {
1480c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1481c3bc407cSdh 			    "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came "
1482c3bc407cSdh 			    "ready", __func__);
14834c06356bSdh 			pmcs_register_dump(pwp);
14844c06356bSdh 			if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
14854c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
14864c06356bSdh 			    (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14874c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
14884c06356bSdh 				pwp->state = STATE_DEAD;
14894c06356bSdh 				pwp->blocked = 0;
14904c06356bSdh 				if (pwp->locks_initted) {
14914c06356bSdh 					mutex_exit(&pwp->lock);
14924c06356bSdh 				}
14934c06356bSdh 				return (-1);
14944c06356bSdh 			}
14954c06356bSdh 		}
14964c06356bSdh 	}
14974c06356bSdh 
14984c06356bSdh 	/*
14994c06356bSdh 	 * Step 2
15004c06356bSdh 	 */
15014c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
15024c06356bSdh 	drv_usecwait(10);
15034c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
15044c06356bSdh 	drv_usecwait(10);
15054c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
15064c06356bSdh 	drv_usecwait(10);
15074c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
15084c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
15094c06356bSdh 	drv_usecwait(10);
15104c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
15114c06356bSdh 	drv_usecwait(10);
15124c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
15134c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
15144c06356bSdh 	drv_usecwait(10);
15154c06356bSdh 
15164c06356bSdh 	sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
15174c06356bSdh 	    PMCS_MSGU_AAP_SFR_PROGRESS;
15184c06356bSdh 	sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
1519c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 "
1520c3bc407cSdh 	    "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0),
1521c3bc407cSdh 	    HST_SFT_RESET_SIG);
15224c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
15234c06356bSdh 
15244c06356bSdh 	/*
15254c06356bSdh 	 * Step 3
15264c06356bSdh 	 */
15272ac4abe8SDavid Hollister 	gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
1528c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
15294c06356bSdh 	    gsm & ~PMCS_SOFT_RESET_BITS);
15304c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
15314c06356bSdh 
15324c06356bSdh 	/*
15334c06356bSdh 	 * Step 4
15344c06356bSdh 	 */
15352ac4abe8SDavid Hollister 	rapchk = pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN);
1536c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
1537c3bc407cSdh 	    "%08x -> %08x", rapchk, 0);
15384c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
15392ac4abe8SDavid Hollister 	wapchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN);
1540c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
1541c3bc407cSdh 	    "%08x -> %08x", wapchk, 0);
15424c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
15432ac4abe8SDavid Hollister 	wdpchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN);
1544c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
1545c3bc407cSdh 	    "%08x -> %08x", wdpchk, 0);
15464c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
15474c06356bSdh 
15484c06356bSdh 	/*
15494c06356bSdh 	 * Step 5
15504c06356bSdh 	 */
15514c06356bSdh 	drv_usecwait(100);
15524c06356bSdh 
15534c06356bSdh 	/*
15544c06356bSdh 	 * Step 5.5 (Temporary workaround for 1.07.xx Beta)
15554c06356bSdh 	 */
15562ac4abe8SDavid Hollister 	tsmode = pmcs_rd_gsm_reg(pwp, 0, PMCS_GPIO_TRISTATE_MODE_ADDR);
1557c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x",
1558c3bc407cSdh 	    tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
15594c06356bSdh 	pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
15604c06356bSdh 	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
15614c06356bSdh 	drv_usecwait(10);
15624c06356bSdh 
15634c06356bSdh 	/*
15644c06356bSdh 	 * Step 6
15654c06356bSdh 	 */
15664c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1567c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1568c3bc407cSdh 	    spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
15694c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
15704c06356bSdh 	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
15714c06356bSdh 	drv_usecwait(10);
15724c06356bSdh 
15734c06356bSdh 	/*
15744c06356bSdh 	 * Step 7
15754c06356bSdh 	 */
15764c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1577c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1578c3bc407cSdh 	    spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
15794c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
15804c06356bSdh 
15814c06356bSdh 	/*
15824c06356bSdh 	 * Step 8
15834c06356bSdh 	 */
15844c06356bSdh 	drv_usecwait(100);
15854c06356bSdh 
15864c06356bSdh 	/*
15874c06356bSdh 	 * Step 9
15884c06356bSdh 	 */
15894c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1590c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1591c3bc407cSdh 	    spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
15924c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
15934c06356bSdh 
15944c06356bSdh 	/*
15954c06356bSdh 	 * Step 10
15964c06356bSdh 	 */
15974c06356bSdh 	drv_usecwait(100);
15984c06356bSdh 
15994c06356bSdh 	/*
16004c06356bSdh 	 * Step 11
16014c06356bSdh 	 */
16022ac4abe8SDavid Hollister 	gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
1603c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
16044c06356bSdh 	    gsm | PMCS_SOFT_RESET_BITS);
16054c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
16064c06356bSdh 	drv_usecwait(10);
16074c06356bSdh 
16084c06356bSdh 	/*
16094c06356bSdh 	 * Step 12
16104c06356bSdh 	 */
1611c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
16122ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN),
1613c3bc407cSdh 	    rapchk);
16144c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
16154c06356bSdh 	drv_usecwait(10);
1616c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
16172ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN),
1618c3bc407cSdh 	    wapchk);
16194c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
16204c06356bSdh 	drv_usecwait(10);
1621c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
16222ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN),
1623c3bc407cSdh 	    wapchk);
16244c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
16254c06356bSdh 	drv_usecwait(10);
16264c06356bSdh 
16274c06356bSdh 	/*
16284c06356bSdh 	 * Step 13
16294c06356bSdh 	 */
16304c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1631c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1632c3bc407cSdh 	    spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
16334c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
16344c06356bSdh 	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
16354c06356bSdh 
16364c06356bSdh 	/*
16374c06356bSdh 	 * Step 14
16384c06356bSdh 	 */
16394c06356bSdh 	drv_usecwait(100);
16404c06356bSdh 
16414c06356bSdh 	/*
16424c06356bSdh 	 * Step 15
16434c06356bSdh 	 */
16444c06356bSdh 	for (spc = 0, i = 0; i < 1000; i++) {
16454c06356bSdh 		drv_usecwait(1000);
16464c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
16474c06356bSdh 		if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
16484c06356bSdh 			break;
16494c06356bSdh 		}
16504c06356bSdh 	}
16514c06356bSdh 
16524c06356bSdh 	if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
1653c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16544c06356bSdh 		    "SFR didn't toggle (sfr 0x%x)", spc);
16554c06356bSdh 		pwp->state = STATE_DEAD;
16564c06356bSdh 		pwp->blocked = 0;
16574c06356bSdh 		if (pwp->locks_initted) {
16584c06356bSdh 			mutex_exit(&pwp->lock);
16594c06356bSdh 		}
16604c06356bSdh 		return (-1);
16614c06356bSdh 	}
16624c06356bSdh 
16634c06356bSdh 	/*
16644c06356bSdh 	 * Step 16
16654c06356bSdh 	 */
16664c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
16674c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
16684c06356bSdh 
16694c06356bSdh 	/*
16704c06356bSdh 	 * Wait for up to 5 seconds for AAP state to come either ready or error.
16714c06356bSdh 	 */
16724c06356bSdh 	for (i = 0; i < 50; i++) {
16734c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
16744c06356bSdh 		    PMCS_MSGU_AAP_STATE_MASK;
16754c06356bSdh 		if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
16764c06356bSdh 		    spc == PMCS_MSGU_AAP_STATE_READY) {
16774c06356bSdh 			break;
16784c06356bSdh 		}
16794c06356bSdh 		drv_usecwait(100000);
16804c06356bSdh 	}
16814c06356bSdh 	spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
16824c06356bSdh 	if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
1683c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16844c06356bSdh 		    "soft reset failed (state 0x%x)", spc);
16854c06356bSdh 		pwp->state = STATE_DEAD;
16864c06356bSdh 		pwp->blocked = 0;
16874c06356bSdh 		if (pwp->locks_initted) {
16884c06356bSdh 			mutex_exit(&pwp->lock);
16894c06356bSdh 		}
16904c06356bSdh 		return (-1);
16914c06356bSdh 	}
16924c06356bSdh 
16935c45adf0SJesse Butler 	/* Clear the firmware log */
16945c45adf0SJesse Butler 	if (pwp->fwlogp) {
16955c45adf0SJesse Butler 		bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
16965c45adf0SJesse Butler 	}
16975c45adf0SJesse Butler 
16985c45adf0SJesse Butler 	/* Reset our queue indices and entries */
16995c45adf0SJesse Butler 	bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
17005c45adf0SJesse Butler 	bzero(pwp->last_iqci, sizeof (pwp->last_iqci));
1701d78a6b7eSJesse Butler 	bzero(pwp->last_htag, sizeof (pwp->last_htag));
17025c45adf0SJesse Butler 	for (i = 0; i < PMCS_NIQ; i++) {
17035c45adf0SJesse Butler 		if (pwp->iqp[i]) {
17045c45adf0SJesse Butler 			bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
17055c45adf0SJesse Butler 			pmcs_wr_iqpi(pwp, i, 0);
17065c45adf0SJesse Butler 			pmcs_wr_iqci(pwp, i, 0);
17075c45adf0SJesse Butler 		}
17085c45adf0SJesse Butler 	}
17095c45adf0SJesse Butler 	for (i = 0; i < PMCS_NOQ; i++) {
17105c45adf0SJesse Butler 		if (pwp->oqp[i]) {
17115c45adf0SJesse Butler 			bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
17125c45adf0SJesse Butler 			pmcs_wr_oqpi(pwp, i, 0);
17135c45adf0SJesse Butler 			pmcs_wr_oqci(pwp, i, 0);
17145c45adf0SJesse Butler 		}
17155c45adf0SJesse Butler 
17165c45adf0SJesse Butler 	}
17174c06356bSdh 
17184c06356bSdh 	if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
17194c06356bSdh 	    pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
17204c06356bSdh 		pwp->blocked = 0;
17214c06356bSdh 		if (pwp->locks_initted) {
17224c06356bSdh 			mutex_exit(&pwp->lock);
17234c06356bSdh 		}
17244c06356bSdh 		return (0);
17254c06356bSdh 	}
17264c06356bSdh 
17274c06356bSdh 	/*
17284c06356bSdh 	 * Return at this point if we dont need to startup.
17294c06356bSdh 	 */
17304c06356bSdh 	if (no_restart) {
17314c06356bSdh 		return (0);
17324c06356bSdh 	}
17334c06356bSdh 
17344c06356bSdh 	ASSERT(pwp->locks_initted != 0);
17354c06356bSdh 
17364c06356bSdh 	/*
17375c45adf0SJesse Butler 	 * Flush the target queues and clear each target's PHY
17384c06356bSdh 	 */
17394c06356bSdh 	if (pwp->targets) {
17404c06356bSdh 		for (i = 0; i < pwp->max_dev; i++) {
17414c06356bSdh 			pmcs_xscsi_t *xp = pwp->targets[i];
17424c06356bSdh 
17434c06356bSdh 			if (xp == NULL) {
17444c06356bSdh 				continue;
17454c06356bSdh 			}
17465c45adf0SJesse Butler 
17474c06356bSdh 			mutex_enter(&xp->statlock);
17485c45adf0SJesse Butler 			pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
17495c45adf0SJesse Butler 			xp->phy = NULL;
17504c06356bSdh 			mutex_exit(&xp->statlock);
17514c06356bSdh 		}
17524c06356bSdh 	}
17534c06356bSdh 
17544c06356bSdh 	/*
17555c45adf0SJesse Butler 	 * Zero out the ports list, free non root phys, clear root phys
17564c06356bSdh 	 */
17575c45adf0SJesse Butler 	bzero(pwp->ports, sizeof (pwp->ports));
17585c45adf0SJesse Butler 	pmcs_free_all_phys(pwp, pwp->root_phys);
17595c45adf0SJesse Butler 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
17605c45adf0SJesse Butler 		pmcs_lock_phy(pptr);
17615c45adf0SJesse Butler 		pmcs_clear_phy(pwp, pptr);
17625c45adf0SJesse Butler 		pptr->target = NULL;
17635c45adf0SJesse Butler 		pmcs_unlock_phy(pptr);
17644c06356bSdh 	}
17654c06356bSdh 
17664c06356bSdh 	/*
17674c06356bSdh 	 * Restore Interrupt Mask
17684c06356bSdh 	 */
17694c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
17704c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
17714c06356bSdh 
17724c06356bSdh 	pwp->mpi_table_setup = 0;
17734c06356bSdh 	mutex_exit(&pwp->lock);
17744c06356bSdh 
17754c06356bSdh 	/*
17764c06356bSdh 	 * Set up MPI again.
17774c06356bSdh 	 */
17784c06356bSdh 	if (pmcs_setup(pwp)) {
17794c06356bSdh 		msg = "unable to setup MPI tables again";
17804c06356bSdh 		goto fail_restart;
17814c06356bSdh 	}
17824c06356bSdh 	pmcs_report_fwversion(pwp);
17834c06356bSdh 
17844c06356bSdh 	/*
17854c06356bSdh 	 * Restart MPI
17864c06356bSdh 	 */
17874c06356bSdh 	if (pmcs_start_mpi(pwp)) {
17884c06356bSdh 		msg = "unable to restart MPI again";
17894c06356bSdh 		goto fail_restart;
17904c06356bSdh 	}
17914c06356bSdh 
17924c06356bSdh 	mutex_enter(&pwp->lock);
17934c06356bSdh 	SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
17944c06356bSdh 	mutex_exit(&pwp->lock);
17954c06356bSdh 
17964c06356bSdh 	/*
17974c06356bSdh 	 * Run any completions
17984c06356bSdh 	 */
17994c06356bSdh 	PMCS_CQ_RUN(pwp);
18004c06356bSdh 
18014c06356bSdh 	/*
18024c06356bSdh 	 * Delay
18034c06356bSdh 	 */
18044c06356bSdh 	drv_usecwait(1000000);
18054c06356bSdh 	return (0);
18064c06356bSdh 
18074c06356bSdh fail_restart:
18084c06356bSdh 	mutex_enter(&pwp->lock);
18094c06356bSdh 	pwp->state = STATE_DEAD;
18104c06356bSdh 	mutex_exit(&pwp->lock);
1811c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
1812c3bc407cSdh 	    "%s: Failed: %s", __func__, msg);
18134c06356bSdh 	return (-1);
18144c06356bSdh }
18154c06356bSdh 
18165c45adf0SJesse Butler 
18175c45adf0SJesse Butler /*
18185c45adf0SJesse Butler  * Perform a 'hot' reset, which will soft reset the chip and
18195c45adf0SJesse Butler  * restore the state back to pre-reset context. Called with pwp
18205c45adf0SJesse Butler  * lock held.
18215c45adf0SJesse Butler  */
18225c45adf0SJesse Butler int
18235c45adf0SJesse Butler pmcs_hot_reset(pmcs_hw_t *pwp)
18245c45adf0SJesse Butler {
18255c45adf0SJesse Butler 	pmcs_iport_t	*iport;
18265c45adf0SJesse Butler 
18275c45adf0SJesse Butler 	ASSERT(mutex_owned(&pwp->lock));
18285c45adf0SJesse Butler 	pwp->state = STATE_IN_RESET;
18295c45adf0SJesse Butler 
18305c45adf0SJesse Butler 	/*
18315c45adf0SJesse Butler 	 * For any iports on this HBA, report empty target sets and
18325c45adf0SJesse Butler 	 * then tear them down.
18335c45adf0SJesse Butler 	 */
18345c45adf0SJesse Butler 	rw_enter(&pwp->iports_lock, RW_READER);
18355c45adf0SJesse Butler 	for (iport = list_head(&pwp->iports); iport != NULL;
18365c45adf0SJesse Butler 	    iport = list_next(&pwp->iports, iport)) {
18375c45adf0SJesse Butler 		mutex_enter(&iport->lock);
18385c45adf0SJesse Butler 		(void) scsi_hba_tgtmap_set_begin(iport->iss_tgtmap);
18395c45adf0SJesse Butler 		(void) scsi_hba_tgtmap_set_end(iport->iss_tgtmap, 0);
18405c45adf0SJesse Butler 		pmcs_iport_teardown_phys(iport);
18415c45adf0SJesse Butler 		mutex_exit(&iport->lock);
18425c45adf0SJesse Butler 	}
18435c45adf0SJesse Butler 	rw_exit(&pwp->iports_lock);
18445c45adf0SJesse Butler 
18455c45adf0SJesse Butler 	/* Grab a register dump, in the event that reset fails */
18465c45adf0SJesse Butler 	pmcs_register_dump_int(pwp);
18475c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
18485c45adf0SJesse Butler 
184932b54db7SJesse Butler 	/* Ensure discovery is not running before we proceed */
185032b54db7SJesse Butler 	mutex_enter(&pwp->config_lock);
185132b54db7SJesse Butler 	while (pwp->configuring) {
185232b54db7SJesse Butler 		cv_wait(&pwp->config_cv, &pwp->config_lock);
185332b54db7SJesse Butler 	}
185432b54db7SJesse Butler 	mutex_exit(&pwp->config_lock);
185532b54db7SJesse Butler 
18565c45adf0SJesse Butler 	/* Issue soft reset and clean up related softstate */
18575c45adf0SJesse Butler 	if (pmcs_soft_reset(pwp, B_FALSE)) {
18585c45adf0SJesse Butler 		/*
18595c45adf0SJesse Butler 		 * Disable interrupts, in case we got far enough along to
18605c45adf0SJesse Butler 		 * enable them, then fire off ereport and service impact.
18615c45adf0SJesse Butler 		 */
18625c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18635c45adf0SJesse Butler 		    "%s: failed soft reset", __func__);
18645c45adf0SJesse Butler 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
18655c45adf0SJesse Butler 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
18665c45adf0SJesse Butler 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE);
18675c45adf0SJesse Butler 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
18685c45adf0SJesse Butler 		mutex_enter(&pwp->lock);
18695c45adf0SJesse Butler 		pwp->state = STATE_DEAD;
18705c45adf0SJesse Butler 		return (DDI_FAILURE);
18715c45adf0SJesse Butler 	}
18725c45adf0SJesse Butler 
18735c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
18745c45adf0SJesse Butler 	pwp->state = STATE_RUNNING;
18755c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
18765c45adf0SJesse Butler 
18775c45adf0SJesse Butler 	/*
18785c45adf0SJesse Butler 	 * Finally, restart the phys, which will bring the iports back
18795c45adf0SJesse Butler 	 * up and eventually result in discovery running.
18805c45adf0SJesse Butler 	 */
18815c45adf0SJesse Butler 	if (pmcs_start_phys(pwp)) {
18825c45adf0SJesse Butler 		/* We should be up and running now, so retry */
18835c45adf0SJesse Butler 		if (pmcs_start_phys(pwp)) {
18845c45adf0SJesse Butler 			/* Apparently unable to restart PHYs, fail */
18855c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18865c45adf0SJesse Butler 			    "%s: failed to restart PHYs after soft reset",
18875c45adf0SJesse Butler 			    __func__);
18885c45adf0SJesse Butler 			mutex_enter(&pwp->lock);
18895c45adf0SJesse Butler 			return (DDI_FAILURE);
18905c45adf0SJesse Butler 		}
18915c45adf0SJesse Butler 	}
18925c45adf0SJesse Butler 
18935c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
18945c45adf0SJesse Butler 	return (DDI_SUCCESS);
18955c45adf0SJesse Butler }
18965c45adf0SJesse Butler 
18974c06356bSdh /*
18984c06356bSdh  * Reset a device or a logical unit.
18994c06356bSdh  */
19004c06356bSdh int
19014c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
19024c06356bSdh {
19034c06356bSdh 	int rval = 0;
19044c06356bSdh 
19054c06356bSdh 	if (pptr == NULL) {
19064c06356bSdh 		return (ENXIO);
19074c06356bSdh 	}
19084c06356bSdh 
19094c06356bSdh 	pmcs_lock_phy(pptr);
19104c06356bSdh 	if (pptr->dtype == SAS) {
19114c06356bSdh 		/*
19124c06356bSdh 		 * Some devices do not support SAS_I_T_NEXUS_RESET as
19134c06356bSdh 		 * it is not a mandatory (in SAM4) task management
19144c06356bSdh 		 * function, while LOGIC_UNIT_RESET is mandatory.
19154c06356bSdh 		 *
19164c06356bSdh 		 * The problem here is that we need to iterate over
19174c06356bSdh 		 * all known LUNs to emulate the semantics of
19184c06356bSdh 		 * "RESET_TARGET".
19194c06356bSdh 		 *
19204c06356bSdh 		 * XXX: FIX ME
19214c06356bSdh 		 */
19224c06356bSdh 		if (lun == (uint64_t)-1) {
19234c06356bSdh 			lun = 0;
19244c06356bSdh 		}
19254c06356bSdh 		rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
19264c06356bSdh 		    NULL);
19274c06356bSdh 	} else if (pptr->dtype == SATA) {
19284c06356bSdh 		if (lun != 0ull) {
19294c06356bSdh 			pmcs_unlock_phy(pptr);
19304c06356bSdh 			return (EINVAL);
19314c06356bSdh 		}
19324c06356bSdh 		rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
19334c06356bSdh 	} else {
19344c06356bSdh 		pmcs_unlock_phy(pptr);
1935c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
19364c06356bSdh 		    "%s: cannot reset a SMP device yet (%s)",
19374c06356bSdh 		    __func__, pptr->path);
19384c06356bSdh 		return (EINVAL);
19394c06356bSdh 	}
19404c06356bSdh 
19414c06356bSdh 	/*
19424c06356bSdh 	 * Now harvest any commands killed by this action
19434c06356bSdh 	 * by issuing an ABORT for all commands on this device.
19444c06356bSdh 	 *
19454c06356bSdh 	 * We do this even if the the tmf or reset fails (in case there
19464c06356bSdh 	 * are any dead commands around to be harvested *anyway*).
19474c06356bSdh 	 * We don't have to await for the abort to complete.
19484c06356bSdh 	 */
19494c06356bSdh 	if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
19504c06356bSdh 		pptr->abort_pending = 1;
19514c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
19524c06356bSdh 	}
19534c06356bSdh 
19544c06356bSdh 	pmcs_unlock_phy(pptr);
19554c06356bSdh 	return (rval);
19564c06356bSdh }
19574c06356bSdh 
19584c06356bSdh /*
19594c06356bSdh  * Called with PHY locked.
19604c06356bSdh  */
19614c06356bSdh static int
19624c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
19634c06356bSdh {
19644c06356bSdh 	if (pptr->valid_device_id == 0) {
19654c06356bSdh 		int result = pmcs_register_device(pwp, pptr);
19664c06356bSdh 
19674c06356bSdh 		/*
19684c06356bSdh 		 * If we changed while registering, punt
19694c06356bSdh 		 */
19704c06356bSdh 		if (pptr->changed) {
19714c06356bSdh 			RESTART_DISCOVERY(pwp);
19724c06356bSdh 			return (-1);
19734c06356bSdh 		}
19744c06356bSdh 
19754c06356bSdh 		/*
19764c06356bSdh 		 * If we had a failure to register, check against errors.
19774c06356bSdh 		 * An ENOMEM error means we just retry (temp resource shortage).
19784c06356bSdh 		 */
19794c06356bSdh 		if (result == ENOMEM) {
19804c06356bSdh 			PHY_CHANGED(pwp, pptr);
19814c06356bSdh 			RESTART_DISCOVERY(pwp);
19824c06356bSdh 			return (-1);
19834c06356bSdh 		}
19844c06356bSdh 
19854c06356bSdh 		/*
19864c06356bSdh 		 * An ETIMEDOUT error means we retry (if our counter isn't
19874c06356bSdh 		 * exhausted)
19884c06356bSdh 		 */
19894c06356bSdh 		if (result == ETIMEDOUT) {
19904c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
19914c06356bSdh 				PHY_CHANGED(pwp, pptr);
19924c06356bSdh 				RESTART_DISCOVERY(pwp);
19934c06356bSdh 			} else {
1994c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
19954c06356bSdh 				    "%s: Retries exhausted for %s, killing",
19964c06356bSdh 				    __func__, pptr->path);
19974c06356bSdh 				pptr->config_stop = 0;
19984c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
19994c06356bSdh 			}
20004c06356bSdh 			return (-1);
20014c06356bSdh 		}
20024c06356bSdh 		/*
20034c06356bSdh 		 * Other errors or no valid device id is fatal, but don't
20044c06356bSdh 		 * preclude a future action.
20054c06356bSdh 		 */
20064c06356bSdh 		if (result || pptr->valid_device_id == 0) {
2007c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
2008c3bc407cSdh 			    "%s: %s could not be registered", __func__,
2009c3bc407cSdh 			    pptr->path);
20104c06356bSdh 			return (-1);
20114c06356bSdh 		}
20124c06356bSdh 	}
20134c06356bSdh 	return (0);
20144c06356bSdh }
20154c06356bSdh 
20164c06356bSdh int
20174c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport)
20184c06356bSdh {
20194c06356bSdh 	ASSERT(iport);
20204c06356bSdh 	if (iport == NULL)
20214c06356bSdh 		return (B_FALSE);
20224c06356bSdh 
2023c3bc407cSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
20244c06356bSdh 
20254c06356bSdh 	/* create target map */
202660aabb4cSChris Horne 	if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET,
202760aabb4cSChris Horne 	    tgtmap_csync_usec, tgtmap_stable_usec, (void *)iport,
202860aabb4cSChris Horne 	    pmcs_tgtmap_activate_cb, pmcs_tgtmap_deactivate_cb,
20299aed1621SDavid Hollister 	    &iport->iss_tgtmap) != DDI_SUCCESS) {
2030c3bc407cSdh 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL,
20314c06356bSdh 		    "%s: failed to create tgtmap", __func__);
20324c06356bSdh 		return (B_FALSE);
20334c06356bSdh 	}
20344c06356bSdh 	return (B_TRUE);
20354c06356bSdh }
20364c06356bSdh 
20374c06356bSdh int
20384c06356bSdh pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport)
20394c06356bSdh {
20404c06356bSdh 	ASSERT(iport && iport->iss_tgtmap);
20414c06356bSdh 	if ((iport == NULL) || (iport->iss_tgtmap == NULL))
20424c06356bSdh 		return (B_FALSE);
20434c06356bSdh 
2044c3bc407cSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
20454c06356bSdh 
20464c06356bSdh 	/* destroy target map */
20474c06356bSdh 	scsi_hba_tgtmap_destroy(iport->iss_tgtmap);
20484c06356bSdh 	return (B_TRUE);
20494c06356bSdh }
20504c06356bSdh 
2051f73ae3dbSJesse Butler /*
2052f73ae3dbSJesse Butler  * Remove all phys from an iport's phymap and empty it's phylist.
20535c45adf0SJesse Butler  * Called when a port has been reset by the host (see pmcs_intr.c)
20545c45adf0SJesse Butler  * or prior to issuing a soft reset if we detect a stall on the chip
20555c45adf0SJesse Butler  * (see pmcs_attach.c).
2056f73ae3dbSJesse Butler  */
2057f73ae3dbSJesse Butler void
2058f73ae3dbSJesse Butler pmcs_iport_teardown_phys(pmcs_iport_t *iport)
2059f73ae3dbSJesse Butler {
2060f73ae3dbSJesse Butler 	pmcs_hw_t		*pwp;
2061f73ae3dbSJesse Butler 	sas_phymap_phys_t	*phys;
2062f73ae3dbSJesse Butler 	int			phynum;
2063f73ae3dbSJesse Butler 
2064f73ae3dbSJesse Butler 	ASSERT(iport);
2065f73ae3dbSJesse Butler 	ASSERT(mutex_owned(&iport->lock));
2066f73ae3dbSJesse Butler 	pwp = iport->pwp;
2067f73ae3dbSJesse Butler 	ASSERT(pwp);
2068f73ae3dbSJesse Butler 
2069f73ae3dbSJesse Butler 	/*
2070f73ae3dbSJesse Butler 	 * Remove all phys from the iport handle's phy list, unset its
2071f73ae3dbSJesse Butler 	 * primary phy and update its state.
2072f73ae3dbSJesse Butler 	 */
2073f73ae3dbSJesse Butler 	pmcs_remove_phy_from_iport(iport, NULL);
2074f73ae3dbSJesse Butler 	iport->pptr = NULL;
2075f73ae3dbSJesse Butler 	iport->ua_state = UA_PEND_DEACTIVATE;
2076f73ae3dbSJesse Butler 
2077f73ae3dbSJesse Butler 	/* Remove all phys from the phymap */
2078f73ae3dbSJesse Butler 	phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
20795c45adf0SJesse Butler 	if (phys) {
20805c45adf0SJesse Butler 		while ((phynum = sas_phymap_phys_next(phys)) != -1) {
20815c45adf0SJesse Butler 			(void) sas_phymap_phy_rem(pwp->hss_phymap, phynum);
20825c45adf0SJesse Butler 		}
20835c45adf0SJesse Butler 		sas_phymap_phys_free(phys);
2084f73ae3dbSJesse Butler 	}
2085f73ae3dbSJesse Butler }
2086f73ae3dbSJesse Butler 
20874c06356bSdh /*
20884c06356bSdh  * Query the phymap and populate the iport handle passed in.
20894c06356bSdh  * Called with iport lock held.
20904c06356bSdh  */
20914c06356bSdh int
20924c06356bSdh pmcs_iport_configure_phys(pmcs_iport_t *iport)
20934c06356bSdh {
20944c06356bSdh 	pmcs_hw_t		*pwp;
20954c06356bSdh 	pmcs_phy_t		*pptr;
20964c06356bSdh 	sas_phymap_phys_t	*phys;
20974c06356bSdh 	int			phynum;
20984c06356bSdh 	int			inst;
20994c06356bSdh 
21004c06356bSdh 	ASSERT(iport);
21014c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
21024c06356bSdh 	pwp = iport->pwp;
21034c06356bSdh 	ASSERT(pwp);
21044c06356bSdh 	inst = ddi_get_instance(iport->dip);
21054c06356bSdh 
21064c06356bSdh 	mutex_enter(&pwp->lock);
21074c06356bSdh 	ASSERT(pwp->root_phys != NULL);
21084c06356bSdh 
21094c06356bSdh 	/*
21104c06356bSdh 	 * Query the phymap regarding the phys in this iport and populate
21114c06356bSdh 	 * the iport's phys list. Hereafter this list is maintained via
21124c06356bSdh 	 * port up and down events in pmcs_intr.c
21134c06356bSdh 	 */
21144c06356bSdh 	ASSERT(list_is_empty(&iport->phys));
21154c06356bSdh 	phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
21165c45adf0SJesse Butler 	ASSERT(phys != NULL);
21174c06356bSdh 	while ((phynum = sas_phymap_phys_next(phys)) != -1) {
21184c06356bSdh 		/* Grab the phy pointer from root_phys */
21194c06356bSdh 		pptr = pwp->root_phys + phynum;
21204c06356bSdh 		ASSERT(pptr);
21214c06356bSdh 		pmcs_lock_phy(pptr);
21224c06356bSdh 		ASSERT(pptr->phynum == phynum);
21234c06356bSdh 
21244c06356bSdh 		/*
21254c06356bSdh 		 * Set a back pointer in the phy to this iport.
21264c06356bSdh 		 */
21274c06356bSdh 		pptr->iport = iport;
21284c06356bSdh 
21294c06356bSdh 		/*
21304c06356bSdh 		 * If this phy is the primary, set a pointer to it on our
21314c06356bSdh 		 * iport handle, and set our portid from it.
21324c06356bSdh 		 */
21334c06356bSdh 		if (!pptr->subsidiary) {
21344c06356bSdh 			iport->pptr = pptr;
21354c06356bSdh 			iport->portid = pptr->portid;
21364c06356bSdh 		}
21374c06356bSdh 
21384c06356bSdh 		/*
21394c06356bSdh 		 * Finally, insert the phy into our list
21404c06356bSdh 		 */
21414c06356bSdh 		pmcs_unlock_phy(pptr);
2142145e0143Sdh 		pmcs_add_phy_to_iport(iport, pptr);
21434c06356bSdh 
2144c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: found "
2145c3bc407cSdh 		    "phy %d [0x%p] on iport%d, refcnt(%d)", __func__, phynum,
21464c06356bSdh 		    (void *)pptr, inst, iport->refcnt);
21474c06356bSdh 	}
21484c06356bSdh 	mutex_exit(&pwp->lock);
21494c06356bSdh 	sas_phymap_phys_free(phys);
21504c06356bSdh 	RESTART_DISCOVERY(pwp);
21514c06356bSdh 	return (DDI_SUCCESS);
21524c06356bSdh }
21534c06356bSdh 
21544c06356bSdh /*
21554c06356bSdh  * Return the iport that ua is associated with, or NULL.  If an iport is
21564c06356bSdh  * returned, it will be held and the caller must release the hold.
21574c06356bSdh  */
21584c06356bSdh static pmcs_iport_t *
21594c06356bSdh pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua)
21604c06356bSdh {
21614c06356bSdh 	pmcs_iport_t	*iport = NULL;
21624c06356bSdh 
21634c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
21644c06356bSdh 	for (iport = list_head(&pwp->iports);
21654c06356bSdh 	    iport != NULL;
21664c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
21674c06356bSdh 		mutex_enter(&iport->lock);
21684c06356bSdh 		if (strcmp(iport->ua, ua) == 0) {
21694c06356bSdh 			mutex_exit(&iport->lock);
2170*3be32c0fSJesse Butler 			pmcs_hold_iport(iport);
21714c06356bSdh 			break;
21724c06356bSdh 		}
21734c06356bSdh 		mutex_exit(&iport->lock);
21744c06356bSdh 	}
21754c06356bSdh 	rw_exit(&pwp->iports_lock);
21764c06356bSdh 
21774c06356bSdh 	return (iport);
21784c06356bSdh }
21794c06356bSdh 
21804c06356bSdh /*
21814c06356bSdh  * Return the iport that pptr is associated with, or NULL.
21824c06356bSdh  * If an iport is returned, there is a hold that the caller must release.
21834c06356bSdh  */
21844c06356bSdh pmcs_iport_t *
21859aed1621SDavid Hollister pmcs_get_iport_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
21864c06356bSdh {
21874c06356bSdh 	pmcs_iport_t	*iport = NULL;
21884c06356bSdh 	char		*ua;
21894c06356bSdh 
21909aed1621SDavid Hollister 	ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], wwn);
21914c06356bSdh 	if (ua) {
21924c06356bSdh 		iport = pmcs_get_iport_by_ua(pwp, ua);
21934c06356bSdh 		if (iport) {
21944c06356bSdh 			mutex_enter(&iport->lock);
21959aed1621SDavid Hollister 			pmcs_iport_active(iport);
21969aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
21979aed1621SDavid Hollister 			    "found iport [0x%p] on ua (%s), refcnt (%d)",
21989aed1621SDavid Hollister 			    __func__, (void *)iport, ua, iport->refcnt);
21994c06356bSdh 			mutex_exit(&iport->lock);
22004c06356bSdh 		}
22014c06356bSdh 	}
22024c06356bSdh 
22034c06356bSdh 	return (iport);
22044c06356bSdh }
22054c06356bSdh 
2206f73ae3dbSJesse Butler /*
22076745c559SJesse Butler  * Promote the next phy on this port to primary, and return it.
2208f73ae3dbSJesse Butler  * Called when the primary PHY on a port is going down, but the port
2209f73ae3dbSJesse Butler  * remains up (see pmcs_intr.c).
2210f73ae3dbSJesse Butler  */
2211f73ae3dbSJesse Butler pmcs_phy_t *
2212f73ae3dbSJesse Butler pmcs_promote_next_phy(pmcs_phy_t *prev_primary)
2213f73ae3dbSJesse Butler {
22146745c559SJesse Butler 	pmcs_hw_t	*pwp;
22156745c559SJesse Butler 	pmcs_iport_t	*iport;
22166745c559SJesse Butler 	pmcs_phy_t	*pptr, *child;
22176745c559SJesse Butler 	int		portid;
2218f73ae3dbSJesse Butler 
22196745c559SJesse Butler 	pmcs_lock_phy(prev_primary);
22206745c559SJesse Butler 	portid = prev_primary->portid;
22216745c559SJesse Butler 	iport  = prev_primary->iport;
22226745c559SJesse Butler 	pwp    = prev_primary->pwp;
2223f73ae3dbSJesse Butler 
22246745c559SJesse Butler 	/* Use the first available phy in this port */
22256745c559SJesse Butler 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
22266745c559SJesse Butler 		if ((pptr->portid == portid) && (pptr != prev_primary)) {
22276745c559SJesse Butler 			mutex_enter(&pptr->phy_lock);
2228f73ae3dbSJesse Butler 			break;
2229f73ae3dbSJesse Butler 		}
2230f73ae3dbSJesse Butler 	}
2231f73ae3dbSJesse Butler 
2232f73ae3dbSJesse Butler 	if (pptr == NULL) {
22336745c559SJesse Butler 		pmcs_unlock_phy(prev_primary);
2234f73ae3dbSJesse Butler 		return (NULL);
2235f73ae3dbSJesse Butler 	}
2236f73ae3dbSJesse Butler 
22376745c559SJesse Butler 	if (iport) {
22386745c559SJesse Butler 		mutex_enter(&iport->lock);
22396745c559SJesse Butler 		iport->pptr = pptr;
22406745c559SJesse Butler 		mutex_exit(&iport->lock);
22416745c559SJesse Butler 	}
2242f73ae3dbSJesse Butler 
22436745c559SJesse Butler 	/* Update the phy handle with the data from the previous primary */
2244f73ae3dbSJesse Butler 	pptr->children		= prev_primary->children;
2245f73ae3dbSJesse Butler 	child = pptr->children;
2246f73ae3dbSJesse Butler 	while (child) {
2247f73ae3dbSJesse Butler 		child->parent = pptr;
2248f73ae3dbSJesse Butler 		child = child->sibling;
2249f73ae3dbSJesse Butler 	}
2250f73ae3dbSJesse Butler 	pptr->ncphy		= prev_primary->ncphy;
2251f73ae3dbSJesse Butler 	pptr->width		= prev_primary->width;
2252f73ae3dbSJesse Butler 	pptr->dtype		= prev_primary->dtype;
2253f73ae3dbSJesse Butler 	pptr->pend_dtype	= prev_primary->pend_dtype;
2254f73ae3dbSJesse Butler 	pptr->tolerates_sas2	= prev_primary->tolerates_sas2;
2255f73ae3dbSJesse Butler 	pptr->atdt		= prev_primary->atdt;
2256f73ae3dbSJesse Butler 	pptr->portid		= prev_primary->portid;
2257f73ae3dbSJesse Butler 	pptr->link_rate		= prev_primary->link_rate;
2258f73ae3dbSJesse Butler 	pptr->configured	= prev_primary->configured;
2259f73ae3dbSJesse Butler 	pptr->iport		= prev_primary->iport;
2260f73ae3dbSJesse Butler 	pptr->target		= prev_primary->target;
22616745c559SJesse Butler 	if (pptr->target) {
22626745c559SJesse Butler 		pptr->target->phy = pptr;
22636745c559SJesse Butler 	}
2264499cfd15SDavid Hollister 
2265499cfd15SDavid Hollister 	/* Update the phy mask properties for the affected PHYs */
2266499cfd15SDavid Hollister 	/* Clear the current values... */
2267499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
2268499cfd15SDavid Hollister 	    pptr->tgt_port_pm_tmp, B_FALSE);
2269499cfd15SDavid Hollister 	/* ...replace with the values from prev_primary... */
2270499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp,
2271499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm_tmp, B_TRUE);
2272499cfd15SDavid Hollister 	/* ...then clear prev_primary's PHY values from the new primary */
2273499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm,
2274499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm, B_FALSE);
2275499cfd15SDavid Hollister 	/* Clear the prev_primary's values */
2276499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp,
2277499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm_tmp, B_FALSE);
2278499cfd15SDavid Hollister 
2279f73ae3dbSJesse Butler 	pptr->subsidiary = 0;
2280f73ae3dbSJesse Butler 
2281f73ae3dbSJesse Butler 	prev_primary->subsidiary = 1;
2282f73ae3dbSJesse Butler 	prev_primary->children = NULL;
22836745c559SJesse Butler 	prev_primary->target = NULL;
2284601c90f1SSrikanth, Ramana 	pptr->device_id = prev_primary->device_id;
2285*3be32c0fSJesse Butler 	pptr->valid_device_id = prev_primary->valid_device_id;
2286f73ae3dbSJesse Butler 	pmcs_unlock_phy(prev_primary);
2287f73ae3dbSJesse Butler 
2288f73ae3dbSJesse Butler 	/*
2289f73ae3dbSJesse Butler 	 * We call pmcs_unlock_phy() on pptr because it now contains the
2290f73ae3dbSJesse Butler 	 * list of children.
2291f73ae3dbSJesse Butler 	 */
2292f73ae3dbSJesse Butler 	pmcs_unlock_phy(pptr);
2293f73ae3dbSJesse Butler 
2294f73ae3dbSJesse Butler 	return (pptr);
2295f73ae3dbSJesse Butler }
2296f73ae3dbSJesse Butler 
2297*3be32c0fSJesse Butler void
2298*3be32c0fSJesse Butler pmcs_hold_iport(pmcs_iport_t *iport)
2299*3be32c0fSJesse Butler {
2300*3be32c0fSJesse Butler 	/*
2301*3be32c0fSJesse Butler 	 * Grab a reference to this iport.
2302*3be32c0fSJesse Butler 	 */
2303*3be32c0fSJesse Butler 	ASSERT(iport);
2304*3be32c0fSJesse Butler 	mutex_enter(&iport->refcnt_lock);
2305*3be32c0fSJesse Butler 	iport->refcnt++;
2306*3be32c0fSJesse Butler 	mutex_exit(&iport->refcnt_lock);
2307*3be32c0fSJesse Butler 
2308*3be32c0fSJesse Butler 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport "
2309*3be32c0fSJesse Butler 	    "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
2310*3be32c0fSJesse Butler }
2311*3be32c0fSJesse Butler 
23124c06356bSdh void
23134c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport)
23144c06356bSdh {
23154c06356bSdh 	/*
23164c06356bSdh 	 * Release a refcnt on this iport. If this is the last reference,
23174c06356bSdh 	 * signal the potential waiter in pmcs_iport_unattach().
23184c06356bSdh 	 */
23194c06356bSdh 	ASSERT(iport->refcnt > 0);
23204c06356bSdh 	mutex_enter(&iport->refcnt_lock);
23214c06356bSdh 	iport->refcnt--;
23224c06356bSdh 	mutex_exit(&iport->refcnt_lock);
23234c06356bSdh 	if (iport->refcnt == 0) {
23244c06356bSdh 		cv_signal(&iport->refcnt_cv);
23254c06356bSdh 	}
2326*3be32c0fSJesse Butler 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport "
2327c3bc407cSdh 	    "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
23284c06356bSdh }
23294c06356bSdh 
23304c06356bSdh void
23314c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp)
23324c06356bSdh {
23334c06356bSdh 	_NOTE(ARGUNUSED(privp));
23344c06356bSdh 	pmcs_hw_t	*pwp = arg;
23354c06356bSdh 	pmcs_iport_t	*iport = NULL;
23364c06356bSdh 
23374c06356bSdh 	mutex_enter(&pwp->lock);
2338978d7443SSrikanth Suravajhala 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD) ||
2339978d7443SSrikanth Suravajhala 	    (pwp->state == STATE_IN_RESET)) {
23404c06356bSdh 		mutex_exit(&pwp->lock);
23414c06356bSdh 		return;
23424c06356bSdh 	}
23434c06356bSdh 	pwp->phymap_active++;
23444c06356bSdh 	mutex_exit(&pwp->lock);
23454c06356bSdh 
23464c06356bSdh 	if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
23474c06356bSdh 	    DDI_SUCCESS) {
2348c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
2349c3bc407cSdh 		    "add iport handle on unit address [%s]", __func__, ua);
23504c06356bSdh 	} else {
2351c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
2352c3bc407cSdh 		    "phymap_active count (%d), added iport handle on unit "
2353c3bc407cSdh 		    "address [%s]", __func__, pwp->phymap_active, ua);
23544c06356bSdh 	}
23554c06356bSdh 
23564c06356bSdh 	/* Set the HBA softstate as our private data for this unit address */
23574c06356bSdh 	*privp = (void *)pwp;
23584c06356bSdh 
23594c06356bSdh 	/*
23604c06356bSdh 	 * We are waiting on attach for this iport node, unless it is still
23614c06356bSdh 	 * attached. This can happen if a consumer has an outstanding open
23624c06356bSdh 	 * on our iport node, but the port is down.  If this is the case, we
23634c06356bSdh 	 * need to configure our iport here for reuse.
23644c06356bSdh 	 */
23654c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
23664c06356bSdh 	if (iport) {
23674c06356bSdh 		mutex_enter(&iport->lock);
23684c06356bSdh 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
2369c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
23704c06356bSdh 			    "failed to configure phys on iport [0x%p] at "
23714c06356bSdh 			    "unit address (%s)", __func__, (void *)iport, ua);
23724c06356bSdh 		}
23739aed1621SDavid Hollister 		pmcs_iport_active(iport);
23744c06356bSdh 		pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
23754c06356bSdh 		    &iport->nphy);
23764c06356bSdh 		mutex_exit(&iport->lock);
23774c06356bSdh 		pmcs_rele_iport(iport);
23784c06356bSdh 	}
23794c06356bSdh 
23804c06356bSdh }
23814c06356bSdh 
23824c06356bSdh void
23834c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp)
23844c06356bSdh {
23854c06356bSdh 	_NOTE(ARGUNUSED(privp));
23864c06356bSdh 	pmcs_hw_t	*pwp = arg;
23874c06356bSdh 	pmcs_iport_t	*iport;
23884c06356bSdh 
23894c06356bSdh 	mutex_enter(&pwp->lock);
23904c06356bSdh 	pwp->phymap_active--;
23914c06356bSdh 	mutex_exit(&pwp->lock);
23924c06356bSdh 
23934c06356bSdh 	if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
23944c06356bSdh 	    DDI_SUCCESS) {
2395c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
2396c3bc407cSdh 		    "remove iport handle on unit address [%s]", __func__, ua);
23974c06356bSdh 	} else {
2398c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
2399c3bc407cSdh 		    "phymap_active count (%d), removed iport handle on unit "
2400c3bc407cSdh 		    "address [%s]", __func__, pwp->phymap_active, ua);
24014c06356bSdh 	}
24024c06356bSdh 
24034c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
24044c06356bSdh 
24054c06356bSdh 	if (iport == NULL) {
2406c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed "
2407c3bc407cSdh 		    "lookup of iport handle on unit addr (%s)", __func__, ua);
24084c06356bSdh 		return;
24094c06356bSdh 	}
24104c06356bSdh 
24114c06356bSdh 	mutex_enter(&iport->lock);
24124c06356bSdh 	iport->ua_state = UA_INACTIVE;
24134c06356bSdh 	iport->portid = PMCS_IPORT_INVALID_PORT_ID;
24144c06356bSdh 	pmcs_remove_phy_from_iport(iport, NULL);
24154c06356bSdh 	mutex_exit(&iport->lock);
24164c06356bSdh 	pmcs_rele_iport(iport);
24174c06356bSdh }
24184c06356bSdh 
24194c06356bSdh /*
24204c06356bSdh  * Top-level discovery function
24214c06356bSdh  */
24224c06356bSdh void
24234c06356bSdh pmcs_discover(pmcs_hw_t *pwp)
24244c06356bSdh {
24254c06356bSdh 	pmcs_phy_t		*pptr;
24264c06356bSdh 	pmcs_phy_t		*root_phy;
24274c06356bSdh 
24284c06356bSdh 	DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
24294c06356bSdh 	    boolean_t, pwp->config_changed);
24304c06356bSdh 
24314c06356bSdh 	mutex_enter(&pwp->lock);
24324c06356bSdh 
24334c06356bSdh 	if (pwp->state != STATE_RUNNING) {
24344c06356bSdh 		mutex_exit(&pwp->lock);
24354c06356bSdh 		return;
24364c06356bSdh 	}
24374c06356bSdh 
24384c06356bSdh 	/* Ensure we have at least one phymap active */
24394c06356bSdh 	if (pwp->phymap_active == 0) {
24404c06356bSdh 		mutex_exit(&pwp->lock);
2441c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24424c06356bSdh 		    "%s: phymap inactive, exiting", __func__);
24434c06356bSdh 		return;
24444c06356bSdh 	}
24454c06356bSdh 
24464c06356bSdh 	mutex_exit(&pwp->lock);
24474c06356bSdh 
24484c06356bSdh 	/*
24494c06356bSdh 	 * If no iports have attached, but we have PHYs that are up, we
24504c06356bSdh 	 * are waiting for iport attach to complete.  Restart discovery.
24514c06356bSdh 	 */
24524c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
24534c06356bSdh 	if (!pwp->iports_attached) {
24544c06356bSdh 		rw_exit(&pwp->iports_lock);
2455c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24564c06356bSdh 		    "%s: no iports attached, retry discovery", __func__);
24574c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
24584c06356bSdh 		return;
24594c06356bSdh 	}
24604c06356bSdh 	rw_exit(&pwp->iports_lock);
24614c06356bSdh 
24624c06356bSdh 	mutex_enter(&pwp->config_lock);
24634c06356bSdh 	if (pwp->configuring) {
24644c06356bSdh 		mutex_exit(&pwp->config_lock);
2465c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24664c06356bSdh 		    "%s: configuration already in progress", __func__);
24674c06356bSdh 		return;
24684c06356bSdh 	}
24694c06356bSdh 
24704c06356bSdh 	if (pmcs_acquire_scratch(pwp, B_FALSE)) {
24714c06356bSdh 		mutex_exit(&pwp->config_lock);
2472c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24734c06356bSdh 		    "%s: cannot allocate scratch", __func__);
24744c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
24754c06356bSdh 		return;
24764c06356bSdh 	}
24774c06356bSdh 
24784c06356bSdh 	pwp->configuring = 1;
24794c06356bSdh 	pwp->config_changed = B_FALSE;
24804c06356bSdh 	mutex_exit(&pwp->config_lock);
24814c06356bSdh 
2482c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin");
24834c06356bSdh 
2484601c90f1SSrikanth, Ramana 	/*
2485601c90f1SSrikanth, Ramana 	 * First, tell SCSA that we're beginning set operations.
2486601c90f1SSrikanth, Ramana 	 */
2487601c90f1SSrikanth, Ramana 	pmcs_begin_observations(pwp);
2488601c90f1SSrikanth, Ramana 
24894c06356bSdh 	/*
24904c06356bSdh 	 * The order of the following traversals is important.
24914c06356bSdh 	 *
24924c06356bSdh 	 * The first one checks for changed expanders.
24934c06356bSdh 	 *
24944c06356bSdh 	 * The second one aborts commands for dead devices and deregisters them.
24954c06356bSdh 	 *
24964c06356bSdh 	 * The third one clears the contents of dead expanders from the tree
24974c06356bSdh 	 *
24984c06356bSdh 	 * The fourth one clears now dead devices in expanders that remain.
24994c06356bSdh 	 */
25004c06356bSdh 
25014c06356bSdh 	/*
25024c06356bSdh 	 * 1. Check expanders marked changed (but not dead) to see if they still
25034c06356bSdh 	 * have the same number of phys and the same SAS address. Mark them,
25044c06356bSdh 	 * their subsidiary phys (if wide) and their descendents dead if
25054c06356bSdh 	 * anything has changed. Check the devices they contain to see if
25064c06356bSdh 	 * *they* have changed. If they've changed from type NOTHING we leave
25074c06356bSdh 	 * them marked changed to be configured later (picking up a new SAS
25084c06356bSdh 	 * address and link rate if possible). Otherwise, any change in type,
25094c06356bSdh 	 * SAS address or removal of target role will cause us to mark them
25104c06356bSdh 	 * (and their descendents) as dead (and cause any pending commands
25114c06356bSdh 	 * and associated devices to be removed).
2512ec2c44b8Sdh 	 *
2513ec2c44b8Sdh 	 * NOTE: We don't want to bail on discovery if the config has
2514ec2c44b8Sdh 	 * changed until *after* we run pmcs_kill_devices.
25154c06356bSdh 	 */
25164c06356bSdh 	root_phy = pwp->root_phys;
251773a3eccdSDavid Hollister 	pmcs_check_expanders(pwp, root_phy);
25184c06356bSdh 
25194c06356bSdh 	/*
25204c06356bSdh 	 * 2. Descend the tree looking for dead devices and kill them
25214c06356bSdh 	 * by aborting all active commands and then deregistering them.
25224c06356bSdh 	 */
252373a3eccdSDavid Hollister 	if (pmcs_kill_devices(pwp, root_phy)) {
252473a3eccdSDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
252573a3eccdSDavid Hollister 		    "%s: pmcs_kill_devices failed!", __func__);
25264c06356bSdh 	}
25274c06356bSdh 
25284c06356bSdh 	/*
25294c06356bSdh 	 * 3. Check for dead expanders and remove their children from the tree.
25304c06356bSdh 	 * By the time we get here, the devices and commands for them have
25314c06356bSdh 	 * already been terminated and removed.
25324c06356bSdh 	 *
25334c06356bSdh 	 * We do this independent of the configuration count changing so we can
25344c06356bSdh 	 * free any dead device PHYs that were discovered while checking
25354c06356bSdh 	 * expanders. We ignore any subsidiary phys as pmcs_clear_expander
25364c06356bSdh 	 * will take care of those.
25374c06356bSdh 	 *
25384c06356bSdh 	 * NOTE: pmcs_clear_expander requires softstate lock
25394c06356bSdh 	 */
25404c06356bSdh 	mutex_enter(&pwp->lock);
25414c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
25424c06356bSdh 		/*
25434c06356bSdh 		 * Call pmcs_clear_expander for every root PHY.  It will
25444c06356bSdh 		 * recurse and determine which (if any) expanders actually
25454c06356bSdh 		 * need to be cleared.
25464c06356bSdh 		 */
25474c06356bSdh 		pmcs_lock_phy(pptr);
25484c06356bSdh 		pmcs_clear_expander(pwp, pptr, 0);
25494c06356bSdh 		pmcs_unlock_phy(pptr);
25504c06356bSdh 	}
25514c06356bSdh 	mutex_exit(&pwp->lock);
25524c06356bSdh 
25534c06356bSdh 	/*
25544c06356bSdh 	 * 4. Check for dead devices and nullify them. By the time we get here,
25554c06356bSdh 	 * the devices and commands for them have already been terminated
25564c06356bSdh 	 * and removed. This is different from step 2 in that this just nulls
25574c06356bSdh 	 * phys that are part of expanders that are still here but used to
25584c06356bSdh 	 * be something but are no longer something (e.g., after a pulled
25594c06356bSdh 	 * disk drive). Note that dead expanders had their contained phys
25604c06356bSdh 	 * removed from the tree- here, the expanders themselves are
25614c06356bSdh 	 * nullified (unless they were removed by being contained in another
25624c06356bSdh 	 * expander phy).
25634c06356bSdh 	 */
25644c06356bSdh 	pmcs_clear_phys(pwp, root_phy);
25654c06356bSdh 
25664c06356bSdh 	/*
25674c06356bSdh 	 * 5. Now check for and configure new devices.
25684c06356bSdh 	 */
25694c06356bSdh 	if (pmcs_configure_new_devices(pwp, root_phy)) {
25704c06356bSdh 		goto restart;
25714c06356bSdh 	}
25724c06356bSdh 
25734c06356bSdh out:
25744c06356bSdh 	DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
25754c06356bSdh 	    boolean_t, pwp->config_changed);
2576c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end");
25774c06356bSdh 
25784c06356bSdh 	mutex_enter(&pwp->config_lock);
25794c06356bSdh 
25804c06356bSdh 	if (pwp->config_changed == B_FALSE) {
25814c06356bSdh 		/*
25824c06356bSdh 		 * Observation is stable, report what we currently see to
25834c06356bSdh 		 * the tgtmaps for delta processing. Start by setting
25844c06356bSdh 		 * BEGIN on all tgtmaps.
25854c06356bSdh 		 */
25864c06356bSdh 		mutex_exit(&pwp->config_lock);
25874c06356bSdh 		if (pmcs_report_observations(pwp) == B_FALSE) {
25884c06356bSdh 			goto restart;
25894c06356bSdh 		}
25904c06356bSdh 		mutex_enter(&pwp->config_lock);
25914c06356bSdh 	} else {
25924c06356bSdh 		/*
25934c06356bSdh 		 * If config_changed is TRUE, we need to reschedule
25944c06356bSdh 		 * discovery now.
25954c06356bSdh 		 */
2596c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
25974c06356bSdh 		    "%s: Config has changed, will re-run discovery", __func__);
25984c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
25994c06356bSdh 	}
26004c06356bSdh 
26014c06356bSdh 	pmcs_release_scratch(pwp);
26025c45adf0SJesse Butler 	if (!pwp->quiesced) {
26035c45adf0SJesse Butler 		pwp->blocked = 0;
26045c45adf0SJesse Butler 	}
26054c06356bSdh 	pwp->configuring = 0;
260632b54db7SJesse Butler 	cv_signal(&pwp->config_cv);
26074c06356bSdh 	mutex_exit(&pwp->config_lock);
26084c06356bSdh 
26094c06356bSdh #ifdef DEBUG
26104c06356bSdh 	pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
26114c06356bSdh 	if (pptr != NULL) {
26124c06356bSdh 		if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
2613c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
26144c06356bSdh 			    "PHY %s dead=%d changed=%d configured=%d "
26154c06356bSdh 			    "but no work scheduled", pptr->path, pptr->dead,
26164c06356bSdh 			    pptr->changed, pptr->configured);
26174c06356bSdh 		}
26184c06356bSdh 		pmcs_unlock_phy(pptr);
26194c06356bSdh 	}
26204c06356bSdh #endif
26214c06356bSdh 
26224c06356bSdh 	return;
26234c06356bSdh 
26244c06356bSdh restart:
26254c06356bSdh 	/* Clean up and restart discovery */
26264c06356bSdh 	pmcs_release_scratch(pwp);
26270b53804eSReed 	pmcs_flush_observations(pwp);
26284c06356bSdh 	mutex_enter(&pwp->config_lock);
26294c06356bSdh 	pwp->configuring = 0;
263032b54db7SJesse Butler 	cv_signal(&pwp->config_cv);
26314c06356bSdh 	RESTART_DISCOVERY_LOCKED(pwp);
26324c06356bSdh 	mutex_exit(&pwp->config_lock);
26334c06356bSdh }
26344c06356bSdh 
26354c06356bSdh /*
26364c06356bSdh  * Return any PHY that needs to have scheduled work done.  The PHY is returned
26374c06356bSdh  * locked.
26384c06356bSdh  */
26394c06356bSdh static pmcs_phy_t *
26404c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
26414c06356bSdh {
26424c06356bSdh 	pmcs_phy_t *cphyp, *pnext;
26434c06356bSdh 
26444c06356bSdh 	while (pptr) {
26454c06356bSdh 		pmcs_lock_phy(pptr);
26464c06356bSdh 
26474c06356bSdh 		if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
26484c06356bSdh 			return (pptr);
26494c06356bSdh 		}
26504c06356bSdh 
26514c06356bSdh 		pnext = pptr->sibling;
26524c06356bSdh 
26534c06356bSdh 		if (pptr->children) {
26544c06356bSdh 			cphyp = pptr->children;
26554c06356bSdh 			pmcs_unlock_phy(pptr);
26564c06356bSdh 			cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
26574c06356bSdh 			if (cphyp) {
26584c06356bSdh 				return (cphyp);
26594c06356bSdh 			}
26604c06356bSdh 		} else {
26614c06356bSdh 			pmcs_unlock_phy(pptr);
26624c06356bSdh 		}
26634c06356bSdh 
26644c06356bSdh 		pptr = pnext;
26654c06356bSdh 	}
26664c06356bSdh 
26674c06356bSdh 	return (NULL);
26684c06356bSdh }
26694c06356bSdh 
26704c06356bSdh /*
2671601c90f1SSrikanth, Ramana  * We may (or may not) report observations to SCSA.  This is prefaced by
2672601c90f1SSrikanth, Ramana  * issuing a set_begin for each iport target map.
26734c06356bSdh  */
2674601c90f1SSrikanth, Ramana static void
2675601c90f1SSrikanth, Ramana pmcs_begin_observations(pmcs_hw_t *pwp)
26764c06356bSdh {
26774c06356bSdh 	pmcs_iport_t		*iport;
26784c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
26794c06356bSdh 
26804c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
26814c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
26824c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
26834c06356bSdh 		/*
26844c06356bSdh 		 * Unless we have at least one phy up, skip this iport.
26854c06356bSdh 		 * Note we don't need to lock the iport for report_skip
26864c06356bSdh 		 * since it is only used here.  We are doing the skip so that
26874c06356bSdh 		 * the phymap and iportmap stabilization times are honored -
26884c06356bSdh 		 * giving us the ability to recover port operation within the
26894c06356bSdh 		 * stabilization time without unconfiguring targets using the
26904c06356bSdh 		 * port.
26914c06356bSdh 		 */
26924c06356bSdh 		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
26934c06356bSdh 			iport->report_skip = 1;
26944c06356bSdh 			continue;		/* skip set_begin */
26954c06356bSdh 		}
26964c06356bSdh 		iport->report_skip = 0;
26974c06356bSdh 
26984c06356bSdh 		tgtmap = iport->iss_tgtmap;
26994c06356bSdh 		ASSERT(tgtmap);
27004c06356bSdh 		if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
2701c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
27024c06356bSdh 			    "%s: cannot set_begin tgtmap ", __func__);
27034c06356bSdh 			rw_exit(&pwp->iports_lock);
2704601c90f1SSrikanth, Ramana 			return;
27054c06356bSdh 		}
2706c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
2707c3bc407cSdh 		    "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap);
27084c06356bSdh 	}
27094c06356bSdh 	rw_exit(&pwp->iports_lock);
2710601c90f1SSrikanth, Ramana }
2711601c90f1SSrikanth, Ramana 
27120b53804eSReed /*
27130b53804eSReed  * Tell SCSA to flush the observations we've already sent (if any), as they
27140b53804eSReed  * are no longer valid.
27150b53804eSReed  */
27160b53804eSReed static void
27170b53804eSReed pmcs_flush_observations(pmcs_hw_t *pwp)
27180b53804eSReed {
27190b53804eSReed 	pmcs_iport_t		*iport;
27200b53804eSReed 	scsi_hba_tgtmap_t	*tgtmap;
27210b53804eSReed 
27220b53804eSReed 	rw_enter(&pwp->iports_lock, RW_READER);
27230b53804eSReed 	for (iport = list_head(&pwp->iports); iport != NULL;
27240b53804eSReed 	    iport = list_next(&pwp->iports, iport)) {
27250b53804eSReed 		/*
27260b53804eSReed 		 * Skip this iport if it has no PHYs up.
27270b53804eSReed 		 */
27280b53804eSReed 		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
27290b53804eSReed 			continue;
27300b53804eSReed 		}
27310b53804eSReed 
27320b53804eSReed 		tgtmap = iport->iss_tgtmap;
27330b53804eSReed 		ASSERT(tgtmap);
27340b53804eSReed 		if (scsi_hba_tgtmap_set_flush(tgtmap) != DDI_SUCCESS) {
27350b53804eSReed 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
27360b53804eSReed 			    "%s: Failed set_flush on tgtmap 0x%p", __func__,
27370b53804eSReed 			    (void *)tgtmap);
27380b53804eSReed 		} else {
27390b53804eSReed 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
27400b53804eSReed 			    "%s: set flush on tgtmap 0x%p", __func__,
27410b53804eSReed 			    (void *)tgtmap);
27420b53804eSReed 		}
27430b53804eSReed 	}
27440b53804eSReed 	rw_exit(&pwp->iports_lock);
27450b53804eSReed }
27460b53804eSReed 
2747601c90f1SSrikanth, Ramana /*
2748601c90f1SSrikanth, Ramana  * Report current observations to SCSA.
2749601c90f1SSrikanth, Ramana  */
2750601c90f1SSrikanth, Ramana static boolean_t
2751601c90f1SSrikanth, Ramana pmcs_report_observations(pmcs_hw_t *pwp)
2752601c90f1SSrikanth, Ramana {
2753601c90f1SSrikanth, Ramana 	pmcs_iport_t		*iport;
2754601c90f1SSrikanth, Ramana 	scsi_hba_tgtmap_t	*tgtmap;
2755601c90f1SSrikanth, Ramana 	char			*ap;
2756601c90f1SSrikanth, Ramana 	pmcs_phy_t		*pptr;
2757601c90f1SSrikanth, Ramana 	uint64_t		wwn;
27584c06356bSdh 
27594c06356bSdh 	/*
2760601c90f1SSrikanth, Ramana 	 * Observation is stable, report what we currently see to the tgtmaps
2761601c90f1SSrikanth, Ramana 	 * for delta processing.
27624c06356bSdh 	 */
27634c06356bSdh 	pptr = pwp->root_phys;
27644c06356bSdh 
27654c06356bSdh 	while (pptr) {
27664c06356bSdh 		pmcs_lock_phy(pptr);
27674c06356bSdh 
27684c06356bSdh 		/*
27694c06356bSdh 		 * Skip PHYs that have nothing attached or are dead.
27704c06356bSdh 		 */
27714c06356bSdh 		if ((pptr->dtype == NOTHING) || pptr->dead) {
27724c06356bSdh 			pmcs_unlock_phy(pptr);
27734c06356bSdh 			pptr = pptr->sibling;
27744c06356bSdh 			continue;
27754c06356bSdh 		}
27764c06356bSdh 
27774c06356bSdh 		if (pptr->changed) {
2778c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
27794c06356bSdh 			    "%s: oops, PHY %s changed; restart discovery",
27804c06356bSdh 			    __func__, pptr->path);
27814c06356bSdh 			pmcs_unlock_phy(pptr);
27824c06356bSdh 			return (B_FALSE);
27834c06356bSdh 		}
27844c06356bSdh 
27854c06356bSdh 		/*
27864c06356bSdh 		 * Get the iport for this root PHY, then call the helper
27874c06356bSdh 		 * to report observations for this iport's targets
27884c06356bSdh 		 */
27899aed1621SDavid Hollister 		wwn = pmcs_barray2wwn(pptr->sas_address);
27909aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
27919aed1621SDavid Hollister 		iport = pmcs_get_iport_by_wwn(pwp, wwn);
27924c06356bSdh 		if (iport == NULL) {
27934c06356bSdh 			/* No iport for this tgt */
2794c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
2795c3bc407cSdh 			    "%s: no iport for this target", __func__);
27964c06356bSdh 			pptr = pptr->sibling;
27974c06356bSdh 			continue;
27984c06356bSdh 		}
27994c06356bSdh 
28009aed1621SDavid Hollister 		pmcs_lock_phy(pptr);
28014c06356bSdh 		if (!iport->report_skip) {
28024c06356bSdh 			if (pmcs_report_iport_observations(
28034c06356bSdh 			    pwp, iport, pptr) == B_FALSE) {
28044c06356bSdh 				pmcs_rele_iport(iport);
28054c06356bSdh 				pmcs_unlock_phy(pptr);
28064c06356bSdh 				return (B_FALSE);
28074c06356bSdh 			}
28084c06356bSdh 		}
28094c06356bSdh 		pmcs_rele_iport(iport);
28104c06356bSdh 		pmcs_unlock_phy(pptr);
28114c06356bSdh 		pptr = pptr->sibling;
28124c06356bSdh 	}
28134c06356bSdh 
28144c06356bSdh 	/*
28154c06356bSdh 	 * The observation is complete, end sets. Note we will skip any
28164c06356bSdh 	 * iports that are active, but have no PHYs in them (i.e. awaiting
28174c06356bSdh 	 * unconfigure). Set to restart discovery if we find this.
28184c06356bSdh 	 */
28194c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
28204c06356bSdh 	for (iport = list_head(&pwp->iports);
28214c06356bSdh 	    iport != NULL;
28224c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
28234c06356bSdh 
28244c06356bSdh 		if (iport->report_skip)
28254c06356bSdh 			continue;		/* skip set_end */
28264c06356bSdh 
28274c06356bSdh 		tgtmap = iport->iss_tgtmap;
28284c06356bSdh 		ASSERT(tgtmap);
28294c06356bSdh 		if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
2830c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
28314c06356bSdh 			    "%s: cannot set_end tgtmap ", __func__);
28324c06356bSdh 			rw_exit(&pwp->iports_lock);
28334c06356bSdh 			return (B_FALSE);
28344c06356bSdh 		}
2835c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
2836c3bc407cSdh 		    "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap);
28374c06356bSdh 	}
28384c06356bSdh 
28394c06356bSdh 	/*
28404c06356bSdh 	 * Now that discovery is complete, set up the necessary
28414c06356bSdh 	 * DDI properties on each iport node.
28424c06356bSdh 	 */
28434c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
28444c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
28454c06356bSdh 		/* Set up the 'attached-port' property on the iport */
28464c06356bSdh 		ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
28474c06356bSdh 		mutex_enter(&iport->lock);
28484c06356bSdh 		pptr = iport->pptr;
28494c06356bSdh 		mutex_exit(&iport->lock);
28504c06356bSdh 		if (pptr == NULL) {
28514c06356bSdh 			/*
28524c06356bSdh 			 * This iport is down, but has not been
28534c06356bSdh 			 * removed from our list (unconfigured).
28544c06356bSdh 			 * Set our value to '0'.
28554c06356bSdh 			 */
28564c06356bSdh 			(void) snprintf(ap, 1, "%s", "0");
28574c06356bSdh 		} else {
28584c06356bSdh 			/* Otherwise, set it to remote phy's wwn */
28594c06356bSdh 			pmcs_lock_phy(pptr);
28604c06356bSdh 			wwn = pmcs_barray2wwn(pptr->sas_address);
28614c06356bSdh 			(void) scsi_wwn_to_wwnstr(wwn, 1, ap);
28624c06356bSdh 			pmcs_unlock_phy(pptr);
28634c06356bSdh 		}
28644c06356bSdh 		if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
2865499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) {
2866c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed "
2867c3bc407cSdh 			    "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
28684c06356bSdh 			    __func__);
28694c06356bSdh 		}
28704c06356bSdh 		kmem_free(ap, PMCS_MAX_UA_SIZE);
28714c06356bSdh 	}
28724c06356bSdh 	rw_exit(&pwp->iports_lock);
28734c06356bSdh 
28744c06356bSdh 	return (B_TRUE);
28754c06356bSdh }
28764c06356bSdh 
28774c06356bSdh /*
28784c06356bSdh  * Report observations into a particular iport's target map
28794c06356bSdh  *
28804c06356bSdh  * Called with phyp (and all descendents) locked
28814c06356bSdh  */
28824c06356bSdh static boolean_t
28834c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
28844c06356bSdh     pmcs_phy_t *phyp)
28854c06356bSdh {
28864c06356bSdh 	pmcs_phy_t		*lphyp;
28874c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
28884c06356bSdh 	scsi_tgtmap_tgt_type_t	tgt_type;
28894c06356bSdh 	char			*ua;
28904c06356bSdh 	uint64_t		wwn;
28914c06356bSdh 
28924c06356bSdh 	tgtmap = iport->iss_tgtmap;
28934c06356bSdh 	ASSERT(tgtmap);
28944c06356bSdh 
28954c06356bSdh 	lphyp = phyp;
28964c06356bSdh 	while (lphyp) {
28974c06356bSdh 		switch (lphyp->dtype) {
28984c06356bSdh 		default:		/* Skip unknown PHYs. */
28994c06356bSdh 			/* for non-root phys, skip to sibling */
29004c06356bSdh 			goto next_phy;
29014c06356bSdh 
29024c06356bSdh 		case SATA:
29034c06356bSdh 		case SAS:
29044c06356bSdh 			tgt_type = SCSI_TGT_SCSI_DEVICE;
29054c06356bSdh 			break;
29064c06356bSdh 
29074c06356bSdh 		case EXPANDER:
29084c06356bSdh 			tgt_type = SCSI_TGT_SMP_DEVICE;
29094c06356bSdh 			break;
29104c06356bSdh 		}
29114c06356bSdh 
29129aed1621SDavid Hollister 		if (lphyp->dead || !lphyp->configured) {
29134c06356bSdh 			goto next_phy;
29144c06356bSdh 		}
29154c06356bSdh 
2916a25672a1SDavid Hollister 		/*
2917a25672a1SDavid Hollister 		 * Validate the PHY's SAS address
2918a25672a1SDavid Hollister 		 */
2919a25672a1SDavid Hollister 		if (((lphyp->sas_address[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
2920a25672a1SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_ERR, lphyp, NULL,
2921a25672a1SDavid Hollister 			    "PHY 0x%p (%s) has invalid SAS address; "
2922a25672a1SDavid Hollister 			    "will not enumerate", (void *)lphyp, lphyp->path);
2923a25672a1SDavid Hollister 			goto next_phy;
2924a25672a1SDavid Hollister 		}
2925a25672a1SDavid Hollister 
29264c06356bSdh 		wwn = pmcs_barray2wwn(lphyp->sas_address);
29274c06356bSdh 		ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
29284c06356bSdh 
2929c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL,
29304c06356bSdh 		    "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
29314c06356bSdh 		    ua, (void *)tgtmap, (void*)lphyp);
29324c06356bSdh 
29334c06356bSdh 		if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
29344c06356bSdh 		    DDI_SUCCESS) {
2935c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,  NULL, NULL,
29364c06356bSdh 			    "%s: failed to add address %s", __func__, ua);
29374c06356bSdh 			scsi_free_wwnstr(ua);
29384c06356bSdh 			return (B_FALSE);
29394c06356bSdh 		}
29404c06356bSdh 		scsi_free_wwnstr(ua);
29414c06356bSdh 
29424c06356bSdh 		if (lphyp->children) {
29434c06356bSdh 			if (pmcs_report_iport_observations(pwp, iport,
29444c06356bSdh 			    lphyp->children) == B_FALSE) {
29454c06356bSdh 				return (B_FALSE);
29464c06356bSdh 			}
29474c06356bSdh 		}
29484c06356bSdh 
29494c06356bSdh 		/* for non-root phys, report siblings too */
29504c06356bSdh next_phy:
29514c06356bSdh 		if (IS_ROOT_PHY(lphyp)) {
29524c06356bSdh 			lphyp = NULL;
29534c06356bSdh 		} else {
29544c06356bSdh 			lphyp = lphyp->sibling;
29554c06356bSdh 		}
29564c06356bSdh 	}
29574c06356bSdh 
29584c06356bSdh 	return (B_TRUE);
29594c06356bSdh }
29604c06356bSdh 
29614c06356bSdh /*
29624c06356bSdh  * Check for and configure new devices.
29634c06356bSdh  *
29644c06356bSdh  * If the changed device is a SATA device, add a SATA device.
29654c06356bSdh  *
29664c06356bSdh  * If the changed device is a SAS device, add a SAS device.
29674c06356bSdh  *
29684c06356bSdh  * If the changed device is an EXPANDER device, do a REPORT
29694c06356bSdh  * GENERAL SMP command to find out the number of contained phys.
29704c06356bSdh  *
29714c06356bSdh  * For each number of contained phys, allocate a phy, do a
29724c06356bSdh  * DISCOVERY SMP command to find out what kind of device it
29734c06356bSdh  * is and add it to the linked list of phys on the *next* level.
29744c06356bSdh  *
29754c06356bSdh  * NOTE: pptr passed in by the caller will be a root PHY
29764c06356bSdh  */
29774c06356bSdh static int
29784c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
29794c06356bSdh {
29804c06356bSdh 	int rval = 0;
29814c06356bSdh 	pmcs_iport_t *iport;
29824c06356bSdh 	pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
29839aed1621SDavid Hollister 	uint64_t wwn;
29844c06356bSdh 
29854c06356bSdh 	/*
29864c06356bSdh 	 * First, walk through each PHY at this level
29874c06356bSdh 	 */
29884c06356bSdh 	while (pptr) {
29894c06356bSdh 		pmcs_lock_phy(pptr);
29904c06356bSdh 		pnext = pptr->sibling;
29914c06356bSdh 
29924c06356bSdh 		/*
29934c06356bSdh 		 * Set the new dtype if it has changed
29944c06356bSdh 		 */
29954c06356bSdh 		if ((pptr->pend_dtype != NEW) &&
29964c06356bSdh 		    (pptr->pend_dtype != pptr->dtype)) {
29974c06356bSdh 			pptr->dtype = pptr->pend_dtype;
29984c06356bSdh 		}
29994c06356bSdh 
30004c06356bSdh 		if (pptr->changed == 0 || pptr->dead || pptr->configured) {
30014c06356bSdh 			goto next_phy;
30024c06356bSdh 		}
30034c06356bSdh 
3004*3be32c0fSJesse Butler 		/* Confirm that this iport is configured */
30054c06356bSdh 		root_phy = pmcs_get_root_phy(pptr);
30069aed1621SDavid Hollister 		wwn = pmcs_barray2wwn(root_phy->sas_address);
30079aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
30089aed1621SDavid Hollister 		iport = pmcs_get_iport_by_wwn(pwp, wwn);
30094c06356bSdh 		if (iport == NULL) {
3010c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
30114c06356bSdh 			    "%s: iport not yet configured, "
30124c06356bSdh 			    "retry discovery", __func__);
30134c06356bSdh 			pnext = NULL;
30144c06356bSdh 			rval = -1;
30159aed1621SDavid Hollister 			pmcs_lock_phy(pptr);
30164c06356bSdh 			goto next_phy;
30174c06356bSdh 		}
30184c06356bSdh 
30199aed1621SDavid Hollister 		pmcs_lock_phy(pptr);
30204c06356bSdh 		switch (pptr->dtype) {
30214c06356bSdh 		case NOTHING:
30224c06356bSdh 			pptr->changed = 0;
30234c06356bSdh 			break;
30244c06356bSdh 		case SATA:
30254c06356bSdh 		case SAS:
30264c06356bSdh 			pptr->iport = iport;
30274c06356bSdh 			pmcs_new_tport(pwp, pptr);
30284c06356bSdh 			break;
30294c06356bSdh 		case EXPANDER:
30304c06356bSdh 			pmcs_configure_expander(pwp, pptr, iport);
30314c06356bSdh 			break;
30324c06356bSdh 		}
30334c06356bSdh 		pmcs_rele_iport(iport);
30344c06356bSdh 
30354c06356bSdh 		mutex_enter(&pwp->config_lock);
30364c06356bSdh 		if (pwp->config_changed) {
30374c06356bSdh 			mutex_exit(&pwp->config_lock);
30384c06356bSdh 			pnext = NULL;
30394c06356bSdh 			goto next_phy;
30404c06356bSdh 		}
30414c06356bSdh 		mutex_exit(&pwp->config_lock);
30424c06356bSdh 
30434c06356bSdh next_phy:
30444c06356bSdh 		pmcs_unlock_phy(pptr);
30454c06356bSdh 		pptr = pnext;
30464c06356bSdh 	}
30474c06356bSdh 
30484c06356bSdh 	if (rval != 0) {
30494c06356bSdh 		return (rval);
30504c06356bSdh 	}
30514c06356bSdh 
30524c06356bSdh 	/*
30534c06356bSdh 	 * Now walk through each PHY again, recalling ourselves if they
30544c06356bSdh 	 * have children
30554c06356bSdh 	 */
30564c06356bSdh 	pptr = orig_pptr;
30574c06356bSdh 	while (pptr) {
30584c06356bSdh 		pmcs_lock_phy(pptr);
30594c06356bSdh 		pnext = pptr->sibling;
30604c06356bSdh 		pchild = pptr->children;
30614c06356bSdh 		pmcs_unlock_phy(pptr);
30624c06356bSdh 
30634c06356bSdh 		if (pchild) {
30644c06356bSdh 			rval = pmcs_configure_new_devices(pwp, pchild);
30654c06356bSdh 			if (rval != 0) {
30664c06356bSdh 				break;
30674c06356bSdh 			}
30684c06356bSdh 		}
30694c06356bSdh 
30704c06356bSdh 		pptr = pnext;
30714c06356bSdh 	}
30724c06356bSdh 
30734c06356bSdh 	return (rval);
30744c06356bSdh }
30754c06356bSdh 
30764c06356bSdh /*
30774c06356bSdh  * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
30784c06356bSdh  * mark them all as not changed.
30794c06356bSdh  *
30804c06356bSdh  * Called with parent PHY locked.
30814c06356bSdh  */
30824c06356bSdh void
30834c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
30844c06356bSdh     int level)
30854c06356bSdh {
30864c06356bSdh 	pmcs_phy_t *pptr;
30874c06356bSdh 
30884c06356bSdh 	if (level == 0) {
30894c06356bSdh 		if (changed) {
30904c06356bSdh 			PHY_CHANGED(pwp, parent);
30914c06356bSdh 		} else {
30924c06356bSdh 			parent->changed = 0;
30934c06356bSdh 		}
30944c06356bSdh 		if (parent->dtype == EXPANDER && parent->level) {
30954c06356bSdh 			parent->width = 1;
30964c06356bSdh 		}
30974c06356bSdh 		if (parent->children) {
30984c06356bSdh 			pmcs_set_changed(pwp, parent->children, changed,
30994c06356bSdh 			    level + 1);
31004c06356bSdh 		}
31014c06356bSdh 	} else {
31024c06356bSdh 		pptr = parent;
31034c06356bSdh 		while (pptr) {
31044c06356bSdh 			if (changed) {
31054c06356bSdh 				PHY_CHANGED(pwp, pptr);
31064c06356bSdh 			} else {
31074c06356bSdh 				pptr->changed = 0;
31084c06356bSdh 			}
31094c06356bSdh 			if (pptr->dtype == EXPANDER && pptr->level) {
31104c06356bSdh 				pptr->width = 1;
31114c06356bSdh 			}
31124c06356bSdh 			if (pptr->children) {
31134c06356bSdh 				pmcs_set_changed(pwp, pptr->children, changed,
31144c06356bSdh 				    level + 1);
31154c06356bSdh 			}
31164c06356bSdh 			pptr = pptr->sibling;
31174c06356bSdh 		}
31184c06356bSdh 	}
31194c06356bSdh }
31204c06356bSdh 
31214c06356bSdh /*
31224c06356bSdh  * Take the passed phy mark it and its descendants as dead.
31234c06356bSdh  * Fire up reconfiguration to abort commands and bury it.
31244c06356bSdh  *
31254c06356bSdh  * Called with the parent PHY locked.
31264c06356bSdh  */
31274c06356bSdh void
31284c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
31294c06356bSdh {
31304c06356bSdh 	pmcs_phy_t *pptr = parent;
31314c06356bSdh 
31324c06356bSdh 	while (pptr) {
31334c06356bSdh 		pptr->link_rate = 0;
31344c06356bSdh 		pptr->abort_sent = 0;
31354c06356bSdh 		pptr->abort_pending = 1;
31364c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
31374c06356bSdh 		pptr->need_rl_ext = 0;
31384c06356bSdh 
31394c06356bSdh 		if (pptr->dead == 0) {
31404c06356bSdh 			PHY_CHANGED(pwp, pptr);
31414c06356bSdh 			RESTART_DISCOVERY(pwp);
31424c06356bSdh 		}
31434c06356bSdh 
31444c06356bSdh 		pptr->dead = 1;
31454c06356bSdh 
31464c06356bSdh 		if (pptr->children) {
31474c06356bSdh 			pmcs_kill_changed(pwp, pptr->children, level + 1);
31484c06356bSdh 		}
31494c06356bSdh 
31504c06356bSdh 		/*
31514c06356bSdh 		 * Only kill siblings at level > 0
31524c06356bSdh 		 */
31534c06356bSdh 		if (level == 0) {
31544c06356bSdh 			return;
31554c06356bSdh 		}
31564c06356bSdh 
31574c06356bSdh 		pptr = pptr->sibling;
31584c06356bSdh 	}
31594c06356bSdh }
31604c06356bSdh 
31614c06356bSdh /*
31624c06356bSdh  * Go through every PHY and clear any that are dead (unless they're expanders)
31634c06356bSdh  */
31644c06356bSdh static void
31654c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
31664c06356bSdh {
31674c06356bSdh 	pmcs_phy_t *pnext, *phyp;
31684c06356bSdh 
31694c06356bSdh 	phyp = pptr;
31704c06356bSdh 	while (phyp) {
31714c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
31724c06356bSdh 			pmcs_lock_phy(phyp);
31734c06356bSdh 		}
31744c06356bSdh 
31754c06356bSdh 		if ((phyp->dtype != EXPANDER) && phyp->dead) {
31764c06356bSdh 			pmcs_clear_phy(pwp, phyp);
31774c06356bSdh 		}
31784c06356bSdh 
31794c06356bSdh 		if (phyp->children) {
31804c06356bSdh 			pmcs_clear_phys(pwp, phyp->children);
31814c06356bSdh 		}
31824c06356bSdh 
31834c06356bSdh 		pnext = phyp->sibling;
31844c06356bSdh 
31854c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
31864c06356bSdh 			pmcs_unlock_phy(phyp);
31874c06356bSdh 		}
31884c06356bSdh 
31894c06356bSdh 		phyp = pnext;
31904c06356bSdh 	}
31914c06356bSdh }
31924c06356bSdh 
31934c06356bSdh /*
31944c06356bSdh  * Clear volatile parts of a phy.  Called with PHY locked.
31954c06356bSdh  */
31964c06356bSdh void
31974c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
31984c06356bSdh {
3199c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s",
3200c3bc407cSdh 	    __func__, pptr->path);
32014c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
32024c06356bSdh 	/* keep sibling */
32034c06356bSdh 	/* keep children */
32044c06356bSdh 	/* keep parent */
32054c06356bSdh 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
32064c06356bSdh 	/* keep hw_event_ack */
32074c06356bSdh 	pptr->ncphy = 0;
32084c06356bSdh 	/* keep phynum */
32094c06356bSdh 	pptr->width = 0;
32104c06356bSdh 	pptr->ds_recovery_retries = 0;
3211af685682SSrikanth, Ramana 	pptr->ds_prev_good_recoveries = 0;
3212af685682SSrikanth, Ramana 	pptr->last_good_recovery = 0;
3213af685682SSrikanth, Ramana 	pptr->prev_recovery = 0;
3214145e0143Sdh 
32154c06356bSdh 	/* keep dtype */
32164c06356bSdh 	pptr->config_stop = 0;
32174c06356bSdh 	pptr->spinup_hold = 0;
32184c06356bSdh 	pptr->atdt = 0;
32194c06356bSdh 	/* keep portid */
32204c06356bSdh 	pptr->link_rate = 0;
32214c06356bSdh 	pptr->valid_device_id = 0;
32224c06356bSdh 	pptr->abort_sent = 0;
32234c06356bSdh 	pptr->abort_pending = 0;
32244c06356bSdh 	pptr->need_rl_ext = 0;
32254c06356bSdh 	pptr->subsidiary = 0;
32264c06356bSdh 	pptr->configured = 0;
3227601c90f1SSrikanth, Ramana 	pptr->deregister_wait = 0;
32289aed1621SDavid Hollister 	pptr->reenumerate = 0;
32294c06356bSdh 	/* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
32304c06356bSdh 	/* XXX: What about directly attached disks? */
32314c06356bSdh 	if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
32324c06356bSdh 		pptr->dead = 1;
32334c06356bSdh 	pptr->changed = 0;
32344c06356bSdh 	/* keep SAS address */
32354c06356bSdh 	/* keep path */
32364c06356bSdh 	/* keep ref_count */
32374c06356bSdh 	/* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
32384c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
323973a3eccdSDavid Hollister 		pptr->last_iport = pptr->iport;
32404c06356bSdh 		pptr->iport = NULL;
32414c06356bSdh 	}
3242b18a19c2SJesse Butler 	/* keep target */
32434c06356bSdh }
32444c06356bSdh 
32454c06356bSdh /*
32464c06356bSdh  * Allocate softstate for this target if there isn't already one.  If there
32474c06356bSdh  * is, just redo our internal configuration.  If it is actually "new", we'll
32484c06356bSdh  * soon get a tran_tgt_init for it.
32494c06356bSdh  *
32504c06356bSdh  * Called with PHY locked.
32514c06356bSdh  */
32524c06356bSdh static void
32534c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
32544c06356bSdh {
3255c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s",
3256c3bc407cSdh 	    __func__, (void *)pptr, pptr->path);
32574c06356bSdh 
32584c06356bSdh 	if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
32594c06356bSdh 		/*
32604c06356bSdh 		 * If the config failed, mark the PHY as changed.
32614c06356bSdh 		 */
32624c06356bSdh 		PHY_CHANGED(pwp, pptr);
3263c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
32644c06356bSdh 		    "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
32654c06356bSdh 		    (void *)pptr);
32664c06356bSdh 		return;
32674c06356bSdh 	}
32684c06356bSdh 
32694c06356bSdh 	/* Mark PHY as no longer changed */
32704c06356bSdh 	pptr->changed = 0;
32714c06356bSdh 
32724c06356bSdh 	/*
327373a3eccdSDavid Hollister 	 * If the PHY has no target pointer:
327473a3eccdSDavid Hollister 	 *
327573a3eccdSDavid Hollister 	 * If it's a root PHY, see if another PHY in the iport holds the
327673a3eccdSDavid Hollister 	 * target pointer (primary PHY changed).  If so, move it over.
327773a3eccdSDavid Hollister 	 *
327873a3eccdSDavid Hollister 	 * If it's not a root PHY, see if there's a PHY on the dead_phys
327973a3eccdSDavid Hollister 	 * list that matches.
32804c06356bSdh 	 */
32814c06356bSdh 	if (pptr->target == NULL) {
328273a3eccdSDavid Hollister 		if (IS_ROOT_PHY(pptr)) {
328373a3eccdSDavid Hollister 			pmcs_phy_t *rphy = pwp->root_phys;
328473a3eccdSDavid Hollister 
328573a3eccdSDavid Hollister 			while (rphy) {
328673a3eccdSDavid Hollister 				if (rphy == pptr) {
328773a3eccdSDavid Hollister 					rphy = rphy->sibling;
328873a3eccdSDavid Hollister 					continue;
328973a3eccdSDavid Hollister 				}
329073a3eccdSDavid Hollister 
329173a3eccdSDavid Hollister 				mutex_enter(&rphy->phy_lock);
329273a3eccdSDavid Hollister 				if ((rphy->iport == pptr->iport) &&
329373a3eccdSDavid Hollister 				    (rphy->target != NULL)) {
329473a3eccdSDavid Hollister 					mutex_enter(&rphy->target->statlock);
329573a3eccdSDavid Hollister 					pptr->target = rphy->target;
329673a3eccdSDavid Hollister 					rphy->target = NULL;
329773a3eccdSDavid Hollister 					pptr->target->phy = pptr;
329873a3eccdSDavid Hollister 					/* The target is now on pptr */
329973a3eccdSDavid Hollister 					mutex_exit(&pptr->target->statlock);
330073a3eccdSDavid Hollister 					mutex_exit(&rphy->phy_lock);
330173a3eccdSDavid Hollister 					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
330273a3eccdSDavid Hollister 					    pptr, pptr->target,
330373a3eccdSDavid Hollister 					    "%s: Moved target from %s to %s",
330473a3eccdSDavid Hollister 					    __func__, rphy->path, pptr->path);
330573a3eccdSDavid Hollister 					break;
330673a3eccdSDavid Hollister 				}
330773a3eccdSDavid Hollister 				mutex_exit(&rphy->phy_lock);
330873a3eccdSDavid Hollister 
330973a3eccdSDavid Hollister 				rphy = rphy->sibling;
331073a3eccdSDavid Hollister 			}
331173a3eccdSDavid Hollister 		} else {
331273a3eccdSDavid Hollister 			pmcs_reap_dead_phy(pptr);
331373a3eccdSDavid Hollister 		}
33144c06356bSdh 	}
33154c06356bSdh 
33164c06356bSdh 	/*
33174c06356bSdh 	 * Only assign the device if there is a target for this PHY with a
33184c06356bSdh 	 * matching SAS address.  If an iport is disconnected from one piece
33194c06356bSdh 	 * of storage and connected to another within the iport stabilization
33204c06356bSdh 	 * time, we can get the PHY/target mismatch situation.
33214c06356bSdh 	 *
33224c06356bSdh 	 * Otherwise, it'll get done in tran_tgt_init.
33234c06356bSdh 	 */
33244c06356bSdh 	if (pptr->target) {
33254c06356bSdh 		mutex_enter(&pptr->target->statlock);
33264c06356bSdh 		if (pmcs_phy_target_match(pptr) == B_FALSE) {
33274c06356bSdh 			mutex_exit(&pptr->target->statlock);
33284c06356bSdh 			if (!IS_ROOT_PHY(pptr)) {
33294c06356bSdh 				pmcs_dec_phy_ref_count(pptr);
33304c06356bSdh 			}
3331c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
33324c06356bSdh 			    "%s: Not assigning existing tgt %p for PHY %p "
33334c06356bSdh 			    "(WWN mismatch)", __func__, (void *)pptr->target,
33344c06356bSdh 			    (void *)pptr);
33354c06356bSdh 			pptr->target = NULL;
33364c06356bSdh 			return;
33374c06356bSdh 		}
33384c06356bSdh 
33394c06356bSdh 		if (!pmcs_assign_device(pwp, pptr->target)) {
3340c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
33414c06356bSdh 			    "%s: pmcs_assign_device failed for target 0x%p",
33424c06356bSdh 			    __func__, (void *)pptr->target);
33434c06356bSdh 		}
33444c06356bSdh 		mutex_exit(&pptr->target->statlock);
33454c06356bSdh 	}
33464c06356bSdh }
33474c06356bSdh 
33484c06356bSdh /*
33494c06356bSdh  * Called with PHY lock held.
33504c06356bSdh  */
33514c06356bSdh static boolean_t
33524c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
33534c06356bSdh {
33544c06356bSdh 	char *dtype;
33554c06356bSdh 
33564c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
33574c06356bSdh 
33584c06356bSdh 	/*
33594c06356bSdh 	 * Mark this device as no longer changed.
33604c06356bSdh 	 */
33614c06356bSdh 	pptr->changed = 0;
33624c06356bSdh 
33634c06356bSdh 	/*
33644c06356bSdh 	 * If we don't have a device handle, get one.
33654c06356bSdh 	 */
33664c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
33674c06356bSdh 		return (B_FALSE);
33684c06356bSdh 	}
33694c06356bSdh 
33704c06356bSdh 	pptr->configured = 1;
33714c06356bSdh 
33724c06356bSdh 	switch (pptr->dtype) {
33734c06356bSdh 	case SAS:
33744c06356bSdh 		dtype = "SAS";
33754c06356bSdh 		break;
33764c06356bSdh 	case SATA:
33774c06356bSdh 		dtype = "SATA";
33784c06356bSdh 		break;
33794c06356bSdh 	case EXPANDER:
33804c06356bSdh 		dtype = "SMP";
33814c06356bSdh 		break;
33824c06356bSdh 	default:
33834c06356bSdh 		dtype = "???";
33844c06356bSdh 	}
33854c06356bSdh 
3386c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s "
3387c3bc407cSdh 	    "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
33884c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
33894c06356bSdh 
33904c06356bSdh 	return (B_TRUE);
33914c06356bSdh }
33924c06356bSdh 
33934c06356bSdh /*
33944c06356bSdh  * Called with PHY locked
33954c06356bSdh  */
33964c06356bSdh static void
33974c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
33984c06356bSdh {
33994c06356bSdh 	pmcs_phy_t *ctmp, *clist = NULL, *cnext;
34004c06356bSdh 	int result, i, nphy = 0;
34014c06356bSdh 	boolean_t root_phy = B_FALSE;
34024c06356bSdh 
34034c06356bSdh 	ASSERT(iport);
34044c06356bSdh 
34054c06356bSdh 	/*
34064c06356bSdh 	 * Step 1- clear our "changed" bit. If we need to retry/restart due
34074c06356bSdh 	 * to resource shortages, we'll set it again. While we're doing
34084c06356bSdh 	 * configuration, other events may set it again as well.  If the PHY
34094c06356bSdh 	 * is a root PHY and is currently marked as having changed, reset the
34104c06356bSdh 	 * config_stop timer as well.
34114c06356bSdh 	 */
34124c06356bSdh 	if (IS_ROOT_PHY(pptr) && pptr->changed) {
34134c06356bSdh 		pptr->config_stop = ddi_get_lbolt() +
34144c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
34154c06356bSdh 	}
34164c06356bSdh 	pptr->changed = 0;
34174c06356bSdh 
34184c06356bSdh 	/*
34194c06356bSdh 	 * Step 2- make sure we don't overflow
34204c06356bSdh 	 */
34214c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
3422c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL,
34234c06356bSdh 		    "%s: SAS expansion tree too deep", __func__);
34244c06356bSdh 		return;
34254c06356bSdh 	}
34264c06356bSdh 
34274c06356bSdh 	/*
34284c06356bSdh 	 * Step 3- Check if this expander is part of a wide phy that has
34294c06356bSdh 	 * already been configured.
34304c06356bSdh 	 *
34314c06356bSdh 	 * This is known by checking this level for another EXPANDER device
34324c06356bSdh 	 * with the same SAS address and isn't already marked as a subsidiary
34334c06356bSdh 	 * phy and a parent whose SAS address is the same as our SAS address
34344c06356bSdh 	 * (if there are parents).
34354c06356bSdh 	 */
34364c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
34374c06356bSdh 		/*
34384c06356bSdh 		 * No need to lock the parent here because we're in discovery
34394c06356bSdh 		 * and the only time a PHY's children pointer can change is
34404c06356bSdh 		 * in discovery; either in pmcs_clear_expander (which has
34414c06356bSdh 		 * already been called) or here, down below.  Plus, trying to
34424c06356bSdh 		 * grab the parent's lock here can cause deadlock.
34434c06356bSdh 		 */
34444c06356bSdh 		ctmp = pptr->parent->children;
34454c06356bSdh 	} else {
34464c06356bSdh 		ctmp = pwp->root_phys;
34474c06356bSdh 		root_phy = B_TRUE;
34484c06356bSdh 	}
34494c06356bSdh 
34504c06356bSdh 	while (ctmp) {
34514c06356bSdh 		/*
34524c06356bSdh 		 * If we've checked all PHYs up to pptr, we stop. Otherwise,
34534c06356bSdh 		 * we'll be checking for a primary PHY with a higher PHY
34544c06356bSdh 		 * number than pptr, which will never happen.  The primary
34554c06356bSdh 		 * PHY on non-root expanders will ALWAYS be the lowest
34564c06356bSdh 		 * numbered PHY.
34574c06356bSdh 		 */
34584c06356bSdh 		if (ctmp == pptr) {
34594c06356bSdh 			break;
34604c06356bSdh 		}
34614c06356bSdh 
34624c06356bSdh 		/*
34634c06356bSdh 		 * If pptr and ctmp are root PHYs, just grab the mutex on
34644c06356bSdh 		 * ctmp.  No need to lock the entire tree.  If they are not
34654c06356bSdh 		 * root PHYs, there is no need to lock since a non-root PHY's
34664c06356bSdh 		 * SAS address and other characteristics can only change in
34674c06356bSdh 		 * discovery anyway.
34684c06356bSdh 		 */
34694c06356bSdh 		if (root_phy) {
34704c06356bSdh 			mutex_enter(&ctmp->phy_lock);
34714c06356bSdh 		}
34724c06356bSdh 
34734c06356bSdh 		if (ctmp->dtype == EXPANDER && ctmp->width &&
34744c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
34754c06356bSdh 			int widephy = 0;
34764c06356bSdh 			/*
34774c06356bSdh 			 * If these phys are not root PHYs, compare their SAS
34784c06356bSdh 			 * addresses too.
34794c06356bSdh 			 */
34804c06356bSdh 			if (!root_phy) {
34814c06356bSdh 				if (memcmp(ctmp->parent->sas_address,
34824c06356bSdh 				    pptr->parent->sas_address, 8) == 0) {
34834c06356bSdh 					widephy = 1;
34844c06356bSdh 				}
34854c06356bSdh 			} else {
34864c06356bSdh 				widephy = 1;
34874c06356bSdh 			}
34884c06356bSdh 			if (widephy) {
34894c06356bSdh 				ctmp->width++;
34904c06356bSdh 				pptr->subsidiary = 1;
3491499cfd15SDavid Hollister 
3492499cfd15SDavid Hollister 				/*
3493499cfd15SDavid Hollister 				 * Update the primary PHY's attached-port-pm
3494499cfd15SDavid Hollister 				 * and target-port-pm information with the info
3495499cfd15SDavid Hollister 				 * from this subsidiary
3496499cfd15SDavid Hollister 				 */
3497499cfd15SDavid Hollister 				pmcs_update_phy_pm_props(ctmp,
3498499cfd15SDavid Hollister 				    pptr->att_port_pm_tmp,
3499499cfd15SDavid Hollister 				    pptr->tgt_port_pm_tmp, B_TRUE);
3500499cfd15SDavid Hollister 
3501c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
3502c3bc407cSdh 				    "%s: PHY %s part of wide PHY %s "
3503c3bc407cSdh 				    "(now %d wide)", __func__, pptr->path,
3504c3bc407cSdh 				    ctmp->path, ctmp->width);
35054c06356bSdh 				if (root_phy) {
35064c06356bSdh 					mutex_exit(&ctmp->phy_lock);
35074c06356bSdh 				}
35084c06356bSdh 				return;
35094c06356bSdh 			}
35104c06356bSdh 		}
35114c06356bSdh 
35124c06356bSdh 		cnext = ctmp->sibling;
35134c06356bSdh 		if (root_phy) {
35144c06356bSdh 			mutex_exit(&ctmp->phy_lock);
35154c06356bSdh 		}
35164c06356bSdh 		ctmp = cnext;
35174c06356bSdh 	}
35184c06356bSdh 
35194c06356bSdh 	/*
35204c06356bSdh 	 * Step 4- If we don't have a device handle, get one.  Since this
35214c06356bSdh 	 * is the primary PHY, make sure subsidiary is cleared.
35224c06356bSdh 	 */
35234c06356bSdh 	pptr->subsidiary = 0;
35246745c559SJesse Butler 	pptr->iport = iport;
35254c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
35264c06356bSdh 		goto out;
35274c06356bSdh 	}
3528c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s "
35294c06356bSdh 	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
35304c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
35314c06356bSdh 
35324c06356bSdh 	/*
35334c06356bSdh 	 * Step 5- figure out how many phys are in this expander.
35344c06356bSdh 	 */
35354c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
35364c06356bSdh 	if (nphy <= 0) {
35374c06356bSdh 		if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
35384c06356bSdh 			PHY_CHANGED(pwp, pptr);
35394c06356bSdh 			RESTART_DISCOVERY(pwp);
35404c06356bSdh 		} else {
3541c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
35424c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
35434c06356bSdh 			    pptr->path);
35444c06356bSdh 			pptr->config_stop = 0;
35454c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
35464c06356bSdh 		}
35474c06356bSdh 		goto out;
35484c06356bSdh 	}
35494c06356bSdh 
35504c06356bSdh 	/*
35514c06356bSdh 	 * Step 6- Allocate a list of phys for this expander and figure out
35524c06356bSdh 	 * what each one is.
35534c06356bSdh 	 */
35544c06356bSdh 	for (i = 0; i < nphy; i++) {
35554c06356bSdh 		ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
35564c06356bSdh 		bzero(ctmp, sizeof (pmcs_phy_t));
35574c06356bSdh 		ctmp->device_id = PMCS_INVALID_DEVICE_ID;
35584c06356bSdh 		ctmp->sibling = clist;
35594c06356bSdh 		ctmp->pend_dtype = NEW;	/* Init pending dtype */
35604c06356bSdh 		ctmp->config_stop = ddi_get_lbolt() +
35614c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
35624c06356bSdh 		clist = ctmp;
35634c06356bSdh 	}
35644c06356bSdh 
35654c06356bSdh 	mutex_enter(&pwp->config_lock);
35664c06356bSdh 	if (pwp->config_changed) {
35674c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
35684c06356bSdh 		mutex_exit(&pwp->config_lock);
35694c06356bSdh 		/*
35704c06356bSdh 		 * Clean up the newly allocated PHYs and return
35714c06356bSdh 		 */
35724c06356bSdh 		while (clist) {
35734c06356bSdh 			ctmp = clist->sibling;
35744c06356bSdh 			kmem_cache_free(pwp->phy_cache, clist);
35754c06356bSdh 			clist = ctmp;
35764c06356bSdh 		}
35774c06356bSdh 		return;
35784c06356bSdh 	}
35794c06356bSdh 	mutex_exit(&pwp->config_lock);
35804c06356bSdh 
35814c06356bSdh 	/*
35824c06356bSdh 	 * Step 7- Now fill in the rest of the static portions of the phy.
35834c06356bSdh 	 */
35844c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
35854c06356bSdh 		ctmp->parent = pptr;
35864c06356bSdh 		ctmp->pwp = pwp;
35874c06356bSdh 		ctmp->level = pptr->level+1;
35884c06356bSdh 		ctmp->portid = pptr->portid;
35894c06356bSdh 		if (ctmp->tolerates_sas2) {
35904c06356bSdh 			ASSERT(i < SAS2_PHYNUM_MAX);
35914c06356bSdh 			ctmp->phynum = i & SAS2_PHYNUM_MASK;
35924c06356bSdh 		} else {
35934c06356bSdh 			ASSERT(i < SAS_PHYNUM_MAX);
35944c06356bSdh 			ctmp->phynum = i & SAS_PHYNUM_MASK;
35954c06356bSdh 		}
35964c06356bSdh 		pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
35974c06356bSdh 		pmcs_lock_phy(ctmp);
35984c06356bSdh 	}
35994c06356bSdh 
36004c06356bSdh 	/*
36014c06356bSdh 	 * Step 8- Discover things about each phy in the expander.
36024c06356bSdh 	 */
36034c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
36044c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, ctmp);
36054c06356bSdh 		if (result <= 0) {
36064c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
36074c06356bSdh 				PHY_CHANGED(pwp, pptr);
36084c06356bSdh 				RESTART_DISCOVERY(pwp);
36094c06356bSdh 			} else {
36104c06356bSdh 				pptr->config_stop = 0;
3611c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
36124c06356bSdh 				    "%s: Retries exhausted for %s, killing",
36134c06356bSdh 				    __func__, pptr->path);
36144c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
36154c06356bSdh 			}
36164c06356bSdh 			goto out;
36174c06356bSdh 		}
36184c06356bSdh 
36194c06356bSdh 		/* Set pend_dtype to dtype for 1st time initialization */
36204c06356bSdh 		ctmp->pend_dtype = ctmp->dtype;
36214c06356bSdh 	}
36224c06356bSdh 
36234c06356bSdh 	/*
36249aed1621SDavid Hollister 	 * Step 9: Install the new list on the next level. There should
36259aed1621SDavid Hollister 	 * typically be no children pointer on this PHY.  There is one known
36269aed1621SDavid Hollister 	 * case where this can happen, though.  If a root PHY goes down and
36279aed1621SDavid Hollister 	 * comes back up before discovery can run, we will fail to remove the
36289aed1621SDavid Hollister 	 * children from that PHY since it will no longer be marked dead.
36299aed1621SDavid Hollister 	 * However, in this case, all children should also be marked dead.  If
36309aed1621SDavid Hollister 	 * we see that, take those children and put them on the dead_phys list.
36314c06356bSdh 	 */
36324c06356bSdh 	if (pptr->children != NULL) {
36339aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
36349aed1621SDavid Hollister 		    "%s: Expander @ %s still has children: Clean up",
3635c3bc407cSdh 		    __func__, pptr->path);
36369aed1621SDavid Hollister 		pmcs_add_dead_phys(pwp, pptr->children);
36374c06356bSdh 	}
36384c06356bSdh 
36399aed1621SDavid Hollister 	/*
36409aed1621SDavid Hollister 	 * Set the new children pointer for this expander
36419aed1621SDavid Hollister 	 */
36429aed1621SDavid Hollister 	pptr->children = clist;
36434c06356bSdh 	clist = NULL;
36444c06356bSdh 	pptr->ncphy = nphy;
36454c06356bSdh 	pptr->configured = 1;
36464c06356bSdh 
36474c06356bSdh 	/*
36484c06356bSdh 	 * We only set width if we're greater than level 0.
36494c06356bSdh 	 */
36504c06356bSdh 	if (pptr->level) {
36514c06356bSdh 		pptr->width = 1;
36524c06356bSdh 	}
36534c06356bSdh 
36544c06356bSdh 	/*
36554c06356bSdh 	 * Now tell the rest of the world about us, as an SMP node.
36564c06356bSdh 	 */
36574c06356bSdh 	pptr->iport = iport;
36584c06356bSdh 	pmcs_new_tport(pwp, pptr);
36594c06356bSdh 
36604c06356bSdh out:
36614c06356bSdh 	while (clist) {
36624c06356bSdh 		ctmp = clist->sibling;
36634c06356bSdh 		pmcs_unlock_phy(clist);
36644c06356bSdh 		kmem_cache_free(pwp->phy_cache, clist);
36654c06356bSdh 		clist = ctmp;
36664c06356bSdh 	}
36674c06356bSdh }
36684c06356bSdh 
36694c06356bSdh /*
36704c06356bSdh  * 2. Check expanders marked changed (but not dead) to see if they still have
36714c06356bSdh  * the same number of phys and the same SAS address. Mark them, their subsidiary
36724c06356bSdh  * phys (if wide) and their descendents dead if anything has changed. Check the
36734c06356bSdh  * the devices they contain to see if *they* have changed. If they've changed
36744c06356bSdh  * from type NOTHING we leave them marked changed to be configured later
36754c06356bSdh  * (picking up a new SAS address and link rate if possible). Otherwise, any
36764c06356bSdh  * change in type, SAS address or removal of target role will cause us to
36774c06356bSdh  * mark them (and their descendents) as dead and cause any pending commands
36784c06356bSdh  * and associated devices to be removed.
36794c06356bSdh  *
36804c06356bSdh  * Called with PHY (pptr) locked.
36814c06356bSdh  */
36824c06356bSdh 
36834c06356bSdh static void
36844c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
36854c06356bSdh {
36864c06356bSdh 	int nphy, result;
36874c06356bSdh 	pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
36884c06356bSdh 	boolean_t kill_changed, changed;
36894c06356bSdh 
3690c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
36914c06356bSdh 	    "%s: check %s", __func__, pptr->path);
36924c06356bSdh 
36934c06356bSdh 	/*
36944c06356bSdh 	 * Step 1: Mark phy as not changed. We will mark it changed if we need
36954c06356bSdh 	 * to retry.
36964c06356bSdh 	 */
36974c06356bSdh 	pptr->changed = 0;
36984c06356bSdh 
36994c06356bSdh 	/*
37004c06356bSdh 	 * Reset the config_stop time. Although we're not actually configuring
37014c06356bSdh 	 * anything here, we do want some indication of when to give up trying
37024c06356bSdh 	 * if we can't communicate with the expander.
37034c06356bSdh 	 */
37044c06356bSdh 	pptr->config_stop = ddi_get_lbolt() +
37054c06356bSdh 	    drv_usectohz(PMCS_MAX_CONFIG_TIME);
37064c06356bSdh 
37074c06356bSdh 	/*
37084c06356bSdh 	 * Step 2: Figure out how many phys are in this expander. If
37094c06356bSdh 	 * pmcs_expander_get_nphy returns 0 we ran out of resources,
37104c06356bSdh 	 * so reschedule and try later. If it returns another error,
37114c06356bSdh 	 * just return.
37124c06356bSdh 	 */
37134c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
37144c06356bSdh 	if (nphy <= 0) {
37154c06356bSdh 		if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
37164c06356bSdh 			PHY_CHANGED(pwp, pptr);
37174c06356bSdh 			RESTART_DISCOVERY(pwp);
37184c06356bSdh 		} else {
37194c06356bSdh 			pptr->config_stop = 0;
3720c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37214c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
37224c06356bSdh 			    pptr->path);
37234c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
37244c06356bSdh 		}
37254c06356bSdh 		return;
37264c06356bSdh 	}
37274c06356bSdh 
37284c06356bSdh 	/*
37294c06356bSdh 	 * Step 3: If the number of phys don't agree, kill the old sub-tree.
37304c06356bSdh 	 */
37314c06356bSdh 	if (nphy != pptr->ncphy) {
3732c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37334c06356bSdh 		    "%s: number of contained phys for %s changed from %d to %d",
37344c06356bSdh 		    __func__, pptr->path, pptr->ncphy, nphy);
37354c06356bSdh 		/*
37364c06356bSdh 		 * Force a rescan of this expander after dead contents
37374c06356bSdh 		 * are cleared and removed.
37384c06356bSdh 		 */
37394c06356bSdh 		pmcs_kill_changed(pwp, pptr, 0);
37404c06356bSdh 		return;
37414c06356bSdh 	}
37424c06356bSdh 
37434c06356bSdh 	/*
37444c06356bSdh 	 * Step 4: if we're at the bottom of the stack, we're done
37454c06356bSdh 	 * (we can't have any levels below us)
37464c06356bSdh 	 */
37474c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
37484c06356bSdh 		return;
37494c06356bSdh 	}
37504c06356bSdh 
37514c06356bSdh 	/*
37524c06356bSdh 	 * Step 5: Discover things about each phy in this expander.  We do
37534c06356bSdh 	 * this by walking the current list of contained phys and doing a
37544c06356bSdh 	 * content discovery for it to a local phy.
37554c06356bSdh 	 */
37564c06356bSdh 	ctmp = pptr->children;
37574c06356bSdh 	ASSERT(ctmp);
37584c06356bSdh 	if (ctmp == NULL) {
3759c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37604c06356bSdh 		    "%s: No children attached to expander @ %s?", __func__,
37614c06356bSdh 		    pptr->path);
37624c06356bSdh 		return;
37634c06356bSdh 	}
37644c06356bSdh 
37654c06356bSdh 	while (ctmp) {
37664c06356bSdh 		/*
37674c06356bSdh 		 * Allocate a local PHY to contain the proposed new contents
37684c06356bSdh 		 * and link it to the rest of the local PHYs so that they
37694c06356bSdh 		 * can all be freed later.
37704c06356bSdh 		 */
37714c06356bSdh 		local = pmcs_clone_phy(ctmp);
37724c06356bSdh 
37734c06356bSdh 		if (local_list == NULL) {
37744c06356bSdh 			local_list = local;
37754c06356bSdh 			local_tail = local;
37764c06356bSdh 		} else {
37774c06356bSdh 			local_tail->sibling = local;
37784c06356bSdh 			local_tail = local;
37794c06356bSdh 		}
37804c06356bSdh 
37814c06356bSdh 		/*
37824c06356bSdh 		 * Need to lock the local PHY since pmcs_expander_content_
37834c06356bSdh 		 * discovery may call pmcs_clear_phy on it, which expects
37844c06356bSdh 		 * the PHY to be locked.
37854c06356bSdh 		 */
37864c06356bSdh 		pmcs_lock_phy(local);
37874c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, local);
37884c06356bSdh 		pmcs_unlock_phy(local);
37894c06356bSdh 		if (result <= 0) {
37904c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
37914c06356bSdh 				PHY_CHANGED(pwp, pptr);
37924c06356bSdh 				RESTART_DISCOVERY(pwp);
37934c06356bSdh 			} else {
37944c06356bSdh 				pptr->config_stop = 0;
3795c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37964c06356bSdh 				    "%s: Retries exhausted for %s, killing",
37974c06356bSdh 				    __func__, pptr->path);
37984c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
37994c06356bSdh 			}
38004c06356bSdh 
38014c06356bSdh 			/*
38024c06356bSdh 			 * Release all the local PHYs that we allocated.
38034c06356bSdh 			 */
38044c06356bSdh 			pmcs_free_phys(pwp, local_list);
38054c06356bSdh 			return;
38064c06356bSdh 		}
38074c06356bSdh 
38084c06356bSdh 		ctmp = ctmp->sibling;
38094c06356bSdh 	}
38104c06356bSdh 
38114c06356bSdh 	/*
38124c06356bSdh 	 * Step 6: Compare the local PHY's contents to our current PHY.  If
38134c06356bSdh 	 * there are changes, take the appropriate action.
38144c06356bSdh 	 * This is done in two steps (step 5 above, and 6 here) so that if we
38154c06356bSdh 	 * have to bail during this process (e.g. pmcs_expander_content_discover
38164c06356bSdh 	 * fails), we haven't actually changed the state of any of the real
38174c06356bSdh 	 * PHYs.  Next time we come through here, we'll be starting over from
38184c06356bSdh 	 * scratch.  This keeps us from marking a changed PHY as no longer
38194c06356bSdh 	 * changed, but then having to bail only to come back next time and
38204c06356bSdh 	 * think that the PHY hadn't changed.  If this were to happen, we
38214c06356bSdh 	 * would fail to properly configure the device behind this PHY.
38224c06356bSdh 	 */
38234c06356bSdh 	local = local_list;
38244c06356bSdh 	ctmp = pptr->children;
38254c06356bSdh 
38264c06356bSdh 	while (ctmp) {
38274c06356bSdh 		changed = B_FALSE;
38284c06356bSdh 		kill_changed = B_FALSE;
38294c06356bSdh 
38304c06356bSdh 		/*
38314c06356bSdh 		 * We set local to local_list prior to this loop so that we
38324c06356bSdh 		 * can simply walk the local_list while we walk this list.  The
38334c06356bSdh 		 * two lists should be completely in sync.
38344c06356bSdh 		 *
38354c06356bSdh 		 * Clear the changed flag here.
38364c06356bSdh 		 */
38374c06356bSdh 		ctmp->changed = 0;
38384c06356bSdh 
38394c06356bSdh 		if (ctmp->dtype != local->dtype) {
38404c06356bSdh 			if (ctmp->dtype != NOTHING) {
3841c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
3842c3bc407cSdh 				    "%s: %s type changed from %s to %s "
3843c3bc407cSdh 				    "(killing)", __func__, ctmp->path,
3844c3bc407cSdh 				    PHY_TYPE(ctmp), PHY_TYPE(local));
38454c06356bSdh 				/*
38464c06356bSdh 				 * Force a rescan of this expander after dead
38474c06356bSdh 				 * contents are cleared and removed.
38484c06356bSdh 				 */
38494c06356bSdh 				changed = B_TRUE;
38504c06356bSdh 				kill_changed = B_TRUE;
38514c06356bSdh 			} else {
38524c06356bSdh 				changed = B_TRUE;
3853c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
38544c06356bSdh 				    "%s: %s type changed from NOTHING to %s",
38554c06356bSdh 				    __func__, ctmp->path, PHY_TYPE(local));
385673a3eccdSDavid Hollister 				/*
385773a3eccdSDavid Hollister 				 * Since this PHY was nothing and is now
385873a3eccdSDavid Hollister 				 * something, reset the config_stop timer.
385973a3eccdSDavid Hollister 				 */
386073a3eccdSDavid Hollister 				ctmp->config_stop = ddi_get_lbolt() +
386173a3eccdSDavid Hollister 				    drv_usectohz(PMCS_MAX_CONFIG_TIME);
38624c06356bSdh 			}
38634c06356bSdh 
38644c06356bSdh 		} else if (ctmp->atdt != local->atdt) {
3865c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: "
3866c3bc407cSdh 			    "%s attached device type changed from %d to %d "
3867c3bc407cSdh 			    "(killing)", __func__, ctmp->path, ctmp->atdt,
3868c3bc407cSdh 			    local->atdt);
38694c06356bSdh 			/*
38704c06356bSdh 			 * Force a rescan of this expander after dead
38714c06356bSdh 			 * contents are cleared and removed.
38724c06356bSdh 			 */
38734c06356bSdh 			changed = B_TRUE;
38744c06356bSdh 
38754c06356bSdh 			if (local->atdt == 0) {
38764c06356bSdh 				kill_changed = B_TRUE;
38774c06356bSdh 			}
38784c06356bSdh 		} else if (ctmp->link_rate != local->link_rate) {
3879c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s "
3880c3bc407cSdh 			    "changed speed from %s to %s", __func__, ctmp->path,
38814c06356bSdh 			    pmcs_get_rate(ctmp->link_rate),
38824c06356bSdh 			    pmcs_get_rate(local->link_rate));
38834c06356bSdh 			/* If the speed changed from invalid, force rescan */
38844c06356bSdh 			if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
38854c06356bSdh 				changed = B_TRUE;
38864c06356bSdh 				RESTART_DISCOVERY(pwp);
38874c06356bSdh 			} else {
38884c06356bSdh 				/* Just update to the new link rate */
38894c06356bSdh 				ctmp->link_rate = local->link_rate;
38904c06356bSdh 			}
38914c06356bSdh 
38924c06356bSdh 			if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
38934c06356bSdh 				kill_changed = B_TRUE;
38944c06356bSdh 			}
38954c06356bSdh 		} else if (memcmp(ctmp->sas_address, local->sas_address,
38964c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
3897c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
3898c3bc407cSdh 			    "%s: SAS Addr for %s changed from " SAS_ADDR_FMT
3899c3bc407cSdh 			    "to " SAS_ADDR_FMT " (kill old tree)", __func__,
39004c06356bSdh 			    ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
39014c06356bSdh 			    SAS_ADDR_PRT(local->sas_address));
39024c06356bSdh 			/*
39034c06356bSdh 			 * Force a rescan of this expander after dead
39044c06356bSdh 			 * contents are cleared and removed.
39054c06356bSdh 			 */
39064c06356bSdh 			changed = B_TRUE;
39074c06356bSdh 		} else {
3908c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
39094c06356bSdh 			    "%s: %s looks the same (type %s)",
39104c06356bSdh 			    __func__, ctmp->path, PHY_TYPE(ctmp));
39114c06356bSdh 			/*
39124c06356bSdh 			 * If EXPANDER, still mark it changed so we
39134c06356bSdh 			 * re-evaluate its contents.  If it's not an expander,
39144c06356bSdh 			 * but it hasn't been configured, also mark it as
39154c06356bSdh 			 * changed so that it will undergo configuration.
39164c06356bSdh 			 */
39174c06356bSdh 			if (ctmp->dtype == EXPANDER) {
39184c06356bSdh 				changed = B_TRUE;
39194c06356bSdh 			} else if ((ctmp->dtype != NOTHING) &&
39204c06356bSdh 			    !ctmp->configured) {
39214c06356bSdh 				ctmp->changed = 1;
39224c06356bSdh 			} else {
39234c06356bSdh 				/* It simply hasn't changed */
39244c06356bSdh 				ctmp->changed = 0;
39254c06356bSdh 			}
39264c06356bSdh 		}
39274c06356bSdh 
39284c06356bSdh 		/*
39294c06356bSdh 		 * If the PHY changed, call pmcs_kill_changed if indicated,
39304c06356bSdh 		 * update its contents to reflect its current state and mark it
39314c06356bSdh 		 * as changed.
39324c06356bSdh 		 */
39334c06356bSdh 		if (changed) {
39344c06356bSdh 			/*
39354c06356bSdh 			 * pmcs_kill_changed will mark the PHY as changed, so
39364c06356bSdh 			 * only do PHY_CHANGED if we did not do kill_changed.
39374c06356bSdh 			 */
39384c06356bSdh 			if (kill_changed) {
39394c06356bSdh 				pmcs_kill_changed(pwp, ctmp, 0);
39404c06356bSdh 			} else {
39414c06356bSdh 				/*
39424c06356bSdh 				 * If we're not killing the device, it's not
39434c06356bSdh 				 * dead.  Mark the PHY as changed.
39444c06356bSdh 				 */
39454c06356bSdh 				PHY_CHANGED(pwp, ctmp);
39464c06356bSdh 
39474c06356bSdh 				if (ctmp->dead) {
39484c06356bSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3949c3bc407cSdh 					    ctmp, NULL, "%s: Unmarking PHY %s "
3950c3bc407cSdh 					    "dead, restarting discovery",
39514c06356bSdh 					    __func__, ctmp->path);
39524c06356bSdh 					ctmp->dead = 0;
39534c06356bSdh 					RESTART_DISCOVERY(pwp);
39544c06356bSdh 				}
39554c06356bSdh 			}
39564c06356bSdh 
39574c06356bSdh 			/*
39584c06356bSdh 			 * If the dtype of this PHY is now NOTHING, mark it as
39594c06356bSdh 			 * unconfigured.  Set pend_dtype to what the new dtype
39604c06356bSdh 			 * is.  It'll get updated at the end of the discovery
39614c06356bSdh 			 * process.
39624c06356bSdh 			 */
39634c06356bSdh 			if (local->dtype == NOTHING) {
39644c06356bSdh 				bzero(ctmp->sas_address,
39654c06356bSdh 				    sizeof (local->sas_address));
39664c06356bSdh 				ctmp->atdt = 0;
39674c06356bSdh 				ctmp->link_rate = 0;
39684c06356bSdh 				ctmp->pend_dtype = NOTHING;
39694c06356bSdh 				ctmp->configured = 0;
39704c06356bSdh 			} else {
39714c06356bSdh 				(void) memcpy(ctmp->sas_address,
39724c06356bSdh 				    local->sas_address,
39734c06356bSdh 				    sizeof (local->sas_address));
39744c06356bSdh 				ctmp->atdt = local->atdt;
39754c06356bSdh 				ctmp->link_rate = local->link_rate;
39764c06356bSdh 				ctmp->pend_dtype = local->dtype;
39774c06356bSdh 			}
39784c06356bSdh 		}
39794c06356bSdh 
39804c06356bSdh 		local = local->sibling;
39814c06356bSdh 		ctmp = ctmp->sibling;
39824c06356bSdh 	}
39834c06356bSdh 
39844c06356bSdh 	/*
39854c06356bSdh 	 * If we got to here, that means we were able to see all the PHYs
39864c06356bSdh 	 * and we can now update all of the real PHYs with the information
39874c06356bSdh 	 * we got on the local PHYs.  Once that's done, free all the local
39884c06356bSdh 	 * PHYs.
39894c06356bSdh 	 */
39904c06356bSdh 
39914c06356bSdh 	pmcs_free_phys(pwp, local_list);
39924c06356bSdh }
39934c06356bSdh 
39944c06356bSdh /*
39954c06356bSdh  * Top level routine to check expanders.  We call pmcs_check_expander for
39964c06356bSdh  * each expander.  Since we're not doing any configuration right now, it
39974c06356bSdh  * doesn't matter if this is breadth-first.
39984c06356bSdh  */
399973a3eccdSDavid Hollister static void
40004c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
40014c06356bSdh {
40024c06356bSdh 	pmcs_phy_t *phyp, *pnext, *pchild;
40034c06356bSdh 
4004c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4005c3bc407cSdh 	    "%s: %s", __func__, pptr->path);
40064c06356bSdh 
40074c06356bSdh 	/*
40084c06356bSdh 	 * Check each expander at this level
40094c06356bSdh 	 */
40104c06356bSdh 	phyp = pptr;
401173a3eccdSDavid Hollister 	while (phyp) {
40124c06356bSdh 		pmcs_lock_phy(phyp);
40134c06356bSdh 
40144c06356bSdh 		if ((phyp->dtype == EXPANDER) && phyp->changed &&
40154c06356bSdh 		    !phyp->dead && !phyp->subsidiary &&
40164c06356bSdh 		    phyp->configured) {
40174c06356bSdh 			pmcs_check_expander(pwp, phyp);
40184c06356bSdh 		}
40194c06356bSdh 
40204c06356bSdh 		pnext = phyp->sibling;
40214c06356bSdh 		pmcs_unlock_phy(phyp);
40224c06356bSdh 		phyp = pnext;
40234c06356bSdh 	}
40244c06356bSdh 
40254c06356bSdh 	/*
40264c06356bSdh 	 * Now check the children
40274c06356bSdh 	 */
40284c06356bSdh 	phyp = pptr;
402973a3eccdSDavid Hollister 	while (phyp) {
40304c06356bSdh 		pmcs_lock_phy(phyp);
40314c06356bSdh 		pnext = phyp->sibling;
40324c06356bSdh 		pchild = phyp->children;
40334c06356bSdh 		pmcs_unlock_phy(phyp);
40344c06356bSdh 
40354c06356bSdh 		if (pchild) {
403673a3eccdSDavid Hollister 			pmcs_check_expanders(pwp, pchild);
40374c06356bSdh 		}
40384c06356bSdh 
40394c06356bSdh 		phyp = pnext;
40404c06356bSdh 	}
40414c06356bSdh }
40424c06356bSdh 
40434c06356bSdh /*
40444c06356bSdh  * Called with softstate and PHY locked
40454c06356bSdh  */
40464c06356bSdh static void
40474c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
40484c06356bSdh {
40494c06356bSdh 	pmcs_phy_t *ctmp;
40504c06356bSdh 
40514c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
40524c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
40534c06356bSdh 	ASSERT(pptr->level < PMCS_MAX_XPND - 1);
40544c06356bSdh 
4055c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4056c3bc407cSdh 	    "%s: checking %s", __func__, pptr->path);
40574c06356bSdh 
40584c06356bSdh 	ctmp = pptr->children;
40594c06356bSdh 	while (ctmp) {
40604c06356bSdh 		/*
40614c06356bSdh 		 * If the expander is dead, mark its children dead
40624c06356bSdh 		 */
40634c06356bSdh 		if (pptr->dead) {
40644c06356bSdh 			ctmp->dead = 1;
40654c06356bSdh 		}
40664c06356bSdh 		if (ctmp->dtype == EXPANDER) {
40674c06356bSdh 			pmcs_clear_expander(pwp, ctmp, level + 1);
40684c06356bSdh 		}
40694c06356bSdh 		ctmp = ctmp->sibling;
40704c06356bSdh 	}
40714c06356bSdh 
40724c06356bSdh 	/*
40734c06356bSdh 	 * If this expander is not dead, we're done here.
40744c06356bSdh 	 */
40754c06356bSdh 	if (!pptr->dead) {
40764c06356bSdh 		return;
40774c06356bSdh 	}
40784c06356bSdh 
40794c06356bSdh 	/*
40804c06356bSdh 	 * Now snip out the list of children below us and release them
40814c06356bSdh 	 */
40829aed1621SDavid Hollister 	if (pptr->children) {
40839aed1621SDavid Hollister 		pmcs_add_dead_phys(pwp, pptr->children);
40844c06356bSdh 	}
40854c06356bSdh 
40864c06356bSdh 	pptr->children = NULL;
40874c06356bSdh 
40884c06356bSdh 	/*
40894c06356bSdh 	 * Clear subsidiary phys as well.  Getting the parent's PHY lock
40904c06356bSdh 	 * is only necessary if level == 0 since otherwise the parent is
40914c06356bSdh 	 * already locked.
40924c06356bSdh 	 */
40934c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
40944c06356bSdh 		if (level == 0) {
40954c06356bSdh 			mutex_enter(&pptr->parent->phy_lock);
40964c06356bSdh 		}
40974c06356bSdh 		ctmp = pptr->parent->children;
40984c06356bSdh 		if (level == 0) {
40994c06356bSdh 			mutex_exit(&pptr->parent->phy_lock);
41004c06356bSdh 		}
41014c06356bSdh 	} else {
41024c06356bSdh 		ctmp = pwp->root_phys;
41034c06356bSdh 	}
41044c06356bSdh 
41054c06356bSdh 	while (ctmp) {
41064c06356bSdh 		if (ctmp == pptr) {
41074c06356bSdh 			ctmp = ctmp->sibling;
41084c06356bSdh 			continue;
41094c06356bSdh 		}
41104c06356bSdh 		/*
41114c06356bSdh 		 * We only need to lock subsidiary PHYs on the level 0
41124c06356bSdh 		 * expander.  Any children of that expander, subsidiaries or
41134c06356bSdh 		 * not, will already be locked.
41144c06356bSdh 		 */
41154c06356bSdh 		if (level == 0) {
41164c06356bSdh 			pmcs_lock_phy(ctmp);
41174c06356bSdh 		}
41184c06356bSdh 		if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
41194c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address,
41204c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
41214c06356bSdh 			if (level == 0) {
41224c06356bSdh 				pmcs_unlock_phy(ctmp);
41234c06356bSdh 			}
41244c06356bSdh 			ctmp = ctmp->sibling;
41254c06356bSdh 			continue;
41264c06356bSdh 		}
4127c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
4128c3bc407cSdh 		    "%s: subsidiary %s", __func__, ctmp->path);
41294c06356bSdh 		pmcs_clear_phy(pwp, ctmp);
41304c06356bSdh 		if (level == 0) {
41314c06356bSdh 			pmcs_unlock_phy(ctmp);
41324c06356bSdh 		}
41334c06356bSdh 		ctmp = ctmp->sibling;
41344c06356bSdh 	}
41354c06356bSdh 
41364c06356bSdh 	pmcs_clear_phy(pwp, pptr);
41374c06356bSdh }
41384c06356bSdh 
41394c06356bSdh /*
41404c06356bSdh  * Called with PHY locked and with scratch acquired. We return 0 if
41414c06356bSdh  * we fail to allocate resources or notice that the configuration
41424c06356bSdh  * count changed while we were running the command. We return
41434c06356bSdh  * less than zero if we had an I/O error or received an unsupported
41444c06356bSdh  * configuration. Otherwise we return the number of phys in the
41454c06356bSdh  * expander.
41464c06356bSdh  */
41474c06356bSdh #define	DFM(m, y) if (m == NULL) m = y
41484c06356bSdh static int
41494c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
41504c06356bSdh {
41514c06356bSdh 	struct pmcwork *pwrk;
4152*3be32c0fSJesse Butler 	pmcs_iport_t *iport;
41534c06356bSdh 	char buf[64];
41544c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
41554c06356bSdh 	smp_response_frame_t *srf;
41564c06356bSdh 	smp_report_general_resp_t *srgr;
41574c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
41589aed1621SDavid Hollister 	int result = 0;
41594c06356bSdh 
41604c06356bSdh 	ival = 0x40001100;
41619aed1621SDavid Hollister 
41624c06356bSdh again:
41639aed1621SDavid Hollister 	if (!pptr->iport || !pptr->valid_device_id) {
41649aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
41659aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
41669aed1621SDavid Hollister 		goto out;
41679aed1621SDavid Hollister 	}
41689aed1621SDavid Hollister 
41694c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
41704c06356bSdh 	if (pwrk == NULL) {
41714c06356bSdh 		goto out;
41724c06356bSdh 	}
41734c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
41744c06356bSdh 	pwrk->arg = pwp->scratch;
41754c06356bSdh 	pwrk->dtype = pptr->dtype;
4176*3be32c0fSJesse Butler 	pwrk->xp = pptr->target;
4177978d7443SSrikanth Suravajhala 	pwrk->htag |= PMCS_TAG_NONIO_CMD;
41784c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
41794c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
41804c06356bSdh 	if (ptr == NULL) {
41814c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4182c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL,
4183c3bc407cSdh 		    "%s: GET_IQ_ENTRY failed", __func__);
41844c06356bSdh 		pmcs_pwork(pwp, pwrk);
41854c06356bSdh 		goto out;
41864c06356bSdh 	}
41874c06356bSdh 
41884c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
41894c06356bSdh 	msg[1] = LE_32(pwrk->htag);
41904c06356bSdh 	msg[2] = LE_32(pptr->device_id);
41914c06356bSdh 	msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
41924c06356bSdh 	/*
41934c06356bSdh 	 * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
41944c06356bSdh 	 */
41954c06356bSdh 	msg[4] = BE_32(ival);
41964c06356bSdh 	msg[5] = 0;
41974c06356bSdh 	msg[6] = 0;
41984c06356bSdh 	msg[7] = 0;
41994c06356bSdh 	msg[8] = 0;
42004c06356bSdh 	msg[9] = 0;
42014c06356bSdh 	msg[10] = 0;
42024c06356bSdh 	msg[11] = 0;
42034c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
42044c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
42054c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
42064c06356bSdh 	msg[15] = 0;
42074c06356bSdh 
42084c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
42096745c559SJesse Butler 
4210*3be32c0fSJesse Butler 	pmcs_hold_iport(pptr->iport);
4211*3be32c0fSJesse Butler 	iport = pptr->iport;
4212*3be32c0fSJesse Butler 	pmcs_smp_acquire(iport);
42134c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
42144c06356bSdh 	htag = pwrk->htag;
42154c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
42164c06356bSdh 	pmcs_unlock_phy(pptr);
42174c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
4218*3be32c0fSJesse Butler 	pmcs_pwork(pwp, pwrk);
4219*3be32c0fSJesse Butler 	pmcs_smp_release(iport);
4220*3be32c0fSJesse Butler 	pmcs_rele_iport(iport);
4221601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
42226745c559SJesse Butler 
42234c06356bSdh 
42244c06356bSdh 	mutex_enter(&pwp->config_lock);
42254c06356bSdh 	if (pwp->config_changed) {
42264c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
42274c06356bSdh 		mutex_exit(&pwp->config_lock);
42284c06356bSdh 		result = 0;
42294c06356bSdh 		goto out;
42304c06356bSdh 	}
42314c06356bSdh 	mutex_exit(&pwp->config_lock);
42324c06356bSdh 
42334c06356bSdh 	if (result) {
42344c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
4235c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42364c06356bSdh 		    "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
42374c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
4238c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42394c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
42404c06356bSdh 			    __func__, htag);
42414c06356bSdh 		} else {
4242c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42434c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
42444c06356bSdh 			    __func__, htag);
42454c06356bSdh 		}
42464c06356bSdh 		result = 0;
42474c06356bSdh 		goto out;
42484c06356bSdh 	}
42494c06356bSdh 	ptr = (void *)pwp->scratch;
42504c06356bSdh 	status = LE_32(ptr[2]);
42514c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
42524c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
4253c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
42544c06356bSdh 		    "%s: over/underflow", __func__);
42554c06356bSdh 		status = PMCOUT_STATUS_OK;
42564c06356bSdh 	}
42574c06356bSdh 	srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
42584c06356bSdh 	srgr = (smp_report_general_resp_t *)
42594c06356bSdh 	    &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
42604c06356bSdh 
42614c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
42624c06356bSdh 		char *nag = NULL;
42634c06356bSdh 		(void) snprintf(buf, sizeof (buf),
42644c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
42654c06356bSdh 		switch (status) {
42664c06356bSdh 		case PMCOUT_STATUS_IO_PORT_IN_RESET:
42674c06356bSdh 			DFM(nag, "I/O Port In Reset");
42684c06356bSdh 			/* FALLTHROUGH */
42694c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
42704c06356bSdh 			DFM(nag, "Hardware Timeout");
42714c06356bSdh 			/* FALLTHROUGH */
42724c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
42734c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
42744c06356bSdh 			/* FALLTHROUGH */
42754c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
42764c06356bSdh 			DFM(nag, "PHY Not Ready");
42774c06356bSdh 			/* FALLTHROUGH */
42784c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
42794c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
42804c06356bSdh 			/* FALLTHROUGH */
42814c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
42824c06356bSdh 			DFM(nag, "Open Retry Timeout");
42834c06356bSdh 			/* FALLTHROUGH */
4284a25672a1SDavid Hollister 		case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
4285a25672a1SDavid Hollister 			DFM(nag, "HW Resource Busy");
4286a25672a1SDavid Hollister 			/* FALLTHROUGH */
42874c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
42884c06356bSdh 			DFM(nag, "Response Connection Error");
4289c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
42904c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
42914c06356bSdh 			    __func__, pptr->path, nag);
42924c06356bSdh 			break;
42934c06356bSdh 
42944c06356bSdh 		/*
42954c06356bSdh 		 * For the IO_DS_NON_OPERATIONAL case, we need to kick off
42964c06356bSdh 		 * device state recovery and return 0 so that the caller
42974c06356bSdh 		 * doesn't assume this expander is dead for good.
42984c06356bSdh 		 */
42994c06356bSdh 		case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
43004c06356bSdh 			pmcs_xscsi_t *xp = pptr->target;
43014c06356bSdh 
4302c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp,
43034c06356bSdh 			    "%s: expander %s device state non-operational",
43044c06356bSdh 			    __func__, pptr->path);
43054c06356bSdh 
43064c06356bSdh 			if (xp == NULL) {
43076745c559SJesse Butler 				/*
43086745c559SJesse Butler 				 * Kick off recovery right now.
43096745c559SJesse Butler 				 */
43106745c559SJesse Butler 				SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
43116745c559SJesse Butler 				(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
43126745c559SJesse Butler 				    pwp, DDI_NOSLEEP);
43136745c559SJesse Butler 			} else {
43146745c559SJesse Butler 				mutex_enter(&xp->statlock);
43156745c559SJesse Butler 				pmcs_start_dev_state_recovery(xp, pptr);
43166745c559SJesse Butler 				mutex_exit(&xp->statlock);
43174c06356bSdh 			}
43184c06356bSdh 
43194c06356bSdh 			break;
43204c06356bSdh 		}
43214c06356bSdh 
43224c06356bSdh 		default:
43234c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
43244c06356bSdh 			result = -EIO;
43254c06356bSdh 			break;
43264c06356bSdh 		}
43274c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
4328c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43294c06356bSdh 		    "%s: bad response frame type 0x%x",
43304c06356bSdh 		    __func__, srf->srf_frame_type);
43314c06356bSdh 		result = -EINVAL;
43324c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
4333c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4334c3bc407cSdh 		    "%s: bad response function 0x%x",
43354c06356bSdh 		    __func__, srf->srf_function);
43364c06356bSdh 		result = -EINVAL;
43374c06356bSdh 	} else if (srf->srf_result != 0) {
43384c06356bSdh 		/*
43394c06356bSdh 		 * Check to see if we have a value of 3 for failure and
43404c06356bSdh 		 * whether we were using a SAS2.0 allocation length value
43414c06356bSdh 		 * and retry without it.
43424c06356bSdh 		 */
43434c06356bSdh 		if (srf->srf_result == 3 && (ival & 0xff00)) {
43444c06356bSdh 			ival &= ~0xff00;
4345c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43464c06356bSdh 			    "%s: err 0x%x with SAS2 request- retry with SAS1",
43474c06356bSdh 			    __func__, srf->srf_result);
43484c06356bSdh 			goto again;
43494c06356bSdh 		}
4350c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4351c3bc407cSdh 		    "%s: bad response 0x%x", __func__, srf->srf_result);
43524c06356bSdh 		result = -EINVAL;
43534c06356bSdh 	} else if (srgr->srgr_configuring) {
4354c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43554c06356bSdh 		    "%s: expander at phy %s is still configuring",
43564c06356bSdh 		    __func__, pptr->path);
43574c06356bSdh 		result = 0;
43584c06356bSdh 	} else {
43594c06356bSdh 		result = srgr->srgr_number_of_phys;
43604c06356bSdh 		if (ival & 0xff00) {
43614c06356bSdh 			pptr->tolerates_sas2 = 1;
43624c06356bSdh 		}
43639aed1621SDavid Hollister 		/*
43649aed1621SDavid Hollister 		 * Save off the REPORT_GENERAL response
43659aed1621SDavid Hollister 		 */
43669aed1621SDavid Hollister 		bcopy(srgr, &pptr->rg_resp, sizeof (smp_report_general_resp_t));
4367c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
43684c06356bSdh 		    "%s has %d phys and %s SAS2", pptr->path, result,
43694c06356bSdh 		    pptr->tolerates_sas2? "tolerates" : "does not tolerate");
43704c06356bSdh 	}
43714c06356bSdh out:
43724c06356bSdh 	return (result);
43734c06356bSdh }
43744c06356bSdh 
43754c06356bSdh /*
43764c06356bSdh  * Called with expander locked (and thus, pptr) as well as all PHYs up to
43774c06356bSdh  * the root, and scratch acquired. Return 0 if we fail to allocate resources
43784c06356bSdh  * or notice that the configuration changed while we were running the command.
43794c06356bSdh  *
43804c06356bSdh  * We return less than zero if we had an I/O error or received an
43814c06356bSdh  * unsupported configuration.
43824c06356bSdh  */
43834c06356bSdh static int
43844c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
43854c06356bSdh     pmcs_phy_t *pptr)
43864c06356bSdh {
43874c06356bSdh 	struct pmcwork *pwrk;
4388*3be32c0fSJesse Butler 	pmcs_iport_t *iport;
43894c06356bSdh 	char buf[64];
43904c06356bSdh 	uint8_t sas_address[8];
43914c06356bSdh 	uint8_t att_sas_address[8];
43924c06356bSdh 	smp_response_frame_t *srf;
43934c06356bSdh 	smp_discover_resp_t *sdr;
43944c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
43954c06356bSdh 	uint8_t *roff;
43964c06356bSdh 	uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
43979aed1621SDavid Hollister 	int result = 0;
43984c06356bSdh 	uint8_t	ini_support;
43994c06356bSdh 	uint8_t	tgt_support;
44004c06356bSdh 
44019aed1621SDavid Hollister 	if (!expander->iport || !expander->valid_device_id) {
44029aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, expander, expander->target,
44039aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, expander->path);
44049aed1621SDavid Hollister 		goto out;
44059aed1621SDavid Hollister 	}
44069aed1621SDavid Hollister 
44074c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
44084c06356bSdh 	if (pwrk == NULL) {
44094c06356bSdh 		goto out;
44104c06356bSdh 	}
44114c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
44124c06356bSdh 	pwrk->arg = pwp->scratch;
44134c06356bSdh 	pwrk->dtype = expander->dtype;
4414*3be32c0fSJesse Butler 	pwrk->xp = expander->target;
4415978d7443SSrikanth Suravajhala 	pwrk->htag |= PMCS_TAG_NONIO_CMD;
44164c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
44174c06356bSdh 	msg[1] = LE_32(pwrk->htag);
44184c06356bSdh 	msg[2] = LE_32(expander->device_id);
44194c06356bSdh 	msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
44204c06356bSdh 	    SMP_INDIRECT_RESPONSE);
44214c06356bSdh 	/*
44224c06356bSdh 	 * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
44234c06356bSdh 	 */
44244c06356bSdh 	if (expander->tolerates_sas2) {
44254c06356bSdh 		msg[4] = BE_32(0x40101B00);
44264c06356bSdh 	} else {
44274c06356bSdh 		msg[4] = BE_32(0x40100000);
44284c06356bSdh 	}
44294c06356bSdh 	msg[5] = 0;
44304c06356bSdh 	msg[6] = BE_32((pptr->phynum << 16));
44314c06356bSdh 	msg[7] = 0;
44324c06356bSdh 	msg[8] = 0;
44334c06356bSdh 	msg[9] = 0;
44344c06356bSdh 	msg[10] = 0;
44354c06356bSdh 	msg[11] = 0;
44364c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
44374c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
44384c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
44394c06356bSdh 	msg[15] = 0;
44404c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
44414c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
44424c06356bSdh 	if (ptr == NULL) {
44434c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
44444c06356bSdh 		goto out;
44454c06356bSdh 	}
44464c06356bSdh 
44474c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
44486745c559SJesse Butler 
4449*3be32c0fSJesse Butler 	pmcs_hold_iport(expander->iport);
4450*3be32c0fSJesse Butler 	iport = expander->iport;
4451*3be32c0fSJesse Butler 	pmcs_smp_acquire(iport);
44524c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
44534c06356bSdh 	htag = pwrk->htag;
44544c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
44554c06356bSdh 	pmcs_unlock_phy(expander);
44564c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
44574c06356bSdh 	pmcs_pwork(pwp, pwrk);
4458*3be32c0fSJesse Butler 	pmcs_smp_release(iport);
4459*3be32c0fSJesse Butler 	pmcs_rele_iport(iport);
4460*3be32c0fSJesse Butler 	pmcs_lock_phy(expander);
44614c06356bSdh 
44624c06356bSdh 	mutex_enter(&pwp->config_lock);
44634c06356bSdh 	if (pwp->config_changed) {
44644c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
44654c06356bSdh 		mutex_exit(&pwp->config_lock);
44664c06356bSdh 		result = 0;
44674c06356bSdh 		goto out;
44684c06356bSdh 	}
44694c06356bSdh 	mutex_exit(&pwp->config_lock);
44704c06356bSdh 
44714c06356bSdh 	if (result) {
44726745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
44734c06356bSdh 		if (pmcs_abort(pwp, expander, htag, 0, 0)) {
4474c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
44754c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
44764c06356bSdh 			    __func__, htag);
44774c06356bSdh 		} else {
4478c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
44794c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
44804c06356bSdh 			    __func__, htag);
44814c06356bSdh 		}
44824c06356bSdh 		result = -ETIMEDOUT;
44834c06356bSdh 		goto out;
44844c06356bSdh 	}
44854c06356bSdh 	ptr = (void *)pwp->scratch;
44864c06356bSdh 	/*
44874c06356bSdh 	 * Point roff to the DMA offset for returned data
44884c06356bSdh 	 */
44894c06356bSdh 	roff = pwp->scratch;
44904c06356bSdh 	roff += rdoff;
44914c06356bSdh 	srf = (smp_response_frame_t *)roff;
44924c06356bSdh 	sdr = (smp_discover_resp_t *)(roff+4);
44934c06356bSdh 	status = LE_32(ptr[2]);
44944c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
44954c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
4496c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
44974c06356bSdh 		    "%s: over/underflow", __func__);
44984c06356bSdh 		status = PMCOUT_STATUS_OK;
44994c06356bSdh 	}
45004c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
45014c06356bSdh 		char *nag = NULL;
45024c06356bSdh 		(void) snprintf(buf, sizeof (buf),
45034c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
45044c06356bSdh 		switch (status) {
45054c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
45064c06356bSdh 			DFM(nag, "Hardware Timeout");
45074c06356bSdh 			/* FALLTHROUGH */
45084c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
45094c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
45104c06356bSdh 			/* FALLTHROUGH */
45114c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
45124c06356bSdh 			DFM(nag, "PHY Not Ready");
45134c06356bSdh 			/* FALLTHROUGH */
45144c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
45154c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
45164c06356bSdh 			/* FALLTHROUGH */
45174c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
45184c06356bSdh 			DFM(nag, "Open Retry Timeout");
45194c06356bSdh 			/* FALLTHROUGH */
4520a25672a1SDavid Hollister 		case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
4521a25672a1SDavid Hollister 			DFM(nag, "HW Resource Busy");
4522a25672a1SDavid Hollister 			/* FALLTHROUGH */
45234c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
45244c06356bSdh 			DFM(nag, "Response Connection Error");
4525c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
45264c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
45274c06356bSdh 			    __func__, pptr->path, nag);
45284c06356bSdh 			break;
45294c06356bSdh 		default:
45304c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
45314c06356bSdh 			result = -EIO;
45324c06356bSdh 			break;
45334c06356bSdh 		}
45344c06356bSdh 		goto out;
45354c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
4536c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
45374c06356bSdh 		    "%s: bad response frame type 0x%x",
45384c06356bSdh 		    __func__, srf->srf_frame_type);
45394c06356bSdh 		result = -EINVAL;
45404c06356bSdh 		goto out;
45414c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_DISCOVER) {
4542c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4543c3bc407cSdh 		    "%s: bad response function 0x%x",
45444c06356bSdh 		    __func__, srf->srf_function);
45454c06356bSdh 		result = -EINVAL;
45464c06356bSdh 		goto out;
45474c06356bSdh 	} else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
45484c06356bSdh 		result = pmcs_smp_function_result(pwp, srf);
45494c06356bSdh 		/* Need not fail if PHY is Vacant */
45504c06356bSdh 		if (result != SMP_RES_PHY_VACANT) {
45514c06356bSdh 			result = -EINVAL;
45524c06356bSdh 			goto out;
45534c06356bSdh 		}
45544c06356bSdh 	}
45554c06356bSdh 
45569aed1621SDavid Hollister 	/*
45579aed1621SDavid Hollister 	 * Save off the DISCOVER response
45589aed1621SDavid Hollister 	 */
45599aed1621SDavid Hollister 	bcopy(sdr, &pptr->disc_resp, sizeof (smp_discover_resp_t));
45609aed1621SDavid Hollister 
45614c06356bSdh 	ini_support = (sdr->sdr_attached_sata_host |
45624c06356bSdh 	    (sdr->sdr_attached_smp_initiator << 1) |
45634c06356bSdh 	    (sdr->sdr_attached_stp_initiator << 2) |
45644c06356bSdh 	    (sdr->sdr_attached_ssp_initiator << 3));
45654c06356bSdh 
45664c06356bSdh 	tgt_support = (sdr->sdr_attached_sata_device |
45674c06356bSdh 	    (sdr->sdr_attached_smp_target << 1) |
45684c06356bSdh 	    (sdr->sdr_attached_stp_target << 2) |
45694c06356bSdh 	    (sdr->sdr_attached_ssp_target << 3));
45704c06356bSdh 
45714c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
45724c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
45734c06356bSdh 
4574c80dec56SDavid Hollister 	pptr->virtual = sdr->sdr_virtual_phy;
4575c80dec56SDavid Hollister 
45769aed1621SDavid Hollister 	/*
45779aed1621SDavid Hollister 	 * Set the routing attribute regardless of the PHY type.
45789aed1621SDavid Hollister 	 */
45799aed1621SDavid Hollister 	pptr->routing_attr = sdr->sdr_routing_attr;
45809aed1621SDavid Hollister 
45814c06356bSdh 	switch (sdr->sdr_attached_device_type) {
45824c06356bSdh 	case SAS_IF_DTYPE_ENDPOINT:
4583c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
45844c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
45854c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
45864c06356bSdh 		    pptr->path,
45874c06356bSdh 		    sdr->sdr_attached_device_type,
45884c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
45894c06356bSdh 		    ini_support,
45904c06356bSdh 		    tgt_support,
45914c06356bSdh 		    SAS_ADDR_PRT(sas_address),
45924c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
45934c06356bSdh 		    sdr->sdr_attached_phy_identifier);
45944c06356bSdh 
45954c06356bSdh 		if (sdr->sdr_attached_sata_device ||
45964c06356bSdh 		    sdr->sdr_attached_stp_target) {
45974c06356bSdh 			pptr->dtype = SATA;
45984c06356bSdh 		} else if (sdr->sdr_attached_ssp_target) {
45994c06356bSdh 			pptr->dtype = SAS;
46004c06356bSdh 		} else if (tgt_support || ini_support) {
4601c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4602c3bc407cSdh 			    "%s: %s has tgt support=%x init support=(%x)",
46034c06356bSdh 			    __func__, pptr->path, tgt_support, ini_support);
46044c06356bSdh 		}
46059aed1621SDavid Hollister 
46069aed1621SDavid Hollister 		switch (pptr->routing_attr) {
46079aed1621SDavid Hollister 		case SMP_ROUTING_SUBTRACTIVE:
46089aed1621SDavid Hollister 		case SMP_ROUTING_TABLE:
46099aed1621SDavid Hollister 		case SMP_ROUTING_DIRECT:
46109aed1621SDavid Hollister 			pptr->routing_method = SMP_ROUTING_DIRECT;
46119aed1621SDavid Hollister 			break;
46129aed1621SDavid Hollister 		default:
46139aed1621SDavid Hollister 			pptr->routing_method = 0xff;	/* Invalid method */
46149aed1621SDavid Hollister 			break;
46159aed1621SDavid Hollister 		}
4616616875b4SDavid Hollister 		pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
4617616875b4SDavid Hollister 		    (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
46184c06356bSdh 		break;
46194c06356bSdh 	case SAS_IF_DTYPE_EDGE:
46204c06356bSdh 	case SAS_IF_DTYPE_FANOUT:
4621c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
46224c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
46234c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
46244c06356bSdh 		    pptr->path,
46254c06356bSdh 		    sdr->sdr_attached_device_type,
46264c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
46274c06356bSdh 		    ini_support,
46284c06356bSdh 		    tgt_support,
46294c06356bSdh 		    SAS_ADDR_PRT(sas_address),
46304c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
46314c06356bSdh 		    sdr->sdr_attached_phy_identifier);
46324c06356bSdh 		if (sdr->sdr_attached_smp_target) {
46334c06356bSdh 			/*
46344c06356bSdh 			 * Avoid configuring phys that just point back
46354c06356bSdh 			 * at a parent phy
46364c06356bSdh 			 */
46374c06356bSdh 			if (expander->parent &&
46384c06356bSdh 			    memcmp(expander->parent->sas_address,
46394c06356bSdh 			    att_sas_address,
46404c06356bSdh 			    sizeof (expander->parent->sas_address)) == 0) {
4641c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL,
46424c06356bSdh 				    "%s: skipping port back to parent "
46434c06356bSdh 				    "expander (%s)", __func__, pptr->path);
46444c06356bSdh 				pptr->dtype = NOTHING;
46454c06356bSdh 				break;
46464c06356bSdh 			}
46474c06356bSdh 			pptr->dtype = EXPANDER;
46484c06356bSdh 
46494c06356bSdh 		} else if (tgt_support || ini_support) {
4650c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4651c3bc407cSdh 			    "%s has tgt support=%x init support=(%x)",
46524c06356bSdh 			    pptr->path, tgt_support, ini_support);
46534c06356bSdh 			pptr->dtype = EXPANDER;
46544c06356bSdh 		}
46559aed1621SDavid Hollister 		if (pptr->routing_attr == SMP_ROUTING_DIRECT) {
46569aed1621SDavid Hollister 			pptr->routing_method = 0xff;	/* Invalid method */
46579aed1621SDavid Hollister 		} else {
46589aed1621SDavid Hollister 			pptr->routing_method = pptr->routing_attr;
46599aed1621SDavid Hollister 		}
4660616875b4SDavid Hollister 		pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
4661616875b4SDavid Hollister 		    (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
46624c06356bSdh 		break;
46634c06356bSdh 	default:
46644c06356bSdh 		pptr->dtype = NOTHING;
46654c06356bSdh 		break;
46664c06356bSdh 	}
46674c06356bSdh 	if (pptr->dtype != NOTHING) {
46684c06356bSdh 		pmcs_phy_t *ctmp;
46694c06356bSdh 
46704c06356bSdh 		/*
46714c06356bSdh 		 * If the attached device is a SATA device and the expander
46724c06356bSdh 		 * is (possibly) a SAS2 compliant expander, check for whether
46734c06356bSdh 		 * there is a NAA=5 WWN field starting at this offset and
46744c06356bSdh 		 * use that for the SAS Address for this device.
46754c06356bSdh 		 */
46764c06356bSdh 		if (expander->tolerates_sas2 && pptr->dtype == SATA &&
4677a25672a1SDavid Hollister 		    (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == NAA_IEEE_REG) {
46784c06356bSdh 			(void) memcpy(pptr->sas_address,
46794c06356bSdh 			    &roff[SAS_ATTACHED_NAME_OFFSET], 8);
46804c06356bSdh 		} else {
46814c06356bSdh 			(void) memcpy(pptr->sas_address, att_sas_address, 8);
46824c06356bSdh 		}
46834c06356bSdh 		pptr->atdt = (sdr->sdr_attached_device_type);
46844c06356bSdh 		/*
46854c06356bSdh 		 * Now run up from the expander's parent up to the top to
46864c06356bSdh 		 * make sure we only use the least common link_rate.
46874c06356bSdh 		 */
46884c06356bSdh 		for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
46894c06356bSdh 			if (ctmp->link_rate <
46904c06356bSdh 			    sdr->sdr_negotiated_logical_link_rate) {
4691c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
46924c06356bSdh 				    "%s: derating link rate from %x to %x due "
46934c06356bSdh 				    "to %s being slower", pptr->path,
46944c06356bSdh 				    sdr->sdr_negotiated_logical_link_rate,
46954c06356bSdh 				    ctmp->link_rate,
46964c06356bSdh 				    ctmp->path);
46974c06356bSdh 				sdr->sdr_negotiated_logical_link_rate =
46984c06356bSdh 				    ctmp->link_rate;
46994c06356bSdh 			}
47004c06356bSdh 		}
47014c06356bSdh 		pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
47024c06356bSdh 		pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
47034c06356bSdh 		pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
47044c06356bSdh 		pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
47054c06356bSdh 		pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
47064c06356bSdh 		PHY_CHANGED(pwp, pptr);
47074c06356bSdh 	} else {
47084c06356bSdh 		pmcs_clear_phy(pwp, pptr);
47094c06356bSdh 	}
47104c06356bSdh 	result = 1;
47114c06356bSdh out:
47124c06356bSdh 	return (result);
47134c06356bSdh }
47144c06356bSdh 
47154c06356bSdh /*
47164c06356bSdh  * Get a work structure and assign it a tag with type and serial number
47174c06356bSdh  * If a structure is returned, it is returned locked.
47184c06356bSdh  */
47194c06356bSdh pmcwork_t *
47204c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
47214c06356bSdh {
47224c06356bSdh 	pmcwork_t *p;
47234c06356bSdh 	uint16_t snum;
47244c06356bSdh 	uint32_t off;
47254c06356bSdh 
47264c06356bSdh 	mutex_enter(&pwp->wfree_lock);
47274c06356bSdh 	p = STAILQ_FIRST(&pwp->wf);
47284c06356bSdh 	if (p == NULL) {
47294c06356bSdh 		/*
47304c06356bSdh 		 * If we couldn't get a work structure, it's time to bite
47314c06356bSdh 		 * the bullet, grab the pfree_lock and copy over all the
47324c06356bSdh 		 * work structures from the pending free list to the actual
473302b04f6eSSrikanth, Ramana 		 * free list (assuming it's not also empty).
47344c06356bSdh 		 */
47354c06356bSdh 		mutex_enter(&pwp->pfree_lock);
473602b04f6eSSrikanth, Ramana 		if (STAILQ_FIRST(&pwp->pf) == NULL) {
473702b04f6eSSrikanth, Ramana 			mutex_exit(&pwp->pfree_lock);
473802b04f6eSSrikanth, Ramana 			mutex_exit(&pwp->wfree_lock);
473902b04f6eSSrikanth, Ramana 			return (NULL);
474002b04f6eSSrikanth, Ramana 		}
47414c06356bSdh 		pwp->wf.stqh_first = pwp->pf.stqh_first;
47424c06356bSdh 		pwp->wf.stqh_last = pwp->pf.stqh_last;
47434c06356bSdh 		STAILQ_INIT(&pwp->pf);
47444c06356bSdh 		mutex_exit(&pwp->pfree_lock);
47454c06356bSdh 
47464c06356bSdh 		p = STAILQ_FIRST(&pwp->wf);
474702b04f6eSSrikanth, Ramana 		ASSERT(p != NULL);
47484c06356bSdh 	}
47494c06356bSdh 	STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
47504c06356bSdh 	snum = pwp->wserno++;
47514c06356bSdh 	mutex_exit(&pwp->wfree_lock);
47524c06356bSdh 
47534c06356bSdh 	off = p - pwp->work;
47544c06356bSdh 
47554c06356bSdh 	mutex_enter(&p->lock);
47564c06356bSdh 	ASSERT(p->state == PMCS_WORK_STATE_NIL);
47574c06356bSdh 	ASSERT(p->htag == PMCS_TAG_FREE);
47584c06356bSdh 	p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
47594c06356bSdh 	p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
47604c06356bSdh 	p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
47614c06356bSdh 	p->start = gethrtime();
47624c06356bSdh 	p->state = PMCS_WORK_STATE_READY;
47634c06356bSdh 	p->ssp_event = 0;
47644c06356bSdh 	p->dead = 0;
47654c06356bSdh 
47664c06356bSdh 	if (phyp) {
47674c06356bSdh 		p->phy = phyp;
47684c06356bSdh 		pmcs_inc_phy_ref_count(phyp);
47694c06356bSdh 	}
47704c06356bSdh 
47714c06356bSdh 	return (p);
47724c06356bSdh }
47734c06356bSdh 
47744c06356bSdh /*
47754c06356bSdh  * Called with pwrk lock held.  Returned with lock released.
47764c06356bSdh  */
47774c06356bSdh void
47784c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
47794c06356bSdh {
47804c06356bSdh 	ASSERT(p != NULL);
47814c06356bSdh 	ASSERT(mutex_owned(&p->lock));
47824c06356bSdh 
47834c06356bSdh 	p->last_ptr = p->ptr;
47844c06356bSdh 	p->last_arg = p->arg;
47854c06356bSdh 	p->last_phy = p->phy;
47864c06356bSdh 	p->last_xp = p->xp;
47874c06356bSdh 	p->last_htag = p->htag;
47884c06356bSdh 	p->last_state = p->state;
47894c06356bSdh 	p->finish = gethrtime();
47904c06356bSdh 
47914c06356bSdh 	if (p->phy) {
47924c06356bSdh 		pmcs_dec_phy_ref_count(p->phy);
47934c06356bSdh 	}
47944c06356bSdh 
47954c06356bSdh 	p->state = PMCS_WORK_STATE_NIL;
47964c06356bSdh 	p->htag = PMCS_TAG_FREE;
47974c06356bSdh 	p->xp = NULL;
47984c06356bSdh 	p->ptr = NULL;
47994c06356bSdh 	p->arg = NULL;
48004c06356bSdh 	p->phy = NULL;
4801c3bc407cSdh 	p->abt_htag = 0;
48024c06356bSdh 	p->timer = 0;
48034c06356bSdh 	mutex_exit(&p->lock);
48044c06356bSdh 
48054c06356bSdh 	if (mutex_tryenter(&pwp->wfree_lock) == 0) {
48064c06356bSdh 		mutex_enter(&pwp->pfree_lock);
48074c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->pf, p, next);
48084c06356bSdh 		mutex_exit(&pwp->pfree_lock);
48094c06356bSdh 	} else {
48104c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, p, next);
48114c06356bSdh 		mutex_exit(&pwp->wfree_lock);
48124c06356bSdh 	}
48134c06356bSdh }
48144c06356bSdh 
48154c06356bSdh /*
48164c06356bSdh  * Find a work structure based upon a tag and make sure that the tag
48174c06356bSdh  * serial number matches the work structure we've found.
48184c06356bSdh  * If a structure is found, its lock is held upon return.
4819978d7443SSrikanth Suravajhala  * If lock_phy is B_TRUE, then lock the phy also when returning the work struct
48204c06356bSdh  */
48214c06356bSdh pmcwork_t *
4822978d7443SSrikanth Suravajhala pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag, boolean_t lock_phy)
48234c06356bSdh {
48244c06356bSdh 	pmcwork_t *p;
48254c06356bSdh 	uint32_t idx = PMCS_TAG_INDEX(htag);
48264c06356bSdh 
48274c06356bSdh 	p = &pwp->work[idx];
48284c06356bSdh 
48294c06356bSdh 	mutex_enter(&p->lock);
48304c06356bSdh 	if (p->htag == htag) {
4831978d7443SSrikanth Suravajhala 		if (lock_phy) {
4832978d7443SSrikanth Suravajhala 			mutex_exit(&p->lock);
4833978d7443SSrikanth Suravajhala 			mutex_enter(&p->phy->phy_lock);
4834978d7443SSrikanth Suravajhala 			mutex_enter(&p->lock);
4835978d7443SSrikanth Suravajhala 		}
48364c06356bSdh 		return (p);
48374c06356bSdh 	}
48384c06356bSdh 	mutex_exit(&p->lock);
4839c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
4840c3bc407cSdh 	    "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag);
48414c06356bSdh 	return (NULL);
48424c06356bSdh }
48434c06356bSdh 
48444c06356bSdh /*
48454c06356bSdh  * Issue an abort for a command or for all commands.
48464c06356bSdh  *
48474c06356bSdh  * Since this can be called from interrupt context,
48484c06356bSdh  * we don't wait for completion if wait is not set.
48494c06356bSdh  *
48504c06356bSdh  * Called with PHY lock held.
48514c06356bSdh  */
48524c06356bSdh int
48534c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
48544c06356bSdh     int wait)
48554c06356bSdh {
48564c06356bSdh 	pmcwork_t *pwrk;
48574c06356bSdh 	pmcs_xscsi_t *tgt;
48584c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
48594c06356bSdh 	int result, abt_type;
48604c06356bSdh 	uint32_t abt_htag, status;
48614c06356bSdh 
48624c06356bSdh 	if (pptr->abort_all_start) {
4863c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for "
4864c3bc407cSdh 		    "(%s) already in progress.", __func__, pptr->path);
48654c06356bSdh 		return (EBUSY);
48664c06356bSdh 	}
48674c06356bSdh 
48684c06356bSdh 	switch (pptr->dtype) {
48694c06356bSdh 	case SAS:
48704c06356bSdh 		abt_type = PMCIN_SSP_ABORT;
48714c06356bSdh 		break;
48724c06356bSdh 	case SATA:
48734c06356bSdh 		abt_type = PMCIN_SATA_ABORT;
48744c06356bSdh 		break;
48754c06356bSdh 	case EXPANDER:
48764c06356bSdh 		abt_type = PMCIN_SMP_ABORT;
48774c06356bSdh 		break;
48784c06356bSdh 	default:
48794c06356bSdh 		return (0);
48804c06356bSdh 	}
48814c06356bSdh 
48824c06356bSdh 	pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
48834c06356bSdh 	    pptr);
48844c06356bSdh 
48854c06356bSdh 	if (pwrk == NULL) {
4886c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
48874c06356bSdh 		return (ENOMEM);
48884c06356bSdh 	}
48894c06356bSdh 
48904c06356bSdh 	pwrk->dtype = pptr->dtype;
4891*3be32c0fSJesse Butler 	pwrk->xp = pptr->target;
4892978d7443SSrikanth Suravajhala 	pwrk->htag |= PMCS_TAG_NONIO_CMD;
48934c06356bSdh 	if (wait) {
48944c06356bSdh 		pwrk->arg = msg;
48954c06356bSdh 	}
48964c06356bSdh 	if (pptr->valid_device_id == 0) {
48974c06356bSdh 		pmcs_pwork(pwp, pwrk);
4898c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4899c3bc407cSdh 		    "%s: Invalid DeviceID", __func__);
49004c06356bSdh 		return (ENODEV);
49014c06356bSdh 	}
49024c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
49034c06356bSdh 	msg[1] = LE_32(pwrk->htag);
49044c06356bSdh 	msg[2] = LE_32(pptr->device_id);
49054c06356bSdh 	if (all_cmds) {
49064c06356bSdh 		msg[3] = 0;
49074c06356bSdh 		msg[4] = LE_32(1);
49084c06356bSdh 		pwrk->ptr = NULL;
49094c06356bSdh 		pptr->abort_all_start = gethrtime();
49104c06356bSdh 	} else {
49114c06356bSdh 		msg[3] = LE_32(tag);
49124c06356bSdh 		msg[4] = 0;
4913c3bc407cSdh 		pwrk->abt_htag = tag;
49144c06356bSdh 	}
49154c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
49164c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
49174c06356bSdh 	if (ptr == NULL) {
49184c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
49194c06356bSdh 		pmcs_pwork(pwp, pwrk);
4920*3be32c0fSJesse Butler 		pptr->abort_all_start = 0;
4921c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
49224c06356bSdh 		return (ENOMEM);
49234c06356bSdh 	}
49244c06356bSdh 
49254c06356bSdh 	COPY_MESSAGE(ptr, msg, 5);
49264c06356bSdh 	if (all_cmds) {
4927c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
49284c06356bSdh 		    "%s: aborting all commands for %s device %s. (htag=0x%x)",
49294c06356bSdh 		    __func__, pmcs_get_typename(pptr->dtype), pptr->path,
49304c06356bSdh 		    msg[1]);
49314c06356bSdh 	} else {
4932c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
49334c06356bSdh 		    "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
49344c06356bSdh 		    __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
49354c06356bSdh 		    msg[1]);
49364c06356bSdh 	}
49374c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
49384c06356bSdh 
49394c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
49404c06356bSdh 	if (!wait) {
49414c06356bSdh 		mutex_exit(&pwrk->lock);
49424c06356bSdh 		return (0);
49434c06356bSdh 	}
49444c06356bSdh 
49454c06356bSdh 	abt_htag = pwrk->htag;
4946*3be32c0fSJesse Butler 	pmcs_unlock_phy(pptr);
49474c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
49484c06356bSdh 	pmcs_pwork(pwp, pwrk);
4949*3be32c0fSJesse Butler 	pmcs_lock_phy(pptr);
4950*3be32c0fSJesse Butler 	tgt = pptr->target;
49514c06356bSdh 
49524c06356bSdh 	if (all_cmds) {
49534c06356bSdh 		pptr->abort_all_start = 0;
49544c06356bSdh 		cv_signal(&pptr->abort_all_cv);
49554c06356bSdh 	}
49564c06356bSdh 
49574c06356bSdh 	if (result) {
4958c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49594c06356bSdh 		    "%s: Abort (htag 0x%08x) request timed out",
49604c06356bSdh 		    __func__, abt_htag);
49614c06356bSdh 		if (tgt != NULL) {
49624c06356bSdh 			mutex_enter(&tgt->statlock);
49634c06356bSdh 			if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
49644c06356bSdh 			    (tgt->dev_state !=
49654c06356bSdh 			    PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
4966c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49674c06356bSdh 				    "%s: Trying DS error recovery for tgt 0x%p",
49684c06356bSdh 				    __func__, (void *)tgt);
49694c06356bSdh 				(void) pmcs_send_err_recovery_cmd(pwp,
4970145e0143Sdh 				    PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt);
49714c06356bSdh 			}
49724c06356bSdh 			mutex_exit(&tgt->statlock);
49734c06356bSdh 		}
49744c06356bSdh 		return (ETIMEDOUT);
49754c06356bSdh 	}
49764c06356bSdh 
49774c06356bSdh 	status = LE_32(msg[2]);
49784c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
49794c06356bSdh 		/*
49804c06356bSdh 		 * The only non-success status are IO_NOT_VALID &
49814c06356bSdh 		 * IO_ABORT_IN_PROGRESS.
49824c06356bSdh 		 * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
49834c06356bSdh 		 * status is of concern and this duplicate cmd status can
49844c06356bSdh 		 * be ignored.
49854c06356bSdh 		 * If IO_NOT_VALID, that's not an error per-se.
49864c06356bSdh 		 * For abort of single I/O complete the command anyway.
49874c06356bSdh 		 * If, however, we were aborting all, that is a problem
49884c06356bSdh 		 * as IO_NOT_VALID really means that the IO or device is
49894c06356bSdh 		 * not there. So, discovery process will take of the cleanup.
49904c06356bSdh 		 */
4991c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
4992c3bc407cSdh 		    "%s: abort result 0x%x", __func__, LE_32(msg[2]));
49934c06356bSdh 		if (all_cmds) {
49944c06356bSdh 			PHY_CHANGED(pwp, pptr);
49954c06356bSdh 			RESTART_DISCOVERY(pwp);
49964c06356bSdh 		} else {
49974c06356bSdh 			return (EINVAL);
49984c06356bSdh 		}
49994c06356bSdh 
50004c06356bSdh 		return (0);
50014c06356bSdh 	}
50024c06356bSdh 
50034c06356bSdh 	if (tgt != NULL) {
50044c06356bSdh 		mutex_enter(&tgt->statlock);
50054c06356bSdh 		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
5006c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
50074c06356bSdh 			    "%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
50084c06356bSdh 			    __func__, (void *)tgt);
50094c06356bSdh 			(void) pmcs_send_err_recovery_cmd(pwp,
5010145e0143Sdh 			    PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt);
50114c06356bSdh 		}
50124c06356bSdh 		mutex_exit(&tgt->statlock);
50134c06356bSdh 	}
50144c06356bSdh 
50154c06356bSdh 	return (0);
50164c06356bSdh }
50174c06356bSdh 
50184c06356bSdh /*
50194c06356bSdh  * Issue a task management function to an SSP device.
50204c06356bSdh  *
50214c06356bSdh  * Called with PHY lock held.
50224c06356bSdh  * statlock CANNOT be held upon entry.
50234c06356bSdh  */
50244c06356bSdh int
50254c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
50264c06356bSdh     uint64_t lun, uint32_t *response)
50274c06356bSdh {
50284c06356bSdh 	int result, ds;
50294c06356bSdh 	uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
50304c06356bSdh 	sas_ssp_rsp_iu_t *rptr = (void *)local;
50314c06356bSdh 	static const uint8_t ssp_rsp_evec[] = {
50324c06356bSdh 		0x58, 0x61, 0x56, 0x72, 0x00
50334c06356bSdh 	};
50344c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
50354c06356bSdh 	struct pmcwork *pwrk;
50364c06356bSdh 	pmcs_xscsi_t *xp;
50374c06356bSdh 
50384c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
50394c06356bSdh 	if (pwrk == NULL) {
5040c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
50414c06356bSdh 		return (ENOMEM);
50424c06356bSdh 	}
50434c06356bSdh 	/*
50444c06356bSdh 	 * NB: We use the PMCS_OQ_GENERAL outbound queue
50454c06356bSdh 	 * NB: so as to not get entangled in normal I/O
50464c06356bSdh 	 * NB: processing.
50474c06356bSdh 	 */
5048978d7443SSrikanth Suravajhala 	pwrk->htag |= PMCS_TAG_NONIO_CMD;
50494c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
50504c06356bSdh 	    PMCIN_SSP_INI_TM_START));
50514c06356bSdh 	msg[1] = LE_32(pwrk->htag);
50524c06356bSdh 	msg[2] = LE_32(pptr->device_id);
50534c06356bSdh 	if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
50544c06356bSdh 		msg[3] = LE_32(tag);
50554c06356bSdh 	} else {
50564c06356bSdh 		msg[3] = 0;
50574c06356bSdh 	}
50584c06356bSdh 	msg[4] = LE_32(tmf);
50594c06356bSdh 	msg[5] = BE_32((uint32_t)lun);
50604c06356bSdh 	msg[6] = BE_32((uint32_t)(lun >> 32));
50614c06356bSdh 	msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
50624c06356bSdh 
50634c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50644c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
50654c06356bSdh 	if (ptr == NULL) {
50664c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50674c06356bSdh 		pmcs_pwork(pwp, pwrk);
5068c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
50694c06356bSdh 		return (ENOMEM);
50704c06356bSdh 	}
50714c06356bSdh 	COPY_MESSAGE(ptr, msg, 7);
50724c06356bSdh 	pwrk->arg = msg;
50734c06356bSdh 	pwrk->dtype = pptr->dtype;
50744c06356bSdh 	xp = pptr->target;
507556976565SDavid Hollister 	pwrk->xp = xp;
507656976565SDavid Hollister 
50774c06356bSdh 	if (xp != NULL) {
50784c06356bSdh 		mutex_enter(&xp->statlock);
50794c06356bSdh 		if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
50804c06356bSdh 			mutex_exit(&xp->statlock);
50814c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50824c06356bSdh 			pmcs_pwork(pwp, pwrk);
5083c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not "
5084c3bc407cSdh 			    "sending '%s' because DS is '%s'", __func__,
5085c3bc407cSdh 			    pmcs_tmf2str(tmf), pmcs_status_str
50864c06356bSdh 			    (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
50874c06356bSdh 			return (EIO);
50884c06356bSdh 		}
50894c06356bSdh 		mutex_exit(&xp->statlock);
50904c06356bSdh 	}
50914c06356bSdh 
5092c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
50934c06356bSdh 	    "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
50944c06356bSdh 	    pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
50954c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
50964c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
50974c06356bSdh 
50984c06356bSdh 	pmcs_unlock_phy(pptr);
50994c06356bSdh 	/*
51004c06356bSdh 	 * This is a command sent to the target device, so it can take
51014c06356bSdh 	 * significant amount of time to complete when path & device is busy.
51024c06356bSdh 	 * Set a timeout to 20 seconds
51034c06356bSdh 	 */
51044c06356bSdh 	WAIT_FOR(pwrk, 20000, result);
51054c06356bSdh 	pmcs_pwork(pwp, pwrk);
5106*3be32c0fSJesse Butler 	pmcs_lock_phy(pptr);
5107*3be32c0fSJesse Butler 	xp = pptr->target;
51084c06356bSdh 
51094c06356bSdh 	if (result) {
51104c06356bSdh 		if (xp == NULL) {
51114c06356bSdh 			return (ETIMEDOUT);
51124c06356bSdh 		}
51134c06356bSdh 
51144c06356bSdh 		mutex_enter(&xp->statlock);
51154c06356bSdh 		pmcs_start_dev_state_recovery(xp, pptr);
51164c06356bSdh 		mutex_exit(&xp->statlock);
51174c06356bSdh 		return (ETIMEDOUT);
51184c06356bSdh 	}
51194c06356bSdh 
51204c06356bSdh 	status = LE_32(msg[2]);
51214c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
5122c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51234c06356bSdh 		    "%s: status %s for TMF %s action to %s, lun %llu",
51244c06356bSdh 		    __func__, pmcs_status_str(status),  pmcs_tmf2str(tmf),
51254c06356bSdh 		    pptr->path, (unsigned long long) lun);
51264c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
51274c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
51284c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
51294c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
51304c06356bSdh 		} else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
51314c06356bSdh 			/*
51324c06356bSdh 			 * If the status is IN_RECOVERY, it's an indication
51334c06356bSdh 			 * that it's now time for us to request to have the
51344c06356bSdh 			 * device state set to OPERATIONAL since we're the ones
51354c06356bSdh 			 * that requested recovery to begin with.
51364c06356bSdh 			 */
51374c06356bSdh 			ds = PMCS_DEVICE_STATE_OPERATIONAL;
51384c06356bSdh 		} else {
51394c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
51404c06356bSdh 		}
51414c06356bSdh 		if (xp != NULL) {
51424c06356bSdh 			mutex_enter(&xp->statlock);
51434c06356bSdh 			if (xp->dev_state != ds) {
5144c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51454c06356bSdh 				    "%s: Sending err recovery cmd"
51464c06356bSdh 				    " for tgt 0x%p (status = %s)",
51474c06356bSdh 				    __func__, (void *)xp,
51484c06356bSdh 				    pmcs_status_str(status));
5149145e0143Sdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds,
5150145e0143Sdh 				    pptr, xp);
51514c06356bSdh 			}
51524c06356bSdh 			mutex_exit(&xp->statlock);
51534c06356bSdh 		}
51544c06356bSdh 		return (EIO);
51554c06356bSdh 	} else {
51564c06356bSdh 		ds = PMCS_DEVICE_STATE_OPERATIONAL;
51574c06356bSdh 		if (xp != NULL) {
51584c06356bSdh 			mutex_enter(&xp->statlock);
51594c06356bSdh 			if (xp->dev_state != ds) {
5160c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51614c06356bSdh 				    "%s: Sending err recovery cmd"
51624c06356bSdh 				    " for tgt 0x%p (status = %s)",
51634c06356bSdh 				    __func__, (void *)xp,
51644c06356bSdh 				    pmcs_status_str(status));
5165145e0143Sdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds,
5166145e0143Sdh 				    pptr, xp);
51674c06356bSdh 			}
51684c06356bSdh 			mutex_exit(&xp->statlock);
51694c06356bSdh 		}
51704c06356bSdh 	}
51714c06356bSdh 	if (LE_32(msg[3]) == 0) {
5172c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5173c3bc407cSdh 		    "TMF completed with no response");
51744c06356bSdh 		return (EIO);
51754c06356bSdh 	}
51764c06356bSdh 	pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
51774c06356bSdh 	xd = (uint8_t *)(&msg[5]);
51784c06356bSdh 	xd += SAS_RSP_HDR_SIZE;
51794c06356bSdh 	if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
5180c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51814c06356bSdh 		    "%s: TMF response not RESPONSE DATA (0x%x)",
51824c06356bSdh 		    __func__, rptr->datapres);
51834c06356bSdh 		return (EIO);
51844c06356bSdh 	}
51854c06356bSdh 	if (rptr->response_data_length != 4) {
51864c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
51874c06356bSdh 		    "Bad SAS RESPONSE DATA LENGTH", msg);
51884c06356bSdh 		return (EIO);
51894c06356bSdh 	}
51904c06356bSdh 	(void) memcpy(&status, xd, sizeof (uint32_t));
51914c06356bSdh 	status = BE_32(status);
51924c06356bSdh 	if (response != NULL)
51934c06356bSdh 		*response = status;
51944c06356bSdh 	/*
51954c06356bSdh 	 * The status is actually in the low-order byte.  The upper three
51964c06356bSdh 	 * bytes contain additional information for the TMFs that support them.
51974c06356bSdh 	 * However, at this time we do not issue any of those.  In the other
51984c06356bSdh 	 * cases, the upper three bytes are supposed to be 0, but it appears
51994c06356bSdh 	 * they aren't always.  Just mask them off.
52004c06356bSdh 	 */
52014c06356bSdh 	switch (status & 0xff) {
52024c06356bSdh 	case SAS_RSP_TMF_COMPLETE:
5203c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5204c3bc407cSdh 		    "%s: TMF complete", __func__);
52054c06356bSdh 		result = 0;
52064c06356bSdh 		break;
52074c06356bSdh 	case SAS_RSP_TMF_SUCCEEDED:
5208c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5209c3bc407cSdh 		    "%s: TMF succeeded", __func__);
52104c06356bSdh 		result = 0;
52114c06356bSdh 		break;
52124c06356bSdh 	case SAS_RSP_INVALID_FRAME:
5213c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52144c06356bSdh 		    "%s: TMF returned INVALID FRAME", __func__);
52154c06356bSdh 		result = EIO;
52164c06356bSdh 		break;
52174c06356bSdh 	case SAS_RSP_TMF_NOT_SUPPORTED:
5218c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52194c06356bSdh 		    "%s: TMF returned TMF NOT SUPPORTED", __func__);
52204c06356bSdh 		result = EIO;
52214c06356bSdh 		break;
52224c06356bSdh 	case SAS_RSP_TMF_FAILED:
5223c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52244c06356bSdh 		    "%s: TMF returned TMF FAILED", __func__);
52254c06356bSdh 		result = EIO;
52264c06356bSdh 		break;
52274c06356bSdh 	case SAS_RSP_TMF_INCORRECT_LUN:
5228c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52294c06356bSdh 		    "%s: TMF returned INCORRECT LUN", __func__);
52304c06356bSdh 		result = EIO;
52314c06356bSdh 		break;
52324c06356bSdh 	case SAS_RSP_OVERLAPPED_OIPTTA:
5233c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52344c06356bSdh 		    "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
52354c06356bSdh 		    "ATTEMPTED", __func__);
52364c06356bSdh 		result = EIO;
52374c06356bSdh 		break;
52384c06356bSdh 	default:
5239c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52404c06356bSdh 		    "%s: TMF returned unknown code 0x%x", __func__, status);
52414c06356bSdh 		result = EIO;
52424c06356bSdh 		break;
52434c06356bSdh 	}
52444c06356bSdh 	return (result);
52454c06356bSdh }
52464c06356bSdh 
52474c06356bSdh /*
52484c06356bSdh  * Called with PHY lock held and scratch acquired
52494c06356bSdh  */
52504c06356bSdh int
52514c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
52524c06356bSdh {
52534c06356bSdh 	const char *utag_fail_fmt = "%s: untagged NCQ command failure";
52544c06356bSdh 	const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
52554c06356bSdh 	uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
52564c06356bSdh 	uint8_t *fp = pwp->scratch, ds;
52574c06356bSdh 	fis_t fis;
52584c06356bSdh 	pmcwork_t *pwrk;
52594c06356bSdh 	pmcs_xscsi_t *tgt;
52604c06356bSdh 
52614c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
52624c06356bSdh 	if (pwrk == NULL) {
52634c06356bSdh 		return (ENOMEM);
52644c06356bSdh 	}
5265978d7443SSrikanth Suravajhala 	pwrk->htag |= PMCS_TAG_NONIO_CMD;
52664c06356bSdh 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
52674c06356bSdh 	    PMCIN_SATA_HOST_IO_START));
52684c06356bSdh 	msg[1] = LE_32(pwrk->htag);
52694c06356bSdh 	msg[2] = LE_32(pptr->device_id);
52704c06356bSdh 	msg[3] = LE_32(512);
52714c06356bSdh 	msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
52724c06356bSdh 	msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
52734c06356bSdh 	msg[6] = LE_32(0x10);
52744c06356bSdh 	msg[8] = LE_32(1);
52754c06356bSdh 	msg[9] = 0;
52764c06356bSdh 	msg[10] = 0;
52774c06356bSdh 	msg[11] = 0;
52784c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma));
52794c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma));
52804c06356bSdh 	msg[14] = LE_32(512);
52814c06356bSdh 	msg[15] = 0;
52824c06356bSdh 
52834c06356bSdh 	pwrk->arg = msg;
52844c06356bSdh 	pwrk->dtype = pptr->dtype;
5285*3be32c0fSJesse Butler 	pwrk->xp = pptr->target;
52864c06356bSdh 
52874c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
52884c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
52894c06356bSdh 	if (ptr == NULL) {
52904c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
52914c06356bSdh 		pmcs_pwork(pwp, pwrk);
52924c06356bSdh 		return (ENOMEM);
52934c06356bSdh 	}
52944c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
52954c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
52964c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
52974c06356bSdh 
52984c06356bSdh 	pmcs_unlock_phy(pptr);
52994c06356bSdh 	WAIT_FOR(pwrk, 250, result);
53004c06356bSdh 	pmcs_pwork(pwp, pwrk);
5301*3be32c0fSJesse Butler 	pmcs_lock_phy(pptr);
53024c06356bSdh 
5303c3bc407cSdh 	tgt = pptr->target;
53044c06356bSdh 	if (result) {
53056745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__);
53064c06356bSdh 		return (EIO);
53074c06356bSdh 	}
53084c06356bSdh 	status = LE_32(msg[2]);
53094c06356bSdh 	if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
53104c06356bSdh 		if (tgt == NULL) {
5311c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
53124c06356bSdh 			    "%s: cannot find target for phy 0x%p for "
53134c06356bSdh 			    "dev state recovery", __func__, (void *)pptr);
53144c06356bSdh 			return (EIO);
53154c06356bSdh 		}
53164c06356bSdh 
53174c06356bSdh 		mutex_enter(&tgt->statlock);
53184c06356bSdh 
53194c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
53204c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
53214c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
53224c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
53234c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
53244c06356bSdh 		} else {
53254c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
53264c06356bSdh 		}
53274c06356bSdh 		if (tgt->dev_state != ds) {
5328c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying "
5329c3bc407cSdh 			    "SATA DS Recovery for tgt(0x%p) for status(%s)",
53304c06356bSdh 			    __func__, (void *)tgt, pmcs_status_str(status));
5331145e0143Sdh 			(void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt);
53324c06356bSdh 		}
53334c06356bSdh 
53344c06356bSdh 		mutex_exit(&tgt->statlock);
53354c06356bSdh 		return (EIO);
53364c06356bSdh 	}
53374c06356bSdh 	fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
53384c06356bSdh 	fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
53394c06356bSdh 	fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
53404c06356bSdh 	fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
53414c06356bSdh 	fis[4] = 0;
53424c06356bSdh 	if (fp[0] & 0x80) {
5343c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
5344c3bc407cSdh 		    utag_fail_fmt, __func__);
53454c06356bSdh 	} else {
5346c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
5347c3bc407cSdh 		    tag_fail_fmt, __func__, fp[0] & 0x1f);
53484c06356bSdh 	}
53494c06356bSdh 	pmcs_fis_dump(pwp, fis);
53504c06356bSdh 	pptr->need_rl_ext = 0;
53514c06356bSdh 	return (0);
53524c06356bSdh }
53534c06356bSdh 
53544c06356bSdh /*
53554c06356bSdh  * Transform a structure from CPU to Device endian format, or
53564c06356bSdh  * vice versa, based upon a transformation vector.
53574c06356bSdh  *
53584c06356bSdh  * A transformation vector is an array of bytes, each byte
53594c06356bSdh  * of which is defined thusly:
53604c06356bSdh  *
53614c06356bSdh  *  bit 7: from CPU to desired endian, otherwise from desired endian
53624c06356bSdh  *	   to CPU format
53634c06356bSdh  *  bit 6: Big Endian, else Little Endian
53644c06356bSdh  *  bits 5-4:
53654c06356bSdh  *       00 Undefined
53664c06356bSdh  *       01 One Byte quantities
53674c06356bSdh  *       02 Two Byte quantities
53684c06356bSdh  *       03 Four Byte quantities
53694c06356bSdh  *
53704c06356bSdh  *  bits 3-0:
53714c06356bSdh  *       00 Undefined
53724c06356bSdh  *       Number of quantities to transform
53734c06356bSdh  *
53744c06356bSdh  * The vector is terminated by a 0 value.
53754c06356bSdh  */
53764c06356bSdh 
53774c06356bSdh void
53784c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
53794c06356bSdh     const uint8_t *xfvec)
53804c06356bSdh {
53814c06356bSdh 	uint8_t c, *out = orig_out, *in = orig_in;
53824c06356bSdh 
53834c06356bSdh 	if (xfvec == NULL) {
5384c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5385c3bc407cSdh 		    "%s: null xfvec", __func__);
53864c06356bSdh 		return;
53874c06356bSdh 	}
53884c06356bSdh 	if (out == NULL) {
5389c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5390c3bc407cSdh 		    "%s: null out", __func__);
53914c06356bSdh 		return;
53924c06356bSdh 	}
53934c06356bSdh 	if (in == NULL) {
5394c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5395c3bc407cSdh 		    "%s: null in", __func__);
53964c06356bSdh 		return;
53974c06356bSdh 	}
53984c06356bSdh 	while ((c = *xfvec++) != 0) {
53994c06356bSdh 		int nbyt = (c & 0xf);
54004c06356bSdh 		int size = (c >> 4) & 0x3;
54014c06356bSdh 		int bige = (c >> 4) & 0x4;
54024c06356bSdh 
54034c06356bSdh 		switch (size) {
54044c06356bSdh 		case 1:
54054c06356bSdh 		{
54064c06356bSdh 			while (nbyt-- > 0) {
54074c06356bSdh 				*out++ = *in++;
54084c06356bSdh 			}
54094c06356bSdh 			break;
54104c06356bSdh 		}
54114c06356bSdh 		case 2:
54124c06356bSdh 		{
54134c06356bSdh 			uint16_t tmp;
54144c06356bSdh 			while (nbyt-- > 0) {
54154c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint16_t));
54164c06356bSdh 				if (bige) {
54174c06356bSdh 					tmp = BE_16(tmp);
54184c06356bSdh 				} else {
54194c06356bSdh 					tmp = LE_16(tmp);
54204c06356bSdh 				}
54214c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint16_t));
54224c06356bSdh 				out += sizeof (uint16_t);
54234c06356bSdh 				in += sizeof (uint16_t);
54244c06356bSdh 			}
54254c06356bSdh 			break;
54264c06356bSdh 		}
54274c06356bSdh 		case 3:
54284c06356bSdh 		{
54294c06356bSdh 			uint32_t tmp;
54304c06356bSdh 			while (nbyt-- > 0) {
54314c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint32_t));
54324c06356bSdh 				if (bige) {
54334c06356bSdh 					tmp = BE_32(tmp);
54344c06356bSdh 				} else {
54354c06356bSdh 					tmp = LE_32(tmp);
54364c06356bSdh 				}
54374c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint32_t));
54384c06356bSdh 				out += sizeof (uint32_t);
54394c06356bSdh 				in += sizeof (uint32_t);
54404c06356bSdh 			}
54414c06356bSdh 			break;
54424c06356bSdh 		}
54434c06356bSdh 		default:
5444c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5445c3bc407cSdh 			    "%s: bad size", __func__);
54464c06356bSdh 			return;
54474c06356bSdh 		}
54484c06356bSdh 	}
54494c06356bSdh }
54504c06356bSdh 
54514c06356bSdh const char *
54524c06356bSdh pmcs_get_rate(unsigned int linkrt)
54534c06356bSdh {
54544c06356bSdh 	const char *rate;
54554c06356bSdh 	switch (linkrt) {
54564c06356bSdh 	case SAS_LINK_RATE_1_5GBIT:
54574c06356bSdh 		rate = "1.5";
54584c06356bSdh 		break;
54594c06356bSdh 	case SAS_LINK_RATE_3GBIT:
54604c06356bSdh 		rate = "3.0";
54614c06356bSdh 		break;
54624c06356bSdh 	case SAS_LINK_RATE_6GBIT:
54634c06356bSdh 		rate = "6.0";
54644c06356bSdh 		break;
54654c06356bSdh 	default:
54664c06356bSdh 		rate = "???";
54674c06356bSdh 		break;
54684c06356bSdh 	}
54694c06356bSdh 	return (rate);
54704c06356bSdh }
54714c06356bSdh 
54724c06356bSdh const char *
54734c06356bSdh pmcs_get_typename(pmcs_dtype_t type)
54744c06356bSdh {
54754c06356bSdh 	switch (type) {
54764c06356bSdh 	case NOTHING:
54774c06356bSdh 		return ("NIL");
54784c06356bSdh 	case SATA:
54794c06356bSdh 		return ("SATA");
54804c06356bSdh 	case SAS:
54814c06356bSdh 		return ("SSP");
54824c06356bSdh 	case EXPANDER:
54834c06356bSdh 		return ("EXPANDER");
54844c06356bSdh 	}
54854c06356bSdh 	return ("????");
54864c06356bSdh }
54874c06356bSdh 
54884c06356bSdh const char *
54894c06356bSdh pmcs_tmf2str(int tmf)
54904c06356bSdh {
54914c06356bSdh 	switch (tmf) {
54924c06356bSdh 	case SAS_ABORT_TASK:
54934c06356bSdh 		return ("Abort Task");
54944c06356bSdh 	case SAS_ABORT_TASK_SET:
54954c06356bSdh 		return ("Abort Task Set");
54964c06356bSdh 	case SAS_CLEAR_TASK_SET:
54974c06356bSdh 		return ("Clear Task Set");
54984c06356bSdh 	case SAS_LOGICAL_UNIT_RESET:
54994c06356bSdh 		return ("Logical Unit Reset");
55004c06356bSdh 	case SAS_I_T_NEXUS_RESET:
55014c06356bSdh 		return ("I_T Nexus Reset");
55024c06356bSdh 	case SAS_CLEAR_ACA:
55034c06356bSdh 		return ("Clear ACA");
55044c06356bSdh 	case SAS_QUERY_TASK:
55054c06356bSdh 		return ("Query Task");
55064c06356bSdh 	case SAS_QUERY_TASK_SET:
55074c06356bSdh 		return ("Query Task Set");
55084c06356bSdh 	case SAS_QUERY_UNIT_ATTENTION:
55094c06356bSdh 		return ("Query Unit Attention");
55104c06356bSdh 	default:
55114c06356bSdh 		return ("Unknown");
55124c06356bSdh 	}
55134c06356bSdh }
55144c06356bSdh 
55154c06356bSdh const char *
55164c06356bSdh pmcs_status_str(uint32_t status)
55174c06356bSdh {
55184c06356bSdh 	switch (status) {
55194c06356bSdh 	case PMCOUT_STATUS_OK:
55204c06356bSdh 		return ("OK");
55214c06356bSdh 	case PMCOUT_STATUS_ABORTED:
55224c06356bSdh 		return ("ABORTED");
55234c06356bSdh 	case PMCOUT_STATUS_OVERFLOW:
55244c06356bSdh 		return ("OVERFLOW");
55254c06356bSdh 	case PMCOUT_STATUS_UNDERFLOW:
55264c06356bSdh 		return ("UNDERFLOW");
55274c06356bSdh 	case PMCOUT_STATUS_FAILED:
55284c06356bSdh 		return ("FAILED");
55294c06356bSdh 	case PMCOUT_STATUS_ABORT_RESET:
55304c06356bSdh 		return ("ABORT_RESET");
55314c06356bSdh 	case PMCOUT_STATUS_IO_NOT_VALID:
55324c06356bSdh 		return ("IO_NOT_VALID");
55334c06356bSdh 	case PMCOUT_STATUS_NO_DEVICE:
55344c06356bSdh 		return ("NO_DEVICE");
55354c06356bSdh 	case PMCOUT_STATUS_ILLEGAL_PARAMETER:
55364c06356bSdh 		return ("ILLEGAL_PARAMETER");
55374c06356bSdh 	case PMCOUT_STATUS_LINK_FAILURE:
55384c06356bSdh 		return ("LINK_FAILURE");
55394c06356bSdh 	case PMCOUT_STATUS_PROG_ERROR:
55404c06356bSdh 		return ("PROG_ERROR");
55414c06356bSdh 	case PMCOUT_STATUS_EDC_IN_ERROR:
55424c06356bSdh 		return ("EDC_IN_ERROR");
55434c06356bSdh 	case PMCOUT_STATUS_EDC_OUT_ERROR:
55444c06356bSdh 		return ("EDC_OUT_ERROR");
55454c06356bSdh 	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
55464c06356bSdh 		return ("ERROR_HW_TIMEOUT");
55474c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_BREAK:
55484c06356bSdh 		return ("XFER_ERR_BREAK");
55494c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
55504c06356bSdh 		return ("XFER_ERR_PHY_NOT_READY");
55514c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
55524c06356bSdh 		return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
55534c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
55544c06356bSdh 		return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
55554c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
55564c06356bSdh 		return ("OPEN_CNX_ERROR_BREAK");
55574c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
55584c06356bSdh 		return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
55594c06356bSdh 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
55604c06356bSdh 		return ("OPENCNX_ERROR_BAD_DESTINATION");
55614c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
55624c06356bSdh 		return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
55634c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
55644c06356bSdh 		return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
55654c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
55664c06356bSdh 		return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
55679aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
55689aed1621SDavid Hollister 		return ("OPEN_CNX_ERROR_UNKNOWN_ERROR");
55694c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
55704c06356bSdh 		return ("IO_XFER_ERROR_NAK_RECEIVED");
55714c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
55724c06356bSdh 		return ("XFER_ERROR_ACK_NAK_TIMEOUT");
55734c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
55744c06356bSdh 		return ("XFER_ERROR_PEER_ABORTED");
55754c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
55764c06356bSdh 		return ("XFER_ERROR_RX_FRAME");
55774c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
55784c06356bSdh 		return ("IO_XFER_ERROR_DMA");
55794c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
55804c06356bSdh 		return ("XFER_ERROR_CREDIT_TIMEOUT");
55814c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
55824c06356bSdh 		return ("XFER_ERROR_SATA_LINK_TIMEOUT");
55834c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA:
55844c06356bSdh 		return ("XFER_ERROR_SATA");
55854c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
55864c06356bSdh 		return ("XFER_ERROR_REJECTED_NCQ_MODE");
55874c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
55884c06356bSdh 		return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
55894c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
55904c06356bSdh 		return ("XFER_ERROR_ABORTED_NCQ_MODE");
55914c06356bSdh 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
55924c06356bSdh 		return ("IO_XFER_OPEN_RETRY_TIMEOUT");
55934c06356bSdh 	case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
55944c06356bSdh 		return ("SMP_RESP_CONNECTION_ERROR");
55954c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
55964c06356bSdh 		return ("XFER_ERROR_UNEXPECTED_PHASE");
55974c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
55984c06356bSdh 		return ("XFER_ERROR_RDY_OVERRUN");
55994c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
56004c06356bSdh 		return ("XFER_ERROR_RDY_NOT_EXPECTED");
56014c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
56024c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
56034c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
56044c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
56054c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
56064c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
56074c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
56084c06356bSdh 		return ("XFER_ERROR_OFFSET_MISMATCH");
56094c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
56104c06356bSdh 		return ("XFER_ERROR_ZERO_DATA_LEN");
56114c06356bSdh 	case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
56124c06356bSdh 		return ("XFER_CMD_FRAME_ISSUED");
56134c06356bSdh 	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
56144c06356bSdh 		return ("ERROR_INTERNAL_SMP_RESOURCE");
56154c06356bSdh 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
56164c06356bSdh 		return ("IO_PORT_IN_RESET");
56174c06356bSdh 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
56184c06356bSdh 		return ("DEVICE STATE NON-OPERATIONAL");
56194c06356bSdh 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
56204c06356bSdh 		return ("DEVICE STATE IN RECOVERY");
5621a25672a1SDavid Hollister 	case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
5622a25672a1SDavid Hollister 		return ("OPEN CNX ERR HW RESOURCE BUSY");
56234c06356bSdh 	default:
56244c06356bSdh 		return (NULL);
56254c06356bSdh 	}
56264c06356bSdh }
56274c06356bSdh 
56284c06356bSdh uint64_t
56294c06356bSdh pmcs_barray2wwn(uint8_t ba[8])
56304c06356bSdh {
56314c06356bSdh 	uint64_t result = 0;
56324c06356bSdh 	int i;
56334c06356bSdh 
56344c06356bSdh 	for (i = 0; i < 8; i++) {
56354c06356bSdh 		result <<= 8;
56364c06356bSdh 		result |= ba[i];
56374c06356bSdh 	}
56384c06356bSdh 	return (result);
56394c06356bSdh }
56404c06356bSdh 
56414c06356bSdh void
56424c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
56434c06356bSdh {
56444c06356bSdh 	int i;
56454c06356bSdh 	for (i = 0; i < 8; i++) {
56464c06356bSdh 		ba[7 - i] = wwn & 0xff;
56474c06356bSdh 		wwn >>= 8;
56484c06356bSdh 	}
56494c06356bSdh }
56504c06356bSdh 
56514c06356bSdh void
56524c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp)
56534c06356bSdh {
56544c06356bSdh 	const char *fwsupport;
56554c06356bSdh 	switch (PMCS_FW_TYPE(pwp)) {
56564c06356bSdh 	case PMCS_FW_TYPE_RELEASED:
56574c06356bSdh 		fwsupport = "Released";
56584c06356bSdh 		break;
56594c06356bSdh 	case PMCS_FW_TYPE_DEVELOPMENT:
56604c06356bSdh 		fwsupport = "Development";
56614c06356bSdh 		break;
56624c06356bSdh 	case PMCS_FW_TYPE_ALPHA:
56634c06356bSdh 		fwsupport = "Alpha";
56644c06356bSdh 		break;
56654c06356bSdh 	case PMCS_FW_TYPE_BETA:
56664c06356bSdh 		fwsupport = "Beta";
56674c06356bSdh 		break;
56684c06356bSdh 	default:
56694c06356bSdh 		fwsupport = "Special";
56704c06356bSdh 		break;
56714c06356bSdh 	}
5672c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
56732ac4abe8SDavid Hollister 	    "Chip Revision: %c; F/W Revision %x.%x.%x %s (ILA rev %08x)",
56742ac4abe8SDavid Hollister 	    'A' + pwp->chiprev, PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp),
56752ac4abe8SDavid Hollister 	    PMCS_FW_MICRO(pwp), fwsupport, pwp->ila_ver);
56764c06356bSdh }
56774c06356bSdh 
56784c06356bSdh void
56794c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
56804c06356bSdh {
56814c06356bSdh 	if (pptr->parent) {
56824c06356bSdh 		pmcs_phy_name(pwp, pptr->parent, obuf, olen);
56834c06356bSdh 		(void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
56844c06356bSdh 	} else {
56854c06356bSdh 		(void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
56864c06356bSdh 	}
56874c06356bSdh }
56884c06356bSdh 
56894c06356bSdh /*
56904c06356bSdh  * This function is called as a sanity check to ensure that a newly registered
56914c06356bSdh  * PHY doesn't have a device_id that exists with another registered PHY.
56924c06356bSdh  */
56934c06356bSdh static boolean_t
56944c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
56954c06356bSdh {
56969aed1621SDavid Hollister 	pmcs_phy_t *pptr, *pchild;
56974c06356bSdh 	boolean_t rval;
56984c06356bSdh 
56994c06356bSdh 	pptr = parent;
57004c06356bSdh 
57014c06356bSdh 	while (pptr) {
57024c06356bSdh 		if (pptr->valid_device_id && (pptr != phyp) &&
57034c06356bSdh 		    (pptr->device_id == device_id)) {
57049aed1621SDavid Hollister 			/*
57059aed1621SDavid Hollister 			 * This can still be OK if both of these PHYs actually
57069aed1621SDavid Hollister 			 * represent the same device (e.g. expander).  It could
57079aed1621SDavid Hollister 			 * be a case of a new "primary" PHY.  If the SAS address
57089aed1621SDavid Hollister 			 * is the same and they have the same parent, we'll
57099aed1621SDavid Hollister 			 * accept this if the PHY to be registered is the
57109aed1621SDavid Hollister 			 * primary.
57119aed1621SDavid Hollister 			 */
57129aed1621SDavid Hollister 			if ((phyp->parent == pptr->parent) &&
57139aed1621SDavid Hollister 			    (memcmp(phyp->sas_address,
57149aed1621SDavid Hollister 			    pptr->sas_address, 8) == 0) && (phyp->width > 1)) {
57159aed1621SDavid Hollister 				/*
57169aed1621SDavid Hollister 				 * Move children over to the new primary and
57179aed1621SDavid Hollister 				 * update both PHYs
57189aed1621SDavid Hollister 				 */
57199aed1621SDavid Hollister 				pmcs_lock_phy(pptr);
57209aed1621SDavid Hollister 				phyp->children = pptr->children;
57219aed1621SDavid Hollister 				pchild = phyp->children;
57229aed1621SDavid Hollister 				while (pchild) {
57239aed1621SDavid Hollister 					pchild->parent = phyp;
57249aed1621SDavid Hollister 					pchild = pchild->sibling;
57259aed1621SDavid Hollister 				}
57269aed1621SDavid Hollister 				phyp->subsidiary = 0;
57279aed1621SDavid Hollister 				phyp->ncphy = pptr->ncphy;
57289aed1621SDavid Hollister 				/*
57299aed1621SDavid Hollister 				 * device_id, valid_device_id, and configured
57309aed1621SDavid Hollister 				 * will be set by the caller
57319aed1621SDavid Hollister 				 */
57329aed1621SDavid Hollister 				pptr->children = NULL;
57339aed1621SDavid Hollister 				pptr->subsidiary = 1;
57349aed1621SDavid Hollister 				pptr->ncphy = 0;
57359aed1621SDavid Hollister 				pmcs_unlock_phy(pptr);
57369aed1621SDavid Hollister 				pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
57379aed1621SDavid Hollister 				    "%s: Moving device_id %d from PHY %s to %s",
57389aed1621SDavid Hollister 				    __func__, device_id, pptr->path,
57399aed1621SDavid Hollister 				    phyp->path);
57409aed1621SDavid Hollister 				return (B_TRUE);
57419aed1621SDavid Hollister 			}
5742c3bc407cSdh 			pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
57434c06356bSdh 			    "%s: phy %s already exists as %s with "
57444c06356bSdh 			    "device id 0x%x", __func__, phyp->path,
57454c06356bSdh 			    pptr->path, device_id);
57464c06356bSdh 			return (B_FALSE);
57474c06356bSdh 		}
57484c06356bSdh 
57494c06356bSdh 		if (pptr->children) {
57504c06356bSdh 			rval = pmcs_validate_devid(pptr->children, phyp,
57514c06356bSdh 			    device_id);
57524c06356bSdh 			if (rval == B_FALSE) {
57534c06356bSdh 				return (rval);
57544c06356bSdh 			}
57554c06356bSdh 		}
57564c06356bSdh 
57574c06356bSdh 		pptr = pptr->sibling;
57584c06356bSdh 	}
57594c06356bSdh 
57604c06356bSdh 	/* This PHY and device_id are valid */
57614c06356bSdh 	return (B_TRUE);
57624c06356bSdh }
57634c06356bSdh 
57644c06356bSdh /*
57654c06356bSdh  * If the PHY is found, it is returned locked
57664c06356bSdh  */
57674c06356bSdh static pmcs_phy_t *
57684c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
57694c06356bSdh {
57704c06356bSdh 	pmcs_phy_t *matched_phy, *cphyp, *nphyp;
57714c06356bSdh 
57724c06356bSdh 	ASSERT(!mutex_owned(&phyp->phy_lock));
57734c06356bSdh 
57744c06356bSdh 	while (phyp) {
57754c06356bSdh 		pmcs_lock_phy(phyp);
57764c06356bSdh 
57774c06356bSdh 		if (phyp->valid_device_id) {
57784c06356bSdh 			if (memcmp(phyp->sas_address, wwn, 8) == 0) {
57794c06356bSdh 				return (phyp);
57804c06356bSdh 			}
57814c06356bSdh 		}
57824c06356bSdh 
57834c06356bSdh 		if (phyp->children) {
57844c06356bSdh 			cphyp = phyp->children;
57854c06356bSdh 			pmcs_unlock_phy(phyp);
57864c06356bSdh 			matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
57874c06356bSdh 			if (matched_phy) {
57884c06356bSdh 				ASSERT(mutex_owned(&matched_phy->phy_lock));
57894c06356bSdh 				return (matched_phy);
57904c06356bSdh 			}
57914c06356bSdh 			pmcs_lock_phy(phyp);
57924c06356bSdh 		}
57934c06356bSdh 
57944c06356bSdh 		/*
57954c06356bSdh 		 * Only iterate through non-root PHYs
57964c06356bSdh 		 */
57974c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
57984c06356bSdh 			pmcs_unlock_phy(phyp);
57994c06356bSdh 			phyp = NULL;
58004c06356bSdh 		} else {
58014c06356bSdh 			nphyp = phyp->sibling;
58024c06356bSdh 			pmcs_unlock_phy(phyp);
58034c06356bSdh 			phyp = nphyp;
58044c06356bSdh 		}
58054c06356bSdh 	}
58064c06356bSdh 
58074c06356bSdh 	return (NULL);
58084c06356bSdh }
58094c06356bSdh 
58104c06356bSdh pmcs_phy_t *
58114c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
58124c06356bSdh {
58134c06356bSdh 	uint8_t ebstr[8];
58144c06356bSdh 	pmcs_phy_t *pptr, *matched_phy;
58154c06356bSdh 
58164c06356bSdh 	pmcs_wwn2barray(wwn, ebstr);
58174c06356bSdh 
58184c06356bSdh 	pptr = pwp->root_phys;
58194c06356bSdh 	while (pptr) {
58204c06356bSdh 		matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
58214c06356bSdh 		if (matched_phy) {
58224c06356bSdh 			ASSERT(mutex_owned(&matched_phy->phy_lock));
58234c06356bSdh 			return (matched_phy);
58244c06356bSdh 		}
58254c06356bSdh 
58264c06356bSdh 		pptr = pptr->sibling;
58274c06356bSdh 	}
58284c06356bSdh 
58294c06356bSdh 	return (NULL);
58304c06356bSdh }
58314c06356bSdh 
58324c06356bSdh 
58334c06356bSdh /*
58344c06356bSdh  * pmcs_find_phy_by_sas_address
58354c06356bSdh  *
58364c06356bSdh  * Find a PHY that both matches "sas_addr" and is on "iport".
58374c06356bSdh  * If a matching PHY is found, it is returned locked.
58384c06356bSdh  */
58394c06356bSdh pmcs_phy_t *
58404c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
58414c06356bSdh     pmcs_phy_t *root, char *sas_addr)
58424c06356bSdh {
58434c06356bSdh 	int ua_form = 1;
58444c06356bSdh 	uint64_t wwn;
58454c06356bSdh 	char addr[PMCS_MAX_UA_SIZE];
58464c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pchild;
58474c06356bSdh 
58484c06356bSdh 	if (root == NULL) {
58494c06356bSdh 		pptr = pwp->root_phys;
58504c06356bSdh 	} else {
58514c06356bSdh 		pptr = root;
58524c06356bSdh 	}
58534c06356bSdh 
58544c06356bSdh 	while (pptr) {
58554c06356bSdh 		pmcs_lock_phy(pptr);
58564c06356bSdh 		/*
58574c06356bSdh 		 * If the PHY is dead or does not have a valid device ID,
58584c06356bSdh 		 * skip it.
58594c06356bSdh 		 */
58604c06356bSdh 		if ((pptr->dead) || (!pptr->valid_device_id)) {
58614c06356bSdh 			goto next_phy;
58624c06356bSdh 		}
58634c06356bSdh 
58644c06356bSdh 		if (pptr->iport != iport) {
58654c06356bSdh 			goto next_phy;
58664c06356bSdh 		}
58674c06356bSdh 
58684c06356bSdh 		wwn = pmcs_barray2wwn(pptr->sas_address);
58694c06356bSdh 		(void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
58704c06356bSdh 		if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
58714c06356bSdh 			return (pptr);
58724c06356bSdh 		}
58734c06356bSdh 
58744c06356bSdh 		if (pptr->children) {
58754c06356bSdh 			pchild = pptr->children;
58764c06356bSdh 			pmcs_unlock_phy(pptr);
58774c06356bSdh 			pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
58784c06356bSdh 			    sas_addr);
58794c06356bSdh 			if (pnext) {
58804c06356bSdh 				return (pnext);
58814c06356bSdh 			}
58824c06356bSdh 			pmcs_lock_phy(pptr);
58834c06356bSdh 		}
58844c06356bSdh 
58854c06356bSdh next_phy:
58864c06356bSdh 		pnext = pptr->sibling;
58874c06356bSdh 		pmcs_unlock_phy(pptr);
58884c06356bSdh 		pptr = pnext;
58894c06356bSdh 	}
58904c06356bSdh 
58914c06356bSdh 	return (NULL);
58924c06356bSdh }
58934c06356bSdh 
58944c06356bSdh void
58954c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
58964c06356bSdh {
58974c06356bSdh 	switch (fis[0] & 0xff) {
58984c06356bSdh 	case FIS_REG_H2DEV:
5899c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5900c3bc407cSdh 		    "FIS REGISTER HOST TO DEVICE: "
59014c06356bSdh 		    "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
59024c06356bSdh 		    "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
59034c06356bSdh 		    BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
59044c06356bSdh 		    (unsigned long long)
59054c06356bSdh 		    (((uint64_t)fis[2] & 0x00ffffff) << 24 |
59064c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
59074c06356bSdh 		break;
59084c06356bSdh 	case FIS_REG_D2H:
5909c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5910c3bc407cSdh 		    "FIS REGISTER DEVICE TO HOST: Status=0x%02x "
5911c3bc407cSdh 		    "Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
59124c06356bSdh 		    BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
59134c06356bSdh 		    (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
59144c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
59154c06356bSdh 		break;
59164c06356bSdh 	default:
5917c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5918a25672a1SDavid Hollister 		    "FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
5919a25672a1SDavid Hollister 		    fis[0], fis[1], fis[2], fis[3], fis[4]);
59204c06356bSdh 		break;
59214c06356bSdh 	}
59224c06356bSdh }
59234c06356bSdh 
59244c06356bSdh void
59254c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
59264c06356bSdh {
59274c06356bSdh 	uint32_t *mb = arg;
59284c06356bSdh 	size_t i;
59294c06356bSdh 
5930c3bc407cSdh 	pmcs_prt(pwp, level, NULL, NULL, msg);
59314c06356bSdh 	for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
5932c3bc407cSdh 		pmcs_prt(pwp, level, NULL, NULL,
5933c3bc407cSdh 		    "Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x",
5934c3bc407cSdh 		    i * sizeof (uint32_t), LE_32(mb[i]),
5935c3bc407cSdh 		    LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3]));
59364c06356bSdh 	}
59374c06356bSdh }
59384c06356bSdh 
59394c06356bSdh /*
59404c06356bSdh  * If phyp == NULL we're being called from the worker thread, in which
59414c06356bSdh  * case we need to check all the PHYs.  In this case, the softstate lock
59424c06356bSdh  * will be held.
59434c06356bSdh  * If phyp is non-NULL, just issue the spinup release for the specified PHY
59444c06356bSdh  * (which will already be locked).
59454c06356bSdh  */
59464c06356bSdh void
59474c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
59484c06356bSdh {
59494c06356bSdh 	uint32_t *msg;
59504c06356bSdh 	struct pmcwork *pwrk;
59514c06356bSdh 	pmcs_phy_t *tphyp;
59524c06356bSdh 
59534c06356bSdh 	if (phyp != NULL) {
59544c06356bSdh 		ASSERT(mutex_owned(&phyp->phy_lock));
5955c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
59564c06356bSdh 		    "%s: Issuing spinup release only for PHY %s", __func__,
59574c06356bSdh 		    phyp->path);
59584c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
59594c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
59604c06356bSdh 		if (msg == NULL || (pwrk =
59614c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
59624c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
59634c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
59644c06356bSdh 			return;
59654c06356bSdh 		}
59664c06356bSdh 
59674c06356bSdh 		phyp->spinup_hold = 0;
59684c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
5969978d7443SSrikanth Suravajhala 		pwrk->htag |= PMCS_TAG_NONIO_CMD;
59704c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
59714c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
59724c06356bSdh 		msg[1] = LE_32(pwrk->htag);
59734c06356bSdh 		msg[2] = LE_32((0x10 << 8) | phyp->phynum);
59744c06356bSdh 
59754c06356bSdh 		pwrk->dtype = phyp->dtype;
59764c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5977*3be32c0fSJesse Butler 		pwrk->xp = phyp->target;
59784c06356bSdh 		mutex_exit(&pwrk->lock);
59794c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
59804c06356bSdh 		return;
59814c06356bSdh 	}
59824c06356bSdh 
59834c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
59844c06356bSdh 
59854c06356bSdh 	tphyp = pwp->root_phys;
59864c06356bSdh 	while (tphyp) {
59874c06356bSdh 		pmcs_lock_phy(tphyp);
59884c06356bSdh 		if (tphyp->spinup_hold == 0) {
59894c06356bSdh 			pmcs_unlock_phy(tphyp);
59904c06356bSdh 			tphyp = tphyp->sibling;
59914c06356bSdh 			continue;
59924c06356bSdh 		}
59934c06356bSdh 
5994*3be32c0fSJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, tphyp, NULL,
59954c06356bSdh 		    "%s: Issuing spinup release for PHY %s", __func__,
5996*3be32c0fSJesse Butler 		    tphyp->path);
59974c06356bSdh 
59984c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
59994c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60004c06356bSdh 		if (msg == NULL || (pwrk =
60014c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
60024c06356bSdh 			pmcs_unlock_phy(tphyp);
60034c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
60044c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
60054c06356bSdh 			break;
60064c06356bSdh 		}
60074c06356bSdh 
60084c06356bSdh 		tphyp->spinup_hold = 0;
60094c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
60104c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
60114c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
60124c06356bSdh 		msg[1] = LE_32(pwrk->htag);
60134c06356bSdh 		msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
60144c06356bSdh 
6015*3be32c0fSJesse Butler 		pwrk->dtype = tphyp->dtype;
60164c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
6017*3be32c0fSJesse Butler 		pwrk->xp = tphyp->target;
60184c06356bSdh 		mutex_exit(&pwrk->lock);
60194c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60204c06356bSdh 		pmcs_unlock_phy(tphyp);
60214c06356bSdh 
60224c06356bSdh 		tphyp = tphyp->sibling;
60234c06356bSdh 	}
60244c06356bSdh }
60254c06356bSdh 
60264c06356bSdh /*
60274c06356bSdh  * Abort commands on dead PHYs and deregister them as well as removing
60284c06356bSdh  * the associated targets.
60294c06356bSdh  */
60304c06356bSdh static int
60314c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
60324c06356bSdh {
60334c06356bSdh 	pmcs_phy_t *pnext, *pchild;
60344c06356bSdh 	boolean_t remove_device;
60354c06356bSdh 	int rval = 0;
60364c06356bSdh 
60374c06356bSdh 	while (phyp) {
60384c06356bSdh 		pmcs_lock_phy(phyp);
60394c06356bSdh 		pchild = phyp->children;
60404c06356bSdh 		pnext = phyp->sibling;
60414c06356bSdh 		pmcs_unlock_phy(phyp);
60424c06356bSdh 
60434c06356bSdh 		if (pchild) {
60444c06356bSdh 			rval = pmcs_kill_devices(pwp, pchild);
60454c06356bSdh 			if (rval) {
60464c06356bSdh 				return (rval);
60474c06356bSdh 			}
60484c06356bSdh 		}
60494c06356bSdh 
60504c06356bSdh 		mutex_enter(&pwp->lock);
60514c06356bSdh 		pmcs_lock_phy(phyp);
60524c06356bSdh 		if (phyp->dead && phyp->valid_device_id) {
60534c06356bSdh 			remove_device = B_TRUE;
60544c06356bSdh 		} else {
60554c06356bSdh 			remove_device = B_FALSE;
60564c06356bSdh 		}
60574c06356bSdh 
60584c06356bSdh 		if (remove_device) {
60594c06356bSdh 			pmcs_remove_device(pwp, phyp);
60604c06356bSdh 			mutex_exit(&pwp->lock);
60614c06356bSdh 
60624c06356bSdh 			rval = pmcs_kill_device(pwp, phyp);
60634c06356bSdh 			if (rval) {
60644c06356bSdh 				pmcs_unlock_phy(phyp);
60654c06356bSdh 				return (rval);
60664c06356bSdh 			}
60674c06356bSdh 		} else {
60684c06356bSdh 			mutex_exit(&pwp->lock);
60694c06356bSdh 		}
60704c06356bSdh 
60714c06356bSdh 		pmcs_unlock_phy(phyp);
60724c06356bSdh 		phyp = pnext;
60734c06356bSdh 	}
60744c06356bSdh 
60754c06356bSdh 	return (rval);
60764c06356bSdh }
60774c06356bSdh 
60784c06356bSdh /*
60794c06356bSdh  * Called with PHY locked
60804c06356bSdh  */
60814c06356bSdh int
60824c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
60834c06356bSdh {
6084*3be32c0fSJesse Butler 	int rval;
60854c06356bSdh 
6086c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s",
60874c06356bSdh 	    pmcs_get_typename(pptr->dtype), pptr->path);
60884c06356bSdh 
60894c06356bSdh 	/*
60904c06356bSdh 	 * There may be an outstanding ABORT_ALL running, which we wouldn't
60914c06356bSdh 	 * know just by checking abort_pending.  We can, however, check
60924c06356bSdh 	 * abort_all_start.  If it's non-zero, there is one, and we'll just
60934c06356bSdh 	 * sit here and wait for it to complete.  If we don't, we'll remove
60944c06356bSdh 	 * the device while there are still commands pending.
60954c06356bSdh 	 */
60964c06356bSdh 	if (pptr->abort_all_start) {
60974c06356bSdh 		while (pptr->abort_all_start) {
6098c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
60994c06356bSdh 			    "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
61004c06356bSdh 			    __func__, (void *)pptr);
61014c06356bSdh 			cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
61024c06356bSdh 		}
61034c06356bSdh 	} else if (pptr->abort_pending) {
6104*3be32c0fSJesse Butler 		rval = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
6105*3be32c0fSJesse Butler 		if (rval) {
6106c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
61074c06356bSdh 			    "%s: ABORT_ALL returned non-zero status (%d) for "
6108*3be32c0fSJesse Butler 			    "PHY 0x%p", __func__, rval, (void *)pptr);
6109*3be32c0fSJesse Butler 			return (rval);
61104c06356bSdh 		}
61114c06356bSdh 		pptr->abort_pending = 0;
61124c06356bSdh 	}
61134c06356bSdh 
6114*3be32c0fSJesse Butler 	if (pptr->valid_device_id) {
6115*3be32c0fSJesse Butler 		pmcs_deregister_device(pwp, pptr);
61164c06356bSdh 	}
61174c06356bSdh 
61184c06356bSdh 	PHY_CHANGED(pwp, pptr);
61194c06356bSdh 	RESTART_DISCOVERY(pwp);
61204c06356bSdh 	pptr->valid_device_id = 0;
61214c06356bSdh 	return (0);
61224c06356bSdh }
61234c06356bSdh 
61244c06356bSdh /*
61254c06356bSdh  * Acknowledge the SAS h/w events that need acknowledgement.
61264c06356bSdh  * This is only needed for first level PHYs.
61274c06356bSdh  */
61284c06356bSdh void
61294c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp)
61304c06356bSdh {
61314c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
61324c06356bSdh 	struct pmcwork *pwrk;
61334c06356bSdh 	pmcs_phy_t *pptr;
61344c06356bSdh 
61354c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
61364c06356bSdh 		pmcs_lock_phy(pptr);
61374c06356bSdh 		if (pptr->hw_event_ack == 0) {
61384c06356bSdh 			pmcs_unlock_phy(pptr);
61394c06356bSdh 			continue;
61404c06356bSdh 		}
61414c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
61424c06356bSdh 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
61434c06356bSdh 
61444c06356bSdh 		if ((ptr == NULL) || (pwrk =
61454c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
61464c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
61474c06356bSdh 			pmcs_unlock_phy(pptr);
61484c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
61494c06356bSdh 			break;
61504c06356bSdh 		}
61514c06356bSdh 
61524c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
6153978d7443SSrikanth Suravajhala 		    PMCIN_SAS_HW_EVENT_ACK));
61544c06356bSdh 		msg[1] = LE_32(pwrk->htag);
61554c06356bSdh 		msg[2] = LE_32(pptr->hw_event_ack);
61564c06356bSdh 
61574c06356bSdh 		mutex_exit(&pwrk->lock);
61584c06356bSdh 		pwrk->dtype = pptr->dtype;
61594c06356bSdh 		pptr->hw_event_ack = 0;
61604c06356bSdh 		COPY_MESSAGE(ptr, msg, 3);
61614c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
61624c06356bSdh 		pmcs_unlock_phy(pptr);
61634c06356bSdh 	}
61644c06356bSdh }
61654c06356bSdh 
61664c06356bSdh /*
61674c06356bSdh  * Load DMA
61684c06356bSdh  */
61694c06356bSdh int
61704c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
61714c06356bSdh {
61724c06356bSdh 	ddi_dma_cookie_t *sg;
61734c06356bSdh 	pmcs_dmachunk_t *tc;
61744c06356bSdh 	pmcs_dmasgl_t *sgl, *prior;
61754c06356bSdh 	int seg, tsc;
61764c06356bSdh 	uint64_t sgl_addr;
61774c06356bSdh 
61784c06356bSdh 	/*
61794c06356bSdh 	 * If we have no data segments, we're done.
61804c06356bSdh 	 */
61814c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 0) {
61824c06356bSdh 		return (0);
61834c06356bSdh 	}
61844c06356bSdh 
61854c06356bSdh 	/*
61864c06356bSdh 	 * Get the S/G list pointer.
61874c06356bSdh 	 */
61884c06356bSdh 	sg = CMD2PKT(sp)->pkt_cookies;
61894c06356bSdh 
61904c06356bSdh 	/*
61914c06356bSdh 	 * If we only have one dma segment, we can directly address that
61924c06356bSdh 	 * data within the Inbound message itself.
61934c06356bSdh 	 */
61944c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 1) {
61954c06356bSdh 		msg[12] = LE_32(DWORD0(sg->dmac_laddress));
61964c06356bSdh 		msg[13] = LE_32(DWORD1(sg->dmac_laddress));
61974c06356bSdh 		msg[14] = LE_32(sg->dmac_size);
61984c06356bSdh 		msg[15] = 0;
61994c06356bSdh 		return (0);
62004c06356bSdh 	}
62014c06356bSdh 
62024c06356bSdh 	/*
62034c06356bSdh 	 * Otherwise, we'll need one or more external S/G list chunks.
62044c06356bSdh 	 * Get the first one and its dma address into the Inbound message.
62054c06356bSdh 	 */
62064c06356bSdh 	mutex_enter(&pwp->dma_lock);
62074c06356bSdh 	tc = pwp->dma_freelist;
62084c06356bSdh 	if (tc == NULL) {
62094c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
62104c06356bSdh 		mutex_exit(&pwp->dma_lock);
6211c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
6212c3bc407cSdh 		    "%s: out of SG lists", __func__);
62134c06356bSdh 		return (-1);
62144c06356bSdh 	}
62154c06356bSdh 	pwp->dma_freelist = tc->nxt;
62164c06356bSdh 	mutex_exit(&pwp->dma_lock);
62174c06356bSdh 
62184c06356bSdh 	tc->nxt = NULL;
62194c06356bSdh 	sp->cmd_clist = tc;
62204c06356bSdh 	sgl = tc->chunks;
62214c06356bSdh 	(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
62224c06356bSdh 	sgl_addr = tc->addr;
62234c06356bSdh 	msg[12] = LE_32(DWORD0(sgl_addr));
62244c06356bSdh 	msg[13] = LE_32(DWORD1(sgl_addr));
62254c06356bSdh 	msg[14] = 0;
62264c06356bSdh 	msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
62274c06356bSdh 
62284c06356bSdh 	prior = sgl;
62294c06356bSdh 	tsc = 0;
62304c06356bSdh 
62314c06356bSdh 	for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
62324c06356bSdh 		/*
62334c06356bSdh 		 * If the current segment count for this chunk is one less than
62344c06356bSdh 		 * the number s/g lists per chunk and we have more than one seg
62354c06356bSdh 		 * to go, we need another chunk. Get it, and make sure that the
62364c06356bSdh 		 * tail end of the the previous chunk points the new chunk
62374c06356bSdh 		 * (if remembering an offset can be called 'pointing to').
62384c06356bSdh 		 *
62394c06356bSdh 		 * Note that we can store the offset into our command area that
62404c06356bSdh 		 * represents the new chunk in the length field of the part
62414c06356bSdh 		 * that points the PMC chip at the next chunk- the PMC chip
62424c06356bSdh 		 * ignores this field when the EXTENSION bit is set.
62434c06356bSdh 		 *
62444c06356bSdh 		 * This is required for dma unloads later.
62454c06356bSdh 		 */
62464c06356bSdh 		if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
62474c06356bSdh 		    seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
62484c06356bSdh 			mutex_enter(&pwp->dma_lock);
62494c06356bSdh 			tc = pwp->dma_freelist;
62504c06356bSdh 			if (tc == NULL) {
62514c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
62524c06356bSdh 				mutex_exit(&pwp->dma_lock);
62534c06356bSdh 				pmcs_dma_unload(pwp, sp);
6254c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
62554c06356bSdh 				    "%s: out of SG lists", __func__);
62564c06356bSdh 				return (-1);
62574c06356bSdh 			}
62584c06356bSdh 			pwp->dma_freelist = tc->nxt;
62594c06356bSdh 			tc->nxt = sp->cmd_clist;
62604c06356bSdh 			mutex_exit(&pwp->dma_lock);
62614c06356bSdh 
62624c06356bSdh 			sp->cmd_clist = tc;
62634c06356bSdh 			(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
62644c06356bSdh 			sgl = tc->chunks;
62654c06356bSdh 			sgl_addr = tc->addr;
62664c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglal =
62674c06356bSdh 			    LE_32(DWORD0(sgl_addr));
62684c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglah =
62694c06356bSdh 			    LE_32(DWORD1(sgl_addr));
62704c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
62714c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].flags =
62724c06356bSdh 			    LE_32(PMCS_DMASGL_EXTENSION);
62734c06356bSdh 			prior = sgl;
62744c06356bSdh 			tsc = 0;
62754c06356bSdh 		}
62764c06356bSdh 		sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
62774c06356bSdh 		sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
62784c06356bSdh 		sgl[tsc].sglen = LE_32(sg->dmac_size);
62794c06356bSdh 		sgl[tsc++].flags = 0;
62804c06356bSdh 		sg++;
62814c06356bSdh 	}
62824c06356bSdh 	return (0);
62834c06356bSdh }
62844c06356bSdh 
62854c06356bSdh /*
62864c06356bSdh  * Unload DMA
62874c06356bSdh  */
62884c06356bSdh void
62894c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
62904c06356bSdh {
62914c06356bSdh 	pmcs_dmachunk_t *cp;
62924c06356bSdh 
62934c06356bSdh 	mutex_enter(&pwp->dma_lock);
62944c06356bSdh 	while ((cp = sp->cmd_clist) != NULL) {
62954c06356bSdh 		sp->cmd_clist = cp->nxt;
62964c06356bSdh 		cp->nxt = pwp->dma_freelist;
62974c06356bSdh 		pwp->dma_freelist = cp;
62984c06356bSdh 	}
62994c06356bSdh 	mutex_exit(&pwp->dma_lock);
63004c06356bSdh }
63014c06356bSdh 
63024c06356bSdh /*
63034c06356bSdh  * Take a chunk of consistent memory that has just been allocated and inserted
63044c06356bSdh  * into the cip indices and prepare it for DMA chunk usage and add it to the
63054c06356bSdh  * freelist.
63064c06356bSdh  *
63074c06356bSdh  * Called with dma_lock locked (except during attach when it's unnecessary)
63084c06356bSdh  */
63094c06356bSdh void
63104c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
63114c06356bSdh     pmcs_chunk_t *pchunk, unsigned long lim)
63124c06356bSdh {
63134c06356bSdh 	unsigned long off, n;
63144c06356bSdh 	pmcs_dmachunk_t *np = dcp;
63154c06356bSdh 	pmcs_chunk_t *tmp_chunk;
63164c06356bSdh 
63174c06356bSdh 	if (pwp->dma_chunklist == NULL) {
63184c06356bSdh 		pwp->dma_chunklist = pchunk;
63194c06356bSdh 	} else {
63204c06356bSdh 		tmp_chunk = pwp->dma_chunklist;
63214c06356bSdh 		while (tmp_chunk->next) {
63224c06356bSdh 			tmp_chunk = tmp_chunk->next;
63234c06356bSdh 		}
63244c06356bSdh 		tmp_chunk->next = pchunk;
63254c06356bSdh 	}
63264c06356bSdh 
63274c06356bSdh 	/*
63284c06356bSdh 	 * Install offsets into chunk lists.
63294c06356bSdh 	 */
63304c06356bSdh 	for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
63314c06356bSdh 		np->chunks = (void *)&pchunk->addrp[off];
63324c06356bSdh 		np->addr = pchunk->dma_addr + off;
63334c06356bSdh 		np->acc_handle = pchunk->acc_handle;
63344c06356bSdh 		np->dma_handle = pchunk->dma_handle;
63354c06356bSdh 		if ((off + PMCS_SGL_CHUNKSZ) < lim) {
63364c06356bSdh 			np = np->nxt;
63374c06356bSdh 		}
63384c06356bSdh 	}
63394c06356bSdh 	np->nxt = pwp->dma_freelist;
63404c06356bSdh 	pwp->dma_freelist = dcp;
6341c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
63424c06356bSdh 	    "added %lu DMA chunks ", n);
63434c06356bSdh }
63444c06356bSdh 
63454c06356bSdh /*
63464c06356bSdh  * Change the value of the interrupt coalescing timer.  This is done currently
63474c06356bSdh  * only for I/O completions.  If we're using the "auto clear" feature, it can
63484c06356bSdh  * be turned back on when interrupt coalescing is turned off and must be
63494c06356bSdh  * turned off when the coalescing timer is on.
63504c06356bSdh  * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value.  As long
63514c06356bSdh  * as that's true, we don't need to distinguish between them.
63524c06356bSdh  */
63534c06356bSdh 
63544c06356bSdh void
63554c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
63564c06356bSdh {
63574c06356bSdh 	if (adj == DECREASE_TIMER) {
63584c06356bSdh 		/* If the timer is already off, nothing to do. */
63594c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
63604c06356bSdh 			return;
63614c06356bSdh 		}
63624c06356bSdh 
63634c06356bSdh 		pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
63644c06356bSdh 
63654c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer == 0) {
63664c06356bSdh 			/* Disable the timer */
63674c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
63684c06356bSdh 
63694c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
63704c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
63714c06356bSdh 				    pwp->odb_auto_clear);
63724c06356bSdh 			}
63734c06356bSdh 
63744c06356bSdh 			pwp->io_intr_coal.timer_on = B_FALSE;
63754c06356bSdh 			pwp->io_intr_coal.max_io_completions = B_FALSE;
63764c06356bSdh 			pwp->io_intr_coal.num_intrs = 0;
63774c06356bSdh 			pwp->io_intr_coal.int_cleared = B_FALSE;
63784c06356bSdh 			pwp->io_intr_coal.num_io_completions = 0;
63794c06356bSdh 
63804c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
63814c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
63824c06356bSdh 		} else {
63834c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
63844c06356bSdh 			    pwp->io_intr_coal.intr_coal_timer);
63854c06356bSdh 		}
63864c06356bSdh 	} else {
63874c06356bSdh 		/*
63884c06356bSdh 		 * If the timer isn't on yet, do the setup for it now.
63894c06356bSdh 		 */
63904c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
63914c06356bSdh 			/* If auto clear is being used, turn it off. */
63924c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
63934c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
63944c06356bSdh 				    (pwp->odb_auto_clear &
63954c06356bSdh 				    ~(1 << PMCS_MSIX_IODONE)));
63964c06356bSdh 			}
63974c06356bSdh 
63984c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
63994c06356bSdh 			    (1 << PMCS_MSIX_IODONE));
64004c06356bSdh 			pwp->io_intr_coal.timer_on = B_TRUE;
64014c06356bSdh 			pwp->io_intr_coal.intr_coal_timer =
64024c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
64034c06356bSdh 
64044c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
64054c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
64064c06356bSdh 		} else {
64074c06356bSdh 			pwp->io_intr_coal.intr_coal_timer +=
64084c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
64094c06356bSdh 		}
64104c06356bSdh 
64114c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
64124c06356bSdh 			pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
64134c06356bSdh 		}
64144c06356bSdh 
64154c06356bSdh 		pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
64164c06356bSdh 		    pwp->io_intr_coal.intr_coal_timer);
64174c06356bSdh 	}
64184c06356bSdh 
64194c06356bSdh 	/*
64204c06356bSdh 	 * Adjust the interrupt threshold based on the current timer value
64214c06356bSdh 	 */
64224c06356bSdh 	pwp->io_intr_coal.intr_threshold =
64234c06356bSdh 	    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
64244c06356bSdh 	    (pwp->io_intr_coal.intr_latency +
64254c06356bSdh 	    (pwp->io_intr_coal.intr_coal_timer * 1000)));
64264c06356bSdh }
64274c06356bSdh 
64284c06356bSdh /*
64294c06356bSdh  * Register Access functions
64304c06356bSdh  */
64314c06356bSdh uint32_t
64324c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
64334c06356bSdh {
64344c06356bSdh 	uint32_t iqci;
64354c06356bSdh 
64364c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
64374c06356bSdh 	    DDI_SUCCESS) {
6438c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6439c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
64404c06356bSdh 	}
64414c06356bSdh 
64424c06356bSdh 	iqci = LE_32(
64434c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
64444c06356bSdh 
64454c06356bSdh 	return (iqci);
64464c06356bSdh }
64474c06356bSdh 
64484c06356bSdh uint32_t
64494c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
64504c06356bSdh {
64514c06356bSdh 	uint32_t oqpi;
64524c06356bSdh 
64534c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
64544c06356bSdh 	    DDI_SUCCESS) {
6455c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6456c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
64574c06356bSdh 	}
64584c06356bSdh 
64594c06356bSdh 	oqpi = LE_32(
64604c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
64614c06356bSdh 
64624c06356bSdh 	return (oqpi);
64634c06356bSdh }
64644c06356bSdh 
64654c06356bSdh uint32_t
64662ac4abe8SDavid Hollister pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint8_t hi, uint32_t off)
64674c06356bSdh {
64682ac4abe8SDavid Hollister 	uint32_t rv, newaxil, oldaxil, oldaxih;
64694c06356bSdh 
64704c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
64714c06356bSdh 	off &= GSM_BASE_MASK;
64724c06356bSdh 	mutex_enter(&pwp->axil_lock);
64734c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
64744c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
64754c06356bSdh 	ddi_put32(pwp->top_acc_handle,
64764c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
64774c06356bSdh 	drv_usecwait(10);
64784c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
64794c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6480c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6481c3bc407cSdh 		    "AXIL register update failed");
64824c06356bSdh 	}
64832ac4abe8SDavid Hollister 	if (hi) {
64842ac4abe8SDavid Hollister 		oldaxih = ddi_get32(pwp->top_acc_handle,
64852ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]);
64862ac4abe8SDavid Hollister 		ddi_put32(pwp->top_acc_handle,
64872ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], hi);
64882ac4abe8SDavid Hollister 		drv_usecwait(10);
64892ac4abe8SDavid Hollister 		if (ddi_get32(pwp->top_acc_handle,
64902ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != hi) {
64912ac4abe8SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
64922ac4abe8SDavid Hollister 			    "AXIH register update failed");
64932ac4abe8SDavid Hollister 		}
64942ac4abe8SDavid Hollister 	}
64954c06356bSdh 	rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
64962ac4abe8SDavid Hollister 	if (hi) {
64972ac4abe8SDavid Hollister 		ddi_put32(pwp->top_acc_handle,
64982ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], oldaxih);
64992ac4abe8SDavid Hollister 		drv_usecwait(10);
65002ac4abe8SDavid Hollister 		if (ddi_get32(pwp->top_acc_handle,
65012ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != oldaxih) {
65022ac4abe8SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
65032ac4abe8SDavid Hollister 			    "AXIH register restore failed");
65042ac4abe8SDavid Hollister 		}
65052ac4abe8SDavid Hollister 	}
65064c06356bSdh 	ddi_put32(pwp->top_acc_handle,
65074c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
65084c06356bSdh 	drv_usecwait(10);
65094c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
65104c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6511c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6512c3bc407cSdh 		    "AXIL register restore failed");
65134c06356bSdh 	}
65144c06356bSdh 	mutex_exit(&pwp->axil_lock);
65154c06356bSdh 	return (rv);
65164c06356bSdh }
65174c06356bSdh 
65184c06356bSdh void
65194c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
65204c06356bSdh {
65214c06356bSdh 	uint32_t newaxil, oldaxil;
65224c06356bSdh 
65234c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
65244c06356bSdh 	off &= GSM_BASE_MASK;
65254c06356bSdh 	mutex_enter(&pwp->axil_lock);
65264c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
65274c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
65284c06356bSdh 	ddi_put32(pwp->top_acc_handle,
65294c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
65304c06356bSdh 	drv_usecwait(10);
65314c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
65324c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6533c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6534c3bc407cSdh 		    "AXIL register update failed");
65354c06356bSdh 	}
65364c06356bSdh 	ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
65374c06356bSdh 	ddi_put32(pwp->top_acc_handle,
65384c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
65394c06356bSdh 	drv_usecwait(10);
65404c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
65414c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6542c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6543c3bc407cSdh 		    "AXIL register restore failed");
65444c06356bSdh 	}
65454c06356bSdh 	mutex_exit(&pwp->axil_lock);
65464c06356bSdh }
65474c06356bSdh 
65484c06356bSdh uint32_t
65494c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
65504c06356bSdh {
65514c06356bSdh 	switch (off) {
65524c06356bSdh 	case PMCS_SPC_RESET:
65534c06356bSdh 	case PMCS_SPC_BOOT_STRAP:
65544c06356bSdh 	case PMCS_SPC_DEVICE_ID:
65554c06356bSdh 	case PMCS_DEVICE_REVISION:
65562ac4abe8SDavid Hollister 		off = pmcs_rd_gsm_reg(pwp, 0, off);
65574c06356bSdh 		break;
65584c06356bSdh 	default:
65594c06356bSdh 		off = ddi_get32(pwp->top_acc_handle,
65604c06356bSdh 		    &pwp->top_regs[off >> 2]);
65614c06356bSdh 		break;
65624c06356bSdh 	}
65634c06356bSdh 	return (off);
65644c06356bSdh }
65654c06356bSdh 
65664c06356bSdh void
65674c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
65684c06356bSdh {
65694c06356bSdh 	switch (off) {
65704c06356bSdh 	case PMCS_SPC_RESET:
65714c06356bSdh 	case PMCS_DEVICE_REVISION:
65724c06356bSdh 		pmcs_wr_gsm_reg(pwp, off, val);
65734c06356bSdh 		break;
65744c06356bSdh 	default:
65754c06356bSdh 		ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
65764c06356bSdh 		break;
65774c06356bSdh 	}
65784c06356bSdh }
65794c06356bSdh 
65804c06356bSdh uint32_t
65814c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
65824c06356bSdh {
65834c06356bSdh 	return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
65844c06356bSdh }
65854c06356bSdh 
65864c06356bSdh uint32_t
65874c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
65884c06356bSdh {
65894c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
65904c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
65914c06356bSdh }
65924c06356bSdh 
65934c06356bSdh uint32_t
65944c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
65954c06356bSdh {
65964c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
65974c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
65984c06356bSdh }
65994c06356bSdh 
66004c06356bSdh uint32_t
66014c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
66024c06356bSdh {
66034c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66044c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
66054c06356bSdh }
66064c06356bSdh 
66074c06356bSdh uint32_t
66084c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
66094c06356bSdh {
66104c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66114c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
66124c06356bSdh }
66134c06356bSdh 
66144c06356bSdh uint32_t
66154c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
66164c06356bSdh {
66174c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66184c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
66194c06356bSdh }
66204c06356bSdh 
66214c06356bSdh uint32_t
66224c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
66234c06356bSdh {
66244c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66254c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
66264c06356bSdh }
66274c06356bSdh 
66284c06356bSdh void
66294c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66304c06356bSdh {
66314c06356bSdh 	ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
66324c06356bSdh }
66334c06356bSdh 
66344c06356bSdh void
66354c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66364c06356bSdh {
66374c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66384c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
66394c06356bSdh }
66404c06356bSdh 
66414c06356bSdh void
66424c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66434c06356bSdh {
66444c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66454c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
66464c06356bSdh }
66474c06356bSdh 
66484c06356bSdh void
66494c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66504c06356bSdh {
66514c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66524c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
66534c06356bSdh }
66544c06356bSdh 
66554c06356bSdh void
66564c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66574c06356bSdh {
66584c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66594c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
66604c06356bSdh }
66614c06356bSdh 
66624c06356bSdh void
66634c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
66644c06356bSdh {
66654c06356bSdh 	((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
66664c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
66674c06356bSdh 	    DDI_SUCCESS) {
6668c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6669c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
66704c06356bSdh 	}
66714c06356bSdh }
66724c06356bSdh 
66734c06356bSdh void
66744c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
66754c06356bSdh {
66764c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66774c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
66784c06356bSdh }
66794c06356bSdh 
66804c06356bSdh void
66814c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
66824c06356bSdh {
66834c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
66844c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
66854c06356bSdh }
66864c06356bSdh 
66874c06356bSdh void
66884c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
66894c06356bSdh {
66904c06356bSdh 	((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
66914c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
66924c06356bSdh 	    DDI_SUCCESS) {
6693c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6694c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
66954c06356bSdh 	}
66964c06356bSdh }
66974c06356bSdh 
66984c06356bSdh /*
66994c06356bSdh  * Check the status value of an outbound IOMB and report anything bad
67004c06356bSdh  */
67014c06356bSdh 
67024c06356bSdh void
67034c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
67044c06356bSdh {
67054c06356bSdh 	uint16_t 	opcode;
67064c06356bSdh 	int		offset;
67074c06356bSdh 
67084c06356bSdh 	if (iomb == NULL) {
67094c06356bSdh 		return;
67104c06356bSdh 	}
67114c06356bSdh 
67124c06356bSdh 	opcode = LE_32(iomb[0]) & 0xfff;
67134c06356bSdh 
67144c06356bSdh 	switch (opcode) {
67154c06356bSdh 		/*
67164c06356bSdh 		 * The following have no status field, so ignore them
67174c06356bSdh 		 */
67184c06356bSdh 	case PMCOUT_ECHO:
67194c06356bSdh 	case PMCOUT_SAS_HW_EVENT:
67204c06356bSdh 	case PMCOUT_GET_DEVICE_HANDLE:
67214c06356bSdh 	case PMCOUT_SATA_EVENT:
67224c06356bSdh 	case PMCOUT_SSP_EVENT:
67234c06356bSdh 	case PMCOUT_DEVICE_HANDLE_ARRIVED:
67244c06356bSdh 	case PMCOUT_SMP_REQUEST_RECEIVED:
67254c06356bSdh 	case PMCOUT_GPIO:
67264c06356bSdh 	case PMCOUT_GPIO_EVENT:
67274c06356bSdh 	case PMCOUT_GET_TIME_STAMP:
67284c06356bSdh 	case PMCOUT_SKIP_ENTRIES:
67294c06356bSdh 	case PMCOUT_GET_NVMD_DATA:	/* Actually lower 16 bits of word 3 */
67304c06356bSdh 	case PMCOUT_SET_NVMD_DATA:	/* but ignore - we don't use these */
67314c06356bSdh 	case PMCOUT_DEVICE_HANDLE_REMOVED:
67324c06356bSdh 	case PMCOUT_SSP_REQUEST_RECEIVED:
67334c06356bSdh 		return;
67344c06356bSdh 
67354c06356bSdh 	case PMCOUT_GENERAL_EVENT:
67364c06356bSdh 		offset = 1;
67374c06356bSdh 		break;
67384c06356bSdh 
67394c06356bSdh 	case PMCOUT_SSP_COMPLETION:
67404c06356bSdh 	case PMCOUT_SMP_COMPLETION:
67414c06356bSdh 	case PMCOUT_DEVICE_REGISTRATION:
67424c06356bSdh 	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
67434c06356bSdh 	case PMCOUT_SATA_COMPLETION:
67444c06356bSdh 	case PMCOUT_DEVICE_INFO:
67454c06356bSdh 	case PMCOUT_FW_FLASH_UPDATE:
67464c06356bSdh 	case PMCOUT_SSP_ABORT:
67474c06356bSdh 	case PMCOUT_SATA_ABORT:
67484c06356bSdh 	case PMCOUT_SAS_DIAG_MODE_START_END:
67494c06356bSdh 	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
67504c06356bSdh 	case PMCOUT_SMP_ABORT:
67514c06356bSdh 	case PMCOUT_SET_DEVICE_STATE:
67524c06356bSdh 	case PMCOUT_GET_DEVICE_STATE:
67534c06356bSdh 	case PMCOUT_SET_DEVICE_INFO:
67544c06356bSdh 		offset = 2;
67554c06356bSdh 		break;
67564c06356bSdh 
67574c06356bSdh 	case PMCOUT_LOCAL_PHY_CONTROL:
67584c06356bSdh 	case PMCOUT_SAS_DIAG_EXECUTE:
67594c06356bSdh 	case PMCOUT_PORT_CONTROL:
67604c06356bSdh 		offset = 3;
67614c06356bSdh 		break;
67624c06356bSdh 
67634c06356bSdh 	case PMCOUT_GET_INFO:
67644c06356bSdh 	case PMCOUT_GET_VPD:
67654c06356bSdh 	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
67664c06356bSdh 	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
67674c06356bSdh 	case PMCOUT_SET_VPD:
67684c06356bSdh 	case PMCOUT_TWI:
67694c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
67704c06356bSdh 		    "Got response for deprecated opcode", iomb);
67714c06356bSdh 		return;
67724c06356bSdh 
67734c06356bSdh 	default:
67744c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
67754c06356bSdh 		    "Got response for unknown opcode", iomb);
67764c06356bSdh 		return;
67774c06356bSdh 	}
67784c06356bSdh 
67794c06356bSdh 	if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
67804c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
67814c06356bSdh 		    "bad status on TAG_TYPE_NONE command", iomb);
67824c06356bSdh 	}
67834c06356bSdh }
67844c06356bSdh 
67854c06356bSdh /*
67864c06356bSdh  * Called with statlock held
67874c06356bSdh  */
67884c06356bSdh void
67894c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
67904c06356bSdh {
67914c06356bSdh 	_NOTE(ARGUNUSED(pwp));
67924c06356bSdh 
67934c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
67944c06356bSdh 
6795c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.",
6796c3bc407cSdh 	    __func__, (void *)xp);
67974c06356bSdh 
67984c06356bSdh 	/*
6799b18a19c2SJesse Butler 	 * Clear the dip now.  This keeps pmcs_remove_device from attempting
68004c06356bSdh 	 * to call us on the same device while we're still flushing queues.
68014c06356bSdh 	 * The only side effect is we can no longer update SM-HBA properties,
68024c06356bSdh 	 * but this device is going away anyway, so no matter.
68034c06356bSdh 	 */
68044c06356bSdh 	xp->dip = NULL;
6805499cfd15SDavid Hollister 	xp->smpd = NULL;
68064c06356bSdh 	xp->special_running = 0;
68074c06356bSdh 	xp->recovering = 0;
68084c06356bSdh 	xp->recover_wait = 0;
68094c06356bSdh 	xp->draining = 0;
68104c06356bSdh 	xp->new = 0;
68114c06356bSdh 	xp->assigned = 0;
68124c06356bSdh 	xp->dev_state = 0;
68134c06356bSdh 	xp->tagmap = 0;
68144c06356bSdh 	xp->dev_gone = 1;
68154c06356bSdh 	xp->event_recovery = 0;
68164c06356bSdh 	xp->dtype = NOTHING;
68174c06356bSdh 	xp->wq_recovery_tail = NULL;
68184c06356bSdh 	/* Don't clear xp->phy */
68194c06356bSdh 	/* Don't clear xp->actv_cnt */
6820601c90f1SSrikanth, Ramana 	/* Don't clear xp->actv_pkts */
6821b18a19c2SJesse Butler 
6822b18a19c2SJesse Butler 	/*
6823b18a19c2SJesse Butler 	 * Flush all target queues
6824b18a19c2SJesse Butler 	 */
6825b18a19c2SJesse Butler 	pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
68264c06356bSdh }
68274c06356bSdh 
68284c06356bSdh static int
68294c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
68304c06356bSdh {
68314c06356bSdh 	int result = srf->srf_result;
68324c06356bSdh 
68334c06356bSdh 	switch (result) {
68344c06356bSdh 	case SMP_RES_UNKNOWN_FUNCTION:
6835c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6836c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68374c06356bSdh 		    "Function Result: Unknown SMP Function(0x%x)",
68384c06356bSdh 		    __func__, result);
68394c06356bSdh 		break;
68404c06356bSdh 	case SMP_RES_FUNCTION_FAILED:
6841c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6842c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68434c06356bSdh 		    "Function Result: SMP Function Failed(0x%x)",
68444c06356bSdh 		    __func__, result);
68454c06356bSdh 		break;
68464c06356bSdh 	case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
6847c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6848c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68494c06356bSdh 		    "Function Result: Invalid Request Frame Length(0x%x)",
68504c06356bSdh 		    __func__, result);
68514c06356bSdh 		break;
68524c06356bSdh 	case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
6853c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6854c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68554c06356bSdh 		    "Function Result: Incomplete Descriptor List(0x%x)",
68564c06356bSdh 		    __func__, result);
68574c06356bSdh 		break;
68584c06356bSdh 	case SMP_RES_PHY_DOES_NOT_EXIST:
6859c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6860c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68614c06356bSdh 		    "Function Result: PHY does not exist(0x%x)",
68624c06356bSdh 		    __func__, result);
68634c06356bSdh 		break;
68644c06356bSdh 	case SMP_RES_PHY_VACANT:
6865c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6866c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68674c06356bSdh 		    "Function Result: PHY Vacant(0x%x)",
68684c06356bSdh 		    __func__, result);
68694c06356bSdh 		break;
68704c06356bSdh 	default:
6871c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6872c3bc407cSdh 		    "%s: SMP DISCOVER Response "
68734c06356bSdh 		    "Function Result: (0x%x)",
68744c06356bSdh 		    __func__, result);
68754c06356bSdh 		break;
68764c06356bSdh 	}
68774c06356bSdh 
68784c06356bSdh 	return (result);
68794c06356bSdh }
68804c06356bSdh 
68814c06356bSdh /*
68824c06356bSdh  * Do all the repetitive stuff necessary to setup for DMA
68834c06356bSdh  *
68844c06356bSdh  * pwp: Used for dip
68854c06356bSdh  * dma_attr: ddi_dma_attr_t to use for the mapping
68864c06356bSdh  * acch: ddi_acc_handle_t to use for the mapping
68874c06356bSdh  * dmah: ddi_dma_handle_t to use
68884c06356bSdh  * length: Amount of memory for mapping
6889af4c679fSSean McEnroe  * kvap: Pointer filled in with kernel virtual address on successful return
68904c06356bSdh  * dma_addr: Pointer filled in with DMA address on successful return
68914c06356bSdh  */
68924c06356bSdh boolean_t
68934c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
6894af4c679fSSean McEnroe     ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr)
68954c06356bSdh {
68964c06356bSdh 	dev_info_t		*dip = pwp->dip;
68974c06356bSdh 	ddi_dma_cookie_t	cookie;
68984c06356bSdh 	size_t			real_length;
68994c06356bSdh 	uint_t			ddma_flag = DDI_DMA_CONSISTENT;
69004c06356bSdh 	uint_t			ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
69014c06356bSdh 	uint_t			cookie_cnt;
69024c06356bSdh 	ddi_device_acc_attr_t	mattr = {
69034c06356bSdh 		DDI_DEVICE_ATTR_V0,
69044c06356bSdh 		DDI_NEVERSWAP_ACC,
69054c06356bSdh 		DDI_STRICTORDER_ACC,
69064c06356bSdh 		DDI_DEFAULT_ACC
69074c06356bSdh 	};
69084c06356bSdh 
69094c06356bSdh 	*acch = NULL;
69104c06356bSdh 	*dmah = NULL;
69114c06356bSdh 
69124c06356bSdh 	if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
69134c06356bSdh 	    DDI_SUCCESS) {
6914c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6915c3bc407cSdh 		    "Failed to allocate DMA handle");
69164c06356bSdh 		return (B_FALSE);
69174c06356bSdh 	}
69184c06356bSdh 
69194c06356bSdh 	if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
6920af4c679fSSean McEnroe 	    NULL, kvap, &real_length, acch) != DDI_SUCCESS) {
6921c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6922c3bc407cSdh 		    "Failed to allocate DMA mem");
69234c06356bSdh 		ddi_dma_free_handle(dmah);
69244c06356bSdh 		*dmah = NULL;
69254c06356bSdh 		return (B_FALSE);
69264c06356bSdh 	}
69274c06356bSdh 
6928af4c679fSSean McEnroe 	if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length,
69294c06356bSdh 	    ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
69304c06356bSdh 	    != DDI_DMA_MAPPED) {
6931c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA");
69324c06356bSdh 		ddi_dma_free_handle(dmah);
69334c06356bSdh 		ddi_dma_mem_free(acch);
69344c06356bSdh 		*dmah = NULL;
69354c06356bSdh 		*acch = NULL;
69364c06356bSdh 		return (B_FALSE);
69374c06356bSdh 	}
69384c06356bSdh 
69394c06356bSdh 	if (cookie_cnt != 1) {
6940c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies");
69414c06356bSdh 		if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
6942c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition "
6943c3bc407cSdh 			    "failed at %s():%d", __func__, __LINE__);
69444c06356bSdh 		}
69454c06356bSdh 		ddi_dma_free_handle(dmah);
69464c06356bSdh 		ddi_dma_mem_free(acch);
69474c06356bSdh 		*dmah = NULL;
69484c06356bSdh 		*acch = NULL;
69494c06356bSdh 		return (B_FALSE);
69504c06356bSdh 	}
69514c06356bSdh 
69524c06356bSdh 	*dma_addr = cookie.dmac_laddress;
69534c06356bSdh 
69544c06356bSdh 	return (B_TRUE);
69554c06356bSdh }
69564c06356bSdh 
69574c06356bSdh /*
69584c06356bSdh  * Flush requested queues for a particular target.  Called with statlock held
69594c06356bSdh  */
69604c06356bSdh void
69614c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
69624c06356bSdh {
69639aed1621SDavid Hollister 	pmcs_cmd_t	*sp, *sp_next;
69644c06356bSdh 	pmcwork_t	*pwrk;
69654c06356bSdh 
69664c06356bSdh 	ASSERT(pwp != NULL);
69674c06356bSdh 	ASSERT(tgt != NULL);
69684c06356bSdh 
6969c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt,
69704c06356bSdh 	    "%s: Flushing queues (%d) for target 0x%p", __func__,
69714c06356bSdh 	    queues, (void *)tgt);
69724c06356bSdh 
69734c06356bSdh 	/*
69744c06356bSdh 	 * Commands on the wait queue (or the special queue below) don't have
69754c06356bSdh 	 * work structures associated with them.
69764c06356bSdh 	 */
69774c06356bSdh 	if (queues & PMCS_TGT_WAIT_QUEUE) {
69784c06356bSdh 		mutex_enter(&tgt->wqlock);
69794c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
69804c06356bSdh 			STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
6981c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
69824c06356bSdh 			    "%s: Removing cmd 0x%p from wq for target 0x%p",
69834c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
69844c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
69854c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
69864c06356bSdh 			mutex_exit(&tgt->wqlock);
69874c06356bSdh 			pmcs_dma_unload(pwp, sp);
69884c06356bSdh 			mutex_enter(&pwp->cq_lock);
69894c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
69908f514e74SDavid Hollister 			PMCS_CQ_RUN_LOCKED(pwp);
69914c06356bSdh 			mutex_exit(&pwp->cq_lock);
69924c06356bSdh 			mutex_enter(&tgt->wqlock);
69934c06356bSdh 		}
69944c06356bSdh 		mutex_exit(&tgt->wqlock);
69954c06356bSdh 	}
69964c06356bSdh 
69974c06356bSdh 	/*
69984c06356bSdh 	 * Commands on the active queue will have work structures associated
69994c06356bSdh 	 * with them.
70004c06356bSdh 	 */
70014c06356bSdh 	if (queues & PMCS_TGT_ACTIVE_QUEUE) {
700232b54db7SJesse Butler 		mutex_exit(&tgt->statlock);
70034c06356bSdh 		mutex_enter(&tgt->aqlock);
70049aed1621SDavid Hollister 		sp = STAILQ_FIRST(&tgt->aq);
70059aed1621SDavid Hollister 		while (sp) {
70069aed1621SDavid Hollister 			sp_next = STAILQ_NEXT(sp, cmd_next);
7007978d7443SSrikanth Suravajhala 			pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
70089aed1621SDavid Hollister 
70099aed1621SDavid Hollister 			/*
70109aed1621SDavid Hollister 			 * If we don't find a work structure, it's because
70119aed1621SDavid Hollister 			 * the command is already complete.  If so, move on
70129aed1621SDavid Hollister 			 * to the next one.
70139aed1621SDavid Hollister 			 */
70149aed1621SDavid Hollister 			if (pwrk == NULL) {
70159aed1621SDavid Hollister 				pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
70169aed1621SDavid Hollister 				    "%s: Not removing cmd 0x%p (htag 0x%x) "
70179aed1621SDavid Hollister 				    "from aq", __func__, (void *)sp,
70189aed1621SDavid Hollister 				    sp->cmd_tag);
70199aed1621SDavid Hollister 				sp = sp_next;
70209aed1621SDavid Hollister 				continue;
70219aed1621SDavid Hollister 			}
70229aed1621SDavid Hollister 
70239aed1621SDavid Hollister 			STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
70249aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
70259aed1621SDavid Hollister 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq for "
70269aed1621SDavid Hollister 			    "target 0x%p", __func__, (void *)sp, sp->cmd_tag,
70279aed1621SDavid Hollister 			    (void *)tgt);
70284c06356bSdh 			mutex_exit(&tgt->aqlock);
7029*3be32c0fSJesse Butler 
70304c06356bSdh 			/*
70319aed1621SDavid Hollister 			 * Mark the work structure as dead and complete it
70324c06356bSdh 			 */
70339aed1621SDavid Hollister 			pwrk->dead = 1;
70349aed1621SDavid Hollister 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
70359aed1621SDavid Hollister 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
70369aed1621SDavid Hollister 			pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
70374c06356bSdh 			pmcs_dma_unload(pwp, sp);
70384c06356bSdh 			mutex_enter(&pwp->cq_lock);
70394c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
70404c06356bSdh 			mutex_exit(&pwp->cq_lock);
70414c06356bSdh 			mutex_enter(&tgt->aqlock);
70429aed1621SDavid Hollister 			sp = sp_next;
70434c06356bSdh 		}
70444c06356bSdh 		mutex_exit(&tgt->aqlock);
704532b54db7SJesse Butler 		mutex_enter(&tgt->statlock);
70464c06356bSdh 	}
70474c06356bSdh 
70484c06356bSdh 	if (queues & PMCS_TGT_SPECIAL_QUEUE) {
70494c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
70504c06356bSdh 			STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
70519aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
70524c06356bSdh 			    "%s: Removing cmd 0x%p from sq for target 0x%p",
70534c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
70544c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
70554c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
70564c06356bSdh 			pmcs_dma_unload(pwp, sp);
70574c06356bSdh 			mutex_enter(&pwp->cq_lock);
70584c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
70594c06356bSdh 			mutex_exit(&pwp->cq_lock);
70604c06356bSdh 		}
70614c06356bSdh 	}
7062978d7443SSrikanth Suravajhala 
7063978d7443SSrikanth Suravajhala 	if (queues == PMCS_TGT_ALL_QUEUES) {
7064978d7443SSrikanth Suravajhala 		mutex_exit(&tgt->statlock);
7065*3be32c0fSJesse Butler 		pmcs_flush_nonio_cmds(pwp, tgt);
7066978d7443SSrikanth Suravajhala 		mutex_enter(&tgt->statlock);
7067978d7443SSrikanth Suravajhala 	}
7068978d7443SSrikanth Suravajhala }
7069978d7443SSrikanth Suravajhala 
7070978d7443SSrikanth Suravajhala /*
7071*3be32c0fSJesse Butler  * Flush non-IO commands for this target. This cleans up the off-queue
7072*3be32c0fSJesse Butler  * work with no pmcs_cmd_t associated.
7073978d7443SSrikanth Suravajhala  */
7074*3be32c0fSJesse Butler static void
7075*3be32c0fSJesse Butler pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
7076978d7443SSrikanth Suravajhala {
7077978d7443SSrikanth Suravajhala 	int i;
7078978d7443SSrikanth Suravajhala 	pmcwork_t *p;
7079978d7443SSrikanth Suravajhala 
7080978d7443SSrikanth Suravajhala 	for (i = 0; i < pwp->max_cmd; i++) {
7081978d7443SSrikanth Suravajhala 		p = &pwp->work[i];
7082978d7443SSrikanth Suravajhala 		mutex_enter(&p->lock);
7083*3be32c0fSJesse Butler 		if (p->xp != tgt) {
7084*3be32c0fSJesse Butler 			mutex_exit(&p->lock);
7085*3be32c0fSJesse Butler 			continue;
7086*3be32c0fSJesse Butler 		}
7087978d7443SSrikanth Suravajhala 		if (p->htag & PMCS_TAG_NONIO_CMD) {
7088978d7443SSrikanth Suravajhala 			if (!PMCS_COMMAND_ACTIVE(p) || PMCS_COMMAND_DONE(p)) {
7089978d7443SSrikanth Suravajhala 				mutex_exit(&p->lock);
7090978d7443SSrikanth Suravajhala 				continue;
7091978d7443SSrikanth Suravajhala 			}
7092978d7443SSrikanth Suravajhala 			pmcs_prt(pwp, PMCS_PRT_DEBUG, p->phy, p->xp,
7093978d7443SSrikanth Suravajhala 			    "%s: Completing non-io cmd with HTAG 0x%x",
7094978d7443SSrikanth Suravajhala 			    __func__, p->htag);
7095978d7443SSrikanth Suravajhala 			pmcs_complete_work_impl(pwp, p, NULL, 0);
7096978d7443SSrikanth Suravajhala 		} else {
7097978d7443SSrikanth Suravajhala 			mutex_exit(&p->lock);
7098978d7443SSrikanth Suravajhala 		}
7099978d7443SSrikanth Suravajhala 	}
71004c06356bSdh }
71014c06356bSdh 
71024c06356bSdh void
71034c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
71044c06356bSdh     size_t amt)
71054c06356bSdh {
71064c06356bSdh 	switch (PMCS_TAG_TYPE(pwrk->htag)) {
71074c06356bSdh 	case PMCS_TAG_TYPE_CBACK:
71084c06356bSdh 	{
71094c06356bSdh 		pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
71104c06356bSdh 		(*callback)(pwp, pwrk, iomb);
71114c06356bSdh 		break;
71124c06356bSdh 	}
71134c06356bSdh 	case PMCS_TAG_TYPE_WAIT:
71144c06356bSdh 		if (pwrk->arg && iomb && amt) {
71154c06356bSdh 			(void) memcpy(pwrk->arg, iomb, amt);
71164c06356bSdh 		}
71174c06356bSdh 		cv_signal(&pwrk->sleep_cv);
71184c06356bSdh 		mutex_exit(&pwrk->lock);
71194c06356bSdh 		break;
71204c06356bSdh 	case PMCS_TAG_TYPE_NONE:
71214c06356bSdh #ifdef DEBUG
71224c06356bSdh 		pmcs_check_iomb_status(pwp, iomb);
71234c06356bSdh #endif
71244c06356bSdh 		pmcs_pwork(pwp, pwrk);
71254c06356bSdh 		break;
71264c06356bSdh 	default:
71274c06356bSdh 		/*
71284c06356bSdh 		 * We will leak a structure here if we don't know
71294c06356bSdh 		 * what happened
71304c06356bSdh 		 */
7131c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7132c3bc407cSdh 		    "%s: Unknown PMCS_TAG_TYPE (%x)",
71334c06356bSdh 		    __func__, PMCS_TAG_TYPE(pwrk->htag));
71344c06356bSdh 		break;
71354c06356bSdh 	}
71364c06356bSdh }
71374c06356bSdh 
71384c06356bSdh /*
71394c06356bSdh  * Determine if iport still has targets. During detach(9E), if SCSA is
71404c06356bSdh  * successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
71414c06356bSdh  * this should always return B_FALSE.
71424c06356bSdh  */
71434c06356bSdh boolean_t
71444c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
71454c06356bSdh {
71464c06356bSdh 	pmcs_xscsi_t *xp;
71474c06356bSdh 	int i;
71484c06356bSdh 
71494c06356bSdh 	mutex_enter(&pwp->lock);
71504c06356bSdh 
71514c06356bSdh 	if (!pwp->targets || !pwp->max_dev) {
71524c06356bSdh 		mutex_exit(&pwp->lock);
71534c06356bSdh 		return (B_FALSE);
71544c06356bSdh 	}
71554c06356bSdh 
71564c06356bSdh 	for (i = 0; i < pwp->max_dev; i++) {
71574c06356bSdh 		xp = pwp->targets[i];
71584c06356bSdh 		if ((xp == NULL) || (xp->phy == NULL) ||
71594c06356bSdh 		    (xp->phy->iport != iport)) {
71604c06356bSdh 			continue;
71614c06356bSdh 		}
71624c06356bSdh 
71634c06356bSdh 		mutex_exit(&pwp->lock);
71644c06356bSdh 		return (B_TRUE);
71654c06356bSdh 	}
71664c06356bSdh 
71674c06356bSdh 	mutex_exit(&pwp->lock);
71684c06356bSdh 	return (B_FALSE);
71694c06356bSdh }
71704c06356bSdh 
71714c06356bSdh /*
71724c06356bSdh  * Called with softstate lock held
71734c06356bSdh  */
71744c06356bSdh void
71754c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target)
71764c06356bSdh {
71774c06356bSdh 	pmcs_hw_t *pwp = target->pwp;
71784c06356bSdh 	pmcs_iport_t *iport;
71794c06356bSdh 
71804c06356bSdh 	ASSERT(pwp);
71814c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
71824c06356bSdh 
71834c06356bSdh 	if (!target->ua) {
7184c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
7185c3bc407cSdh 		    "%s: target %p iport address is null",
71864c06356bSdh 		    __func__, (void *)target);
71874c06356bSdh 	}
71884c06356bSdh 
71894c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, target->ua);
71904c06356bSdh 	if (iport == NULL) {
7191c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
71924c06356bSdh 		    "%s: no iport associated with tgt(0x%p)",
71934c06356bSdh 		    __func__, (void *)target);
71944c06356bSdh 		return;
71954c06356bSdh 	}
71964c06356bSdh 
7197c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target,
71984c06356bSdh 	    "%s: free target %p", __func__, (void *)target);
71994c06356bSdh 	if (target->ua) {
72004c06356bSdh 		strfree(target->ua);
72014c06356bSdh 	}
72024c06356bSdh 
72034c06356bSdh 	mutex_destroy(&target->wqlock);
72044c06356bSdh 	mutex_destroy(&target->aqlock);
72054c06356bSdh 	mutex_destroy(&target->statlock);
72064c06356bSdh 	cv_destroy(&target->reset_cv);
72074c06356bSdh 	cv_destroy(&target->abort_cv);
72084c06356bSdh 	ddi_soft_state_bystr_fini(&target->lun_sstate);
72094c06356bSdh 	ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
72104c06356bSdh 	pmcs_rele_iport(iport);
72114c06356bSdh }
72124c06356bSdh 
72134c06356bSdh /*
72144c06356bSdh  * pmcs_lock_phy_impl
72154c06356bSdh  *
72164c06356bSdh  * This function is what does the actual work for pmcs_lock_phy.  It will
72174c06356bSdh  * lock all PHYs from phyp down in a top-down fashion.
72184c06356bSdh  *
72194c06356bSdh  * Locking notes:
72204c06356bSdh  * 1. level starts from 0 for the PHY ("parent") that's passed in.  It is
72214c06356bSdh  * not a reflection of the actual level of the PHY in the SAS topology.
72224c06356bSdh  * 2. If parent is an expander, then parent is locked along with all its
72234c06356bSdh  * descendents.
72244c06356bSdh  * 3. Expander subsidiary PHYs at level 0 are not locked.  It is the
72254c06356bSdh  * responsibility of the caller to individually lock expander subsidiary PHYs
72264c06356bSdh  * at level 0 if necessary.
72274c06356bSdh  * 4. Siblings at level 0 are not traversed due to the possibility that we're
72284c06356bSdh  * locking a PHY on the dead list.  The siblings could be pointing to invalid
72294c06356bSdh  * PHYs.  We don't lock siblings at level 0 anyway.
72304c06356bSdh  */
72314c06356bSdh static void
72324c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
72334c06356bSdh {
72344c06356bSdh 	pmcs_phy_t *tphyp;
72354c06356bSdh 
72364c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
72374c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
72384c06356bSdh 
72394c06356bSdh 	/*
72404c06356bSdh 	 * Start walking the PHYs.
72414c06356bSdh 	 */
72424c06356bSdh 	tphyp = phyp;
72434c06356bSdh 	while (tphyp) {
72444c06356bSdh 		/*
72454c06356bSdh 		 * If we're at the top level, only lock ourselves.  For anything
72464c06356bSdh 		 * at level > 0, traverse children while locking everything.
72474c06356bSdh 		 */
72484c06356bSdh 		if ((level > 0) || (tphyp == phyp)) {
7249c3bc407cSdh 			pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp,
7250c3bc407cSdh 			    NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d",
72514c06356bSdh 			    __func__, (void *)tphyp, (void *)tphyp->parent,
72524c06356bSdh 			    tphyp->path, level);
72534c06356bSdh 			mutex_enter(&tphyp->phy_lock);
72544c06356bSdh 
72554c06356bSdh 			if (tphyp->children) {
72564c06356bSdh 				pmcs_lock_phy_impl(tphyp->children, level + 1);
72574c06356bSdh 			}
72584c06356bSdh 		}
72594c06356bSdh 
72604c06356bSdh 		if (level == 0) {
72614c06356bSdh 			return;
72624c06356bSdh 		}
72634c06356bSdh 
72644c06356bSdh 		tphyp = tphyp->sibling;
72654c06356bSdh 	}
72664c06356bSdh }
72674c06356bSdh 
72684c06356bSdh /*
72694c06356bSdh  * pmcs_lock_phy
72704c06356bSdh  *
72714c06356bSdh  * This function is responsible for locking a PHY and all its descendents
72724c06356bSdh  */
72734c06356bSdh void
72744c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp)
72754c06356bSdh {
72764c06356bSdh #ifdef DEBUG
72774c06356bSdh 	char *callername = NULL;
72784c06356bSdh 	ulong_t off;
72794c06356bSdh 
72804c06356bSdh 	ASSERT(phyp != NULL);
72814c06356bSdh 
72824c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
72834c06356bSdh 
72844c06356bSdh 	if (callername == NULL) {
7285c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
72864c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
72874c06356bSdh 		    (void *)phyp, phyp->path);
72884c06356bSdh 	} else {
7289c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
72904c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
72914c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
72924c06356bSdh 	}
72934c06356bSdh #else
7294c3bc407cSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
72954c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
72964c06356bSdh #endif
72974c06356bSdh 	pmcs_lock_phy_impl(phyp, 0);
72984c06356bSdh }
72994c06356bSdh 
73004c06356bSdh /*
73014c06356bSdh  * pmcs_unlock_phy_impl
73024c06356bSdh  *
73034c06356bSdh  * Unlock all PHYs from phyp down in a bottom-up fashion.
73044c06356bSdh  */
73054c06356bSdh static void
73064c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
73074c06356bSdh {
73084c06356bSdh 	pmcs_phy_t *phy_next;
73094c06356bSdh 
73104c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
73114c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
73124c06356bSdh 
73134c06356bSdh 	/*
73144c06356bSdh 	 * Recurse down to the bottom PHYs
73154c06356bSdh 	 */
73164c06356bSdh 	if (level == 0) {
73174c06356bSdh 		if (phyp->children) {
73184c06356bSdh 			pmcs_unlock_phy_impl(phyp->children, level + 1);
73194c06356bSdh 		}
73204c06356bSdh 	} else {
73214c06356bSdh 		phy_next = phyp;
73224c06356bSdh 		while (phy_next) {
73234c06356bSdh 			if (phy_next->children) {
73244c06356bSdh 				pmcs_unlock_phy_impl(phy_next->children,
73254c06356bSdh 				    level + 1);
73264c06356bSdh 			}
73274c06356bSdh 			phy_next = phy_next->sibling;
73284c06356bSdh 		}
73294c06356bSdh 	}
73304c06356bSdh 
73314c06356bSdh 	/*
73324c06356bSdh 	 * Iterate through PHYs unlocking all at level > 0 as well the top PHY
73334c06356bSdh 	 */
73344c06356bSdh 	phy_next = phyp;
73354c06356bSdh 	while (phy_next) {
73364c06356bSdh 		if ((level > 0) || (phy_next == phyp)) {
73374c06356bSdh 			pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7338c3bc407cSdh 			    phy_next, NULL,
73394c06356bSdh 			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
73404c06356bSdh 			    __func__, (void *)phy_next,
73414c06356bSdh 			    (void *)phy_next->parent, phy_next->path, level);
73424c06356bSdh 			mutex_exit(&phy_next->phy_lock);
73434c06356bSdh 		}
73444c06356bSdh 
73454c06356bSdh 		if (level == 0) {
73464c06356bSdh 			return;
73474c06356bSdh 		}
73484c06356bSdh 
73494c06356bSdh 		phy_next = phy_next->sibling;
73504c06356bSdh 	}
73514c06356bSdh }
73524c06356bSdh 
73534c06356bSdh /*
73544c06356bSdh  * pmcs_unlock_phy
73554c06356bSdh  *
73564c06356bSdh  * Unlock a PHY and all its descendents
73574c06356bSdh  */
73584c06356bSdh void
73594c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp)
73604c06356bSdh {
73614c06356bSdh #ifdef DEBUG
73624c06356bSdh 	char *callername = NULL;
73634c06356bSdh 	ulong_t off;
73644c06356bSdh 
73654c06356bSdh 	ASSERT(phyp != NULL);
73664c06356bSdh 
73674c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
73684c06356bSdh 
73694c06356bSdh 	if (callername == NULL) {
7370c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73714c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
73724c06356bSdh 		    (void *)phyp, phyp->path);
73734c06356bSdh 	} else {
7374c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73754c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
73764c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
73774c06356bSdh 	}
73784c06356bSdh #else
7379c3bc407cSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73804c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
73814c06356bSdh #endif
73824c06356bSdh 	pmcs_unlock_phy_impl(phyp, 0);
73834c06356bSdh }
73844c06356bSdh 
73854c06356bSdh /*
73864c06356bSdh  * pmcs_get_root_phy
73874c06356bSdh  *
73884c06356bSdh  * For a given phy pointer return its root phy.
73899aed1621SDavid Hollister  * This function must only be called during discovery in order to ensure that
73909aed1621SDavid Hollister  * the chain of PHYs from phyp up to the root PHY doesn't change.
73914c06356bSdh  */
73924c06356bSdh pmcs_phy_t *
73934c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp)
73944c06356bSdh {
73954c06356bSdh 	ASSERT(phyp);
73964c06356bSdh 
73974c06356bSdh 	while (phyp) {
73984c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
73994c06356bSdh 			break;
74004c06356bSdh 		}
74014c06356bSdh 		phyp = phyp->parent;
74024c06356bSdh 	}
74034c06356bSdh 
74044c06356bSdh 	return (phyp);
74054c06356bSdh }
74064c06356bSdh 
74074c06356bSdh /*
74084c06356bSdh  * pmcs_free_dma_chunklist
74094c06356bSdh  *
74104c06356bSdh  * Free DMA S/G chunk list
74114c06356bSdh  */
74124c06356bSdh void
74134c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
74144c06356bSdh {
74154c06356bSdh 	pmcs_chunk_t	*pchunk;
74164c06356bSdh 
74174c06356bSdh 	while (pwp->dma_chunklist) {
74184c06356bSdh 		pchunk = pwp->dma_chunklist;
74194c06356bSdh 		pwp->dma_chunklist = pwp->dma_chunklist->next;
74204c06356bSdh 		if (pchunk->dma_handle) {
74214c06356bSdh 			if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
74224c06356bSdh 			    DDI_SUCCESS) {
7423c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7424c3bc407cSdh 				    "Condition failed at %s():%d",
7425c3bc407cSdh 				    __func__, __LINE__);
74264c06356bSdh 			}
74274c06356bSdh 			ddi_dma_free_handle(&pchunk->dma_handle);
74284c06356bSdh 			ddi_dma_mem_free(&pchunk->acc_handle);
74294c06356bSdh 		}
74304c06356bSdh 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
74314c06356bSdh 	}
74324c06356bSdh }
74334c06356bSdh 
74344c06356bSdh /*ARGSUSED2*/
74354c06356bSdh int
74364c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags)
74374c06356bSdh {
74384c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
74394c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
74404c06356bSdh 
74414c06356bSdh 	mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
74424c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
74434c06356bSdh 	cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
74444c06356bSdh 	return (0);
74454c06356bSdh }
74464c06356bSdh 
74474c06356bSdh /*ARGSUSED1*/
74484c06356bSdh void
74494c06356bSdh pmcs_phy_destructor(void *buf, void *arg)
74504c06356bSdh {
74514c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
74524c06356bSdh 
74534c06356bSdh 	cv_destroy(&phyp->abort_all_cv);
74544c06356bSdh 	mutex_destroy(&phyp->phy_lock);
74554c06356bSdh }
74564c06356bSdh 
74574c06356bSdh /*
74584c06356bSdh  * Free all PHYs from the kmem_cache starting at phyp as well as everything
74594c06356bSdh  * on the dead_phys list.
74604c06356bSdh  *
74614c06356bSdh  * NOTE: This function does not free root PHYs as they are not allocated
74624c06356bSdh  * from the kmem_cache.
74634c06356bSdh  *
74644c06356bSdh  * No PHY locks are acquired as this should only be called during DDI_DETACH
74654c06356bSdh  * or soft reset (while pmcs interrupts are disabled).
74664c06356bSdh  */
74674c06356bSdh void
74684c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
74694c06356bSdh {
74701b94a41bSChris Horne 	pmcs_phy_t *tphyp, *nphyp, *cphyp;
74714c06356bSdh 
74724c06356bSdh 	if (phyp == NULL) {
74734c06356bSdh 		return;
74744c06356bSdh 	}
74754c06356bSdh 
74761b94a41bSChris Horne 	for (tphyp = phyp; tphyp; tphyp = nphyp) {
74774c06356bSdh 		nphyp = tphyp->sibling;
74781b94a41bSChris Horne 		cphyp = tphyp->children;
74794c06356bSdh 
74801b94a41bSChris Horne 		if (cphyp) {
74814c06356bSdh 			tphyp->children = NULL;
74821b94a41bSChris Horne 			pmcs_free_all_phys(pwp, cphyp);
74834c06356bSdh 		}
74841b94a41bSChris Horne 
74854c06356bSdh 		if (!IS_ROOT_PHY(tphyp)) {
74864c06356bSdh 			kmem_cache_free(pwp->phy_cache, tphyp);
74874c06356bSdh 		}
74884c06356bSdh 	}
74894c06356bSdh 
7490978d7443SSrikanth Suravajhala 	mutex_enter(&pwp->dead_phylist_lock);
74911b94a41bSChris Horne 	for (tphyp = pwp->dead_phys; tphyp; tphyp = nphyp) {
7492978d7443SSrikanth Suravajhala 		nphyp = tphyp->dead_next;
74934c06356bSdh 		kmem_cache_free(pwp->phy_cache, tphyp);
74944c06356bSdh 	}
74954c06356bSdh 	pwp->dead_phys = NULL;
7496978d7443SSrikanth Suravajhala 	mutex_exit(&pwp->dead_phylist_lock);
74974c06356bSdh }
74984c06356bSdh 
74994c06356bSdh /*
75004c06356bSdh  * Free a list of PHYs linked together by the sibling pointer back to the
75014c06356bSdh  * kmem cache from whence they came.  This function does not recurse, so the
75024c06356bSdh  * caller must ensure there are no children.
75034c06356bSdh  */
75044c06356bSdh void
75054c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
75064c06356bSdh {
75074c06356bSdh 	pmcs_phy_t *next_phy;
75084c06356bSdh 
75094c06356bSdh 	while (phyp) {
75104c06356bSdh 		next_phy = phyp->sibling;
75114c06356bSdh 		ASSERT(!mutex_owned(&phyp->phy_lock));
75124c06356bSdh 		kmem_cache_free(pwp->phy_cache, phyp);
75134c06356bSdh 		phyp = next_phy;
75144c06356bSdh 	}
75154c06356bSdh }
75164c06356bSdh 
75174c06356bSdh /*
75184c06356bSdh  * Make a copy of an existing PHY structure.  This is used primarily in
75194c06356bSdh  * discovery to compare the contents of an existing PHY with what gets
75204c06356bSdh  * reported back by an expander.
75214c06356bSdh  *
75224c06356bSdh  * This function must not be called from any context where sleeping is
75234c06356bSdh  * not possible.
75244c06356bSdh  *
75254c06356bSdh  * The new PHY is returned unlocked.
75264c06356bSdh  */
75274c06356bSdh static pmcs_phy_t *
75284c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy)
75294c06356bSdh {
75304c06356bSdh 	pmcs_phy_t *local;
75314c06356bSdh 
75324c06356bSdh 	local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
75334c06356bSdh 
75344c06356bSdh 	/*
75354c06356bSdh 	 * Go ahead and just copy everything...
75364c06356bSdh 	 */
75374c06356bSdh 	*local = *orig_phy;
75384c06356bSdh 
75394c06356bSdh 	/*
75404c06356bSdh 	 * But the following must be set appropriately for this copy
75414c06356bSdh 	 */
75424c06356bSdh 	local->sibling = NULL;
75434c06356bSdh 	local->children = NULL;
75444c06356bSdh 	mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
75454c06356bSdh 	    DDI_INTR_PRI(orig_phy->pwp->intr_pri));
75464c06356bSdh 
75474c06356bSdh 	return (local);
75484c06356bSdh }
75494c06356bSdh 
75504c06356bSdh int
75514c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle)
75524c06356bSdh {
75534c06356bSdh 	ddi_fm_error_t de;
75544c06356bSdh 
75554c06356bSdh 	if (handle == NULL) {
75564c06356bSdh 		return (DDI_FAILURE);
75574c06356bSdh 	}
75584c06356bSdh 	ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
75594c06356bSdh 	return (de.fme_status);
75604c06356bSdh }
75614c06356bSdh 
75624c06356bSdh int
75634c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle)
75644c06356bSdh {
75654c06356bSdh 	ddi_fm_error_t de;
75664c06356bSdh 
75674c06356bSdh 	if (handle == NULL) {
75684c06356bSdh 		return (DDI_FAILURE);
75694c06356bSdh 	}
75704c06356bSdh 	ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
75714c06356bSdh 	return (de.fme_status);
75724c06356bSdh }
75734c06356bSdh 
75744c06356bSdh 
75754c06356bSdh void
75764c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
75774c06356bSdh {
75784c06356bSdh 	uint64_t ena;
75794c06356bSdh 	char buf[FM_MAX_CLASS];
75804c06356bSdh 
75814c06356bSdh 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
75824c06356bSdh 	ena = fm_ena_generate(0, FM_ENA_FMT1);
75834c06356bSdh 	if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
75844c06356bSdh 		ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
75854c06356bSdh 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
75864c06356bSdh 	}
75874c06356bSdh }
75884c06356bSdh 
75894c06356bSdh int
75904c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
75914c06356bSdh {
75924c06356bSdh 	pmcs_chunk_t *pchunk;
75934c06356bSdh 	int i;
75944c06356bSdh 
75954c06356bSdh 	/* check all acc & dma handles allocated in attach */
75964c06356bSdh 	if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
75974c06356bSdh 	    (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
75984c06356bSdh 	    (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
75994c06356bSdh 	    (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
76004c06356bSdh 	    (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
76014c06356bSdh 		goto check_failed;
76024c06356bSdh 	}
76034c06356bSdh 
76044c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
76054c06356bSdh 		if ((pmcs_check_dma_handle(
76064c06356bSdh 		    pwp->iqp_handles[i]) != DDI_SUCCESS) ||
76074c06356bSdh 		    (pmcs_check_acc_handle(
76084c06356bSdh 		    pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
76094c06356bSdh 			goto check_failed;
76104c06356bSdh 		}
76114c06356bSdh 	}
76124c06356bSdh 
76134c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
76144c06356bSdh 		if ((pmcs_check_dma_handle(
76154c06356bSdh 		    pwp->oqp_handles[i]) != DDI_SUCCESS) ||
76164c06356bSdh 		    (pmcs_check_acc_handle(
76174c06356bSdh 		    pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
76184c06356bSdh 			goto check_failed;
76194c06356bSdh 		}
76204c06356bSdh 	}
76214c06356bSdh 
76224c06356bSdh 	if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
76234c06356bSdh 	    (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
76244c06356bSdh 		goto check_failed;
76254c06356bSdh 	}
76264c06356bSdh 
76274c06356bSdh 	if (pwp->fwlog &&
76284c06356bSdh 	    ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
76294c06356bSdh 	    (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
76304c06356bSdh 		goto check_failed;
76314c06356bSdh 	}
76324c06356bSdh 
76334c06356bSdh 	if (pwp->regdump_hndl && pwp->regdump_acchdl &&
76344c06356bSdh 	    ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
76354c06356bSdh 	    (pmcs_check_acc_handle(pwp->regdump_acchdl)
76364c06356bSdh 	    != DDI_SUCCESS))) {
76374c06356bSdh 		goto check_failed;
76384c06356bSdh 	}
76394c06356bSdh 
76404c06356bSdh 
76414c06356bSdh 	pchunk = pwp->dma_chunklist;
76424c06356bSdh 	while (pchunk) {
76434c06356bSdh 		if ((pmcs_check_acc_handle(pchunk->acc_handle)
76444c06356bSdh 		    != DDI_SUCCESS) ||
76454c06356bSdh 		    (pmcs_check_dma_handle(pchunk->dma_handle)
76464c06356bSdh 		    != DDI_SUCCESS)) {
76474c06356bSdh 			goto check_failed;
76484c06356bSdh 		}
76494c06356bSdh 		pchunk = pchunk->next;
76504c06356bSdh 	}
76514c06356bSdh 
76524c06356bSdh 	return (0);
76534c06356bSdh 
76544c06356bSdh check_failed:
76554c06356bSdh 
76564c06356bSdh 	return (1);
76574c06356bSdh }
76584c06356bSdh 
76594c06356bSdh /*
76604c06356bSdh  * pmcs_handle_dead_phys
76614c06356bSdh  *
76624c06356bSdh  * If the PHY has no outstanding work associated with it, remove it from
76634c06356bSdh  * the dead PHY list and free it.
76644c06356bSdh  *
76654c06356bSdh  * If pwp->ds_err_recovering or pwp->configuring is set, don't run.
76664c06356bSdh  * This keeps routines that need to submit work to the chip from having to
76674c06356bSdh  * hold PHY locks to ensure that PHYs don't disappear while they do their work.
76684c06356bSdh  */
76694c06356bSdh void
76704c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp)
76714c06356bSdh {
76724c06356bSdh 	pmcs_phy_t *phyp, *nphyp, *pphyp;
76734c06356bSdh 
76744c06356bSdh 	mutex_enter(&pwp->lock);
76754c06356bSdh 	mutex_enter(&pwp->config_lock);
76764c06356bSdh 
76774c06356bSdh 	if (pwp->configuring | pwp->ds_err_recovering) {
76784c06356bSdh 		mutex_exit(&pwp->config_lock);
76794c06356bSdh 		mutex_exit(&pwp->lock);
76804c06356bSdh 		return;
76814c06356bSdh 	}
76824c06356bSdh 
76834c06356bSdh 	/*
76844c06356bSdh 	 * Check every PHY in the dead PHY list
76854c06356bSdh 	 */
76864c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
76874c06356bSdh 	phyp = pwp->dead_phys;
76884c06356bSdh 	pphyp = NULL;	/* Set previous PHY to NULL */
76894c06356bSdh 
76904c06356bSdh 	while (phyp != NULL) {
76914c06356bSdh 		pmcs_lock_phy(phyp);
76924c06356bSdh 		ASSERT(phyp->dead);
76934c06356bSdh 
76944c06356bSdh 		nphyp = phyp->dead_next;
76954c06356bSdh 
76964c06356bSdh 		/*
76974c06356bSdh 		 * Check for outstanding work
76984c06356bSdh 		 */
76994c06356bSdh 		if (phyp->ref_count > 0) {
77004c06356bSdh 			pmcs_unlock_phy(phyp);
77014c06356bSdh 			pphyp = phyp;	/* This PHY becomes "previous" */
77024c06356bSdh 		} else if (phyp->target) {
77034c06356bSdh 			pmcs_unlock_phy(phyp);
7704c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target,
77054c06356bSdh 			    "%s: Not freeing PHY 0x%p: target 0x%p is not free",
77064c06356bSdh 			    __func__, (void *)phyp, (void *)phyp->target);
77074c06356bSdh 			pphyp = phyp;
77084c06356bSdh 		} else {
77094c06356bSdh 			/*
77104c06356bSdh 			 * No outstanding work or target references. Remove it
77114c06356bSdh 			 * from the list and free it
77124c06356bSdh 			 */
7713c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
77144c06356bSdh 			    "%s: Freeing inactive dead PHY 0x%p @ %s "
77154c06356bSdh 			    "target = 0x%p", __func__, (void *)phyp,
77164c06356bSdh 			    phyp->path, (void *)phyp->target);
77174c06356bSdh 			/*
77184c06356bSdh 			 * If pphyp is NULL, then phyp was the head of the list,
77194c06356bSdh 			 * so just reset the head to nphyp. Otherwise, the
77204c06356bSdh 			 * previous PHY will now point to nphyp (the next PHY)
77214c06356bSdh 			 */
77224c06356bSdh 			if (pphyp == NULL) {
77234c06356bSdh 				pwp->dead_phys = nphyp;
77244c06356bSdh 			} else {
77254c06356bSdh 				pphyp->dead_next = nphyp;
77264c06356bSdh 			}
77274c06356bSdh 			/*
77284c06356bSdh 			 * If the target still points to this PHY, remove
77294c06356bSdh 			 * that linkage now.
77304c06356bSdh 			 */
77314c06356bSdh 			if (phyp->target) {
77324c06356bSdh 				mutex_enter(&phyp->target->statlock);
77334c06356bSdh 				if (phyp->target->phy == phyp) {
77344c06356bSdh 					phyp->target->phy = NULL;
77354c06356bSdh 				}
77364c06356bSdh 				mutex_exit(&phyp->target->statlock);
77374c06356bSdh 			}
7738b18a19c2SJesse Butler 			pmcs_unlock_phy(phyp);
77394c06356bSdh 			kmem_cache_free(pwp->phy_cache, phyp);
77404c06356bSdh 		}
77414c06356bSdh 
77424c06356bSdh 		phyp = nphyp;
77434c06356bSdh 	}
77444c06356bSdh 
77454c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
77464c06356bSdh 	mutex_exit(&pwp->config_lock);
77474c06356bSdh 	mutex_exit(&pwp->lock);
77484c06356bSdh }
77494c06356bSdh 
77504c06356bSdh void
77514c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
77524c06356bSdh {
77534c06356bSdh 	atomic_inc_32(&phyp->ref_count);
77544c06356bSdh }
77554c06356bSdh 
77564c06356bSdh void
77574c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
77584c06356bSdh {
77594c06356bSdh 	ASSERT(phyp->ref_count != 0);
77604c06356bSdh 	atomic_dec_32(&phyp->ref_count);
77614c06356bSdh }
77624c06356bSdh 
77634c06356bSdh /*
77644c06356bSdh  * pmcs_reap_dead_phy
77654c06356bSdh  *
77664c06356bSdh  * This function is called from pmcs_new_tport when we have a PHY
77674c06356bSdh  * without a target pointer.  It's possible in that case that this PHY
77684c06356bSdh  * may have a "brother" on the dead_phys list.  That is, it may be the same as
77694c06356bSdh  * this one but with a different root PHY number (e.g. pp05 vs. pp04).  If
77704c06356bSdh  * that's the case, update the dead PHY and this new PHY.  If that's not the
77714c06356bSdh  * case, we should get a tran_tgt_init on this after it's reported to SCSA.
77724c06356bSdh  *
77734c06356bSdh  * Called with PHY locked.
77744c06356bSdh  */
77754c06356bSdh static void
77764c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp)
77774c06356bSdh {
77784c06356bSdh 	pmcs_hw_t *pwp = phyp->pwp;
77794c06356bSdh 	pmcs_phy_t *ctmp;
778073a3eccdSDavid Hollister 	pmcs_iport_t *iport_cmp;
77814c06356bSdh 
77824c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
77834c06356bSdh 
77844c06356bSdh 	/*
77854c06356bSdh 	 * Check the dead PHYs list
77864c06356bSdh 	 */
77874c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
77884c06356bSdh 	ctmp = pwp->dead_phys;
77894c06356bSdh 	while (ctmp) {
779073a3eccdSDavid Hollister 		/*
779173a3eccdSDavid Hollister 		 * If the iport is NULL, compare against last_iport.
779273a3eccdSDavid Hollister 		 */
779373a3eccdSDavid Hollister 		if (ctmp->iport) {
779473a3eccdSDavid Hollister 			iport_cmp = ctmp->iport;
779573a3eccdSDavid Hollister 		} else {
779673a3eccdSDavid Hollister 			iport_cmp = ctmp->last_iport;
779773a3eccdSDavid Hollister 		}
779873a3eccdSDavid Hollister 
779973a3eccdSDavid Hollister 		if ((iport_cmp != phyp->iport) ||
78004c06356bSdh 		    (memcmp((void *)&ctmp->sas_address[0],
78014c06356bSdh 		    (void *)&phyp->sas_address[0], 8))) {
78024c06356bSdh 			ctmp = ctmp->dead_next;
78034c06356bSdh 			continue;
78044c06356bSdh 		}
78054c06356bSdh 
78064c06356bSdh 		/*
78074c06356bSdh 		 * Same SAS address on same iport.  Now check to see if
78084c06356bSdh 		 * the PHY path is the same with the possible exception
78094c06356bSdh 		 * of the root PHY number.
78104c06356bSdh 		 * The "5" is the string length of "pp00."
78114c06356bSdh 		 */
78124c06356bSdh 		if ((strnlen(phyp->path, 5) >= 5) &&
78134c06356bSdh 		    (strnlen(ctmp->path, 5) >= 5)) {
78144c06356bSdh 			if (memcmp((void *)&phyp->path[5],
78154c06356bSdh 			    (void *)&ctmp->path[5],
78164c06356bSdh 			    strnlen(phyp->path, 32) - 5) == 0) {
78174c06356bSdh 				break;
78184c06356bSdh 			}
78194c06356bSdh 		}
78204c06356bSdh 
78214c06356bSdh 		ctmp = ctmp->dead_next;
78224c06356bSdh 	}
78234c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
78244c06356bSdh 
78254c06356bSdh 	/*
78264c06356bSdh 	 * Found a match.  Remove the target linkage and drop the
78274c06356bSdh 	 * ref count on the old PHY.  Then, increment the ref count
78284c06356bSdh 	 * on the new PHY to compensate.
78294c06356bSdh 	 */
78304c06356bSdh 	if (ctmp) {
7831c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
783273a3eccdSDavid Hollister 		    "%s: Found match in dead PHY list (0x%p) for new PHY %s",
783373a3eccdSDavid Hollister 		    __func__, (void *)ctmp, phyp->path);
783473a3eccdSDavid Hollister 		/*
783573a3eccdSDavid Hollister 		 * If there is a pointer to the target in the dead PHY, move
783673a3eccdSDavid Hollister 		 * all reference counts to the new PHY.
783773a3eccdSDavid Hollister 		 */
78384c06356bSdh 		if (ctmp->target) {
783973a3eccdSDavid Hollister 			mutex_enter(&ctmp->target->statlock);
78404c06356bSdh 			phyp->target = ctmp->target;
784173a3eccdSDavid Hollister 
784273a3eccdSDavid Hollister 			while (ctmp->ref_count != 0) {
784373a3eccdSDavid Hollister 				pmcs_inc_phy_ref_count(phyp);
784473a3eccdSDavid Hollister 				pmcs_dec_phy_ref_count(ctmp);
784573a3eccdSDavid Hollister 			}
78464c06356bSdh 			/*
78474c06356bSdh 			 * Update the target's linkage as well
78484c06356bSdh 			 */
78494c06356bSdh 			phyp->target->phy = phyp;
78504c06356bSdh 			phyp->target->dtype = phyp->dtype;
785173a3eccdSDavid Hollister 			ctmp->target = NULL;
78524c06356bSdh 			mutex_exit(&phyp->target->statlock);
78534c06356bSdh 		}
78544c06356bSdh 	}
78554c06356bSdh }
78564c06356bSdh 
78574c06356bSdh /*
78584c06356bSdh  * Called with iport lock held
78594c06356bSdh  */
78604c06356bSdh void
78614c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
78624c06356bSdh {
78634c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
78644c06356bSdh 	ASSERT(phyp);
78654c06356bSdh 	ASSERT(!list_link_active(&phyp->list_node));
7866978d7443SSrikanth Suravajhala 
78674c06356bSdh 	iport->nphy++;
7868145e0143Sdh 	list_insert_tail(&iport->phys, phyp);
78694c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
78704c06356bSdh 	    &iport->nphy);
787135dae232SSrikanth Suravajhala 	mutex_enter(&phyp->phy_lock);
787235dae232SSrikanth Suravajhala 	pmcs_create_one_phy_stats(iport, phyp);
787335dae232SSrikanth Suravajhala 	mutex_exit(&phyp->phy_lock);
7874*3be32c0fSJesse Butler 	pmcs_hold_iport(iport);
78754c06356bSdh }
78764c06356bSdh 
78774c06356bSdh /*
78784c06356bSdh  * Called with the iport lock held
78794c06356bSdh  */
78804c06356bSdh void
78814c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
78824c06356bSdh {
78834c06356bSdh 	pmcs_phy_t *pptr, *next_pptr;
78844c06356bSdh 
78854c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
78864c06356bSdh 
78874c06356bSdh 	/*
78884c06356bSdh 	 * If phyp is NULL, remove all PHYs from the iport
78894c06356bSdh 	 */
78904c06356bSdh 	if (phyp == NULL) {
78914c06356bSdh 		for (pptr = list_head(&iport->phys); pptr != NULL;
78924c06356bSdh 		    pptr = next_pptr) {
78934c06356bSdh 			next_pptr = list_next(&iport->phys, pptr);
78944c06356bSdh 			mutex_enter(&pptr->phy_lock);
789535dae232SSrikanth Suravajhala 			if (pptr->phy_stats != NULL) {
789635dae232SSrikanth Suravajhala 				kstat_delete(pptr->phy_stats);
789735dae232SSrikanth Suravajhala 				pptr->phy_stats = NULL;
789835dae232SSrikanth Suravajhala 			}
78994c06356bSdh 			pptr->iport = NULL;
79005c45adf0SJesse Butler 			pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
79015c45adf0SJesse Butler 			    pptr->tgt_port_pm_tmp, B_FALSE);
79024c06356bSdh 			mutex_exit(&pptr->phy_lock);
79034c06356bSdh 			pmcs_rele_iport(iport);
79044c06356bSdh 			list_remove(&iport->phys, pptr);
79059aed1621SDavid Hollister 			pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32,
79069aed1621SDavid Hollister 			    PMCS_NUM_PHYS, &iport->nphy);
79074c06356bSdh 		}
79084c06356bSdh 		iport->nphy = 0;
79094c06356bSdh 		return;
79104c06356bSdh 	}
79114c06356bSdh 
79124c06356bSdh 	ASSERT(phyp);
79134c06356bSdh 	ASSERT(iport->nphy > 0);
79144c06356bSdh 	ASSERT(list_link_active(&phyp->list_node));
79154c06356bSdh 	iport->nphy--;
7916145e0143Sdh 	list_remove(&iport->phys, phyp);
7917499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp,
7918499cfd15SDavid Hollister 	    phyp->tgt_port_pm_tmp, B_FALSE);
79194c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
79204c06356bSdh 	    &iport->nphy);
79214c06356bSdh 	pmcs_rele_iport(iport);
79224c06356bSdh }
79234c06356bSdh 
79244c06356bSdh /*
79254c06356bSdh  * This function checks to see if the target pointed to by phyp is still
79264c06356bSdh  * correct.  This is done by comparing the target's unit address with the
79274c06356bSdh  * SAS address in phyp.
79284c06356bSdh  *
79294c06356bSdh  * Called with PHY locked and target statlock held
79304c06356bSdh  */
79314c06356bSdh static boolean_t
79324c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp)
79334c06356bSdh {
79344c06356bSdh 	uint64_t wwn;
79354c06356bSdh 	char unit_address[PMCS_MAX_UA_SIZE];
79364c06356bSdh 	boolean_t rval = B_FALSE;
79374c06356bSdh 
79384c06356bSdh 	ASSERT(phyp);
79394c06356bSdh 	ASSERT(phyp->target);
79404c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
79414c06356bSdh 	ASSERT(mutex_owned(&phyp->target->statlock));
79424c06356bSdh 
79434c06356bSdh 	wwn = pmcs_barray2wwn(phyp->sas_address);
79444c06356bSdh 	(void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
79454c06356bSdh 
79464c06356bSdh 	if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
79474c06356bSdh 	    strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
79484c06356bSdh 		rval = B_TRUE;
79494c06356bSdh 	}
79504c06356bSdh 
79514c06356bSdh 	return (rval);
79524c06356bSdh }
79536745c559SJesse Butler /*
79546745c559SJesse Butler  * Commands used to serialize SMP requests.
79556745c559SJesse Butler  *
79566745c559SJesse Butler  * The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd
79576745c559SJesse Butler  * queued for the same SMP target. If a third SMP cmd is sent to the SPC for an
79586745c559SJesse Butler  * SMP target that already has a SMP cmd pending and one queued, then the
79596745c559SJesse Butler  * SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response.
79606745c559SJesse Butler  *
79616745c559SJesse Butler  * Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP
79626745c559SJesse Butler  * cmds that can be queued is controlled by the PORT_CONTROL IOMB. The
79636745c559SJesse Butler  * SPC default is 1 SMP command/port (iport).  These 2 queued SMP cmds would
79646745c559SJesse Butler  * have to be for different SMP targets.  The INTERNAL_SMP_RESOURCE error will
79656745c559SJesse Butler  * also be returned if a 2nd SMP cmd is sent to the controller when there is
79666745c559SJesse Butler  * already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the
79676745c559SJesse Butler  * queue if there are already 2 queued SMP cmds.
79686745c559SJesse Butler  */
79696745c559SJesse Butler void
79706745c559SJesse Butler pmcs_smp_acquire(pmcs_iport_t *iport)
79716745c559SJesse Butler {
79726745c559SJesse Butler 	if (iport == NULL) {
79736745c559SJesse Butler 		return;
79746745c559SJesse Butler 	}
79756745c559SJesse Butler 
79766745c559SJesse Butler 	mutex_enter(&iport->smp_lock);
79776745c559SJesse Butler 	while (iport->smp_active) {
79786745c559SJesse Butler 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
79796745c559SJesse Butler 		    "%s: SMP is active on thread 0x%p, waiting", __func__,
79806745c559SJesse Butler 		    (void *)iport->smp_active_thread);
79816745c559SJesse Butler 		cv_wait(&iport->smp_cv, &iport->smp_lock);
79826745c559SJesse Butler 	}
79836745c559SJesse Butler 	iport->smp_active = B_TRUE;
79846745c559SJesse Butler 	iport->smp_active_thread = curthread;
79859aed1621SDavid Hollister 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
79866745c559SJesse Butler 	    "%s: SMP acquired by thread 0x%p", __func__,
79876745c559SJesse Butler 	    (void *)iport->smp_active_thread);
79886745c559SJesse Butler 	mutex_exit(&iport->smp_lock);
79896745c559SJesse Butler }
79906745c559SJesse Butler 
79916745c559SJesse Butler void
79926745c559SJesse Butler pmcs_smp_release(pmcs_iport_t *iport)
79936745c559SJesse Butler {
79946745c559SJesse Butler 	if (iport == NULL) {
79956745c559SJesse Butler 		return;
79966745c559SJesse Butler 	}
79976745c559SJesse Butler 
79986745c559SJesse Butler 	mutex_enter(&iport->smp_lock);
79999aed1621SDavid Hollister 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
80006745c559SJesse Butler 	    "%s: SMP released by thread 0x%p", __func__, (void *)curthread);
80016745c559SJesse Butler 	iport->smp_active = B_FALSE;
80026745c559SJesse Butler 	iport->smp_active_thread = NULL;
80036745c559SJesse Butler 	cv_signal(&iport->smp_cv);
80046745c559SJesse Butler 	mutex_exit(&iport->smp_lock);
80056745c559SJesse Butler }
8006499cfd15SDavid Hollister 
8007499cfd15SDavid Hollister /*
8008499cfd15SDavid Hollister  * Update a PHY's attached-port-pm and target-port-pm properties
8009499cfd15SDavid Hollister  *
8010499cfd15SDavid Hollister  * phyp: PHY whose properties are to be updated
8011499cfd15SDavid Hollister  *
8012499cfd15SDavid Hollister  * att_bv: Bit value of the attached-port-pm property to be updated in the
8013499cfd15SDavid Hollister  * 64-bit holding area for the PHY.
8014499cfd15SDavid Hollister  *
8015499cfd15SDavid Hollister  * tgt_bv: Bit value of the target-port-pm property to update in the 64-bit
8016499cfd15SDavid Hollister  * holding area for the PHY.
8017499cfd15SDavid Hollister  *
8018499cfd15SDavid Hollister  * prop_add_val: If TRUE, we're adding bits into the property value.
8019499cfd15SDavid Hollister  * Otherwise, we're taking them out.  Either way, the properties for this
8020499cfd15SDavid Hollister  * PHY will be updated.
8021499cfd15SDavid Hollister  */
8022499cfd15SDavid Hollister void
8023499cfd15SDavid Hollister pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv,
8024499cfd15SDavid Hollister     boolean_t prop_add_val)
8025499cfd15SDavid Hollister {
8026499cfd15SDavid Hollister 	if (prop_add_val) {
8027499cfd15SDavid Hollister 		/*
8028499cfd15SDavid Hollister 		 * If the values are currently 0, then we're setting the
8029499cfd15SDavid Hollister 		 * phymask for just this PHY as well.
8030499cfd15SDavid Hollister 		 */
8031499cfd15SDavid Hollister 		if (phyp->att_port_pm_tmp == 0) {
8032616875b4SDavid Hollister 			phyp->att_port_pm = att_bv;
8033616875b4SDavid Hollister 			phyp->tgt_port_pm = tgt_bv;
8034499cfd15SDavid Hollister 		}
8035616875b4SDavid Hollister 		phyp->att_port_pm_tmp |= att_bv;
8036616875b4SDavid Hollister 		phyp->tgt_port_pm_tmp |= tgt_bv;
8037499cfd15SDavid Hollister 		(void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN,
8038499cfd15SDavid Hollister 		    "%"PRIx64, phyp->att_port_pm_tmp);
8039499cfd15SDavid Hollister 		(void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN,
8040499cfd15SDavid Hollister 		    "%"PRIx64, phyp->tgt_port_pm_tmp);
8041499cfd15SDavid Hollister 	} else {
8042616875b4SDavid Hollister 		phyp->att_port_pm_tmp &= ~att_bv;
8043616875b4SDavid Hollister 		phyp->tgt_port_pm_tmp &= ~tgt_bv;
8044499cfd15SDavid Hollister 		if (phyp->att_port_pm_tmp) {
8045499cfd15SDavid Hollister 			(void) snprintf(phyp->att_port_pm_str,
8046499cfd15SDavid Hollister 			    PMCS_PM_MAX_NAMELEN, "%"PRIx64,
8047499cfd15SDavid Hollister 			    phyp->att_port_pm_tmp);
8048499cfd15SDavid Hollister 		} else {
8049499cfd15SDavid Hollister 			phyp->att_port_pm_str[0] = '\0';
8050499cfd15SDavid Hollister 			phyp->att_port_pm = 0;
8051499cfd15SDavid Hollister 		}
8052499cfd15SDavid Hollister 		if (phyp->tgt_port_pm_tmp) {
8053499cfd15SDavid Hollister 			(void) snprintf(phyp->tgt_port_pm_str,
8054499cfd15SDavid Hollister 			    PMCS_PM_MAX_NAMELEN, "%"PRIx64,
8055499cfd15SDavid Hollister 			    phyp->tgt_port_pm_tmp);
8056499cfd15SDavid Hollister 		} else {
8057499cfd15SDavid Hollister 			phyp->tgt_port_pm_str[0] = '\0';
8058499cfd15SDavid Hollister 			phyp->tgt_port_pm = 0;
8059499cfd15SDavid Hollister 		}
8060499cfd15SDavid Hollister 	}
8061499cfd15SDavid Hollister 
8062499cfd15SDavid Hollister 	if (phyp->target == NULL) {
8063499cfd15SDavid Hollister 		return;
8064499cfd15SDavid Hollister 	}
8065499cfd15SDavid Hollister 
806673a3eccdSDavid Hollister 	mutex_enter(&phyp->target->statlock);
806773a3eccdSDavid Hollister 	if (!list_is_empty(&phyp->target->lun_list)) {
806873a3eccdSDavid Hollister 		pmcs_lun_t *lunp;
806973a3eccdSDavid Hollister 
807073a3eccdSDavid Hollister 		lunp = list_head(&phyp->target->lun_list);
807173a3eccdSDavid Hollister 		while (lunp) {
807273a3eccdSDavid Hollister 			(void) scsi_device_prop_update_string(lunp->sd,
807373a3eccdSDavid Hollister 			    SCSI_DEVICE_PROP_PATH,
807473a3eccdSDavid Hollister 			    SCSI_ADDR_PROP_ATTACHED_PORT_PM,
807573a3eccdSDavid Hollister 			    phyp->att_port_pm_str);
807673a3eccdSDavid Hollister 			(void) scsi_device_prop_update_string(lunp->sd,
807773a3eccdSDavid Hollister 			    SCSI_DEVICE_PROP_PATH,
807873a3eccdSDavid Hollister 			    SCSI_ADDR_PROP_TARGET_PORT_PM,
807973a3eccdSDavid Hollister 			    phyp->tgt_port_pm_str);
808073a3eccdSDavid Hollister 			lunp = list_next(&phyp->target->lun_list, lunp);
808173a3eccdSDavid Hollister 		}
8082499cfd15SDavid Hollister 	} else if (phyp->target->smpd) {
8083499cfd15SDavid Hollister 		(void) smp_device_prop_update_string(phyp->target->smpd,
8084499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_ATTACHED_PORT_PM,
8085499cfd15SDavid Hollister 		    phyp->att_port_pm_str);
8086499cfd15SDavid Hollister 		(void) smp_device_prop_update_string(phyp->target->smpd,
8087499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_TARGET_PORT_PM,
8088499cfd15SDavid Hollister 		    phyp->tgt_port_pm_str);
8089499cfd15SDavid Hollister 	}
809073a3eccdSDavid Hollister 	mutex_exit(&phyp->target->statlock);
8091499cfd15SDavid Hollister }
8092601c90f1SSrikanth, Ramana 
8093601c90f1SSrikanth, Ramana /* ARGSUSED */
8094601c90f1SSrikanth, Ramana void
8095601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
8096601c90f1SSrikanth, Ramana {
8097601c90f1SSrikanth, Ramana 	pmcs_phy_t	*pptr;
8098601c90f1SSrikanth, Ramana 
8099601c90f1SSrikanth, Ramana 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8100601c90f1SSrikanth, Ramana 		pmcs_lock_phy(pptr);
8101601c90f1SSrikanth, Ramana 		if (pptr->deregister_wait) {
8102601c90f1SSrikanth, Ramana 			pmcs_deregister_device(pwp, pptr);
8103601c90f1SSrikanth, Ramana 		}
8104601c90f1SSrikanth, Ramana 		pmcs_unlock_phy(pptr);
8105601c90f1SSrikanth, Ramana 	}
8106601c90f1SSrikanth, Ramana }
81079aed1621SDavid Hollister 
81089aed1621SDavid Hollister /*
81099aed1621SDavid Hollister  * pmcs_iport_active
81109aed1621SDavid Hollister  *
81119aed1621SDavid Hollister  * Mark this iport as active.  Called with the iport lock held.
81129aed1621SDavid Hollister  */
81139aed1621SDavid Hollister static void
81149aed1621SDavid Hollister pmcs_iport_active(pmcs_iport_t *iport)
81159aed1621SDavid Hollister {
81169aed1621SDavid Hollister 	ASSERT(mutex_owned(&iport->lock));
81179aed1621SDavid Hollister 
81189aed1621SDavid Hollister 	iport->ua_state = UA_ACTIVE;
81199aed1621SDavid Hollister 	iport->smp_active = B_FALSE;
81209aed1621SDavid Hollister 	iport->smp_active_thread = NULL;
81219aed1621SDavid Hollister }
81229aed1621SDavid Hollister 
81239aed1621SDavid Hollister /* ARGSUSED */
81249aed1621SDavid Hollister static void
81259aed1621SDavid Hollister pmcs_tgtmap_activate_cb(void *tgtmap_priv, char *tgt_addr,
81269aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t tgt_type, void **tgt_privp)
81279aed1621SDavid Hollister {
81289aed1621SDavid Hollister 	pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
81295c45adf0SJesse Butler 	pmcs_hw_t *pwp = iport->pwp;
81305c45adf0SJesse Butler 	pmcs_xscsi_t *target;
81319aed1621SDavid Hollister 
81325c45adf0SJesse Butler 	/*
81335c45adf0SJesse Butler 	 * Look up the target.  If there is one, and it doesn't have a PHY
81345c45adf0SJesse Butler 	 * pointer, re-establish that linkage here.
81355c45adf0SJesse Butler 	 */
81365c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
81375c45adf0SJesse Butler 	target = pmcs_get_target(iport, tgt_addr, B_FALSE);
81385c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
81395c45adf0SJesse Butler 
81405c45adf0SJesse Butler 	/*
81415c45adf0SJesse Butler 	 * If we got a target, it will now have a PHY pointer and the PHY
81425c45adf0SJesse Butler 	 * will point to the target.  The PHY will be locked, so we'll need
81435c45adf0SJesse Butler 	 * to unlock it.
81445c45adf0SJesse Butler 	 */
8145*3be32c0fSJesse Butler 	if (target != NULL) {
81465c45adf0SJesse Butler 		pmcs_unlock_phy(target->phy);
81475c45adf0SJesse Butler 	}
81489aed1621SDavid Hollister 
81499aed1621SDavid Hollister 	/*
81509aed1621SDavid Hollister 	 * Update config_restart_time so we don't try to restart discovery
81519aed1621SDavid Hollister 	 * while enumeration is still in progress.
81529aed1621SDavid Hollister 	 */
81535c45adf0SJesse Butler 	mutex_enter(&pwp->config_lock);
81545c45adf0SJesse Butler 	pwp->config_restart_time = ddi_get_lbolt() +
81559aed1621SDavid Hollister 	    drv_usectohz(PMCS_REDISCOVERY_DELAY);
81565c45adf0SJesse Butler 	mutex_exit(&pwp->config_lock);
81579aed1621SDavid Hollister }
81589aed1621SDavid Hollister 
81599aed1621SDavid Hollister /* ARGSUSED */
81609aed1621SDavid Hollister static boolean_t
81619aed1621SDavid Hollister pmcs_tgtmap_deactivate_cb(void *tgtmap_priv, char *tgt_addr,
81629aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t tgt_type, void *tgt_priv,
81639aed1621SDavid Hollister     scsi_tgtmap_deact_rsn_t tgt_deact_rsn)
81649aed1621SDavid Hollister {
81659aed1621SDavid Hollister 	pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
81669aed1621SDavid Hollister 	pmcs_phy_t *phyp;
81679aed1621SDavid Hollister 	boolean_t rediscover = B_FALSE;
81689aed1621SDavid Hollister 
81699aed1621SDavid Hollister 	ASSERT(iport);
81709aed1621SDavid Hollister 
81719aed1621SDavid Hollister 	phyp = pmcs_find_phy_by_sas_address(iport->pwp, iport, NULL, tgt_addr);
81729aed1621SDavid Hollister 	if (phyp == NULL) {
81739aed1621SDavid Hollister 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
81749aed1621SDavid Hollister 		    "%s: Couldn't find PHY at %s", __func__, tgt_addr);
81759aed1621SDavid Hollister 		return (rediscover);
81769aed1621SDavid Hollister 	}
81779aed1621SDavid Hollister 	/* phyp is locked */
81789aed1621SDavid Hollister 
81799aed1621SDavid Hollister 	if (!phyp->reenumerate && phyp->configured) {
81809aed1621SDavid Hollister 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, phyp->target,
81819aed1621SDavid Hollister 		    "%s: PHY @ %s is configured... re-enumerate", __func__,
81829aed1621SDavid Hollister 		    tgt_addr);
81839aed1621SDavid Hollister 		phyp->reenumerate = 1;
81849aed1621SDavid Hollister 	}
81859aed1621SDavid Hollister 
81869aed1621SDavid Hollister 	/*
81879aed1621SDavid Hollister 	 * Check to see if reenumerate is set, and if so, if we've reached our
81889aed1621SDavid Hollister 	 * maximum number of retries.
81899aed1621SDavid Hollister 	 */
81909aed1621SDavid Hollister 	if (phyp->reenumerate) {
81919aed1621SDavid Hollister 		if (phyp->enum_attempts == PMCS_MAX_REENUMERATE) {
81929aed1621SDavid Hollister 			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
81939aed1621SDavid Hollister 			    phyp->target,
81949aed1621SDavid Hollister 			    "%s: No more enumeration attempts for %s", __func__,
81959aed1621SDavid Hollister 			    tgt_addr);
81969aed1621SDavid Hollister 		} else {
81979aed1621SDavid Hollister 			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
81989aed1621SDavid Hollister 			    phyp->target, "%s: Re-attempt enumeration for %s",
81999aed1621SDavid Hollister 			    __func__, tgt_addr);
82009aed1621SDavid Hollister 			++phyp->enum_attempts;
82019aed1621SDavid Hollister 			rediscover = B_TRUE;
82029aed1621SDavid Hollister 		}
82039aed1621SDavid Hollister 
82049aed1621SDavid Hollister 		phyp->reenumerate = 0;
82059aed1621SDavid Hollister 	}
82069aed1621SDavid Hollister 
82079aed1621SDavid Hollister 	pmcs_unlock_phy(phyp);
82089aed1621SDavid Hollister 
82099aed1621SDavid Hollister 	mutex_enter(&iport->pwp->config_lock);
82109aed1621SDavid Hollister 	iport->pwp->config_restart_time = ddi_get_lbolt() +
82119aed1621SDavid Hollister 	    drv_usectohz(PMCS_REDISCOVERY_DELAY);
82129aed1621SDavid Hollister 	if (rediscover) {
82139aed1621SDavid Hollister 		iport->pwp->config_restart = B_TRUE;
82149aed1621SDavid Hollister 	} else if (iport->pwp->config_restart == B_TRUE) {
82159aed1621SDavid Hollister 		/*
82169aed1621SDavid Hollister 		 * If we aren't asking for rediscovery because of this PHY,
82179aed1621SDavid Hollister 		 * check to see if we're already asking for it on behalf of
82189aed1621SDavid Hollister 		 * some other PHY.  If so, we'll want to return TRUE, so reset
82199aed1621SDavid Hollister 		 * "rediscover" here.
82209aed1621SDavid Hollister 		 */
82219aed1621SDavid Hollister 		rediscover = B_TRUE;
82229aed1621SDavid Hollister 	}
82239aed1621SDavid Hollister 
82249aed1621SDavid Hollister 	mutex_exit(&iport->pwp->config_lock);
82259aed1621SDavid Hollister 
82269aed1621SDavid Hollister 	return (rediscover);
82279aed1621SDavid Hollister }
82289aed1621SDavid Hollister 
82299aed1621SDavid Hollister void
82309aed1621SDavid Hollister pmcs_status_disposition(pmcs_phy_t *phyp, uint32_t status)
82319aed1621SDavid Hollister {
82329aed1621SDavid Hollister 	ASSERT(phyp);
82339aed1621SDavid Hollister 	ASSERT(!mutex_owned(&phyp->phy_lock));
82349aed1621SDavid Hollister 
82359aed1621SDavid Hollister 	if (phyp == NULL) {
82369aed1621SDavid Hollister 		return;
82379aed1621SDavid Hollister 	}
82389aed1621SDavid Hollister 
82399aed1621SDavid Hollister 	pmcs_lock_phy(phyp);
82409aed1621SDavid Hollister 
82419aed1621SDavid Hollister 	/*
82429aed1621SDavid Hollister 	 * XXX: Do we need to call this function from an SSP_EVENT?
82439aed1621SDavid Hollister 	 */
82449aed1621SDavid Hollister 
82459aed1621SDavid Hollister 	switch (status) {
82469aed1621SDavid Hollister 	case PMCOUT_STATUS_NO_DEVICE:
82479aed1621SDavid Hollister 	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
82489aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERR_BREAK:
82499aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
82509aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
82519aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
82529aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
82539aed1621SDavid Hollister 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
82549aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
82559aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
82569aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
82579aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
82589aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
82599aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
82609aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
82619aed1621SDavid Hollister 	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
82629aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
82639aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
82649aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
82659aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
82669aed1621SDavid Hollister 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
82679aed1621SDavid Hollister 		    "%s: status = 0x%x for " SAS_ADDR_FMT ", reenumerate",
82689aed1621SDavid Hollister 		    __func__, status, SAS_ADDR_PRT(phyp->sas_address));
82699aed1621SDavid Hollister 		phyp->reenumerate = 1;
82709aed1621SDavid Hollister 		break;
82719aed1621SDavid Hollister 
82729aed1621SDavid Hollister 	default:
82739aed1621SDavid Hollister 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
82749aed1621SDavid Hollister 		    "%s: status = 0x%x for " SAS_ADDR_FMT ", no reenumeration",
82759aed1621SDavid Hollister 		    __func__, status, SAS_ADDR_PRT(phyp->sas_address));
82769aed1621SDavid Hollister 		break;
82779aed1621SDavid Hollister 	}
82789aed1621SDavid Hollister 
82799aed1621SDavid Hollister 	pmcs_unlock_phy(phyp);
82809aed1621SDavid Hollister }
82819aed1621SDavid Hollister 
82829aed1621SDavid Hollister /*
82839aed1621SDavid Hollister  * Add the list of PHYs pointed to by phyp to the dead_phys_list
82849aed1621SDavid Hollister  *
82859aed1621SDavid Hollister  * Called with all PHYs in the list locked
82869aed1621SDavid Hollister  */
82879aed1621SDavid Hollister static void
82889aed1621SDavid Hollister pmcs_add_dead_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
82899aed1621SDavid Hollister {
82909aed1621SDavid Hollister 	mutex_enter(&pwp->dead_phylist_lock);
82919aed1621SDavid Hollister 	while (phyp) {
82929aed1621SDavid Hollister 		pmcs_phy_t *nxt = phyp->sibling;
82939aed1621SDavid Hollister 		ASSERT(phyp->dead);
82949aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
82959aed1621SDavid Hollister 		    "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
82969aed1621SDavid Hollister 		    (void *)phyp, phyp->path, phyp->ref_count);
82979aed1621SDavid Hollister 		/*
82989aed1621SDavid Hollister 		 * Put this PHY on the dead PHY list for the watchdog to
82999aed1621SDavid Hollister 		 * clean up after any outstanding work has completed.
83009aed1621SDavid Hollister 		 */
83019aed1621SDavid Hollister 		phyp->dead_next = pwp->dead_phys;
83029aed1621SDavid Hollister 		pwp->dead_phys = phyp;
83039aed1621SDavid Hollister 		pmcs_unlock_phy(phyp);
83049aed1621SDavid Hollister 		phyp = nxt;
83059aed1621SDavid Hollister 	}
83069aed1621SDavid Hollister 	mutex_exit(&pwp->dead_phylist_lock);
83079aed1621SDavid Hollister }
83082ac4abe8SDavid Hollister 
83092ac4abe8SDavid Hollister static void
83102ac4abe8SDavid Hollister pmcs_get_fw_version(pmcs_hw_t *pwp)
83112ac4abe8SDavid Hollister {
83122ac4abe8SDavid Hollister 	uint32_t ila_len, ver_hi, ver_lo;
83132ac4abe8SDavid Hollister 	uint8_t ila_ver_string[9], img_flag;
83142ac4abe8SDavid Hollister 	char uc, *ucp = &uc;
83152ac4abe8SDavid Hollister 	unsigned long ila_ver;
83162ac4abe8SDavid Hollister 	uint64_t ver_hilo;
83172ac4abe8SDavid Hollister 
83182ac4abe8SDavid Hollister 	/* Firmware version is easy. */
83192ac4abe8SDavid Hollister 	pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
83202ac4abe8SDavid Hollister 
83212ac4abe8SDavid Hollister 	/*
83222ac4abe8SDavid Hollister 	 * Get the image size (2nd to last dword)
83232ac4abe8SDavid Hollister 	 * NOTE: The GSM registers are mapped little-endian, but the data
83242ac4abe8SDavid Hollister 	 * on the flash is actually big-endian, so we need to swap these values
83252ac4abe8SDavid Hollister 	 * regardless of which platform we're on.
83262ac4abe8SDavid Hollister 	 */
83272ac4abe8SDavid Hollister 	ila_len = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83282ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + GSM_SM_BLKSZ - (2 << 2)));
83292ac4abe8SDavid Hollister 	if (ila_len > 65535) {
83302ac4abe8SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
83312ac4abe8SDavid Hollister 		    "%s: Invalid ILA image size (0x%x)?", __func__, ila_len);
83322ac4abe8SDavid Hollister 		return;
83332ac4abe8SDavid Hollister 	}
83342ac4abe8SDavid Hollister 
83352ac4abe8SDavid Hollister 	/*
83362ac4abe8SDavid Hollister 	 * The numeric version is at ila_len - PMCS_ILA_VER_OFFSET
83372ac4abe8SDavid Hollister 	 */
83382ac4abe8SDavid Hollister 	ver_hi = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83392ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET));
83402ac4abe8SDavid Hollister 	ver_lo = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83412ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET + 4));
83422ac4abe8SDavid Hollister 	ver_hilo = BE_64(((uint64_t)ver_hi << 32) | ver_lo);
83432ac4abe8SDavid Hollister 	bcopy((const void *)&ver_hilo, &ila_ver_string[0], 8);
83442ac4abe8SDavid Hollister 	ila_ver_string[8] = '\0';
83452ac4abe8SDavid Hollister 
83462ac4abe8SDavid Hollister 	(void) ddi_strtoul((const char *)ila_ver_string, &ucp, 16, &ila_ver);
83472ac4abe8SDavid Hollister 	pwp->ila_ver = (int)(ila_ver & 0xffffffff);
83482ac4abe8SDavid Hollister 
83492ac4abe8SDavid Hollister 	img_flag = (BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83502ac4abe8SDavid Hollister 	    GSM_FLASH_IMG_FLAGS)) & 0xff000000) >> 24;
83512ac4abe8SDavid Hollister 	if (img_flag & PMCS_IMG_FLAG_A) {
83522ac4abe8SDavid Hollister 		pwp->fw_active_img = 1;
83532ac4abe8SDavid Hollister 	} else {
83542ac4abe8SDavid Hollister 		pwp->fw_active_img = 0;
83552ac4abe8SDavid Hollister 	}
83562ac4abe8SDavid Hollister }
8357