xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c (revision 1f81b46471e38fdeb9ab74c25510b2f903f8af12)
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  */
344c06356bSdh static int tgtmap_usec = MICROSEC;
354c06356bSdh 
364c06356bSdh /*
374c06356bSdh  * SAS Topology Configuration
384c06356bSdh  */
394c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
404c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
414c06356bSdh 
4273a3eccdSDavid Hollister static void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
434c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
444c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
454c06356bSdh 
464c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
474c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
484c06356bSdh     pmcs_phy_t *);
494c06356bSdh 
504c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
514c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
524c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
534c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
54601c90f1SSrikanth, Ramana static void pmcs_begin_observations(pmcs_hw_t *);
550b53804eSReed static void pmcs_flush_observations(pmcs_hw_t *);
564c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *);
574c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
584c06356bSdh     pmcs_phy_t *);
594c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
604c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
614c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int);
624c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
634c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
644c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
654c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *);
664c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
674c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *);
689aed1621SDavid Hollister static void pmcs_iport_active(pmcs_iport_t *);
699aed1621SDavid Hollister static void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t,
709aed1621SDavid Hollister     void **);
719aed1621SDavid Hollister static boolean_t pmcs_tgtmap_deactivate_cb(void *, char *,
729aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t);
739aed1621SDavid Hollister static void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *);
742ac4abe8SDavid Hollister static void pmcs_get_fw_version(pmcs_hw_t *);
75*1f81b464SDavid Hollister static int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *);
764c06356bSdh 
774c06356bSdh /*
784c06356bSdh  * Often used strings
794c06356bSdh  */
804c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure";
814c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
826745c559SJesse Butler const char pmcs_timeo[] = "%s: command timed out";
834c06356bSdh 
844c06356bSdh extern const ddi_dma_attr_t pmcs_dattr;
85*1f81b464SDavid Hollister extern kmutex_t pmcs_trace_lock;
864c06356bSdh 
874c06356bSdh /*
884c06356bSdh  * Some Initial setup steps.
894c06356bSdh  */
904c06356bSdh 
914c06356bSdh int
924c06356bSdh pmcs_setup(pmcs_hw_t *pwp)
934c06356bSdh {
944c06356bSdh 	uint32_t barval = pwp->mpibar;
954c06356bSdh 	uint32_t i, scratch, regbar, regoff, barbar, baroff;
964c06356bSdh 	uint32_t new_ioq_depth, ferr = 0;
974c06356bSdh 
984c06356bSdh 	/*
994c06356bSdh 	 * Check current state. If we're not at READY state,
1004c06356bSdh 	 * we can't go further.
1014c06356bSdh 	 */
1024c06356bSdh 	scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
1034c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
104c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
105c3bc407cSdh 		    "%s: AAP Error State (0x%x)",
1064c06356bSdh 		    __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1074c06356bSdh 		    PMCS_MSGU_AAP_ERROR_MASK);
1084c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1094c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1104c06356bSdh 		return (-1);
1114c06356bSdh 	}
1124c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
113c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1144c06356bSdh 		    "%s: AAP unit not ready (state 0x%x)",
1154c06356bSdh 		    __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
1164c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1174c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1184c06356bSdh 		return (-1);
1194c06356bSdh 	}
1204c06356bSdh 
1214c06356bSdh 	/*
1224c06356bSdh 	 * Read the offset from the Message Unit scratchpad 0 register.
1234c06356bSdh 	 * This allows us to read the MPI Configuration table.
1244c06356bSdh 	 *
1254c06356bSdh 	 * Check its signature for validity.
1264c06356bSdh 	 */
1274c06356bSdh 	baroff = barval;
1284c06356bSdh 	barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
1294c06356bSdh 	baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
1304c06356bSdh 
1314c06356bSdh 	regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
1324c06356bSdh 	regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
1334c06356bSdh 	regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
1344c06356bSdh 
1354c06356bSdh 	if (regoff > baroff) {
136c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
137c3bc407cSdh 		    "%s: bad MPI Table Length (register offset=0x%08x, "
138c3bc407cSdh 		    "passed offset=0x%08x)", __func__, regoff, baroff);
1394c06356bSdh 		return (-1);
1404c06356bSdh 	}
1414c06356bSdh 	if (regbar != barbar) {
142c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
143c3bc407cSdh 		    "%s: bad MPI BAR (register BAROFF=0x%08x, "
144c3bc407cSdh 		    "passed BAROFF=0x%08x)", __func__, regbar, barbar);
1454c06356bSdh 		return (-1);
1464c06356bSdh 	}
1474c06356bSdh 	pwp->mpi_offset = regoff;
1484c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
149c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1504c06356bSdh 		    "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
1514c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
1524c06356bSdh 		return (-1);
1534c06356bSdh 	}
1544c06356bSdh 
1554c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
156c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1574c06356bSdh 		    "%s: Bad MPI Configuration Revision 0x%x", __func__,
1584c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
1594c06356bSdh 		return (-1);
1604c06356bSdh 	}
1614c06356bSdh 
1624c06356bSdh 	/*
1634c06356bSdh 	 * Generate offsets for the General System, Inbound Queue Configuration
1644c06356bSdh 	 * and Outbound Queue configuration tables. This way the macros to
1654c06356bSdh 	 * access those tables will work correctly.
1664c06356bSdh 	 */
1674c06356bSdh 	pwp->mpi_gst_offset =
1684c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
1694c06356bSdh 	pwp->mpi_iqc_offset =
1704c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
1714c06356bSdh 	pwp->mpi_oqc_offset =
1724c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
1734c06356bSdh 
1742ac4abe8SDavid Hollister 	pmcs_get_fw_version(pwp);
1754c06356bSdh 
1764c06356bSdh 	pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
1774c06356bSdh 	pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
1784c06356bSdh 
1794c06356bSdh 	pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1804c06356bSdh 	pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1814c06356bSdh 	pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1824c06356bSdh 	if (pwp->max_iq <= PMCS_NIQ) {
183c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
184c3bc407cSdh 		    "%s: not enough Inbound Queues supported "
185c3bc407cSdh 		    "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ);
1864c06356bSdh 		return (-1);
1874c06356bSdh 	}
1884c06356bSdh 	if (pwp->max_oq <= PMCS_NOQ) {
189c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
190c3bc407cSdh 		    "%s: not enough Outbound Queues supported "
191c3bc407cSdh 		    "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ);
1924c06356bSdh 		return (-1);
1934c06356bSdh 	}
1944c06356bSdh 	if (pwp->nphy == 0) {
195c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
196c3bc407cSdh 		    "%s: zero phys reported", __func__);
1974c06356bSdh 		return (-1);
1984c06356bSdh 	}
1994c06356bSdh 	if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
2004c06356bSdh 		pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
2014c06356bSdh 	}
2024c06356bSdh 
2034c06356bSdh 
2044c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
2054c06356bSdh 		PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
2064c06356bSdh 		PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
2074c06356bSdh 	}
2084c06356bSdh 
2094c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
2104c06356bSdh 	    (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
2114c06356bSdh 	    (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
2124c06356bSdh 
2134c06356bSdh 	/*
2144c06356bSdh 	 * Verify that ioq_depth is valid (> 0 and not so high that it
2154c06356bSdh 	 * would cause us to overrun the chip with commands).
2164c06356bSdh 	 */
2174c06356bSdh 	if (pwp->ioq_depth == 0) {
218c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2194c06356bSdh 		    "%s: I/O queue depth set to 0. Setting to %d",
2204c06356bSdh 		    __func__, PMCS_NQENTRY);
2214c06356bSdh 		pwp->ioq_depth = PMCS_NQENTRY;
2224c06356bSdh 	}
2234c06356bSdh 
2244c06356bSdh 	if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
225c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2264c06356bSdh 		    "%s: I/O queue depth set too low (%d). Setting to %d",
2274c06356bSdh 		    __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
2284c06356bSdh 		pwp->ioq_depth = PMCS_MIN_NQENTRY;
2294c06356bSdh 	}
2304c06356bSdh 
2314c06356bSdh 	if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
2324c06356bSdh 		new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
233c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2344c06356bSdh 		    "%s: I/O queue depth set too high (%d). Setting to %d",
2354c06356bSdh 		    __func__, pwp->ioq_depth, new_ioq_depth);
2364c06356bSdh 		pwp->ioq_depth = new_ioq_depth;
2374c06356bSdh 	}
2384c06356bSdh 
2394c06356bSdh 	/*
2404c06356bSdh 	 * Allocate consistent memory for OQs and IQs.
2414c06356bSdh 	 */
2424c06356bSdh 	pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
2434c06356bSdh 	pwp->iqp_dma_attr.dma_attr_align =
2444c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
2454c06356bSdh 
2464c06356bSdh 	/*
2474c06356bSdh 	 * The Rev C chip has the ability to do PIO to or from consistent
2484c06356bSdh 	 * memory anywhere in a 64 bit address space, but the firmware is
2494c06356bSdh 	 * not presently set up to do so.
2504c06356bSdh 	 */
2514c06356bSdh 	pwp->iqp_dma_attr.dma_attr_addr_hi =
2524c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
2534c06356bSdh 
2544c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
2554c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
2564c06356bSdh 		    &pwp->iqp_acchdls[i],
2574c06356bSdh 		    &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2584c06356bSdh 		    (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
259c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2604c06356bSdh 			    "Failed to setup DMA for iqp[%d]", i);
2614c06356bSdh 			return (-1);
2624c06356bSdh 		}
2634c06356bSdh 		bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2644c06356bSdh 	}
2654c06356bSdh 
2664c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
2674c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
2684c06356bSdh 		    &pwp->oqp_acchdls[i],
2694c06356bSdh 		    &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2704c06356bSdh 		    (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
271c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2724c06356bSdh 			    "Failed to setup DMA for oqp[%d]", i);
2734c06356bSdh 			return (-1);
2744c06356bSdh 		}
2754c06356bSdh 		bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2764c06356bSdh 	}
2774c06356bSdh 
2784c06356bSdh 	/*
2794c06356bSdh 	 * Install the IQ and OQ addresses (and null out the rest).
2804c06356bSdh 	 */
2814c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
2824c06356bSdh 		pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
2834c06356bSdh 		if (i < PMCS_NIQ) {
2844c06356bSdh 			if (i != PMCS_IQ_OTHER) {
2854c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2864c06356bSdh 				    pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
2874c06356bSdh 			} else {
2884c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2894c06356bSdh 				    (1 << 30) | pwp->ioq_depth |
2904c06356bSdh 				    (PMCS_QENTRY_SIZE << 16));
2914c06356bSdh 			}
2924c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
2934c06356bSdh 			    DWORD1(pwp->iqaddr[i]));
2944c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
2954c06356bSdh 			    DWORD0(pwp->iqaddr[i]));
2964c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
2974c06356bSdh 			    DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
2984c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
2994c06356bSdh 			    DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
3004c06356bSdh 		} else {
3014c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
3024c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
3034c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
3044c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
3054c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
3064c06356bSdh 		}
3074c06356bSdh 	}
3084c06356bSdh 
3094c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
3104c06356bSdh 		pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
3114c06356bSdh 		if (i < PMCS_NOQ) {
3124c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
3134c06356bSdh 			    (PMCS_QENTRY_SIZE << 16) | OQIEX);
3144c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
3154c06356bSdh 			    DWORD1(pwp->oqaddr[i]));
3164c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
3174c06356bSdh 			    DWORD0(pwp->oqaddr[i]));
3184c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
3194c06356bSdh 			    DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
3204c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
3214c06356bSdh 			    DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
3224c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
3234c06356bSdh 			    pwp->oqvec[i] << 24);
3244c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3254c06356bSdh 		} else {
3264c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
3274c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
3284c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
3294c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
3304c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
3314c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
3324c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3334c06356bSdh 		}
3344c06356bSdh 	}
3354c06356bSdh 
3364c06356bSdh 	/*
3374c06356bSdh 	 * Set up logging, if defined.
3384c06356bSdh 	 */
3394c06356bSdh 	if (pwp->fwlog) {
3404c06356bSdh 		uint64_t logdma = pwp->fwaddr;
3414c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
3424c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
3434c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
3444c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
3454c06356bSdh 		logdma += (PMCS_FWLOG_SIZE >> 1);
3464c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
3474c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
3484c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
3494c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
3504c06356bSdh 	}
3514c06356bSdh 
3524c06356bSdh 	/*
3534c06356bSdh 	 * Interrupt vectors, outbound queues, and odb_auto_clear
3544c06356bSdh 	 *
3554c06356bSdh 	 * MSI/MSI-X:
3564c06356bSdh 	 * If we got 4 interrupt vectors, we'll assign one to each outbound
3574c06356bSdh 	 * queue as well as the fatal interrupt, and auto clear can be set
3584c06356bSdh 	 * for each.
3594c06356bSdh 	 *
3604c06356bSdh 	 * If we only got 2 vectors, one will be used for I/O completions
3614c06356bSdh 	 * and the other for the other two vectors.  In this case, auto_
3624c06356bSdh 	 * clear can only be set for I/Os, which is fine.  The fatal
3634c06356bSdh 	 * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
3644c06356bSdh 	 * is not an interrupt vector.
3654c06356bSdh 	 *
3664c06356bSdh 	 * MSI/MSI-X/INT-X:
3674c06356bSdh 	 * If we only got 1 interrupt vector, auto_clear must be set to 0,
3684c06356bSdh 	 * and again the fatal interrupt will be mapped to the
3694c06356bSdh 	 * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
3704c06356bSdh 	 */
3714c06356bSdh 
3724c06356bSdh 	switch (pwp->int_type) {
3734c06356bSdh 	case PMCS_INT_MSIX:
3744c06356bSdh 	case PMCS_INT_MSI:
3754c06356bSdh 		switch (pwp->intr_cnt) {
3764c06356bSdh 		case 1:
3774c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3784c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3794c06356bSdh 			pwp->odb_auto_clear = 0;
3804c06356bSdh 			break;
3814c06356bSdh 		case 2:
3824c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3834c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3844c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
3854c06356bSdh 			    (1 << PMCS_MSIX_IODONE);
3864c06356bSdh 			break;
3874c06356bSdh 		case 4:
3884c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3894c06356bSdh 			    (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
3904c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
3914c06356bSdh 			    (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
3924c06356bSdh 			    (1 << PMCS_MSIX_EVENTS);
3934c06356bSdh 			break;
3944c06356bSdh 		}
3954c06356bSdh 		break;
3964c06356bSdh 
3974c06356bSdh 	case PMCS_INT_FIXED:
3984c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
3994c06356bSdh 		    PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
4004c06356bSdh 		pwp->odb_auto_clear = 0;
4014c06356bSdh 		break;
4024c06356bSdh 	}
4034c06356bSdh 
404658280b6SDavid Hollister 	/*
405658280b6SDavid Hollister 	 * If the open retry interval is non-zero, set it.
406658280b6SDavid Hollister 	 */
407658280b6SDavid Hollister 	if (pwp->open_retry_interval != 0) {
408658280b6SDavid Hollister 		int phynum;
409658280b6SDavid Hollister 
410658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
411658280b6SDavid Hollister 		    "%s: Setting open retry interval to %d usecs", __func__,
412658280b6SDavid Hollister 		    pwp->open_retry_interval);
413658280b6SDavid Hollister 		for (phynum = 0; phynum < pwp->nphy; phynum ++) {
414658280b6SDavid Hollister 			pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum),
415658280b6SDavid Hollister 			    pwp->open_retry_interval);
416658280b6SDavid Hollister 		}
417658280b6SDavid Hollister 	}
418658280b6SDavid Hollister 
4194c06356bSdh 	/*
4204c06356bSdh 	 * Enable Interrupt Reassertion
4214c06356bSdh 	 * Default Delay 1000us
4224c06356bSdh 	 */
4234c06356bSdh 	ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
4244c06356bSdh 	if ((ferr & PMCS_MPI_IRAE) == 0) {
4254c06356bSdh 		ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
4264c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
4274c06356bSdh 	}
4284c06356bSdh 
4294c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
4304c06356bSdh 	pwp->mpi_table_setup = 1;
4314c06356bSdh 	return (0);
4324c06356bSdh }
4334c06356bSdh 
4344c06356bSdh /*
4354c06356bSdh  * Start the Message Passing protocol with the PMC chip.
4364c06356bSdh  */
4374c06356bSdh int
4384c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp)
4394c06356bSdh {
4404c06356bSdh 	int i;
4414c06356bSdh 
4424c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
4434c06356bSdh 	for (i = 0; i < 1000; i++) {
4444c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4454c06356bSdh 		    PMCS_MSGU_IBDB_MPIINI) == 0) {
4464c06356bSdh 			break;
4474c06356bSdh 		}
4484c06356bSdh 		drv_usecwait(1000);
4494c06356bSdh 	}
4504c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
4514c06356bSdh 		return (-1);
4524c06356bSdh 	}
4534c06356bSdh 	drv_usecwait(500000);
4544c06356bSdh 
4554c06356bSdh 	/*
4564c06356bSdh 	 * Check to make sure we got to INIT state.
4574c06356bSdh 	 */
4584c06356bSdh 	if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
4594c06356bSdh 	    PMCS_MPI_STATE_INIT) {
460c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
461c3bc407cSdh 		    "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
4624c06356bSdh 		    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
4634c06356bSdh 		    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
4644c06356bSdh 		return (-1);
4654c06356bSdh 	}
4664c06356bSdh 	return (0);
4674c06356bSdh }
4684c06356bSdh 
4694c06356bSdh /*
4704c06356bSdh  * Stop the Message Passing protocol with the PMC chip.
4714c06356bSdh  */
4724c06356bSdh int
4734c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp)
4744c06356bSdh {
4754c06356bSdh 	int i;
4764c06356bSdh 
4774c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
4784c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
4794c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
4804c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
4814c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
4824c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
4834c06356bSdh 	}
4844c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
4854c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
4864c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
4874c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
4884c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
4894c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
4904c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
4914c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
4924c06356bSdh 	}
4934c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
4944c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
4954c06356bSdh 	for (i = 0; i < 2000; i++) {
4964c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4974c06356bSdh 		    PMCS_MSGU_IBDB_MPICTU) == 0) {
4984c06356bSdh 			break;
4994c06356bSdh 		}
5004c06356bSdh 		drv_usecwait(1000);
5014c06356bSdh 	}
5024c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
503c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
504c3bc407cSdh 		    "%s: MPI stop failed", __func__);
5054c06356bSdh 		return (-1);
5064c06356bSdh 	}
5074c06356bSdh 	return (0);
5084c06356bSdh }
5094c06356bSdh 
5104c06356bSdh /*
5114c06356bSdh  * Do a sequence of ECHO messages to test for MPI functionality,
5124c06356bSdh  * all inbound and outbound queue functionality and interrupts.
5134c06356bSdh  */
5144c06356bSdh int
5154c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp)
5164c06356bSdh {
5174c06356bSdh 	echo_test_t fred;
5184c06356bSdh 	struct pmcwork *pwrk;
5194c06356bSdh 	uint32_t *msg, count;
5204c06356bSdh 	int iqe = 0, iqo = 0, result, rval = 0;
5214c06356bSdh 	int iterations;
5224c06356bSdh 	hrtime_t echo_start, echo_end, echo_total;
5234c06356bSdh 
5244c06356bSdh 	ASSERT(pwp->max_cmd > 0);
5254c06356bSdh 
5264c06356bSdh 	/*
5274c06356bSdh 	 * We want iterations to be max_cmd * 3 to ensure that we run the
5284c06356bSdh 	 * echo test enough times to iterate through every inbound queue
5294c06356bSdh 	 * at least twice.
5304c06356bSdh 	 */
5314c06356bSdh 	iterations = pwp->max_cmd * 3;
5324c06356bSdh 
5334c06356bSdh 	echo_total = 0;
5344c06356bSdh 	count = 0;
5354c06356bSdh 
5364c06356bSdh 	while (count < iterations) {
5374c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
5384c06356bSdh 		if (pwrk == NULL) {
539c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
540c3bc407cSdh 			    pmcs_nowrk, __func__);
5414c06356bSdh 			rval = -1;
5424c06356bSdh 			break;
5434c06356bSdh 		}
5444c06356bSdh 
5454c06356bSdh 		mutex_enter(&pwp->iqp_lock[iqe]);
5464c06356bSdh 		msg = GET_IQ_ENTRY(pwp, iqe);
5474c06356bSdh 		if (msg == NULL) {
5484c06356bSdh 			mutex_exit(&pwp->iqp_lock[iqe]);
5494c06356bSdh 			pmcs_pwork(pwp, pwrk);
550c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
551c3bc407cSdh 			    pmcs_nomsg, __func__);
5524c06356bSdh 			rval = -1;
5534c06356bSdh 			break;
5544c06356bSdh 		}
5554c06356bSdh 
5564c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
5574c06356bSdh 
5584c06356bSdh 		if (iqe == PMCS_IQ_OTHER) {
5594c06356bSdh 			/* This is on the high priority queue */
5604c06356bSdh 			msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
5614c06356bSdh 		} else {
5624c06356bSdh 			msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
5634c06356bSdh 		}
5644c06356bSdh 		msg[1] = LE_32(pwrk->htag);
5654c06356bSdh 		fred.signature = 0xdeadbeef;
5664c06356bSdh 		fred.count = count;
5674c06356bSdh 		fred.ptr = &count;
5684c06356bSdh 		(void) memcpy(&msg[2], &fred, sizeof (fred));
5694c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5704c06356bSdh 
5714c06356bSdh 		INC_IQ_ENTRY(pwp, iqe);
5724c06356bSdh 
5734c06356bSdh 		echo_start = gethrtime();
5744c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__start,
5754c06356bSdh 		    hrtime_t, echo_start, uint32_t, pwrk->htag);
5764c06356bSdh 
5774c06356bSdh 		if (++iqe == PMCS_NIQ) {
5784c06356bSdh 			iqe = 0;
5794c06356bSdh 		}
5804c06356bSdh 		if (++iqo == PMCS_NOQ) {
5814c06356bSdh 			iqo = 0;
5824c06356bSdh 		}
5834c06356bSdh 
5844c06356bSdh 		WAIT_FOR(pwrk, 250, result);
5854c06356bSdh 
5864c06356bSdh 		echo_end = gethrtime();
5874c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__end,
5884c06356bSdh 		    hrtime_t, echo_end, int, result);
5894c06356bSdh 
5904c06356bSdh 		echo_total += (echo_end - echo_start);
5914c06356bSdh 
5924c06356bSdh 		pmcs_pwork(pwp, pwrk);
5934c06356bSdh 		if (result) {
594c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5954c06356bSdh 			    "%s: command timed out on echo test #%d",
5964c06356bSdh 			    __func__, count);
5974c06356bSdh 			rval = -1;
5984c06356bSdh 			break;
5994c06356bSdh 		}
6004c06356bSdh 	}
6014c06356bSdh 
6024c06356bSdh 	/*
6034c06356bSdh 	 * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
6044c06356bSdh 	 * remove the overhead of things like the delay in getting signaled
6054c06356bSdh 	 * for completion.
6064c06356bSdh 	 */
6074c06356bSdh 	if (echo_total != 0) {
6084c06356bSdh 		pwp->io_intr_coal.intr_latency =
6094c06356bSdh 		    (echo_total / iterations) / 2;
6104c06356bSdh 		pwp->io_intr_coal.intr_threshold =
6114c06356bSdh 		    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
6124c06356bSdh 		    pwp->io_intr_coal.intr_latency);
6134c06356bSdh 	}
6144c06356bSdh 
6154c06356bSdh 	return (rval);
6164c06356bSdh }
6174c06356bSdh 
6184c06356bSdh /*
6194c06356bSdh  * Start the (real) phys
6204c06356bSdh  */
6214c06356bSdh int
6224c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
6234c06356bSdh {
6244c06356bSdh 	int result;
6254c06356bSdh 	uint32_t *msg;
6264c06356bSdh 	struct pmcwork *pwrk;
6274c06356bSdh 	pmcs_phy_t *pptr;
6284c06356bSdh 	sas_identify_af_t sap;
6294c06356bSdh 
6304c06356bSdh 	mutex_enter(&pwp->lock);
6314c06356bSdh 	pptr = pwp->root_phys + phynum;
6324c06356bSdh 	if (pptr == NULL) {
6334c06356bSdh 		mutex_exit(&pwp->lock);
634c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
635c3bc407cSdh 		    "%s: cannot find port %d", __func__, phynum);
6364c06356bSdh 		return (0);
6374c06356bSdh 	}
6384c06356bSdh 
6394c06356bSdh 	pmcs_lock_phy(pptr);
6404c06356bSdh 	mutex_exit(&pwp->lock);
6414c06356bSdh 
6424c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
6434c06356bSdh 	if (pwrk == NULL) {
6444c06356bSdh 		pmcs_unlock_phy(pptr);
645c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
6464c06356bSdh 		return (-1);
6474c06356bSdh 	}
6484c06356bSdh 
6494c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6504c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6514c06356bSdh 
6524c06356bSdh 	if (msg == NULL) {
6534c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6544c06356bSdh 		pmcs_unlock_phy(pptr);
6554c06356bSdh 		pmcs_pwork(pwp, pwrk);
656c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
6574c06356bSdh 		return (-1);
6584c06356bSdh 	}
6594c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
6604c06356bSdh 	msg[1] = LE_32(pwrk->htag);
6614c06356bSdh 	msg[2] = LE_32(linkmode | speed | phynum);
6624c06356bSdh 	bzero(&sap, sizeof (sap));
6634c06356bSdh 	sap.device_type = SAS_IF_DTYPE_ENDPOINT;
6644c06356bSdh 	sap.ssp_ini_port = 1;
6654c06356bSdh 
6664c06356bSdh 	if (pwp->separate_ports) {
6674c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
6684c06356bSdh 	} else {
6694c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
6704c06356bSdh 	}
6714c06356bSdh 
6724c06356bSdh 	ASSERT(phynum < SAS2_PHYNUM_MAX);
6734c06356bSdh 	sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
6744c06356bSdh 	(void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
6754c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6764c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6774c06356bSdh 
6784c06356bSdh 	pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
6794c06356bSdh 	pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
6804c06356bSdh 	pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
6814c06356bSdh 	pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
6824c06356bSdh 
6834c06356bSdh 	pmcs_unlock_phy(pptr);
6844c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
6854c06356bSdh 	pmcs_pwork(pwp, pwrk);
6864c06356bSdh 
6874c06356bSdh 	if (result) {
6886745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
6894c06356bSdh 	} else {
6904c06356bSdh 		mutex_enter(&pwp->lock);
6914c06356bSdh 		pwp->phys_started |= (1 << phynum);
6924c06356bSdh 		mutex_exit(&pwp->lock);
6934c06356bSdh 	}
6944c06356bSdh 
6954c06356bSdh 	return (0);
6964c06356bSdh }
6974c06356bSdh 
6984c06356bSdh int
6994c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp)
7004c06356bSdh {
701*1f81b464SDavid Hollister 	int i, rval;
7024c06356bSdh 
7034c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
7044c06356bSdh 		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
7054c06356bSdh 			if (pmcs_start_phy(pwp, i,
7064c06356bSdh 			    (pwp->phymode << PHY_MODE_SHIFT),
7074c06356bSdh 			    pwp->physpeed << PHY_LINK_SHIFT)) {
7084c06356bSdh 				return (-1);
7094c06356bSdh 			}
7104c06356bSdh 			if (pmcs_clear_diag_counters(pwp, i)) {
711c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
712c3bc407cSdh 				    "%s: failed to reset counters on PHY (%d)",
713c3bc407cSdh 				    __func__, i);
7144c06356bSdh 			}
7154c06356bSdh 		}
7164c06356bSdh 	}
717*1f81b464SDavid Hollister 
718*1f81b464SDavid Hollister 	rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp);
719*1f81b464SDavid Hollister 	if (rval) {
720*1f81b464SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
721*1f81b464SDavid Hollister 		    "%s: Failed to obtain firmware timestamp", __func__);
722*1f81b464SDavid Hollister 	} else {
723*1f81b464SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
724*1f81b464SDavid Hollister 		    "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp);
725*1f81b464SDavid Hollister 	}
726*1f81b464SDavid Hollister 
7274c06356bSdh 	return (0);
7284c06356bSdh }
7294c06356bSdh 
7304c06356bSdh /*
7314c06356bSdh  * Called with PHY locked
7324c06356bSdh  */
7334c06356bSdh int
7344c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
7354c06356bSdh {
7364c06356bSdh 	uint32_t *msg;
7374c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
7384c06356bSdh 	const char *mbar;
7394c06356bSdh 	uint32_t amt;
7404c06356bSdh 	uint32_t pdevid;
7414c06356bSdh 	uint32_t stsoff;
7424c06356bSdh 	uint32_t status;
7434c06356bSdh 	int result, level, phynum;
7444c06356bSdh 	struct pmcwork *pwrk;
7454c06356bSdh 	uint32_t htag;
7464c06356bSdh 
7474c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
7484c06356bSdh 
7494c06356bSdh 	bzero(iomb, PMCS_QENTRY_SIZE);
7504c06356bSdh 	phynum = pptr->phynum;
7514c06356bSdh 	level = pptr->level;
7524c06356bSdh 	if (level > 0) {
7534c06356bSdh 		pdevid = pptr->parent->device_id;
754601c90f1SSrikanth, Ramana 	} else if ((level == 0) && (pptr->dtype == EXPANDER)) {
755601c90f1SSrikanth, Ramana 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
756601c90f1SSrikanth, Ramana 		    "%s: Not resetting HBA PHY @ %s", __func__, pptr->path);
757601c90f1SSrikanth, Ramana 		return (0);
7584c06356bSdh 	}
7594c06356bSdh 
7609aed1621SDavid Hollister 	if (!pptr->iport || !pptr->valid_device_id) {
7619aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
7629aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
7639aed1621SDavid Hollister 		return (0);
7649aed1621SDavid Hollister 	}
7659aed1621SDavid Hollister 
7664c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
7674c06356bSdh 
7684c06356bSdh 	if (pwrk == NULL) {
769c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
7704c06356bSdh 		return (ENOMEM);
7714c06356bSdh 	}
7724c06356bSdh 
7734c06356bSdh 	pwrk->arg = iomb;
7744c06356bSdh 
7754c06356bSdh 	/*
7764c06356bSdh 	 * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
7774c06356bSdh 	 * function to do either a link reset or hard reset.  If level == 0,
7784c06356bSdh 	 * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
7794c06356bSdh 	 * root (local) PHY
7804c06356bSdh 	 */
7814c06356bSdh 	if (level) {
7824c06356bSdh 		stsoff = 2;
7834c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
7844c06356bSdh 		    PMCIN_SMP_REQUEST));
7854c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
7864c06356bSdh 		iomb[2] = LE_32(pdevid);
7874c06356bSdh 		iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
7884c06356bSdh 		/*
7894c06356bSdh 		 * Send SMP PHY CONTROL/HARD or LINK RESET
7904c06356bSdh 		 */
7914c06356bSdh 		iomb[4] = BE_32(0x40910000);
7924c06356bSdh 		iomb[5] = 0;
7934c06356bSdh 
7944c06356bSdh 		if (type == PMCS_PHYOP_HARD_RESET) {
7954c06356bSdh 			mbar = "SMP PHY CONTROL/HARD RESET";
7964c06356bSdh 			iomb[6] = BE_32((phynum << 24) |
7974c06356bSdh 			    (PMCS_PHYOP_HARD_RESET << 16));
7984c06356bSdh 		} else {
7994c06356bSdh 			mbar = "SMP PHY CONTROL/LINK RESET";
8004c06356bSdh 			iomb[6] = BE_32((phynum << 24) |
8014c06356bSdh 			    (PMCS_PHYOP_LINK_RESET << 16));
8024c06356bSdh 		}
803c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8044c06356bSdh 		    "%s: sending %s to %s for phy 0x%x",
8054c06356bSdh 		    __func__, mbar, pptr->parent->path, pptr->phynum);
8064c06356bSdh 		amt = 7;
8074c06356bSdh 	} else {
8084c06356bSdh 		/*
8094c06356bSdh 		 * Unlike most other Outbound messages, status for
8104c06356bSdh 		 * a local phy operation is in DWORD 3.
8114c06356bSdh 		 */
8124c06356bSdh 		stsoff = 3;
8134c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8144c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
8154c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
8164c06356bSdh 		if (type == PMCS_PHYOP_LINK_RESET) {
8174c06356bSdh 			mbar = "LOCAL PHY LINK RESET";
8184c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
8194c06356bSdh 		} else {
8204c06356bSdh 			mbar = "LOCAL PHY HARD RESET";
8214c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
8224c06356bSdh 		}
823c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8244c06356bSdh 		    "%s: sending %s to %s", __func__, mbar, pptr->path);
8254c06356bSdh 		amt = 3;
8264c06356bSdh 	}
8274c06356bSdh 
8284c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8294c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8304c06356bSdh 	if (msg == NULL) {
8314c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8324c06356bSdh 		pmcs_pwork(pwp, pwrk);
833c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8344c06356bSdh 		return (ENOMEM);
8354c06356bSdh 	}
8364c06356bSdh 	COPY_MESSAGE(msg, iomb, amt);
8374c06356bSdh 	htag = pwrk->htag;
8386745c559SJesse Butler 
8396745c559SJesse Butler 	/* SMP serialization */
8406745c559SJesse Butler 	pmcs_smp_acquire(pptr->iport);
8416745c559SJesse Butler 
8424c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
8434c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8444c06356bSdh 
8454c06356bSdh 	pmcs_unlock_phy(pptr);
8464c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
8474c06356bSdh 	pmcs_pwork(pwp, pwrk);
848601c90f1SSrikanth, Ramana 	/* Release SMP lock before reacquiring PHY lock */
8496745c559SJesse Butler 	pmcs_smp_release(pptr->iport);
850601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
8516745c559SJesse Butler 
8524c06356bSdh 	if (result) {
8536745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8544c06356bSdh 
8554c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
856c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8574c06356bSdh 			    "%s: Unable to issue SMP abort for htag 0x%08x",
8584c06356bSdh 			    __func__, htag);
8594c06356bSdh 		} else {
860c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8614c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
8624c06356bSdh 			    __func__, htag);
8634c06356bSdh 		}
8644c06356bSdh 		return (EIO);
8654c06356bSdh 	}
8664c06356bSdh 	status = LE_32(iomb[stsoff]);
8674c06356bSdh 
8684c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
8694c06356bSdh 		char buf[32];
8704c06356bSdh 		const char *es =  pmcs_status_str(status);
8714c06356bSdh 		if (es == NULL) {
8724c06356bSdh 			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
8734c06356bSdh 			    status);
8744c06356bSdh 			es = buf;
8754c06356bSdh 		}
876c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8774c06356bSdh 		    "%s: %s action returned %s for %s", __func__, mbar, es,
8784c06356bSdh 		    pptr->path);
879601c90f1SSrikanth, Ramana 		return (status);
8804c06356bSdh 	}
8814c06356bSdh 
8824c06356bSdh 	return (0);
8834c06356bSdh }
8844c06356bSdh 
8854c06356bSdh /*
8864c06356bSdh  * Stop the (real) phys.  No PHY or softstate locks are required as this only
8874c06356bSdh  * happens during detach.
8884c06356bSdh  */
8894c06356bSdh void
8904c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
8914c06356bSdh {
8924c06356bSdh 	int result;
8934c06356bSdh 	pmcs_phy_t *pptr;
8944c06356bSdh 	uint32_t *msg;
8954c06356bSdh 	struct pmcwork *pwrk;
8964c06356bSdh 
8974c06356bSdh 	pptr =  pwp->root_phys + phynum;
8984c06356bSdh 	if (pptr == NULL) {
899c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
9004c06356bSdh 		    "%s: unable to find port %d", __func__, phynum);
9014c06356bSdh 		return;
9024c06356bSdh 	}
9034c06356bSdh 
9044c06356bSdh 	if (pwp->phys_started & (1 << phynum)) {
9054c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
9064c06356bSdh 
9074c06356bSdh 		if (pwrk == NULL) {
908c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
909c3bc407cSdh 			    pmcs_nowrk, __func__);
9104c06356bSdh 			return;
9114c06356bSdh 		}
9124c06356bSdh 
9134c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9144c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9154c06356bSdh 
9164c06356bSdh 		if (msg == NULL) {
9174c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9184c06356bSdh 			pmcs_pwork(pwp, pwrk);
919c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
920c3bc407cSdh 			    pmcs_nomsg, __func__);
9214c06356bSdh 			return;
9224c06356bSdh 		}
9234c06356bSdh 
9244c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
9254c06356bSdh 		msg[1] = LE_32(pwrk->htag);
9264c06356bSdh 		msg[2] = LE_32(phynum);
9274c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
9284c06356bSdh 		/*
9294c06356bSdh 		 * Make this unconfigured now.
9304c06356bSdh 		 */
9314c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9324c06356bSdh 		WAIT_FOR(pwrk, 1000, result);
9334c06356bSdh 
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 
9994c06356bSdh 	pmcs_pwork(pwp, pwrk);
10004c06356bSdh 
10014c06356bSdh 	if (result) {
10024c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
10034c06356bSdh 		return (DDI_FAILURE);
10044c06356bSdh 	}
10054c06356bSdh 
10064c06356bSdh 	status = LE_32(msg[3]);
10074c06356bSdh 
10084c06356bSdh 	/* Return for counter reset */
10094c06356bSdh 	if (cmd == PMCS_ERR_CNT_RESET)
10104c06356bSdh 		return (status);
10114c06356bSdh 
10124c06356bSdh 	/* Return for counter value */
10134c06356bSdh 	if (status) {
1014c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1015c3bc407cSdh 		    "%s: failed, status (0x%x)", __func__, status);
10164c06356bSdh 		return (DDI_FAILURE);
10174c06356bSdh 	}
10184c06356bSdh 	return (LE_32(msg[4]));
10194c06356bSdh }
10204c06356bSdh 
10214c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */
10224c06356bSdh int
10234c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
10244c06356bSdh {
10254c06356bSdh 	return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
10264c06356bSdh }
10274c06356bSdh 
10284c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */
10294c06356bSdh int
10304c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
10314c06356bSdh {
10324c06356bSdh 	uint32_t	cmd = PMCS_ERR_CNT_RESET;
10334c06356bSdh 	uint32_t	cmd_desc;
10344c06356bSdh 
10354c06356bSdh 	cmd_desc = PMCS_INVALID_DWORD_CNT;
10364c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10374c06356bSdh 		return (DDI_FAILURE);
10384c06356bSdh 
10394c06356bSdh 	cmd_desc = PMCS_DISPARITY_ERR_CNT;
10404c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10414c06356bSdh 		return (DDI_FAILURE);
10424c06356bSdh 
10434c06356bSdh 	cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
10444c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10454c06356bSdh 		return (DDI_FAILURE);
10464c06356bSdh 
10474c06356bSdh 	cmd_desc = PMCS_RESET_FAILED_CNT;
10484c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
10494c06356bSdh 		return (DDI_FAILURE);
10504c06356bSdh 
10514c06356bSdh 	return (DDI_SUCCESS);
10524c06356bSdh }
10534c06356bSdh 
10544c06356bSdh /*
10554c06356bSdh  * Get firmware timestamp
10564c06356bSdh  */
1057*1f81b464SDavid Hollister static int
1058*1f81b464SDavid Hollister pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *fw_ts, hrtime_t *sys_hr_ts)
10594c06356bSdh {
10604c06356bSdh 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
10614c06356bSdh 	int result;
10624c06356bSdh 	struct pmcwork *pwrk;
10634c06356bSdh 
10644c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
10654c06356bSdh 	if (pwrk == NULL) {
1066c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
10674c06356bSdh 		return (-1);
10684c06356bSdh 	}
10694c06356bSdh 	pwrk->arg = msg;
10704c06356bSdh 	htag = pwrk->htag;
10714c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
10724c06356bSdh 	msg[1] = LE_32(pwrk->htag);
10734c06356bSdh 
10744c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10754c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10764c06356bSdh 	if (ptr == NULL) {
10774c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10784c06356bSdh 		pmcs_pwork(pwp, pwrk);
1079c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
10804c06356bSdh 		return (-1);
10814c06356bSdh 	}
10824c06356bSdh 	COPY_MESSAGE(ptr, msg, 2);
10834c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
10844c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10854c06356bSdh 
10864c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
10874c06356bSdh 
10884c06356bSdh 	pmcs_pwork(pwp, pwrk);
10894c06356bSdh 
10904c06356bSdh 	if (result) {
10914c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
10924c06356bSdh 		return (-1);
10934c06356bSdh 	}
1094*1f81b464SDavid Hollister 
1095*1f81b464SDavid Hollister 	mutex_enter(&pmcs_trace_lock);
1096*1f81b464SDavid Hollister 	*sys_hr_ts = gethrtime();
1097*1f81b464SDavid Hollister 	gethrestime(&pwp->sys_timestamp);
1098*1f81b464SDavid Hollister 	*fw_ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
1099*1f81b464SDavid Hollister 	mutex_exit(&pmcs_trace_lock);
11004c06356bSdh 	return (0);
11014c06356bSdh }
11024c06356bSdh 
11034c06356bSdh /*
11044c06356bSdh  * Dump all pertinent registers
11054c06356bSdh  */
11064c06356bSdh 
11074c06356bSdh void
11084c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp)
11094c06356bSdh {
11104c06356bSdh 	int i;
11114c06356bSdh 	uint32_t val;
11124c06356bSdh 
1113c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start",
11144c06356bSdh 	    ddi_get_instance(pwp->dip));
1115c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11164c06356bSdh 	    "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
11174c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
11184c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
11194c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
1120c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x",
11214c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
1122c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x",
11234c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
1124c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x",
11254c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
1126c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x",
11274c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
11284c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
1129c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u",
11304c06356bSdh 		    i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
11314c06356bSdh 	}
11324c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
1133c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u",
11344c06356bSdh 		    i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
11354c06356bSdh 	}
11364c06356bSdh 	val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
1137c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11384c06356bSdh 	    "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
11394c06356bSdh 	    val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
11404c06356bSdh 	    PMCS_HMI_ERR(val));
1141c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x",
11424c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
1143c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x",
11444c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
1145c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x",
11464c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
1147c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x",
11484c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
11494c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
11504c06356bSdh 		uint32_t rerrf, pinfo, started = 0, link = 0;
11514c06356bSdh 		pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
11524c06356bSdh 		if (pinfo & 1) {
11534c06356bSdh 			started = 1;
11544c06356bSdh 			link = pinfo & 2;
11554c06356bSdh 		}
11564c06356bSdh 		rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
1157c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
11584c06356bSdh 		    "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
11594c06356bSdh 		    i, started, link, rerrf);
11604c06356bSdh 	}
1161c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end",
11624c06356bSdh 	    ddi_get_instance(pwp->dip));
11634c06356bSdh }
11644c06356bSdh 
11654c06356bSdh /*
11664c06356bSdh  * Handle SATA Abort and other error processing
11674c06356bSdh  */
11684c06356bSdh int
11694c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp)
11704c06356bSdh {
11714c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
1172601c90f1SSrikanth, Ramana 	pmcs_xscsi_t *tgt;
11734c06356bSdh 	int r, level = 0;
11744c06356bSdh 
1175c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__);
11764c06356bSdh 
11774c06356bSdh 	mutex_enter(&pwp->lock);
11784c06356bSdh 	pptr = pwp->root_phys;
11794c06356bSdh 	mutex_exit(&pwp->lock);
11804c06356bSdh 
11814c06356bSdh 	while (pptr) {
11824c06356bSdh 		/*
11834c06356bSdh 		 * XXX: Need to make sure this doesn't happen
11844c06356bSdh 		 * XXX: when non-NCQ commands are running.
11854c06356bSdh 		 */
11864c06356bSdh 		pmcs_lock_phy(pptr);
11874c06356bSdh 		if (pptr->need_rl_ext) {
11884c06356bSdh 			ASSERT(pptr->dtype == SATA);
11894c06356bSdh 			if (pmcs_acquire_scratch(pwp, B_FALSE)) {
11904c06356bSdh 				goto next_phy;
11914c06356bSdh 			}
11924c06356bSdh 			r = pmcs_sata_abort_ncq(pwp, pptr);
11934c06356bSdh 			pmcs_release_scratch(pwp);
11944c06356bSdh 			if (r == ENOMEM) {
11954c06356bSdh 				goto next_phy;
11964c06356bSdh 			}
11974c06356bSdh 			if (r) {
11984c06356bSdh 				r = pmcs_reset_phy(pwp, pptr,
11994c06356bSdh 				    PMCS_PHYOP_LINK_RESET);
12004c06356bSdh 				if (r == ENOMEM) {
12014c06356bSdh 					goto next_phy;
12024c06356bSdh 				}
12034c06356bSdh 				/* what if other failures happened? */
12044c06356bSdh 				pptr->abort_pending = 1;
12054c06356bSdh 				pptr->abort_sent = 0;
12064c06356bSdh 			}
12074c06356bSdh 		}
12084c06356bSdh 		if (pptr->abort_pending == 0 || pptr->abort_sent) {
12094c06356bSdh 			goto next_phy;
12104c06356bSdh 		}
12114c06356bSdh 		pptr->abort_pending = 0;
12124c06356bSdh 		if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
12134c06356bSdh 			pptr->abort_pending = 1;
12144c06356bSdh 			goto next_phy;
12154c06356bSdh 		}
12164c06356bSdh 		pptr->abort_sent = 1;
12174c06356bSdh 
1218601c90f1SSrikanth, Ramana 		/*
1219601c90f1SSrikanth, Ramana 		 * If the iport is no longer active, flush the queues
1220601c90f1SSrikanth, Ramana 		 */
1221601c90f1SSrikanth, Ramana 		if ((pptr->iport == NULL) ||
1222601c90f1SSrikanth, Ramana 		    (pptr->iport->ua_state != UA_ACTIVE)) {
1223601c90f1SSrikanth, Ramana 			tgt = pptr->target;
1224601c90f1SSrikanth, Ramana 			if (tgt) {
1225601c90f1SSrikanth, Ramana 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
1226601c90f1SSrikanth, Ramana 				    "%s: Clearing target 0x%p, inactive iport",
1227601c90f1SSrikanth, Ramana 				    __func__, (void *) tgt);
1228601c90f1SSrikanth, Ramana 				mutex_enter(&tgt->statlock);
1229601c90f1SSrikanth, Ramana 				pmcs_clear_xp(pwp, tgt);
1230601c90f1SSrikanth, Ramana 				mutex_exit(&tgt->statlock);
1231601c90f1SSrikanth, Ramana 			}
1232601c90f1SSrikanth, Ramana 		}
1233601c90f1SSrikanth, Ramana 
12344c06356bSdh next_phy:
12354c06356bSdh 		if (pptr->children) {
12364c06356bSdh 			pnext = pptr->children;
12374c06356bSdh 			pnext_uplevel[level++] = pptr->sibling;
12384c06356bSdh 		} else {
12394c06356bSdh 			pnext = pptr->sibling;
12404c06356bSdh 			while ((pnext == NULL) && (level > 0)) {
12414c06356bSdh 				pnext = pnext_uplevel[--level];
12424c06356bSdh 			}
12434c06356bSdh 		}
12444c06356bSdh 
12454c06356bSdh 		pmcs_unlock_phy(pptr);
12464c06356bSdh 		pptr = pnext;
12474c06356bSdh 	}
12484c06356bSdh 
12494c06356bSdh 	return (0);
12504c06356bSdh }
12514c06356bSdh 
12524c06356bSdh /*
12534c06356bSdh  * Register a device (get a device handle for it).
12544c06356bSdh  * Called with PHY lock held.
12554c06356bSdh  */
12564c06356bSdh int
12574c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
12584c06356bSdh {
12594c06356bSdh 	struct pmcwork *pwrk;
12604c06356bSdh 	int result = 0;
12614c06356bSdh 	uint32_t *msg;
12624c06356bSdh 	uint32_t tmp, status;
12634c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
12644c06356bSdh 
12654c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
12664c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
12674c06356bSdh 
12684c06356bSdh 	if (msg == NULL ||
12694c06356bSdh 	    (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
12704c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
12714c06356bSdh 		result = ENOMEM;
12724c06356bSdh 		goto out;
12734c06356bSdh 	}
12744c06356bSdh 
12754c06356bSdh 	pwrk->arg = iomb;
12764c06356bSdh 	pwrk->dtype = pptr->dtype;
12774c06356bSdh 
12784c06356bSdh 	msg[1] = LE_32(pwrk->htag);
12794c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
12804c06356bSdh 	tmp = PMCS_DEVREG_TLR |
12814c06356bSdh 	    (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
12824c06356bSdh 	if (IS_ROOT_PHY(pptr)) {
12834c06356bSdh 		msg[2] = LE_32(pptr->portid |
12844c06356bSdh 		    (pptr->phynum << PMCS_PHYID_SHIFT));
12854c06356bSdh 	} else {
12864c06356bSdh 		msg[2] = LE_32(pptr->portid);
12874c06356bSdh 	}
12884c06356bSdh 	if (pptr->dtype == SATA) {
12894c06356bSdh 		if (IS_ROOT_PHY(pptr)) {
12904c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
12914c06356bSdh 		} else {
12924c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA;
12934c06356bSdh 		}
12944c06356bSdh 	} else {
12954c06356bSdh 		tmp |= PMCS_DEVREG_TYPE_SAS;
12964c06356bSdh 	}
12974c06356bSdh 	msg[3] = LE_32(tmp);
12984c06356bSdh 	msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
12994c06356bSdh 	(void) memcpy(&msg[5], pptr->sas_address, 8);
13004c06356bSdh 
13014c06356bSdh 	CLEAN_MESSAGE(msg, 7);
13024c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
13034c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13044c06356bSdh 
13054c06356bSdh 	pmcs_unlock_phy(pptr);
13064c06356bSdh 	WAIT_FOR(pwrk, 250, result);
13074c06356bSdh 	pmcs_lock_phy(pptr);
13084c06356bSdh 	pmcs_pwork(pwp, pwrk);
13094c06356bSdh 
13104c06356bSdh 	if (result) {
13116745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
13124c06356bSdh 		result = ETIMEDOUT;
13134c06356bSdh 		goto out;
13144c06356bSdh 	}
13154c06356bSdh 	status = LE_32(iomb[2]);
13164c06356bSdh 	tmp = LE_32(iomb[3]);
13174c06356bSdh 	switch (status) {
13184c06356bSdh 	case PMCS_DEVREG_OK:
13194c06356bSdh 	case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
13204c06356bSdh 	case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
13214c06356bSdh 		if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
13224c06356bSdh 			result = EEXIST;
13234c06356bSdh 			goto out;
13244c06356bSdh 		} else if (status != PMCS_DEVREG_OK) {
13254c06356bSdh 			if (tmp == 0xffffffff) {	/* F/W bug */
1326c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
13274c06356bSdh 				    "%s: phy %s already has bogus devid 0x%x",
13284c06356bSdh 				    __func__, pptr->path, tmp);
13294c06356bSdh 				result = EIO;
13304c06356bSdh 				goto out;
13314c06356bSdh 			} else {
1332c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
13334c06356bSdh 				    "%s: phy %s already has a device id 0x%x",
13344c06356bSdh 				    __func__, pptr->path, tmp);
13354c06356bSdh 			}
13364c06356bSdh 		}
13374c06356bSdh 		break;
13384c06356bSdh 	default:
1339c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1340c3bc407cSdh 		    "%s: status 0x%x when trying to register device %s",
1341c3bc407cSdh 		    __func__, status, pptr->path);
13424c06356bSdh 		result = EIO;
13434c06356bSdh 		goto out;
13444c06356bSdh 	}
13454c06356bSdh 	pptr->device_id = tmp;
13464c06356bSdh 	pptr->valid_device_id = 1;
1347c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT
13484c06356bSdh 	    " registered with device_id 0x%x (portid %d)", pptr->path,
13494c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
13504c06356bSdh out:
13514c06356bSdh 	return (result);
13524c06356bSdh }
13534c06356bSdh 
13544c06356bSdh /*
13554c06356bSdh  * Deregister a device (remove a device handle).
13564c06356bSdh  * Called with PHY locked.
13574c06356bSdh  */
13584c06356bSdh void
13594c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
13604c06356bSdh {
13614c06356bSdh 	struct pmcwork *pwrk;
13624c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
13634c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
13644c06356bSdh 	int result;
13654c06356bSdh 
13664c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
13674c06356bSdh 	if (pwrk == NULL) {
13684c06356bSdh 		return;
13694c06356bSdh 	}
13704c06356bSdh 
13714c06356bSdh 	pwrk->arg = iomb;
13724c06356bSdh 	pwrk->dtype = pptr->dtype;
13734c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
13744c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13754c06356bSdh 	if (ptr == NULL) {
13764c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
13774c06356bSdh 		pmcs_pwork(pwp, pwrk);
13784c06356bSdh 		return;
13794c06356bSdh 	}
13804c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
13814c06356bSdh 	    PMCIN_DEREGISTER_DEVICE_HANDLE));
13824c06356bSdh 	msg[1] = LE_32(pwrk->htag);
13834c06356bSdh 	msg[2] = LE_32(pptr->device_id);
13844c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
13854c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
13864c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
13874c06356bSdh 
13884c06356bSdh 	pmcs_unlock_phy(pptr);
13894c06356bSdh 	WAIT_FOR(pwrk, 250, result);
13904c06356bSdh 	pmcs_pwork(pwp, pwrk);
13914c06356bSdh 	pmcs_lock_phy(pptr);
13924c06356bSdh 
13934c06356bSdh 	if (result) {
13946745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
13954c06356bSdh 		return;
13964c06356bSdh 	}
13974c06356bSdh 	status = LE_32(iomb[2]);
13984c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
1399c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1400c3bc407cSdh 		    "%s: status 0x%x when trying to deregister device %s",
1401c3bc407cSdh 		    __func__, status, pptr->path);
14024c06356bSdh 	} else {
1403c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1404c3bc407cSdh 		    "%s: device %s deregistered", __func__, pptr->path);
14054c06356bSdh 		pptr->valid_device_id = 0;
14064c06356bSdh 		pptr->device_id = PMCS_INVALID_DEVICE_ID;
1407601c90f1SSrikanth, Ramana 		pptr->configured = 0;
1408601c90f1SSrikanth, Ramana 		pptr->deregister_wait = 0;
14094c06356bSdh 	}
14104c06356bSdh }
14114c06356bSdh 
14124c06356bSdh /*
14134c06356bSdh  * Deregister all registered devices.
14144c06356bSdh  */
14154c06356bSdh void
14164c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
14174c06356bSdh {
14184c06356bSdh 	/*
14194c06356bSdh 	 * Start at the maximum level and walk back to level 0.  This only
14204c06356bSdh 	 * gets done during detach after all threads and timers have been
14214c06356bSdh 	 * destroyed, so there's no need to hold the softstate or PHY lock.
14224c06356bSdh 	 */
14234c06356bSdh 	while (phyp) {
14244c06356bSdh 		if (phyp->children) {
14254c06356bSdh 			pmcs_deregister_devices(pwp, phyp->children);
14264c06356bSdh 		}
14274c06356bSdh 		if (phyp->valid_device_id) {
14284c06356bSdh 			pmcs_deregister_device(pwp, phyp);
14294c06356bSdh 		}
14304c06356bSdh 		phyp = phyp->sibling;
14314c06356bSdh 	}
14324c06356bSdh }
14334c06356bSdh 
14344c06356bSdh /*
14354c06356bSdh  * Perform a 'soft' reset on the PMC chip
14364c06356bSdh  */
14374c06356bSdh int
14384c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
14394c06356bSdh {
14404c06356bSdh 	uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
14414c06356bSdh 	pmcs_phy_t *pptr;
14424c06356bSdh 	char *msg = NULL;
14434c06356bSdh 	int i;
14444c06356bSdh 
14454c06356bSdh 	/*
14464c06356bSdh 	 * Disable interrupts
14474c06356bSdh 	 */
14484c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
14494c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
14504c06356bSdh 
1451c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__);
14524c06356bSdh 
14534c06356bSdh 	if (pwp->locks_initted) {
14544c06356bSdh 		mutex_enter(&pwp->lock);
14554c06356bSdh 	}
14564c06356bSdh 	pwp->blocked = 1;
14574c06356bSdh 
14585c45adf0SJesse Butler 	/*
14595c45adf0SJesse Butler 	 * Clear our softstate copies of the MSGU and IOP heartbeats.
14605c45adf0SJesse Butler 	 */
14615c45adf0SJesse Butler 	pwp->last_msgu_tick = pwp->last_iop_tick = 0;
14625c45adf0SJesse Butler 
14634c06356bSdh 	/*
14644c06356bSdh 	 * Step 1
14654c06356bSdh 	 */
14664c06356bSdh 	s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
14674c06356bSdh 	if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
14684c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
14694c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
14704c06356bSdh 		for (i = 0; i < 100; i++) {
14714c06356bSdh 			s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14724c06356bSdh 			    PMCS_MSGU_HOST_SOFT_RESET_READY;
14734c06356bSdh 			if (s2) {
14744c06356bSdh 				break;
14754c06356bSdh 			}
14764c06356bSdh 			drv_usecwait(10000);
14774c06356bSdh 		}
14784c06356bSdh 		s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14794c06356bSdh 		    PMCS_MSGU_HOST_SOFT_RESET_READY;
14804c06356bSdh 		if (s2 == 0) {
1481c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1482c3bc407cSdh 			    "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came "
1483c3bc407cSdh 			    "ready", __func__);
14844c06356bSdh 			pmcs_register_dump(pwp);
14854c06356bSdh 			if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
14864c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
14874c06356bSdh 			    (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
14884c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
14894c06356bSdh 				pwp->state = STATE_DEAD;
14904c06356bSdh 				pwp->blocked = 0;
14914c06356bSdh 				if (pwp->locks_initted) {
14924c06356bSdh 					mutex_exit(&pwp->lock);
14934c06356bSdh 				}
14944c06356bSdh 				return (-1);
14954c06356bSdh 			}
14964c06356bSdh 		}
14974c06356bSdh 	}
14984c06356bSdh 
14994c06356bSdh 	/*
15004c06356bSdh 	 * Step 2
15014c06356bSdh 	 */
15024c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
15034c06356bSdh 	drv_usecwait(10);
15044c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
15054c06356bSdh 	drv_usecwait(10);
15064c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
15074c06356bSdh 	drv_usecwait(10);
15084c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
15094c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
15104c06356bSdh 	drv_usecwait(10);
15114c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
15124c06356bSdh 	drv_usecwait(10);
15134c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
15144c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
15154c06356bSdh 	drv_usecwait(10);
15164c06356bSdh 
15174c06356bSdh 	sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
15184c06356bSdh 	    PMCS_MSGU_AAP_SFR_PROGRESS;
15194c06356bSdh 	sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
1520c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 "
1521c3bc407cSdh 	    "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0),
1522c3bc407cSdh 	    HST_SFT_RESET_SIG);
15234c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
15244c06356bSdh 
15254c06356bSdh 	/*
15264c06356bSdh 	 * Step 3
15274c06356bSdh 	 */
15282ac4abe8SDavid Hollister 	gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
1529c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
15304c06356bSdh 	    gsm & ~PMCS_SOFT_RESET_BITS);
15314c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
15324c06356bSdh 
15334c06356bSdh 	/*
15344c06356bSdh 	 * Step 4
15354c06356bSdh 	 */
15362ac4abe8SDavid Hollister 	rapchk = pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN);
1537c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
1538c3bc407cSdh 	    "%08x -> %08x", rapchk, 0);
15394c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
15402ac4abe8SDavid Hollister 	wapchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN);
1541c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
1542c3bc407cSdh 	    "%08x -> %08x", wapchk, 0);
15434c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
15442ac4abe8SDavid Hollister 	wdpchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN);
1545c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
1546c3bc407cSdh 	    "%08x -> %08x", wdpchk, 0);
15474c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
15484c06356bSdh 
15494c06356bSdh 	/*
15504c06356bSdh 	 * Step 5
15514c06356bSdh 	 */
15524c06356bSdh 	drv_usecwait(100);
15534c06356bSdh 
15544c06356bSdh 	/*
15554c06356bSdh 	 * Step 5.5 (Temporary workaround for 1.07.xx Beta)
15564c06356bSdh 	 */
15572ac4abe8SDavid Hollister 	tsmode = pmcs_rd_gsm_reg(pwp, 0, PMCS_GPIO_TRISTATE_MODE_ADDR);
1558c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x",
1559c3bc407cSdh 	    tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
15604c06356bSdh 	pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
15614c06356bSdh 	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
15624c06356bSdh 	drv_usecwait(10);
15634c06356bSdh 
15644c06356bSdh 	/*
15654c06356bSdh 	 * Step 6
15664c06356bSdh 	 */
15674c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1568c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1569c3bc407cSdh 	    spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
15704c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
15714c06356bSdh 	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
15724c06356bSdh 	drv_usecwait(10);
15734c06356bSdh 
15744c06356bSdh 	/*
15754c06356bSdh 	 * Step 7
15764c06356bSdh 	 */
15774c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1578c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1579c3bc407cSdh 	    spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
15804c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
15814c06356bSdh 
15824c06356bSdh 	/*
15834c06356bSdh 	 * Step 8
15844c06356bSdh 	 */
15854c06356bSdh 	drv_usecwait(100);
15864c06356bSdh 
15874c06356bSdh 	/*
15884c06356bSdh 	 * Step 9
15894c06356bSdh 	 */
15904c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1591c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1592c3bc407cSdh 	    spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
15934c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
15944c06356bSdh 
15954c06356bSdh 	/*
15964c06356bSdh 	 * Step 10
15974c06356bSdh 	 */
15984c06356bSdh 	drv_usecwait(100);
15994c06356bSdh 
16004c06356bSdh 	/*
16014c06356bSdh 	 * Step 11
16024c06356bSdh 	 */
16032ac4abe8SDavid Hollister 	gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
1604c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
16054c06356bSdh 	    gsm | PMCS_SOFT_RESET_BITS);
16064c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
16074c06356bSdh 	drv_usecwait(10);
16084c06356bSdh 
16094c06356bSdh 	/*
16104c06356bSdh 	 * Step 12
16114c06356bSdh 	 */
1612c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
16132ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN),
1614c3bc407cSdh 	    rapchk);
16154c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
16164c06356bSdh 	drv_usecwait(10);
1617c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
16182ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN),
1619c3bc407cSdh 	    wapchk);
16204c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
16214c06356bSdh 	drv_usecwait(10);
1622c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
16232ac4abe8SDavid Hollister 	    "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN),
1624c3bc407cSdh 	    wapchk);
16254c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
16264c06356bSdh 	drv_usecwait(10);
16274c06356bSdh 
16284c06356bSdh 	/*
16294c06356bSdh 	 * Step 13
16304c06356bSdh 	 */
16314c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1632c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
1633c3bc407cSdh 	    spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
16344c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
16354c06356bSdh 	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
16364c06356bSdh 
16374c06356bSdh 	/*
16384c06356bSdh 	 * Step 14
16394c06356bSdh 	 */
16404c06356bSdh 	drv_usecwait(100);
16414c06356bSdh 
16424c06356bSdh 	/*
16434c06356bSdh 	 * Step 15
16444c06356bSdh 	 */
16454c06356bSdh 	for (spc = 0, i = 0; i < 1000; i++) {
16464c06356bSdh 		drv_usecwait(1000);
16474c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
16484c06356bSdh 		if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
16494c06356bSdh 			break;
16504c06356bSdh 		}
16514c06356bSdh 	}
16524c06356bSdh 
16534c06356bSdh 	if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
1654c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16554c06356bSdh 		    "SFR didn't toggle (sfr 0x%x)", spc);
16564c06356bSdh 		pwp->state = STATE_DEAD;
16574c06356bSdh 		pwp->blocked = 0;
16584c06356bSdh 		if (pwp->locks_initted) {
16594c06356bSdh 			mutex_exit(&pwp->lock);
16604c06356bSdh 		}
16614c06356bSdh 		return (-1);
16624c06356bSdh 	}
16634c06356bSdh 
16644c06356bSdh 	/*
16654c06356bSdh 	 * Step 16
16664c06356bSdh 	 */
16674c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
16684c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
16694c06356bSdh 
16704c06356bSdh 	/*
16714c06356bSdh 	 * Wait for up to 5 seconds for AAP state to come either ready or error.
16724c06356bSdh 	 */
16734c06356bSdh 	for (i = 0; i < 50; i++) {
16744c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
16754c06356bSdh 		    PMCS_MSGU_AAP_STATE_MASK;
16764c06356bSdh 		if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
16774c06356bSdh 		    spc == PMCS_MSGU_AAP_STATE_READY) {
16784c06356bSdh 			break;
16794c06356bSdh 		}
16804c06356bSdh 		drv_usecwait(100000);
16814c06356bSdh 	}
16824c06356bSdh 	spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
16834c06356bSdh 	if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
1684c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16854c06356bSdh 		    "soft reset failed (state 0x%x)", spc);
16864c06356bSdh 		pwp->state = STATE_DEAD;
16874c06356bSdh 		pwp->blocked = 0;
16884c06356bSdh 		if (pwp->locks_initted) {
16894c06356bSdh 			mutex_exit(&pwp->lock);
16904c06356bSdh 		}
16914c06356bSdh 		return (-1);
16924c06356bSdh 	}
16934c06356bSdh 
16945c45adf0SJesse Butler 	/* Clear the firmware log */
16955c45adf0SJesse Butler 	if (pwp->fwlogp) {
16965c45adf0SJesse Butler 		bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
16975c45adf0SJesse Butler 	}
16985c45adf0SJesse Butler 
16995c45adf0SJesse Butler 	/* Reset our queue indices and entries */
17005c45adf0SJesse Butler 	bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
17015c45adf0SJesse Butler 	bzero(pwp->last_iqci, sizeof (pwp->last_iqci));
1702d78a6b7eSJesse Butler 	bzero(pwp->last_htag, sizeof (pwp->last_htag));
17035c45adf0SJesse Butler 	for (i = 0; i < PMCS_NIQ; i++) {
17045c45adf0SJesse Butler 		if (pwp->iqp[i]) {
17055c45adf0SJesse Butler 			bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
17065c45adf0SJesse Butler 			pmcs_wr_iqpi(pwp, i, 0);
17075c45adf0SJesse Butler 			pmcs_wr_iqci(pwp, i, 0);
17085c45adf0SJesse Butler 		}
17095c45adf0SJesse Butler 	}
17105c45adf0SJesse Butler 	for (i = 0; i < PMCS_NOQ; i++) {
17115c45adf0SJesse Butler 		if (pwp->oqp[i]) {
17125c45adf0SJesse Butler 			bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
17135c45adf0SJesse Butler 			pmcs_wr_oqpi(pwp, i, 0);
17145c45adf0SJesse Butler 			pmcs_wr_oqci(pwp, i, 0);
17155c45adf0SJesse Butler 		}
17165c45adf0SJesse Butler 
17175c45adf0SJesse Butler 	}
17184c06356bSdh 
17194c06356bSdh 	if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
17204c06356bSdh 	    pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
17214c06356bSdh 		pwp->blocked = 0;
17224c06356bSdh 		if (pwp->locks_initted) {
17234c06356bSdh 			mutex_exit(&pwp->lock);
17244c06356bSdh 		}
17254c06356bSdh 		return (0);
17264c06356bSdh 	}
17274c06356bSdh 
17284c06356bSdh 	/*
17294c06356bSdh 	 * Return at this point if we dont need to startup.
17304c06356bSdh 	 */
17314c06356bSdh 	if (no_restart) {
17324c06356bSdh 		return (0);
17334c06356bSdh 	}
17344c06356bSdh 
17354c06356bSdh 	ASSERT(pwp->locks_initted != 0);
17364c06356bSdh 
17374c06356bSdh 	/*
17385c45adf0SJesse Butler 	 * Flush the target queues and clear each target's PHY
17394c06356bSdh 	 */
17404c06356bSdh 	if (pwp->targets) {
17414c06356bSdh 		for (i = 0; i < pwp->max_dev; i++) {
17424c06356bSdh 			pmcs_xscsi_t *xp = pwp->targets[i];
17434c06356bSdh 
17444c06356bSdh 			if (xp == NULL) {
17454c06356bSdh 				continue;
17464c06356bSdh 			}
17475c45adf0SJesse Butler 
17484c06356bSdh 			mutex_enter(&xp->statlock);
17495c45adf0SJesse Butler 			pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
17505c45adf0SJesse Butler 			xp->phy = NULL;
17514c06356bSdh 			mutex_exit(&xp->statlock);
17524c06356bSdh 		}
17534c06356bSdh 	}
17544c06356bSdh 
17554c06356bSdh 	/*
17565c45adf0SJesse Butler 	 * Zero out the ports list, free non root phys, clear root phys
17574c06356bSdh 	 */
17585c45adf0SJesse Butler 	bzero(pwp->ports, sizeof (pwp->ports));
17595c45adf0SJesse Butler 	pmcs_free_all_phys(pwp, pwp->root_phys);
17605c45adf0SJesse Butler 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
17615c45adf0SJesse Butler 		pmcs_lock_phy(pptr);
17625c45adf0SJesse Butler 		pmcs_clear_phy(pwp, pptr);
17635c45adf0SJesse Butler 		pptr->target = NULL;
17645c45adf0SJesse Butler 		pmcs_unlock_phy(pptr);
17654c06356bSdh 	}
17664c06356bSdh 
17674c06356bSdh 	/*
17684c06356bSdh 	 * Restore Interrupt Mask
17694c06356bSdh 	 */
17704c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
17714c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
17724c06356bSdh 
17734c06356bSdh 	pwp->mpi_table_setup = 0;
17744c06356bSdh 	mutex_exit(&pwp->lock);
17754c06356bSdh 
17764c06356bSdh 	/*
17774c06356bSdh 	 * Set up MPI again.
17784c06356bSdh 	 */
17794c06356bSdh 	if (pmcs_setup(pwp)) {
17804c06356bSdh 		msg = "unable to setup MPI tables again";
17814c06356bSdh 		goto fail_restart;
17824c06356bSdh 	}
17834c06356bSdh 	pmcs_report_fwversion(pwp);
17844c06356bSdh 
17854c06356bSdh 	/*
17864c06356bSdh 	 * Restart MPI
17874c06356bSdh 	 */
17884c06356bSdh 	if (pmcs_start_mpi(pwp)) {
17894c06356bSdh 		msg = "unable to restart MPI again";
17904c06356bSdh 		goto fail_restart;
17914c06356bSdh 	}
17924c06356bSdh 
17934c06356bSdh 	mutex_enter(&pwp->lock);
17944c06356bSdh 	SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
17954c06356bSdh 	mutex_exit(&pwp->lock);
17964c06356bSdh 
17974c06356bSdh 	/*
17984c06356bSdh 	 * Run any completions
17994c06356bSdh 	 */
18004c06356bSdh 	PMCS_CQ_RUN(pwp);
18014c06356bSdh 
18024c06356bSdh 	/*
18034c06356bSdh 	 * Delay
18044c06356bSdh 	 */
18054c06356bSdh 	drv_usecwait(1000000);
18064c06356bSdh 	return (0);
18074c06356bSdh 
18084c06356bSdh fail_restart:
18094c06356bSdh 	mutex_enter(&pwp->lock);
18104c06356bSdh 	pwp->state = STATE_DEAD;
18114c06356bSdh 	mutex_exit(&pwp->lock);
1812c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
1813c3bc407cSdh 	    "%s: Failed: %s", __func__, msg);
18144c06356bSdh 	return (-1);
18154c06356bSdh }
18164c06356bSdh 
18175c45adf0SJesse Butler 
18185c45adf0SJesse Butler /*
18195c45adf0SJesse Butler  * Perform a 'hot' reset, which will soft reset the chip and
18205c45adf0SJesse Butler  * restore the state back to pre-reset context. Called with pwp
18215c45adf0SJesse Butler  * lock held.
18225c45adf0SJesse Butler  */
18235c45adf0SJesse Butler int
18245c45adf0SJesse Butler pmcs_hot_reset(pmcs_hw_t *pwp)
18255c45adf0SJesse Butler {
18265c45adf0SJesse Butler 	pmcs_iport_t	*iport;
18275c45adf0SJesse Butler 
18285c45adf0SJesse Butler 	ASSERT(mutex_owned(&pwp->lock));
18295c45adf0SJesse Butler 	pwp->state = STATE_IN_RESET;
18305c45adf0SJesse Butler 
18315c45adf0SJesse Butler 	/*
18325c45adf0SJesse Butler 	 * For any iports on this HBA, report empty target sets and
18335c45adf0SJesse Butler 	 * then tear them down.
18345c45adf0SJesse Butler 	 */
18355c45adf0SJesse Butler 	rw_enter(&pwp->iports_lock, RW_READER);
18365c45adf0SJesse Butler 	for (iport = list_head(&pwp->iports); iport != NULL;
18375c45adf0SJesse Butler 	    iport = list_next(&pwp->iports, iport)) {
18385c45adf0SJesse Butler 		mutex_enter(&iport->lock);
18395c45adf0SJesse Butler 		(void) scsi_hba_tgtmap_set_begin(iport->iss_tgtmap);
18405c45adf0SJesse Butler 		(void) scsi_hba_tgtmap_set_end(iport->iss_tgtmap, 0);
18415c45adf0SJesse Butler 		pmcs_iport_teardown_phys(iport);
18425c45adf0SJesse Butler 		mutex_exit(&iport->lock);
18435c45adf0SJesse Butler 	}
18445c45adf0SJesse Butler 	rw_exit(&pwp->iports_lock);
18455c45adf0SJesse Butler 
18465c45adf0SJesse Butler 	/* Grab a register dump, in the event that reset fails */
18475c45adf0SJesse Butler 	pmcs_register_dump_int(pwp);
18485c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
18495c45adf0SJesse Butler 
185032b54db7SJesse Butler 	/* Ensure discovery is not running before we proceed */
185132b54db7SJesse Butler 	mutex_enter(&pwp->config_lock);
185232b54db7SJesse Butler 	while (pwp->configuring) {
185332b54db7SJesse Butler 		cv_wait(&pwp->config_cv, &pwp->config_lock);
185432b54db7SJesse Butler 	}
185532b54db7SJesse Butler 	mutex_exit(&pwp->config_lock);
185632b54db7SJesse Butler 
18575c45adf0SJesse Butler 	/* Issue soft reset and clean up related softstate */
18585c45adf0SJesse Butler 	if (pmcs_soft_reset(pwp, B_FALSE)) {
18595c45adf0SJesse Butler 		/*
18605c45adf0SJesse Butler 		 * Disable interrupts, in case we got far enough along to
18615c45adf0SJesse Butler 		 * enable them, then fire off ereport and service impact.
18625c45adf0SJesse Butler 		 */
18635c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18645c45adf0SJesse Butler 		    "%s: failed soft reset", __func__);
18655c45adf0SJesse Butler 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
18665c45adf0SJesse Butler 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
18675c45adf0SJesse Butler 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE);
18685c45adf0SJesse Butler 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
18695c45adf0SJesse Butler 		mutex_enter(&pwp->lock);
18705c45adf0SJesse Butler 		pwp->state = STATE_DEAD;
18715c45adf0SJesse Butler 		return (DDI_FAILURE);
18725c45adf0SJesse Butler 	}
18735c45adf0SJesse Butler 
18745c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
18755c45adf0SJesse Butler 	pwp->state = STATE_RUNNING;
18765c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
18775c45adf0SJesse Butler 
18785c45adf0SJesse Butler 	/*
18795c45adf0SJesse Butler 	 * Finally, restart the phys, which will bring the iports back
18805c45adf0SJesse Butler 	 * up and eventually result in discovery running.
18815c45adf0SJesse Butler 	 */
18825c45adf0SJesse Butler 	if (pmcs_start_phys(pwp)) {
18835c45adf0SJesse Butler 		/* We should be up and running now, so retry */
18845c45adf0SJesse Butler 		if (pmcs_start_phys(pwp)) {
18855c45adf0SJesse Butler 			/* Apparently unable to restart PHYs, fail */
18865c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18875c45adf0SJesse Butler 			    "%s: failed to restart PHYs after soft reset",
18885c45adf0SJesse Butler 			    __func__);
18895c45adf0SJesse Butler 			mutex_enter(&pwp->lock);
18905c45adf0SJesse Butler 			return (DDI_FAILURE);
18915c45adf0SJesse Butler 		}
18925c45adf0SJesse Butler 	}
18935c45adf0SJesse Butler 
18945c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
18955c45adf0SJesse Butler 	return (DDI_SUCCESS);
18965c45adf0SJesse Butler }
18975c45adf0SJesse Butler 
18984c06356bSdh /*
18994c06356bSdh  * Reset a device or a logical unit.
19004c06356bSdh  */
19014c06356bSdh int
19024c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
19034c06356bSdh {
19044c06356bSdh 	int rval = 0;
19054c06356bSdh 
19064c06356bSdh 	if (pptr == NULL) {
19074c06356bSdh 		return (ENXIO);
19084c06356bSdh 	}
19094c06356bSdh 
19104c06356bSdh 	pmcs_lock_phy(pptr);
19114c06356bSdh 	if (pptr->dtype == SAS) {
19124c06356bSdh 		/*
19134c06356bSdh 		 * Some devices do not support SAS_I_T_NEXUS_RESET as
19144c06356bSdh 		 * it is not a mandatory (in SAM4) task management
19154c06356bSdh 		 * function, while LOGIC_UNIT_RESET is mandatory.
19164c06356bSdh 		 *
19174c06356bSdh 		 * The problem here is that we need to iterate over
19184c06356bSdh 		 * all known LUNs to emulate the semantics of
19194c06356bSdh 		 * "RESET_TARGET".
19204c06356bSdh 		 *
19214c06356bSdh 		 * XXX: FIX ME
19224c06356bSdh 		 */
19234c06356bSdh 		if (lun == (uint64_t)-1) {
19244c06356bSdh 			lun = 0;
19254c06356bSdh 		}
19264c06356bSdh 		rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
19274c06356bSdh 		    NULL);
19284c06356bSdh 	} else if (pptr->dtype == SATA) {
19294c06356bSdh 		if (lun != 0ull) {
19304c06356bSdh 			pmcs_unlock_phy(pptr);
19314c06356bSdh 			return (EINVAL);
19324c06356bSdh 		}
19334c06356bSdh 		rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
19344c06356bSdh 	} else {
19354c06356bSdh 		pmcs_unlock_phy(pptr);
1936c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
19374c06356bSdh 		    "%s: cannot reset a SMP device yet (%s)",
19384c06356bSdh 		    __func__, pptr->path);
19394c06356bSdh 		return (EINVAL);
19404c06356bSdh 	}
19414c06356bSdh 
19424c06356bSdh 	/*
19434c06356bSdh 	 * Now harvest any commands killed by this action
19444c06356bSdh 	 * by issuing an ABORT for all commands on this device.
19454c06356bSdh 	 *
19464c06356bSdh 	 * We do this even if the the tmf or reset fails (in case there
19474c06356bSdh 	 * are any dead commands around to be harvested *anyway*).
19484c06356bSdh 	 * We don't have to await for the abort to complete.
19494c06356bSdh 	 */
19504c06356bSdh 	if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
19514c06356bSdh 		pptr->abort_pending = 1;
19524c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
19534c06356bSdh 	}
19544c06356bSdh 
19554c06356bSdh 	pmcs_unlock_phy(pptr);
19564c06356bSdh 	return (rval);
19574c06356bSdh }
19584c06356bSdh 
19594c06356bSdh /*
19604c06356bSdh  * Called with PHY locked.
19614c06356bSdh  */
19624c06356bSdh static int
19634c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
19644c06356bSdh {
19654c06356bSdh 	if (pptr->valid_device_id == 0) {
19664c06356bSdh 		int result = pmcs_register_device(pwp, pptr);
19674c06356bSdh 
19684c06356bSdh 		/*
19694c06356bSdh 		 * If we changed while registering, punt
19704c06356bSdh 		 */
19714c06356bSdh 		if (pptr->changed) {
19724c06356bSdh 			RESTART_DISCOVERY(pwp);
19734c06356bSdh 			return (-1);
19744c06356bSdh 		}
19754c06356bSdh 
19764c06356bSdh 		/*
19774c06356bSdh 		 * If we had a failure to register, check against errors.
19784c06356bSdh 		 * An ENOMEM error means we just retry (temp resource shortage).
19794c06356bSdh 		 */
19804c06356bSdh 		if (result == ENOMEM) {
19814c06356bSdh 			PHY_CHANGED(pwp, pptr);
19824c06356bSdh 			RESTART_DISCOVERY(pwp);
19834c06356bSdh 			return (-1);
19844c06356bSdh 		}
19854c06356bSdh 
19864c06356bSdh 		/*
19874c06356bSdh 		 * An ETIMEDOUT error means we retry (if our counter isn't
19884c06356bSdh 		 * exhausted)
19894c06356bSdh 		 */
19904c06356bSdh 		if (result == ETIMEDOUT) {
19914c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
19924c06356bSdh 				PHY_CHANGED(pwp, pptr);
19934c06356bSdh 				RESTART_DISCOVERY(pwp);
19944c06356bSdh 			} else {
1995c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
19964c06356bSdh 				    "%s: Retries exhausted for %s, killing",
19974c06356bSdh 				    __func__, pptr->path);
19984c06356bSdh 				pptr->config_stop = 0;
19994c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
20004c06356bSdh 			}
20014c06356bSdh 			return (-1);
20024c06356bSdh 		}
20034c06356bSdh 		/*
20044c06356bSdh 		 * Other errors or no valid device id is fatal, but don't
20054c06356bSdh 		 * preclude a future action.
20064c06356bSdh 		 */
20074c06356bSdh 		if (result || pptr->valid_device_id == 0) {
2008c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
2009c3bc407cSdh 			    "%s: %s could not be registered", __func__,
2010c3bc407cSdh 			    pptr->path);
20114c06356bSdh 			return (-1);
20124c06356bSdh 		}
20134c06356bSdh 	}
20144c06356bSdh 	return (0);
20154c06356bSdh }
20164c06356bSdh 
20174c06356bSdh int
20184c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport)
20194c06356bSdh {
20204c06356bSdh 	ASSERT(iport);
20214c06356bSdh 	if (iport == NULL)
20224c06356bSdh 		return (B_FALSE);
20234c06356bSdh 
2024c3bc407cSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
20254c06356bSdh 
20264c06356bSdh 	/* create target map */
20274c06356bSdh 	if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec,
20289aed1621SDavid Hollister 	    (void *)iport, 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);
21704c06356bSdh 			mutex_enter(&iport->refcnt_lock);
21714c06356bSdh 			iport->refcnt++;
21724c06356bSdh 			mutex_exit(&iport->refcnt_lock);
21734c06356bSdh 			break;
21744c06356bSdh 		}
21754c06356bSdh 		mutex_exit(&iport->lock);
21764c06356bSdh 	}
21774c06356bSdh 	rw_exit(&pwp->iports_lock);
21784c06356bSdh 
21794c06356bSdh 	return (iport);
21804c06356bSdh }
21814c06356bSdh 
21824c06356bSdh /*
21834c06356bSdh  * Return the iport that pptr is associated with, or NULL.
21844c06356bSdh  * If an iport is returned, there is a hold that the caller must release.
21854c06356bSdh  */
21864c06356bSdh pmcs_iport_t *
21879aed1621SDavid Hollister pmcs_get_iport_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
21884c06356bSdh {
21894c06356bSdh 	pmcs_iport_t	*iport = NULL;
21904c06356bSdh 	char		*ua;
21914c06356bSdh 
21929aed1621SDavid Hollister 	ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], wwn);
21934c06356bSdh 	if (ua) {
21944c06356bSdh 		iport = pmcs_get_iport_by_ua(pwp, ua);
21954c06356bSdh 		if (iport) {
21964c06356bSdh 			mutex_enter(&iport->lock);
21979aed1621SDavid Hollister 			pmcs_iport_active(iport);
21989aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
21999aed1621SDavid Hollister 			    "found iport [0x%p] on ua (%s), refcnt (%d)",
22009aed1621SDavid Hollister 			    __func__, (void *)iport, ua, iport->refcnt);
22014c06356bSdh 			mutex_exit(&iport->lock);
22024c06356bSdh 		}
22034c06356bSdh 	}
22044c06356bSdh 
22054c06356bSdh 	return (iport);
22064c06356bSdh }
22074c06356bSdh 
2208f73ae3dbSJesse Butler /*
22096745c559SJesse Butler  * Promote the next phy on this port to primary, and return it.
2210f73ae3dbSJesse Butler  * Called when the primary PHY on a port is going down, but the port
2211f73ae3dbSJesse Butler  * remains up (see pmcs_intr.c).
2212f73ae3dbSJesse Butler  */
2213f73ae3dbSJesse Butler pmcs_phy_t *
2214f73ae3dbSJesse Butler pmcs_promote_next_phy(pmcs_phy_t *prev_primary)
2215f73ae3dbSJesse Butler {
22166745c559SJesse Butler 	pmcs_hw_t	*pwp;
22176745c559SJesse Butler 	pmcs_iport_t	*iport;
22186745c559SJesse Butler 	pmcs_phy_t	*pptr, *child;
22196745c559SJesse Butler 	int		portid;
2220f73ae3dbSJesse Butler 
22216745c559SJesse Butler 	pmcs_lock_phy(prev_primary);
22226745c559SJesse Butler 	portid = prev_primary->portid;
22236745c559SJesse Butler 	iport  = prev_primary->iport;
22246745c559SJesse Butler 	pwp    = prev_primary->pwp;
2225f73ae3dbSJesse Butler 
22266745c559SJesse Butler 	/* Use the first available phy in this port */
22276745c559SJesse Butler 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
22286745c559SJesse Butler 		if ((pptr->portid == portid) && (pptr != prev_primary)) {
22296745c559SJesse Butler 			mutex_enter(&pptr->phy_lock);
2230f73ae3dbSJesse Butler 			break;
2231f73ae3dbSJesse Butler 		}
2232f73ae3dbSJesse Butler 	}
2233f73ae3dbSJesse Butler 
2234f73ae3dbSJesse Butler 	if (pptr == NULL) {
22356745c559SJesse Butler 		pmcs_unlock_phy(prev_primary);
2236f73ae3dbSJesse Butler 		return (NULL);
2237f73ae3dbSJesse Butler 	}
2238f73ae3dbSJesse Butler 
22396745c559SJesse Butler 	if (iport) {
22406745c559SJesse Butler 		mutex_enter(&iport->lock);
22416745c559SJesse Butler 		iport->pptr = pptr;
22426745c559SJesse Butler 		mutex_exit(&iport->lock);
22436745c559SJesse Butler 	}
2244f73ae3dbSJesse Butler 
22456745c559SJesse Butler 	/* Update the phy handle with the data from the previous primary */
2246f73ae3dbSJesse Butler 	pptr->children		= prev_primary->children;
2247f73ae3dbSJesse Butler 	child = pptr->children;
2248f73ae3dbSJesse Butler 	while (child) {
2249f73ae3dbSJesse Butler 		child->parent = pptr;
2250f73ae3dbSJesse Butler 		child = child->sibling;
2251f73ae3dbSJesse Butler 	}
2252f73ae3dbSJesse Butler 	pptr->ncphy		= prev_primary->ncphy;
2253f73ae3dbSJesse Butler 	pptr->width		= prev_primary->width;
2254f73ae3dbSJesse Butler 	pptr->dtype		= prev_primary->dtype;
2255f73ae3dbSJesse Butler 	pptr->pend_dtype	= prev_primary->pend_dtype;
2256f73ae3dbSJesse Butler 	pptr->tolerates_sas2	= prev_primary->tolerates_sas2;
2257f73ae3dbSJesse Butler 	pptr->atdt		= prev_primary->atdt;
2258f73ae3dbSJesse Butler 	pptr->portid		= prev_primary->portid;
2259f73ae3dbSJesse Butler 	pptr->link_rate		= prev_primary->link_rate;
2260f73ae3dbSJesse Butler 	pptr->configured	= prev_primary->configured;
2261f73ae3dbSJesse Butler 	pptr->iport		= prev_primary->iport;
2262f73ae3dbSJesse Butler 	pptr->target		= prev_primary->target;
22636745c559SJesse Butler 	if (pptr->target) {
22646745c559SJesse Butler 		pptr->target->phy = pptr;
22656745c559SJesse Butler 	}
2266499cfd15SDavid Hollister 
2267499cfd15SDavid Hollister 	/* Update the phy mask properties for the affected PHYs */
2268499cfd15SDavid Hollister 	/* Clear the current values... */
2269499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
2270499cfd15SDavid Hollister 	    pptr->tgt_port_pm_tmp, B_FALSE);
2271499cfd15SDavid Hollister 	/* ...replace with the values from prev_primary... */
2272499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp,
2273499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm_tmp, B_TRUE);
2274499cfd15SDavid Hollister 	/* ...then clear prev_primary's PHY values from the new primary */
2275499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm,
2276499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm, B_FALSE);
2277499cfd15SDavid Hollister 	/* Clear the prev_primary's values */
2278499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp,
2279499cfd15SDavid Hollister 	    prev_primary->tgt_port_pm_tmp, B_FALSE);
2280499cfd15SDavid Hollister 
2281f73ae3dbSJesse Butler 	pptr->subsidiary = 0;
2282f73ae3dbSJesse Butler 
2283f73ae3dbSJesse Butler 	prev_primary->subsidiary = 1;
2284f73ae3dbSJesse Butler 	prev_primary->children = NULL;
22856745c559SJesse Butler 	prev_primary->target = NULL;
2286601c90f1SSrikanth, Ramana 	pptr->device_id = prev_primary->device_id;
2287601c90f1SSrikanth, Ramana 	pptr->valid_device_id = 1;
2288f73ae3dbSJesse Butler 	pmcs_unlock_phy(prev_primary);
2289f73ae3dbSJesse Butler 
2290f73ae3dbSJesse Butler 	/*
2291f73ae3dbSJesse Butler 	 * We call pmcs_unlock_phy() on pptr because it now contains the
2292f73ae3dbSJesse Butler 	 * list of children.
2293f73ae3dbSJesse Butler 	 */
2294f73ae3dbSJesse Butler 	pmcs_unlock_phy(pptr);
2295f73ae3dbSJesse Butler 
2296f73ae3dbSJesse Butler 	return (pptr);
2297f73ae3dbSJesse Butler }
2298f73ae3dbSJesse Butler 
22994c06356bSdh void
23004c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport)
23014c06356bSdh {
23024c06356bSdh 	/*
23034c06356bSdh 	 * Release a refcnt on this iport. If this is the last reference,
23044c06356bSdh 	 * signal the potential waiter in pmcs_iport_unattach().
23054c06356bSdh 	 */
23064c06356bSdh 	ASSERT(iport->refcnt > 0);
23074c06356bSdh 	mutex_enter(&iport->refcnt_lock);
23084c06356bSdh 	iport->refcnt--;
23094c06356bSdh 	mutex_exit(&iport->refcnt_lock);
23104c06356bSdh 	if (iport->refcnt == 0) {
23114c06356bSdh 		cv_signal(&iport->refcnt_cv);
23124c06356bSdh 	}
2313c3bc407cSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: iport "
2314c3bc407cSdh 	    "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
23154c06356bSdh }
23164c06356bSdh 
23174c06356bSdh void
23184c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp)
23194c06356bSdh {
23204c06356bSdh 	_NOTE(ARGUNUSED(privp));
23214c06356bSdh 	pmcs_hw_t	*pwp = arg;
23224c06356bSdh 	pmcs_iport_t	*iport = NULL;
23234c06356bSdh 
23244c06356bSdh 	mutex_enter(&pwp->lock);
23254c06356bSdh 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
23264c06356bSdh 		mutex_exit(&pwp->lock);
23274c06356bSdh 		return;
23284c06356bSdh 	}
23294c06356bSdh 	pwp->phymap_active++;
23304c06356bSdh 	mutex_exit(&pwp->lock);
23314c06356bSdh 
23324c06356bSdh 	if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
23334c06356bSdh 	    DDI_SUCCESS) {
2334c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
2335c3bc407cSdh 		    "add iport handle on unit address [%s]", __func__, ua);
23364c06356bSdh 	} else {
2337c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
2338c3bc407cSdh 		    "phymap_active count (%d), added iport handle on unit "
2339c3bc407cSdh 		    "address [%s]", __func__, pwp->phymap_active, ua);
23404c06356bSdh 	}
23414c06356bSdh 
23424c06356bSdh 	/* Set the HBA softstate as our private data for this unit address */
23434c06356bSdh 	*privp = (void *)pwp;
23444c06356bSdh 
23454c06356bSdh 	/*
23464c06356bSdh 	 * We are waiting on attach for this iport node, unless it is still
23474c06356bSdh 	 * attached. This can happen if a consumer has an outstanding open
23484c06356bSdh 	 * on our iport node, but the port is down.  If this is the case, we
23494c06356bSdh 	 * need to configure our iport here for reuse.
23504c06356bSdh 	 */
23514c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
23524c06356bSdh 	if (iport) {
23534c06356bSdh 		mutex_enter(&iport->lock);
23544c06356bSdh 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
2355c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
23564c06356bSdh 			    "failed to configure phys on iport [0x%p] at "
23574c06356bSdh 			    "unit address (%s)", __func__, (void *)iport, ua);
23584c06356bSdh 		}
23599aed1621SDavid Hollister 		pmcs_iport_active(iport);
23604c06356bSdh 		pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
23614c06356bSdh 		    &iport->nphy);
23624c06356bSdh 		mutex_exit(&iport->lock);
23634c06356bSdh 		pmcs_rele_iport(iport);
23644c06356bSdh 	}
23654c06356bSdh 
23664c06356bSdh }
23674c06356bSdh 
23684c06356bSdh void
23694c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp)
23704c06356bSdh {
23714c06356bSdh 	_NOTE(ARGUNUSED(privp));
23724c06356bSdh 	pmcs_hw_t	*pwp = arg;
23734c06356bSdh 	pmcs_iport_t	*iport;
23744c06356bSdh 
23754c06356bSdh 	mutex_enter(&pwp->lock);
23764c06356bSdh 	pwp->phymap_active--;
23774c06356bSdh 	mutex_exit(&pwp->lock);
23784c06356bSdh 
23794c06356bSdh 	if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
23804c06356bSdh 	    DDI_SUCCESS) {
2381c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
2382c3bc407cSdh 		    "remove iport handle on unit address [%s]", __func__, ua);
23834c06356bSdh 	} else {
2384c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
2385c3bc407cSdh 		    "phymap_active count (%d), removed iport handle on unit "
2386c3bc407cSdh 		    "address [%s]", __func__, pwp->phymap_active, ua);
23874c06356bSdh 	}
23884c06356bSdh 
23894c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
23904c06356bSdh 
23914c06356bSdh 	if (iport == NULL) {
2392c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed "
2393c3bc407cSdh 		    "lookup of iport handle on unit addr (%s)", __func__, ua);
23944c06356bSdh 		return;
23954c06356bSdh 	}
23964c06356bSdh 
23974c06356bSdh 	mutex_enter(&iport->lock);
23984c06356bSdh 	iport->ua_state = UA_INACTIVE;
23994c06356bSdh 	iport->portid = PMCS_IPORT_INVALID_PORT_ID;
24004c06356bSdh 	pmcs_remove_phy_from_iport(iport, NULL);
24014c06356bSdh 	mutex_exit(&iport->lock);
24024c06356bSdh 	pmcs_rele_iport(iport);
24034c06356bSdh }
24044c06356bSdh 
24054c06356bSdh /*
24064c06356bSdh  * Top-level discovery function
24074c06356bSdh  */
24084c06356bSdh void
24094c06356bSdh pmcs_discover(pmcs_hw_t *pwp)
24104c06356bSdh {
24114c06356bSdh 	pmcs_phy_t		*pptr;
24124c06356bSdh 	pmcs_phy_t		*root_phy;
24134c06356bSdh 
24144c06356bSdh 	DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
24154c06356bSdh 	    boolean_t, pwp->config_changed);
24164c06356bSdh 
24174c06356bSdh 	mutex_enter(&pwp->lock);
24184c06356bSdh 
24194c06356bSdh 	if (pwp->state != STATE_RUNNING) {
24204c06356bSdh 		mutex_exit(&pwp->lock);
24214c06356bSdh 		return;
24224c06356bSdh 	}
24234c06356bSdh 
24244c06356bSdh 	/* Ensure we have at least one phymap active */
24254c06356bSdh 	if (pwp->phymap_active == 0) {
24264c06356bSdh 		mutex_exit(&pwp->lock);
2427c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24284c06356bSdh 		    "%s: phymap inactive, exiting", __func__);
24294c06356bSdh 		return;
24304c06356bSdh 	}
24314c06356bSdh 
24324c06356bSdh 	mutex_exit(&pwp->lock);
24334c06356bSdh 
24344c06356bSdh 	/*
24354c06356bSdh 	 * If no iports have attached, but we have PHYs that are up, we
24364c06356bSdh 	 * are waiting for iport attach to complete.  Restart discovery.
24374c06356bSdh 	 */
24384c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
24394c06356bSdh 	if (!pwp->iports_attached) {
24404c06356bSdh 		rw_exit(&pwp->iports_lock);
2441c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24424c06356bSdh 		    "%s: no iports attached, retry discovery", __func__);
24434c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
24444c06356bSdh 		return;
24454c06356bSdh 	}
24464c06356bSdh 	rw_exit(&pwp->iports_lock);
24474c06356bSdh 
24484c06356bSdh 	mutex_enter(&pwp->config_lock);
24494c06356bSdh 	if (pwp->configuring) {
24504c06356bSdh 		mutex_exit(&pwp->config_lock);
2451c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24524c06356bSdh 		    "%s: configuration already in progress", __func__);
24534c06356bSdh 		return;
24544c06356bSdh 	}
24554c06356bSdh 
24564c06356bSdh 	if (pmcs_acquire_scratch(pwp, B_FALSE)) {
24574c06356bSdh 		mutex_exit(&pwp->config_lock);
2458c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
24594c06356bSdh 		    "%s: cannot allocate scratch", __func__);
24604c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
24614c06356bSdh 		return;
24624c06356bSdh 	}
24634c06356bSdh 
24644c06356bSdh 	pwp->configuring = 1;
24654c06356bSdh 	pwp->config_changed = B_FALSE;
24664c06356bSdh 	mutex_exit(&pwp->config_lock);
24674c06356bSdh 
2468c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin");
24694c06356bSdh 
2470601c90f1SSrikanth, Ramana 	/*
2471601c90f1SSrikanth, Ramana 	 * First, tell SCSA that we're beginning set operations.
2472601c90f1SSrikanth, Ramana 	 */
2473601c90f1SSrikanth, Ramana 	pmcs_begin_observations(pwp);
2474601c90f1SSrikanth, Ramana 
24754c06356bSdh 	/*
24764c06356bSdh 	 * The order of the following traversals is important.
24774c06356bSdh 	 *
24784c06356bSdh 	 * The first one checks for changed expanders.
24794c06356bSdh 	 *
24804c06356bSdh 	 * The second one aborts commands for dead devices and deregisters them.
24814c06356bSdh 	 *
24824c06356bSdh 	 * The third one clears the contents of dead expanders from the tree
24834c06356bSdh 	 *
24844c06356bSdh 	 * The fourth one clears now dead devices in expanders that remain.
24854c06356bSdh 	 */
24864c06356bSdh 
24874c06356bSdh 	/*
24884c06356bSdh 	 * 1. Check expanders marked changed (but not dead) to see if they still
24894c06356bSdh 	 * have the same number of phys and the same SAS address. Mark them,
24904c06356bSdh 	 * their subsidiary phys (if wide) and their descendents dead if
24914c06356bSdh 	 * anything has changed. Check the devices they contain to see if
24924c06356bSdh 	 * *they* have changed. If they've changed from type NOTHING we leave
24934c06356bSdh 	 * them marked changed to be configured later (picking up a new SAS
24944c06356bSdh 	 * address and link rate if possible). Otherwise, any change in type,
24954c06356bSdh 	 * SAS address or removal of target role will cause us to mark them
24964c06356bSdh 	 * (and their descendents) as dead (and cause any pending commands
24974c06356bSdh 	 * and associated devices to be removed).
2498ec2c44b8Sdh 	 *
2499ec2c44b8Sdh 	 * NOTE: We don't want to bail on discovery if the config has
2500ec2c44b8Sdh 	 * changed until *after* we run pmcs_kill_devices.
25014c06356bSdh 	 */
25024c06356bSdh 	root_phy = pwp->root_phys;
250373a3eccdSDavid Hollister 	pmcs_check_expanders(pwp, root_phy);
25044c06356bSdh 
25054c06356bSdh 	/*
25064c06356bSdh 	 * 2. Descend the tree looking for dead devices and kill them
25074c06356bSdh 	 * by aborting all active commands and then deregistering them.
25084c06356bSdh 	 */
250973a3eccdSDavid Hollister 	if (pmcs_kill_devices(pwp, root_phy)) {
251073a3eccdSDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
251173a3eccdSDavid Hollister 		    "%s: pmcs_kill_devices failed!", __func__);
25124c06356bSdh 	}
25134c06356bSdh 
25144c06356bSdh 	/*
25154c06356bSdh 	 * 3. Check for dead expanders and remove their children from the tree.
25164c06356bSdh 	 * By the time we get here, the devices and commands for them have
25174c06356bSdh 	 * already been terminated and removed.
25184c06356bSdh 	 *
25194c06356bSdh 	 * We do this independent of the configuration count changing so we can
25204c06356bSdh 	 * free any dead device PHYs that were discovered while checking
25214c06356bSdh 	 * expanders. We ignore any subsidiary phys as pmcs_clear_expander
25224c06356bSdh 	 * will take care of those.
25234c06356bSdh 	 *
25244c06356bSdh 	 * NOTE: pmcs_clear_expander requires softstate lock
25254c06356bSdh 	 */
25264c06356bSdh 	mutex_enter(&pwp->lock);
25274c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
25284c06356bSdh 		/*
25294c06356bSdh 		 * Call pmcs_clear_expander for every root PHY.  It will
25304c06356bSdh 		 * recurse and determine which (if any) expanders actually
25314c06356bSdh 		 * need to be cleared.
25324c06356bSdh 		 */
25334c06356bSdh 		pmcs_lock_phy(pptr);
25344c06356bSdh 		pmcs_clear_expander(pwp, pptr, 0);
25354c06356bSdh 		pmcs_unlock_phy(pptr);
25364c06356bSdh 	}
25374c06356bSdh 	mutex_exit(&pwp->lock);
25384c06356bSdh 
25394c06356bSdh 	/*
25404c06356bSdh 	 * 4. Check for dead devices and nullify them. By the time we get here,
25414c06356bSdh 	 * the devices and commands for them have already been terminated
25424c06356bSdh 	 * and removed. This is different from step 2 in that this just nulls
25434c06356bSdh 	 * phys that are part of expanders that are still here but used to
25444c06356bSdh 	 * be something but are no longer something (e.g., after a pulled
25454c06356bSdh 	 * disk drive). Note that dead expanders had their contained phys
25464c06356bSdh 	 * removed from the tree- here, the expanders themselves are
25474c06356bSdh 	 * nullified (unless they were removed by being contained in another
25484c06356bSdh 	 * expander phy).
25494c06356bSdh 	 */
25504c06356bSdh 	pmcs_clear_phys(pwp, root_phy);
25514c06356bSdh 
25524c06356bSdh 	/*
25534c06356bSdh 	 * 5. Now check for and configure new devices.
25544c06356bSdh 	 */
25554c06356bSdh 	if (pmcs_configure_new_devices(pwp, root_phy)) {
25564c06356bSdh 		goto restart;
25574c06356bSdh 	}
25584c06356bSdh 
25594c06356bSdh out:
25604c06356bSdh 	DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
25614c06356bSdh 	    boolean_t, pwp->config_changed);
2562c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end");
25634c06356bSdh 
25644c06356bSdh 	mutex_enter(&pwp->config_lock);
25654c06356bSdh 
25664c06356bSdh 	if (pwp->config_changed == B_FALSE) {
25674c06356bSdh 		/*
25684c06356bSdh 		 * Observation is stable, report what we currently see to
25694c06356bSdh 		 * the tgtmaps for delta processing. Start by setting
25704c06356bSdh 		 * BEGIN on all tgtmaps.
25714c06356bSdh 		 */
25724c06356bSdh 		mutex_exit(&pwp->config_lock);
25734c06356bSdh 		if (pmcs_report_observations(pwp) == B_FALSE) {
25744c06356bSdh 			goto restart;
25754c06356bSdh 		}
25764c06356bSdh 		mutex_enter(&pwp->config_lock);
25774c06356bSdh 	} else {
25784c06356bSdh 		/*
25794c06356bSdh 		 * If config_changed is TRUE, we need to reschedule
25804c06356bSdh 		 * discovery now.
25814c06356bSdh 		 */
2582c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
25834c06356bSdh 		    "%s: Config has changed, will re-run discovery", __func__);
25844c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
25854c06356bSdh 	}
25864c06356bSdh 
25874c06356bSdh 	pmcs_release_scratch(pwp);
25885c45adf0SJesse Butler 	if (!pwp->quiesced) {
25895c45adf0SJesse Butler 		pwp->blocked = 0;
25905c45adf0SJesse Butler 	}
25914c06356bSdh 	pwp->configuring = 0;
259232b54db7SJesse Butler 	cv_signal(&pwp->config_cv);
25934c06356bSdh 	mutex_exit(&pwp->config_lock);
25944c06356bSdh 
25954c06356bSdh #ifdef DEBUG
25964c06356bSdh 	pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
25974c06356bSdh 	if (pptr != NULL) {
25984c06356bSdh 		if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
2599c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
26004c06356bSdh 			    "PHY %s dead=%d changed=%d configured=%d "
26014c06356bSdh 			    "but no work scheduled", pptr->path, pptr->dead,
26024c06356bSdh 			    pptr->changed, pptr->configured);
26034c06356bSdh 		}
26044c06356bSdh 		pmcs_unlock_phy(pptr);
26054c06356bSdh 	}
26064c06356bSdh #endif
26074c06356bSdh 
26084c06356bSdh 	return;
26094c06356bSdh 
26104c06356bSdh restart:
26114c06356bSdh 	/* Clean up and restart discovery */
26124c06356bSdh 	pmcs_release_scratch(pwp);
26130b53804eSReed 	pmcs_flush_observations(pwp);
26144c06356bSdh 	mutex_enter(&pwp->config_lock);
26154c06356bSdh 	pwp->configuring = 0;
261632b54db7SJesse Butler 	cv_signal(&pwp->config_cv);
26174c06356bSdh 	RESTART_DISCOVERY_LOCKED(pwp);
26184c06356bSdh 	mutex_exit(&pwp->config_lock);
26194c06356bSdh }
26204c06356bSdh 
26214c06356bSdh /*
26224c06356bSdh  * Return any PHY that needs to have scheduled work done.  The PHY is returned
26234c06356bSdh  * locked.
26244c06356bSdh  */
26254c06356bSdh static pmcs_phy_t *
26264c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
26274c06356bSdh {
26284c06356bSdh 	pmcs_phy_t *cphyp, *pnext;
26294c06356bSdh 
26304c06356bSdh 	while (pptr) {
26314c06356bSdh 		pmcs_lock_phy(pptr);
26324c06356bSdh 
26334c06356bSdh 		if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
26344c06356bSdh 			return (pptr);
26354c06356bSdh 		}
26364c06356bSdh 
26374c06356bSdh 		pnext = pptr->sibling;
26384c06356bSdh 
26394c06356bSdh 		if (pptr->children) {
26404c06356bSdh 			cphyp = pptr->children;
26414c06356bSdh 			pmcs_unlock_phy(pptr);
26424c06356bSdh 			cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
26434c06356bSdh 			if (cphyp) {
26444c06356bSdh 				return (cphyp);
26454c06356bSdh 			}
26464c06356bSdh 		} else {
26474c06356bSdh 			pmcs_unlock_phy(pptr);
26484c06356bSdh 		}
26494c06356bSdh 
26504c06356bSdh 		pptr = pnext;
26514c06356bSdh 	}
26524c06356bSdh 
26534c06356bSdh 	return (NULL);
26544c06356bSdh }
26554c06356bSdh 
26564c06356bSdh /*
2657601c90f1SSrikanth, Ramana  * We may (or may not) report observations to SCSA.  This is prefaced by
2658601c90f1SSrikanth, Ramana  * issuing a set_begin for each iport target map.
26594c06356bSdh  */
2660601c90f1SSrikanth, Ramana static void
2661601c90f1SSrikanth, Ramana pmcs_begin_observations(pmcs_hw_t *pwp)
26624c06356bSdh {
26634c06356bSdh 	pmcs_iport_t		*iport;
26644c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
26654c06356bSdh 
26664c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
26674c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
26684c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
26694c06356bSdh 		/*
26704c06356bSdh 		 * Unless we have at least one phy up, skip this iport.
26714c06356bSdh 		 * Note we don't need to lock the iport for report_skip
26724c06356bSdh 		 * since it is only used here.  We are doing the skip so that
26734c06356bSdh 		 * the phymap and iportmap stabilization times are honored -
26744c06356bSdh 		 * giving us the ability to recover port operation within the
26754c06356bSdh 		 * stabilization time without unconfiguring targets using the
26764c06356bSdh 		 * port.
26774c06356bSdh 		 */
26784c06356bSdh 		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
26794c06356bSdh 			iport->report_skip = 1;
26804c06356bSdh 			continue;		/* skip set_begin */
26814c06356bSdh 		}
26824c06356bSdh 		iport->report_skip = 0;
26834c06356bSdh 
26844c06356bSdh 		tgtmap = iport->iss_tgtmap;
26854c06356bSdh 		ASSERT(tgtmap);
26864c06356bSdh 		if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
2687c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
26884c06356bSdh 			    "%s: cannot set_begin tgtmap ", __func__);
26894c06356bSdh 			rw_exit(&pwp->iports_lock);
2690601c90f1SSrikanth, Ramana 			return;
26914c06356bSdh 		}
2692c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
2693c3bc407cSdh 		    "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap);
26944c06356bSdh 	}
26954c06356bSdh 	rw_exit(&pwp->iports_lock);
2696601c90f1SSrikanth, Ramana }
2697601c90f1SSrikanth, Ramana 
26980b53804eSReed /*
26990b53804eSReed  * Tell SCSA to flush the observations we've already sent (if any), as they
27000b53804eSReed  * are no longer valid.
27010b53804eSReed  */
27020b53804eSReed static void
27030b53804eSReed pmcs_flush_observations(pmcs_hw_t *pwp)
27040b53804eSReed {
27050b53804eSReed 	pmcs_iport_t		*iport;
27060b53804eSReed 	scsi_hba_tgtmap_t	*tgtmap;
27070b53804eSReed 
27080b53804eSReed 	rw_enter(&pwp->iports_lock, RW_READER);
27090b53804eSReed 	for (iport = list_head(&pwp->iports); iport != NULL;
27100b53804eSReed 	    iport = list_next(&pwp->iports, iport)) {
27110b53804eSReed 		/*
27120b53804eSReed 		 * Skip this iport if it has no PHYs up.
27130b53804eSReed 		 */
27140b53804eSReed 		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
27150b53804eSReed 			continue;
27160b53804eSReed 		}
27170b53804eSReed 
27180b53804eSReed 		tgtmap = iport->iss_tgtmap;
27190b53804eSReed 		ASSERT(tgtmap);
27200b53804eSReed 		if (scsi_hba_tgtmap_set_flush(tgtmap) != DDI_SUCCESS) {
27210b53804eSReed 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
27220b53804eSReed 			    "%s: Failed set_flush on tgtmap 0x%p", __func__,
27230b53804eSReed 			    (void *)tgtmap);
27240b53804eSReed 		} else {
27250b53804eSReed 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
27260b53804eSReed 			    "%s: set flush on tgtmap 0x%p", __func__,
27270b53804eSReed 			    (void *)tgtmap);
27280b53804eSReed 		}
27290b53804eSReed 	}
27300b53804eSReed 	rw_exit(&pwp->iports_lock);
27310b53804eSReed }
27320b53804eSReed 
2733601c90f1SSrikanth, Ramana /*
2734601c90f1SSrikanth, Ramana  * Report current observations to SCSA.
2735601c90f1SSrikanth, Ramana  */
2736601c90f1SSrikanth, Ramana static boolean_t
2737601c90f1SSrikanth, Ramana pmcs_report_observations(pmcs_hw_t *pwp)
2738601c90f1SSrikanth, Ramana {
2739601c90f1SSrikanth, Ramana 	pmcs_iport_t		*iport;
2740601c90f1SSrikanth, Ramana 	scsi_hba_tgtmap_t	*tgtmap;
2741601c90f1SSrikanth, Ramana 	char			*ap;
2742601c90f1SSrikanth, Ramana 	pmcs_phy_t		*pptr;
2743601c90f1SSrikanth, Ramana 	uint64_t		wwn;
27444c06356bSdh 
27454c06356bSdh 	/*
2746601c90f1SSrikanth, Ramana 	 * Observation is stable, report what we currently see to the tgtmaps
2747601c90f1SSrikanth, Ramana 	 * for delta processing.
27484c06356bSdh 	 */
27494c06356bSdh 	pptr = pwp->root_phys;
27504c06356bSdh 
27514c06356bSdh 	while (pptr) {
27524c06356bSdh 		pmcs_lock_phy(pptr);
27534c06356bSdh 
27544c06356bSdh 		/*
27554c06356bSdh 		 * Skip PHYs that have nothing attached or are dead.
27564c06356bSdh 		 */
27574c06356bSdh 		if ((pptr->dtype == NOTHING) || pptr->dead) {
27584c06356bSdh 			pmcs_unlock_phy(pptr);
27594c06356bSdh 			pptr = pptr->sibling;
27604c06356bSdh 			continue;
27614c06356bSdh 		}
27624c06356bSdh 
27634c06356bSdh 		if (pptr->changed) {
2764c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
27654c06356bSdh 			    "%s: oops, PHY %s changed; restart discovery",
27664c06356bSdh 			    __func__, pptr->path);
27674c06356bSdh 			pmcs_unlock_phy(pptr);
27684c06356bSdh 			return (B_FALSE);
27694c06356bSdh 		}
27704c06356bSdh 
27714c06356bSdh 		/*
27724c06356bSdh 		 * Get the iport for this root PHY, then call the helper
27734c06356bSdh 		 * to report observations for this iport's targets
27744c06356bSdh 		 */
27759aed1621SDavid Hollister 		wwn = pmcs_barray2wwn(pptr->sas_address);
27769aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
27779aed1621SDavid Hollister 		iport = pmcs_get_iport_by_wwn(pwp, wwn);
27784c06356bSdh 		if (iport == NULL) {
27794c06356bSdh 			/* No iport for this tgt */
2780c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
2781c3bc407cSdh 			    "%s: no iport for this target", __func__);
27824c06356bSdh 			pptr = pptr->sibling;
27834c06356bSdh 			continue;
27844c06356bSdh 		}
27854c06356bSdh 
27869aed1621SDavid Hollister 		pmcs_lock_phy(pptr);
27874c06356bSdh 		if (!iport->report_skip) {
27884c06356bSdh 			if (pmcs_report_iport_observations(
27894c06356bSdh 			    pwp, iport, pptr) == B_FALSE) {
27904c06356bSdh 				pmcs_rele_iport(iport);
27914c06356bSdh 				pmcs_unlock_phy(pptr);
27924c06356bSdh 				return (B_FALSE);
27934c06356bSdh 			}
27944c06356bSdh 		}
27954c06356bSdh 		pmcs_rele_iport(iport);
27964c06356bSdh 		pmcs_unlock_phy(pptr);
27974c06356bSdh 		pptr = pptr->sibling;
27984c06356bSdh 	}
27994c06356bSdh 
28004c06356bSdh 	/*
28014c06356bSdh 	 * The observation is complete, end sets. Note we will skip any
28024c06356bSdh 	 * iports that are active, but have no PHYs in them (i.e. awaiting
28034c06356bSdh 	 * unconfigure). Set to restart discovery if we find this.
28044c06356bSdh 	 */
28054c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
28064c06356bSdh 	for (iport = list_head(&pwp->iports);
28074c06356bSdh 	    iport != NULL;
28084c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
28094c06356bSdh 
28104c06356bSdh 		if (iport->report_skip)
28114c06356bSdh 			continue;		/* skip set_end */
28124c06356bSdh 
28134c06356bSdh 		tgtmap = iport->iss_tgtmap;
28144c06356bSdh 		ASSERT(tgtmap);
28154c06356bSdh 		if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
2816c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
28174c06356bSdh 			    "%s: cannot set_end tgtmap ", __func__);
28184c06356bSdh 			rw_exit(&pwp->iports_lock);
28194c06356bSdh 			return (B_FALSE);
28204c06356bSdh 		}
2821c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
2822c3bc407cSdh 		    "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap);
28234c06356bSdh 	}
28244c06356bSdh 
28254c06356bSdh 	/*
28264c06356bSdh 	 * Now that discovery is complete, set up the necessary
28274c06356bSdh 	 * DDI properties on each iport node.
28284c06356bSdh 	 */
28294c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
28304c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
28314c06356bSdh 		/* Set up the 'attached-port' property on the iport */
28324c06356bSdh 		ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
28334c06356bSdh 		mutex_enter(&iport->lock);
28344c06356bSdh 		pptr = iport->pptr;
28354c06356bSdh 		mutex_exit(&iport->lock);
28364c06356bSdh 		if (pptr == NULL) {
28374c06356bSdh 			/*
28384c06356bSdh 			 * This iport is down, but has not been
28394c06356bSdh 			 * removed from our list (unconfigured).
28404c06356bSdh 			 * Set our value to '0'.
28414c06356bSdh 			 */
28424c06356bSdh 			(void) snprintf(ap, 1, "%s", "0");
28434c06356bSdh 		} else {
28444c06356bSdh 			/* Otherwise, set it to remote phy's wwn */
28454c06356bSdh 			pmcs_lock_phy(pptr);
28464c06356bSdh 			wwn = pmcs_barray2wwn(pptr->sas_address);
28474c06356bSdh 			(void) scsi_wwn_to_wwnstr(wwn, 1, ap);
28484c06356bSdh 			pmcs_unlock_phy(pptr);
28494c06356bSdh 		}
28504c06356bSdh 		if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
2851499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) {
2852c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed "
2853c3bc407cSdh 			    "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
28544c06356bSdh 			    __func__);
28554c06356bSdh 		}
28564c06356bSdh 		kmem_free(ap, PMCS_MAX_UA_SIZE);
28574c06356bSdh 	}
28584c06356bSdh 	rw_exit(&pwp->iports_lock);
28594c06356bSdh 
28604c06356bSdh 	return (B_TRUE);
28614c06356bSdh }
28624c06356bSdh 
28634c06356bSdh /*
28644c06356bSdh  * Report observations into a particular iport's target map
28654c06356bSdh  *
28664c06356bSdh  * Called with phyp (and all descendents) locked
28674c06356bSdh  */
28684c06356bSdh static boolean_t
28694c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
28704c06356bSdh     pmcs_phy_t *phyp)
28714c06356bSdh {
28724c06356bSdh 	pmcs_phy_t		*lphyp;
28734c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
28744c06356bSdh 	scsi_tgtmap_tgt_type_t	tgt_type;
28754c06356bSdh 	char			*ua;
28764c06356bSdh 	uint64_t		wwn;
28774c06356bSdh 
28784c06356bSdh 	tgtmap = iport->iss_tgtmap;
28794c06356bSdh 	ASSERT(tgtmap);
28804c06356bSdh 
28814c06356bSdh 	lphyp = phyp;
28824c06356bSdh 	while (lphyp) {
28834c06356bSdh 		switch (lphyp->dtype) {
28844c06356bSdh 		default:		/* Skip unknown PHYs. */
28854c06356bSdh 			/* for non-root phys, skip to sibling */
28864c06356bSdh 			goto next_phy;
28874c06356bSdh 
28884c06356bSdh 		case SATA:
28894c06356bSdh 		case SAS:
28904c06356bSdh 			tgt_type = SCSI_TGT_SCSI_DEVICE;
28914c06356bSdh 			break;
28924c06356bSdh 
28934c06356bSdh 		case EXPANDER:
28944c06356bSdh 			tgt_type = SCSI_TGT_SMP_DEVICE;
28954c06356bSdh 			break;
28964c06356bSdh 		}
28974c06356bSdh 
28989aed1621SDavid Hollister 		if (lphyp->dead || !lphyp->configured) {
28994c06356bSdh 			goto next_phy;
29004c06356bSdh 		}
29014c06356bSdh 
2902a25672a1SDavid Hollister 		/*
2903a25672a1SDavid Hollister 		 * Validate the PHY's SAS address
2904a25672a1SDavid Hollister 		 */
2905a25672a1SDavid Hollister 		if (((lphyp->sas_address[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
2906a25672a1SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_ERR, lphyp, NULL,
2907a25672a1SDavid Hollister 			    "PHY 0x%p (%s) has invalid SAS address; "
2908a25672a1SDavid Hollister 			    "will not enumerate", (void *)lphyp, lphyp->path);
2909a25672a1SDavid Hollister 			goto next_phy;
2910a25672a1SDavid Hollister 		}
2911a25672a1SDavid Hollister 
29124c06356bSdh 		wwn = pmcs_barray2wwn(lphyp->sas_address);
29134c06356bSdh 		ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
29144c06356bSdh 
2915c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL,
29164c06356bSdh 		    "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
29174c06356bSdh 		    ua, (void *)tgtmap, (void*)lphyp);
29184c06356bSdh 
29194c06356bSdh 		if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
29204c06356bSdh 		    DDI_SUCCESS) {
2921c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,  NULL, NULL,
29224c06356bSdh 			    "%s: failed to add address %s", __func__, ua);
29234c06356bSdh 			scsi_free_wwnstr(ua);
29244c06356bSdh 			return (B_FALSE);
29254c06356bSdh 		}
29264c06356bSdh 		scsi_free_wwnstr(ua);
29274c06356bSdh 
29284c06356bSdh 		if (lphyp->children) {
29294c06356bSdh 			if (pmcs_report_iport_observations(pwp, iport,
29304c06356bSdh 			    lphyp->children) == B_FALSE) {
29314c06356bSdh 				return (B_FALSE);
29324c06356bSdh 			}
29334c06356bSdh 		}
29344c06356bSdh 
29354c06356bSdh 		/* for non-root phys, report siblings too */
29364c06356bSdh next_phy:
29374c06356bSdh 		if (IS_ROOT_PHY(lphyp)) {
29384c06356bSdh 			lphyp = NULL;
29394c06356bSdh 		} else {
29404c06356bSdh 			lphyp = lphyp->sibling;
29414c06356bSdh 		}
29424c06356bSdh 	}
29434c06356bSdh 
29444c06356bSdh 	return (B_TRUE);
29454c06356bSdh }
29464c06356bSdh 
29474c06356bSdh /*
29484c06356bSdh  * Check for and configure new devices.
29494c06356bSdh  *
29504c06356bSdh  * If the changed device is a SATA device, add a SATA device.
29514c06356bSdh  *
29524c06356bSdh  * If the changed device is a SAS device, add a SAS device.
29534c06356bSdh  *
29544c06356bSdh  * If the changed device is an EXPANDER device, do a REPORT
29554c06356bSdh  * GENERAL SMP command to find out the number of contained phys.
29564c06356bSdh  *
29574c06356bSdh  * For each number of contained phys, allocate a phy, do a
29584c06356bSdh  * DISCOVERY SMP command to find out what kind of device it
29594c06356bSdh  * is and add it to the linked list of phys on the *next* level.
29604c06356bSdh  *
29614c06356bSdh  * NOTE: pptr passed in by the caller will be a root PHY
29624c06356bSdh  */
29634c06356bSdh static int
29644c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
29654c06356bSdh {
29664c06356bSdh 	int rval = 0;
29674c06356bSdh 	pmcs_iport_t *iport;
29684c06356bSdh 	pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
29699aed1621SDavid Hollister 	uint64_t wwn;
29704c06356bSdh 
29714c06356bSdh 	/*
29724c06356bSdh 	 * First, walk through each PHY at this level
29734c06356bSdh 	 */
29744c06356bSdh 	while (pptr) {
29754c06356bSdh 		pmcs_lock_phy(pptr);
29764c06356bSdh 		pnext = pptr->sibling;
29774c06356bSdh 
29784c06356bSdh 		/*
29794c06356bSdh 		 * Set the new dtype if it has changed
29804c06356bSdh 		 */
29814c06356bSdh 		if ((pptr->pend_dtype != NEW) &&
29824c06356bSdh 		    (pptr->pend_dtype != pptr->dtype)) {
29834c06356bSdh 			pptr->dtype = pptr->pend_dtype;
29844c06356bSdh 		}
29854c06356bSdh 
29864c06356bSdh 		if (pptr->changed == 0 || pptr->dead || pptr->configured) {
29874c06356bSdh 			goto next_phy;
29884c06356bSdh 		}
29894c06356bSdh 
29904c06356bSdh 		/*
29914c06356bSdh 		 * Confirm that this target's iport is configured
29924c06356bSdh 		 */
29934c06356bSdh 		root_phy = pmcs_get_root_phy(pptr);
29949aed1621SDavid Hollister 		wwn = pmcs_barray2wwn(root_phy->sas_address);
29959aed1621SDavid Hollister 		pmcs_unlock_phy(pptr);
29969aed1621SDavid Hollister 		iport = pmcs_get_iport_by_wwn(pwp, wwn);
29974c06356bSdh 		if (iport == NULL) {
29984c06356bSdh 			/* No iport for this tgt, restart */
2999c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
30004c06356bSdh 			    "%s: iport not yet configured, "
30014c06356bSdh 			    "retry discovery", __func__);
30024c06356bSdh 			pnext = NULL;
30034c06356bSdh 			rval = -1;
30049aed1621SDavid Hollister 			pmcs_lock_phy(pptr);
30054c06356bSdh 			goto next_phy;
30064c06356bSdh 		}
30074c06356bSdh 
30089aed1621SDavid Hollister 		pmcs_lock_phy(pptr);
30094c06356bSdh 		switch (pptr->dtype) {
30104c06356bSdh 		case NOTHING:
30114c06356bSdh 			pptr->changed = 0;
30124c06356bSdh 			break;
30134c06356bSdh 		case SATA:
30144c06356bSdh 		case SAS:
30154c06356bSdh 			pptr->iport = iport;
30164c06356bSdh 			pmcs_new_tport(pwp, pptr);
30174c06356bSdh 			break;
30184c06356bSdh 		case EXPANDER:
30194c06356bSdh 			pmcs_configure_expander(pwp, pptr, iport);
30204c06356bSdh 			break;
30214c06356bSdh 		}
30224c06356bSdh 		pmcs_rele_iport(iport);
30234c06356bSdh 
30244c06356bSdh 		mutex_enter(&pwp->config_lock);
30254c06356bSdh 		if (pwp->config_changed) {
30264c06356bSdh 			mutex_exit(&pwp->config_lock);
30274c06356bSdh 			pnext = NULL;
30284c06356bSdh 			goto next_phy;
30294c06356bSdh 		}
30304c06356bSdh 		mutex_exit(&pwp->config_lock);
30314c06356bSdh 
30324c06356bSdh next_phy:
30334c06356bSdh 		pmcs_unlock_phy(pptr);
30344c06356bSdh 		pptr = pnext;
30354c06356bSdh 	}
30364c06356bSdh 
30374c06356bSdh 	if (rval != 0) {
30384c06356bSdh 		return (rval);
30394c06356bSdh 	}
30404c06356bSdh 
30414c06356bSdh 	/*
30424c06356bSdh 	 * Now walk through each PHY again, recalling ourselves if they
30434c06356bSdh 	 * have children
30444c06356bSdh 	 */
30454c06356bSdh 	pptr = orig_pptr;
30464c06356bSdh 	while (pptr) {
30474c06356bSdh 		pmcs_lock_phy(pptr);
30484c06356bSdh 		pnext = pptr->sibling;
30494c06356bSdh 		pchild = pptr->children;
30504c06356bSdh 		pmcs_unlock_phy(pptr);
30514c06356bSdh 
30524c06356bSdh 		if (pchild) {
30534c06356bSdh 			rval = pmcs_configure_new_devices(pwp, pchild);
30544c06356bSdh 			if (rval != 0) {
30554c06356bSdh 				break;
30564c06356bSdh 			}
30574c06356bSdh 		}
30584c06356bSdh 
30594c06356bSdh 		pptr = pnext;
30604c06356bSdh 	}
30614c06356bSdh 
30624c06356bSdh 	return (rval);
30634c06356bSdh }
30644c06356bSdh 
30654c06356bSdh /*
30664c06356bSdh  * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
30674c06356bSdh  * mark them all as not changed.
30684c06356bSdh  *
30694c06356bSdh  * Called with parent PHY locked.
30704c06356bSdh  */
30714c06356bSdh void
30724c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
30734c06356bSdh     int level)
30744c06356bSdh {
30754c06356bSdh 	pmcs_phy_t *pptr;
30764c06356bSdh 
30774c06356bSdh 	if (level == 0) {
30784c06356bSdh 		if (changed) {
30794c06356bSdh 			PHY_CHANGED(pwp, parent);
30804c06356bSdh 		} else {
30814c06356bSdh 			parent->changed = 0;
30824c06356bSdh 		}
30834c06356bSdh 		if (parent->dtype == EXPANDER && parent->level) {
30844c06356bSdh 			parent->width = 1;
30854c06356bSdh 		}
30864c06356bSdh 		if (parent->children) {
30874c06356bSdh 			pmcs_set_changed(pwp, parent->children, changed,
30884c06356bSdh 			    level + 1);
30894c06356bSdh 		}
30904c06356bSdh 	} else {
30914c06356bSdh 		pptr = parent;
30924c06356bSdh 		while (pptr) {
30934c06356bSdh 			if (changed) {
30944c06356bSdh 				PHY_CHANGED(pwp, pptr);
30954c06356bSdh 			} else {
30964c06356bSdh 				pptr->changed = 0;
30974c06356bSdh 			}
30984c06356bSdh 			if (pptr->dtype == EXPANDER && pptr->level) {
30994c06356bSdh 				pptr->width = 1;
31004c06356bSdh 			}
31014c06356bSdh 			if (pptr->children) {
31024c06356bSdh 				pmcs_set_changed(pwp, pptr->children, changed,
31034c06356bSdh 				    level + 1);
31044c06356bSdh 			}
31054c06356bSdh 			pptr = pptr->sibling;
31064c06356bSdh 		}
31074c06356bSdh 	}
31084c06356bSdh }
31094c06356bSdh 
31104c06356bSdh /*
31114c06356bSdh  * Take the passed phy mark it and its descendants as dead.
31124c06356bSdh  * Fire up reconfiguration to abort commands and bury it.
31134c06356bSdh  *
31144c06356bSdh  * Called with the parent PHY locked.
31154c06356bSdh  */
31164c06356bSdh void
31174c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
31184c06356bSdh {
31194c06356bSdh 	pmcs_phy_t *pptr = parent;
31204c06356bSdh 
31214c06356bSdh 	while (pptr) {
31224c06356bSdh 		pptr->link_rate = 0;
31234c06356bSdh 		pptr->abort_sent = 0;
31244c06356bSdh 		pptr->abort_pending = 1;
31254c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
31264c06356bSdh 		pptr->need_rl_ext = 0;
31274c06356bSdh 
31284c06356bSdh 		if (pptr->dead == 0) {
31294c06356bSdh 			PHY_CHANGED(pwp, pptr);
31304c06356bSdh 			RESTART_DISCOVERY(pwp);
31314c06356bSdh 		}
31324c06356bSdh 
31334c06356bSdh 		pptr->dead = 1;
31344c06356bSdh 
31354c06356bSdh 		if (pptr->children) {
31364c06356bSdh 			pmcs_kill_changed(pwp, pptr->children, level + 1);
31374c06356bSdh 		}
31384c06356bSdh 
31394c06356bSdh 		/*
31404c06356bSdh 		 * Only kill siblings at level > 0
31414c06356bSdh 		 */
31424c06356bSdh 		if (level == 0) {
31434c06356bSdh 			return;
31444c06356bSdh 		}
31454c06356bSdh 
31464c06356bSdh 		pptr = pptr->sibling;
31474c06356bSdh 	}
31484c06356bSdh }
31494c06356bSdh 
31504c06356bSdh /*
31514c06356bSdh  * Go through every PHY and clear any that are dead (unless they're expanders)
31524c06356bSdh  */
31534c06356bSdh static void
31544c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
31554c06356bSdh {
31564c06356bSdh 	pmcs_phy_t *pnext, *phyp;
31574c06356bSdh 
31584c06356bSdh 	phyp = pptr;
31594c06356bSdh 	while (phyp) {
31604c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
31614c06356bSdh 			pmcs_lock_phy(phyp);
31624c06356bSdh 		}
31634c06356bSdh 
31644c06356bSdh 		if ((phyp->dtype != EXPANDER) && phyp->dead) {
31654c06356bSdh 			pmcs_clear_phy(pwp, phyp);
31664c06356bSdh 		}
31674c06356bSdh 
31684c06356bSdh 		if (phyp->children) {
31694c06356bSdh 			pmcs_clear_phys(pwp, phyp->children);
31704c06356bSdh 		}
31714c06356bSdh 
31724c06356bSdh 		pnext = phyp->sibling;
31734c06356bSdh 
31744c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
31754c06356bSdh 			pmcs_unlock_phy(phyp);
31764c06356bSdh 		}
31774c06356bSdh 
31784c06356bSdh 		phyp = pnext;
31794c06356bSdh 	}
31804c06356bSdh }
31814c06356bSdh 
31824c06356bSdh /*
31834c06356bSdh  * Clear volatile parts of a phy.  Called with PHY locked.
31844c06356bSdh  */
31854c06356bSdh void
31864c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
31874c06356bSdh {
3188c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s",
3189c3bc407cSdh 	    __func__, pptr->path);
31904c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
31914c06356bSdh 	/* keep sibling */
31924c06356bSdh 	/* keep children */
31934c06356bSdh 	/* keep parent */
31944c06356bSdh 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
31954c06356bSdh 	/* keep hw_event_ack */
31964c06356bSdh 	pptr->ncphy = 0;
31974c06356bSdh 	/* keep phynum */
31984c06356bSdh 	pptr->width = 0;
31994c06356bSdh 	pptr->ds_recovery_retries = 0;
3200af685682SSrikanth, Ramana 	pptr->ds_prev_good_recoveries = 0;
3201af685682SSrikanth, Ramana 	pptr->last_good_recovery = 0;
3202af685682SSrikanth, Ramana 	pptr->prev_recovery = 0;
3203145e0143Sdh 
32044c06356bSdh 	/* keep dtype */
32054c06356bSdh 	pptr->config_stop = 0;
32064c06356bSdh 	pptr->spinup_hold = 0;
32074c06356bSdh 	pptr->atdt = 0;
32084c06356bSdh 	/* keep portid */
32094c06356bSdh 	pptr->link_rate = 0;
32104c06356bSdh 	pptr->valid_device_id = 0;
32114c06356bSdh 	pptr->abort_sent = 0;
32124c06356bSdh 	pptr->abort_pending = 0;
32134c06356bSdh 	pptr->need_rl_ext = 0;
32144c06356bSdh 	pptr->subsidiary = 0;
32154c06356bSdh 	pptr->configured = 0;
3216601c90f1SSrikanth, Ramana 	pptr->deregister_wait = 0;
32179aed1621SDavid Hollister 	pptr->reenumerate = 0;
32184c06356bSdh 	/* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
32194c06356bSdh 	/* XXX: What about directly attached disks? */
32204c06356bSdh 	if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
32214c06356bSdh 		pptr->dead = 1;
32224c06356bSdh 	pptr->changed = 0;
32234c06356bSdh 	/* keep SAS address */
32244c06356bSdh 	/* keep path */
32254c06356bSdh 	/* keep ref_count */
32264c06356bSdh 	/* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
32274c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
322873a3eccdSDavid Hollister 		pptr->last_iport = pptr->iport;
32294c06356bSdh 		pptr->iport = NULL;
32304c06356bSdh 	}
3231b18a19c2SJesse Butler 	/* keep target */
32324c06356bSdh }
32334c06356bSdh 
32344c06356bSdh /*
32354c06356bSdh  * Allocate softstate for this target if there isn't already one.  If there
32364c06356bSdh  * is, just redo our internal configuration.  If it is actually "new", we'll
32374c06356bSdh  * soon get a tran_tgt_init for it.
32384c06356bSdh  *
32394c06356bSdh  * Called with PHY locked.
32404c06356bSdh  */
32414c06356bSdh static void
32424c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
32434c06356bSdh {
3244c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s",
3245c3bc407cSdh 	    __func__, (void *)pptr, pptr->path);
32464c06356bSdh 
32474c06356bSdh 	if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
32484c06356bSdh 		/*
32494c06356bSdh 		 * If the config failed, mark the PHY as changed.
32504c06356bSdh 		 */
32514c06356bSdh 		PHY_CHANGED(pwp, pptr);
3252c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
32534c06356bSdh 		    "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
32544c06356bSdh 		    (void *)pptr);
32554c06356bSdh 		return;
32564c06356bSdh 	}
32574c06356bSdh 
32584c06356bSdh 	/* Mark PHY as no longer changed */
32594c06356bSdh 	pptr->changed = 0;
32604c06356bSdh 
32614c06356bSdh 	/*
326273a3eccdSDavid Hollister 	 * If the PHY has no target pointer:
326373a3eccdSDavid Hollister 	 *
326473a3eccdSDavid Hollister 	 * If it's a root PHY, see if another PHY in the iport holds the
326573a3eccdSDavid Hollister 	 * target pointer (primary PHY changed).  If so, move it over.
326673a3eccdSDavid Hollister 	 *
326773a3eccdSDavid Hollister 	 * If it's not a root PHY, see if there's a PHY on the dead_phys
326873a3eccdSDavid Hollister 	 * list that matches.
32694c06356bSdh 	 */
32704c06356bSdh 	if (pptr->target == NULL) {
327173a3eccdSDavid Hollister 		if (IS_ROOT_PHY(pptr)) {
327273a3eccdSDavid Hollister 			pmcs_phy_t *rphy = pwp->root_phys;
327373a3eccdSDavid Hollister 
327473a3eccdSDavid Hollister 			while (rphy) {
327573a3eccdSDavid Hollister 				if (rphy == pptr) {
327673a3eccdSDavid Hollister 					rphy = rphy->sibling;
327773a3eccdSDavid Hollister 					continue;
327873a3eccdSDavid Hollister 				}
327973a3eccdSDavid Hollister 
328073a3eccdSDavid Hollister 				mutex_enter(&rphy->phy_lock);
328173a3eccdSDavid Hollister 				if ((rphy->iport == pptr->iport) &&
328273a3eccdSDavid Hollister 				    (rphy->target != NULL)) {
328373a3eccdSDavid Hollister 					mutex_enter(&rphy->target->statlock);
328473a3eccdSDavid Hollister 					pptr->target = rphy->target;
328573a3eccdSDavid Hollister 					rphy->target = NULL;
328673a3eccdSDavid Hollister 					pptr->target->phy = pptr;
328773a3eccdSDavid Hollister 					/* The target is now on pptr */
328873a3eccdSDavid Hollister 					mutex_exit(&pptr->target->statlock);
328973a3eccdSDavid Hollister 					mutex_exit(&rphy->phy_lock);
329073a3eccdSDavid Hollister 					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
329173a3eccdSDavid Hollister 					    pptr, pptr->target,
329273a3eccdSDavid Hollister 					    "%s: Moved target from %s to %s",
329373a3eccdSDavid Hollister 					    __func__, rphy->path, pptr->path);
329473a3eccdSDavid Hollister 					break;
329573a3eccdSDavid Hollister 				}
329673a3eccdSDavid Hollister 				mutex_exit(&rphy->phy_lock);
329773a3eccdSDavid Hollister 
329873a3eccdSDavid Hollister 				rphy = rphy->sibling;
329973a3eccdSDavid Hollister 			}
330073a3eccdSDavid Hollister 		} else {
330173a3eccdSDavid Hollister 			pmcs_reap_dead_phy(pptr);
330273a3eccdSDavid Hollister 		}
33034c06356bSdh 	}
33044c06356bSdh 
33054c06356bSdh 	/*
33064c06356bSdh 	 * Only assign the device if there is a target for this PHY with a
33074c06356bSdh 	 * matching SAS address.  If an iport is disconnected from one piece
33084c06356bSdh 	 * of storage and connected to another within the iport stabilization
33094c06356bSdh 	 * time, we can get the PHY/target mismatch situation.
33104c06356bSdh 	 *
33114c06356bSdh 	 * Otherwise, it'll get done in tran_tgt_init.
33124c06356bSdh 	 */
33134c06356bSdh 	if (pptr->target) {
33144c06356bSdh 		mutex_enter(&pptr->target->statlock);
33154c06356bSdh 		if (pmcs_phy_target_match(pptr) == B_FALSE) {
33164c06356bSdh 			mutex_exit(&pptr->target->statlock);
33174c06356bSdh 			if (!IS_ROOT_PHY(pptr)) {
33184c06356bSdh 				pmcs_dec_phy_ref_count(pptr);
33194c06356bSdh 			}
3320c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
33214c06356bSdh 			    "%s: Not assigning existing tgt %p for PHY %p "
33224c06356bSdh 			    "(WWN mismatch)", __func__, (void *)pptr->target,
33234c06356bSdh 			    (void *)pptr);
33244c06356bSdh 			pptr->target = NULL;
33254c06356bSdh 			return;
33264c06356bSdh 		}
33274c06356bSdh 
33284c06356bSdh 		if (!pmcs_assign_device(pwp, pptr->target)) {
3329c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
33304c06356bSdh 			    "%s: pmcs_assign_device failed for target 0x%p",
33314c06356bSdh 			    __func__, (void *)pptr->target);
33324c06356bSdh 		}
33334c06356bSdh 		mutex_exit(&pptr->target->statlock);
33344c06356bSdh 	}
33354c06356bSdh }
33364c06356bSdh 
33374c06356bSdh /*
33384c06356bSdh  * Called with PHY lock held.
33394c06356bSdh  */
33404c06356bSdh static boolean_t
33414c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
33424c06356bSdh {
33434c06356bSdh 	char *dtype;
33444c06356bSdh 
33454c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
33464c06356bSdh 
33474c06356bSdh 	/*
33484c06356bSdh 	 * Mark this device as no longer changed.
33494c06356bSdh 	 */
33504c06356bSdh 	pptr->changed = 0;
33514c06356bSdh 
33524c06356bSdh 	/*
33534c06356bSdh 	 * If we don't have a device handle, get one.
33544c06356bSdh 	 */
33554c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
33564c06356bSdh 		return (B_FALSE);
33574c06356bSdh 	}
33584c06356bSdh 
33594c06356bSdh 	pptr->configured = 1;
33604c06356bSdh 
33614c06356bSdh 	switch (pptr->dtype) {
33624c06356bSdh 	case SAS:
33634c06356bSdh 		dtype = "SAS";
33644c06356bSdh 		break;
33654c06356bSdh 	case SATA:
33664c06356bSdh 		dtype = "SATA";
33674c06356bSdh 		break;
33684c06356bSdh 	case EXPANDER:
33694c06356bSdh 		dtype = "SMP";
33704c06356bSdh 		break;
33714c06356bSdh 	default:
33724c06356bSdh 		dtype = "???";
33734c06356bSdh 	}
33744c06356bSdh 
3375c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s "
3376c3bc407cSdh 	    "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
33774c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
33784c06356bSdh 
33794c06356bSdh 	return (B_TRUE);
33804c06356bSdh }
33814c06356bSdh 
33824c06356bSdh /*
33834c06356bSdh  * Called with PHY locked
33844c06356bSdh  */
33854c06356bSdh static void
33864c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
33874c06356bSdh {
33884c06356bSdh 	pmcs_phy_t *ctmp, *clist = NULL, *cnext;
33894c06356bSdh 	int result, i, nphy = 0;
33904c06356bSdh 	boolean_t root_phy = B_FALSE;
33914c06356bSdh 
33924c06356bSdh 	ASSERT(iport);
33934c06356bSdh 
33944c06356bSdh 	/*
33954c06356bSdh 	 * Step 1- clear our "changed" bit. If we need to retry/restart due
33964c06356bSdh 	 * to resource shortages, we'll set it again. While we're doing
33974c06356bSdh 	 * configuration, other events may set it again as well.  If the PHY
33984c06356bSdh 	 * is a root PHY and is currently marked as having changed, reset the
33994c06356bSdh 	 * config_stop timer as well.
34004c06356bSdh 	 */
34014c06356bSdh 	if (IS_ROOT_PHY(pptr) && pptr->changed) {
34024c06356bSdh 		pptr->config_stop = ddi_get_lbolt() +
34034c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
34044c06356bSdh 	}
34054c06356bSdh 	pptr->changed = 0;
34064c06356bSdh 
34074c06356bSdh 	/*
34084c06356bSdh 	 * Step 2- make sure we don't overflow
34094c06356bSdh 	 */
34104c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
3411c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL,
34124c06356bSdh 		    "%s: SAS expansion tree too deep", __func__);
34134c06356bSdh 		return;
34144c06356bSdh 	}
34154c06356bSdh 
34164c06356bSdh 	/*
34174c06356bSdh 	 * Step 3- Check if this expander is part of a wide phy that has
34184c06356bSdh 	 * already been configured.
34194c06356bSdh 	 *
34204c06356bSdh 	 * This is known by checking this level for another EXPANDER device
34214c06356bSdh 	 * with the same SAS address and isn't already marked as a subsidiary
34224c06356bSdh 	 * phy and a parent whose SAS address is the same as our SAS address
34234c06356bSdh 	 * (if there are parents).
34244c06356bSdh 	 */
34254c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
34264c06356bSdh 		/*
34274c06356bSdh 		 * No need to lock the parent here because we're in discovery
34284c06356bSdh 		 * and the only time a PHY's children pointer can change is
34294c06356bSdh 		 * in discovery; either in pmcs_clear_expander (which has
34304c06356bSdh 		 * already been called) or here, down below.  Plus, trying to
34314c06356bSdh 		 * grab the parent's lock here can cause deadlock.
34324c06356bSdh 		 */
34334c06356bSdh 		ctmp = pptr->parent->children;
34344c06356bSdh 	} else {
34354c06356bSdh 		ctmp = pwp->root_phys;
34364c06356bSdh 		root_phy = B_TRUE;
34374c06356bSdh 	}
34384c06356bSdh 
34394c06356bSdh 	while (ctmp) {
34404c06356bSdh 		/*
34414c06356bSdh 		 * If we've checked all PHYs up to pptr, we stop. Otherwise,
34424c06356bSdh 		 * we'll be checking for a primary PHY with a higher PHY
34434c06356bSdh 		 * number than pptr, which will never happen.  The primary
34444c06356bSdh 		 * PHY on non-root expanders will ALWAYS be the lowest
34454c06356bSdh 		 * numbered PHY.
34464c06356bSdh 		 */
34474c06356bSdh 		if (ctmp == pptr) {
34484c06356bSdh 			break;
34494c06356bSdh 		}
34504c06356bSdh 
34514c06356bSdh 		/*
34524c06356bSdh 		 * If pptr and ctmp are root PHYs, just grab the mutex on
34534c06356bSdh 		 * ctmp.  No need to lock the entire tree.  If they are not
34544c06356bSdh 		 * root PHYs, there is no need to lock since a non-root PHY's
34554c06356bSdh 		 * SAS address and other characteristics can only change in
34564c06356bSdh 		 * discovery anyway.
34574c06356bSdh 		 */
34584c06356bSdh 		if (root_phy) {
34594c06356bSdh 			mutex_enter(&ctmp->phy_lock);
34604c06356bSdh 		}
34614c06356bSdh 
34624c06356bSdh 		if (ctmp->dtype == EXPANDER && ctmp->width &&
34634c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
34644c06356bSdh 			int widephy = 0;
34654c06356bSdh 			/*
34664c06356bSdh 			 * If these phys are not root PHYs, compare their SAS
34674c06356bSdh 			 * addresses too.
34684c06356bSdh 			 */
34694c06356bSdh 			if (!root_phy) {
34704c06356bSdh 				if (memcmp(ctmp->parent->sas_address,
34714c06356bSdh 				    pptr->parent->sas_address, 8) == 0) {
34724c06356bSdh 					widephy = 1;
34734c06356bSdh 				}
34744c06356bSdh 			} else {
34754c06356bSdh 				widephy = 1;
34764c06356bSdh 			}
34774c06356bSdh 			if (widephy) {
34784c06356bSdh 				ctmp->width++;
34794c06356bSdh 				pptr->subsidiary = 1;
3480499cfd15SDavid Hollister 
3481499cfd15SDavid Hollister 				/*
3482499cfd15SDavid Hollister 				 * Update the primary PHY's attached-port-pm
3483499cfd15SDavid Hollister 				 * and target-port-pm information with the info
3484499cfd15SDavid Hollister 				 * from this subsidiary
3485499cfd15SDavid Hollister 				 */
3486499cfd15SDavid Hollister 				pmcs_update_phy_pm_props(ctmp,
3487499cfd15SDavid Hollister 				    pptr->att_port_pm_tmp,
3488499cfd15SDavid Hollister 				    pptr->tgt_port_pm_tmp, B_TRUE);
3489499cfd15SDavid Hollister 
3490c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
3491c3bc407cSdh 				    "%s: PHY %s part of wide PHY %s "
3492c3bc407cSdh 				    "(now %d wide)", __func__, pptr->path,
3493c3bc407cSdh 				    ctmp->path, ctmp->width);
34944c06356bSdh 				if (root_phy) {
34954c06356bSdh 					mutex_exit(&ctmp->phy_lock);
34964c06356bSdh 				}
34974c06356bSdh 				return;
34984c06356bSdh 			}
34994c06356bSdh 		}
35004c06356bSdh 
35014c06356bSdh 		cnext = ctmp->sibling;
35024c06356bSdh 		if (root_phy) {
35034c06356bSdh 			mutex_exit(&ctmp->phy_lock);
35044c06356bSdh 		}
35054c06356bSdh 		ctmp = cnext;
35064c06356bSdh 	}
35074c06356bSdh 
35084c06356bSdh 	/*
35094c06356bSdh 	 * Step 4- If we don't have a device handle, get one.  Since this
35104c06356bSdh 	 * is the primary PHY, make sure subsidiary is cleared.
35114c06356bSdh 	 */
35124c06356bSdh 	pptr->subsidiary = 0;
35136745c559SJesse Butler 	pptr->iport = iport;
35144c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
35154c06356bSdh 		goto out;
35164c06356bSdh 	}
3517c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s "
35184c06356bSdh 	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
35194c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
35204c06356bSdh 
35214c06356bSdh 	/*
35224c06356bSdh 	 * Step 5- figure out how many phys are in this expander.
35234c06356bSdh 	 */
35244c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
35254c06356bSdh 	if (nphy <= 0) {
35264c06356bSdh 		if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
35274c06356bSdh 			PHY_CHANGED(pwp, pptr);
35284c06356bSdh 			RESTART_DISCOVERY(pwp);
35294c06356bSdh 		} else {
3530c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
35314c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
35324c06356bSdh 			    pptr->path);
35334c06356bSdh 			pptr->config_stop = 0;
35344c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
35354c06356bSdh 		}
35364c06356bSdh 		goto out;
35374c06356bSdh 	}
35384c06356bSdh 
35394c06356bSdh 	/*
35404c06356bSdh 	 * Step 6- Allocate a list of phys for this expander and figure out
35414c06356bSdh 	 * what each one is.
35424c06356bSdh 	 */
35434c06356bSdh 	for (i = 0; i < nphy; i++) {
35444c06356bSdh 		ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
35454c06356bSdh 		bzero(ctmp, sizeof (pmcs_phy_t));
35464c06356bSdh 		ctmp->device_id = PMCS_INVALID_DEVICE_ID;
35474c06356bSdh 		ctmp->sibling = clist;
35484c06356bSdh 		ctmp->pend_dtype = NEW;	/* Init pending dtype */
35494c06356bSdh 		ctmp->config_stop = ddi_get_lbolt() +
35504c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
35514c06356bSdh 		clist = ctmp;
35524c06356bSdh 	}
35534c06356bSdh 
35544c06356bSdh 	mutex_enter(&pwp->config_lock);
35554c06356bSdh 	if (pwp->config_changed) {
35564c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
35574c06356bSdh 		mutex_exit(&pwp->config_lock);
35584c06356bSdh 		/*
35594c06356bSdh 		 * Clean up the newly allocated PHYs and return
35604c06356bSdh 		 */
35614c06356bSdh 		while (clist) {
35624c06356bSdh 			ctmp = clist->sibling;
35634c06356bSdh 			kmem_cache_free(pwp->phy_cache, clist);
35644c06356bSdh 			clist = ctmp;
35654c06356bSdh 		}
35664c06356bSdh 		return;
35674c06356bSdh 	}
35684c06356bSdh 	mutex_exit(&pwp->config_lock);
35694c06356bSdh 
35704c06356bSdh 	/*
35714c06356bSdh 	 * Step 7- Now fill in the rest of the static portions of the phy.
35724c06356bSdh 	 */
35734c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
35744c06356bSdh 		ctmp->parent = pptr;
35754c06356bSdh 		ctmp->pwp = pwp;
35764c06356bSdh 		ctmp->level = pptr->level+1;
35774c06356bSdh 		ctmp->portid = pptr->portid;
35784c06356bSdh 		if (ctmp->tolerates_sas2) {
35794c06356bSdh 			ASSERT(i < SAS2_PHYNUM_MAX);
35804c06356bSdh 			ctmp->phynum = i & SAS2_PHYNUM_MASK;
35814c06356bSdh 		} else {
35824c06356bSdh 			ASSERT(i < SAS_PHYNUM_MAX);
35834c06356bSdh 			ctmp->phynum = i & SAS_PHYNUM_MASK;
35844c06356bSdh 		}
35854c06356bSdh 		pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
35864c06356bSdh 		pmcs_lock_phy(ctmp);
35874c06356bSdh 	}
35884c06356bSdh 
35894c06356bSdh 	/*
35904c06356bSdh 	 * Step 8- Discover things about each phy in the expander.
35914c06356bSdh 	 */
35924c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
35934c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, ctmp);
35944c06356bSdh 		if (result <= 0) {
35954c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
35964c06356bSdh 				PHY_CHANGED(pwp, pptr);
35974c06356bSdh 				RESTART_DISCOVERY(pwp);
35984c06356bSdh 			} else {
35994c06356bSdh 				pptr->config_stop = 0;
3600c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
36014c06356bSdh 				    "%s: Retries exhausted for %s, killing",
36024c06356bSdh 				    __func__, pptr->path);
36034c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
36044c06356bSdh 			}
36054c06356bSdh 			goto out;
36064c06356bSdh 		}
36074c06356bSdh 
36084c06356bSdh 		/* Set pend_dtype to dtype for 1st time initialization */
36094c06356bSdh 		ctmp->pend_dtype = ctmp->dtype;
36104c06356bSdh 	}
36114c06356bSdh 
36124c06356bSdh 	/*
36139aed1621SDavid Hollister 	 * Step 9: Install the new list on the next level. There should
36149aed1621SDavid Hollister 	 * typically be no children pointer on this PHY.  There is one known
36159aed1621SDavid Hollister 	 * case where this can happen, though.  If a root PHY goes down and
36169aed1621SDavid Hollister 	 * comes back up before discovery can run, we will fail to remove the
36179aed1621SDavid Hollister 	 * children from that PHY since it will no longer be marked dead.
36189aed1621SDavid Hollister 	 * However, in this case, all children should also be marked dead.  If
36199aed1621SDavid Hollister 	 * we see that, take those children and put them on the dead_phys list.
36204c06356bSdh 	 */
36214c06356bSdh 	if (pptr->children != NULL) {
36229aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
36239aed1621SDavid Hollister 		    "%s: Expander @ %s still has children: Clean up",
3624c3bc407cSdh 		    __func__, pptr->path);
36259aed1621SDavid Hollister 		pmcs_add_dead_phys(pwp, pptr->children);
36264c06356bSdh 	}
36274c06356bSdh 
36289aed1621SDavid Hollister 	/*
36299aed1621SDavid Hollister 	 * Set the new children pointer for this expander
36309aed1621SDavid Hollister 	 */
36319aed1621SDavid Hollister 	pptr->children = clist;
36324c06356bSdh 	clist = NULL;
36334c06356bSdh 	pptr->ncphy = nphy;
36344c06356bSdh 	pptr->configured = 1;
36354c06356bSdh 
36364c06356bSdh 	/*
36374c06356bSdh 	 * We only set width if we're greater than level 0.
36384c06356bSdh 	 */
36394c06356bSdh 	if (pptr->level) {
36404c06356bSdh 		pptr->width = 1;
36414c06356bSdh 	}
36424c06356bSdh 
36434c06356bSdh 	/*
36444c06356bSdh 	 * Now tell the rest of the world about us, as an SMP node.
36454c06356bSdh 	 */
36464c06356bSdh 	pptr->iport = iport;
36474c06356bSdh 	pmcs_new_tport(pwp, pptr);
36484c06356bSdh 
36494c06356bSdh out:
36504c06356bSdh 	while (clist) {
36514c06356bSdh 		ctmp = clist->sibling;
36524c06356bSdh 		pmcs_unlock_phy(clist);
36534c06356bSdh 		kmem_cache_free(pwp->phy_cache, clist);
36544c06356bSdh 		clist = ctmp;
36554c06356bSdh 	}
36564c06356bSdh }
36574c06356bSdh 
36584c06356bSdh /*
36594c06356bSdh  * 2. Check expanders marked changed (but not dead) to see if they still have
36604c06356bSdh  * the same number of phys and the same SAS address. Mark them, their subsidiary
36614c06356bSdh  * phys (if wide) and their descendents dead if anything has changed. Check the
36624c06356bSdh  * the devices they contain to see if *they* have changed. If they've changed
36634c06356bSdh  * from type NOTHING we leave them marked changed to be configured later
36644c06356bSdh  * (picking up a new SAS address and link rate if possible). Otherwise, any
36654c06356bSdh  * change in type, SAS address or removal of target role will cause us to
36664c06356bSdh  * mark them (and their descendents) as dead and cause any pending commands
36674c06356bSdh  * and associated devices to be removed.
36684c06356bSdh  *
36694c06356bSdh  * Called with PHY (pptr) locked.
36704c06356bSdh  */
36714c06356bSdh 
36724c06356bSdh static void
36734c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
36744c06356bSdh {
36754c06356bSdh 	int nphy, result;
36764c06356bSdh 	pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
36774c06356bSdh 	boolean_t kill_changed, changed;
36784c06356bSdh 
3679c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
36804c06356bSdh 	    "%s: check %s", __func__, pptr->path);
36814c06356bSdh 
36824c06356bSdh 	/*
36834c06356bSdh 	 * Step 1: Mark phy as not changed. We will mark it changed if we need
36844c06356bSdh 	 * to retry.
36854c06356bSdh 	 */
36864c06356bSdh 	pptr->changed = 0;
36874c06356bSdh 
36884c06356bSdh 	/*
36894c06356bSdh 	 * Reset the config_stop time. Although we're not actually configuring
36904c06356bSdh 	 * anything here, we do want some indication of when to give up trying
36914c06356bSdh 	 * if we can't communicate with the expander.
36924c06356bSdh 	 */
36934c06356bSdh 	pptr->config_stop = ddi_get_lbolt() +
36944c06356bSdh 	    drv_usectohz(PMCS_MAX_CONFIG_TIME);
36954c06356bSdh 
36964c06356bSdh 	/*
36974c06356bSdh 	 * Step 2: Figure out how many phys are in this expander. If
36984c06356bSdh 	 * pmcs_expander_get_nphy returns 0 we ran out of resources,
36994c06356bSdh 	 * so reschedule and try later. If it returns another error,
37004c06356bSdh 	 * just return.
37014c06356bSdh 	 */
37024c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
37034c06356bSdh 	if (nphy <= 0) {
37044c06356bSdh 		if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
37054c06356bSdh 			PHY_CHANGED(pwp, pptr);
37064c06356bSdh 			RESTART_DISCOVERY(pwp);
37074c06356bSdh 		} else {
37084c06356bSdh 			pptr->config_stop = 0;
3709c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37104c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
37114c06356bSdh 			    pptr->path);
37124c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
37134c06356bSdh 		}
37144c06356bSdh 		return;
37154c06356bSdh 	}
37164c06356bSdh 
37174c06356bSdh 	/*
37184c06356bSdh 	 * Step 3: If the number of phys don't agree, kill the old sub-tree.
37194c06356bSdh 	 */
37204c06356bSdh 	if (nphy != pptr->ncphy) {
3721c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37224c06356bSdh 		    "%s: number of contained phys for %s changed from %d to %d",
37234c06356bSdh 		    __func__, pptr->path, pptr->ncphy, nphy);
37244c06356bSdh 		/*
37254c06356bSdh 		 * Force a rescan of this expander after dead contents
37264c06356bSdh 		 * are cleared and removed.
37274c06356bSdh 		 */
37284c06356bSdh 		pmcs_kill_changed(pwp, pptr, 0);
37294c06356bSdh 		return;
37304c06356bSdh 	}
37314c06356bSdh 
37324c06356bSdh 	/*
37334c06356bSdh 	 * Step 4: if we're at the bottom of the stack, we're done
37344c06356bSdh 	 * (we can't have any levels below us)
37354c06356bSdh 	 */
37364c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
37374c06356bSdh 		return;
37384c06356bSdh 	}
37394c06356bSdh 
37404c06356bSdh 	/*
37414c06356bSdh 	 * Step 5: Discover things about each phy in this expander.  We do
37424c06356bSdh 	 * this by walking the current list of contained phys and doing a
37434c06356bSdh 	 * content discovery for it to a local phy.
37444c06356bSdh 	 */
37454c06356bSdh 	ctmp = pptr->children;
37464c06356bSdh 	ASSERT(ctmp);
37474c06356bSdh 	if (ctmp == NULL) {
3748c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37494c06356bSdh 		    "%s: No children attached to expander @ %s?", __func__,
37504c06356bSdh 		    pptr->path);
37514c06356bSdh 		return;
37524c06356bSdh 	}
37534c06356bSdh 
37544c06356bSdh 	while (ctmp) {
37554c06356bSdh 		/*
37564c06356bSdh 		 * Allocate a local PHY to contain the proposed new contents
37574c06356bSdh 		 * and link it to the rest of the local PHYs so that they
37584c06356bSdh 		 * can all be freed later.
37594c06356bSdh 		 */
37604c06356bSdh 		local = pmcs_clone_phy(ctmp);
37614c06356bSdh 
37624c06356bSdh 		if (local_list == NULL) {
37634c06356bSdh 			local_list = local;
37644c06356bSdh 			local_tail = local;
37654c06356bSdh 		} else {
37664c06356bSdh 			local_tail->sibling = local;
37674c06356bSdh 			local_tail = local;
37684c06356bSdh 		}
37694c06356bSdh 
37704c06356bSdh 		/*
37714c06356bSdh 		 * Need to lock the local PHY since pmcs_expander_content_
37724c06356bSdh 		 * discovery may call pmcs_clear_phy on it, which expects
37734c06356bSdh 		 * the PHY to be locked.
37744c06356bSdh 		 */
37754c06356bSdh 		pmcs_lock_phy(local);
37764c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, local);
37774c06356bSdh 		pmcs_unlock_phy(local);
37784c06356bSdh 		if (result <= 0) {
37794c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
37804c06356bSdh 				PHY_CHANGED(pwp, pptr);
37814c06356bSdh 				RESTART_DISCOVERY(pwp);
37824c06356bSdh 			} else {
37834c06356bSdh 				pptr->config_stop = 0;
3784c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
37854c06356bSdh 				    "%s: Retries exhausted for %s, killing",
37864c06356bSdh 				    __func__, pptr->path);
37874c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
37884c06356bSdh 			}
37894c06356bSdh 
37904c06356bSdh 			/*
37914c06356bSdh 			 * Release all the local PHYs that we allocated.
37924c06356bSdh 			 */
37934c06356bSdh 			pmcs_free_phys(pwp, local_list);
37944c06356bSdh 			return;
37954c06356bSdh 		}
37964c06356bSdh 
37974c06356bSdh 		ctmp = ctmp->sibling;
37984c06356bSdh 	}
37994c06356bSdh 
38004c06356bSdh 	/*
38014c06356bSdh 	 * Step 6: Compare the local PHY's contents to our current PHY.  If
38024c06356bSdh 	 * there are changes, take the appropriate action.
38034c06356bSdh 	 * This is done in two steps (step 5 above, and 6 here) so that if we
38044c06356bSdh 	 * have to bail during this process (e.g. pmcs_expander_content_discover
38054c06356bSdh 	 * fails), we haven't actually changed the state of any of the real
38064c06356bSdh 	 * PHYs.  Next time we come through here, we'll be starting over from
38074c06356bSdh 	 * scratch.  This keeps us from marking a changed PHY as no longer
38084c06356bSdh 	 * changed, but then having to bail only to come back next time and
38094c06356bSdh 	 * think that the PHY hadn't changed.  If this were to happen, we
38104c06356bSdh 	 * would fail to properly configure the device behind this PHY.
38114c06356bSdh 	 */
38124c06356bSdh 	local = local_list;
38134c06356bSdh 	ctmp = pptr->children;
38144c06356bSdh 
38154c06356bSdh 	while (ctmp) {
38164c06356bSdh 		changed = B_FALSE;
38174c06356bSdh 		kill_changed = B_FALSE;
38184c06356bSdh 
38194c06356bSdh 		/*
38204c06356bSdh 		 * We set local to local_list prior to this loop so that we
38214c06356bSdh 		 * can simply walk the local_list while we walk this list.  The
38224c06356bSdh 		 * two lists should be completely in sync.
38234c06356bSdh 		 *
38244c06356bSdh 		 * Clear the changed flag here.
38254c06356bSdh 		 */
38264c06356bSdh 		ctmp->changed = 0;
38274c06356bSdh 
38284c06356bSdh 		if (ctmp->dtype != local->dtype) {
38294c06356bSdh 			if (ctmp->dtype != NOTHING) {
3830c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
3831c3bc407cSdh 				    "%s: %s type changed from %s to %s "
3832c3bc407cSdh 				    "(killing)", __func__, ctmp->path,
3833c3bc407cSdh 				    PHY_TYPE(ctmp), PHY_TYPE(local));
38344c06356bSdh 				/*
38354c06356bSdh 				 * Force a rescan of this expander after dead
38364c06356bSdh 				 * contents are cleared and removed.
38374c06356bSdh 				 */
38384c06356bSdh 				changed = B_TRUE;
38394c06356bSdh 				kill_changed = B_TRUE;
38404c06356bSdh 			} else {
38414c06356bSdh 				changed = B_TRUE;
3842c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
38434c06356bSdh 				    "%s: %s type changed from NOTHING to %s",
38444c06356bSdh 				    __func__, ctmp->path, PHY_TYPE(local));
384573a3eccdSDavid Hollister 				/*
384673a3eccdSDavid Hollister 				 * Since this PHY was nothing and is now
384773a3eccdSDavid Hollister 				 * something, reset the config_stop timer.
384873a3eccdSDavid Hollister 				 */
384973a3eccdSDavid Hollister 				ctmp->config_stop = ddi_get_lbolt() +
385073a3eccdSDavid Hollister 				    drv_usectohz(PMCS_MAX_CONFIG_TIME);
38514c06356bSdh 			}
38524c06356bSdh 
38534c06356bSdh 		} else if (ctmp->atdt != local->atdt) {
3854c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: "
3855c3bc407cSdh 			    "%s attached device type changed from %d to %d "
3856c3bc407cSdh 			    "(killing)", __func__, ctmp->path, ctmp->atdt,
3857c3bc407cSdh 			    local->atdt);
38584c06356bSdh 			/*
38594c06356bSdh 			 * Force a rescan of this expander after dead
38604c06356bSdh 			 * contents are cleared and removed.
38614c06356bSdh 			 */
38624c06356bSdh 			changed = B_TRUE;
38634c06356bSdh 
38644c06356bSdh 			if (local->atdt == 0) {
38654c06356bSdh 				kill_changed = B_TRUE;
38664c06356bSdh 			}
38674c06356bSdh 		} else if (ctmp->link_rate != local->link_rate) {
3868c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s "
3869c3bc407cSdh 			    "changed speed from %s to %s", __func__, ctmp->path,
38704c06356bSdh 			    pmcs_get_rate(ctmp->link_rate),
38714c06356bSdh 			    pmcs_get_rate(local->link_rate));
38724c06356bSdh 			/* If the speed changed from invalid, force rescan */
38734c06356bSdh 			if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
38744c06356bSdh 				changed = B_TRUE;
38754c06356bSdh 				RESTART_DISCOVERY(pwp);
38764c06356bSdh 			} else {
38774c06356bSdh 				/* Just update to the new link rate */
38784c06356bSdh 				ctmp->link_rate = local->link_rate;
38794c06356bSdh 			}
38804c06356bSdh 
38814c06356bSdh 			if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
38824c06356bSdh 				kill_changed = B_TRUE;
38834c06356bSdh 			}
38844c06356bSdh 		} else if (memcmp(ctmp->sas_address, local->sas_address,
38854c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
3886c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
3887c3bc407cSdh 			    "%s: SAS Addr for %s changed from " SAS_ADDR_FMT
3888c3bc407cSdh 			    "to " SAS_ADDR_FMT " (kill old tree)", __func__,
38894c06356bSdh 			    ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
38904c06356bSdh 			    SAS_ADDR_PRT(local->sas_address));
38914c06356bSdh 			/*
38924c06356bSdh 			 * Force a rescan of this expander after dead
38934c06356bSdh 			 * contents are cleared and removed.
38944c06356bSdh 			 */
38954c06356bSdh 			changed = B_TRUE;
38964c06356bSdh 		} else {
3897c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
38984c06356bSdh 			    "%s: %s looks the same (type %s)",
38994c06356bSdh 			    __func__, ctmp->path, PHY_TYPE(ctmp));
39004c06356bSdh 			/*
39014c06356bSdh 			 * If EXPANDER, still mark it changed so we
39024c06356bSdh 			 * re-evaluate its contents.  If it's not an expander,
39034c06356bSdh 			 * but it hasn't been configured, also mark it as
39044c06356bSdh 			 * changed so that it will undergo configuration.
39054c06356bSdh 			 */
39064c06356bSdh 			if (ctmp->dtype == EXPANDER) {
39074c06356bSdh 				changed = B_TRUE;
39084c06356bSdh 			} else if ((ctmp->dtype != NOTHING) &&
39094c06356bSdh 			    !ctmp->configured) {
39104c06356bSdh 				ctmp->changed = 1;
39114c06356bSdh 			} else {
39124c06356bSdh 				/* It simply hasn't changed */
39134c06356bSdh 				ctmp->changed = 0;
39144c06356bSdh 			}
39154c06356bSdh 		}
39164c06356bSdh 
39174c06356bSdh 		/*
39184c06356bSdh 		 * If the PHY changed, call pmcs_kill_changed if indicated,
39194c06356bSdh 		 * update its contents to reflect its current state and mark it
39204c06356bSdh 		 * as changed.
39214c06356bSdh 		 */
39224c06356bSdh 		if (changed) {
39234c06356bSdh 			/*
39244c06356bSdh 			 * pmcs_kill_changed will mark the PHY as changed, so
39254c06356bSdh 			 * only do PHY_CHANGED if we did not do kill_changed.
39264c06356bSdh 			 */
39274c06356bSdh 			if (kill_changed) {
39284c06356bSdh 				pmcs_kill_changed(pwp, ctmp, 0);
39294c06356bSdh 			} else {
39304c06356bSdh 				/*
39314c06356bSdh 				 * If we're not killing the device, it's not
39324c06356bSdh 				 * dead.  Mark the PHY as changed.
39334c06356bSdh 				 */
39344c06356bSdh 				PHY_CHANGED(pwp, ctmp);
39354c06356bSdh 
39364c06356bSdh 				if (ctmp->dead) {
39374c06356bSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3938c3bc407cSdh 					    ctmp, NULL, "%s: Unmarking PHY %s "
3939c3bc407cSdh 					    "dead, restarting discovery",
39404c06356bSdh 					    __func__, ctmp->path);
39414c06356bSdh 					ctmp->dead = 0;
39424c06356bSdh 					RESTART_DISCOVERY(pwp);
39434c06356bSdh 				}
39444c06356bSdh 			}
39454c06356bSdh 
39464c06356bSdh 			/*
39474c06356bSdh 			 * If the dtype of this PHY is now NOTHING, mark it as
39484c06356bSdh 			 * unconfigured.  Set pend_dtype to what the new dtype
39494c06356bSdh 			 * is.  It'll get updated at the end of the discovery
39504c06356bSdh 			 * process.
39514c06356bSdh 			 */
39524c06356bSdh 			if (local->dtype == NOTHING) {
39534c06356bSdh 				bzero(ctmp->sas_address,
39544c06356bSdh 				    sizeof (local->sas_address));
39554c06356bSdh 				ctmp->atdt = 0;
39564c06356bSdh 				ctmp->link_rate = 0;
39574c06356bSdh 				ctmp->pend_dtype = NOTHING;
39584c06356bSdh 				ctmp->configured = 0;
39594c06356bSdh 			} else {
39604c06356bSdh 				(void) memcpy(ctmp->sas_address,
39614c06356bSdh 				    local->sas_address,
39624c06356bSdh 				    sizeof (local->sas_address));
39634c06356bSdh 				ctmp->atdt = local->atdt;
39644c06356bSdh 				ctmp->link_rate = local->link_rate;
39654c06356bSdh 				ctmp->pend_dtype = local->dtype;
39664c06356bSdh 			}
39674c06356bSdh 		}
39684c06356bSdh 
39694c06356bSdh 		local = local->sibling;
39704c06356bSdh 		ctmp = ctmp->sibling;
39714c06356bSdh 	}
39724c06356bSdh 
39734c06356bSdh 	/*
39744c06356bSdh 	 * If we got to here, that means we were able to see all the PHYs
39754c06356bSdh 	 * and we can now update all of the real PHYs with the information
39764c06356bSdh 	 * we got on the local PHYs.  Once that's done, free all the local
39774c06356bSdh 	 * PHYs.
39784c06356bSdh 	 */
39794c06356bSdh 
39804c06356bSdh 	pmcs_free_phys(pwp, local_list);
39814c06356bSdh }
39824c06356bSdh 
39834c06356bSdh /*
39844c06356bSdh  * Top level routine to check expanders.  We call pmcs_check_expander for
39854c06356bSdh  * each expander.  Since we're not doing any configuration right now, it
39864c06356bSdh  * doesn't matter if this is breadth-first.
39874c06356bSdh  */
398873a3eccdSDavid Hollister static void
39894c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
39904c06356bSdh {
39914c06356bSdh 	pmcs_phy_t *phyp, *pnext, *pchild;
39924c06356bSdh 
3993c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
3994c3bc407cSdh 	    "%s: %s", __func__, pptr->path);
39954c06356bSdh 
39964c06356bSdh 	/*
39974c06356bSdh 	 * Check each expander at this level
39984c06356bSdh 	 */
39994c06356bSdh 	phyp = pptr;
400073a3eccdSDavid Hollister 	while (phyp) {
40014c06356bSdh 		pmcs_lock_phy(phyp);
40024c06356bSdh 
40034c06356bSdh 		if ((phyp->dtype == EXPANDER) && phyp->changed &&
40044c06356bSdh 		    !phyp->dead && !phyp->subsidiary &&
40054c06356bSdh 		    phyp->configured) {
40064c06356bSdh 			pmcs_check_expander(pwp, phyp);
40074c06356bSdh 		}
40084c06356bSdh 
40094c06356bSdh 		pnext = phyp->sibling;
40104c06356bSdh 		pmcs_unlock_phy(phyp);
40114c06356bSdh 		phyp = pnext;
40124c06356bSdh 	}
40134c06356bSdh 
40144c06356bSdh 	/*
40154c06356bSdh 	 * Now check the children
40164c06356bSdh 	 */
40174c06356bSdh 	phyp = pptr;
401873a3eccdSDavid Hollister 	while (phyp) {
40194c06356bSdh 		pmcs_lock_phy(phyp);
40204c06356bSdh 		pnext = phyp->sibling;
40214c06356bSdh 		pchild = phyp->children;
40224c06356bSdh 		pmcs_unlock_phy(phyp);
40234c06356bSdh 
40244c06356bSdh 		if (pchild) {
402573a3eccdSDavid Hollister 			pmcs_check_expanders(pwp, pchild);
40264c06356bSdh 		}
40274c06356bSdh 
40284c06356bSdh 		phyp = pnext;
40294c06356bSdh 	}
40304c06356bSdh }
40314c06356bSdh 
40324c06356bSdh /*
40334c06356bSdh  * Called with softstate and PHY locked
40344c06356bSdh  */
40354c06356bSdh static void
40364c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
40374c06356bSdh {
40384c06356bSdh 	pmcs_phy_t *ctmp;
40394c06356bSdh 
40404c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
40414c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
40424c06356bSdh 	ASSERT(pptr->level < PMCS_MAX_XPND - 1);
40434c06356bSdh 
4044c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4045c3bc407cSdh 	    "%s: checking %s", __func__, pptr->path);
40464c06356bSdh 
40474c06356bSdh 	ctmp = pptr->children;
40484c06356bSdh 	while (ctmp) {
40494c06356bSdh 		/*
40504c06356bSdh 		 * If the expander is dead, mark its children dead
40514c06356bSdh 		 */
40524c06356bSdh 		if (pptr->dead) {
40534c06356bSdh 			ctmp->dead = 1;
40544c06356bSdh 		}
40554c06356bSdh 		if (ctmp->dtype == EXPANDER) {
40564c06356bSdh 			pmcs_clear_expander(pwp, ctmp, level + 1);
40574c06356bSdh 		}
40584c06356bSdh 		ctmp = ctmp->sibling;
40594c06356bSdh 	}
40604c06356bSdh 
40614c06356bSdh 	/*
40624c06356bSdh 	 * If this expander is not dead, we're done here.
40634c06356bSdh 	 */
40644c06356bSdh 	if (!pptr->dead) {
40654c06356bSdh 		return;
40664c06356bSdh 	}
40674c06356bSdh 
40684c06356bSdh 	/*
40694c06356bSdh 	 * Now snip out the list of children below us and release them
40704c06356bSdh 	 */
40719aed1621SDavid Hollister 	if (pptr->children) {
40729aed1621SDavid Hollister 		pmcs_add_dead_phys(pwp, pptr->children);
40734c06356bSdh 	}
40744c06356bSdh 
40754c06356bSdh 	pptr->children = NULL;
40764c06356bSdh 
40774c06356bSdh 	/*
40784c06356bSdh 	 * Clear subsidiary phys as well.  Getting the parent's PHY lock
40794c06356bSdh 	 * is only necessary if level == 0 since otherwise the parent is
40804c06356bSdh 	 * already locked.
40814c06356bSdh 	 */
40824c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
40834c06356bSdh 		if (level == 0) {
40844c06356bSdh 			mutex_enter(&pptr->parent->phy_lock);
40854c06356bSdh 		}
40864c06356bSdh 		ctmp = pptr->parent->children;
40874c06356bSdh 		if (level == 0) {
40884c06356bSdh 			mutex_exit(&pptr->parent->phy_lock);
40894c06356bSdh 		}
40904c06356bSdh 	} else {
40914c06356bSdh 		ctmp = pwp->root_phys;
40924c06356bSdh 	}
40934c06356bSdh 
40944c06356bSdh 	while (ctmp) {
40954c06356bSdh 		if (ctmp == pptr) {
40964c06356bSdh 			ctmp = ctmp->sibling;
40974c06356bSdh 			continue;
40984c06356bSdh 		}
40994c06356bSdh 		/*
41004c06356bSdh 		 * We only need to lock subsidiary PHYs on the level 0
41014c06356bSdh 		 * expander.  Any children of that expander, subsidiaries or
41024c06356bSdh 		 * not, will already be locked.
41034c06356bSdh 		 */
41044c06356bSdh 		if (level == 0) {
41054c06356bSdh 			pmcs_lock_phy(ctmp);
41064c06356bSdh 		}
41074c06356bSdh 		if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
41084c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address,
41094c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
41104c06356bSdh 			if (level == 0) {
41114c06356bSdh 				pmcs_unlock_phy(ctmp);
41124c06356bSdh 			}
41134c06356bSdh 			ctmp = ctmp->sibling;
41144c06356bSdh 			continue;
41154c06356bSdh 		}
4116c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
4117c3bc407cSdh 		    "%s: subsidiary %s", __func__, ctmp->path);
41184c06356bSdh 		pmcs_clear_phy(pwp, ctmp);
41194c06356bSdh 		if (level == 0) {
41204c06356bSdh 			pmcs_unlock_phy(ctmp);
41214c06356bSdh 		}
41224c06356bSdh 		ctmp = ctmp->sibling;
41234c06356bSdh 	}
41244c06356bSdh 
41254c06356bSdh 	pmcs_clear_phy(pwp, pptr);
41264c06356bSdh }
41274c06356bSdh 
41284c06356bSdh /*
41294c06356bSdh  * Called with PHY locked and with scratch acquired. We return 0 if
41304c06356bSdh  * we fail to allocate resources or notice that the configuration
41314c06356bSdh  * count changed while we were running the command. We return
41324c06356bSdh  * less than zero if we had an I/O error or received an unsupported
41334c06356bSdh  * configuration. Otherwise we return the number of phys in the
41344c06356bSdh  * expander.
41354c06356bSdh  */
41364c06356bSdh #define	DFM(m, y) if (m == NULL) m = y
41374c06356bSdh static int
41384c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
41394c06356bSdh {
41404c06356bSdh 	struct pmcwork *pwrk;
41414c06356bSdh 	char buf[64];
41424c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
41434c06356bSdh 	smp_response_frame_t *srf;
41444c06356bSdh 	smp_report_general_resp_t *srgr;
41454c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
41469aed1621SDavid Hollister 	int result = 0;
41474c06356bSdh 
41484c06356bSdh 	ival = 0x40001100;
41499aed1621SDavid Hollister 
41504c06356bSdh again:
41519aed1621SDavid Hollister 	if (!pptr->iport || !pptr->valid_device_id) {
41529aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
41539aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, pptr->path);
41549aed1621SDavid Hollister 		goto out;
41559aed1621SDavid Hollister 	}
41569aed1621SDavid Hollister 
41574c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
41584c06356bSdh 	if (pwrk == NULL) {
41594c06356bSdh 		goto out;
41604c06356bSdh 	}
41614c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
41624c06356bSdh 	pwrk->arg = pwp->scratch;
41634c06356bSdh 	pwrk->dtype = pptr->dtype;
41644c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
41654c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
41664c06356bSdh 	if (ptr == NULL) {
41674c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4168c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL,
4169c3bc407cSdh 		    "%s: GET_IQ_ENTRY failed", __func__);
41704c06356bSdh 		pmcs_pwork(pwp, pwrk);
41714c06356bSdh 		goto out;
41724c06356bSdh 	}
41734c06356bSdh 
41744c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
41754c06356bSdh 	msg[1] = LE_32(pwrk->htag);
41764c06356bSdh 	msg[2] = LE_32(pptr->device_id);
41774c06356bSdh 	msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
41784c06356bSdh 	/*
41794c06356bSdh 	 * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
41804c06356bSdh 	 */
41814c06356bSdh 	msg[4] = BE_32(ival);
41824c06356bSdh 	msg[5] = 0;
41834c06356bSdh 	msg[6] = 0;
41844c06356bSdh 	msg[7] = 0;
41854c06356bSdh 	msg[8] = 0;
41864c06356bSdh 	msg[9] = 0;
41874c06356bSdh 	msg[10] = 0;
41884c06356bSdh 	msg[11] = 0;
41894c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
41904c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
41914c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
41924c06356bSdh 	msg[15] = 0;
41934c06356bSdh 
41944c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
41956745c559SJesse Butler 
41966745c559SJesse Butler 	/* SMP serialization */
41976745c559SJesse Butler 	pmcs_smp_acquire(pptr->iport);
41986745c559SJesse Butler 
41994c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
42004c06356bSdh 	htag = pwrk->htag;
42014c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
42024c06356bSdh 
42034c06356bSdh 	pmcs_unlock_phy(pptr);
42044c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
4205601c90f1SSrikanth, Ramana 	/* Release SMP lock before reacquiring PHY lock */
42066745c559SJesse Butler 	pmcs_smp_release(pptr->iport);
4207601c90f1SSrikanth, Ramana 	pmcs_lock_phy(pptr);
42086745c559SJesse Butler 
42094c06356bSdh 	pmcs_pwork(pwp, pwrk);
42104c06356bSdh 
42114c06356bSdh 	mutex_enter(&pwp->config_lock);
42124c06356bSdh 	if (pwp->config_changed) {
42134c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
42144c06356bSdh 		mutex_exit(&pwp->config_lock);
42154c06356bSdh 		result = 0;
42164c06356bSdh 		goto out;
42174c06356bSdh 	}
42184c06356bSdh 	mutex_exit(&pwp->config_lock);
42194c06356bSdh 
42204c06356bSdh 	if (result) {
42214c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
4222c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42234c06356bSdh 		    "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
42244c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
4225c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42264c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
42274c06356bSdh 			    __func__, htag);
42284c06356bSdh 		} else {
4229c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
42304c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
42314c06356bSdh 			    __func__, htag);
42324c06356bSdh 		}
42334c06356bSdh 		result = 0;
42344c06356bSdh 		goto out;
42354c06356bSdh 	}
42364c06356bSdh 	ptr = (void *)pwp->scratch;
42374c06356bSdh 	status = LE_32(ptr[2]);
42384c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
42394c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
4240c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
42414c06356bSdh 		    "%s: over/underflow", __func__);
42424c06356bSdh 		status = PMCOUT_STATUS_OK;
42434c06356bSdh 	}
42444c06356bSdh 	srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
42454c06356bSdh 	srgr = (smp_report_general_resp_t *)
42464c06356bSdh 	    &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
42474c06356bSdh 
42484c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
42494c06356bSdh 		char *nag = NULL;
42504c06356bSdh 		(void) snprintf(buf, sizeof (buf),
42514c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
42524c06356bSdh 		switch (status) {
42534c06356bSdh 		case PMCOUT_STATUS_IO_PORT_IN_RESET:
42544c06356bSdh 			DFM(nag, "I/O Port In Reset");
42554c06356bSdh 			/* FALLTHROUGH */
42564c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
42574c06356bSdh 			DFM(nag, "Hardware Timeout");
42584c06356bSdh 			/* FALLTHROUGH */
42594c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
42604c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
42614c06356bSdh 			/* FALLTHROUGH */
42624c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
42634c06356bSdh 			DFM(nag, "PHY Not Ready");
42644c06356bSdh 			/* FALLTHROUGH */
42654c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
42664c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
42674c06356bSdh 			/* FALLTHROUGH */
42684c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
42694c06356bSdh 			DFM(nag, "Open Retry Timeout");
42704c06356bSdh 			/* FALLTHROUGH */
4271a25672a1SDavid Hollister 		case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
4272a25672a1SDavid Hollister 			DFM(nag, "HW Resource Busy");
4273a25672a1SDavid Hollister 			/* FALLTHROUGH */
42744c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
42754c06356bSdh 			DFM(nag, "Response Connection Error");
4276c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
42774c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
42784c06356bSdh 			    __func__, pptr->path, nag);
42794c06356bSdh 			break;
42804c06356bSdh 
42814c06356bSdh 		/*
42824c06356bSdh 		 * For the IO_DS_NON_OPERATIONAL case, we need to kick off
42834c06356bSdh 		 * device state recovery and return 0 so that the caller
42844c06356bSdh 		 * doesn't assume this expander is dead for good.
42854c06356bSdh 		 */
42864c06356bSdh 		case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
42874c06356bSdh 			pmcs_xscsi_t *xp = pptr->target;
42884c06356bSdh 
4289c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp,
42904c06356bSdh 			    "%s: expander %s device state non-operational",
42914c06356bSdh 			    __func__, pptr->path);
42924c06356bSdh 
42934c06356bSdh 			if (xp == NULL) {
42946745c559SJesse Butler 				/*
42956745c559SJesse Butler 				 * Kick off recovery right now.
42966745c559SJesse Butler 				 */
42976745c559SJesse Butler 				SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
42986745c559SJesse Butler 				(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
42996745c559SJesse Butler 				    pwp, DDI_NOSLEEP);
43006745c559SJesse Butler 			} else {
43016745c559SJesse Butler 				mutex_enter(&xp->statlock);
43026745c559SJesse Butler 				pmcs_start_dev_state_recovery(xp, pptr);
43036745c559SJesse Butler 				mutex_exit(&xp->statlock);
43044c06356bSdh 			}
43054c06356bSdh 
43064c06356bSdh 			break;
43074c06356bSdh 		}
43084c06356bSdh 
43094c06356bSdh 		default:
43104c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
43114c06356bSdh 			result = -EIO;
43124c06356bSdh 			break;
43134c06356bSdh 		}
43144c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
4315c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43164c06356bSdh 		    "%s: bad response frame type 0x%x",
43174c06356bSdh 		    __func__, srf->srf_frame_type);
43184c06356bSdh 		result = -EINVAL;
43194c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
4320c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4321c3bc407cSdh 		    "%s: bad response function 0x%x",
43224c06356bSdh 		    __func__, srf->srf_function);
43234c06356bSdh 		result = -EINVAL;
43244c06356bSdh 	} else if (srf->srf_result != 0) {
43254c06356bSdh 		/*
43264c06356bSdh 		 * Check to see if we have a value of 3 for failure and
43274c06356bSdh 		 * whether we were using a SAS2.0 allocation length value
43284c06356bSdh 		 * and retry without it.
43294c06356bSdh 		 */
43304c06356bSdh 		if (srf->srf_result == 3 && (ival & 0xff00)) {
43314c06356bSdh 			ival &= ~0xff00;
4332c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43334c06356bSdh 			    "%s: err 0x%x with SAS2 request- retry with SAS1",
43344c06356bSdh 			    __func__, srf->srf_result);
43354c06356bSdh 			goto again;
43364c06356bSdh 		}
4337c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4338c3bc407cSdh 		    "%s: bad response 0x%x", __func__, srf->srf_result);
43394c06356bSdh 		result = -EINVAL;
43404c06356bSdh 	} else if (srgr->srgr_configuring) {
4341c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
43424c06356bSdh 		    "%s: expander at phy %s is still configuring",
43434c06356bSdh 		    __func__, pptr->path);
43444c06356bSdh 		result = 0;
43454c06356bSdh 	} else {
43464c06356bSdh 		result = srgr->srgr_number_of_phys;
43474c06356bSdh 		if (ival & 0xff00) {
43484c06356bSdh 			pptr->tolerates_sas2 = 1;
43494c06356bSdh 		}
43509aed1621SDavid Hollister 		/*
43519aed1621SDavid Hollister 		 * Save off the REPORT_GENERAL response
43529aed1621SDavid Hollister 		 */
43539aed1621SDavid Hollister 		bcopy(srgr, &pptr->rg_resp, sizeof (smp_report_general_resp_t));
4354c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
43554c06356bSdh 		    "%s has %d phys and %s SAS2", pptr->path, result,
43564c06356bSdh 		    pptr->tolerates_sas2? "tolerates" : "does not tolerate");
43574c06356bSdh 	}
43584c06356bSdh out:
43594c06356bSdh 	return (result);
43604c06356bSdh }
43614c06356bSdh 
43624c06356bSdh /*
43634c06356bSdh  * Called with expander locked (and thus, pptr) as well as all PHYs up to
43644c06356bSdh  * the root, and scratch acquired. Return 0 if we fail to allocate resources
43654c06356bSdh  * or notice that the configuration changed while we were running the command.
43664c06356bSdh  *
43674c06356bSdh  * We return less than zero if we had an I/O error or received an
43684c06356bSdh  * unsupported configuration.
43694c06356bSdh  */
43704c06356bSdh static int
43714c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
43724c06356bSdh     pmcs_phy_t *pptr)
43734c06356bSdh {
43744c06356bSdh 	struct pmcwork *pwrk;
43754c06356bSdh 	char buf[64];
43764c06356bSdh 	uint8_t sas_address[8];
43774c06356bSdh 	uint8_t att_sas_address[8];
43784c06356bSdh 	smp_response_frame_t *srf;
43794c06356bSdh 	smp_discover_resp_t *sdr;
43804c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
43814c06356bSdh 	uint8_t *roff;
43824c06356bSdh 	uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
43839aed1621SDavid Hollister 	int result = 0;
43844c06356bSdh 	uint8_t	ini_support;
43854c06356bSdh 	uint8_t	tgt_support;
43864c06356bSdh 
43879aed1621SDavid Hollister 	if (!expander->iport || !expander->valid_device_id) {
43889aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, expander, expander->target,
43899aed1621SDavid Hollister 		    "%s: Can't reach PHY %s", __func__, expander->path);
43909aed1621SDavid Hollister 		goto out;
43919aed1621SDavid Hollister 	}
43929aed1621SDavid Hollister 
43934c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
43944c06356bSdh 	if (pwrk == NULL) {
43954c06356bSdh 		goto out;
43964c06356bSdh 	}
43974c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
43984c06356bSdh 	pwrk->arg = pwp->scratch;
43994c06356bSdh 	pwrk->dtype = expander->dtype;
44004c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
44014c06356bSdh 	msg[1] = LE_32(pwrk->htag);
44024c06356bSdh 	msg[2] = LE_32(expander->device_id);
44034c06356bSdh 	msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
44044c06356bSdh 	    SMP_INDIRECT_RESPONSE);
44054c06356bSdh 	/*
44064c06356bSdh 	 * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
44074c06356bSdh 	 */
44084c06356bSdh 	if (expander->tolerates_sas2) {
44094c06356bSdh 		msg[4] = BE_32(0x40101B00);
44104c06356bSdh 	} else {
44114c06356bSdh 		msg[4] = BE_32(0x40100000);
44124c06356bSdh 	}
44134c06356bSdh 	msg[5] = 0;
44144c06356bSdh 	msg[6] = BE_32((pptr->phynum << 16));
44154c06356bSdh 	msg[7] = 0;
44164c06356bSdh 	msg[8] = 0;
44174c06356bSdh 	msg[9] = 0;
44184c06356bSdh 	msg[10] = 0;
44194c06356bSdh 	msg[11] = 0;
44204c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
44214c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
44224c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
44234c06356bSdh 	msg[15] = 0;
44244c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
44254c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
44264c06356bSdh 	if (ptr == NULL) {
44274c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
44284c06356bSdh 		goto out;
44294c06356bSdh 	}
44304c06356bSdh 
44314c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
44326745c559SJesse Butler 
44336745c559SJesse Butler 	/* SMP serialization */
44346745c559SJesse Butler 	pmcs_smp_acquire(expander->iport);
44356745c559SJesse Butler 
44364c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
44374c06356bSdh 	htag = pwrk->htag;
44384c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
44394c06356bSdh 
44404c06356bSdh 	/*
44414c06356bSdh 	 * Drop PHY lock while waiting so other completions aren't potentially
44424c06356bSdh 	 * blocked.
44434c06356bSdh 	 */
44444c06356bSdh 	pmcs_unlock_phy(expander);
44454c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
4446601c90f1SSrikanth, Ramana 	/* Release SMP lock before reacquiring PHY lock */
44476745c559SJesse Butler 	pmcs_smp_release(expander->iport);
4448601c90f1SSrikanth, Ramana 	pmcs_lock_phy(expander);
44496745c559SJesse Butler 
44504c06356bSdh 	pmcs_pwork(pwp, pwrk);
44514c06356bSdh 
44524c06356bSdh 	mutex_enter(&pwp->config_lock);
44534c06356bSdh 	if (pwp->config_changed) {
44544c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
44554c06356bSdh 		mutex_exit(&pwp->config_lock);
44564c06356bSdh 		result = 0;
44574c06356bSdh 		goto out;
44584c06356bSdh 	}
44594c06356bSdh 	mutex_exit(&pwp->config_lock);
44604c06356bSdh 
44614c06356bSdh 	if (result) {
44626745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
44634c06356bSdh 		if (pmcs_abort(pwp, expander, htag, 0, 0)) {
4464c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
44654c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
44664c06356bSdh 			    __func__, htag);
44674c06356bSdh 		} else {
4468c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
44694c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
44704c06356bSdh 			    __func__, htag);
44714c06356bSdh 		}
44724c06356bSdh 		result = -ETIMEDOUT;
44734c06356bSdh 		goto out;
44744c06356bSdh 	}
44754c06356bSdh 	ptr = (void *)pwp->scratch;
44764c06356bSdh 	/*
44774c06356bSdh 	 * Point roff to the DMA offset for returned data
44784c06356bSdh 	 */
44794c06356bSdh 	roff = pwp->scratch;
44804c06356bSdh 	roff += rdoff;
44814c06356bSdh 	srf = (smp_response_frame_t *)roff;
44824c06356bSdh 	sdr = (smp_discover_resp_t *)(roff+4);
44834c06356bSdh 	status = LE_32(ptr[2]);
44844c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
44854c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
4486c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
44874c06356bSdh 		    "%s: over/underflow", __func__);
44884c06356bSdh 		status = PMCOUT_STATUS_OK;
44894c06356bSdh 	}
44904c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
44914c06356bSdh 		char *nag = NULL;
44924c06356bSdh 		(void) snprintf(buf, sizeof (buf),
44934c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
44944c06356bSdh 		switch (status) {
44954c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
44964c06356bSdh 			DFM(nag, "Hardware Timeout");
44974c06356bSdh 			/* FALLTHROUGH */
44984c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
44994c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
45004c06356bSdh 			/* FALLTHROUGH */
45014c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
45024c06356bSdh 			DFM(nag, "PHY Not Ready");
45034c06356bSdh 			/* FALLTHROUGH */
45044c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
45054c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
45064c06356bSdh 			/* FALLTHROUGH */
45074c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
45084c06356bSdh 			DFM(nag, "Open Retry Timeout");
45094c06356bSdh 			/* FALLTHROUGH */
4510a25672a1SDavid Hollister 		case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
4511a25672a1SDavid Hollister 			DFM(nag, "HW Resource Busy");
4512a25672a1SDavid Hollister 			/* FALLTHROUGH */
45134c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
45144c06356bSdh 			DFM(nag, "Response Connection Error");
4515c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
45164c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
45174c06356bSdh 			    __func__, pptr->path, nag);
45184c06356bSdh 			break;
45194c06356bSdh 		default:
45204c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
45214c06356bSdh 			result = -EIO;
45224c06356bSdh 			break;
45234c06356bSdh 		}
45244c06356bSdh 		goto out;
45254c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
4526c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
45274c06356bSdh 		    "%s: bad response frame type 0x%x",
45284c06356bSdh 		    __func__, srf->srf_frame_type);
45294c06356bSdh 		result = -EINVAL;
45304c06356bSdh 		goto out;
45314c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_DISCOVER) {
4532c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4533c3bc407cSdh 		    "%s: bad response function 0x%x",
45344c06356bSdh 		    __func__, srf->srf_function);
45354c06356bSdh 		result = -EINVAL;
45364c06356bSdh 		goto out;
45374c06356bSdh 	} else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
45384c06356bSdh 		result = pmcs_smp_function_result(pwp, srf);
45394c06356bSdh 		/* Need not fail if PHY is Vacant */
45404c06356bSdh 		if (result != SMP_RES_PHY_VACANT) {
45414c06356bSdh 			result = -EINVAL;
45424c06356bSdh 			goto out;
45434c06356bSdh 		}
45444c06356bSdh 	}
45454c06356bSdh 
45469aed1621SDavid Hollister 	/*
45479aed1621SDavid Hollister 	 * Save off the DISCOVER response
45489aed1621SDavid Hollister 	 */
45499aed1621SDavid Hollister 	bcopy(sdr, &pptr->disc_resp, sizeof (smp_discover_resp_t));
45509aed1621SDavid Hollister 
45514c06356bSdh 	ini_support = (sdr->sdr_attached_sata_host |
45524c06356bSdh 	    (sdr->sdr_attached_smp_initiator << 1) |
45534c06356bSdh 	    (sdr->sdr_attached_stp_initiator << 2) |
45544c06356bSdh 	    (sdr->sdr_attached_ssp_initiator << 3));
45554c06356bSdh 
45564c06356bSdh 	tgt_support = (sdr->sdr_attached_sata_device |
45574c06356bSdh 	    (sdr->sdr_attached_smp_target << 1) |
45584c06356bSdh 	    (sdr->sdr_attached_stp_target << 2) |
45594c06356bSdh 	    (sdr->sdr_attached_ssp_target << 3));
45604c06356bSdh 
45614c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
45624c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
45634c06356bSdh 
45649aed1621SDavid Hollister 	/*
45659aed1621SDavid Hollister 	 * Set the routing attribute regardless of the PHY type.
45669aed1621SDavid Hollister 	 */
45679aed1621SDavid Hollister 	pptr->routing_attr = sdr->sdr_routing_attr;
45689aed1621SDavid Hollister 
45694c06356bSdh 	switch (sdr->sdr_attached_device_type) {
45704c06356bSdh 	case SAS_IF_DTYPE_ENDPOINT:
4571c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
45724c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
45734c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
45744c06356bSdh 		    pptr->path,
45754c06356bSdh 		    sdr->sdr_attached_device_type,
45764c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
45774c06356bSdh 		    ini_support,
45784c06356bSdh 		    tgt_support,
45794c06356bSdh 		    SAS_ADDR_PRT(sas_address),
45804c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
45814c06356bSdh 		    sdr->sdr_attached_phy_identifier);
45824c06356bSdh 
45834c06356bSdh 		if (sdr->sdr_attached_sata_device ||
45844c06356bSdh 		    sdr->sdr_attached_stp_target) {
45854c06356bSdh 			pptr->dtype = SATA;
45864c06356bSdh 		} else if (sdr->sdr_attached_ssp_target) {
45874c06356bSdh 			pptr->dtype = SAS;
45884c06356bSdh 		} else if (tgt_support || ini_support) {
4589c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4590c3bc407cSdh 			    "%s: %s has tgt support=%x init support=(%x)",
45914c06356bSdh 			    __func__, pptr->path, tgt_support, ini_support);
45924c06356bSdh 		}
45939aed1621SDavid Hollister 
45949aed1621SDavid Hollister 		switch (pptr->routing_attr) {
45959aed1621SDavid Hollister 		case SMP_ROUTING_SUBTRACTIVE:
45969aed1621SDavid Hollister 		case SMP_ROUTING_TABLE:
45979aed1621SDavid Hollister 		case SMP_ROUTING_DIRECT:
45989aed1621SDavid Hollister 			pptr->routing_method = SMP_ROUTING_DIRECT;
45999aed1621SDavid Hollister 			break;
46009aed1621SDavid Hollister 		default:
46019aed1621SDavid Hollister 			pptr->routing_method = 0xff;	/* Invalid method */
46029aed1621SDavid Hollister 			break;
46039aed1621SDavid Hollister 		}
4604616875b4SDavid Hollister 		pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
4605616875b4SDavid Hollister 		    (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
46064c06356bSdh 		break;
46074c06356bSdh 	case SAS_IF_DTYPE_EDGE:
46084c06356bSdh 	case SAS_IF_DTYPE_FANOUT:
4609c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
46104c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
46114c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
46124c06356bSdh 		    pptr->path,
46134c06356bSdh 		    sdr->sdr_attached_device_type,
46144c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
46154c06356bSdh 		    ini_support,
46164c06356bSdh 		    tgt_support,
46174c06356bSdh 		    SAS_ADDR_PRT(sas_address),
46184c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
46194c06356bSdh 		    sdr->sdr_attached_phy_identifier);
46204c06356bSdh 		if (sdr->sdr_attached_smp_target) {
46214c06356bSdh 			/*
46224c06356bSdh 			 * Avoid configuring phys that just point back
46234c06356bSdh 			 * at a parent phy
46244c06356bSdh 			 */
46254c06356bSdh 			if (expander->parent &&
46264c06356bSdh 			    memcmp(expander->parent->sas_address,
46274c06356bSdh 			    att_sas_address,
46284c06356bSdh 			    sizeof (expander->parent->sas_address)) == 0) {
4629c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL,
46304c06356bSdh 				    "%s: skipping port back to parent "
46314c06356bSdh 				    "expander (%s)", __func__, pptr->path);
46324c06356bSdh 				pptr->dtype = NOTHING;
46334c06356bSdh 				break;
46344c06356bSdh 			}
46354c06356bSdh 			pptr->dtype = EXPANDER;
46364c06356bSdh 
46374c06356bSdh 		} else if (tgt_support || ini_support) {
4638c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
4639c3bc407cSdh 			    "%s has tgt support=%x init support=(%x)",
46404c06356bSdh 			    pptr->path, tgt_support, ini_support);
46414c06356bSdh 			pptr->dtype = EXPANDER;
46424c06356bSdh 		}
46439aed1621SDavid Hollister 		if (pptr->routing_attr == SMP_ROUTING_DIRECT) {
46449aed1621SDavid Hollister 			pptr->routing_method = 0xff;	/* Invalid method */
46459aed1621SDavid Hollister 		} else {
46469aed1621SDavid Hollister 			pptr->routing_method = pptr->routing_attr;
46479aed1621SDavid Hollister 		}
4648616875b4SDavid Hollister 		pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
4649616875b4SDavid Hollister 		    (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
46504c06356bSdh 		break;
46514c06356bSdh 	default:
46524c06356bSdh 		pptr->dtype = NOTHING;
46534c06356bSdh 		break;
46544c06356bSdh 	}
46554c06356bSdh 	if (pptr->dtype != NOTHING) {
46564c06356bSdh 		pmcs_phy_t *ctmp;
46574c06356bSdh 
46584c06356bSdh 		/*
46594c06356bSdh 		 * If the attached device is a SATA device and the expander
46604c06356bSdh 		 * is (possibly) a SAS2 compliant expander, check for whether
46614c06356bSdh 		 * there is a NAA=5 WWN field starting at this offset and
46624c06356bSdh 		 * use that for the SAS Address for this device.
46634c06356bSdh 		 */
46644c06356bSdh 		if (expander->tolerates_sas2 && pptr->dtype == SATA &&
4665a25672a1SDavid Hollister 		    (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == NAA_IEEE_REG) {
46664c06356bSdh 			(void) memcpy(pptr->sas_address,
46674c06356bSdh 			    &roff[SAS_ATTACHED_NAME_OFFSET], 8);
46684c06356bSdh 		} else {
46694c06356bSdh 			(void) memcpy(pptr->sas_address, att_sas_address, 8);
46704c06356bSdh 		}
46714c06356bSdh 		pptr->atdt = (sdr->sdr_attached_device_type);
46724c06356bSdh 		/*
46734c06356bSdh 		 * Now run up from the expander's parent up to the top to
46744c06356bSdh 		 * make sure we only use the least common link_rate.
46754c06356bSdh 		 */
46764c06356bSdh 		for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
46774c06356bSdh 			if (ctmp->link_rate <
46784c06356bSdh 			    sdr->sdr_negotiated_logical_link_rate) {
4679c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
46804c06356bSdh 				    "%s: derating link rate from %x to %x due "
46814c06356bSdh 				    "to %s being slower", pptr->path,
46824c06356bSdh 				    sdr->sdr_negotiated_logical_link_rate,
46834c06356bSdh 				    ctmp->link_rate,
46844c06356bSdh 				    ctmp->path);
46854c06356bSdh 				sdr->sdr_negotiated_logical_link_rate =
46864c06356bSdh 				    ctmp->link_rate;
46874c06356bSdh 			}
46884c06356bSdh 		}
46894c06356bSdh 		pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
46904c06356bSdh 		pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
46914c06356bSdh 		pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
46924c06356bSdh 		pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
46934c06356bSdh 		pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
46944c06356bSdh 		PHY_CHANGED(pwp, pptr);
46954c06356bSdh 	} else {
46964c06356bSdh 		pmcs_clear_phy(pwp, pptr);
46974c06356bSdh 	}
46984c06356bSdh 	result = 1;
46994c06356bSdh out:
47004c06356bSdh 	return (result);
47014c06356bSdh }
47024c06356bSdh 
47034c06356bSdh /*
47044c06356bSdh  * Get a work structure and assign it a tag with type and serial number
47054c06356bSdh  * If a structure is returned, it is returned locked.
47064c06356bSdh  */
47074c06356bSdh pmcwork_t *
47084c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
47094c06356bSdh {
47104c06356bSdh 	pmcwork_t *p;
47114c06356bSdh 	uint16_t snum;
47124c06356bSdh 	uint32_t off;
47134c06356bSdh 
47144c06356bSdh 	mutex_enter(&pwp->wfree_lock);
47154c06356bSdh 	p = STAILQ_FIRST(&pwp->wf);
47164c06356bSdh 	if (p == NULL) {
47174c06356bSdh 		/*
47184c06356bSdh 		 * If we couldn't get a work structure, it's time to bite
47194c06356bSdh 		 * the bullet, grab the pfree_lock and copy over all the
47204c06356bSdh 		 * work structures from the pending free list to the actual
472102b04f6eSSrikanth, Ramana 		 * free list (assuming it's not also empty).
47224c06356bSdh 		 */
47234c06356bSdh 		mutex_enter(&pwp->pfree_lock);
472402b04f6eSSrikanth, Ramana 		if (STAILQ_FIRST(&pwp->pf) == NULL) {
472502b04f6eSSrikanth, Ramana 			mutex_exit(&pwp->pfree_lock);
472602b04f6eSSrikanth, Ramana 			mutex_exit(&pwp->wfree_lock);
472702b04f6eSSrikanth, Ramana 			return (NULL);
472802b04f6eSSrikanth, Ramana 		}
47294c06356bSdh 		pwp->wf.stqh_first = pwp->pf.stqh_first;
47304c06356bSdh 		pwp->wf.stqh_last = pwp->pf.stqh_last;
47314c06356bSdh 		STAILQ_INIT(&pwp->pf);
47324c06356bSdh 		mutex_exit(&pwp->pfree_lock);
47334c06356bSdh 
47344c06356bSdh 		p = STAILQ_FIRST(&pwp->wf);
473502b04f6eSSrikanth, Ramana 		ASSERT(p != NULL);
47364c06356bSdh 	}
47374c06356bSdh 	STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
47384c06356bSdh 	snum = pwp->wserno++;
47394c06356bSdh 	mutex_exit(&pwp->wfree_lock);
47404c06356bSdh 
47414c06356bSdh 	off = p - pwp->work;
47424c06356bSdh 
47434c06356bSdh 	mutex_enter(&p->lock);
47444c06356bSdh 	ASSERT(p->state == PMCS_WORK_STATE_NIL);
47454c06356bSdh 	ASSERT(p->htag == PMCS_TAG_FREE);
47464c06356bSdh 	p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
47474c06356bSdh 	p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
47484c06356bSdh 	p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
47494c06356bSdh 	p->start = gethrtime();
47504c06356bSdh 	p->state = PMCS_WORK_STATE_READY;
47514c06356bSdh 	p->ssp_event = 0;
47524c06356bSdh 	p->dead = 0;
47534c06356bSdh 
47544c06356bSdh 	if (phyp) {
47554c06356bSdh 		p->phy = phyp;
47564c06356bSdh 		pmcs_inc_phy_ref_count(phyp);
47574c06356bSdh 	}
47584c06356bSdh 
47594c06356bSdh 	return (p);
47604c06356bSdh }
47614c06356bSdh 
47624c06356bSdh /*
47634c06356bSdh  * Called with pwrk lock held.  Returned with lock released.
47644c06356bSdh  */
47654c06356bSdh void
47664c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
47674c06356bSdh {
47684c06356bSdh 	ASSERT(p != NULL);
47694c06356bSdh 	ASSERT(mutex_owned(&p->lock));
47704c06356bSdh 
47714c06356bSdh 	p->last_ptr = p->ptr;
47724c06356bSdh 	p->last_arg = p->arg;
47734c06356bSdh 	p->last_phy = p->phy;
47744c06356bSdh 	p->last_xp = p->xp;
47754c06356bSdh 	p->last_htag = p->htag;
47764c06356bSdh 	p->last_state = p->state;
47774c06356bSdh 	p->finish = gethrtime();
47784c06356bSdh 
47794c06356bSdh 	if (p->phy) {
47804c06356bSdh 		pmcs_dec_phy_ref_count(p->phy);
47814c06356bSdh 	}
47824c06356bSdh 
47834c06356bSdh 	p->state = PMCS_WORK_STATE_NIL;
47844c06356bSdh 	p->htag = PMCS_TAG_FREE;
47854c06356bSdh 	p->xp = NULL;
47864c06356bSdh 	p->ptr = NULL;
47874c06356bSdh 	p->arg = NULL;
47884c06356bSdh 	p->phy = NULL;
4789c3bc407cSdh 	p->abt_htag = 0;
47904c06356bSdh 	p->timer = 0;
47914c06356bSdh 	mutex_exit(&p->lock);
47924c06356bSdh 
47934c06356bSdh 	if (mutex_tryenter(&pwp->wfree_lock) == 0) {
47944c06356bSdh 		mutex_enter(&pwp->pfree_lock);
47954c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->pf, p, next);
47964c06356bSdh 		mutex_exit(&pwp->pfree_lock);
47974c06356bSdh 	} else {
47984c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, p, next);
47994c06356bSdh 		mutex_exit(&pwp->wfree_lock);
48004c06356bSdh 	}
48014c06356bSdh }
48024c06356bSdh 
48034c06356bSdh /*
48044c06356bSdh  * Find a work structure based upon a tag and make sure that the tag
48054c06356bSdh  * serial number matches the work structure we've found.
48064c06356bSdh  * If a structure is found, its lock is held upon return.
48074c06356bSdh  */
48084c06356bSdh pmcwork_t *
48094c06356bSdh pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag)
48104c06356bSdh {
48114c06356bSdh 	pmcwork_t *p;
48124c06356bSdh 	uint32_t idx = PMCS_TAG_INDEX(htag);
48134c06356bSdh 
48144c06356bSdh 	p = &pwp->work[idx];
48154c06356bSdh 
48164c06356bSdh 	mutex_enter(&p->lock);
48174c06356bSdh 	if (p->htag == htag) {
48184c06356bSdh 		return (p);
48194c06356bSdh 	}
48204c06356bSdh 	mutex_exit(&p->lock);
4821c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
4822c3bc407cSdh 	    "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag);
48234c06356bSdh 	return (NULL);
48244c06356bSdh }
48254c06356bSdh 
48264c06356bSdh /*
48274c06356bSdh  * Issue an abort for a command or for all commands.
48284c06356bSdh  *
48294c06356bSdh  * Since this can be called from interrupt context,
48304c06356bSdh  * we don't wait for completion if wait is not set.
48314c06356bSdh  *
48324c06356bSdh  * Called with PHY lock held.
48334c06356bSdh  */
48344c06356bSdh int
48354c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
48364c06356bSdh     int wait)
48374c06356bSdh {
48384c06356bSdh 	pmcwork_t *pwrk;
48394c06356bSdh 	pmcs_xscsi_t *tgt;
48404c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
48414c06356bSdh 	int result, abt_type;
48424c06356bSdh 	uint32_t abt_htag, status;
48434c06356bSdh 
48444c06356bSdh 	if (pptr->abort_all_start) {
4845c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for "
4846c3bc407cSdh 		    "(%s) already in progress.", __func__, pptr->path);
48474c06356bSdh 		return (EBUSY);
48484c06356bSdh 	}
48494c06356bSdh 
48504c06356bSdh 	switch (pptr->dtype) {
48514c06356bSdh 	case SAS:
48524c06356bSdh 		abt_type = PMCIN_SSP_ABORT;
48534c06356bSdh 		break;
48544c06356bSdh 	case SATA:
48554c06356bSdh 		abt_type = PMCIN_SATA_ABORT;
48564c06356bSdh 		break;
48574c06356bSdh 	case EXPANDER:
48584c06356bSdh 		abt_type = PMCIN_SMP_ABORT;
48594c06356bSdh 		break;
48604c06356bSdh 	default:
48614c06356bSdh 		return (0);
48624c06356bSdh 	}
48634c06356bSdh 
48644c06356bSdh 	pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
48654c06356bSdh 	    pptr);
48664c06356bSdh 
48674c06356bSdh 	if (pwrk == NULL) {
4868c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
48694c06356bSdh 		return (ENOMEM);
48704c06356bSdh 	}
48714c06356bSdh 
48724c06356bSdh 	pwrk->dtype = pptr->dtype;
48734c06356bSdh 	if (wait) {
48744c06356bSdh 		pwrk->arg = msg;
48754c06356bSdh 	}
48764c06356bSdh 	if (pptr->valid_device_id == 0) {
48774c06356bSdh 		pmcs_pwork(pwp, pwrk);
4878c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
4879c3bc407cSdh 		    "%s: Invalid DeviceID", __func__);
48804c06356bSdh 		return (ENODEV);
48814c06356bSdh 	}
48824c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
48834c06356bSdh 	msg[1] = LE_32(pwrk->htag);
48844c06356bSdh 	msg[2] = LE_32(pptr->device_id);
48854c06356bSdh 	if (all_cmds) {
48864c06356bSdh 		msg[3] = 0;
48874c06356bSdh 		msg[4] = LE_32(1);
48884c06356bSdh 		pwrk->ptr = NULL;
48894c06356bSdh 		pptr->abort_all_start = gethrtime();
48904c06356bSdh 	} else {
48914c06356bSdh 		msg[3] = LE_32(tag);
48924c06356bSdh 		msg[4] = 0;
4893c3bc407cSdh 		pwrk->abt_htag = tag;
48944c06356bSdh 	}
48954c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
48964c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
48974c06356bSdh 	if (ptr == NULL) {
48984c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
48994c06356bSdh 		pmcs_pwork(pwp, pwrk);
4900c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
49014c06356bSdh 		return (ENOMEM);
49024c06356bSdh 	}
49034c06356bSdh 
49044c06356bSdh 	COPY_MESSAGE(ptr, msg, 5);
49054c06356bSdh 	if (all_cmds) {
4906c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
49074c06356bSdh 		    "%s: aborting all commands for %s device %s. (htag=0x%x)",
49084c06356bSdh 		    __func__, pmcs_get_typename(pptr->dtype), pptr->path,
49094c06356bSdh 		    msg[1]);
49104c06356bSdh 	} else {
4911c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
49124c06356bSdh 		    "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
49134c06356bSdh 		    __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
49144c06356bSdh 		    msg[1]);
49154c06356bSdh 	}
49164c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
49174c06356bSdh 
49184c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
49194c06356bSdh 	if (!wait) {
49204c06356bSdh 		mutex_exit(&pwrk->lock);
49214c06356bSdh 		return (0);
49224c06356bSdh 	}
49234c06356bSdh 
49244c06356bSdh 	abt_htag = pwrk->htag;
49254c06356bSdh 	pmcs_unlock_phy(pwrk->phy);
49264c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
49274c06356bSdh 	pmcs_lock_phy(pwrk->phy);
49284c06356bSdh 
49294c06356bSdh 	tgt = pwrk->xp;
49304c06356bSdh 	pmcs_pwork(pwp, pwrk);
49314c06356bSdh 
49324c06356bSdh 	if (tgt != NULL) {
49334c06356bSdh 		mutex_enter(&tgt->aqlock);
49344c06356bSdh 		if (!STAILQ_EMPTY(&tgt->aq)) {
4935c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49364c06356bSdh 			    "%s: Abort complete (result=0x%x), but "
49374c06356bSdh 			    "aq not empty (tgt 0x%p), waiting",
49384c06356bSdh 			    __func__, result, (void *)tgt);
49394c06356bSdh 			cv_wait(&tgt->abort_cv, &tgt->aqlock);
49404c06356bSdh 		}
49414c06356bSdh 		mutex_exit(&tgt->aqlock);
49424c06356bSdh 	}
49434c06356bSdh 
49444c06356bSdh 	if (all_cmds) {
49454c06356bSdh 		pptr->abort_all_start = 0;
49464c06356bSdh 		cv_signal(&pptr->abort_all_cv);
49474c06356bSdh 	}
49484c06356bSdh 
49494c06356bSdh 	if (result) {
4950c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49514c06356bSdh 		    "%s: Abort (htag 0x%08x) request timed out",
49524c06356bSdh 		    __func__, abt_htag);
49534c06356bSdh 		if (tgt != NULL) {
49544c06356bSdh 			mutex_enter(&tgt->statlock);
49554c06356bSdh 			if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
49564c06356bSdh 			    (tgt->dev_state !=
49574c06356bSdh 			    PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
4958c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49594c06356bSdh 				    "%s: Trying DS error recovery for tgt 0x%p",
49604c06356bSdh 				    __func__, (void *)tgt);
49614c06356bSdh 				(void) pmcs_send_err_recovery_cmd(pwp,
4962145e0143Sdh 				    PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt);
49634c06356bSdh 			}
49644c06356bSdh 			mutex_exit(&tgt->statlock);
49654c06356bSdh 		}
49664c06356bSdh 		return (ETIMEDOUT);
49674c06356bSdh 	}
49684c06356bSdh 
49694c06356bSdh 	status = LE_32(msg[2]);
49704c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
49714c06356bSdh 		/*
49724c06356bSdh 		 * The only non-success status are IO_NOT_VALID &
49734c06356bSdh 		 * IO_ABORT_IN_PROGRESS.
49744c06356bSdh 		 * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
49754c06356bSdh 		 * status is of concern and this duplicate cmd status can
49764c06356bSdh 		 * be ignored.
49774c06356bSdh 		 * If IO_NOT_VALID, that's not an error per-se.
49784c06356bSdh 		 * For abort of single I/O complete the command anyway.
49794c06356bSdh 		 * If, however, we were aborting all, that is a problem
49804c06356bSdh 		 * as IO_NOT_VALID really means that the IO or device is
49814c06356bSdh 		 * not there. So, discovery process will take of the cleanup.
49824c06356bSdh 		 */
4983c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
4984c3bc407cSdh 		    "%s: abort result 0x%x", __func__, LE_32(msg[2]));
49854c06356bSdh 		if (all_cmds) {
49864c06356bSdh 			PHY_CHANGED(pwp, pptr);
49874c06356bSdh 			RESTART_DISCOVERY(pwp);
49884c06356bSdh 		} else {
49894c06356bSdh 			return (EINVAL);
49904c06356bSdh 		}
49914c06356bSdh 
49924c06356bSdh 		return (0);
49934c06356bSdh 	}
49944c06356bSdh 
49954c06356bSdh 	if (tgt != NULL) {
49964c06356bSdh 		mutex_enter(&tgt->statlock);
49974c06356bSdh 		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
4998c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
49994c06356bSdh 			    "%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
50004c06356bSdh 			    __func__, (void *)tgt);
50014c06356bSdh 			(void) pmcs_send_err_recovery_cmd(pwp,
5002145e0143Sdh 			    PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt);
50034c06356bSdh 		}
50044c06356bSdh 		mutex_exit(&tgt->statlock);
50054c06356bSdh 	}
50064c06356bSdh 
50074c06356bSdh 	return (0);
50084c06356bSdh }
50094c06356bSdh 
50104c06356bSdh /*
50114c06356bSdh  * Issue a task management function to an SSP device.
50124c06356bSdh  *
50134c06356bSdh  * Called with PHY lock held.
50144c06356bSdh  * statlock CANNOT be held upon entry.
50154c06356bSdh  */
50164c06356bSdh int
50174c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
50184c06356bSdh     uint64_t lun, uint32_t *response)
50194c06356bSdh {
50204c06356bSdh 	int result, ds;
50214c06356bSdh 	uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
50224c06356bSdh 	sas_ssp_rsp_iu_t *rptr = (void *)local;
50234c06356bSdh 	static const uint8_t ssp_rsp_evec[] = {
50244c06356bSdh 		0x58, 0x61, 0x56, 0x72, 0x00
50254c06356bSdh 	};
50264c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
50274c06356bSdh 	struct pmcwork *pwrk;
50284c06356bSdh 	pmcs_xscsi_t *xp;
50294c06356bSdh 
50304c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
50314c06356bSdh 	if (pwrk == NULL) {
5032c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
50334c06356bSdh 		return (ENOMEM);
50344c06356bSdh 	}
50354c06356bSdh 	/*
50364c06356bSdh 	 * NB: We use the PMCS_OQ_GENERAL outbound queue
50374c06356bSdh 	 * NB: so as to not get entangled in normal I/O
50384c06356bSdh 	 * NB: processing.
50394c06356bSdh 	 */
50404c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
50414c06356bSdh 	    PMCIN_SSP_INI_TM_START));
50424c06356bSdh 	msg[1] = LE_32(pwrk->htag);
50434c06356bSdh 	msg[2] = LE_32(pptr->device_id);
50444c06356bSdh 	if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
50454c06356bSdh 		msg[3] = LE_32(tag);
50464c06356bSdh 	} else {
50474c06356bSdh 		msg[3] = 0;
50484c06356bSdh 	}
50494c06356bSdh 	msg[4] = LE_32(tmf);
50504c06356bSdh 	msg[5] = BE_32((uint32_t)lun);
50514c06356bSdh 	msg[6] = BE_32((uint32_t)(lun >> 32));
50524c06356bSdh 	msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
50534c06356bSdh 
50544c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50554c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
50564c06356bSdh 	if (ptr == NULL) {
50574c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50584c06356bSdh 		pmcs_pwork(pwp, pwrk);
5059c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
50604c06356bSdh 		return (ENOMEM);
50614c06356bSdh 	}
50624c06356bSdh 	COPY_MESSAGE(ptr, msg, 7);
50634c06356bSdh 	pwrk->arg = msg;
50644c06356bSdh 	pwrk->dtype = pptr->dtype;
50654c06356bSdh 	xp = pptr->target;
506656976565SDavid Hollister 	pwrk->xp = xp;
506756976565SDavid Hollister 
50684c06356bSdh 	if (xp != NULL) {
50694c06356bSdh 		mutex_enter(&xp->statlock);
50704c06356bSdh 		if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
50714c06356bSdh 			mutex_exit(&xp->statlock);
50724c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
50734c06356bSdh 			pmcs_pwork(pwp, pwrk);
5074c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not "
5075c3bc407cSdh 			    "sending '%s' because DS is '%s'", __func__,
5076c3bc407cSdh 			    pmcs_tmf2str(tmf), pmcs_status_str
50774c06356bSdh 			    (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
50784c06356bSdh 			return (EIO);
50794c06356bSdh 		}
50804c06356bSdh 		mutex_exit(&xp->statlock);
50814c06356bSdh 	}
50824c06356bSdh 
5083c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
50844c06356bSdh 	    "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
50854c06356bSdh 	    pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
50864c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
50874c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
50884c06356bSdh 
50894c06356bSdh 	pmcs_unlock_phy(pptr);
50904c06356bSdh 	/*
50914c06356bSdh 	 * This is a command sent to the target device, so it can take
50924c06356bSdh 	 * significant amount of time to complete when path & device is busy.
50934c06356bSdh 	 * Set a timeout to 20 seconds
50944c06356bSdh 	 */
50954c06356bSdh 	WAIT_FOR(pwrk, 20000, result);
50964c06356bSdh 	pmcs_lock_phy(pptr);
50974c06356bSdh 	pmcs_pwork(pwp, pwrk);
50984c06356bSdh 
50994c06356bSdh 	if (result) {
51004c06356bSdh 		if (xp == NULL) {
51014c06356bSdh 			return (ETIMEDOUT);
51024c06356bSdh 		}
51034c06356bSdh 
51044c06356bSdh 		mutex_enter(&xp->statlock);
51054c06356bSdh 		pmcs_start_dev_state_recovery(xp, pptr);
51064c06356bSdh 		mutex_exit(&xp->statlock);
51074c06356bSdh 		return (ETIMEDOUT);
51084c06356bSdh 	}
51094c06356bSdh 
51104c06356bSdh 	status = LE_32(msg[2]);
51114c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
5112c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51134c06356bSdh 		    "%s: status %s for TMF %s action to %s, lun %llu",
51144c06356bSdh 		    __func__, pmcs_status_str(status),  pmcs_tmf2str(tmf),
51154c06356bSdh 		    pptr->path, (unsigned long long) lun);
51164c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
51174c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
51184c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
51194c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
51204c06356bSdh 		} else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
51214c06356bSdh 			/*
51224c06356bSdh 			 * If the status is IN_RECOVERY, it's an indication
51234c06356bSdh 			 * that it's now time for us to request to have the
51244c06356bSdh 			 * device state set to OPERATIONAL since we're the ones
51254c06356bSdh 			 * that requested recovery to begin with.
51264c06356bSdh 			 */
51274c06356bSdh 			ds = PMCS_DEVICE_STATE_OPERATIONAL;
51284c06356bSdh 		} else {
51294c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
51304c06356bSdh 		}
51314c06356bSdh 		if (xp != NULL) {
51324c06356bSdh 			mutex_enter(&xp->statlock);
51334c06356bSdh 			if (xp->dev_state != ds) {
5134c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51354c06356bSdh 				    "%s: Sending err recovery cmd"
51364c06356bSdh 				    " for tgt 0x%p (status = %s)",
51374c06356bSdh 				    __func__, (void *)xp,
51384c06356bSdh 				    pmcs_status_str(status));
5139145e0143Sdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds,
5140145e0143Sdh 				    pptr, xp);
51414c06356bSdh 			}
51424c06356bSdh 			mutex_exit(&xp->statlock);
51434c06356bSdh 		}
51444c06356bSdh 		return (EIO);
51454c06356bSdh 	} else {
51464c06356bSdh 		ds = PMCS_DEVICE_STATE_OPERATIONAL;
51474c06356bSdh 		if (xp != NULL) {
51484c06356bSdh 			mutex_enter(&xp->statlock);
51494c06356bSdh 			if (xp->dev_state != ds) {
5150c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51514c06356bSdh 				    "%s: Sending err recovery cmd"
51524c06356bSdh 				    " for tgt 0x%p (status = %s)",
51534c06356bSdh 				    __func__, (void *)xp,
51544c06356bSdh 				    pmcs_status_str(status));
5155145e0143Sdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds,
5156145e0143Sdh 				    pptr, xp);
51574c06356bSdh 			}
51584c06356bSdh 			mutex_exit(&xp->statlock);
51594c06356bSdh 		}
51604c06356bSdh 	}
51614c06356bSdh 	if (LE_32(msg[3]) == 0) {
5162c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5163c3bc407cSdh 		    "TMF completed with no response");
51644c06356bSdh 		return (EIO);
51654c06356bSdh 	}
51664c06356bSdh 	pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
51674c06356bSdh 	xd = (uint8_t *)(&msg[5]);
51684c06356bSdh 	xd += SAS_RSP_HDR_SIZE;
51694c06356bSdh 	if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
5170c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
51714c06356bSdh 		    "%s: TMF response not RESPONSE DATA (0x%x)",
51724c06356bSdh 		    __func__, rptr->datapres);
51734c06356bSdh 		return (EIO);
51744c06356bSdh 	}
51754c06356bSdh 	if (rptr->response_data_length != 4) {
51764c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
51774c06356bSdh 		    "Bad SAS RESPONSE DATA LENGTH", msg);
51784c06356bSdh 		return (EIO);
51794c06356bSdh 	}
51804c06356bSdh 	(void) memcpy(&status, xd, sizeof (uint32_t));
51814c06356bSdh 	status = BE_32(status);
51824c06356bSdh 	if (response != NULL)
51834c06356bSdh 		*response = status;
51844c06356bSdh 	/*
51854c06356bSdh 	 * The status is actually in the low-order byte.  The upper three
51864c06356bSdh 	 * bytes contain additional information for the TMFs that support them.
51874c06356bSdh 	 * However, at this time we do not issue any of those.  In the other
51884c06356bSdh 	 * cases, the upper three bytes are supposed to be 0, but it appears
51894c06356bSdh 	 * they aren't always.  Just mask them off.
51904c06356bSdh 	 */
51914c06356bSdh 	switch (status & 0xff) {
51924c06356bSdh 	case SAS_RSP_TMF_COMPLETE:
5193c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5194c3bc407cSdh 		    "%s: TMF complete", __func__);
51954c06356bSdh 		result = 0;
51964c06356bSdh 		break;
51974c06356bSdh 	case SAS_RSP_TMF_SUCCEEDED:
5198c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
5199c3bc407cSdh 		    "%s: TMF succeeded", __func__);
52004c06356bSdh 		result = 0;
52014c06356bSdh 		break;
52024c06356bSdh 	case SAS_RSP_INVALID_FRAME:
5203c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52044c06356bSdh 		    "%s: TMF returned INVALID FRAME", __func__);
52054c06356bSdh 		result = EIO;
52064c06356bSdh 		break;
52074c06356bSdh 	case SAS_RSP_TMF_NOT_SUPPORTED:
5208c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52094c06356bSdh 		    "%s: TMF returned TMF NOT SUPPORTED", __func__);
52104c06356bSdh 		result = EIO;
52114c06356bSdh 		break;
52124c06356bSdh 	case SAS_RSP_TMF_FAILED:
5213c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52144c06356bSdh 		    "%s: TMF returned TMF FAILED", __func__);
52154c06356bSdh 		result = EIO;
52164c06356bSdh 		break;
52174c06356bSdh 	case SAS_RSP_TMF_INCORRECT_LUN:
5218c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52194c06356bSdh 		    "%s: TMF returned INCORRECT LUN", __func__);
52204c06356bSdh 		result = EIO;
52214c06356bSdh 		break;
52224c06356bSdh 	case SAS_RSP_OVERLAPPED_OIPTTA:
5223c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52244c06356bSdh 		    "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
52254c06356bSdh 		    "ATTEMPTED", __func__);
52264c06356bSdh 		result = EIO;
52274c06356bSdh 		break;
52284c06356bSdh 	default:
5229c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
52304c06356bSdh 		    "%s: TMF returned unknown code 0x%x", __func__, status);
52314c06356bSdh 		result = EIO;
52324c06356bSdh 		break;
52334c06356bSdh 	}
52344c06356bSdh 	return (result);
52354c06356bSdh }
52364c06356bSdh 
52374c06356bSdh /*
52384c06356bSdh  * Called with PHY lock held and scratch acquired
52394c06356bSdh  */
52404c06356bSdh int
52414c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
52424c06356bSdh {
52434c06356bSdh 	const char *utag_fail_fmt = "%s: untagged NCQ command failure";
52444c06356bSdh 	const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
52454c06356bSdh 	uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
52464c06356bSdh 	uint8_t *fp = pwp->scratch, ds;
52474c06356bSdh 	fis_t fis;
52484c06356bSdh 	pmcwork_t *pwrk;
52494c06356bSdh 	pmcs_xscsi_t *tgt;
52504c06356bSdh 
52514c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
52524c06356bSdh 	if (pwrk == NULL) {
52534c06356bSdh 		return (ENOMEM);
52544c06356bSdh 	}
52554c06356bSdh 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
52564c06356bSdh 	    PMCIN_SATA_HOST_IO_START));
52574c06356bSdh 	msg[1] = LE_32(pwrk->htag);
52584c06356bSdh 	msg[2] = LE_32(pptr->device_id);
52594c06356bSdh 	msg[3] = LE_32(512);
52604c06356bSdh 	msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
52614c06356bSdh 	msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
52624c06356bSdh 	msg[6] = LE_32(0x10);
52634c06356bSdh 	msg[8] = LE_32(1);
52644c06356bSdh 	msg[9] = 0;
52654c06356bSdh 	msg[10] = 0;
52664c06356bSdh 	msg[11] = 0;
52674c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma));
52684c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma));
52694c06356bSdh 	msg[14] = LE_32(512);
52704c06356bSdh 	msg[15] = 0;
52714c06356bSdh 
52724c06356bSdh 	pwrk->arg = msg;
52734c06356bSdh 	pwrk->dtype = pptr->dtype;
52744c06356bSdh 
52754c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
52764c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
52774c06356bSdh 	if (ptr == NULL) {
52784c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
52794c06356bSdh 		pmcs_pwork(pwp, pwrk);
52804c06356bSdh 		return (ENOMEM);
52814c06356bSdh 	}
52824c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
52834c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
52844c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
52854c06356bSdh 
52864c06356bSdh 	pmcs_unlock_phy(pptr);
52874c06356bSdh 	WAIT_FOR(pwrk, 250, result);
52884c06356bSdh 	pmcs_lock_phy(pptr);
52894c06356bSdh 	pmcs_pwork(pwp, pwrk);
52904c06356bSdh 
5291c3bc407cSdh 	tgt = pptr->target;
52924c06356bSdh 	if (result) {
52936745c559SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__);
52944c06356bSdh 		return (EIO);
52954c06356bSdh 	}
52964c06356bSdh 	status = LE_32(msg[2]);
52974c06356bSdh 	if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
52984c06356bSdh 		if (tgt == NULL) {
5299c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
53004c06356bSdh 			    "%s: cannot find target for phy 0x%p for "
53014c06356bSdh 			    "dev state recovery", __func__, (void *)pptr);
53024c06356bSdh 			return (EIO);
53034c06356bSdh 		}
53044c06356bSdh 
53054c06356bSdh 		mutex_enter(&tgt->statlock);
53064c06356bSdh 
53074c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
53084c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
53094c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
53104c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
53114c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
53124c06356bSdh 		} else {
53134c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
53144c06356bSdh 		}
53154c06356bSdh 		if (tgt->dev_state != ds) {
5316c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying "
5317c3bc407cSdh 			    "SATA DS Recovery for tgt(0x%p) for status(%s)",
53184c06356bSdh 			    __func__, (void *)tgt, pmcs_status_str(status));
5319145e0143Sdh 			(void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt);
53204c06356bSdh 		}
53214c06356bSdh 
53224c06356bSdh 		mutex_exit(&tgt->statlock);
53234c06356bSdh 		return (EIO);
53244c06356bSdh 	}
53254c06356bSdh 	fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
53264c06356bSdh 	fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
53274c06356bSdh 	fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
53284c06356bSdh 	fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
53294c06356bSdh 	fis[4] = 0;
53304c06356bSdh 	if (fp[0] & 0x80) {
5331c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
5332c3bc407cSdh 		    utag_fail_fmt, __func__);
53334c06356bSdh 	} else {
5334c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
5335c3bc407cSdh 		    tag_fail_fmt, __func__, fp[0] & 0x1f);
53364c06356bSdh 	}
53374c06356bSdh 	pmcs_fis_dump(pwp, fis);
53384c06356bSdh 	pptr->need_rl_ext = 0;
53394c06356bSdh 	return (0);
53404c06356bSdh }
53414c06356bSdh 
53424c06356bSdh /*
53434c06356bSdh  * Transform a structure from CPU to Device endian format, or
53444c06356bSdh  * vice versa, based upon a transformation vector.
53454c06356bSdh  *
53464c06356bSdh  * A transformation vector is an array of bytes, each byte
53474c06356bSdh  * of which is defined thusly:
53484c06356bSdh  *
53494c06356bSdh  *  bit 7: from CPU to desired endian, otherwise from desired endian
53504c06356bSdh  *	   to CPU format
53514c06356bSdh  *  bit 6: Big Endian, else Little Endian
53524c06356bSdh  *  bits 5-4:
53534c06356bSdh  *       00 Undefined
53544c06356bSdh  *       01 One Byte quantities
53554c06356bSdh  *       02 Two Byte quantities
53564c06356bSdh  *       03 Four Byte quantities
53574c06356bSdh  *
53584c06356bSdh  *  bits 3-0:
53594c06356bSdh  *       00 Undefined
53604c06356bSdh  *       Number of quantities to transform
53614c06356bSdh  *
53624c06356bSdh  * The vector is terminated by a 0 value.
53634c06356bSdh  */
53644c06356bSdh 
53654c06356bSdh void
53664c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
53674c06356bSdh     const uint8_t *xfvec)
53684c06356bSdh {
53694c06356bSdh 	uint8_t c, *out = orig_out, *in = orig_in;
53704c06356bSdh 
53714c06356bSdh 	if (xfvec == NULL) {
5372c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5373c3bc407cSdh 		    "%s: null xfvec", __func__);
53744c06356bSdh 		return;
53754c06356bSdh 	}
53764c06356bSdh 	if (out == NULL) {
5377c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5378c3bc407cSdh 		    "%s: null out", __func__);
53794c06356bSdh 		return;
53804c06356bSdh 	}
53814c06356bSdh 	if (in == NULL) {
5382c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5383c3bc407cSdh 		    "%s: null in", __func__);
53844c06356bSdh 		return;
53854c06356bSdh 	}
53864c06356bSdh 	while ((c = *xfvec++) != 0) {
53874c06356bSdh 		int nbyt = (c & 0xf);
53884c06356bSdh 		int size = (c >> 4) & 0x3;
53894c06356bSdh 		int bige = (c >> 4) & 0x4;
53904c06356bSdh 
53914c06356bSdh 		switch (size) {
53924c06356bSdh 		case 1:
53934c06356bSdh 		{
53944c06356bSdh 			while (nbyt-- > 0) {
53954c06356bSdh 				*out++ = *in++;
53964c06356bSdh 			}
53974c06356bSdh 			break;
53984c06356bSdh 		}
53994c06356bSdh 		case 2:
54004c06356bSdh 		{
54014c06356bSdh 			uint16_t tmp;
54024c06356bSdh 			while (nbyt-- > 0) {
54034c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint16_t));
54044c06356bSdh 				if (bige) {
54054c06356bSdh 					tmp = BE_16(tmp);
54064c06356bSdh 				} else {
54074c06356bSdh 					tmp = LE_16(tmp);
54084c06356bSdh 				}
54094c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint16_t));
54104c06356bSdh 				out += sizeof (uint16_t);
54114c06356bSdh 				in += sizeof (uint16_t);
54124c06356bSdh 			}
54134c06356bSdh 			break;
54144c06356bSdh 		}
54154c06356bSdh 		case 3:
54164c06356bSdh 		{
54174c06356bSdh 			uint32_t tmp;
54184c06356bSdh 			while (nbyt-- > 0) {
54194c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint32_t));
54204c06356bSdh 				if (bige) {
54214c06356bSdh 					tmp = BE_32(tmp);
54224c06356bSdh 				} else {
54234c06356bSdh 					tmp = LE_32(tmp);
54244c06356bSdh 				}
54254c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint32_t));
54264c06356bSdh 				out += sizeof (uint32_t);
54274c06356bSdh 				in += sizeof (uint32_t);
54284c06356bSdh 			}
54294c06356bSdh 			break;
54304c06356bSdh 		}
54314c06356bSdh 		default:
5432c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5433c3bc407cSdh 			    "%s: bad size", __func__);
54344c06356bSdh 			return;
54354c06356bSdh 		}
54364c06356bSdh 	}
54374c06356bSdh }
54384c06356bSdh 
54394c06356bSdh const char *
54404c06356bSdh pmcs_get_rate(unsigned int linkrt)
54414c06356bSdh {
54424c06356bSdh 	const char *rate;
54434c06356bSdh 	switch (linkrt) {
54444c06356bSdh 	case SAS_LINK_RATE_1_5GBIT:
54454c06356bSdh 		rate = "1.5";
54464c06356bSdh 		break;
54474c06356bSdh 	case SAS_LINK_RATE_3GBIT:
54484c06356bSdh 		rate = "3.0";
54494c06356bSdh 		break;
54504c06356bSdh 	case SAS_LINK_RATE_6GBIT:
54514c06356bSdh 		rate = "6.0";
54524c06356bSdh 		break;
54534c06356bSdh 	default:
54544c06356bSdh 		rate = "???";
54554c06356bSdh 		break;
54564c06356bSdh 	}
54574c06356bSdh 	return (rate);
54584c06356bSdh }
54594c06356bSdh 
54604c06356bSdh const char *
54614c06356bSdh pmcs_get_typename(pmcs_dtype_t type)
54624c06356bSdh {
54634c06356bSdh 	switch (type) {
54644c06356bSdh 	case NOTHING:
54654c06356bSdh 		return ("NIL");
54664c06356bSdh 	case SATA:
54674c06356bSdh 		return ("SATA");
54684c06356bSdh 	case SAS:
54694c06356bSdh 		return ("SSP");
54704c06356bSdh 	case EXPANDER:
54714c06356bSdh 		return ("EXPANDER");
54724c06356bSdh 	}
54734c06356bSdh 	return ("????");
54744c06356bSdh }
54754c06356bSdh 
54764c06356bSdh const char *
54774c06356bSdh pmcs_tmf2str(int tmf)
54784c06356bSdh {
54794c06356bSdh 	switch (tmf) {
54804c06356bSdh 	case SAS_ABORT_TASK:
54814c06356bSdh 		return ("Abort Task");
54824c06356bSdh 	case SAS_ABORT_TASK_SET:
54834c06356bSdh 		return ("Abort Task Set");
54844c06356bSdh 	case SAS_CLEAR_TASK_SET:
54854c06356bSdh 		return ("Clear Task Set");
54864c06356bSdh 	case SAS_LOGICAL_UNIT_RESET:
54874c06356bSdh 		return ("Logical Unit Reset");
54884c06356bSdh 	case SAS_I_T_NEXUS_RESET:
54894c06356bSdh 		return ("I_T Nexus Reset");
54904c06356bSdh 	case SAS_CLEAR_ACA:
54914c06356bSdh 		return ("Clear ACA");
54924c06356bSdh 	case SAS_QUERY_TASK:
54934c06356bSdh 		return ("Query Task");
54944c06356bSdh 	case SAS_QUERY_TASK_SET:
54954c06356bSdh 		return ("Query Task Set");
54964c06356bSdh 	case SAS_QUERY_UNIT_ATTENTION:
54974c06356bSdh 		return ("Query Unit Attention");
54984c06356bSdh 	default:
54994c06356bSdh 		return ("Unknown");
55004c06356bSdh 	}
55014c06356bSdh }
55024c06356bSdh 
55034c06356bSdh const char *
55044c06356bSdh pmcs_status_str(uint32_t status)
55054c06356bSdh {
55064c06356bSdh 	switch (status) {
55074c06356bSdh 	case PMCOUT_STATUS_OK:
55084c06356bSdh 		return ("OK");
55094c06356bSdh 	case PMCOUT_STATUS_ABORTED:
55104c06356bSdh 		return ("ABORTED");
55114c06356bSdh 	case PMCOUT_STATUS_OVERFLOW:
55124c06356bSdh 		return ("OVERFLOW");
55134c06356bSdh 	case PMCOUT_STATUS_UNDERFLOW:
55144c06356bSdh 		return ("UNDERFLOW");
55154c06356bSdh 	case PMCOUT_STATUS_FAILED:
55164c06356bSdh 		return ("FAILED");
55174c06356bSdh 	case PMCOUT_STATUS_ABORT_RESET:
55184c06356bSdh 		return ("ABORT_RESET");
55194c06356bSdh 	case PMCOUT_STATUS_IO_NOT_VALID:
55204c06356bSdh 		return ("IO_NOT_VALID");
55214c06356bSdh 	case PMCOUT_STATUS_NO_DEVICE:
55224c06356bSdh 		return ("NO_DEVICE");
55234c06356bSdh 	case PMCOUT_STATUS_ILLEGAL_PARAMETER:
55244c06356bSdh 		return ("ILLEGAL_PARAMETER");
55254c06356bSdh 	case PMCOUT_STATUS_LINK_FAILURE:
55264c06356bSdh 		return ("LINK_FAILURE");
55274c06356bSdh 	case PMCOUT_STATUS_PROG_ERROR:
55284c06356bSdh 		return ("PROG_ERROR");
55294c06356bSdh 	case PMCOUT_STATUS_EDC_IN_ERROR:
55304c06356bSdh 		return ("EDC_IN_ERROR");
55314c06356bSdh 	case PMCOUT_STATUS_EDC_OUT_ERROR:
55324c06356bSdh 		return ("EDC_OUT_ERROR");
55334c06356bSdh 	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
55344c06356bSdh 		return ("ERROR_HW_TIMEOUT");
55354c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_BREAK:
55364c06356bSdh 		return ("XFER_ERR_BREAK");
55374c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
55384c06356bSdh 		return ("XFER_ERR_PHY_NOT_READY");
55394c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
55404c06356bSdh 		return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
55414c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
55424c06356bSdh 		return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
55434c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
55444c06356bSdh 		return ("OPEN_CNX_ERROR_BREAK");
55454c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
55464c06356bSdh 		return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
55474c06356bSdh 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
55484c06356bSdh 		return ("OPENCNX_ERROR_BAD_DESTINATION");
55494c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
55504c06356bSdh 		return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
55514c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
55524c06356bSdh 		return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
55534c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
55544c06356bSdh 		return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
55559aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
55569aed1621SDavid Hollister 		return ("OPEN_CNX_ERROR_UNKNOWN_ERROR");
55574c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
55584c06356bSdh 		return ("IO_XFER_ERROR_NAK_RECEIVED");
55594c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
55604c06356bSdh 		return ("XFER_ERROR_ACK_NAK_TIMEOUT");
55614c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
55624c06356bSdh 		return ("XFER_ERROR_PEER_ABORTED");
55634c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
55644c06356bSdh 		return ("XFER_ERROR_RX_FRAME");
55654c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
55664c06356bSdh 		return ("IO_XFER_ERROR_DMA");
55674c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
55684c06356bSdh 		return ("XFER_ERROR_CREDIT_TIMEOUT");
55694c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
55704c06356bSdh 		return ("XFER_ERROR_SATA_LINK_TIMEOUT");
55714c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA:
55724c06356bSdh 		return ("XFER_ERROR_SATA");
55734c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
55744c06356bSdh 		return ("XFER_ERROR_REJECTED_NCQ_MODE");
55754c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
55764c06356bSdh 		return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
55774c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
55784c06356bSdh 		return ("XFER_ERROR_ABORTED_NCQ_MODE");
55794c06356bSdh 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
55804c06356bSdh 		return ("IO_XFER_OPEN_RETRY_TIMEOUT");
55814c06356bSdh 	case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
55824c06356bSdh 		return ("SMP_RESP_CONNECTION_ERROR");
55834c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
55844c06356bSdh 		return ("XFER_ERROR_UNEXPECTED_PHASE");
55854c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
55864c06356bSdh 		return ("XFER_ERROR_RDY_OVERRUN");
55874c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
55884c06356bSdh 		return ("XFER_ERROR_RDY_NOT_EXPECTED");
55894c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
55904c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
55914c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
55924c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
55934c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
55944c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
55954c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
55964c06356bSdh 		return ("XFER_ERROR_OFFSET_MISMATCH");
55974c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
55984c06356bSdh 		return ("XFER_ERROR_ZERO_DATA_LEN");
55994c06356bSdh 	case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
56004c06356bSdh 		return ("XFER_CMD_FRAME_ISSUED");
56014c06356bSdh 	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
56024c06356bSdh 		return ("ERROR_INTERNAL_SMP_RESOURCE");
56034c06356bSdh 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
56044c06356bSdh 		return ("IO_PORT_IN_RESET");
56054c06356bSdh 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
56064c06356bSdh 		return ("DEVICE STATE NON-OPERATIONAL");
56074c06356bSdh 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
56084c06356bSdh 		return ("DEVICE STATE IN RECOVERY");
5609a25672a1SDavid Hollister 	case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
5610a25672a1SDavid Hollister 		return ("OPEN CNX ERR HW RESOURCE BUSY");
56114c06356bSdh 	default:
56124c06356bSdh 		return (NULL);
56134c06356bSdh 	}
56144c06356bSdh }
56154c06356bSdh 
56164c06356bSdh uint64_t
56174c06356bSdh pmcs_barray2wwn(uint8_t ba[8])
56184c06356bSdh {
56194c06356bSdh 	uint64_t result = 0;
56204c06356bSdh 	int i;
56214c06356bSdh 
56224c06356bSdh 	for (i = 0; i < 8; i++) {
56234c06356bSdh 		result <<= 8;
56244c06356bSdh 		result |= ba[i];
56254c06356bSdh 	}
56264c06356bSdh 	return (result);
56274c06356bSdh }
56284c06356bSdh 
56294c06356bSdh void
56304c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
56314c06356bSdh {
56324c06356bSdh 	int i;
56334c06356bSdh 	for (i = 0; i < 8; i++) {
56344c06356bSdh 		ba[7 - i] = wwn & 0xff;
56354c06356bSdh 		wwn >>= 8;
56364c06356bSdh 	}
56374c06356bSdh }
56384c06356bSdh 
56394c06356bSdh void
56404c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp)
56414c06356bSdh {
56424c06356bSdh 	const char *fwsupport;
56434c06356bSdh 	switch (PMCS_FW_TYPE(pwp)) {
56444c06356bSdh 	case PMCS_FW_TYPE_RELEASED:
56454c06356bSdh 		fwsupport = "Released";
56464c06356bSdh 		break;
56474c06356bSdh 	case PMCS_FW_TYPE_DEVELOPMENT:
56484c06356bSdh 		fwsupport = "Development";
56494c06356bSdh 		break;
56504c06356bSdh 	case PMCS_FW_TYPE_ALPHA:
56514c06356bSdh 		fwsupport = "Alpha";
56524c06356bSdh 		break;
56534c06356bSdh 	case PMCS_FW_TYPE_BETA:
56544c06356bSdh 		fwsupport = "Beta";
56554c06356bSdh 		break;
56564c06356bSdh 	default:
56574c06356bSdh 		fwsupport = "Special";
56584c06356bSdh 		break;
56594c06356bSdh 	}
5660c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
56612ac4abe8SDavid Hollister 	    "Chip Revision: %c; F/W Revision %x.%x.%x %s (ILA rev %08x)",
56622ac4abe8SDavid Hollister 	    'A' + pwp->chiprev, PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp),
56632ac4abe8SDavid Hollister 	    PMCS_FW_MICRO(pwp), fwsupport, pwp->ila_ver);
56644c06356bSdh }
56654c06356bSdh 
56664c06356bSdh void
56674c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
56684c06356bSdh {
56694c06356bSdh 	if (pptr->parent) {
56704c06356bSdh 		pmcs_phy_name(pwp, pptr->parent, obuf, olen);
56714c06356bSdh 		(void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
56724c06356bSdh 	} else {
56734c06356bSdh 		(void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
56744c06356bSdh 	}
56754c06356bSdh }
56764c06356bSdh 
56774c06356bSdh /*
56784c06356bSdh  * Implementation for pmcs_find_phy_by_devid.
56794c06356bSdh  * If the PHY is found, it is returned locked.
56804c06356bSdh  */
56814c06356bSdh static pmcs_phy_t *
56824c06356bSdh pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id)
56834c06356bSdh {
56844c06356bSdh 	pmcs_phy_t *match, *cphyp, *nphyp;
56854c06356bSdh 
56864c06356bSdh 	ASSERT(!mutex_owned(&phyp->phy_lock));
56874c06356bSdh 
56884c06356bSdh 	while (phyp) {
56894c06356bSdh 		pmcs_lock_phy(phyp);
56904c06356bSdh 
56914c06356bSdh 		if ((phyp->valid_device_id) && (phyp->device_id == device_id)) {
56924c06356bSdh 			return (phyp);
56934c06356bSdh 		}
56944c06356bSdh 		if (phyp->children) {
56954c06356bSdh 			cphyp = phyp->children;
56964c06356bSdh 			pmcs_unlock_phy(phyp);
56974c06356bSdh 			match = pmcs_find_phy_by_devid_impl(cphyp, device_id);
56984c06356bSdh 			if (match) {
56994c06356bSdh 				ASSERT(mutex_owned(&match->phy_lock));
57004c06356bSdh 				return (match);
57014c06356bSdh 			}
57024c06356bSdh 			pmcs_lock_phy(phyp);
57034c06356bSdh 		}
57044c06356bSdh 
57054c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
57064c06356bSdh 			pmcs_unlock_phy(phyp);
57074c06356bSdh 			phyp = NULL;
57084c06356bSdh 		} else {
57094c06356bSdh 			nphyp = phyp->sibling;
57104c06356bSdh 			pmcs_unlock_phy(phyp);
57114c06356bSdh 			phyp = nphyp;
57124c06356bSdh 		}
57134c06356bSdh 	}
57144c06356bSdh 
57154c06356bSdh 	return (NULL);
57164c06356bSdh }
57174c06356bSdh 
57184c06356bSdh /*
57194c06356bSdh  * If the PHY is found, it is returned locked
57204c06356bSdh  */
57214c06356bSdh pmcs_phy_t *
57224c06356bSdh pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id)
57234c06356bSdh {
57244c06356bSdh 	pmcs_phy_t *phyp, *match = NULL;
57254c06356bSdh 
57264c06356bSdh 	phyp = pwp->root_phys;
57274c06356bSdh 
57284c06356bSdh 	while (phyp) {
57294c06356bSdh 		match = pmcs_find_phy_by_devid_impl(phyp, device_id);
57304c06356bSdh 		if (match) {
57314c06356bSdh 			ASSERT(mutex_owned(&match->phy_lock));
57324c06356bSdh 			return (match);
57334c06356bSdh 		}
57344c06356bSdh 		phyp = phyp->sibling;
57354c06356bSdh 	}
57364c06356bSdh 
57374c06356bSdh 	return (NULL);
57384c06356bSdh }
57394c06356bSdh 
57404c06356bSdh /*
57414c06356bSdh  * This function is called as a sanity check to ensure that a newly registered
57424c06356bSdh  * PHY doesn't have a device_id that exists with another registered PHY.
57434c06356bSdh  */
57444c06356bSdh static boolean_t
57454c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
57464c06356bSdh {
57479aed1621SDavid Hollister 	pmcs_phy_t *pptr, *pchild;
57484c06356bSdh 	boolean_t rval;
57494c06356bSdh 
57504c06356bSdh 	pptr = parent;
57514c06356bSdh 
57524c06356bSdh 	while (pptr) {
57534c06356bSdh 		if (pptr->valid_device_id && (pptr != phyp) &&
57544c06356bSdh 		    (pptr->device_id == device_id)) {
57559aed1621SDavid Hollister 			/*
57569aed1621SDavid Hollister 			 * This can still be OK if both of these PHYs actually
57579aed1621SDavid Hollister 			 * represent the same device (e.g. expander).  It could
57589aed1621SDavid Hollister 			 * be a case of a new "primary" PHY.  If the SAS address
57599aed1621SDavid Hollister 			 * is the same and they have the same parent, we'll
57609aed1621SDavid Hollister 			 * accept this if the PHY to be registered is the
57619aed1621SDavid Hollister 			 * primary.
57629aed1621SDavid Hollister 			 */
57639aed1621SDavid Hollister 			if ((phyp->parent == pptr->parent) &&
57649aed1621SDavid Hollister 			    (memcmp(phyp->sas_address,
57659aed1621SDavid Hollister 			    pptr->sas_address, 8) == 0) && (phyp->width > 1)) {
57669aed1621SDavid Hollister 				/*
57679aed1621SDavid Hollister 				 * Move children over to the new primary and
57689aed1621SDavid Hollister 				 * update both PHYs
57699aed1621SDavid Hollister 				 */
57709aed1621SDavid Hollister 				pmcs_lock_phy(pptr);
57719aed1621SDavid Hollister 				phyp->children = pptr->children;
57729aed1621SDavid Hollister 				pchild = phyp->children;
57739aed1621SDavid Hollister 				while (pchild) {
57749aed1621SDavid Hollister 					pchild->parent = phyp;
57759aed1621SDavid Hollister 					pchild = pchild->sibling;
57769aed1621SDavid Hollister 				}
57779aed1621SDavid Hollister 				phyp->subsidiary = 0;
57789aed1621SDavid Hollister 				phyp->ncphy = pptr->ncphy;
57799aed1621SDavid Hollister 				/*
57809aed1621SDavid Hollister 				 * device_id, valid_device_id, and configured
57819aed1621SDavid Hollister 				 * will be set by the caller
57829aed1621SDavid Hollister 				 */
57839aed1621SDavid Hollister 				pptr->children = NULL;
57849aed1621SDavid Hollister 				pptr->subsidiary = 1;
57859aed1621SDavid Hollister 				pptr->ncphy = 0;
57869aed1621SDavid Hollister 				pmcs_unlock_phy(pptr);
57879aed1621SDavid Hollister 				pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
57889aed1621SDavid Hollister 				    "%s: Moving device_id %d from PHY %s to %s",
57899aed1621SDavid Hollister 				    __func__, device_id, pptr->path,
57909aed1621SDavid Hollister 				    phyp->path);
57919aed1621SDavid Hollister 				return (B_TRUE);
57929aed1621SDavid Hollister 			}
5793c3bc407cSdh 			pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
57944c06356bSdh 			    "%s: phy %s already exists as %s with "
57954c06356bSdh 			    "device id 0x%x", __func__, phyp->path,
57964c06356bSdh 			    pptr->path, device_id);
57974c06356bSdh 			return (B_FALSE);
57984c06356bSdh 		}
57994c06356bSdh 
58004c06356bSdh 		if (pptr->children) {
58014c06356bSdh 			rval = pmcs_validate_devid(pptr->children, phyp,
58024c06356bSdh 			    device_id);
58034c06356bSdh 			if (rval == B_FALSE) {
58044c06356bSdh 				return (rval);
58054c06356bSdh 			}
58064c06356bSdh 		}
58074c06356bSdh 
58084c06356bSdh 		pptr = pptr->sibling;
58094c06356bSdh 	}
58104c06356bSdh 
58114c06356bSdh 	/* This PHY and device_id are valid */
58124c06356bSdh 	return (B_TRUE);
58134c06356bSdh }
58144c06356bSdh 
58154c06356bSdh /*
58164c06356bSdh  * If the PHY is found, it is returned locked
58174c06356bSdh  */
58184c06356bSdh static pmcs_phy_t *
58194c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
58204c06356bSdh {
58214c06356bSdh 	pmcs_phy_t *matched_phy, *cphyp, *nphyp;
58224c06356bSdh 
58234c06356bSdh 	ASSERT(!mutex_owned(&phyp->phy_lock));
58244c06356bSdh 
58254c06356bSdh 	while (phyp) {
58264c06356bSdh 		pmcs_lock_phy(phyp);
58274c06356bSdh 
58284c06356bSdh 		if (phyp->valid_device_id) {
58294c06356bSdh 			if (memcmp(phyp->sas_address, wwn, 8) == 0) {
58304c06356bSdh 				return (phyp);
58314c06356bSdh 			}
58324c06356bSdh 		}
58334c06356bSdh 
58344c06356bSdh 		if (phyp->children) {
58354c06356bSdh 			cphyp = phyp->children;
58364c06356bSdh 			pmcs_unlock_phy(phyp);
58374c06356bSdh 			matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
58384c06356bSdh 			if (matched_phy) {
58394c06356bSdh 				ASSERT(mutex_owned(&matched_phy->phy_lock));
58404c06356bSdh 				return (matched_phy);
58414c06356bSdh 			}
58424c06356bSdh 			pmcs_lock_phy(phyp);
58434c06356bSdh 		}
58444c06356bSdh 
58454c06356bSdh 		/*
58464c06356bSdh 		 * Only iterate through non-root PHYs
58474c06356bSdh 		 */
58484c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
58494c06356bSdh 			pmcs_unlock_phy(phyp);
58504c06356bSdh 			phyp = NULL;
58514c06356bSdh 		} else {
58524c06356bSdh 			nphyp = phyp->sibling;
58534c06356bSdh 			pmcs_unlock_phy(phyp);
58544c06356bSdh 			phyp = nphyp;
58554c06356bSdh 		}
58564c06356bSdh 	}
58574c06356bSdh 
58584c06356bSdh 	return (NULL);
58594c06356bSdh }
58604c06356bSdh 
58614c06356bSdh pmcs_phy_t *
58624c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
58634c06356bSdh {
58644c06356bSdh 	uint8_t ebstr[8];
58654c06356bSdh 	pmcs_phy_t *pptr, *matched_phy;
58664c06356bSdh 
58674c06356bSdh 	pmcs_wwn2barray(wwn, ebstr);
58684c06356bSdh 
58694c06356bSdh 	pptr = pwp->root_phys;
58704c06356bSdh 	while (pptr) {
58714c06356bSdh 		matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
58724c06356bSdh 		if (matched_phy) {
58734c06356bSdh 			ASSERT(mutex_owned(&matched_phy->phy_lock));
58744c06356bSdh 			return (matched_phy);
58754c06356bSdh 		}
58764c06356bSdh 
58774c06356bSdh 		pptr = pptr->sibling;
58784c06356bSdh 	}
58794c06356bSdh 
58804c06356bSdh 	return (NULL);
58814c06356bSdh }
58824c06356bSdh 
58834c06356bSdh 
58844c06356bSdh /*
58854c06356bSdh  * pmcs_find_phy_by_sas_address
58864c06356bSdh  *
58874c06356bSdh  * Find a PHY that both matches "sas_addr" and is on "iport".
58884c06356bSdh  * If a matching PHY is found, it is returned locked.
58894c06356bSdh  */
58904c06356bSdh pmcs_phy_t *
58914c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
58924c06356bSdh     pmcs_phy_t *root, char *sas_addr)
58934c06356bSdh {
58944c06356bSdh 	int ua_form = 1;
58954c06356bSdh 	uint64_t wwn;
58964c06356bSdh 	char addr[PMCS_MAX_UA_SIZE];
58974c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pchild;
58984c06356bSdh 
58994c06356bSdh 	if (root == NULL) {
59004c06356bSdh 		pptr = pwp->root_phys;
59014c06356bSdh 	} else {
59024c06356bSdh 		pptr = root;
59034c06356bSdh 	}
59044c06356bSdh 
59054c06356bSdh 	while (pptr) {
59064c06356bSdh 		pmcs_lock_phy(pptr);
59074c06356bSdh 		/*
59084c06356bSdh 		 * If the PHY is dead or does not have a valid device ID,
59094c06356bSdh 		 * skip it.
59104c06356bSdh 		 */
59114c06356bSdh 		if ((pptr->dead) || (!pptr->valid_device_id)) {
59124c06356bSdh 			goto next_phy;
59134c06356bSdh 		}
59144c06356bSdh 
59154c06356bSdh 		if (pptr->iport != iport) {
59164c06356bSdh 			goto next_phy;
59174c06356bSdh 		}
59184c06356bSdh 
59194c06356bSdh 		wwn = pmcs_barray2wwn(pptr->sas_address);
59204c06356bSdh 		(void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
59214c06356bSdh 		if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
59224c06356bSdh 			return (pptr);
59234c06356bSdh 		}
59244c06356bSdh 
59254c06356bSdh 		if (pptr->children) {
59264c06356bSdh 			pchild = pptr->children;
59274c06356bSdh 			pmcs_unlock_phy(pptr);
59284c06356bSdh 			pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
59294c06356bSdh 			    sas_addr);
59304c06356bSdh 			if (pnext) {
59314c06356bSdh 				return (pnext);
59324c06356bSdh 			}
59334c06356bSdh 			pmcs_lock_phy(pptr);
59344c06356bSdh 		}
59354c06356bSdh 
59364c06356bSdh next_phy:
59374c06356bSdh 		pnext = pptr->sibling;
59384c06356bSdh 		pmcs_unlock_phy(pptr);
59394c06356bSdh 		pptr = pnext;
59404c06356bSdh 	}
59414c06356bSdh 
59424c06356bSdh 	return (NULL);
59434c06356bSdh }
59444c06356bSdh 
59454c06356bSdh void
59464c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
59474c06356bSdh {
59484c06356bSdh 	switch (fis[0] & 0xff) {
59494c06356bSdh 	case FIS_REG_H2DEV:
5950c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5951c3bc407cSdh 		    "FIS REGISTER HOST TO DEVICE: "
59524c06356bSdh 		    "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
59534c06356bSdh 		    "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
59544c06356bSdh 		    BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
59554c06356bSdh 		    (unsigned long long)
59564c06356bSdh 		    (((uint64_t)fis[2] & 0x00ffffff) << 24 |
59574c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
59584c06356bSdh 		break;
59594c06356bSdh 	case FIS_REG_D2H:
5960c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5961c3bc407cSdh 		    "FIS REGISTER DEVICE TO HOST: Status=0x%02x "
5962c3bc407cSdh 		    "Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
59634c06356bSdh 		    BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
59644c06356bSdh 		    (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
59654c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
59664c06356bSdh 		break;
59674c06356bSdh 	default:
5968c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
5969a25672a1SDavid Hollister 		    "FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
5970a25672a1SDavid Hollister 		    fis[0], fis[1], fis[2], fis[3], fis[4]);
59714c06356bSdh 		break;
59724c06356bSdh 	}
59734c06356bSdh }
59744c06356bSdh 
59754c06356bSdh void
59764c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
59774c06356bSdh {
59784c06356bSdh 	uint32_t *mb = arg;
59794c06356bSdh 	size_t i;
59804c06356bSdh 
5981c3bc407cSdh 	pmcs_prt(pwp, level, NULL, NULL, msg);
59824c06356bSdh 	for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
5983c3bc407cSdh 		pmcs_prt(pwp, level, NULL, NULL,
5984c3bc407cSdh 		    "Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x",
5985c3bc407cSdh 		    i * sizeof (uint32_t), LE_32(mb[i]),
5986c3bc407cSdh 		    LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3]));
59874c06356bSdh 	}
59884c06356bSdh }
59894c06356bSdh 
59904c06356bSdh /*
59914c06356bSdh  * If phyp == NULL we're being called from the worker thread, in which
59924c06356bSdh  * case we need to check all the PHYs.  In this case, the softstate lock
59934c06356bSdh  * will be held.
59944c06356bSdh  * If phyp is non-NULL, just issue the spinup release for the specified PHY
59954c06356bSdh  * (which will already be locked).
59964c06356bSdh  */
59974c06356bSdh void
59984c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
59994c06356bSdh {
60004c06356bSdh 	uint32_t *msg;
60014c06356bSdh 	struct pmcwork *pwrk;
60024c06356bSdh 	pmcs_phy_t *tphyp;
60034c06356bSdh 
60044c06356bSdh 	if (phyp != NULL) {
60054c06356bSdh 		ASSERT(mutex_owned(&phyp->phy_lock));
6006c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
60074c06356bSdh 		    "%s: Issuing spinup release only for PHY %s", __func__,
60084c06356bSdh 		    phyp->path);
60094c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
60104c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60114c06356bSdh 		if (msg == NULL || (pwrk =
60124c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
60134c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
60144c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
60154c06356bSdh 			return;
60164c06356bSdh 		}
60174c06356bSdh 
60184c06356bSdh 		phyp->spinup_hold = 0;
60194c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
60204c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
60214c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
60224c06356bSdh 		msg[1] = LE_32(pwrk->htag);
60234c06356bSdh 		msg[2] = LE_32((0x10 << 8) | phyp->phynum);
60244c06356bSdh 
60254c06356bSdh 		pwrk->dtype = phyp->dtype;
60264c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
60274c06356bSdh 		mutex_exit(&pwrk->lock);
60284c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60294c06356bSdh 		return;
60304c06356bSdh 	}
60314c06356bSdh 
60324c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
60334c06356bSdh 
60344c06356bSdh 	tphyp = pwp->root_phys;
60354c06356bSdh 	while (tphyp) {
60364c06356bSdh 		pmcs_lock_phy(tphyp);
60374c06356bSdh 		if (tphyp->spinup_hold == 0) {
60384c06356bSdh 			pmcs_unlock_phy(tphyp);
60394c06356bSdh 			tphyp = tphyp->sibling;
60404c06356bSdh 			continue;
60414c06356bSdh 		}
60424c06356bSdh 
6043c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
60444c06356bSdh 		    "%s: Issuing spinup release for PHY %s", __func__,
60454c06356bSdh 		    phyp->path);
60464c06356bSdh 
60474c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
60484c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60494c06356bSdh 		if (msg == NULL || (pwrk =
60504c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
60514c06356bSdh 			pmcs_unlock_phy(tphyp);
60524c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
60534c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
60544c06356bSdh 			break;
60554c06356bSdh 		}
60564c06356bSdh 
60574c06356bSdh 		tphyp->spinup_hold = 0;
60584c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
60594c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
60604c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
60614c06356bSdh 		msg[1] = LE_32(pwrk->htag);
60624c06356bSdh 		msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
60634c06356bSdh 
60644c06356bSdh 		pwrk->dtype = phyp->dtype;
60654c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
60664c06356bSdh 		mutex_exit(&pwrk->lock);
60674c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
60684c06356bSdh 		pmcs_unlock_phy(tphyp);
60694c06356bSdh 
60704c06356bSdh 		tphyp = tphyp->sibling;
60714c06356bSdh 	}
60724c06356bSdh }
60734c06356bSdh 
60744c06356bSdh /*
60754c06356bSdh  * Abort commands on dead PHYs and deregister them as well as removing
60764c06356bSdh  * the associated targets.
60774c06356bSdh  */
60784c06356bSdh static int
60794c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
60804c06356bSdh {
60814c06356bSdh 	pmcs_phy_t *pnext, *pchild;
60824c06356bSdh 	boolean_t remove_device;
60834c06356bSdh 	int rval = 0;
60844c06356bSdh 
60854c06356bSdh 	while (phyp) {
60864c06356bSdh 		pmcs_lock_phy(phyp);
60874c06356bSdh 		pchild = phyp->children;
60884c06356bSdh 		pnext = phyp->sibling;
60894c06356bSdh 		pmcs_unlock_phy(phyp);
60904c06356bSdh 
60914c06356bSdh 		if (pchild) {
60924c06356bSdh 			rval = pmcs_kill_devices(pwp, pchild);
60934c06356bSdh 			if (rval) {
60944c06356bSdh 				return (rval);
60954c06356bSdh 			}
60964c06356bSdh 		}
60974c06356bSdh 
60984c06356bSdh 		/*
60994c06356bSdh 		 * pmcs_remove_device requires the softstate lock.
61004c06356bSdh 		 */
61014c06356bSdh 		mutex_enter(&pwp->lock);
61024c06356bSdh 		pmcs_lock_phy(phyp);
61034c06356bSdh 		if (phyp->dead && phyp->valid_device_id) {
61044c06356bSdh 			remove_device = B_TRUE;
61054c06356bSdh 		} else {
61064c06356bSdh 			remove_device = B_FALSE;
61074c06356bSdh 		}
61084c06356bSdh 
61094c06356bSdh 		if (remove_device) {
61104c06356bSdh 			pmcs_remove_device(pwp, phyp);
61114c06356bSdh 			mutex_exit(&pwp->lock);
61124c06356bSdh 
61134c06356bSdh 			rval = pmcs_kill_device(pwp, phyp);
61144c06356bSdh 
61154c06356bSdh 			if (rval) {
61164c06356bSdh 				pmcs_unlock_phy(phyp);
61174c06356bSdh 				return (rval);
61184c06356bSdh 			}
61194c06356bSdh 		} else {
61204c06356bSdh 			mutex_exit(&pwp->lock);
61214c06356bSdh 		}
61224c06356bSdh 
61234c06356bSdh 		pmcs_unlock_phy(phyp);
61244c06356bSdh 		phyp = pnext;
61254c06356bSdh 	}
61264c06356bSdh 
61274c06356bSdh 	return (rval);
61284c06356bSdh }
61294c06356bSdh 
61304c06356bSdh /*
61314c06356bSdh  * Called with PHY locked
61324c06356bSdh  */
61334c06356bSdh int
61344c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
61354c06356bSdh {
61364c06356bSdh 	int r, result;
61374c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
61384c06356bSdh 	struct pmcwork *pwrk;
61394c06356bSdh 
6140c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s",
61414c06356bSdh 	    pmcs_get_typename(pptr->dtype), pptr->path);
61424c06356bSdh 
61434c06356bSdh 	/*
61444c06356bSdh 	 * There may be an outstanding ABORT_ALL running, which we wouldn't
61454c06356bSdh 	 * know just by checking abort_pending.  We can, however, check
61464c06356bSdh 	 * abort_all_start.  If it's non-zero, there is one, and we'll just
61474c06356bSdh 	 * sit here and wait for it to complete.  If we don't, we'll remove
61484c06356bSdh 	 * the device while there are still commands pending.
61494c06356bSdh 	 */
61504c06356bSdh 	if (pptr->abort_all_start) {
61514c06356bSdh 		while (pptr->abort_all_start) {
6152c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
61534c06356bSdh 			    "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
61544c06356bSdh 			    __func__, (void *)pptr);
61554c06356bSdh 			cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
61564c06356bSdh 		}
61574c06356bSdh 	} else if (pptr->abort_pending) {
61584c06356bSdh 		r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
61594c06356bSdh 
61604c06356bSdh 		if (r) {
6161c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
61624c06356bSdh 			    "%s: ABORT_ALL returned non-zero status (%d) for "
61634c06356bSdh 			    "PHY 0x%p", __func__, r, (void *)pptr);
61644c06356bSdh 			return (r);
61654c06356bSdh 		}
61664c06356bSdh 		pptr->abort_pending = 0;
61674c06356bSdh 	}
61684c06356bSdh 
61694c06356bSdh 	if (pptr->valid_device_id == 0) {
61704c06356bSdh 		return (0);
61714c06356bSdh 	}
61724c06356bSdh 
61734c06356bSdh 	if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
6174c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
61754c06356bSdh 		return (ENOMEM);
61764c06356bSdh 	}
61774c06356bSdh 	pwrk->arg = msg;
61784c06356bSdh 	pwrk->dtype = pptr->dtype;
61794c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
61804c06356bSdh 	    PMCIN_DEREGISTER_DEVICE_HANDLE));
61814c06356bSdh 	msg[1] = LE_32(pwrk->htag);
61824c06356bSdh 	msg[2] = LE_32(pptr->device_id);
61834c06356bSdh 
61844c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
61854c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
61864c06356bSdh 	if (ptr == NULL) {
61874c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
61884c06356bSdh 		mutex_exit(&pwrk->lock);
6189c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
61904c06356bSdh 		return (ENOMEM);
61914c06356bSdh 	}
61924c06356bSdh 
61934c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
61944c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
61954c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
61964c06356bSdh 
61974c06356bSdh 	pmcs_unlock_phy(pptr);
61984c06356bSdh 	WAIT_FOR(pwrk, 250, result);
61994c06356bSdh 	pmcs_lock_phy(pptr);
62004c06356bSdh 	pmcs_pwork(pwp, pwrk);
62014c06356bSdh 
62024c06356bSdh 	if (result) {
62034c06356bSdh 		return (ETIMEDOUT);
62044c06356bSdh 	}
62054c06356bSdh 	status = LE_32(msg[2]);
62064c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
6207c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
62084c06356bSdh 		    "%s: status 0x%x when trying to deregister device %s",
62094c06356bSdh 		    __func__, status, pptr->path);
62104c06356bSdh 	}
62114c06356bSdh 
62124c06356bSdh 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
62134c06356bSdh 	PHY_CHANGED(pwp, pptr);
62144c06356bSdh 	RESTART_DISCOVERY(pwp);
62154c06356bSdh 	pptr->valid_device_id = 0;
62164c06356bSdh 	return (0);
62174c06356bSdh }
62184c06356bSdh 
62194c06356bSdh /*
62204c06356bSdh  * Acknowledge the SAS h/w events that need acknowledgement.
62214c06356bSdh  * This is only needed for first level PHYs.
62224c06356bSdh  */
62234c06356bSdh void
62244c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp)
62254c06356bSdh {
62264c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
62274c06356bSdh 	struct pmcwork *pwrk;
62284c06356bSdh 	pmcs_phy_t *pptr;
62294c06356bSdh 
62304c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
62314c06356bSdh 		pmcs_lock_phy(pptr);
62324c06356bSdh 		if (pptr->hw_event_ack == 0) {
62334c06356bSdh 			pmcs_unlock_phy(pptr);
62344c06356bSdh 			continue;
62354c06356bSdh 		}
62364c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
62374c06356bSdh 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
62384c06356bSdh 
62394c06356bSdh 		if ((ptr == NULL) || (pwrk =
62404c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
62414c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
62424c06356bSdh 			pmcs_unlock_phy(pptr);
62434c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
62444c06356bSdh 			break;
62454c06356bSdh 		}
62464c06356bSdh 
62474c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
62484c06356bSdh 		    PMCIN_SAW_HW_EVENT_ACK));
62494c06356bSdh 		msg[1] = LE_32(pwrk->htag);
62504c06356bSdh 		msg[2] = LE_32(pptr->hw_event_ack);
62514c06356bSdh 
62524c06356bSdh 		mutex_exit(&pwrk->lock);
62534c06356bSdh 		pwrk->dtype = pptr->dtype;
62544c06356bSdh 		pptr->hw_event_ack = 0;
62554c06356bSdh 		COPY_MESSAGE(ptr, msg, 3);
62564c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
62574c06356bSdh 		pmcs_unlock_phy(pptr);
62584c06356bSdh 	}
62594c06356bSdh }
62604c06356bSdh 
62614c06356bSdh /*
62624c06356bSdh  * Load DMA
62634c06356bSdh  */
62644c06356bSdh int
62654c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
62664c06356bSdh {
62674c06356bSdh 	ddi_dma_cookie_t *sg;
62684c06356bSdh 	pmcs_dmachunk_t *tc;
62694c06356bSdh 	pmcs_dmasgl_t *sgl, *prior;
62704c06356bSdh 	int seg, tsc;
62714c06356bSdh 	uint64_t sgl_addr;
62724c06356bSdh 
62734c06356bSdh 	/*
62744c06356bSdh 	 * If we have no data segments, we're done.
62754c06356bSdh 	 */
62764c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 0) {
62774c06356bSdh 		return (0);
62784c06356bSdh 	}
62794c06356bSdh 
62804c06356bSdh 	/*
62814c06356bSdh 	 * Get the S/G list pointer.
62824c06356bSdh 	 */
62834c06356bSdh 	sg = CMD2PKT(sp)->pkt_cookies;
62844c06356bSdh 
62854c06356bSdh 	/*
62864c06356bSdh 	 * If we only have one dma segment, we can directly address that
62874c06356bSdh 	 * data within the Inbound message itself.
62884c06356bSdh 	 */
62894c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 1) {
62904c06356bSdh 		msg[12] = LE_32(DWORD0(sg->dmac_laddress));
62914c06356bSdh 		msg[13] = LE_32(DWORD1(sg->dmac_laddress));
62924c06356bSdh 		msg[14] = LE_32(sg->dmac_size);
62934c06356bSdh 		msg[15] = 0;
62944c06356bSdh 		return (0);
62954c06356bSdh 	}
62964c06356bSdh 
62974c06356bSdh 	/*
62984c06356bSdh 	 * Otherwise, we'll need one or more external S/G list chunks.
62994c06356bSdh 	 * Get the first one and its dma address into the Inbound message.
63004c06356bSdh 	 */
63014c06356bSdh 	mutex_enter(&pwp->dma_lock);
63024c06356bSdh 	tc = pwp->dma_freelist;
63034c06356bSdh 	if (tc == NULL) {
63044c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
63054c06356bSdh 		mutex_exit(&pwp->dma_lock);
6306c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
6307c3bc407cSdh 		    "%s: out of SG lists", __func__);
63084c06356bSdh 		return (-1);
63094c06356bSdh 	}
63104c06356bSdh 	pwp->dma_freelist = tc->nxt;
63114c06356bSdh 	mutex_exit(&pwp->dma_lock);
63124c06356bSdh 
63134c06356bSdh 	tc->nxt = NULL;
63144c06356bSdh 	sp->cmd_clist = tc;
63154c06356bSdh 	sgl = tc->chunks;
63164c06356bSdh 	(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
63174c06356bSdh 	sgl_addr = tc->addr;
63184c06356bSdh 	msg[12] = LE_32(DWORD0(sgl_addr));
63194c06356bSdh 	msg[13] = LE_32(DWORD1(sgl_addr));
63204c06356bSdh 	msg[14] = 0;
63214c06356bSdh 	msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
63224c06356bSdh 
63234c06356bSdh 	prior = sgl;
63244c06356bSdh 	tsc = 0;
63254c06356bSdh 
63264c06356bSdh 	for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
63274c06356bSdh 		/*
63284c06356bSdh 		 * If the current segment count for this chunk is one less than
63294c06356bSdh 		 * the number s/g lists per chunk and we have more than one seg
63304c06356bSdh 		 * to go, we need another chunk. Get it, and make sure that the
63314c06356bSdh 		 * tail end of the the previous chunk points the new chunk
63324c06356bSdh 		 * (if remembering an offset can be called 'pointing to').
63334c06356bSdh 		 *
63344c06356bSdh 		 * Note that we can store the offset into our command area that
63354c06356bSdh 		 * represents the new chunk in the length field of the part
63364c06356bSdh 		 * that points the PMC chip at the next chunk- the PMC chip
63374c06356bSdh 		 * ignores this field when the EXTENSION bit is set.
63384c06356bSdh 		 *
63394c06356bSdh 		 * This is required for dma unloads later.
63404c06356bSdh 		 */
63414c06356bSdh 		if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
63424c06356bSdh 		    seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
63434c06356bSdh 			mutex_enter(&pwp->dma_lock);
63444c06356bSdh 			tc = pwp->dma_freelist;
63454c06356bSdh 			if (tc == NULL) {
63464c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
63474c06356bSdh 				mutex_exit(&pwp->dma_lock);
63484c06356bSdh 				pmcs_dma_unload(pwp, sp);
6349c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
63504c06356bSdh 				    "%s: out of SG lists", __func__);
63514c06356bSdh 				return (-1);
63524c06356bSdh 			}
63534c06356bSdh 			pwp->dma_freelist = tc->nxt;
63544c06356bSdh 			tc->nxt = sp->cmd_clist;
63554c06356bSdh 			mutex_exit(&pwp->dma_lock);
63564c06356bSdh 
63574c06356bSdh 			sp->cmd_clist = tc;
63584c06356bSdh 			(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
63594c06356bSdh 			sgl = tc->chunks;
63604c06356bSdh 			sgl_addr = tc->addr;
63614c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglal =
63624c06356bSdh 			    LE_32(DWORD0(sgl_addr));
63634c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglah =
63644c06356bSdh 			    LE_32(DWORD1(sgl_addr));
63654c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
63664c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].flags =
63674c06356bSdh 			    LE_32(PMCS_DMASGL_EXTENSION);
63684c06356bSdh 			prior = sgl;
63694c06356bSdh 			tsc = 0;
63704c06356bSdh 		}
63714c06356bSdh 		sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
63724c06356bSdh 		sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
63734c06356bSdh 		sgl[tsc].sglen = LE_32(sg->dmac_size);
63744c06356bSdh 		sgl[tsc++].flags = 0;
63754c06356bSdh 		sg++;
63764c06356bSdh 	}
63774c06356bSdh 	return (0);
63784c06356bSdh }
63794c06356bSdh 
63804c06356bSdh /*
63814c06356bSdh  * Unload DMA
63824c06356bSdh  */
63834c06356bSdh void
63844c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
63854c06356bSdh {
63864c06356bSdh 	pmcs_dmachunk_t *cp;
63874c06356bSdh 
63884c06356bSdh 	mutex_enter(&pwp->dma_lock);
63894c06356bSdh 	while ((cp = sp->cmd_clist) != NULL) {
63904c06356bSdh 		sp->cmd_clist = cp->nxt;
63914c06356bSdh 		cp->nxt = pwp->dma_freelist;
63924c06356bSdh 		pwp->dma_freelist = cp;
63934c06356bSdh 	}
63944c06356bSdh 	mutex_exit(&pwp->dma_lock);
63954c06356bSdh }
63964c06356bSdh 
63974c06356bSdh /*
63984c06356bSdh  * Take a chunk of consistent memory that has just been allocated and inserted
63994c06356bSdh  * into the cip indices and prepare it for DMA chunk usage and add it to the
64004c06356bSdh  * freelist.
64014c06356bSdh  *
64024c06356bSdh  * Called with dma_lock locked (except during attach when it's unnecessary)
64034c06356bSdh  */
64044c06356bSdh void
64054c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
64064c06356bSdh     pmcs_chunk_t *pchunk, unsigned long lim)
64074c06356bSdh {
64084c06356bSdh 	unsigned long off, n;
64094c06356bSdh 	pmcs_dmachunk_t *np = dcp;
64104c06356bSdh 	pmcs_chunk_t *tmp_chunk;
64114c06356bSdh 
64124c06356bSdh 	if (pwp->dma_chunklist == NULL) {
64134c06356bSdh 		pwp->dma_chunklist = pchunk;
64144c06356bSdh 	} else {
64154c06356bSdh 		tmp_chunk = pwp->dma_chunklist;
64164c06356bSdh 		while (tmp_chunk->next) {
64174c06356bSdh 			tmp_chunk = tmp_chunk->next;
64184c06356bSdh 		}
64194c06356bSdh 		tmp_chunk->next = pchunk;
64204c06356bSdh 	}
64214c06356bSdh 
64224c06356bSdh 	/*
64234c06356bSdh 	 * Install offsets into chunk lists.
64244c06356bSdh 	 */
64254c06356bSdh 	for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
64264c06356bSdh 		np->chunks = (void *)&pchunk->addrp[off];
64274c06356bSdh 		np->addr = pchunk->dma_addr + off;
64284c06356bSdh 		np->acc_handle = pchunk->acc_handle;
64294c06356bSdh 		np->dma_handle = pchunk->dma_handle;
64304c06356bSdh 		if ((off + PMCS_SGL_CHUNKSZ) < lim) {
64314c06356bSdh 			np = np->nxt;
64324c06356bSdh 		}
64334c06356bSdh 	}
64344c06356bSdh 	np->nxt = pwp->dma_freelist;
64354c06356bSdh 	pwp->dma_freelist = dcp;
6436c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
64374c06356bSdh 	    "added %lu DMA chunks ", n);
64384c06356bSdh }
64394c06356bSdh 
64404c06356bSdh /*
64414c06356bSdh  * Change the value of the interrupt coalescing timer.  This is done currently
64424c06356bSdh  * only for I/O completions.  If we're using the "auto clear" feature, it can
64434c06356bSdh  * be turned back on when interrupt coalescing is turned off and must be
64444c06356bSdh  * turned off when the coalescing timer is on.
64454c06356bSdh  * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value.  As long
64464c06356bSdh  * as that's true, we don't need to distinguish between them.
64474c06356bSdh  */
64484c06356bSdh 
64494c06356bSdh void
64504c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
64514c06356bSdh {
64524c06356bSdh 	if (adj == DECREASE_TIMER) {
64534c06356bSdh 		/* If the timer is already off, nothing to do. */
64544c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
64554c06356bSdh 			return;
64564c06356bSdh 		}
64574c06356bSdh 
64584c06356bSdh 		pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
64594c06356bSdh 
64604c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer == 0) {
64614c06356bSdh 			/* Disable the timer */
64624c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
64634c06356bSdh 
64644c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
64654c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
64664c06356bSdh 				    pwp->odb_auto_clear);
64674c06356bSdh 			}
64684c06356bSdh 
64694c06356bSdh 			pwp->io_intr_coal.timer_on = B_FALSE;
64704c06356bSdh 			pwp->io_intr_coal.max_io_completions = B_FALSE;
64714c06356bSdh 			pwp->io_intr_coal.num_intrs = 0;
64724c06356bSdh 			pwp->io_intr_coal.int_cleared = B_FALSE;
64734c06356bSdh 			pwp->io_intr_coal.num_io_completions = 0;
64744c06356bSdh 
64754c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
64764c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
64774c06356bSdh 		} else {
64784c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
64794c06356bSdh 			    pwp->io_intr_coal.intr_coal_timer);
64804c06356bSdh 		}
64814c06356bSdh 	} else {
64824c06356bSdh 		/*
64834c06356bSdh 		 * If the timer isn't on yet, do the setup for it now.
64844c06356bSdh 		 */
64854c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
64864c06356bSdh 			/* If auto clear is being used, turn it off. */
64874c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
64884c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
64894c06356bSdh 				    (pwp->odb_auto_clear &
64904c06356bSdh 				    ~(1 << PMCS_MSIX_IODONE)));
64914c06356bSdh 			}
64924c06356bSdh 
64934c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
64944c06356bSdh 			    (1 << PMCS_MSIX_IODONE));
64954c06356bSdh 			pwp->io_intr_coal.timer_on = B_TRUE;
64964c06356bSdh 			pwp->io_intr_coal.intr_coal_timer =
64974c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
64984c06356bSdh 
64994c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
65004c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
65014c06356bSdh 		} else {
65024c06356bSdh 			pwp->io_intr_coal.intr_coal_timer +=
65034c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
65044c06356bSdh 		}
65054c06356bSdh 
65064c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
65074c06356bSdh 			pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
65084c06356bSdh 		}
65094c06356bSdh 
65104c06356bSdh 		pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
65114c06356bSdh 		    pwp->io_intr_coal.intr_coal_timer);
65124c06356bSdh 	}
65134c06356bSdh 
65144c06356bSdh 	/*
65154c06356bSdh 	 * Adjust the interrupt threshold based on the current timer value
65164c06356bSdh 	 */
65174c06356bSdh 	pwp->io_intr_coal.intr_threshold =
65184c06356bSdh 	    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
65194c06356bSdh 	    (pwp->io_intr_coal.intr_latency +
65204c06356bSdh 	    (pwp->io_intr_coal.intr_coal_timer * 1000)));
65214c06356bSdh }
65224c06356bSdh 
65234c06356bSdh /*
65244c06356bSdh  * Register Access functions
65254c06356bSdh  */
65264c06356bSdh uint32_t
65274c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
65284c06356bSdh {
65294c06356bSdh 	uint32_t iqci;
65304c06356bSdh 
65314c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
65324c06356bSdh 	    DDI_SUCCESS) {
6533c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6534c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
65354c06356bSdh 	}
65364c06356bSdh 
65374c06356bSdh 	iqci = LE_32(
65384c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
65394c06356bSdh 
65404c06356bSdh 	return (iqci);
65414c06356bSdh }
65424c06356bSdh 
65434c06356bSdh uint32_t
65444c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
65454c06356bSdh {
65464c06356bSdh 	uint32_t oqpi;
65474c06356bSdh 
65484c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
65494c06356bSdh 	    DDI_SUCCESS) {
6550c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6551c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
65524c06356bSdh 	}
65534c06356bSdh 
65544c06356bSdh 	oqpi = LE_32(
65554c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
65564c06356bSdh 
65574c06356bSdh 	return (oqpi);
65584c06356bSdh }
65594c06356bSdh 
65604c06356bSdh uint32_t
65612ac4abe8SDavid Hollister pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint8_t hi, uint32_t off)
65624c06356bSdh {
65632ac4abe8SDavid Hollister 	uint32_t rv, newaxil, oldaxil, oldaxih;
65644c06356bSdh 
65654c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
65664c06356bSdh 	off &= GSM_BASE_MASK;
65674c06356bSdh 	mutex_enter(&pwp->axil_lock);
65684c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
65694c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
65704c06356bSdh 	ddi_put32(pwp->top_acc_handle,
65714c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
65724c06356bSdh 	drv_usecwait(10);
65734c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
65744c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6575c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6576c3bc407cSdh 		    "AXIL register update failed");
65774c06356bSdh 	}
65782ac4abe8SDavid Hollister 	if (hi) {
65792ac4abe8SDavid Hollister 		oldaxih = ddi_get32(pwp->top_acc_handle,
65802ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]);
65812ac4abe8SDavid Hollister 		ddi_put32(pwp->top_acc_handle,
65822ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], hi);
65832ac4abe8SDavid Hollister 		drv_usecwait(10);
65842ac4abe8SDavid Hollister 		if (ddi_get32(pwp->top_acc_handle,
65852ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != hi) {
65862ac4abe8SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
65872ac4abe8SDavid Hollister 			    "AXIH register update failed");
65882ac4abe8SDavid Hollister 		}
65892ac4abe8SDavid Hollister 	}
65904c06356bSdh 	rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
65912ac4abe8SDavid Hollister 	if (hi) {
65922ac4abe8SDavid Hollister 		ddi_put32(pwp->top_acc_handle,
65932ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], oldaxih);
65942ac4abe8SDavid Hollister 		drv_usecwait(10);
65952ac4abe8SDavid Hollister 		if (ddi_get32(pwp->top_acc_handle,
65962ac4abe8SDavid Hollister 		    &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != oldaxih) {
65972ac4abe8SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
65982ac4abe8SDavid Hollister 			    "AXIH register restore failed");
65992ac4abe8SDavid Hollister 		}
66002ac4abe8SDavid Hollister 	}
66014c06356bSdh 	ddi_put32(pwp->top_acc_handle,
66024c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
66034c06356bSdh 	drv_usecwait(10);
66044c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
66054c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6606c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6607c3bc407cSdh 		    "AXIL register restore failed");
66084c06356bSdh 	}
66094c06356bSdh 	mutex_exit(&pwp->axil_lock);
66104c06356bSdh 	return (rv);
66114c06356bSdh }
66124c06356bSdh 
66134c06356bSdh void
66144c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66154c06356bSdh {
66164c06356bSdh 	uint32_t newaxil, oldaxil;
66174c06356bSdh 
66184c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
66194c06356bSdh 	off &= GSM_BASE_MASK;
66204c06356bSdh 	mutex_enter(&pwp->axil_lock);
66214c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
66224c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
66234c06356bSdh 	ddi_put32(pwp->top_acc_handle,
66244c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
66254c06356bSdh 	drv_usecwait(10);
66264c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
66274c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6628c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6629c3bc407cSdh 		    "AXIL register update failed");
66304c06356bSdh 	}
66314c06356bSdh 	ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
66324c06356bSdh 	ddi_put32(pwp->top_acc_handle,
66334c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
66344c06356bSdh 	drv_usecwait(10);
66354c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
66364c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6637c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6638c3bc407cSdh 		    "AXIL register restore failed");
66394c06356bSdh 	}
66404c06356bSdh 	mutex_exit(&pwp->axil_lock);
66414c06356bSdh }
66424c06356bSdh 
66434c06356bSdh uint32_t
66444c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
66454c06356bSdh {
66464c06356bSdh 	switch (off) {
66474c06356bSdh 	case PMCS_SPC_RESET:
66484c06356bSdh 	case PMCS_SPC_BOOT_STRAP:
66494c06356bSdh 	case PMCS_SPC_DEVICE_ID:
66504c06356bSdh 	case PMCS_DEVICE_REVISION:
66512ac4abe8SDavid Hollister 		off = pmcs_rd_gsm_reg(pwp, 0, off);
66524c06356bSdh 		break;
66534c06356bSdh 	default:
66544c06356bSdh 		off = ddi_get32(pwp->top_acc_handle,
66554c06356bSdh 		    &pwp->top_regs[off >> 2]);
66564c06356bSdh 		break;
66574c06356bSdh 	}
66584c06356bSdh 	return (off);
66594c06356bSdh }
66604c06356bSdh 
66614c06356bSdh void
66624c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
66634c06356bSdh {
66644c06356bSdh 	switch (off) {
66654c06356bSdh 	case PMCS_SPC_RESET:
66664c06356bSdh 	case PMCS_DEVICE_REVISION:
66674c06356bSdh 		pmcs_wr_gsm_reg(pwp, off, val);
66684c06356bSdh 		break;
66694c06356bSdh 	default:
66704c06356bSdh 		ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
66714c06356bSdh 		break;
66724c06356bSdh 	}
66734c06356bSdh }
66744c06356bSdh 
66754c06356bSdh uint32_t
66764c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
66774c06356bSdh {
66784c06356bSdh 	return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
66794c06356bSdh }
66804c06356bSdh 
66814c06356bSdh uint32_t
66824c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
66834c06356bSdh {
66844c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66854c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
66864c06356bSdh }
66874c06356bSdh 
66884c06356bSdh uint32_t
66894c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
66904c06356bSdh {
66914c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66924c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
66934c06356bSdh }
66944c06356bSdh 
66954c06356bSdh uint32_t
66964c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
66974c06356bSdh {
66984c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
66994c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
67004c06356bSdh }
67014c06356bSdh 
67024c06356bSdh uint32_t
67034c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
67044c06356bSdh {
67054c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
67064c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
67074c06356bSdh }
67084c06356bSdh 
67094c06356bSdh uint32_t
67104c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
67114c06356bSdh {
67124c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
67134c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
67144c06356bSdh }
67154c06356bSdh 
67164c06356bSdh uint32_t
67174c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
67184c06356bSdh {
67194c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
67204c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
67214c06356bSdh }
67224c06356bSdh 
67234c06356bSdh void
67244c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
67254c06356bSdh {
67264c06356bSdh 	ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
67274c06356bSdh }
67284c06356bSdh 
67294c06356bSdh void
67304c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
67314c06356bSdh {
67324c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67334c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
67344c06356bSdh }
67354c06356bSdh 
67364c06356bSdh void
67374c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
67384c06356bSdh {
67394c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67404c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
67414c06356bSdh }
67424c06356bSdh 
67434c06356bSdh void
67444c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
67454c06356bSdh {
67464c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67474c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
67484c06356bSdh }
67494c06356bSdh 
67504c06356bSdh void
67514c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
67524c06356bSdh {
67534c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67544c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
67554c06356bSdh }
67564c06356bSdh 
67574c06356bSdh void
67584c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
67594c06356bSdh {
67604c06356bSdh 	((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
67614c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
67624c06356bSdh 	    DDI_SUCCESS) {
6763c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6764c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
67654c06356bSdh 	}
67664c06356bSdh }
67674c06356bSdh 
67684c06356bSdh void
67694c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
67704c06356bSdh {
67714c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67724c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
67734c06356bSdh }
67744c06356bSdh 
67754c06356bSdh void
67764c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
67774c06356bSdh {
67784c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
67794c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
67804c06356bSdh }
67814c06356bSdh 
67824c06356bSdh void
67834c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
67844c06356bSdh {
67854c06356bSdh 	((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
67864c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
67874c06356bSdh 	    DDI_SUCCESS) {
6788c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6789c3bc407cSdh 		    "%s: ddi_dma_sync failed?", __func__);
67904c06356bSdh 	}
67914c06356bSdh }
67924c06356bSdh 
67934c06356bSdh /*
67944c06356bSdh  * Check the status value of an outbound IOMB and report anything bad
67954c06356bSdh  */
67964c06356bSdh 
67974c06356bSdh void
67984c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
67994c06356bSdh {
68004c06356bSdh 	uint16_t 	opcode;
68014c06356bSdh 	int		offset;
68024c06356bSdh 
68034c06356bSdh 	if (iomb == NULL) {
68044c06356bSdh 		return;
68054c06356bSdh 	}
68064c06356bSdh 
68074c06356bSdh 	opcode = LE_32(iomb[0]) & 0xfff;
68084c06356bSdh 
68094c06356bSdh 	switch (opcode) {
68104c06356bSdh 		/*
68114c06356bSdh 		 * The following have no status field, so ignore them
68124c06356bSdh 		 */
68134c06356bSdh 	case PMCOUT_ECHO:
68144c06356bSdh 	case PMCOUT_SAS_HW_EVENT:
68154c06356bSdh 	case PMCOUT_GET_DEVICE_HANDLE:
68164c06356bSdh 	case PMCOUT_SATA_EVENT:
68174c06356bSdh 	case PMCOUT_SSP_EVENT:
68184c06356bSdh 	case PMCOUT_DEVICE_HANDLE_ARRIVED:
68194c06356bSdh 	case PMCOUT_SMP_REQUEST_RECEIVED:
68204c06356bSdh 	case PMCOUT_GPIO:
68214c06356bSdh 	case PMCOUT_GPIO_EVENT:
68224c06356bSdh 	case PMCOUT_GET_TIME_STAMP:
68234c06356bSdh 	case PMCOUT_SKIP_ENTRIES:
68244c06356bSdh 	case PMCOUT_GET_NVMD_DATA:	/* Actually lower 16 bits of word 3 */
68254c06356bSdh 	case PMCOUT_SET_NVMD_DATA:	/* but ignore - we don't use these */
68264c06356bSdh 	case PMCOUT_DEVICE_HANDLE_REMOVED:
68274c06356bSdh 	case PMCOUT_SSP_REQUEST_RECEIVED:
68284c06356bSdh 		return;
68294c06356bSdh 
68304c06356bSdh 	case PMCOUT_GENERAL_EVENT:
68314c06356bSdh 		offset = 1;
68324c06356bSdh 		break;
68334c06356bSdh 
68344c06356bSdh 	case PMCOUT_SSP_COMPLETION:
68354c06356bSdh 	case PMCOUT_SMP_COMPLETION:
68364c06356bSdh 	case PMCOUT_DEVICE_REGISTRATION:
68374c06356bSdh 	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
68384c06356bSdh 	case PMCOUT_SATA_COMPLETION:
68394c06356bSdh 	case PMCOUT_DEVICE_INFO:
68404c06356bSdh 	case PMCOUT_FW_FLASH_UPDATE:
68414c06356bSdh 	case PMCOUT_SSP_ABORT:
68424c06356bSdh 	case PMCOUT_SATA_ABORT:
68434c06356bSdh 	case PMCOUT_SAS_DIAG_MODE_START_END:
68444c06356bSdh 	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
68454c06356bSdh 	case PMCOUT_SMP_ABORT:
68464c06356bSdh 	case PMCOUT_SET_DEVICE_STATE:
68474c06356bSdh 	case PMCOUT_GET_DEVICE_STATE:
68484c06356bSdh 	case PMCOUT_SET_DEVICE_INFO:
68494c06356bSdh 		offset = 2;
68504c06356bSdh 		break;
68514c06356bSdh 
68524c06356bSdh 	case PMCOUT_LOCAL_PHY_CONTROL:
68534c06356bSdh 	case PMCOUT_SAS_DIAG_EXECUTE:
68544c06356bSdh 	case PMCOUT_PORT_CONTROL:
68554c06356bSdh 		offset = 3;
68564c06356bSdh 		break;
68574c06356bSdh 
68584c06356bSdh 	case PMCOUT_GET_INFO:
68594c06356bSdh 	case PMCOUT_GET_VPD:
68604c06356bSdh 	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
68614c06356bSdh 	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
68624c06356bSdh 	case PMCOUT_SET_VPD:
68634c06356bSdh 	case PMCOUT_TWI:
68644c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
68654c06356bSdh 		    "Got response for deprecated opcode", iomb);
68664c06356bSdh 		return;
68674c06356bSdh 
68684c06356bSdh 	default:
68694c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
68704c06356bSdh 		    "Got response for unknown opcode", iomb);
68714c06356bSdh 		return;
68724c06356bSdh 	}
68734c06356bSdh 
68744c06356bSdh 	if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
68754c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
68764c06356bSdh 		    "bad status on TAG_TYPE_NONE command", iomb);
68774c06356bSdh 	}
68784c06356bSdh }
68794c06356bSdh 
68804c06356bSdh /*
68814c06356bSdh  * Called with statlock held
68824c06356bSdh  */
68834c06356bSdh void
68844c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
68854c06356bSdh {
68864c06356bSdh 	_NOTE(ARGUNUSED(pwp));
68874c06356bSdh 
68884c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
68894c06356bSdh 
6890c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.",
6891c3bc407cSdh 	    __func__, (void *)xp);
68924c06356bSdh 
68934c06356bSdh 	/*
6894b18a19c2SJesse Butler 	 * Clear the dip now.  This keeps pmcs_remove_device from attempting
68954c06356bSdh 	 * to call us on the same device while we're still flushing queues.
68964c06356bSdh 	 * The only side effect is we can no longer update SM-HBA properties,
68974c06356bSdh 	 * but this device is going away anyway, so no matter.
68984c06356bSdh 	 */
68994c06356bSdh 	xp->dip = NULL;
6900499cfd15SDavid Hollister 	xp->smpd = NULL;
69014c06356bSdh 	xp->special_running = 0;
69024c06356bSdh 	xp->recovering = 0;
69034c06356bSdh 	xp->recover_wait = 0;
69044c06356bSdh 	xp->draining = 0;
69054c06356bSdh 	xp->new = 0;
69064c06356bSdh 	xp->assigned = 0;
69074c06356bSdh 	xp->dev_state = 0;
69084c06356bSdh 	xp->tagmap = 0;
69094c06356bSdh 	xp->dev_gone = 1;
69104c06356bSdh 	xp->event_recovery = 0;
69114c06356bSdh 	xp->dtype = NOTHING;
69124c06356bSdh 	xp->wq_recovery_tail = NULL;
69134c06356bSdh 	/* Don't clear xp->phy */
69144c06356bSdh 	/* Don't clear xp->actv_cnt */
6915601c90f1SSrikanth, Ramana 	/* Don't clear xp->actv_pkts */
6916b18a19c2SJesse Butler 
6917b18a19c2SJesse Butler 	/*
6918b18a19c2SJesse Butler 	 * Flush all target queues
6919b18a19c2SJesse Butler 	 */
6920b18a19c2SJesse Butler 	pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
69214c06356bSdh }
69224c06356bSdh 
69234c06356bSdh static int
69244c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
69254c06356bSdh {
69264c06356bSdh 	int result = srf->srf_result;
69274c06356bSdh 
69284c06356bSdh 	switch (result) {
69294c06356bSdh 	case SMP_RES_UNKNOWN_FUNCTION:
6930c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6931c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69324c06356bSdh 		    "Function Result: Unknown SMP Function(0x%x)",
69334c06356bSdh 		    __func__, result);
69344c06356bSdh 		break;
69354c06356bSdh 	case SMP_RES_FUNCTION_FAILED:
6936c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6937c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69384c06356bSdh 		    "Function Result: SMP Function Failed(0x%x)",
69394c06356bSdh 		    __func__, result);
69404c06356bSdh 		break;
69414c06356bSdh 	case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
6942c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6943c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69444c06356bSdh 		    "Function Result: Invalid Request Frame Length(0x%x)",
69454c06356bSdh 		    __func__, result);
69464c06356bSdh 		break;
69474c06356bSdh 	case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
6948c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6949c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69504c06356bSdh 		    "Function Result: Incomplete Descriptor List(0x%x)",
69514c06356bSdh 		    __func__, result);
69524c06356bSdh 		break;
69534c06356bSdh 	case SMP_RES_PHY_DOES_NOT_EXIST:
6954c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6955c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69564c06356bSdh 		    "Function Result: PHY does not exist(0x%x)",
69574c06356bSdh 		    __func__, result);
69584c06356bSdh 		break;
69594c06356bSdh 	case SMP_RES_PHY_VACANT:
6960c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6961c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69624c06356bSdh 		    "Function Result: PHY Vacant(0x%x)",
69634c06356bSdh 		    __func__, result);
69644c06356bSdh 		break;
69654c06356bSdh 	default:
6966c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6967c3bc407cSdh 		    "%s: SMP DISCOVER Response "
69684c06356bSdh 		    "Function Result: (0x%x)",
69694c06356bSdh 		    __func__, result);
69704c06356bSdh 		break;
69714c06356bSdh 	}
69724c06356bSdh 
69734c06356bSdh 	return (result);
69744c06356bSdh }
69754c06356bSdh 
69764c06356bSdh /*
69774c06356bSdh  * Do all the repetitive stuff necessary to setup for DMA
69784c06356bSdh  *
69794c06356bSdh  * pwp: Used for dip
69804c06356bSdh  * dma_attr: ddi_dma_attr_t to use for the mapping
69814c06356bSdh  * acch: ddi_acc_handle_t to use for the mapping
69824c06356bSdh  * dmah: ddi_dma_handle_t to use
69834c06356bSdh  * length: Amount of memory for mapping
6984af4c679fSSean McEnroe  * kvap: Pointer filled in with kernel virtual address on successful return
69854c06356bSdh  * dma_addr: Pointer filled in with DMA address on successful return
69864c06356bSdh  */
69874c06356bSdh boolean_t
69884c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
6989af4c679fSSean McEnroe     ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr)
69904c06356bSdh {
69914c06356bSdh 	dev_info_t		*dip = pwp->dip;
69924c06356bSdh 	ddi_dma_cookie_t	cookie;
69934c06356bSdh 	size_t			real_length;
69944c06356bSdh 	uint_t			ddma_flag = DDI_DMA_CONSISTENT;
69954c06356bSdh 	uint_t			ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
69964c06356bSdh 	uint_t			cookie_cnt;
69974c06356bSdh 	ddi_device_acc_attr_t	mattr = {
69984c06356bSdh 		DDI_DEVICE_ATTR_V0,
69994c06356bSdh 		DDI_NEVERSWAP_ACC,
70004c06356bSdh 		DDI_STRICTORDER_ACC,
70014c06356bSdh 		DDI_DEFAULT_ACC
70024c06356bSdh 	};
70034c06356bSdh 
70044c06356bSdh 	*acch = NULL;
70054c06356bSdh 	*dmah = NULL;
70064c06356bSdh 
70074c06356bSdh 	if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
70084c06356bSdh 	    DDI_SUCCESS) {
7009c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7010c3bc407cSdh 		    "Failed to allocate DMA handle");
70114c06356bSdh 		return (B_FALSE);
70124c06356bSdh 	}
70134c06356bSdh 
70144c06356bSdh 	if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
7015af4c679fSSean McEnroe 	    NULL, kvap, &real_length, acch) != DDI_SUCCESS) {
7016c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7017c3bc407cSdh 		    "Failed to allocate DMA mem");
70184c06356bSdh 		ddi_dma_free_handle(dmah);
70194c06356bSdh 		*dmah = NULL;
70204c06356bSdh 		return (B_FALSE);
70214c06356bSdh 	}
70224c06356bSdh 
7023af4c679fSSean McEnroe 	if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length,
70244c06356bSdh 	    ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
70254c06356bSdh 	    != DDI_DMA_MAPPED) {
7026c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA");
70274c06356bSdh 		ddi_dma_free_handle(dmah);
70284c06356bSdh 		ddi_dma_mem_free(acch);
70294c06356bSdh 		*dmah = NULL;
70304c06356bSdh 		*acch = NULL;
70314c06356bSdh 		return (B_FALSE);
70324c06356bSdh 	}
70334c06356bSdh 
70344c06356bSdh 	if (cookie_cnt != 1) {
7035c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies");
70364c06356bSdh 		if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
7037c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition "
7038c3bc407cSdh 			    "failed at %s():%d", __func__, __LINE__);
70394c06356bSdh 		}
70404c06356bSdh 		ddi_dma_free_handle(dmah);
70414c06356bSdh 		ddi_dma_mem_free(acch);
70424c06356bSdh 		*dmah = NULL;
70434c06356bSdh 		*acch = NULL;
70444c06356bSdh 		return (B_FALSE);
70454c06356bSdh 	}
70464c06356bSdh 
70474c06356bSdh 	*dma_addr = cookie.dmac_laddress;
70484c06356bSdh 
70494c06356bSdh 	return (B_TRUE);
70504c06356bSdh }
70514c06356bSdh 
70524c06356bSdh /*
70534c06356bSdh  * Flush requested queues for a particular target.  Called with statlock held
70544c06356bSdh  */
70554c06356bSdh void
70564c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
70574c06356bSdh {
70589aed1621SDavid Hollister 	pmcs_cmd_t	*sp, *sp_next;
70594c06356bSdh 	pmcwork_t	*pwrk;
70604c06356bSdh 
70614c06356bSdh 	ASSERT(pwp != NULL);
70624c06356bSdh 	ASSERT(tgt != NULL);
70634c06356bSdh 
7064c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt,
70654c06356bSdh 	    "%s: Flushing queues (%d) for target 0x%p", __func__,
70664c06356bSdh 	    queues, (void *)tgt);
70674c06356bSdh 
70684c06356bSdh 	/*
70694c06356bSdh 	 * Commands on the wait queue (or the special queue below) don't have
70704c06356bSdh 	 * work structures associated with them.
70714c06356bSdh 	 */
70724c06356bSdh 	if (queues & PMCS_TGT_WAIT_QUEUE) {
70734c06356bSdh 		mutex_enter(&tgt->wqlock);
70744c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
70754c06356bSdh 			STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
7076c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
70774c06356bSdh 			    "%s: Removing cmd 0x%p from wq for target 0x%p",
70784c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
70794c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
70804c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
70814c06356bSdh 			mutex_exit(&tgt->wqlock);
70824c06356bSdh 			pmcs_dma_unload(pwp, sp);
70834c06356bSdh 			mutex_enter(&pwp->cq_lock);
70844c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
70854c06356bSdh 			mutex_exit(&pwp->cq_lock);
70864c06356bSdh 			mutex_enter(&tgt->wqlock);
70874c06356bSdh 		}
70884c06356bSdh 		mutex_exit(&tgt->wqlock);
70894c06356bSdh 	}
70904c06356bSdh 
70914c06356bSdh 	/*
70924c06356bSdh 	 * Commands on the active queue will have work structures associated
70934c06356bSdh 	 * with them.
70944c06356bSdh 	 */
70954c06356bSdh 	if (queues & PMCS_TGT_ACTIVE_QUEUE) {
709632b54db7SJesse Butler 		mutex_exit(&tgt->statlock);
70974c06356bSdh 		mutex_enter(&tgt->aqlock);
70989aed1621SDavid Hollister 		sp = STAILQ_FIRST(&tgt->aq);
70999aed1621SDavid Hollister 		while (sp) {
71009aed1621SDavid Hollister 			sp_next = STAILQ_NEXT(sp, cmd_next);
71014c06356bSdh 			pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
71029aed1621SDavid Hollister 
71039aed1621SDavid Hollister 			/*
71049aed1621SDavid Hollister 			 * If we don't find a work structure, it's because
71059aed1621SDavid Hollister 			 * the command is already complete.  If so, move on
71069aed1621SDavid Hollister 			 * to the next one.
71079aed1621SDavid Hollister 			 */
71089aed1621SDavid Hollister 			if (pwrk == NULL) {
71099aed1621SDavid Hollister 				pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
71109aed1621SDavid Hollister 				    "%s: Not removing cmd 0x%p (htag 0x%x) "
71119aed1621SDavid Hollister 				    "from aq", __func__, (void *)sp,
71129aed1621SDavid Hollister 				    sp->cmd_tag);
71139aed1621SDavid Hollister 				sp = sp_next;
71149aed1621SDavid Hollister 				continue;
71159aed1621SDavid Hollister 			}
71169aed1621SDavid Hollister 
71179aed1621SDavid Hollister 			STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
71189aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
71199aed1621SDavid Hollister 			    "%s: Removing cmd 0x%p (htag 0x%x) from aq for "
71209aed1621SDavid Hollister 			    "target 0x%p", __func__, (void *)sp, sp->cmd_tag,
71219aed1621SDavid Hollister 			    (void *)tgt);
71224c06356bSdh 			mutex_exit(&tgt->aqlock);
71234c06356bSdh 			/*
71249aed1621SDavid Hollister 			 * Mark the work structure as dead and complete it
71254c06356bSdh 			 */
71269aed1621SDavid Hollister 			pwrk->dead = 1;
71279aed1621SDavid Hollister 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
71289aed1621SDavid Hollister 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
71299aed1621SDavid Hollister 			pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
71304c06356bSdh 			pmcs_dma_unload(pwp, sp);
71314c06356bSdh 			mutex_enter(&pwp->cq_lock);
71324c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
71334c06356bSdh 			mutex_exit(&pwp->cq_lock);
71344c06356bSdh 			mutex_enter(&tgt->aqlock);
71359aed1621SDavid Hollister 			sp = sp_next;
71364c06356bSdh 		}
71374c06356bSdh 		mutex_exit(&tgt->aqlock);
713832b54db7SJesse Butler 		mutex_enter(&tgt->statlock);
71394c06356bSdh 	}
71404c06356bSdh 
71414c06356bSdh 	if (queues & PMCS_TGT_SPECIAL_QUEUE) {
71424c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
71434c06356bSdh 			STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
71449aed1621SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
71454c06356bSdh 			    "%s: Removing cmd 0x%p from sq for target 0x%p",
71464c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
71474c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
71484c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
71494c06356bSdh 			pmcs_dma_unload(pwp, sp);
71504c06356bSdh 			mutex_enter(&pwp->cq_lock);
71514c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
71524c06356bSdh 			mutex_exit(&pwp->cq_lock);
71534c06356bSdh 		}
71544c06356bSdh 	}
71554c06356bSdh }
71564c06356bSdh 
71574c06356bSdh void
71584c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
71594c06356bSdh     size_t amt)
71604c06356bSdh {
71614c06356bSdh 	switch (PMCS_TAG_TYPE(pwrk->htag)) {
71624c06356bSdh 	case PMCS_TAG_TYPE_CBACK:
71634c06356bSdh 	{
71644c06356bSdh 		pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
71654c06356bSdh 		(*callback)(pwp, pwrk, iomb);
71664c06356bSdh 		break;
71674c06356bSdh 	}
71684c06356bSdh 	case PMCS_TAG_TYPE_WAIT:
71694c06356bSdh 		if (pwrk->arg && iomb && amt) {
71704c06356bSdh 			(void) memcpy(pwrk->arg, iomb, amt);
71714c06356bSdh 		}
71724c06356bSdh 		cv_signal(&pwrk->sleep_cv);
71734c06356bSdh 		mutex_exit(&pwrk->lock);
71744c06356bSdh 		break;
71754c06356bSdh 	case PMCS_TAG_TYPE_NONE:
71764c06356bSdh #ifdef DEBUG
71774c06356bSdh 		pmcs_check_iomb_status(pwp, iomb);
71784c06356bSdh #endif
71794c06356bSdh 		pmcs_pwork(pwp, pwrk);
71804c06356bSdh 		break;
71814c06356bSdh 	default:
71824c06356bSdh 		/*
71834c06356bSdh 		 * We will leak a structure here if we don't know
71844c06356bSdh 		 * what happened
71854c06356bSdh 		 */
7186c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7187c3bc407cSdh 		    "%s: Unknown PMCS_TAG_TYPE (%x)",
71884c06356bSdh 		    __func__, PMCS_TAG_TYPE(pwrk->htag));
71894c06356bSdh 		break;
71904c06356bSdh 	}
71914c06356bSdh }
71924c06356bSdh 
71934c06356bSdh /*
71944c06356bSdh  * Determine if iport still has targets. During detach(9E), if SCSA is
71954c06356bSdh  * successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
71964c06356bSdh  * this should always return B_FALSE.
71974c06356bSdh  */
71984c06356bSdh boolean_t
71994c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
72004c06356bSdh {
72014c06356bSdh 	pmcs_xscsi_t *xp;
72024c06356bSdh 	int i;
72034c06356bSdh 
72044c06356bSdh 	mutex_enter(&pwp->lock);
72054c06356bSdh 
72064c06356bSdh 	if (!pwp->targets || !pwp->max_dev) {
72074c06356bSdh 		mutex_exit(&pwp->lock);
72084c06356bSdh 		return (B_FALSE);
72094c06356bSdh 	}
72104c06356bSdh 
72114c06356bSdh 	for (i = 0; i < pwp->max_dev; i++) {
72124c06356bSdh 		xp = pwp->targets[i];
72134c06356bSdh 		if ((xp == NULL) || (xp->phy == NULL) ||
72144c06356bSdh 		    (xp->phy->iport != iport)) {
72154c06356bSdh 			continue;
72164c06356bSdh 		}
72174c06356bSdh 
72184c06356bSdh 		mutex_exit(&pwp->lock);
72194c06356bSdh 		return (B_TRUE);
72204c06356bSdh 	}
72214c06356bSdh 
72224c06356bSdh 	mutex_exit(&pwp->lock);
72234c06356bSdh 	return (B_FALSE);
72244c06356bSdh }
72254c06356bSdh 
72264c06356bSdh /*
72274c06356bSdh  * Called with softstate lock held
72284c06356bSdh  */
72294c06356bSdh void
72304c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target)
72314c06356bSdh {
72324c06356bSdh 	pmcs_hw_t *pwp = target->pwp;
72334c06356bSdh 	pmcs_iport_t *iport;
72344c06356bSdh 
72354c06356bSdh 	ASSERT(pwp);
72364c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
72374c06356bSdh 
72384c06356bSdh 	if (!target->ua) {
7239c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
7240c3bc407cSdh 		    "%s: target %p iport address is null",
72414c06356bSdh 		    __func__, (void *)target);
72424c06356bSdh 	}
72434c06356bSdh 
72444c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, target->ua);
72454c06356bSdh 	if (iport == NULL) {
7246c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
72474c06356bSdh 		    "%s: no iport associated with tgt(0x%p)",
72484c06356bSdh 		    __func__, (void *)target);
72494c06356bSdh 		return;
72504c06356bSdh 	}
72514c06356bSdh 
7252c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target,
72534c06356bSdh 	    "%s: free target %p", __func__, (void *)target);
72544c06356bSdh 	if (target->ua) {
72554c06356bSdh 		strfree(target->ua);
72564c06356bSdh 	}
72574c06356bSdh 
72584c06356bSdh 	mutex_destroy(&target->wqlock);
72594c06356bSdh 	mutex_destroy(&target->aqlock);
72604c06356bSdh 	mutex_destroy(&target->statlock);
72614c06356bSdh 	cv_destroy(&target->reset_cv);
72624c06356bSdh 	cv_destroy(&target->abort_cv);
72634c06356bSdh 	ddi_soft_state_bystr_fini(&target->lun_sstate);
72644c06356bSdh 	ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
72654c06356bSdh 	pmcs_rele_iport(iport);
72664c06356bSdh }
72674c06356bSdh 
72684c06356bSdh /*
72694c06356bSdh  * pmcs_lock_phy_impl
72704c06356bSdh  *
72714c06356bSdh  * This function is what does the actual work for pmcs_lock_phy.  It will
72724c06356bSdh  * lock all PHYs from phyp down in a top-down fashion.
72734c06356bSdh  *
72744c06356bSdh  * Locking notes:
72754c06356bSdh  * 1. level starts from 0 for the PHY ("parent") that's passed in.  It is
72764c06356bSdh  * not a reflection of the actual level of the PHY in the SAS topology.
72774c06356bSdh  * 2. If parent is an expander, then parent is locked along with all its
72784c06356bSdh  * descendents.
72794c06356bSdh  * 3. Expander subsidiary PHYs at level 0 are not locked.  It is the
72804c06356bSdh  * responsibility of the caller to individually lock expander subsidiary PHYs
72814c06356bSdh  * at level 0 if necessary.
72824c06356bSdh  * 4. Siblings at level 0 are not traversed due to the possibility that we're
72834c06356bSdh  * locking a PHY on the dead list.  The siblings could be pointing to invalid
72844c06356bSdh  * PHYs.  We don't lock siblings at level 0 anyway.
72854c06356bSdh  */
72864c06356bSdh static void
72874c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
72884c06356bSdh {
72894c06356bSdh 	pmcs_phy_t *tphyp;
72904c06356bSdh 
72914c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
72924c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
72934c06356bSdh 
72944c06356bSdh 	/*
72954c06356bSdh 	 * Start walking the PHYs.
72964c06356bSdh 	 */
72974c06356bSdh 	tphyp = phyp;
72984c06356bSdh 	while (tphyp) {
72994c06356bSdh 		/*
73004c06356bSdh 		 * If we're at the top level, only lock ourselves.  For anything
73014c06356bSdh 		 * at level > 0, traverse children while locking everything.
73024c06356bSdh 		 */
73034c06356bSdh 		if ((level > 0) || (tphyp == phyp)) {
7304c3bc407cSdh 			pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp,
7305c3bc407cSdh 			    NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d",
73064c06356bSdh 			    __func__, (void *)tphyp, (void *)tphyp->parent,
73074c06356bSdh 			    tphyp->path, level);
73084c06356bSdh 			mutex_enter(&tphyp->phy_lock);
73094c06356bSdh 
73104c06356bSdh 			if (tphyp->children) {
73114c06356bSdh 				pmcs_lock_phy_impl(tphyp->children, level + 1);
73124c06356bSdh 			}
73134c06356bSdh 		}
73144c06356bSdh 
73154c06356bSdh 		if (level == 0) {
73164c06356bSdh 			return;
73174c06356bSdh 		}
73184c06356bSdh 
73194c06356bSdh 		tphyp = tphyp->sibling;
73204c06356bSdh 	}
73214c06356bSdh }
73224c06356bSdh 
73234c06356bSdh /*
73244c06356bSdh  * pmcs_lock_phy
73254c06356bSdh  *
73264c06356bSdh  * This function is responsible for locking a PHY and all its descendents
73274c06356bSdh  */
73284c06356bSdh void
73294c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp)
73304c06356bSdh {
73314c06356bSdh #ifdef DEBUG
73324c06356bSdh 	char *callername = NULL;
73334c06356bSdh 	ulong_t off;
73344c06356bSdh 
73354c06356bSdh 	ASSERT(phyp != NULL);
73364c06356bSdh 
73374c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
73384c06356bSdh 
73394c06356bSdh 	if (callername == NULL) {
7340c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73414c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
73424c06356bSdh 		    (void *)phyp, phyp->path);
73434c06356bSdh 	} else {
7344c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73454c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
73464c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
73474c06356bSdh 	}
73484c06356bSdh #else
7349c3bc407cSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
73504c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
73514c06356bSdh #endif
73524c06356bSdh 	pmcs_lock_phy_impl(phyp, 0);
73534c06356bSdh }
73544c06356bSdh 
73554c06356bSdh /*
73564c06356bSdh  * pmcs_unlock_phy_impl
73574c06356bSdh  *
73584c06356bSdh  * Unlock all PHYs from phyp down in a bottom-up fashion.
73594c06356bSdh  */
73604c06356bSdh static void
73614c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
73624c06356bSdh {
73634c06356bSdh 	pmcs_phy_t *phy_next;
73644c06356bSdh 
73654c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
73664c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
73674c06356bSdh 
73684c06356bSdh 	/*
73694c06356bSdh 	 * Recurse down to the bottom PHYs
73704c06356bSdh 	 */
73714c06356bSdh 	if (level == 0) {
73724c06356bSdh 		if (phyp->children) {
73734c06356bSdh 			pmcs_unlock_phy_impl(phyp->children, level + 1);
73744c06356bSdh 		}
73754c06356bSdh 	} else {
73764c06356bSdh 		phy_next = phyp;
73774c06356bSdh 		while (phy_next) {
73784c06356bSdh 			if (phy_next->children) {
73794c06356bSdh 				pmcs_unlock_phy_impl(phy_next->children,
73804c06356bSdh 				    level + 1);
73814c06356bSdh 			}
73824c06356bSdh 			phy_next = phy_next->sibling;
73834c06356bSdh 		}
73844c06356bSdh 	}
73854c06356bSdh 
73864c06356bSdh 	/*
73874c06356bSdh 	 * Iterate through PHYs unlocking all at level > 0 as well the top PHY
73884c06356bSdh 	 */
73894c06356bSdh 	phy_next = phyp;
73904c06356bSdh 	while (phy_next) {
73914c06356bSdh 		if ((level > 0) || (phy_next == phyp)) {
73924c06356bSdh 			pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7393c3bc407cSdh 			    phy_next, NULL,
73944c06356bSdh 			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
73954c06356bSdh 			    __func__, (void *)phy_next,
73964c06356bSdh 			    (void *)phy_next->parent, phy_next->path, level);
73974c06356bSdh 			mutex_exit(&phy_next->phy_lock);
73984c06356bSdh 		}
73994c06356bSdh 
74004c06356bSdh 		if (level == 0) {
74014c06356bSdh 			return;
74024c06356bSdh 		}
74034c06356bSdh 
74044c06356bSdh 		phy_next = phy_next->sibling;
74054c06356bSdh 	}
74064c06356bSdh }
74074c06356bSdh 
74084c06356bSdh /*
74094c06356bSdh  * pmcs_unlock_phy
74104c06356bSdh  *
74114c06356bSdh  * Unlock a PHY and all its descendents
74124c06356bSdh  */
74134c06356bSdh void
74144c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp)
74154c06356bSdh {
74164c06356bSdh #ifdef DEBUG
74174c06356bSdh 	char *callername = NULL;
74184c06356bSdh 	ulong_t off;
74194c06356bSdh 
74204c06356bSdh 	ASSERT(phyp != NULL);
74214c06356bSdh 
74224c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
74234c06356bSdh 
74244c06356bSdh 	if (callername == NULL) {
7425c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
74264c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
74274c06356bSdh 		    (void *)phyp, phyp->path);
74284c06356bSdh 	} else {
7429c3bc407cSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
74304c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
74314c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
74324c06356bSdh 	}
74334c06356bSdh #else
7434c3bc407cSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
74354c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
74364c06356bSdh #endif
74374c06356bSdh 	pmcs_unlock_phy_impl(phyp, 0);
74384c06356bSdh }
74394c06356bSdh 
74404c06356bSdh /*
74414c06356bSdh  * pmcs_get_root_phy
74424c06356bSdh  *
74434c06356bSdh  * For a given phy pointer return its root phy.
74449aed1621SDavid Hollister  * This function must only be called during discovery in order to ensure that
74459aed1621SDavid Hollister  * the chain of PHYs from phyp up to the root PHY doesn't change.
74464c06356bSdh  */
74474c06356bSdh pmcs_phy_t *
74484c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp)
74494c06356bSdh {
74504c06356bSdh 	ASSERT(phyp);
74514c06356bSdh 
74524c06356bSdh 	while (phyp) {
74534c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
74544c06356bSdh 			break;
74554c06356bSdh 		}
74564c06356bSdh 		phyp = phyp->parent;
74574c06356bSdh 	}
74584c06356bSdh 
74594c06356bSdh 	return (phyp);
74604c06356bSdh }
74614c06356bSdh 
74624c06356bSdh /*
74634c06356bSdh  * pmcs_free_dma_chunklist
74644c06356bSdh  *
74654c06356bSdh  * Free DMA S/G chunk list
74664c06356bSdh  */
74674c06356bSdh void
74684c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
74694c06356bSdh {
74704c06356bSdh 	pmcs_chunk_t	*pchunk;
74714c06356bSdh 
74724c06356bSdh 	while (pwp->dma_chunklist) {
74734c06356bSdh 		pchunk = pwp->dma_chunklist;
74744c06356bSdh 		pwp->dma_chunklist = pwp->dma_chunklist->next;
74754c06356bSdh 		if (pchunk->dma_handle) {
74764c06356bSdh 			if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
74774c06356bSdh 			    DDI_SUCCESS) {
7478c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7479c3bc407cSdh 				    "Condition failed at %s():%d",
7480c3bc407cSdh 				    __func__, __LINE__);
74814c06356bSdh 			}
74824c06356bSdh 			ddi_dma_free_handle(&pchunk->dma_handle);
74834c06356bSdh 			ddi_dma_mem_free(&pchunk->acc_handle);
74844c06356bSdh 		}
74854c06356bSdh 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
74864c06356bSdh 	}
74874c06356bSdh }
74884c06356bSdh 
74894c06356bSdh /*ARGSUSED2*/
74904c06356bSdh int
74914c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags)
74924c06356bSdh {
74934c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
74944c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
74954c06356bSdh 
74964c06356bSdh 	mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
74974c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
74984c06356bSdh 	cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
74994c06356bSdh 	return (0);
75004c06356bSdh }
75014c06356bSdh 
75024c06356bSdh /*ARGSUSED1*/
75034c06356bSdh void
75044c06356bSdh pmcs_phy_destructor(void *buf, void *arg)
75054c06356bSdh {
75064c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
75074c06356bSdh 
75084c06356bSdh 	cv_destroy(&phyp->abort_all_cv);
75094c06356bSdh 	mutex_destroy(&phyp->phy_lock);
75104c06356bSdh }
75114c06356bSdh 
75124c06356bSdh /*
75134c06356bSdh  * Free all PHYs from the kmem_cache starting at phyp as well as everything
75144c06356bSdh  * on the dead_phys list.
75154c06356bSdh  *
75164c06356bSdh  * NOTE: This function does not free root PHYs as they are not allocated
75174c06356bSdh  * from the kmem_cache.
75184c06356bSdh  *
75194c06356bSdh  * No PHY locks are acquired as this should only be called during DDI_DETACH
75204c06356bSdh  * or soft reset (while pmcs interrupts are disabled).
75214c06356bSdh  */
75224c06356bSdh void
75234c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
75244c06356bSdh {
75254c06356bSdh 	pmcs_phy_t *tphyp, *nphyp;
75264c06356bSdh 
75274c06356bSdh 	if (phyp == NULL) {
75284c06356bSdh 		return;
75294c06356bSdh 	}
75304c06356bSdh 
75314c06356bSdh 	tphyp = phyp;
75324c06356bSdh 	while (tphyp) {
75334c06356bSdh 		nphyp = tphyp->sibling;
75344c06356bSdh 
75354c06356bSdh 		if (tphyp->children) {
75364c06356bSdh 			pmcs_free_all_phys(pwp, tphyp->children);
75374c06356bSdh 			tphyp->children = NULL;
75384c06356bSdh 		}
75394c06356bSdh 		if (!IS_ROOT_PHY(tphyp)) {
75404c06356bSdh 			kmem_cache_free(pwp->phy_cache, tphyp);
75414c06356bSdh 		}
75424c06356bSdh 
75434c06356bSdh 		tphyp = nphyp;
75444c06356bSdh 	}
75454c06356bSdh 
75464c06356bSdh 	tphyp = pwp->dead_phys;
75474c06356bSdh 	while (tphyp) {
75484c06356bSdh 		nphyp = tphyp->sibling;
75494c06356bSdh 		kmem_cache_free(pwp->phy_cache, tphyp);
75504c06356bSdh 		tphyp = nphyp;
75514c06356bSdh 	}
75524c06356bSdh 	pwp->dead_phys = NULL;
75534c06356bSdh }
75544c06356bSdh 
75554c06356bSdh /*
75564c06356bSdh  * Free a list of PHYs linked together by the sibling pointer back to the
75574c06356bSdh  * kmem cache from whence they came.  This function does not recurse, so the
75584c06356bSdh  * caller must ensure there are no children.
75594c06356bSdh  */
75604c06356bSdh void
75614c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
75624c06356bSdh {
75634c06356bSdh 	pmcs_phy_t *next_phy;
75644c06356bSdh 
75654c06356bSdh 	while (phyp) {
75664c06356bSdh 		next_phy = phyp->sibling;
75674c06356bSdh 		ASSERT(!mutex_owned(&phyp->phy_lock));
75684c06356bSdh 		kmem_cache_free(pwp->phy_cache, phyp);
75694c06356bSdh 		phyp = next_phy;
75704c06356bSdh 	}
75714c06356bSdh }
75724c06356bSdh 
75734c06356bSdh /*
75744c06356bSdh  * Make a copy of an existing PHY structure.  This is used primarily in
75754c06356bSdh  * discovery to compare the contents of an existing PHY with what gets
75764c06356bSdh  * reported back by an expander.
75774c06356bSdh  *
75784c06356bSdh  * This function must not be called from any context where sleeping is
75794c06356bSdh  * not possible.
75804c06356bSdh  *
75814c06356bSdh  * The new PHY is returned unlocked.
75824c06356bSdh  */
75834c06356bSdh static pmcs_phy_t *
75844c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy)
75854c06356bSdh {
75864c06356bSdh 	pmcs_phy_t *local;
75874c06356bSdh 
75884c06356bSdh 	local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
75894c06356bSdh 
75904c06356bSdh 	/*
75914c06356bSdh 	 * Go ahead and just copy everything...
75924c06356bSdh 	 */
75934c06356bSdh 	*local = *orig_phy;
75944c06356bSdh 
75954c06356bSdh 	/*
75964c06356bSdh 	 * But the following must be set appropriately for this copy
75974c06356bSdh 	 */
75984c06356bSdh 	local->sibling = NULL;
75994c06356bSdh 	local->children = NULL;
76004c06356bSdh 	mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
76014c06356bSdh 	    DDI_INTR_PRI(orig_phy->pwp->intr_pri));
76024c06356bSdh 
76034c06356bSdh 	return (local);
76044c06356bSdh }
76054c06356bSdh 
76064c06356bSdh int
76074c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle)
76084c06356bSdh {
76094c06356bSdh 	ddi_fm_error_t de;
76104c06356bSdh 
76114c06356bSdh 	if (handle == NULL) {
76124c06356bSdh 		return (DDI_FAILURE);
76134c06356bSdh 	}
76144c06356bSdh 	ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
76154c06356bSdh 	return (de.fme_status);
76164c06356bSdh }
76174c06356bSdh 
76184c06356bSdh int
76194c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle)
76204c06356bSdh {
76214c06356bSdh 	ddi_fm_error_t de;
76224c06356bSdh 
76234c06356bSdh 	if (handle == NULL) {
76244c06356bSdh 		return (DDI_FAILURE);
76254c06356bSdh 	}
76264c06356bSdh 	ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
76274c06356bSdh 	return (de.fme_status);
76284c06356bSdh }
76294c06356bSdh 
76304c06356bSdh 
76314c06356bSdh void
76324c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
76334c06356bSdh {
76344c06356bSdh 	uint64_t ena;
76354c06356bSdh 	char buf[FM_MAX_CLASS];
76364c06356bSdh 
76374c06356bSdh 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
76384c06356bSdh 	ena = fm_ena_generate(0, FM_ENA_FMT1);
76394c06356bSdh 	if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
76404c06356bSdh 		ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
76414c06356bSdh 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
76424c06356bSdh 	}
76434c06356bSdh }
76444c06356bSdh 
76454c06356bSdh int
76464c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
76474c06356bSdh {
76484c06356bSdh 	pmcs_chunk_t *pchunk;
76494c06356bSdh 	int i;
76504c06356bSdh 
76514c06356bSdh 	/* check all acc & dma handles allocated in attach */
76524c06356bSdh 	if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
76534c06356bSdh 	    (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
76544c06356bSdh 	    (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
76554c06356bSdh 	    (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
76564c06356bSdh 	    (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
76574c06356bSdh 		goto check_failed;
76584c06356bSdh 	}
76594c06356bSdh 
76604c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
76614c06356bSdh 		if ((pmcs_check_dma_handle(
76624c06356bSdh 		    pwp->iqp_handles[i]) != DDI_SUCCESS) ||
76634c06356bSdh 		    (pmcs_check_acc_handle(
76644c06356bSdh 		    pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
76654c06356bSdh 			goto check_failed;
76664c06356bSdh 		}
76674c06356bSdh 	}
76684c06356bSdh 
76694c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
76704c06356bSdh 		if ((pmcs_check_dma_handle(
76714c06356bSdh 		    pwp->oqp_handles[i]) != DDI_SUCCESS) ||
76724c06356bSdh 		    (pmcs_check_acc_handle(
76734c06356bSdh 		    pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
76744c06356bSdh 			goto check_failed;
76754c06356bSdh 		}
76764c06356bSdh 	}
76774c06356bSdh 
76784c06356bSdh 	if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
76794c06356bSdh 	    (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
76804c06356bSdh 		goto check_failed;
76814c06356bSdh 	}
76824c06356bSdh 
76834c06356bSdh 	if (pwp->fwlog &&
76844c06356bSdh 	    ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
76854c06356bSdh 	    (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
76864c06356bSdh 		goto check_failed;
76874c06356bSdh 	}
76884c06356bSdh 
76894c06356bSdh 	if (pwp->regdump_hndl && pwp->regdump_acchdl &&
76904c06356bSdh 	    ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
76914c06356bSdh 	    (pmcs_check_acc_handle(pwp->regdump_acchdl)
76924c06356bSdh 	    != DDI_SUCCESS))) {
76934c06356bSdh 		goto check_failed;
76944c06356bSdh 	}
76954c06356bSdh 
76964c06356bSdh 
76974c06356bSdh 	pchunk = pwp->dma_chunklist;
76984c06356bSdh 	while (pchunk) {
76994c06356bSdh 		if ((pmcs_check_acc_handle(pchunk->acc_handle)
77004c06356bSdh 		    != DDI_SUCCESS) ||
77014c06356bSdh 		    (pmcs_check_dma_handle(pchunk->dma_handle)
77024c06356bSdh 		    != DDI_SUCCESS)) {
77034c06356bSdh 			goto check_failed;
77044c06356bSdh 		}
77054c06356bSdh 		pchunk = pchunk->next;
77064c06356bSdh 	}
77074c06356bSdh 
77084c06356bSdh 	return (0);
77094c06356bSdh 
77104c06356bSdh check_failed:
77114c06356bSdh 
77124c06356bSdh 	return (1);
77134c06356bSdh }
77144c06356bSdh 
77154c06356bSdh /*
77164c06356bSdh  * pmcs_handle_dead_phys
77174c06356bSdh  *
77184c06356bSdh  * If the PHY has no outstanding work associated with it, remove it from
77194c06356bSdh  * the dead PHY list and free it.
77204c06356bSdh  *
77214c06356bSdh  * If pwp->ds_err_recovering or pwp->configuring is set, don't run.
77224c06356bSdh  * This keeps routines that need to submit work to the chip from having to
77234c06356bSdh  * hold PHY locks to ensure that PHYs don't disappear while they do their work.
77244c06356bSdh  */
77254c06356bSdh void
77264c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp)
77274c06356bSdh {
77284c06356bSdh 	pmcs_phy_t *phyp, *nphyp, *pphyp;
77294c06356bSdh 
77304c06356bSdh 	mutex_enter(&pwp->lock);
77314c06356bSdh 	mutex_enter(&pwp->config_lock);
77324c06356bSdh 
77334c06356bSdh 	if (pwp->configuring | pwp->ds_err_recovering) {
77344c06356bSdh 		mutex_exit(&pwp->config_lock);
77354c06356bSdh 		mutex_exit(&pwp->lock);
77364c06356bSdh 		return;
77374c06356bSdh 	}
77384c06356bSdh 
77394c06356bSdh 	/*
77404c06356bSdh 	 * Check every PHY in the dead PHY list
77414c06356bSdh 	 */
77424c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
77434c06356bSdh 	phyp = pwp->dead_phys;
77444c06356bSdh 	pphyp = NULL;	/* Set previous PHY to NULL */
77454c06356bSdh 
77464c06356bSdh 	while (phyp != NULL) {
77474c06356bSdh 		pmcs_lock_phy(phyp);
77484c06356bSdh 		ASSERT(phyp->dead);
77494c06356bSdh 
77504c06356bSdh 		nphyp = phyp->dead_next;
77514c06356bSdh 
77524c06356bSdh 		/*
77534c06356bSdh 		 * Check for outstanding work
77544c06356bSdh 		 */
77554c06356bSdh 		if (phyp->ref_count > 0) {
77564c06356bSdh 			pmcs_unlock_phy(phyp);
77574c06356bSdh 			pphyp = phyp;	/* This PHY becomes "previous" */
77584c06356bSdh 		} else if (phyp->target) {
77594c06356bSdh 			pmcs_unlock_phy(phyp);
7760c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target,
77614c06356bSdh 			    "%s: Not freeing PHY 0x%p: target 0x%p is not free",
77624c06356bSdh 			    __func__, (void *)phyp, (void *)phyp->target);
77634c06356bSdh 			pphyp = phyp;
77644c06356bSdh 		} else {
77654c06356bSdh 			/*
77664c06356bSdh 			 * No outstanding work or target references. Remove it
77674c06356bSdh 			 * from the list and free it
77684c06356bSdh 			 */
7769c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
77704c06356bSdh 			    "%s: Freeing inactive dead PHY 0x%p @ %s "
77714c06356bSdh 			    "target = 0x%p", __func__, (void *)phyp,
77724c06356bSdh 			    phyp->path, (void *)phyp->target);
77734c06356bSdh 			/*
77744c06356bSdh 			 * If pphyp is NULL, then phyp was the head of the list,
77754c06356bSdh 			 * so just reset the head to nphyp. Otherwise, the
77764c06356bSdh 			 * previous PHY will now point to nphyp (the next PHY)
77774c06356bSdh 			 */
77784c06356bSdh 			if (pphyp == NULL) {
77794c06356bSdh 				pwp->dead_phys = nphyp;
77804c06356bSdh 			} else {
77814c06356bSdh 				pphyp->dead_next = nphyp;
77824c06356bSdh 			}
77834c06356bSdh 			/*
77844c06356bSdh 			 * If the target still points to this PHY, remove
77854c06356bSdh 			 * that linkage now.
77864c06356bSdh 			 */
77874c06356bSdh 			if (phyp->target) {
77884c06356bSdh 				mutex_enter(&phyp->target->statlock);
77894c06356bSdh 				if (phyp->target->phy == phyp) {
77904c06356bSdh 					phyp->target->phy = NULL;
77914c06356bSdh 				}
77924c06356bSdh 				mutex_exit(&phyp->target->statlock);
77934c06356bSdh 			}
7794b18a19c2SJesse Butler 			pmcs_unlock_phy(phyp);
77954c06356bSdh 			kmem_cache_free(pwp->phy_cache, phyp);
77964c06356bSdh 		}
77974c06356bSdh 
77984c06356bSdh 		phyp = nphyp;
77994c06356bSdh 	}
78004c06356bSdh 
78014c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
78024c06356bSdh 	mutex_exit(&pwp->config_lock);
78034c06356bSdh 	mutex_exit(&pwp->lock);
78044c06356bSdh }
78054c06356bSdh 
78064c06356bSdh void
78074c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
78084c06356bSdh {
78094c06356bSdh 	atomic_inc_32(&phyp->ref_count);
78104c06356bSdh }
78114c06356bSdh 
78124c06356bSdh void
78134c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
78144c06356bSdh {
78154c06356bSdh 	ASSERT(phyp->ref_count != 0);
78164c06356bSdh 	atomic_dec_32(&phyp->ref_count);
78174c06356bSdh }
78184c06356bSdh 
78194c06356bSdh /*
78204c06356bSdh  * pmcs_reap_dead_phy
78214c06356bSdh  *
78224c06356bSdh  * This function is called from pmcs_new_tport when we have a PHY
78234c06356bSdh  * without a target pointer.  It's possible in that case that this PHY
78244c06356bSdh  * may have a "brother" on the dead_phys list.  That is, it may be the same as
78254c06356bSdh  * this one but with a different root PHY number (e.g. pp05 vs. pp04).  If
78264c06356bSdh  * that's the case, update the dead PHY and this new PHY.  If that's not the
78274c06356bSdh  * case, we should get a tran_tgt_init on this after it's reported to SCSA.
78284c06356bSdh  *
78294c06356bSdh  * Called with PHY locked.
78304c06356bSdh  */
78314c06356bSdh static void
78324c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp)
78334c06356bSdh {
78344c06356bSdh 	pmcs_hw_t *pwp = phyp->pwp;
78354c06356bSdh 	pmcs_phy_t *ctmp;
783673a3eccdSDavid Hollister 	pmcs_iport_t *iport_cmp;
78374c06356bSdh 
78384c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
78394c06356bSdh 
78404c06356bSdh 	/*
78414c06356bSdh 	 * Check the dead PHYs list
78424c06356bSdh 	 */
78434c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
78444c06356bSdh 	ctmp = pwp->dead_phys;
78454c06356bSdh 	while (ctmp) {
784673a3eccdSDavid Hollister 		/*
784773a3eccdSDavid Hollister 		 * If the iport is NULL, compare against last_iport.
784873a3eccdSDavid Hollister 		 */
784973a3eccdSDavid Hollister 		if (ctmp->iport) {
785073a3eccdSDavid Hollister 			iport_cmp = ctmp->iport;
785173a3eccdSDavid Hollister 		} else {
785273a3eccdSDavid Hollister 			iport_cmp = ctmp->last_iport;
785373a3eccdSDavid Hollister 		}
785473a3eccdSDavid Hollister 
785573a3eccdSDavid Hollister 		if ((iport_cmp != phyp->iport) ||
78564c06356bSdh 		    (memcmp((void *)&ctmp->sas_address[0],
78574c06356bSdh 		    (void *)&phyp->sas_address[0], 8))) {
78584c06356bSdh 			ctmp = ctmp->dead_next;
78594c06356bSdh 			continue;
78604c06356bSdh 		}
78614c06356bSdh 
78624c06356bSdh 		/*
78634c06356bSdh 		 * Same SAS address on same iport.  Now check to see if
78644c06356bSdh 		 * the PHY path is the same with the possible exception
78654c06356bSdh 		 * of the root PHY number.
78664c06356bSdh 		 * The "5" is the string length of "pp00."
78674c06356bSdh 		 */
78684c06356bSdh 		if ((strnlen(phyp->path, 5) >= 5) &&
78694c06356bSdh 		    (strnlen(ctmp->path, 5) >= 5)) {
78704c06356bSdh 			if (memcmp((void *)&phyp->path[5],
78714c06356bSdh 			    (void *)&ctmp->path[5],
78724c06356bSdh 			    strnlen(phyp->path, 32) - 5) == 0) {
78734c06356bSdh 				break;
78744c06356bSdh 			}
78754c06356bSdh 		}
78764c06356bSdh 
78774c06356bSdh 		ctmp = ctmp->dead_next;
78784c06356bSdh 	}
78794c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
78804c06356bSdh 
78814c06356bSdh 	/*
78824c06356bSdh 	 * Found a match.  Remove the target linkage and drop the
78834c06356bSdh 	 * ref count on the old PHY.  Then, increment the ref count
78844c06356bSdh 	 * on the new PHY to compensate.
78854c06356bSdh 	 */
78864c06356bSdh 	if (ctmp) {
7887c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
788873a3eccdSDavid Hollister 		    "%s: Found match in dead PHY list (0x%p) for new PHY %s",
788973a3eccdSDavid Hollister 		    __func__, (void *)ctmp, phyp->path);
789073a3eccdSDavid Hollister 		/*
789173a3eccdSDavid Hollister 		 * If there is a pointer to the target in the dead PHY, move
789273a3eccdSDavid Hollister 		 * all reference counts to the new PHY.
789373a3eccdSDavid Hollister 		 */
78944c06356bSdh 		if (ctmp->target) {
789573a3eccdSDavid Hollister 			mutex_enter(&ctmp->target->statlock);
78964c06356bSdh 			phyp->target = ctmp->target;
789773a3eccdSDavid Hollister 
789873a3eccdSDavid Hollister 			while (ctmp->ref_count != 0) {
789973a3eccdSDavid Hollister 				pmcs_inc_phy_ref_count(phyp);
790073a3eccdSDavid Hollister 				pmcs_dec_phy_ref_count(ctmp);
790173a3eccdSDavid Hollister 			}
79024c06356bSdh 			/*
79034c06356bSdh 			 * Update the target's linkage as well
79044c06356bSdh 			 */
79054c06356bSdh 			phyp->target->phy = phyp;
79064c06356bSdh 			phyp->target->dtype = phyp->dtype;
790773a3eccdSDavid Hollister 			ctmp->target = NULL;
79084c06356bSdh 			mutex_exit(&phyp->target->statlock);
79094c06356bSdh 		}
79104c06356bSdh 	}
79114c06356bSdh }
79124c06356bSdh 
79134c06356bSdh /*
79144c06356bSdh  * Called with iport lock held
79154c06356bSdh  */
79164c06356bSdh void
79174c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
79184c06356bSdh {
79194c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
79204c06356bSdh 	ASSERT(phyp);
79214c06356bSdh 	ASSERT(!list_link_active(&phyp->list_node));
79224c06356bSdh 	iport->nphy++;
7923145e0143Sdh 	list_insert_tail(&iport->phys, phyp);
79244c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
79254c06356bSdh 	    &iport->nphy);
792635dae232SSrikanth Suravajhala 	mutex_enter(&phyp->phy_lock);
792735dae232SSrikanth Suravajhala 	pmcs_create_one_phy_stats(iport, phyp);
792835dae232SSrikanth Suravajhala 	mutex_exit(&phyp->phy_lock);
79294c06356bSdh 	mutex_enter(&iport->refcnt_lock);
79304c06356bSdh 	iport->refcnt++;
79314c06356bSdh 	mutex_exit(&iport->refcnt_lock);
79324c06356bSdh }
79334c06356bSdh 
79344c06356bSdh /*
79354c06356bSdh  * Called with the iport lock held
79364c06356bSdh  */
79374c06356bSdh void
79384c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
79394c06356bSdh {
79404c06356bSdh 	pmcs_phy_t *pptr, *next_pptr;
79414c06356bSdh 
79424c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
79434c06356bSdh 
79444c06356bSdh 	/*
79454c06356bSdh 	 * If phyp is NULL, remove all PHYs from the iport
79464c06356bSdh 	 */
79474c06356bSdh 	if (phyp == NULL) {
79484c06356bSdh 		for (pptr = list_head(&iport->phys); pptr != NULL;
79494c06356bSdh 		    pptr = next_pptr) {
79504c06356bSdh 			next_pptr = list_next(&iport->phys, pptr);
79514c06356bSdh 			mutex_enter(&pptr->phy_lock);
795235dae232SSrikanth Suravajhala 			if (pptr->phy_stats != NULL) {
795335dae232SSrikanth Suravajhala 				kstat_delete(pptr->phy_stats);
795435dae232SSrikanth Suravajhala 				pptr->phy_stats = NULL;
795535dae232SSrikanth Suravajhala 			}
79564c06356bSdh 			pptr->iport = NULL;
79575c45adf0SJesse Butler 			pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
79585c45adf0SJesse Butler 			    pptr->tgt_port_pm_tmp, B_FALSE);
79594c06356bSdh 			mutex_exit(&pptr->phy_lock);
79604c06356bSdh 			pmcs_rele_iport(iport);
79614c06356bSdh 			list_remove(&iport->phys, pptr);
79629aed1621SDavid Hollister 			pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32,
79639aed1621SDavid Hollister 			    PMCS_NUM_PHYS, &iport->nphy);
79644c06356bSdh 		}
79654c06356bSdh 		iport->nphy = 0;
79664c06356bSdh 		return;
79674c06356bSdh 	}
79684c06356bSdh 
79694c06356bSdh 	ASSERT(phyp);
79704c06356bSdh 	ASSERT(iport->nphy > 0);
79714c06356bSdh 	ASSERT(list_link_active(&phyp->list_node));
79724c06356bSdh 	iport->nphy--;
7973145e0143Sdh 	list_remove(&iport->phys, phyp);
7974499cfd15SDavid Hollister 	pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp,
7975499cfd15SDavid Hollister 	    phyp->tgt_port_pm_tmp, B_FALSE);
79764c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
79774c06356bSdh 	    &iport->nphy);
79784c06356bSdh 	pmcs_rele_iport(iport);
79794c06356bSdh }
79804c06356bSdh 
79814c06356bSdh /*
79824c06356bSdh  * This function checks to see if the target pointed to by phyp is still
79834c06356bSdh  * correct.  This is done by comparing the target's unit address with the
79844c06356bSdh  * SAS address in phyp.
79854c06356bSdh  *
79864c06356bSdh  * Called with PHY locked and target statlock held
79874c06356bSdh  */
79884c06356bSdh static boolean_t
79894c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp)
79904c06356bSdh {
79914c06356bSdh 	uint64_t wwn;
79924c06356bSdh 	char unit_address[PMCS_MAX_UA_SIZE];
79934c06356bSdh 	boolean_t rval = B_FALSE;
79944c06356bSdh 
79954c06356bSdh 	ASSERT(phyp);
79964c06356bSdh 	ASSERT(phyp->target);
79974c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
79984c06356bSdh 	ASSERT(mutex_owned(&phyp->target->statlock));
79994c06356bSdh 
80004c06356bSdh 	wwn = pmcs_barray2wwn(phyp->sas_address);
80014c06356bSdh 	(void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
80024c06356bSdh 
80034c06356bSdh 	if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
80044c06356bSdh 	    strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
80054c06356bSdh 		rval = B_TRUE;
80064c06356bSdh 	}
80074c06356bSdh 
80084c06356bSdh 	return (rval);
80094c06356bSdh }
80106745c559SJesse Butler /*
80116745c559SJesse Butler  * Commands used to serialize SMP requests.
80126745c559SJesse Butler  *
80136745c559SJesse Butler  * The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd
80146745c559SJesse Butler  * queued for the same SMP target. If a third SMP cmd is sent to the SPC for an
80156745c559SJesse Butler  * SMP target that already has a SMP cmd pending and one queued, then the
80166745c559SJesse Butler  * SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response.
80176745c559SJesse Butler  *
80186745c559SJesse Butler  * Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP
80196745c559SJesse Butler  * cmds that can be queued is controlled by the PORT_CONTROL IOMB. The
80206745c559SJesse Butler  * SPC default is 1 SMP command/port (iport).  These 2 queued SMP cmds would
80216745c559SJesse Butler  * have to be for different SMP targets.  The INTERNAL_SMP_RESOURCE error will
80226745c559SJesse Butler  * also be returned if a 2nd SMP cmd is sent to the controller when there is
80236745c559SJesse Butler  * already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the
80246745c559SJesse Butler  * queue if there are already 2 queued SMP cmds.
80256745c559SJesse Butler  */
80266745c559SJesse Butler void
80276745c559SJesse Butler pmcs_smp_acquire(pmcs_iport_t *iport)
80286745c559SJesse Butler {
80296745c559SJesse Butler 	if (iport == NULL) {
80306745c559SJesse Butler 		return;
80316745c559SJesse Butler 	}
80326745c559SJesse Butler 
80336745c559SJesse Butler 	mutex_enter(&iport->smp_lock);
80346745c559SJesse Butler 	while (iport->smp_active) {
80356745c559SJesse Butler 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
80366745c559SJesse Butler 		    "%s: SMP is active on thread 0x%p, waiting", __func__,
80376745c559SJesse Butler 		    (void *)iport->smp_active_thread);
80386745c559SJesse Butler 		cv_wait(&iport->smp_cv, &iport->smp_lock);
80396745c559SJesse Butler 	}
80406745c559SJesse Butler 	iport->smp_active = B_TRUE;
80416745c559SJesse Butler 	iport->smp_active_thread = curthread;
80429aed1621SDavid Hollister 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
80436745c559SJesse Butler 	    "%s: SMP acquired by thread 0x%p", __func__,
80446745c559SJesse Butler 	    (void *)iport->smp_active_thread);
80456745c559SJesse Butler 	mutex_exit(&iport->smp_lock);
80466745c559SJesse Butler }
80476745c559SJesse Butler 
80486745c559SJesse Butler void
80496745c559SJesse Butler pmcs_smp_release(pmcs_iport_t *iport)
80506745c559SJesse Butler {
80516745c559SJesse Butler 	if (iport == NULL) {
80526745c559SJesse Butler 		return;
80536745c559SJesse Butler 	}
80546745c559SJesse Butler 
80556745c559SJesse Butler 	mutex_enter(&iport->smp_lock);
80569aed1621SDavid Hollister 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
80576745c559SJesse Butler 	    "%s: SMP released by thread 0x%p", __func__, (void *)curthread);
80586745c559SJesse Butler 	iport->smp_active = B_FALSE;
80596745c559SJesse Butler 	iport->smp_active_thread = NULL;
80606745c559SJesse Butler 	cv_signal(&iport->smp_cv);
80616745c559SJesse Butler 	mutex_exit(&iport->smp_lock);
80626745c559SJesse Butler }
8063499cfd15SDavid Hollister 
8064499cfd15SDavid Hollister /*
8065499cfd15SDavid Hollister  * Update a PHY's attached-port-pm and target-port-pm properties
8066499cfd15SDavid Hollister  *
8067499cfd15SDavid Hollister  * phyp: PHY whose properties are to be updated
8068499cfd15SDavid Hollister  *
8069499cfd15SDavid Hollister  * att_bv: Bit value of the attached-port-pm property to be updated in the
8070499cfd15SDavid Hollister  * 64-bit holding area for the PHY.
8071499cfd15SDavid Hollister  *
8072499cfd15SDavid Hollister  * tgt_bv: Bit value of the target-port-pm property to update in the 64-bit
8073499cfd15SDavid Hollister  * holding area for the PHY.
8074499cfd15SDavid Hollister  *
8075499cfd15SDavid Hollister  * prop_add_val: If TRUE, we're adding bits into the property value.
8076499cfd15SDavid Hollister  * Otherwise, we're taking them out.  Either way, the properties for this
8077499cfd15SDavid Hollister  * PHY will be updated.
8078499cfd15SDavid Hollister  */
8079499cfd15SDavid Hollister void
8080499cfd15SDavid Hollister pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv,
8081499cfd15SDavid Hollister     boolean_t prop_add_val)
8082499cfd15SDavid Hollister {
8083499cfd15SDavid Hollister 	if (prop_add_val) {
8084499cfd15SDavid Hollister 		/*
8085499cfd15SDavid Hollister 		 * If the values are currently 0, then we're setting the
8086499cfd15SDavid Hollister 		 * phymask for just this PHY as well.
8087499cfd15SDavid Hollister 		 */
8088499cfd15SDavid Hollister 		if (phyp->att_port_pm_tmp == 0) {
8089616875b4SDavid Hollister 			phyp->att_port_pm = att_bv;
8090616875b4SDavid Hollister 			phyp->tgt_port_pm = tgt_bv;
8091499cfd15SDavid Hollister 		}
8092616875b4SDavid Hollister 		phyp->att_port_pm_tmp |= att_bv;
8093616875b4SDavid Hollister 		phyp->tgt_port_pm_tmp |= tgt_bv;
8094499cfd15SDavid Hollister 		(void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN,
8095499cfd15SDavid Hollister 		    "%"PRIx64, phyp->att_port_pm_tmp);
8096499cfd15SDavid Hollister 		(void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN,
8097499cfd15SDavid Hollister 		    "%"PRIx64, phyp->tgt_port_pm_tmp);
8098499cfd15SDavid Hollister 	} else {
8099616875b4SDavid Hollister 		phyp->att_port_pm_tmp &= ~att_bv;
8100616875b4SDavid Hollister 		phyp->tgt_port_pm_tmp &= ~tgt_bv;
8101499cfd15SDavid Hollister 		if (phyp->att_port_pm_tmp) {
8102499cfd15SDavid Hollister 			(void) snprintf(phyp->att_port_pm_str,
8103499cfd15SDavid Hollister 			    PMCS_PM_MAX_NAMELEN, "%"PRIx64,
8104499cfd15SDavid Hollister 			    phyp->att_port_pm_tmp);
8105499cfd15SDavid Hollister 		} else {
8106499cfd15SDavid Hollister 			phyp->att_port_pm_str[0] = '\0';
8107499cfd15SDavid Hollister 			phyp->att_port_pm = 0;
8108499cfd15SDavid Hollister 		}
8109499cfd15SDavid Hollister 		if (phyp->tgt_port_pm_tmp) {
8110499cfd15SDavid Hollister 			(void) snprintf(phyp->tgt_port_pm_str,
8111499cfd15SDavid Hollister 			    PMCS_PM_MAX_NAMELEN, "%"PRIx64,
8112499cfd15SDavid Hollister 			    phyp->tgt_port_pm_tmp);
8113499cfd15SDavid Hollister 		} else {
8114499cfd15SDavid Hollister 			phyp->tgt_port_pm_str[0] = '\0';
8115499cfd15SDavid Hollister 			phyp->tgt_port_pm = 0;
8116499cfd15SDavid Hollister 		}
8117499cfd15SDavid Hollister 	}
8118499cfd15SDavid Hollister 
8119499cfd15SDavid Hollister 	if (phyp->target == NULL) {
8120499cfd15SDavid Hollister 		return;
8121499cfd15SDavid Hollister 	}
8122499cfd15SDavid Hollister 
812373a3eccdSDavid Hollister 	mutex_enter(&phyp->target->statlock);
812473a3eccdSDavid Hollister 	if (!list_is_empty(&phyp->target->lun_list)) {
812573a3eccdSDavid Hollister 		pmcs_lun_t *lunp;
812673a3eccdSDavid Hollister 
812773a3eccdSDavid Hollister 		lunp = list_head(&phyp->target->lun_list);
812873a3eccdSDavid Hollister 		while (lunp) {
812973a3eccdSDavid Hollister 			(void) scsi_device_prop_update_string(lunp->sd,
813073a3eccdSDavid Hollister 			    SCSI_DEVICE_PROP_PATH,
813173a3eccdSDavid Hollister 			    SCSI_ADDR_PROP_ATTACHED_PORT_PM,
813273a3eccdSDavid Hollister 			    phyp->att_port_pm_str);
813373a3eccdSDavid Hollister 			(void) scsi_device_prop_update_string(lunp->sd,
813473a3eccdSDavid Hollister 			    SCSI_DEVICE_PROP_PATH,
813573a3eccdSDavid Hollister 			    SCSI_ADDR_PROP_TARGET_PORT_PM,
813673a3eccdSDavid Hollister 			    phyp->tgt_port_pm_str);
813773a3eccdSDavid Hollister 			lunp = list_next(&phyp->target->lun_list, lunp);
813873a3eccdSDavid Hollister 		}
8139499cfd15SDavid Hollister 	} else if (phyp->target->smpd) {
8140499cfd15SDavid Hollister 		(void) smp_device_prop_update_string(phyp->target->smpd,
8141499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_ATTACHED_PORT_PM,
8142499cfd15SDavid Hollister 		    phyp->att_port_pm_str);
8143499cfd15SDavid Hollister 		(void) smp_device_prop_update_string(phyp->target->smpd,
8144499cfd15SDavid Hollister 		    SCSI_ADDR_PROP_TARGET_PORT_PM,
8145499cfd15SDavid Hollister 		    phyp->tgt_port_pm_str);
8146499cfd15SDavid Hollister 	}
814773a3eccdSDavid Hollister 	mutex_exit(&phyp->target->statlock);
8148499cfd15SDavid Hollister }
8149601c90f1SSrikanth, Ramana 
8150601c90f1SSrikanth, Ramana /* ARGSUSED */
8151601c90f1SSrikanth, Ramana void
8152601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
8153601c90f1SSrikanth, Ramana {
8154601c90f1SSrikanth, Ramana 	pmcs_phy_t	*pptr;
8155601c90f1SSrikanth, Ramana 
8156601c90f1SSrikanth, Ramana 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8157601c90f1SSrikanth, Ramana 		pmcs_lock_phy(pptr);
8158601c90f1SSrikanth, Ramana 		if (pptr->deregister_wait) {
8159601c90f1SSrikanth, Ramana 			pmcs_deregister_device(pwp, pptr);
8160601c90f1SSrikanth, Ramana 		}
8161601c90f1SSrikanth, Ramana 		pmcs_unlock_phy(pptr);
8162601c90f1SSrikanth, Ramana 	}
8163601c90f1SSrikanth, Ramana }
81649aed1621SDavid Hollister 
81659aed1621SDavid Hollister /*
81669aed1621SDavid Hollister  * pmcs_iport_active
81679aed1621SDavid Hollister  *
81689aed1621SDavid Hollister  * Mark this iport as active.  Called with the iport lock held.
81699aed1621SDavid Hollister  */
81709aed1621SDavid Hollister static void
81719aed1621SDavid Hollister pmcs_iport_active(pmcs_iport_t *iport)
81729aed1621SDavid Hollister {
81739aed1621SDavid Hollister 	ASSERT(mutex_owned(&iport->lock));
81749aed1621SDavid Hollister 
81759aed1621SDavid Hollister 	iport->ua_state = UA_ACTIVE;
81769aed1621SDavid Hollister 	iport->smp_active = B_FALSE;
81779aed1621SDavid Hollister 	iport->smp_active_thread = NULL;
81789aed1621SDavid Hollister }
81799aed1621SDavid Hollister 
81809aed1621SDavid Hollister /* ARGSUSED */
81819aed1621SDavid Hollister static void
81829aed1621SDavid Hollister pmcs_tgtmap_activate_cb(void *tgtmap_priv, char *tgt_addr,
81839aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t tgt_type, void **tgt_privp)
81849aed1621SDavid Hollister {
81859aed1621SDavid Hollister 	pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
81865c45adf0SJesse Butler 	pmcs_hw_t *pwp = iport->pwp;
81875c45adf0SJesse Butler 	pmcs_xscsi_t *target;
81889aed1621SDavid Hollister 
81895c45adf0SJesse Butler 	/*
81905c45adf0SJesse Butler 	 * Look up the target.  If there is one, and it doesn't have a PHY
81915c45adf0SJesse Butler 	 * pointer, re-establish that linkage here.
81925c45adf0SJesse Butler 	 */
81935c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
81945c45adf0SJesse Butler 	target = pmcs_get_target(iport, tgt_addr, B_FALSE);
81955c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
81965c45adf0SJesse Butler 
81975c45adf0SJesse Butler 	/*
81985c45adf0SJesse Butler 	 * If we got a target, it will now have a PHY pointer and the PHY
81995c45adf0SJesse Butler 	 * will point to the target.  The PHY will be locked, so we'll need
82005c45adf0SJesse Butler 	 * to unlock it.
82015c45adf0SJesse Butler 	 */
82025c45adf0SJesse Butler 	if (target) {
82035c45adf0SJesse Butler 		pmcs_unlock_phy(target->phy);
82045c45adf0SJesse Butler 	}
82059aed1621SDavid Hollister 
82069aed1621SDavid Hollister 	/*
82079aed1621SDavid Hollister 	 * Update config_restart_time so we don't try to restart discovery
82089aed1621SDavid Hollister 	 * while enumeration is still in progress.
82099aed1621SDavid Hollister 	 */
82105c45adf0SJesse Butler 	mutex_enter(&pwp->config_lock);
82115c45adf0SJesse Butler 	pwp->config_restart_time = ddi_get_lbolt() +
82129aed1621SDavid Hollister 	    drv_usectohz(PMCS_REDISCOVERY_DELAY);
82135c45adf0SJesse Butler 	mutex_exit(&pwp->config_lock);
82149aed1621SDavid Hollister }
82159aed1621SDavid Hollister 
82169aed1621SDavid Hollister /* ARGSUSED */
82179aed1621SDavid Hollister static boolean_t
82189aed1621SDavid Hollister pmcs_tgtmap_deactivate_cb(void *tgtmap_priv, char *tgt_addr,
82199aed1621SDavid Hollister     scsi_tgtmap_tgt_type_t tgt_type, void *tgt_priv,
82209aed1621SDavid Hollister     scsi_tgtmap_deact_rsn_t tgt_deact_rsn)
82219aed1621SDavid Hollister {
82229aed1621SDavid Hollister 	pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
82239aed1621SDavid Hollister 	pmcs_phy_t *phyp;
82249aed1621SDavid Hollister 	boolean_t rediscover = B_FALSE;
82259aed1621SDavid Hollister 
82269aed1621SDavid Hollister 	ASSERT(iport);
82279aed1621SDavid Hollister 
82289aed1621SDavid Hollister 	phyp = pmcs_find_phy_by_sas_address(iport->pwp, iport, NULL, tgt_addr);
82299aed1621SDavid Hollister 	if (phyp == NULL) {
82309aed1621SDavid Hollister 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
82319aed1621SDavid Hollister 		    "%s: Couldn't find PHY at %s", __func__, tgt_addr);
82329aed1621SDavid Hollister 		return (rediscover);
82339aed1621SDavid Hollister 	}
82349aed1621SDavid Hollister 	/* phyp is locked */
82359aed1621SDavid Hollister 
82369aed1621SDavid Hollister 	if (!phyp->reenumerate && phyp->configured) {
82379aed1621SDavid Hollister 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, phyp->target,
82389aed1621SDavid Hollister 		    "%s: PHY @ %s is configured... re-enumerate", __func__,
82399aed1621SDavid Hollister 		    tgt_addr);
82409aed1621SDavid Hollister 		phyp->reenumerate = 1;
82419aed1621SDavid Hollister 	}
82429aed1621SDavid Hollister 
82439aed1621SDavid Hollister 	/*
82449aed1621SDavid Hollister 	 * Check to see if reenumerate is set, and if so, if we've reached our
82459aed1621SDavid Hollister 	 * maximum number of retries.
82469aed1621SDavid Hollister 	 */
82479aed1621SDavid Hollister 	if (phyp->reenumerate) {
82489aed1621SDavid Hollister 		if (phyp->enum_attempts == PMCS_MAX_REENUMERATE) {
82499aed1621SDavid Hollister 			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
82509aed1621SDavid Hollister 			    phyp->target,
82519aed1621SDavid Hollister 			    "%s: No more enumeration attempts for %s", __func__,
82529aed1621SDavid Hollister 			    tgt_addr);
82539aed1621SDavid Hollister 		} else {
82549aed1621SDavid Hollister 			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
82559aed1621SDavid Hollister 			    phyp->target, "%s: Re-attempt enumeration for %s",
82569aed1621SDavid Hollister 			    __func__, tgt_addr);
82579aed1621SDavid Hollister 			++phyp->enum_attempts;
82589aed1621SDavid Hollister 			rediscover = B_TRUE;
82599aed1621SDavid Hollister 		}
82609aed1621SDavid Hollister 
82619aed1621SDavid Hollister 		phyp->reenumerate = 0;
82629aed1621SDavid Hollister 	}
82639aed1621SDavid Hollister 
82649aed1621SDavid Hollister 	pmcs_unlock_phy(phyp);
82659aed1621SDavid Hollister 
82669aed1621SDavid Hollister 	mutex_enter(&iport->pwp->config_lock);
82679aed1621SDavid Hollister 	iport->pwp->config_restart_time = ddi_get_lbolt() +
82689aed1621SDavid Hollister 	    drv_usectohz(PMCS_REDISCOVERY_DELAY);
82699aed1621SDavid Hollister 	if (rediscover) {
82709aed1621SDavid Hollister 		iport->pwp->config_restart = B_TRUE;
82719aed1621SDavid Hollister 	} else if (iport->pwp->config_restart == B_TRUE) {
82729aed1621SDavid Hollister 		/*
82739aed1621SDavid Hollister 		 * If we aren't asking for rediscovery because of this PHY,
82749aed1621SDavid Hollister 		 * check to see if we're already asking for it on behalf of
82759aed1621SDavid Hollister 		 * some other PHY.  If so, we'll want to return TRUE, so reset
82769aed1621SDavid Hollister 		 * "rediscover" here.
82779aed1621SDavid Hollister 		 */
82789aed1621SDavid Hollister 		rediscover = B_TRUE;
82799aed1621SDavid Hollister 	}
82809aed1621SDavid Hollister 
82819aed1621SDavid Hollister 	mutex_exit(&iport->pwp->config_lock);
82829aed1621SDavid Hollister 
82839aed1621SDavid Hollister 	return (rediscover);
82849aed1621SDavid Hollister }
82859aed1621SDavid Hollister 
82869aed1621SDavid Hollister void
82879aed1621SDavid Hollister pmcs_status_disposition(pmcs_phy_t *phyp, uint32_t status)
82889aed1621SDavid Hollister {
82899aed1621SDavid Hollister 	ASSERT(phyp);
82909aed1621SDavid Hollister 	ASSERT(!mutex_owned(&phyp->phy_lock));
82919aed1621SDavid Hollister 
82929aed1621SDavid Hollister 	if (phyp == NULL) {
82939aed1621SDavid Hollister 		return;
82949aed1621SDavid Hollister 	}
82959aed1621SDavid Hollister 
82969aed1621SDavid Hollister 	pmcs_lock_phy(phyp);
82979aed1621SDavid Hollister 
82989aed1621SDavid Hollister 	/*
82999aed1621SDavid Hollister 	 * XXX: Do we need to call this function from an SSP_EVENT?
83009aed1621SDavid Hollister 	 */
83019aed1621SDavid Hollister 
83029aed1621SDavid Hollister 	switch (status) {
83039aed1621SDavid Hollister 	case PMCOUT_STATUS_NO_DEVICE:
83049aed1621SDavid Hollister 	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
83059aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERR_BREAK:
83069aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
83079aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
83089aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
83099aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
83109aed1621SDavid Hollister 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
83119aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
83129aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
83139aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
83149aed1621SDavid Hollister 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
83159aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
83169aed1621SDavid Hollister 	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
83179aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
83189aed1621SDavid Hollister 	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
83199aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
83209aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
83219aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
83229aed1621SDavid Hollister 	case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
83239aed1621SDavid Hollister 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
83249aed1621SDavid Hollister 		    "%s: status = 0x%x for " SAS_ADDR_FMT ", reenumerate",
83259aed1621SDavid Hollister 		    __func__, status, SAS_ADDR_PRT(phyp->sas_address));
83269aed1621SDavid Hollister 		phyp->reenumerate = 1;
83279aed1621SDavid Hollister 		break;
83289aed1621SDavid Hollister 
83299aed1621SDavid Hollister 	default:
83309aed1621SDavid Hollister 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
83319aed1621SDavid Hollister 		    "%s: status = 0x%x for " SAS_ADDR_FMT ", no reenumeration",
83329aed1621SDavid Hollister 		    __func__, status, SAS_ADDR_PRT(phyp->sas_address));
83339aed1621SDavid Hollister 		break;
83349aed1621SDavid Hollister 	}
83359aed1621SDavid Hollister 
83369aed1621SDavid Hollister 	pmcs_unlock_phy(phyp);
83379aed1621SDavid Hollister }
83389aed1621SDavid Hollister 
83399aed1621SDavid Hollister /*
83409aed1621SDavid Hollister  * Add the list of PHYs pointed to by phyp to the dead_phys_list
83419aed1621SDavid Hollister  *
83429aed1621SDavid Hollister  * Called with all PHYs in the list locked
83439aed1621SDavid Hollister  */
83449aed1621SDavid Hollister static void
83459aed1621SDavid Hollister pmcs_add_dead_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
83469aed1621SDavid Hollister {
83479aed1621SDavid Hollister 	mutex_enter(&pwp->dead_phylist_lock);
83489aed1621SDavid Hollister 	while (phyp) {
83499aed1621SDavid Hollister 		pmcs_phy_t *nxt = phyp->sibling;
83509aed1621SDavid Hollister 		ASSERT(phyp->dead);
83519aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
83529aed1621SDavid Hollister 		    "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
83539aed1621SDavid Hollister 		    (void *)phyp, phyp->path, phyp->ref_count);
83549aed1621SDavid Hollister 		/*
83559aed1621SDavid Hollister 		 * Put this PHY on the dead PHY list for the watchdog to
83569aed1621SDavid Hollister 		 * clean up after any outstanding work has completed.
83579aed1621SDavid Hollister 		 */
83589aed1621SDavid Hollister 		phyp->dead_next = pwp->dead_phys;
83599aed1621SDavid Hollister 		pwp->dead_phys = phyp;
83609aed1621SDavid Hollister 		pmcs_unlock_phy(phyp);
83619aed1621SDavid Hollister 		phyp = nxt;
83629aed1621SDavid Hollister 	}
83639aed1621SDavid Hollister 	mutex_exit(&pwp->dead_phylist_lock);
83649aed1621SDavid Hollister }
83652ac4abe8SDavid Hollister 
83662ac4abe8SDavid Hollister static void
83672ac4abe8SDavid Hollister pmcs_get_fw_version(pmcs_hw_t *pwp)
83682ac4abe8SDavid Hollister {
83692ac4abe8SDavid Hollister 	uint32_t ila_len, ver_hi, ver_lo;
83702ac4abe8SDavid Hollister 	uint8_t ila_ver_string[9], img_flag;
83712ac4abe8SDavid Hollister 	char uc, *ucp = &uc;
83722ac4abe8SDavid Hollister 	unsigned long ila_ver;
83732ac4abe8SDavid Hollister 	uint64_t ver_hilo;
83742ac4abe8SDavid Hollister 
83752ac4abe8SDavid Hollister 	/* Firmware version is easy. */
83762ac4abe8SDavid Hollister 	pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
83772ac4abe8SDavid Hollister 
83782ac4abe8SDavid Hollister 	/*
83792ac4abe8SDavid Hollister 	 * Get the image size (2nd to last dword)
83802ac4abe8SDavid Hollister 	 * NOTE: The GSM registers are mapped little-endian, but the data
83812ac4abe8SDavid Hollister 	 * on the flash is actually big-endian, so we need to swap these values
83822ac4abe8SDavid Hollister 	 * regardless of which platform we're on.
83832ac4abe8SDavid Hollister 	 */
83842ac4abe8SDavid Hollister 	ila_len = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83852ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + GSM_SM_BLKSZ - (2 << 2)));
83862ac4abe8SDavid Hollister 	if (ila_len > 65535) {
83872ac4abe8SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
83882ac4abe8SDavid Hollister 		    "%s: Invalid ILA image size (0x%x)?", __func__, ila_len);
83892ac4abe8SDavid Hollister 		return;
83902ac4abe8SDavid Hollister 	}
83912ac4abe8SDavid Hollister 
83922ac4abe8SDavid Hollister 	/*
83932ac4abe8SDavid Hollister 	 * The numeric version is at ila_len - PMCS_ILA_VER_OFFSET
83942ac4abe8SDavid Hollister 	 */
83952ac4abe8SDavid Hollister 	ver_hi = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83962ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET));
83972ac4abe8SDavid Hollister 	ver_lo = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
83982ac4abe8SDavid Hollister 	    GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET + 4));
83992ac4abe8SDavid Hollister 	ver_hilo = BE_64(((uint64_t)ver_hi << 32) | ver_lo);
84002ac4abe8SDavid Hollister 	bcopy((const void *)&ver_hilo, &ila_ver_string[0], 8);
84012ac4abe8SDavid Hollister 	ila_ver_string[8] = '\0';
84022ac4abe8SDavid Hollister 
84032ac4abe8SDavid Hollister 	(void) ddi_strtoul((const char *)ila_ver_string, &ucp, 16, &ila_ver);
84042ac4abe8SDavid Hollister 	pwp->ila_ver = (int)(ila_ver & 0xffffffff);
84052ac4abe8SDavid Hollister 
84062ac4abe8SDavid Hollister 	img_flag = (BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
84072ac4abe8SDavid Hollister 	    GSM_FLASH_IMG_FLAGS)) & 0xff000000) >> 24;
84082ac4abe8SDavid Hollister 	if (img_flag & PMCS_IMG_FLAG_A) {
84092ac4abe8SDavid Hollister 		pwp->fw_active_img = 1;
84102ac4abe8SDavid Hollister 	} else {
84112ac4abe8SDavid Hollister 		pwp->fw_active_img = 0;
84122ac4abe8SDavid Hollister 	}
84132ac4abe8SDavid Hollister }
8414