xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c (revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6)
1*4c06356bSdh /*
2*4c06356bSdh  * CDDL HEADER START
3*4c06356bSdh  *
4*4c06356bSdh  * The contents of this file are subject to the terms of the
5*4c06356bSdh  * Common Development and Distribution License (the "License").
6*4c06356bSdh  * You may not use this file except in compliance with the License.
7*4c06356bSdh  *
8*4c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4c06356bSdh  * or http://www.opensolaris.org/os/licensing.
10*4c06356bSdh  * See the License for the specific language governing permissions
11*4c06356bSdh  * and limitations under the License.
12*4c06356bSdh  *
13*4c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
14*4c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
16*4c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
17*4c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4c06356bSdh  *
19*4c06356bSdh  * CDDL HEADER END
20*4c06356bSdh  *
21*4c06356bSdh  *
22*4c06356bSdh  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*4c06356bSdh  * Use is subject to license terms.
24*4c06356bSdh  */
25*4c06356bSdh 
26*4c06356bSdh /*
27*4c06356bSdh  * This file contains various support routines.
28*4c06356bSdh  */
29*4c06356bSdh 
30*4c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
31*4c06356bSdh 
32*4c06356bSdh /*
33*4c06356bSdh  * Local static data
34*4c06356bSdh  */
35*4c06356bSdh static int tgtmap_usec = MICROSEC;
36*4c06356bSdh 
37*4c06356bSdh /*
38*4c06356bSdh  * SAS Topology Configuration
39*4c06356bSdh  */
40*4c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
41*4c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
42*4c06356bSdh 
43*4c06356bSdh static boolean_t pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
44*4c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
45*4c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
46*4c06356bSdh 
47*4c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
48*4c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
49*4c06356bSdh     pmcs_phy_t *);
50*4c06356bSdh 
51*4c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
52*4c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
53*4c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
54*4c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
55*4c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *);
56*4c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
57*4c06356bSdh     pmcs_phy_t *);
58*4c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
59*4c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
60*4c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int);
61*4c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
62*4c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
63*4c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
64*4c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *);
65*4c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
66*4c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *);
67*4c06356bSdh static void pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp,
68*4c06356bSdh     pmcs_xscsi_t *tgt, pmcs_hw_t *pwp, const char *func_name, int line,
69*4c06356bSdh     char *reason_string);
70*4c06356bSdh 
71*4c06356bSdh /*
72*4c06356bSdh  * Often used strings
73*4c06356bSdh  */
74*4c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure";
75*4c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
76*4c06356bSdh const char pmcs_timeo[] = "!%s: command timed out";
77*4c06356bSdh 
78*4c06356bSdh extern const ddi_dma_attr_t pmcs_dattr;
79*4c06356bSdh 
80*4c06356bSdh /*
81*4c06356bSdh  * Some Initial setup steps.
82*4c06356bSdh  */
83*4c06356bSdh 
84*4c06356bSdh int
85*4c06356bSdh pmcs_setup(pmcs_hw_t *pwp)
86*4c06356bSdh {
87*4c06356bSdh 	uint32_t barval = pwp->mpibar;
88*4c06356bSdh 	uint32_t i, scratch, regbar, regoff, barbar, baroff;
89*4c06356bSdh 	uint32_t new_ioq_depth, ferr = 0;
90*4c06356bSdh 
91*4c06356bSdh 	/*
92*4c06356bSdh 	 * Check current state. If we're not at READY state,
93*4c06356bSdh 	 * we can't go further.
94*4c06356bSdh 	 */
95*4c06356bSdh 	scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
96*4c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
97*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: AAP Error State (0x%x)",
98*4c06356bSdh 		    __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
99*4c06356bSdh 		    PMCS_MSGU_AAP_ERROR_MASK);
100*4c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
101*4c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
102*4c06356bSdh 		return (-1);
103*4c06356bSdh 	}
104*4c06356bSdh 	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
105*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
106*4c06356bSdh 		    "%s: AAP unit not ready (state 0x%x)",
107*4c06356bSdh 		    __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
108*4c06356bSdh 		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
109*4c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
110*4c06356bSdh 		return (-1);
111*4c06356bSdh 	}
112*4c06356bSdh 
113*4c06356bSdh 	/*
114*4c06356bSdh 	 * Read the offset from the Message Unit scratchpad 0 register.
115*4c06356bSdh 	 * This allows us to read the MPI Configuration table.
116*4c06356bSdh 	 *
117*4c06356bSdh 	 * Check its signature for validity.
118*4c06356bSdh 	 */
119*4c06356bSdh 	baroff = barval;
120*4c06356bSdh 	barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
121*4c06356bSdh 	baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
122*4c06356bSdh 
123*4c06356bSdh 	regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
124*4c06356bSdh 	regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
125*4c06356bSdh 	regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
126*4c06356bSdh 
127*4c06356bSdh 	if (regoff > baroff) {
128*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI Table Length "
129*4c06356bSdh 		    "(register offset=0x%08x, passed offset=0x%08x)", __func__,
130*4c06356bSdh 		    regoff, baroff);
131*4c06356bSdh 		return (-1);
132*4c06356bSdh 	}
133*4c06356bSdh 	if (regbar != barbar) {
134*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI BAR (register "
135*4c06356bSdh 		    "BAROFF=0x%08x, passed BAROFF=0x%08x)", __func__,
136*4c06356bSdh 		    regbar, barbar);
137*4c06356bSdh 		return (-1);
138*4c06356bSdh 	}
139*4c06356bSdh 	pwp->mpi_offset = regoff;
140*4c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
141*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
142*4c06356bSdh 		    "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
143*4c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
144*4c06356bSdh 		return (-1);
145*4c06356bSdh 	}
146*4c06356bSdh 
147*4c06356bSdh 	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
148*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
149*4c06356bSdh 		    "%s: Bad MPI Configuration Revision 0x%x", __func__,
150*4c06356bSdh 		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
151*4c06356bSdh 		return (-1);
152*4c06356bSdh 	}
153*4c06356bSdh 
154*4c06356bSdh 	/*
155*4c06356bSdh 	 * Generate offsets for the General System, Inbound Queue Configuration
156*4c06356bSdh 	 * and Outbound Queue configuration tables. This way the macros to
157*4c06356bSdh 	 * access those tables will work correctly.
158*4c06356bSdh 	 */
159*4c06356bSdh 	pwp->mpi_gst_offset =
160*4c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
161*4c06356bSdh 	pwp->mpi_iqc_offset =
162*4c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
163*4c06356bSdh 	pwp->mpi_oqc_offset =
164*4c06356bSdh 	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
165*4c06356bSdh 
166*4c06356bSdh 	pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
167*4c06356bSdh 
168*4c06356bSdh 	pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
169*4c06356bSdh 	pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
170*4c06356bSdh 
171*4c06356bSdh 	pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
172*4c06356bSdh 	pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
173*4c06356bSdh 	pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
174*4c06356bSdh 	if (pwp->max_iq <= PMCS_NIQ) {
175*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Inbound Queues "
176*4c06356bSdh 		    "supported (need %d, max_oq=%d)", __func__, pwp->max_iq,
177*4c06356bSdh 		    PMCS_NIQ);
178*4c06356bSdh 		return (-1);
179*4c06356bSdh 	}
180*4c06356bSdh 	if (pwp->max_oq <= PMCS_NOQ) {
181*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Outbound Queues "
182*4c06356bSdh 		    "supported (need %d, max_oq=%d)", __func__, pwp->max_oq,
183*4c06356bSdh 		    PMCS_NOQ);
184*4c06356bSdh 		return (-1);
185*4c06356bSdh 	}
186*4c06356bSdh 	if (pwp->nphy == 0) {
187*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: zero phys reported",
188*4c06356bSdh 		    __func__);
189*4c06356bSdh 		return (-1);
190*4c06356bSdh 	}
191*4c06356bSdh 	if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
192*4c06356bSdh 		pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
193*4c06356bSdh 	}
194*4c06356bSdh 
195*4c06356bSdh 
196*4c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
197*4c06356bSdh 		PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
198*4c06356bSdh 		PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
199*4c06356bSdh 	}
200*4c06356bSdh 
201*4c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
202*4c06356bSdh 	    (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
203*4c06356bSdh 	    (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
204*4c06356bSdh 
205*4c06356bSdh 	/*
206*4c06356bSdh 	 * Verify that ioq_depth is valid (> 0 and not so high that it
207*4c06356bSdh 	 * would cause us to overrun the chip with commands).
208*4c06356bSdh 	 */
209*4c06356bSdh 	if (pwp->ioq_depth == 0) {
210*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
211*4c06356bSdh 		    "%s: I/O queue depth set to 0. Setting to %d",
212*4c06356bSdh 		    __func__, PMCS_NQENTRY);
213*4c06356bSdh 		pwp->ioq_depth = PMCS_NQENTRY;
214*4c06356bSdh 	}
215*4c06356bSdh 
216*4c06356bSdh 	if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
217*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
218*4c06356bSdh 		    "%s: I/O queue depth set too low (%d). Setting to %d",
219*4c06356bSdh 		    __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
220*4c06356bSdh 		pwp->ioq_depth = PMCS_MIN_NQENTRY;
221*4c06356bSdh 	}
222*4c06356bSdh 
223*4c06356bSdh 	if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
224*4c06356bSdh 		new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
225*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
226*4c06356bSdh 		    "%s: I/O queue depth set too high (%d). Setting to %d",
227*4c06356bSdh 		    __func__, pwp->ioq_depth, new_ioq_depth);
228*4c06356bSdh 		pwp->ioq_depth = new_ioq_depth;
229*4c06356bSdh 	}
230*4c06356bSdh 
231*4c06356bSdh 	/*
232*4c06356bSdh 	 * Allocate consistent memory for OQs and IQs.
233*4c06356bSdh 	 */
234*4c06356bSdh 	pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
235*4c06356bSdh 	pwp->iqp_dma_attr.dma_attr_align =
236*4c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
237*4c06356bSdh 
238*4c06356bSdh 	/*
239*4c06356bSdh 	 * The Rev C chip has the ability to do PIO to or from consistent
240*4c06356bSdh 	 * memory anywhere in a 64 bit address space, but the firmware is
241*4c06356bSdh 	 * not presently set up to do so.
242*4c06356bSdh 	 */
243*4c06356bSdh 	pwp->iqp_dma_attr.dma_attr_addr_hi =
244*4c06356bSdh 	    pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
245*4c06356bSdh 
246*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
247*4c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
248*4c06356bSdh 		    &pwp->iqp_acchdls[i],
249*4c06356bSdh 		    &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
250*4c06356bSdh 		    (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
251*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
252*4c06356bSdh 			    "Failed to setup DMA for iqp[%d]", i);
253*4c06356bSdh 			return (-1);
254*4c06356bSdh 		}
255*4c06356bSdh 		bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
256*4c06356bSdh 	}
257*4c06356bSdh 
258*4c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
259*4c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
260*4c06356bSdh 		    &pwp->oqp_acchdls[i],
261*4c06356bSdh 		    &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
262*4c06356bSdh 		    (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
263*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
264*4c06356bSdh 			    "Failed to setup DMA for oqp[%d]", i);
265*4c06356bSdh 			return (-1);
266*4c06356bSdh 		}
267*4c06356bSdh 		bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
268*4c06356bSdh 	}
269*4c06356bSdh 
270*4c06356bSdh 	/*
271*4c06356bSdh 	 * Install the IQ and OQ addresses (and null out the rest).
272*4c06356bSdh 	 */
273*4c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
274*4c06356bSdh 		pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
275*4c06356bSdh 		if (i < PMCS_NIQ) {
276*4c06356bSdh 			if (i != PMCS_IQ_OTHER) {
277*4c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
278*4c06356bSdh 				    pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
279*4c06356bSdh 			} else {
280*4c06356bSdh 				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
281*4c06356bSdh 				    (1 << 30) | pwp->ioq_depth |
282*4c06356bSdh 				    (PMCS_QENTRY_SIZE << 16));
283*4c06356bSdh 			}
284*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
285*4c06356bSdh 			    DWORD1(pwp->iqaddr[i]));
286*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
287*4c06356bSdh 			    DWORD0(pwp->iqaddr[i]));
288*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
289*4c06356bSdh 			    DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
290*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
291*4c06356bSdh 			    DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
292*4c06356bSdh 		} else {
293*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
294*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
295*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
296*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
297*4c06356bSdh 			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
298*4c06356bSdh 		}
299*4c06356bSdh 	}
300*4c06356bSdh 
301*4c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
302*4c06356bSdh 		pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
303*4c06356bSdh 		if (i < PMCS_NOQ) {
304*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
305*4c06356bSdh 			    (PMCS_QENTRY_SIZE << 16) | OQIEX);
306*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
307*4c06356bSdh 			    DWORD1(pwp->oqaddr[i]));
308*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
309*4c06356bSdh 			    DWORD0(pwp->oqaddr[i]));
310*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
311*4c06356bSdh 			    DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
312*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
313*4c06356bSdh 			    DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
314*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
315*4c06356bSdh 			    pwp->oqvec[i] << 24);
316*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
317*4c06356bSdh 		} else {
318*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
319*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
320*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
321*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
322*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
323*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
324*4c06356bSdh 			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
325*4c06356bSdh 		}
326*4c06356bSdh 	}
327*4c06356bSdh 
328*4c06356bSdh 	/*
329*4c06356bSdh 	 * Set up logging, if defined.
330*4c06356bSdh 	 */
331*4c06356bSdh 	if (pwp->fwlog) {
332*4c06356bSdh 		uint64_t logdma = pwp->fwaddr;
333*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
334*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
335*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
336*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
337*4c06356bSdh 		logdma += (PMCS_FWLOG_SIZE >> 1);
338*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
339*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
340*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
341*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
342*4c06356bSdh 	}
343*4c06356bSdh 
344*4c06356bSdh 	/*
345*4c06356bSdh 	 * Interrupt vectors, outbound queues, and odb_auto_clear
346*4c06356bSdh 	 *
347*4c06356bSdh 	 * MSI/MSI-X:
348*4c06356bSdh 	 * If we got 4 interrupt vectors, we'll assign one to each outbound
349*4c06356bSdh 	 * queue as well as the fatal interrupt, and auto clear can be set
350*4c06356bSdh 	 * for each.
351*4c06356bSdh 	 *
352*4c06356bSdh 	 * If we only got 2 vectors, one will be used for I/O completions
353*4c06356bSdh 	 * and the other for the other two vectors.  In this case, auto_
354*4c06356bSdh 	 * clear can only be set for I/Os, which is fine.  The fatal
355*4c06356bSdh 	 * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
356*4c06356bSdh 	 * is not an interrupt vector.
357*4c06356bSdh 	 *
358*4c06356bSdh 	 * MSI/MSI-X/INT-X:
359*4c06356bSdh 	 * If we only got 1 interrupt vector, auto_clear must be set to 0,
360*4c06356bSdh 	 * and again the fatal interrupt will be mapped to the
361*4c06356bSdh 	 * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
362*4c06356bSdh 	 */
363*4c06356bSdh 
364*4c06356bSdh 	switch (pwp->int_type) {
365*4c06356bSdh 	case PMCS_INT_MSIX:
366*4c06356bSdh 	case PMCS_INT_MSI:
367*4c06356bSdh 		switch (pwp->intr_cnt) {
368*4c06356bSdh 		case 1:
369*4c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
370*4c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
371*4c06356bSdh 			pwp->odb_auto_clear = 0;
372*4c06356bSdh 			break;
373*4c06356bSdh 		case 2:
374*4c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
375*4c06356bSdh 			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
376*4c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
377*4c06356bSdh 			    (1 << PMCS_MSIX_IODONE);
378*4c06356bSdh 			break;
379*4c06356bSdh 		case 4:
380*4c06356bSdh 			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
381*4c06356bSdh 			    (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
382*4c06356bSdh 			pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
383*4c06356bSdh 			    (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
384*4c06356bSdh 			    (1 << PMCS_MSIX_EVENTS);
385*4c06356bSdh 			break;
386*4c06356bSdh 		}
387*4c06356bSdh 		break;
388*4c06356bSdh 
389*4c06356bSdh 	case PMCS_INT_FIXED:
390*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
391*4c06356bSdh 		    PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
392*4c06356bSdh 		pwp->odb_auto_clear = 0;
393*4c06356bSdh 		break;
394*4c06356bSdh 	}
395*4c06356bSdh 
396*4c06356bSdh 	/*
397*4c06356bSdh 	 * Enable Interrupt Reassertion
398*4c06356bSdh 	 * Default Delay 1000us
399*4c06356bSdh 	 */
400*4c06356bSdh 	ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
401*4c06356bSdh 	if ((ferr & PMCS_MPI_IRAE) == 0) {
402*4c06356bSdh 		ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
403*4c06356bSdh 		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
404*4c06356bSdh 	}
405*4c06356bSdh 
406*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
407*4c06356bSdh 	pwp->mpi_table_setup = 1;
408*4c06356bSdh 	return (0);
409*4c06356bSdh }
410*4c06356bSdh 
411*4c06356bSdh /*
412*4c06356bSdh  * Start the Message Passing protocol with the PMC chip.
413*4c06356bSdh  */
414*4c06356bSdh int
415*4c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp)
416*4c06356bSdh {
417*4c06356bSdh 	int i;
418*4c06356bSdh 
419*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
420*4c06356bSdh 	for (i = 0; i < 1000; i++) {
421*4c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
422*4c06356bSdh 		    PMCS_MSGU_IBDB_MPIINI) == 0) {
423*4c06356bSdh 			break;
424*4c06356bSdh 		}
425*4c06356bSdh 		drv_usecwait(1000);
426*4c06356bSdh 	}
427*4c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
428*4c06356bSdh 		return (-1);
429*4c06356bSdh 	}
430*4c06356bSdh 	drv_usecwait(500000);
431*4c06356bSdh 
432*4c06356bSdh 	/*
433*4c06356bSdh 	 * Check to make sure we got to INIT state.
434*4c06356bSdh 	 */
435*4c06356bSdh 	if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
436*4c06356bSdh 	    PMCS_MPI_STATE_INIT) {
437*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI launch failed (GST 0x%x "
438*4c06356bSdh 		    "DBCLR 0x%x)", __func__,
439*4c06356bSdh 		    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
440*4c06356bSdh 		    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
441*4c06356bSdh 		return (-1);
442*4c06356bSdh 	}
443*4c06356bSdh 	return (0);
444*4c06356bSdh }
445*4c06356bSdh 
446*4c06356bSdh /*
447*4c06356bSdh  * Stop the Message Passing protocol with the PMC chip.
448*4c06356bSdh  */
449*4c06356bSdh int
450*4c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp)
451*4c06356bSdh {
452*4c06356bSdh 	int i;
453*4c06356bSdh 
454*4c06356bSdh 	for (i = 0; i < pwp->max_iq; i++) {
455*4c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
456*4c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
457*4c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
458*4c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
459*4c06356bSdh 		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
460*4c06356bSdh 	}
461*4c06356bSdh 	for (i = 0; i < pwp->max_oq; i++) {
462*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
463*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
464*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
465*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
466*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
467*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
468*4c06356bSdh 		pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
469*4c06356bSdh 	}
470*4c06356bSdh 	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
471*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
472*4c06356bSdh 	for (i = 0; i < 2000; i++) {
473*4c06356bSdh 		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
474*4c06356bSdh 		    PMCS_MSGU_IBDB_MPICTU) == 0) {
475*4c06356bSdh 			break;
476*4c06356bSdh 		}
477*4c06356bSdh 		drv_usecwait(1000);
478*4c06356bSdh 	}
479*4c06356bSdh 	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
480*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI stop failed", __func__);
481*4c06356bSdh 		return (-1);
482*4c06356bSdh 	}
483*4c06356bSdh 	return (0);
484*4c06356bSdh }
485*4c06356bSdh 
486*4c06356bSdh /*
487*4c06356bSdh  * Do a sequence of ECHO messages to test for MPI functionality,
488*4c06356bSdh  * all inbound and outbound queue functionality and interrupts.
489*4c06356bSdh  */
490*4c06356bSdh int
491*4c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp)
492*4c06356bSdh {
493*4c06356bSdh 	echo_test_t fred;
494*4c06356bSdh 	struct pmcwork *pwrk;
495*4c06356bSdh 	uint32_t *msg, count;
496*4c06356bSdh 	int iqe = 0, iqo = 0, result, rval = 0;
497*4c06356bSdh 	int iterations;
498*4c06356bSdh 	hrtime_t echo_start, echo_end, echo_total;
499*4c06356bSdh 
500*4c06356bSdh 	ASSERT(pwp->max_cmd > 0);
501*4c06356bSdh 
502*4c06356bSdh 	/*
503*4c06356bSdh 	 * We want iterations to be max_cmd * 3 to ensure that we run the
504*4c06356bSdh 	 * echo test enough times to iterate through every inbound queue
505*4c06356bSdh 	 * at least twice.
506*4c06356bSdh 	 */
507*4c06356bSdh 	iterations = pwp->max_cmd * 3;
508*4c06356bSdh 
509*4c06356bSdh 	echo_total = 0;
510*4c06356bSdh 	count = 0;
511*4c06356bSdh 
512*4c06356bSdh 	while (count < iterations) {
513*4c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
514*4c06356bSdh 		if (pwrk == NULL) {
515*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
516*4c06356bSdh 			rval = -1;
517*4c06356bSdh 			break;
518*4c06356bSdh 		}
519*4c06356bSdh 
520*4c06356bSdh 		mutex_enter(&pwp->iqp_lock[iqe]);
521*4c06356bSdh 		msg = GET_IQ_ENTRY(pwp, iqe);
522*4c06356bSdh 		if (msg == NULL) {
523*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[iqe]);
524*4c06356bSdh 			pmcs_pwork(pwp, pwrk);
525*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
526*4c06356bSdh 			rval = -1;
527*4c06356bSdh 			break;
528*4c06356bSdh 		}
529*4c06356bSdh 
530*4c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
531*4c06356bSdh 
532*4c06356bSdh 		if (iqe == PMCS_IQ_OTHER) {
533*4c06356bSdh 			/* This is on the high priority queue */
534*4c06356bSdh 			msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
535*4c06356bSdh 		} else {
536*4c06356bSdh 			msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
537*4c06356bSdh 		}
538*4c06356bSdh 		msg[1] = LE_32(pwrk->htag);
539*4c06356bSdh 		fred.signature = 0xdeadbeef;
540*4c06356bSdh 		fred.count = count;
541*4c06356bSdh 		fred.ptr = &count;
542*4c06356bSdh 		(void) memcpy(&msg[2], &fred, sizeof (fred));
543*4c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
544*4c06356bSdh 
545*4c06356bSdh 		INC_IQ_ENTRY(pwp, iqe);
546*4c06356bSdh 
547*4c06356bSdh 		echo_start = gethrtime();
548*4c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__start,
549*4c06356bSdh 		    hrtime_t, echo_start, uint32_t, pwrk->htag);
550*4c06356bSdh 
551*4c06356bSdh 		if (++iqe == PMCS_NIQ) {
552*4c06356bSdh 			iqe = 0;
553*4c06356bSdh 		}
554*4c06356bSdh 		if (++iqo == PMCS_NOQ) {
555*4c06356bSdh 			iqo = 0;
556*4c06356bSdh 		}
557*4c06356bSdh 
558*4c06356bSdh 		WAIT_FOR(pwrk, 250, result);
559*4c06356bSdh 
560*4c06356bSdh 		echo_end = gethrtime();
561*4c06356bSdh 		DTRACE_PROBE2(pmcs__echo__test__wait__end,
562*4c06356bSdh 		    hrtime_t, echo_end, int, result);
563*4c06356bSdh 
564*4c06356bSdh 		echo_total += (echo_end - echo_start);
565*4c06356bSdh 
566*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
567*4c06356bSdh 		if (result) {
568*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
569*4c06356bSdh 			    "%s: command timed out on echo test #%d",
570*4c06356bSdh 			    __func__, count);
571*4c06356bSdh 			rval = -1;
572*4c06356bSdh 			break;
573*4c06356bSdh 		}
574*4c06356bSdh 	}
575*4c06356bSdh 
576*4c06356bSdh 	/*
577*4c06356bSdh 	 * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
578*4c06356bSdh 	 * remove the overhead of things like the delay in getting signaled
579*4c06356bSdh 	 * for completion.
580*4c06356bSdh 	 */
581*4c06356bSdh 	if (echo_total != 0) {
582*4c06356bSdh 		pwp->io_intr_coal.intr_latency =
583*4c06356bSdh 		    (echo_total / iterations) / 2;
584*4c06356bSdh 		pwp->io_intr_coal.intr_threshold =
585*4c06356bSdh 		    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
586*4c06356bSdh 		    pwp->io_intr_coal.intr_latency);
587*4c06356bSdh 	}
588*4c06356bSdh 
589*4c06356bSdh 	return (rval);
590*4c06356bSdh }
591*4c06356bSdh 
592*4c06356bSdh /*
593*4c06356bSdh  * Start the (real) phys
594*4c06356bSdh  */
595*4c06356bSdh int
596*4c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
597*4c06356bSdh {
598*4c06356bSdh 	int result;
599*4c06356bSdh 	uint32_t *msg;
600*4c06356bSdh 	struct pmcwork *pwrk;
601*4c06356bSdh 	pmcs_phy_t *pptr;
602*4c06356bSdh 	sas_identify_af_t sap;
603*4c06356bSdh 
604*4c06356bSdh 	mutex_enter(&pwp->lock);
605*4c06356bSdh 	pptr = pwp->root_phys + phynum;
606*4c06356bSdh 	if (pptr == NULL) {
607*4c06356bSdh 		mutex_exit(&pwp->lock);
608*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cannot find port %d",
609*4c06356bSdh 		    __func__, phynum);
610*4c06356bSdh 		return (0);
611*4c06356bSdh 	}
612*4c06356bSdh 
613*4c06356bSdh 	pmcs_lock_phy(pptr);
614*4c06356bSdh 	mutex_exit(&pwp->lock);
615*4c06356bSdh 
616*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
617*4c06356bSdh 	if (pwrk == NULL) {
618*4c06356bSdh 		pmcs_unlock_phy(pptr);
619*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
620*4c06356bSdh 		return (-1);
621*4c06356bSdh 	}
622*4c06356bSdh 
623*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
624*4c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
625*4c06356bSdh 
626*4c06356bSdh 	if (msg == NULL) {
627*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
628*4c06356bSdh 		pmcs_unlock_phy(pptr);
629*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
630*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
631*4c06356bSdh 		return (-1);
632*4c06356bSdh 	}
633*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
634*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
635*4c06356bSdh 	msg[2] = LE_32(linkmode | speed | phynum);
636*4c06356bSdh 	bzero(&sap, sizeof (sap));
637*4c06356bSdh 	sap.device_type = SAS_IF_DTYPE_ENDPOINT;
638*4c06356bSdh 	sap.ssp_ini_port = 1;
639*4c06356bSdh 
640*4c06356bSdh 	if (pwp->separate_ports) {
641*4c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
642*4c06356bSdh 	} else {
643*4c06356bSdh 		pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
644*4c06356bSdh 	}
645*4c06356bSdh 
646*4c06356bSdh 	ASSERT(phynum < SAS2_PHYNUM_MAX);
647*4c06356bSdh 	sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
648*4c06356bSdh 	(void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
649*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
650*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
651*4c06356bSdh 
652*4c06356bSdh 	pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
653*4c06356bSdh 	pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
654*4c06356bSdh 	pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
655*4c06356bSdh 	pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
656*4c06356bSdh 
657*4c06356bSdh 	pmcs_unlock_phy(pptr);
658*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
659*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
660*4c06356bSdh 
661*4c06356bSdh 	if (result) {
662*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
663*4c06356bSdh 	} else {
664*4c06356bSdh 		mutex_enter(&pwp->lock);
665*4c06356bSdh 		pwp->phys_started |= (1 << phynum);
666*4c06356bSdh 		mutex_exit(&pwp->lock);
667*4c06356bSdh 	}
668*4c06356bSdh 
669*4c06356bSdh 	return (0);
670*4c06356bSdh }
671*4c06356bSdh 
672*4c06356bSdh int
673*4c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp)
674*4c06356bSdh {
675*4c06356bSdh 	int i;
676*4c06356bSdh 
677*4c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
678*4c06356bSdh 		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
679*4c06356bSdh 			if (pmcs_start_phy(pwp, i,
680*4c06356bSdh 			    (pwp->phymode << PHY_MODE_SHIFT),
681*4c06356bSdh 			    pwp->physpeed << PHY_LINK_SHIFT)) {
682*4c06356bSdh 				return (-1);
683*4c06356bSdh 			}
684*4c06356bSdh 			if (pmcs_clear_diag_counters(pwp, i)) {
685*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed to "
686*4c06356bSdh 				    "reset counters on PHY (%d)", __func__, i);
687*4c06356bSdh 			}
688*4c06356bSdh 		}
689*4c06356bSdh 	}
690*4c06356bSdh 	return (0);
691*4c06356bSdh }
692*4c06356bSdh 
693*4c06356bSdh /*
694*4c06356bSdh  * Called with PHY locked
695*4c06356bSdh  */
696*4c06356bSdh int
697*4c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
698*4c06356bSdh {
699*4c06356bSdh 	uint32_t *msg;
700*4c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
701*4c06356bSdh 	const char *mbar;
702*4c06356bSdh 	uint32_t amt;
703*4c06356bSdh 	uint32_t pdevid;
704*4c06356bSdh 	uint32_t stsoff;
705*4c06356bSdh 	uint32_t status;
706*4c06356bSdh 	int result, level, phynum;
707*4c06356bSdh 	struct pmcwork *pwrk;
708*4c06356bSdh 	uint32_t htag;
709*4c06356bSdh 
710*4c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
711*4c06356bSdh 
712*4c06356bSdh 	bzero(iomb, PMCS_QENTRY_SIZE);
713*4c06356bSdh 	phynum = pptr->phynum;
714*4c06356bSdh 	level = pptr->level;
715*4c06356bSdh 	if (level > 0) {
716*4c06356bSdh 		pdevid = pptr->parent->device_id;
717*4c06356bSdh 	}
718*4c06356bSdh 
719*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
720*4c06356bSdh 
721*4c06356bSdh 	if (pwrk == NULL) {
722*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
723*4c06356bSdh 		return (ENOMEM);
724*4c06356bSdh 	}
725*4c06356bSdh 
726*4c06356bSdh 	pwrk->arg = iomb;
727*4c06356bSdh 
728*4c06356bSdh 	/*
729*4c06356bSdh 	 * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
730*4c06356bSdh 	 * function to do either a link reset or hard reset.  If level == 0,
731*4c06356bSdh 	 * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
732*4c06356bSdh 	 * root (local) PHY
733*4c06356bSdh 	 */
734*4c06356bSdh 	if (level) {
735*4c06356bSdh 		stsoff = 2;
736*4c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
737*4c06356bSdh 		    PMCIN_SMP_REQUEST));
738*4c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
739*4c06356bSdh 		iomb[2] = LE_32(pdevid);
740*4c06356bSdh 		iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
741*4c06356bSdh 		/*
742*4c06356bSdh 		 * Send SMP PHY CONTROL/HARD or LINK RESET
743*4c06356bSdh 		 */
744*4c06356bSdh 		iomb[4] = BE_32(0x40910000);
745*4c06356bSdh 		iomb[5] = 0;
746*4c06356bSdh 
747*4c06356bSdh 		if (type == PMCS_PHYOP_HARD_RESET) {
748*4c06356bSdh 			mbar = "SMP PHY CONTROL/HARD RESET";
749*4c06356bSdh 			iomb[6] = BE_32((phynum << 24) |
750*4c06356bSdh 			    (PMCS_PHYOP_HARD_RESET << 16));
751*4c06356bSdh 		} else {
752*4c06356bSdh 			mbar = "SMP PHY CONTROL/LINK RESET";
753*4c06356bSdh 			iomb[6] = BE_32((phynum << 24) |
754*4c06356bSdh 			    (PMCS_PHYOP_LINK_RESET << 16));
755*4c06356bSdh 		}
756*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
757*4c06356bSdh 		    "%s: sending %s to %s for phy 0x%x",
758*4c06356bSdh 		    __func__, mbar, pptr->parent->path, pptr->phynum);
759*4c06356bSdh 		amt = 7;
760*4c06356bSdh 	} else {
761*4c06356bSdh 		/*
762*4c06356bSdh 		 * Unlike most other Outbound messages, status for
763*4c06356bSdh 		 * a local phy operation is in DWORD 3.
764*4c06356bSdh 		 */
765*4c06356bSdh 		stsoff = 3;
766*4c06356bSdh 		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
767*4c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
768*4c06356bSdh 		iomb[1] = LE_32(pwrk->htag);
769*4c06356bSdh 		if (type == PMCS_PHYOP_LINK_RESET) {
770*4c06356bSdh 			mbar = "LOCAL PHY LINK RESET";
771*4c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
772*4c06356bSdh 		} else {
773*4c06356bSdh 			mbar = "LOCAL PHY HARD RESET";
774*4c06356bSdh 			iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
775*4c06356bSdh 		}
776*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
777*4c06356bSdh 		    "%s: sending %s to %s", __func__, mbar, pptr->path);
778*4c06356bSdh 		amt = 3;
779*4c06356bSdh 	}
780*4c06356bSdh 
781*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
782*4c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
783*4c06356bSdh 	if (msg == NULL) {
784*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
785*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
786*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
787*4c06356bSdh 		return (ENOMEM);
788*4c06356bSdh 	}
789*4c06356bSdh 	COPY_MESSAGE(msg, iomb, amt);
790*4c06356bSdh 	htag = pwrk->htag;
791*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
792*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
793*4c06356bSdh 
794*4c06356bSdh 	pmcs_unlock_phy(pptr);
795*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
796*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
797*4c06356bSdh 	pmcs_lock_phy(pptr);
798*4c06356bSdh 
799*4c06356bSdh 	if (result) {
800*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
801*4c06356bSdh 
802*4c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
803*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
804*4c06356bSdh 			    "%s: Unable to issue SMP abort for htag 0x%08x",
805*4c06356bSdh 			    __func__, htag);
806*4c06356bSdh 		} else {
807*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
808*4c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
809*4c06356bSdh 			    __func__, htag);
810*4c06356bSdh 		}
811*4c06356bSdh 		return (EIO);
812*4c06356bSdh 	}
813*4c06356bSdh 	status = LE_32(iomb[stsoff]);
814*4c06356bSdh 
815*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
816*4c06356bSdh 		char buf[32];
817*4c06356bSdh 		const char *es =  pmcs_status_str(status);
818*4c06356bSdh 		if (es == NULL) {
819*4c06356bSdh 			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
820*4c06356bSdh 			    status);
821*4c06356bSdh 			es = buf;
822*4c06356bSdh 		}
823*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
824*4c06356bSdh 		    "%s: %s action returned %s for %s", __func__, mbar, es,
825*4c06356bSdh 		    pptr->path);
826*4c06356bSdh 		return (EIO);
827*4c06356bSdh 	}
828*4c06356bSdh 
829*4c06356bSdh 	return (0);
830*4c06356bSdh }
831*4c06356bSdh 
832*4c06356bSdh /*
833*4c06356bSdh  * Stop the (real) phys.  No PHY or softstate locks are required as this only
834*4c06356bSdh  * happens during detach.
835*4c06356bSdh  */
836*4c06356bSdh void
837*4c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
838*4c06356bSdh {
839*4c06356bSdh 	int result;
840*4c06356bSdh 	pmcs_phy_t *pptr;
841*4c06356bSdh 	uint32_t *msg;
842*4c06356bSdh 	struct pmcwork *pwrk;
843*4c06356bSdh 
844*4c06356bSdh 	pptr =  pwp->root_phys + phynum;
845*4c06356bSdh 	if (pptr == NULL) {
846*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
847*4c06356bSdh 		    "%s: unable to find port %d", __func__, phynum);
848*4c06356bSdh 		return;
849*4c06356bSdh 	}
850*4c06356bSdh 
851*4c06356bSdh 	if (pwp->phys_started & (1 << phynum)) {
852*4c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
853*4c06356bSdh 
854*4c06356bSdh 		if (pwrk == NULL) {
855*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
856*4c06356bSdh 			return;
857*4c06356bSdh 		}
858*4c06356bSdh 
859*4c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
860*4c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
861*4c06356bSdh 
862*4c06356bSdh 		if (msg == NULL) {
863*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
864*4c06356bSdh 			pmcs_pwork(pwp, pwrk);
865*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
866*4c06356bSdh 			return;
867*4c06356bSdh 		}
868*4c06356bSdh 
869*4c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
870*4c06356bSdh 		msg[1] = LE_32(pwrk->htag);
871*4c06356bSdh 		msg[2] = LE_32(phynum);
872*4c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
873*4c06356bSdh 		/*
874*4c06356bSdh 		 * Make this unconfigured now.
875*4c06356bSdh 		 */
876*4c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
877*4c06356bSdh 		WAIT_FOR(pwrk, 1000, result);
878*4c06356bSdh 
879*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
880*4c06356bSdh 		if (result) {
881*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
882*4c06356bSdh 		}
883*4c06356bSdh 
884*4c06356bSdh 		pwp->phys_started &= ~(1 << phynum);
885*4c06356bSdh 	}
886*4c06356bSdh 
887*4c06356bSdh 	pptr->configured = 0;
888*4c06356bSdh }
889*4c06356bSdh 
890*4c06356bSdh /*
891*4c06356bSdh  * No locks should be required as this is only called during detach
892*4c06356bSdh  */
893*4c06356bSdh void
894*4c06356bSdh pmcs_stop_phys(pmcs_hw_t *pwp)
895*4c06356bSdh {
896*4c06356bSdh 	int i;
897*4c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
898*4c06356bSdh 		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
899*4c06356bSdh 			pmcs_stop_phy(pwp, i);
900*4c06356bSdh 		}
901*4c06356bSdh 	}
902*4c06356bSdh }
903*4c06356bSdh 
904*4c06356bSdh /*
905*4c06356bSdh  * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed.
906*4c06356bSdh  * 	ERR_CNT_RESET: return status of cmd
907*4c06356bSdh  *	DIAG_REPORT_GET: return value of the counter
908*4c06356bSdh  */
909*4c06356bSdh int
910*4c06356bSdh pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
911*4c06356bSdh     uint8_t phynum)
912*4c06356bSdh {
913*4c06356bSdh 	uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
914*4c06356bSdh 	int result;
915*4c06356bSdh 	struct pmcwork *pwrk;
916*4c06356bSdh 
917*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
918*4c06356bSdh 	if (pwrk == NULL) {
919*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
920*4c06356bSdh 		return (DDI_FAILURE);
921*4c06356bSdh 	}
922*4c06356bSdh 	pwrk->arg = msg;
923*4c06356bSdh 	htag = pwrk->htag;
924*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
925*4c06356bSdh 	msg[1] = LE_32(htag);
926*4c06356bSdh 	msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) |
927*4c06356bSdh 	    (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
928*4c06356bSdh 
929*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
930*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
931*4c06356bSdh 	if (ptr == NULL) {
932*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
933*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
934*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
935*4c06356bSdh 		return (DDI_FAILURE);
936*4c06356bSdh 	}
937*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
938*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
939*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
940*4c06356bSdh 
941*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
942*4c06356bSdh 
943*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
944*4c06356bSdh 
945*4c06356bSdh 	if (result) {
946*4c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
947*4c06356bSdh 		return (DDI_FAILURE);
948*4c06356bSdh 	}
949*4c06356bSdh 
950*4c06356bSdh 	status = LE_32(msg[3]);
951*4c06356bSdh 
952*4c06356bSdh 	/* Return for counter reset */
953*4c06356bSdh 	if (cmd == PMCS_ERR_CNT_RESET)
954*4c06356bSdh 		return (status);
955*4c06356bSdh 
956*4c06356bSdh 	/* Return for counter value */
957*4c06356bSdh 	if (status) {
958*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed, status (0x%x)",
959*4c06356bSdh 		    __func__, status);
960*4c06356bSdh 		return (DDI_FAILURE);
961*4c06356bSdh 	}
962*4c06356bSdh 	return (LE_32(msg[4]));
963*4c06356bSdh }
964*4c06356bSdh 
965*4c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */
966*4c06356bSdh int
967*4c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
968*4c06356bSdh {
969*4c06356bSdh 	return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
970*4c06356bSdh }
971*4c06356bSdh 
972*4c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */
973*4c06356bSdh int
974*4c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
975*4c06356bSdh {
976*4c06356bSdh 	uint32_t	cmd = PMCS_ERR_CNT_RESET;
977*4c06356bSdh 	uint32_t	cmd_desc;
978*4c06356bSdh 
979*4c06356bSdh 	cmd_desc = PMCS_INVALID_DWORD_CNT;
980*4c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
981*4c06356bSdh 		return (DDI_FAILURE);
982*4c06356bSdh 
983*4c06356bSdh 	cmd_desc = PMCS_DISPARITY_ERR_CNT;
984*4c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
985*4c06356bSdh 		return (DDI_FAILURE);
986*4c06356bSdh 
987*4c06356bSdh 	cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
988*4c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
989*4c06356bSdh 		return (DDI_FAILURE);
990*4c06356bSdh 
991*4c06356bSdh 	cmd_desc = PMCS_RESET_FAILED_CNT;
992*4c06356bSdh 	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
993*4c06356bSdh 		return (DDI_FAILURE);
994*4c06356bSdh 
995*4c06356bSdh 	return (DDI_SUCCESS);
996*4c06356bSdh }
997*4c06356bSdh 
998*4c06356bSdh /*
999*4c06356bSdh  * Get firmware timestamp
1000*4c06356bSdh  */
1001*4c06356bSdh int
1002*4c06356bSdh pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *ts)
1003*4c06356bSdh {
1004*4c06356bSdh 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
1005*4c06356bSdh 	int result;
1006*4c06356bSdh 	struct pmcwork *pwrk;
1007*4c06356bSdh 
1008*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
1009*4c06356bSdh 	if (pwrk == NULL) {
1010*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
1011*4c06356bSdh 		return (-1);
1012*4c06356bSdh 	}
1013*4c06356bSdh 	pwrk->arg = msg;
1014*4c06356bSdh 	htag = pwrk->htag;
1015*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
1016*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
1017*4c06356bSdh 
1018*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1019*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1020*4c06356bSdh 	if (ptr == NULL) {
1021*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1022*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
1023*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
1024*4c06356bSdh 		return (-1);
1025*4c06356bSdh 	}
1026*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 2);
1027*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
1028*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1029*4c06356bSdh 
1030*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
1031*4c06356bSdh 
1032*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
1033*4c06356bSdh 
1034*4c06356bSdh 	if (result) {
1035*4c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
1036*4c06356bSdh 		return (-1);
1037*4c06356bSdh 	}
1038*4c06356bSdh 	*ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
1039*4c06356bSdh 	return (0);
1040*4c06356bSdh }
1041*4c06356bSdh 
1042*4c06356bSdh /*
1043*4c06356bSdh  * Dump all pertinent registers
1044*4c06356bSdh  */
1045*4c06356bSdh 
1046*4c06356bSdh void
1047*4c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp)
1048*4c06356bSdh {
1049*4c06356bSdh 	int i;
1050*4c06356bSdh 	uint32_t val;
1051*4c06356bSdh 
1052*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump start",
1053*4c06356bSdh 	    ddi_get_instance(pwp->dip));
1054*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO,
1055*4c06356bSdh 	    "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
1056*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
1057*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
1058*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
1059*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH0: 0x%08x",
1060*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
1061*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH1: 0x%08x",
1062*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
1063*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH2: 0x%08x",
1064*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
1065*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH3: 0x%08x",
1066*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
1067*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
1068*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "IQ %d: CI %u PI %u",
1069*4c06356bSdh 		    i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
1070*4c06356bSdh 	}
1071*4c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
1072*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "OQ %d: CI %u PI %u",
1073*4c06356bSdh 		    i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
1074*4c06356bSdh 	}
1075*4c06356bSdh 	val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
1076*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO,
1077*4c06356bSdh 	    "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
1078*4c06356bSdh 	    val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
1079*4c06356bSdh 	    PMCS_HMI_ERR(val));
1080*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ0: 0x%08x",
1081*4c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
1082*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ1: 0x%08x",
1083*4c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
1084*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE MSGU TICK: 0x%08x",
1085*4c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
1086*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IOP TICK: 0x%08x",
1087*4c06356bSdh 	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
1088*4c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
1089*4c06356bSdh 		uint32_t rerrf, pinfo, started = 0, link = 0;
1090*4c06356bSdh 		pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
1091*4c06356bSdh 		if (pinfo & 1) {
1092*4c06356bSdh 			started = 1;
1093*4c06356bSdh 			link = pinfo & 2;
1094*4c06356bSdh 		}
1095*4c06356bSdh 		rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
1096*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO,
1097*4c06356bSdh 		    "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
1098*4c06356bSdh 		    i, started, link, rerrf);
1099*4c06356bSdh 	}
1100*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump end",
1101*4c06356bSdh 	    ddi_get_instance(pwp->dip));
1102*4c06356bSdh }
1103*4c06356bSdh 
1104*4c06356bSdh /*
1105*4c06356bSdh  * Handle SATA Abort and other error processing
1106*4c06356bSdh  */
1107*4c06356bSdh int
1108*4c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp)
1109*4c06356bSdh {
1110*4c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
1111*4c06356bSdh 	int r, level = 0;
1112*4c06356bSdh 
1113*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s", __func__);
1114*4c06356bSdh 
1115*4c06356bSdh 	mutex_enter(&pwp->lock);
1116*4c06356bSdh 	pptr = pwp->root_phys;
1117*4c06356bSdh 	mutex_exit(&pwp->lock);
1118*4c06356bSdh 
1119*4c06356bSdh 	while (pptr) {
1120*4c06356bSdh 		/*
1121*4c06356bSdh 		 * XXX: Need to make sure this doesn't happen
1122*4c06356bSdh 		 * XXX: when non-NCQ commands are running.
1123*4c06356bSdh 		 */
1124*4c06356bSdh 		pmcs_lock_phy(pptr);
1125*4c06356bSdh 		if (pptr->need_rl_ext) {
1126*4c06356bSdh 			ASSERT(pptr->dtype == SATA);
1127*4c06356bSdh 			if (pmcs_acquire_scratch(pwp, B_FALSE)) {
1128*4c06356bSdh 				goto next_phy;
1129*4c06356bSdh 			}
1130*4c06356bSdh 			r = pmcs_sata_abort_ncq(pwp, pptr);
1131*4c06356bSdh 			pmcs_release_scratch(pwp);
1132*4c06356bSdh 			if (r == ENOMEM) {
1133*4c06356bSdh 				goto next_phy;
1134*4c06356bSdh 			}
1135*4c06356bSdh 			if (r) {
1136*4c06356bSdh 				r = pmcs_reset_phy(pwp, pptr,
1137*4c06356bSdh 				    PMCS_PHYOP_LINK_RESET);
1138*4c06356bSdh 				if (r == ENOMEM) {
1139*4c06356bSdh 					goto next_phy;
1140*4c06356bSdh 				}
1141*4c06356bSdh 				/* what if other failures happened? */
1142*4c06356bSdh 				pptr->abort_pending = 1;
1143*4c06356bSdh 				pptr->abort_sent = 0;
1144*4c06356bSdh 			}
1145*4c06356bSdh 		}
1146*4c06356bSdh 		if (pptr->abort_pending == 0 || pptr->abort_sent) {
1147*4c06356bSdh 			goto next_phy;
1148*4c06356bSdh 		}
1149*4c06356bSdh 		pptr->abort_pending = 0;
1150*4c06356bSdh 		if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
1151*4c06356bSdh 			pptr->abort_pending = 1;
1152*4c06356bSdh 			goto next_phy;
1153*4c06356bSdh 		}
1154*4c06356bSdh 		pptr->abort_sent = 1;
1155*4c06356bSdh 
1156*4c06356bSdh next_phy:
1157*4c06356bSdh 		if (pptr->children) {
1158*4c06356bSdh 			pnext = pptr->children;
1159*4c06356bSdh 			pnext_uplevel[level++] = pptr->sibling;
1160*4c06356bSdh 		} else {
1161*4c06356bSdh 			pnext = pptr->sibling;
1162*4c06356bSdh 			while ((pnext == NULL) && (level > 0)) {
1163*4c06356bSdh 				pnext = pnext_uplevel[--level];
1164*4c06356bSdh 			}
1165*4c06356bSdh 		}
1166*4c06356bSdh 
1167*4c06356bSdh 		pmcs_unlock_phy(pptr);
1168*4c06356bSdh 		pptr = pnext;
1169*4c06356bSdh 	}
1170*4c06356bSdh 
1171*4c06356bSdh 	return (0);
1172*4c06356bSdh }
1173*4c06356bSdh 
1174*4c06356bSdh /*
1175*4c06356bSdh  * Register a device (get a device handle for it).
1176*4c06356bSdh  * Called with PHY lock held.
1177*4c06356bSdh  */
1178*4c06356bSdh int
1179*4c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
1180*4c06356bSdh {
1181*4c06356bSdh 	struct pmcwork *pwrk;
1182*4c06356bSdh 	int result = 0;
1183*4c06356bSdh 	uint32_t *msg;
1184*4c06356bSdh 	uint32_t tmp, status;
1185*4c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
1186*4c06356bSdh 
1187*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1188*4c06356bSdh 	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1189*4c06356bSdh 
1190*4c06356bSdh 	if (msg == NULL ||
1191*4c06356bSdh 	    (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
1192*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1193*4c06356bSdh 		result = ENOMEM;
1194*4c06356bSdh 		goto out;
1195*4c06356bSdh 	}
1196*4c06356bSdh 
1197*4c06356bSdh 	pwrk->arg = iomb;
1198*4c06356bSdh 	pwrk->dtype = pptr->dtype;
1199*4c06356bSdh 
1200*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
1201*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
1202*4c06356bSdh 	tmp = PMCS_DEVREG_TLR |
1203*4c06356bSdh 	    (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
1204*4c06356bSdh 	if (IS_ROOT_PHY(pptr)) {
1205*4c06356bSdh 		msg[2] = LE_32(pptr->portid |
1206*4c06356bSdh 		    (pptr->phynum << PMCS_PHYID_SHIFT));
1207*4c06356bSdh 	} else {
1208*4c06356bSdh 		msg[2] = LE_32(pptr->portid);
1209*4c06356bSdh 	}
1210*4c06356bSdh 	if (pptr->dtype == SATA) {
1211*4c06356bSdh 		if (IS_ROOT_PHY(pptr)) {
1212*4c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
1213*4c06356bSdh 		} else {
1214*4c06356bSdh 			tmp |= PMCS_DEVREG_TYPE_SATA;
1215*4c06356bSdh 		}
1216*4c06356bSdh 	} else {
1217*4c06356bSdh 		tmp |= PMCS_DEVREG_TYPE_SAS;
1218*4c06356bSdh 	}
1219*4c06356bSdh 	msg[3] = LE_32(tmp);
1220*4c06356bSdh 	msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
1221*4c06356bSdh 	(void) memcpy(&msg[5], pptr->sas_address, 8);
1222*4c06356bSdh 
1223*4c06356bSdh 	CLEAN_MESSAGE(msg, 7);
1224*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
1225*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1226*4c06356bSdh 
1227*4c06356bSdh 	pmcs_unlock_phy(pptr);
1228*4c06356bSdh 	WAIT_FOR(pwrk, 250, result);
1229*4c06356bSdh 	pmcs_lock_phy(pptr);
1230*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
1231*4c06356bSdh 
1232*4c06356bSdh 	if (result) {
1233*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
1234*4c06356bSdh 		result = ETIMEDOUT;
1235*4c06356bSdh 		goto out;
1236*4c06356bSdh 	}
1237*4c06356bSdh 	status = LE_32(iomb[2]);
1238*4c06356bSdh 	tmp = LE_32(iomb[3]);
1239*4c06356bSdh 	switch (status) {
1240*4c06356bSdh 	case PMCS_DEVREG_OK:
1241*4c06356bSdh 	case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
1242*4c06356bSdh 	case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
1243*4c06356bSdh 		if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
1244*4c06356bSdh 			result = EEXIST;
1245*4c06356bSdh 			goto out;
1246*4c06356bSdh 		} else if (status != PMCS_DEVREG_OK) {
1247*4c06356bSdh 			if (tmp == 0xffffffff) {	/* F/W bug */
1248*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_INFO,
1249*4c06356bSdh 				    "%s: phy %s already has bogus devid 0x%x",
1250*4c06356bSdh 				    __func__, pptr->path, tmp);
1251*4c06356bSdh 				result = EIO;
1252*4c06356bSdh 				goto out;
1253*4c06356bSdh 			} else {
1254*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_INFO,
1255*4c06356bSdh 				    "%s: phy %s already has a device id 0x%x",
1256*4c06356bSdh 				    __func__, pptr->path, tmp);
1257*4c06356bSdh 			}
1258*4c06356bSdh 		}
1259*4c06356bSdh 		break;
1260*4c06356bSdh 	default:
1261*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to "
1262*4c06356bSdh 		    "register device %s", __func__, status, pptr->path);
1263*4c06356bSdh 		result = EIO;
1264*4c06356bSdh 		goto out;
1265*4c06356bSdh 	}
1266*4c06356bSdh 	pptr->device_id = tmp;
1267*4c06356bSdh 	pptr->valid_device_id = 1;
1268*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Phy %s/" SAS_ADDR_FMT
1269*4c06356bSdh 	    " registered with device_id 0x%x (portid %d)", pptr->path,
1270*4c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
1271*4c06356bSdh out:
1272*4c06356bSdh 	return (result);
1273*4c06356bSdh }
1274*4c06356bSdh 
1275*4c06356bSdh /*
1276*4c06356bSdh  * Deregister a device (remove a device handle).
1277*4c06356bSdh  * Called with PHY locked.
1278*4c06356bSdh  */
1279*4c06356bSdh void
1280*4c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
1281*4c06356bSdh {
1282*4c06356bSdh 	struct pmcwork *pwrk;
1283*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
1284*4c06356bSdh 	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
1285*4c06356bSdh 	int result;
1286*4c06356bSdh 
1287*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
1288*4c06356bSdh 	if (pwrk == NULL) {
1289*4c06356bSdh 		return;
1290*4c06356bSdh 	}
1291*4c06356bSdh 
1292*4c06356bSdh 	pwrk->arg = iomb;
1293*4c06356bSdh 	pwrk->dtype = pptr->dtype;
1294*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1295*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1296*4c06356bSdh 	if (ptr == NULL) {
1297*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
1298*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
1299*4c06356bSdh 		return;
1300*4c06356bSdh 	}
1301*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
1302*4c06356bSdh 	    PMCIN_DEREGISTER_DEVICE_HANDLE));
1303*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
1304*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
1305*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
1306*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
1307*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
1308*4c06356bSdh 
1309*4c06356bSdh 	pmcs_unlock_phy(pptr);
1310*4c06356bSdh 	WAIT_FOR(pwrk, 250, result);
1311*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
1312*4c06356bSdh 	pmcs_lock_phy(pptr);
1313*4c06356bSdh 
1314*4c06356bSdh 	if (result) {
1315*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
1316*4c06356bSdh 		return;
1317*4c06356bSdh 	}
1318*4c06356bSdh 	status = LE_32(iomb[2]);
1319*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
1320*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to "
1321*4c06356bSdh 		    "deregister device %s", __func__, status, pptr->path);
1322*4c06356bSdh 	} else {
1323*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: device %s deregistered",
1324*4c06356bSdh 		    __func__, pptr->path);
1325*4c06356bSdh 		pptr->valid_device_id = 0;
1326*4c06356bSdh 		pptr->device_id = PMCS_INVALID_DEVICE_ID;
1327*4c06356bSdh 	}
1328*4c06356bSdh }
1329*4c06356bSdh 
1330*4c06356bSdh /*
1331*4c06356bSdh  * Deregister all registered devices.
1332*4c06356bSdh  */
1333*4c06356bSdh void
1334*4c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
1335*4c06356bSdh {
1336*4c06356bSdh 	/*
1337*4c06356bSdh 	 * Start at the maximum level and walk back to level 0.  This only
1338*4c06356bSdh 	 * gets done during detach after all threads and timers have been
1339*4c06356bSdh 	 * destroyed, so there's no need to hold the softstate or PHY lock.
1340*4c06356bSdh 	 */
1341*4c06356bSdh 	while (phyp) {
1342*4c06356bSdh 		if (phyp->children) {
1343*4c06356bSdh 			pmcs_deregister_devices(pwp, phyp->children);
1344*4c06356bSdh 		}
1345*4c06356bSdh 		if (phyp->valid_device_id) {
1346*4c06356bSdh 			pmcs_deregister_device(pwp, phyp);
1347*4c06356bSdh 		}
1348*4c06356bSdh 		phyp = phyp->sibling;
1349*4c06356bSdh 	}
1350*4c06356bSdh }
1351*4c06356bSdh 
1352*4c06356bSdh /*
1353*4c06356bSdh  * Perform a 'soft' reset on the PMC chip
1354*4c06356bSdh  */
1355*4c06356bSdh int
1356*4c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
1357*4c06356bSdh {
1358*4c06356bSdh 	uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
1359*4c06356bSdh 	pmcs_phy_t *pptr;
1360*4c06356bSdh 	char *msg = NULL;
1361*4c06356bSdh 	int i;
1362*4c06356bSdh 
1363*4c06356bSdh 	/*
1364*4c06356bSdh 	 * Disable interrupts
1365*4c06356bSdh 	 */
1366*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
1367*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
1368*4c06356bSdh 
1369*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "%s", __func__);
1370*4c06356bSdh 
1371*4c06356bSdh 	if (pwp->locks_initted) {
1372*4c06356bSdh 		mutex_enter(&pwp->lock);
1373*4c06356bSdh 	}
1374*4c06356bSdh 	pwp->blocked = 1;
1375*4c06356bSdh 
1376*4c06356bSdh 	/*
1377*4c06356bSdh 	 * Step 1
1378*4c06356bSdh 	 */
1379*4c06356bSdh 	s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
1380*4c06356bSdh 	if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
1381*4c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
1382*4c06356bSdh 		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
1383*4c06356bSdh 		for (i = 0; i < 100; i++) {
1384*4c06356bSdh 			s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
1385*4c06356bSdh 			    PMCS_MSGU_HOST_SOFT_RESET_READY;
1386*4c06356bSdh 			if (s2) {
1387*4c06356bSdh 				break;
1388*4c06356bSdh 			}
1389*4c06356bSdh 			drv_usecwait(10000);
1390*4c06356bSdh 		}
1391*4c06356bSdh 		s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
1392*4c06356bSdh 		    PMCS_MSGU_HOST_SOFT_RESET_READY;
1393*4c06356bSdh 		if (s2 == 0) {
1394*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: PMCS_MSGU_HOST_"
1395*4c06356bSdh 			    "SOFT_RESET_READY never came ready", __func__);
1396*4c06356bSdh 			pmcs_register_dump(pwp);
1397*4c06356bSdh 			if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1398*4c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
1399*4c06356bSdh 			    (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
1400*4c06356bSdh 			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
1401*4c06356bSdh 				pwp->state = STATE_DEAD;
1402*4c06356bSdh 				pwp->blocked = 0;
1403*4c06356bSdh 				if (pwp->locks_initted) {
1404*4c06356bSdh 					mutex_exit(&pwp->lock);
1405*4c06356bSdh 				}
1406*4c06356bSdh 				return (-1);
1407*4c06356bSdh 			}
1408*4c06356bSdh 		}
1409*4c06356bSdh 	}
1410*4c06356bSdh 
1411*4c06356bSdh 	/*
1412*4c06356bSdh 	 * Step 2
1413*4c06356bSdh 	 */
1414*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
1415*4c06356bSdh 	drv_usecwait(10);
1416*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
1417*4c06356bSdh 	drv_usecwait(10);
1418*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
1419*4c06356bSdh 	drv_usecwait(10);
1420*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
1421*4c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
1422*4c06356bSdh 	drv_usecwait(10);
1423*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
1424*4c06356bSdh 	drv_usecwait(10);
1425*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
1426*4c06356bSdh 	    pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
1427*4c06356bSdh 	drv_usecwait(10);
1428*4c06356bSdh 
1429*4c06356bSdh 	sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1430*4c06356bSdh 	    PMCS_MSGU_AAP_SFR_PROGRESS;
1431*4c06356bSdh 	sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
1432*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "PMCS_MSGU_HOST_SCRATCH0 %08x -> %08x",
1433*4c06356bSdh 	    pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0), HST_SFT_RESET_SIG);
1434*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
1435*4c06356bSdh 
1436*4c06356bSdh 	/*
1437*4c06356bSdh 	 * Step 3
1438*4c06356bSdh 	 */
1439*4c06356bSdh 	gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
1440*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm,
1441*4c06356bSdh 	    gsm & ~PMCS_SOFT_RESET_BITS);
1442*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
1443*4c06356bSdh 
1444*4c06356bSdh 	/*
1445*4c06356bSdh 	 * Step 4
1446*4c06356bSdh 	 */
1447*4c06356bSdh 	rapchk = pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN);
1448*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x",
1449*4c06356bSdh 	    rapchk, 0);
1450*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
1451*4c06356bSdh 	wapchk = pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN);
1452*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x",
1453*4c06356bSdh 	    wapchk, 0);
1454*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
1455*4c06356bSdh 	wdpchk = pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN);
1456*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x",
1457*4c06356bSdh 	    wdpchk, 0);
1458*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
1459*4c06356bSdh 
1460*4c06356bSdh 	/*
1461*4c06356bSdh 	 * Step 5
1462*4c06356bSdh 	 */
1463*4c06356bSdh 	drv_usecwait(100);
1464*4c06356bSdh 
1465*4c06356bSdh 	/*
1466*4c06356bSdh 	 * Step 5.5 (Temporary workaround for 1.07.xx Beta)
1467*4c06356bSdh 	 */
1468*4c06356bSdh 	tsmode = pmcs_rd_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR);
1469*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GPIO TSMODE %08x -> %08x", tsmode,
1470*4c06356bSdh 	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
1471*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
1472*4c06356bSdh 	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
1473*4c06356bSdh 	drv_usecwait(10);
1474*4c06356bSdh 
1475*4c06356bSdh 	/*
1476*4c06356bSdh 	 * Step 6
1477*4c06356bSdh 	 */
1478*4c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1479*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
1480*4c06356bSdh 	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
1481*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
1482*4c06356bSdh 	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
1483*4c06356bSdh 	drv_usecwait(10);
1484*4c06356bSdh 
1485*4c06356bSdh 	/*
1486*4c06356bSdh 	 * Step 7
1487*4c06356bSdh 	 */
1488*4c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1489*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
1490*4c06356bSdh 	    spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
1491*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
1492*4c06356bSdh 
1493*4c06356bSdh 	/*
1494*4c06356bSdh 	 * Step 8
1495*4c06356bSdh 	 */
1496*4c06356bSdh 	drv_usecwait(100);
1497*4c06356bSdh 
1498*4c06356bSdh 	/*
1499*4c06356bSdh 	 * Step 9
1500*4c06356bSdh 	 */
1501*4c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1502*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
1503*4c06356bSdh 	    spc | (BDMA_CORE_RSTB|OSSP_RSTB));
1504*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
1505*4c06356bSdh 
1506*4c06356bSdh 	/*
1507*4c06356bSdh 	 * Step 10
1508*4c06356bSdh 	 */
1509*4c06356bSdh 	drv_usecwait(100);
1510*4c06356bSdh 
1511*4c06356bSdh 	/*
1512*4c06356bSdh 	 * Step 11
1513*4c06356bSdh 	 */
1514*4c06356bSdh 	gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
1515*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm,
1516*4c06356bSdh 	    gsm | PMCS_SOFT_RESET_BITS);
1517*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
1518*4c06356bSdh 	drv_usecwait(10);
1519*4c06356bSdh 
1520*4c06356bSdh 	/*
1521*4c06356bSdh 	 * Step 12
1522*4c06356bSdh 	 */
1523*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x",
1524*4c06356bSdh 	    pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN), rapchk);
1525*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
1526*4c06356bSdh 	drv_usecwait(10);
1527*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x",
1528*4c06356bSdh 	    pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN), wapchk);
1529*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
1530*4c06356bSdh 	drv_usecwait(10);
1531*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x",
1532*4c06356bSdh 	    pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN), wapchk);
1533*4c06356bSdh 	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
1534*4c06356bSdh 	drv_usecwait(10);
1535*4c06356bSdh 
1536*4c06356bSdh 	/*
1537*4c06356bSdh 	 * Step 13
1538*4c06356bSdh 	 */
1539*4c06356bSdh 	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
1540*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
1541*4c06356bSdh 	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
1542*4c06356bSdh 	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
1543*4c06356bSdh 	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
1544*4c06356bSdh 
1545*4c06356bSdh 	/*
1546*4c06356bSdh 	 * Step 14
1547*4c06356bSdh 	 */
1548*4c06356bSdh 	drv_usecwait(100);
1549*4c06356bSdh 
1550*4c06356bSdh 	/*
1551*4c06356bSdh 	 * Step 15
1552*4c06356bSdh 	 */
1553*4c06356bSdh 	for (spc = 0, i = 0; i < 1000; i++) {
1554*4c06356bSdh 		drv_usecwait(1000);
1555*4c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
1556*4c06356bSdh 		if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
1557*4c06356bSdh 			break;
1558*4c06356bSdh 		}
1559*4c06356bSdh 	}
1560*4c06356bSdh 
1561*4c06356bSdh 	if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
1562*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1563*4c06356bSdh 		    "SFR didn't toggle (sfr 0x%x)", spc);
1564*4c06356bSdh 		pwp->state = STATE_DEAD;
1565*4c06356bSdh 		pwp->blocked = 0;
1566*4c06356bSdh 		if (pwp->locks_initted) {
1567*4c06356bSdh 			mutex_exit(&pwp->lock);
1568*4c06356bSdh 		}
1569*4c06356bSdh 		return (-1);
1570*4c06356bSdh 	}
1571*4c06356bSdh 
1572*4c06356bSdh 	/*
1573*4c06356bSdh 	 * Step 16
1574*4c06356bSdh 	 */
1575*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
1576*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
1577*4c06356bSdh 
1578*4c06356bSdh 	/*
1579*4c06356bSdh 	 * Wait for up to 5 seconds for AAP state to come either ready or error.
1580*4c06356bSdh 	 */
1581*4c06356bSdh 	for (i = 0; i < 50; i++) {
1582*4c06356bSdh 		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1583*4c06356bSdh 		    PMCS_MSGU_AAP_STATE_MASK;
1584*4c06356bSdh 		if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
1585*4c06356bSdh 		    spc == PMCS_MSGU_AAP_STATE_READY) {
1586*4c06356bSdh 			break;
1587*4c06356bSdh 		}
1588*4c06356bSdh 		drv_usecwait(100000);
1589*4c06356bSdh 	}
1590*4c06356bSdh 	spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
1591*4c06356bSdh 	if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
1592*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1593*4c06356bSdh 		    "soft reset failed (state 0x%x)", spc);
1594*4c06356bSdh 		pwp->state = STATE_DEAD;
1595*4c06356bSdh 		pwp->blocked = 0;
1596*4c06356bSdh 		if (pwp->locks_initted) {
1597*4c06356bSdh 			mutex_exit(&pwp->lock);
1598*4c06356bSdh 		}
1599*4c06356bSdh 		return (-1);
1600*4c06356bSdh 	}
1601*4c06356bSdh 
1602*4c06356bSdh 
1603*4c06356bSdh 	if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
1604*4c06356bSdh 	    pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
1605*4c06356bSdh 		pwp->blocked = 0;
1606*4c06356bSdh 		if (pwp->locks_initted) {
1607*4c06356bSdh 			mutex_exit(&pwp->lock);
1608*4c06356bSdh 		}
1609*4c06356bSdh 		return (0);
1610*4c06356bSdh 	}
1611*4c06356bSdh 
1612*4c06356bSdh 	/*
1613*4c06356bSdh 	 * Return at this point if we dont need to startup.
1614*4c06356bSdh 	 */
1615*4c06356bSdh 	if (no_restart) {
1616*4c06356bSdh 		return (0);
1617*4c06356bSdh 	}
1618*4c06356bSdh 
1619*4c06356bSdh 	ASSERT(pwp->locks_initted != 0);
1620*4c06356bSdh 
1621*4c06356bSdh 	/*
1622*4c06356bSdh 	 * Clean up various soft state.
1623*4c06356bSdh 	 */
1624*4c06356bSdh 	bzero(pwp->ports, sizeof (pwp->ports));
1625*4c06356bSdh 
1626*4c06356bSdh 	pmcs_free_all_phys(pwp, pwp->root_phys);
1627*4c06356bSdh 
1628*4c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
1629*4c06356bSdh 		pmcs_lock_phy(pptr);
1630*4c06356bSdh 		pmcs_clear_phy(pwp, pptr);
1631*4c06356bSdh 		pmcs_unlock_phy(pptr);
1632*4c06356bSdh 	}
1633*4c06356bSdh 
1634*4c06356bSdh 	if (pwp->targets) {
1635*4c06356bSdh 		for (i = 0; i < pwp->max_dev; i++) {
1636*4c06356bSdh 			pmcs_xscsi_t *xp = pwp->targets[i];
1637*4c06356bSdh 
1638*4c06356bSdh 			if (xp == NULL) {
1639*4c06356bSdh 				continue;
1640*4c06356bSdh 			}
1641*4c06356bSdh 			mutex_enter(&xp->statlock);
1642*4c06356bSdh 			if (xp->assigned == 0 && xp->dying == 0) {
1643*4c06356bSdh 				if (xp->new) {
1644*4c06356bSdh 					xp->new = 0;
1645*4c06356bSdh 					xp->ca = 0;
1646*4c06356bSdh 					xp->qdepth = 0;
1647*4c06356bSdh 					xp->phy = NULL;
1648*4c06356bSdh 				}
1649*4c06356bSdh 				mutex_exit(&xp->statlock);
1650*4c06356bSdh 				continue;
1651*4c06356bSdh 			}
1652*4c06356bSdh 			xp->tagmap = 0;
1653*4c06356bSdh 			xp->dying = 1;
1654*4c06356bSdh 			xp->assigned = 0;
1655*4c06356bSdh 			mutex_exit(&xp->statlock);
1656*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_REM_DEVICES);
1657*4c06356bSdh 		}
1658*4c06356bSdh 	}
1659*4c06356bSdh 
1660*4c06356bSdh 	bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
1661*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
1662*4c06356bSdh 		if (pwp->iqp[i]) {
1663*4c06356bSdh 			bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
1664*4c06356bSdh 			pmcs_wr_iqpi(pwp, i, 0);
1665*4c06356bSdh 			pmcs_wr_iqci(pwp, i, 0);
1666*4c06356bSdh 		}
1667*4c06356bSdh 	}
1668*4c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
1669*4c06356bSdh 		if (pwp->oqp[i]) {
1670*4c06356bSdh 			bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
1671*4c06356bSdh 			pmcs_wr_oqpi(pwp, i, 0);
1672*4c06356bSdh 			pmcs_wr_oqci(pwp, i, 0);
1673*4c06356bSdh 		}
1674*4c06356bSdh 
1675*4c06356bSdh 	}
1676*4c06356bSdh 	if (pwp->fwlogp) {
1677*4c06356bSdh 		bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
1678*4c06356bSdh 	}
1679*4c06356bSdh 	STAILQ_INIT(&pwp->wf);
1680*4c06356bSdh 	bzero(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
1681*4c06356bSdh 	for (i = 0; i < pwp->max_cmd - 1; i++) {
1682*4c06356bSdh 		pmcwork_t *pwrk = &pwp->work[i];
1683*4c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
1684*4c06356bSdh 	}
1685*4c06356bSdh 
1686*4c06356bSdh 	/*
1687*4c06356bSdh 	 * Clear out any leftover commands sitting in the work list
1688*4c06356bSdh 	 */
1689*4c06356bSdh 	for (i = 0; i < pwp->max_cmd; i++) {
1690*4c06356bSdh 		pmcwork_t *pwrk = &pwp->work[i];
1691*4c06356bSdh 		mutex_enter(&pwrk->lock);
1692*4c06356bSdh 		if (pwrk->state == PMCS_WORK_STATE_ONCHIP) {
1693*4c06356bSdh 			switch (PMCS_TAG_TYPE(pwrk->htag)) {
1694*4c06356bSdh 			case PMCS_TAG_TYPE_WAIT:
1695*4c06356bSdh 				mutex_exit(&pwrk->lock);
1696*4c06356bSdh 				break;
1697*4c06356bSdh 			case PMCS_TAG_TYPE_CBACK:
1698*4c06356bSdh 			case PMCS_TAG_TYPE_NONE:
1699*4c06356bSdh 				pmcs_pwork(pwp, pwrk);
1700*4c06356bSdh 				break;
1701*4c06356bSdh 			default:
1702*4c06356bSdh 				break;
1703*4c06356bSdh 			}
1704*4c06356bSdh 		} else if (pwrk->state == PMCS_WORK_STATE_IOCOMPQ) {
1705*4c06356bSdh 			pwrk->dead = 1;
1706*4c06356bSdh 			mutex_exit(&pwrk->lock);
1707*4c06356bSdh 		} else {
1708*4c06356bSdh 			/*
1709*4c06356bSdh 			 * The other states of NIL, READY and INTR
1710*4c06356bSdh 			 * should not be visible outside of a lock being held.
1711*4c06356bSdh 			 */
1712*4c06356bSdh 			pmcs_pwork(pwp, pwrk);
1713*4c06356bSdh 		}
1714*4c06356bSdh 	}
1715*4c06356bSdh 
1716*4c06356bSdh 	/*
1717*4c06356bSdh 	 * Restore Interrupt Mask
1718*4c06356bSdh 	 */
1719*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
1720*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
1721*4c06356bSdh 
1722*4c06356bSdh 	pwp->blocked = 0;
1723*4c06356bSdh 	pwp->mpi_table_setup = 0;
1724*4c06356bSdh 	mutex_exit(&pwp->lock);
1725*4c06356bSdh 
1726*4c06356bSdh 	/*
1727*4c06356bSdh 	 * Set up MPI again.
1728*4c06356bSdh 	 */
1729*4c06356bSdh 	if (pmcs_setup(pwp)) {
1730*4c06356bSdh 		msg = "unable to setup MPI tables again";
1731*4c06356bSdh 		goto fail_restart;
1732*4c06356bSdh 	}
1733*4c06356bSdh 	pmcs_report_fwversion(pwp);
1734*4c06356bSdh 
1735*4c06356bSdh 	/*
1736*4c06356bSdh 	 * Restart MPI
1737*4c06356bSdh 	 */
1738*4c06356bSdh 	if (pmcs_start_mpi(pwp)) {
1739*4c06356bSdh 		msg = "unable to restart MPI again";
1740*4c06356bSdh 		goto fail_restart;
1741*4c06356bSdh 	}
1742*4c06356bSdh 
1743*4c06356bSdh 	mutex_enter(&pwp->lock);
1744*4c06356bSdh 	pwp->blocked = 0;
1745*4c06356bSdh 	SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1746*4c06356bSdh 	mutex_exit(&pwp->lock);
1747*4c06356bSdh 
1748*4c06356bSdh 	/*
1749*4c06356bSdh 	 * Run any completions
1750*4c06356bSdh 	 */
1751*4c06356bSdh 	PMCS_CQ_RUN(pwp);
1752*4c06356bSdh 
1753*4c06356bSdh 	/*
1754*4c06356bSdh 	 * Delay
1755*4c06356bSdh 	 */
1756*4c06356bSdh 	drv_usecwait(1000000);
1757*4c06356bSdh 	return (0);
1758*4c06356bSdh 
1759*4c06356bSdh fail_restart:
1760*4c06356bSdh 	mutex_enter(&pwp->lock);
1761*4c06356bSdh 	pwp->state = STATE_DEAD;
1762*4c06356bSdh 	mutex_exit(&pwp->lock);
1763*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_ERR, "%s: Failed: %s", __func__, msg);
1764*4c06356bSdh 	return (-1);
1765*4c06356bSdh }
1766*4c06356bSdh 
1767*4c06356bSdh /*
1768*4c06356bSdh  * Reset a device or a logical unit.
1769*4c06356bSdh  */
1770*4c06356bSdh int
1771*4c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
1772*4c06356bSdh {
1773*4c06356bSdh 	int rval = 0;
1774*4c06356bSdh 
1775*4c06356bSdh 	if (pptr == NULL) {
1776*4c06356bSdh 		return (ENXIO);
1777*4c06356bSdh 	}
1778*4c06356bSdh 
1779*4c06356bSdh 	pmcs_lock_phy(pptr);
1780*4c06356bSdh 	if (pptr->dtype == SAS) {
1781*4c06356bSdh 		/*
1782*4c06356bSdh 		 * Some devices do not support SAS_I_T_NEXUS_RESET as
1783*4c06356bSdh 		 * it is not a mandatory (in SAM4) task management
1784*4c06356bSdh 		 * function, while LOGIC_UNIT_RESET is mandatory.
1785*4c06356bSdh 		 *
1786*4c06356bSdh 		 * The problem here is that we need to iterate over
1787*4c06356bSdh 		 * all known LUNs to emulate the semantics of
1788*4c06356bSdh 		 * "RESET_TARGET".
1789*4c06356bSdh 		 *
1790*4c06356bSdh 		 * XXX: FIX ME
1791*4c06356bSdh 		 */
1792*4c06356bSdh 		if (lun == (uint64_t)-1) {
1793*4c06356bSdh 			lun = 0;
1794*4c06356bSdh 		}
1795*4c06356bSdh 		rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
1796*4c06356bSdh 		    NULL);
1797*4c06356bSdh 	} else if (pptr->dtype == SATA) {
1798*4c06356bSdh 		if (lun != 0ull) {
1799*4c06356bSdh 			pmcs_unlock_phy(pptr);
1800*4c06356bSdh 			return (EINVAL);
1801*4c06356bSdh 		}
1802*4c06356bSdh 		rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
1803*4c06356bSdh 	} else {
1804*4c06356bSdh 		pmcs_unlock_phy(pptr);
1805*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1806*4c06356bSdh 		    "%s: cannot reset a SMP device yet (%s)",
1807*4c06356bSdh 		    __func__, pptr->path);
1808*4c06356bSdh 		return (EINVAL);
1809*4c06356bSdh 	}
1810*4c06356bSdh 
1811*4c06356bSdh 	/*
1812*4c06356bSdh 	 * Now harvest any commands killed by this action
1813*4c06356bSdh 	 * by issuing an ABORT for all commands on this device.
1814*4c06356bSdh 	 *
1815*4c06356bSdh 	 * We do this even if the the tmf or reset fails (in case there
1816*4c06356bSdh 	 * are any dead commands around to be harvested *anyway*).
1817*4c06356bSdh 	 * We don't have to await for the abort to complete.
1818*4c06356bSdh 	 */
1819*4c06356bSdh 	if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
1820*4c06356bSdh 		pptr->abort_pending = 1;
1821*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
1822*4c06356bSdh 	}
1823*4c06356bSdh 
1824*4c06356bSdh 	pmcs_unlock_phy(pptr);
1825*4c06356bSdh 	return (rval);
1826*4c06356bSdh }
1827*4c06356bSdh 
1828*4c06356bSdh /*
1829*4c06356bSdh  * Called with PHY locked.
1830*4c06356bSdh  */
1831*4c06356bSdh static int
1832*4c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
1833*4c06356bSdh {
1834*4c06356bSdh 	if (pptr->valid_device_id == 0) {
1835*4c06356bSdh 		int result = pmcs_register_device(pwp, pptr);
1836*4c06356bSdh 
1837*4c06356bSdh 		/*
1838*4c06356bSdh 		 * If we changed while registering, punt
1839*4c06356bSdh 		 */
1840*4c06356bSdh 		if (pptr->changed) {
1841*4c06356bSdh 			RESTART_DISCOVERY(pwp);
1842*4c06356bSdh 			return (-1);
1843*4c06356bSdh 		}
1844*4c06356bSdh 
1845*4c06356bSdh 		/*
1846*4c06356bSdh 		 * If we had a failure to register, check against errors.
1847*4c06356bSdh 		 * An ENOMEM error means we just retry (temp resource shortage).
1848*4c06356bSdh 		 */
1849*4c06356bSdh 		if (result == ENOMEM) {
1850*4c06356bSdh 			PHY_CHANGED(pwp, pptr);
1851*4c06356bSdh 			RESTART_DISCOVERY(pwp);
1852*4c06356bSdh 			return (-1);
1853*4c06356bSdh 		}
1854*4c06356bSdh 
1855*4c06356bSdh 		/*
1856*4c06356bSdh 		 * An ETIMEDOUT error means we retry (if our counter isn't
1857*4c06356bSdh 		 * exhausted)
1858*4c06356bSdh 		 */
1859*4c06356bSdh 		if (result == ETIMEDOUT) {
1860*4c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
1861*4c06356bSdh 				PHY_CHANGED(pwp, pptr);
1862*4c06356bSdh 				RESTART_DISCOVERY(pwp);
1863*4c06356bSdh 			} else {
1864*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
1865*4c06356bSdh 				    "%s: Retries exhausted for %s, killing",
1866*4c06356bSdh 				    __func__, pptr->path);
1867*4c06356bSdh 				pptr->config_stop = 0;
1868*4c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
1869*4c06356bSdh 			}
1870*4c06356bSdh 			return (-1);
1871*4c06356bSdh 		}
1872*4c06356bSdh 		/*
1873*4c06356bSdh 		 * Other errors or no valid device id is fatal, but don't
1874*4c06356bSdh 		 * preclude a future action.
1875*4c06356bSdh 		 */
1876*4c06356bSdh 		if (result || pptr->valid_device_id == 0) {
1877*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s could not "
1878*4c06356bSdh 			    "be registered", __func__,  pptr->path);
1879*4c06356bSdh 			return (-1);
1880*4c06356bSdh 		}
1881*4c06356bSdh 	}
1882*4c06356bSdh 	return (0);
1883*4c06356bSdh }
1884*4c06356bSdh 
1885*4c06356bSdh int
1886*4c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport)
1887*4c06356bSdh {
1888*4c06356bSdh 	ASSERT(iport);
1889*4c06356bSdh 	if (iport == NULL)
1890*4c06356bSdh 		return (B_FALSE);
1891*4c06356bSdh 
1892*4c06356bSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__);
1893*4c06356bSdh 
1894*4c06356bSdh 	/* create target map */
1895*4c06356bSdh 	if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec,
1896*4c06356bSdh 	    2048, NULL, NULL, NULL, &iport->iss_tgtmap) != DDI_SUCCESS) {
1897*4c06356bSdh 		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG,
1898*4c06356bSdh 		    "%s: failed to create tgtmap", __func__);
1899*4c06356bSdh 		return (B_FALSE);
1900*4c06356bSdh 	}
1901*4c06356bSdh 	return (B_TRUE);
1902*4c06356bSdh }
1903*4c06356bSdh 
1904*4c06356bSdh int
1905*4c06356bSdh pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport)
1906*4c06356bSdh {
1907*4c06356bSdh 	ASSERT(iport && iport->iss_tgtmap);
1908*4c06356bSdh 	if ((iport == NULL) || (iport->iss_tgtmap == NULL))
1909*4c06356bSdh 		return (B_FALSE);
1910*4c06356bSdh 
1911*4c06356bSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__);
1912*4c06356bSdh 
1913*4c06356bSdh 	/* destroy target map */
1914*4c06356bSdh 	scsi_hba_tgtmap_destroy(iport->iss_tgtmap);
1915*4c06356bSdh 	return (B_TRUE);
1916*4c06356bSdh }
1917*4c06356bSdh 
1918*4c06356bSdh /*
1919*4c06356bSdh  * Query the phymap and populate the iport handle passed in.
1920*4c06356bSdh  * Called with iport lock held.
1921*4c06356bSdh  */
1922*4c06356bSdh int
1923*4c06356bSdh pmcs_iport_configure_phys(pmcs_iport_t *iport)
1924*4c06356bSdh {
1925*4c06356bSdh 	pmcs_hw_t		*pwp;
1926*4c06356bSdh 	pmcs_phy_t		*pptr;
1927*4c06356bSdh 	sas_phymap_phys_t	*phys;
1928*4c06356bSdh 	int			phynum;
1929*4c06356bSdh 	int			inst;
1930*4c06356bSdh 
1931*4c06356bSdh 	ASSERT(iport);
1932*4c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
1933*4c06356bSdh 	pwp = iport->pwp;
1934*4c06356bSdh 	ASSERT(pwp);
1935*4c06356bSdh 	inst = ddi_get_instance(iport->dip);
1936*4c06356bSdh 
1937*4c06356bSdh 	mutex_enter(&pwp->lock);
1938*4c06356bSdh 	ASSERT(pwp->root_phys != NULL);
1939*4c06356bSdh 
1940*4c06356bSdh 	/*
1941*4c06356bSdh 	 * Query the phymap regarding the phys in this iport and populate
1942*4c06356bSdh 	 * the iport's phys list. Hereafter this list is maintained via
1943*4c06356bSdh 	 * port up and down events in pmcs_intr.c
1944*4c06356bSdh 	 */
1945*4c06356bSdh 	ASSERT(list_is_empty(&iport->phys));
1946*4c06356bSdh 	phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
1947*4c06356bSdh 	while ((phynum = sas_phymap_phys_next(phys)) != -1) {
1948*4c06356bSdh 		/* Grab the phy pointer from root_phys */
1949*4c06356bSdh 		pptr = pwp->root_phys + phynum;
1950*4c06356bSdh 		ASSERT(pptr);
1951*4c06356bSdh 		pmcs_lock_phy(pptr);
1952*4c06356bSdh 		ASSERT(pptr->phynum == phynum);
1953*4c06356bSdh 
1954*4c06356bSdh 		/*
1955*4c06356bSdh 		 * Set a back pointer in the phy to this iport.
1956*4c06356bSdh 		 */
1957*4c06356bSdh 		pptr->iport = iport;
1958*4c06356bSdh 
1959*4c06356bSdh 		/*
1960*4c06356bSdh 		 * If this phy is the primary, set a pointer to it on our
1961*4c06356bSdh 		 * iport handle, and set our portid from it.
1962*4c06356bSdh 		 */
1963*4c06356bSdh 		if (!pptr->subsidiary) {
1964*4c06356bSdh 			iport->pptr = pptr;
1965*4c06356bSdh 			iport->portid = pptr->portid;
1966*4c06356bSdh 		}
1967*4c06356bSdh 
1968*4c06356bSdh 		/*
1969*4c06356bSdh 		 * Finally, insert the phy into our list
1970*4c06356bSdh 		 */
1971*4c06356bSdh 		pmcs_add_phy_to_iport(iport, pptr);
1972*4c06356bSdh 		pmcs_unlock_phy(pptr);
1973*4c06356bSdh 
1974*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: found phy %d [0x%p] "
1975*4c06356bSdh 		    "on iport%d, refcnt(%d)", __func__, phynum,
1976*4c06356bSdh 		    (void *)pptr, inst, iport->refcnt);
1977*4c06356bSdh 	}
1978*4c06356bSdh 	mutex_exit(&pwp->lock);
1979*4c06356bSdh 	sas_phymap_phys_free(phys);
1980*4c06356bSdh 	RESTART_DISCOVERY(pwp);
1981*4c06356bSdh 	return (DDI_SUCCESS);
1982*4c06356bSdh }
1983*4c06356bSdh 
1984*4c06356bSdh /*
1985*4c06356bSdh  * Return the iport that ua is associated with, or NULL.  If an iport is
1986*4c06356bSdh  * returned, it will be held and the caller must release the hold.
1987*4c06356bSdh  */
1988*4c06356bSdh static pmcs_iport_t *
1989*4c06356bSdh pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua)
1990*4c06356bSdh {
1991*4c06356bSdh 	pmcs_iport_t	*iport = NULL;
1992*4c06356bSdh 
1993*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
1994*4c06356bSdh 	for (iport = list_head(&pwp->iports);
1995*4c06356bSdh 	    iport != NULL;
1996*4c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
1997*4c06356bSdh 		mutex_enter(&iport->lock);
1998*4c06356bSdh 		if (strcmp(iport->ua, ua) == 0) {
1999*4c06356bSdh 			mutex_exit(&iport->lock);
2000*4c06356bSdh 			mutex_enter(&iport->refcnt_lock);
2001*4c06356bSdh 			iport->refcnt++;
2002*4c06356bSdh 			mutex_exit(&iport->refcnt_lock);
2003*4c06356bSdh 			break;
2004*4c06356bSdh 		}
2005*4c06356bSdh 		mutex_exit(&iport->lock);
2006*4c06356bSdh 	}
2007*4c06356bSdh 	rw_exit(&pwp->iports_lock);
2008*4c06356bSdh 
2009*4c06356bSdh 	return (iport);
2010*4c06356bSdh }
2011*4c06356bSdh 
2012*4c06356bSdh /*
2013*4c06356bSdh  * Return the iport that pptr is associated with, or NULL.
2014*4c06356bSdh  * If an iport is returned, there is a hold that the caller must release.
2015*4c06356bSdh  */
2016*4c06356bSdh pmcs_iport_t *
2017*4c06356bSdh pmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2018*4c06356bSdh {
2019*4c06356bSdh 	pmcs_iport_t	*iport = NULL;
2020*4c06356bSdh 	char		*ua;
2021*4c06356bSdh 
2022*4c06356bSdh 	ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0],
2023*4c06356bSdh 	    pmcs_barray2wwn(pptr->sas_address));
2024*4c06356bSdh 	if (ua) {
2025*4c06356bSdh 		iport = pmcs_get_iport_by_ua(pwp, ua);
2026*4c06356bSdh 		if (iport) {
2027*4c06356bSdh 			mutex_enter(&iport->lock);
2028*4c06356bSdh 			iport->ua_state = UA_ACTIVE;
2029*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: "
2030*4c06356bSdh 			    "found iport [0x%p] on ua (%s) for phy [0x%p], "
2031*4c06356bSdh 			    "refcnt (%d)", __func__, (void *)iport, ua,
2032*4c06356bSdh 			    (void *)pptr, iport->refcnt);
2033*4c06356bSdh 			mutex_exit(&iport->lock);
2034*4c06356bSdh 		}
2035*4c06356bSdh 	}
2036*4c06356bSdh 
2037*4c06356bSdh 	return (iport);
2038*4c06356bSdh }
2039*4c06356bSdh 
2040*4c06356bSdh void
2041*4c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport)
2042*4c06356bSdh {
2043*4c06356bSdh 	/*
2044*4c06356bSdh 	 * Release a refcnt on this iport. If this is the last reference,
2045*4c06356bSdh 	 * signal the potential waiter in pmcs_iport_unattach().
2046*4c06356bSdh 	 */
2047*4c06356bSdh 	ASSERT(iport->refcnt > 0);
2048*4c06356bSdh 	mutex_enter(&iport->refcnt_lock);
2049*4c06356bSdh 	iport->refcnt--;
2050*4c06356bSdh 	mutex_exit(&iport->refcnt_lock);
2051*4c06356bSdh 	if (iport->refcnt == 0) {
2052*4c06356bSdh 		cv_signal(&iport->refcnt_cv);
2053*4c06356bSdh 	}
2054*4c06356bSdh 	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, "%s: iport [0x%p] "
2055*4c06356bSdh 	    "refcnt (%d)", __func__, (void *)iport, iport->refcnt);
2056*4c06356bSdh }
2057*4c06356bSdh 
2058*4c06356bSdh void
2059*4c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp)
2060*4c06356bSdh {
2061*4c06356bSdh 	_NOTE(ARGUNUSED(privp));
2062*4c06356bSdh 	pmcs_hw_t	*pwp = arg;
2063*4c06356bSdh 	pmcs_iport_t	*iport = NULL;
2064*4c06356bSdh 
2065*4c06356bSdh 	mutex_enter(&pwp->lock);
2066*4c06356bSdh 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
2067*4c06356bSdh 		mutex_exit(&pwp->lock);
2068*4c06356bSdh 		return;
2069*4c06356bSdh 	}
2070*4c06356bSdh 	pwp->phymap_active++;
2071*4c06356bSdh 	mutex_exit(&pwp->lock);
2072*4c06356bSdh 
2073*4c06356bSdh 	if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
2074*4c06356bSdh 	    DDI_SUCCESS) {
2075*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to add "
2076*4c06356bSdh 		    "iport handle on unit address [%s]", __func__, ua);
2077*4c06356bSdh 	} else {
2078*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active count "
2079*4c06356bSdh 		    "(%d), added iport handle on unit address [%s]", __func__,
2080*4c06356bSdh 		    pwp->phymap_active, ua);
2081*4c06356bSdh 	}
2082*4c06356bSdh 
2083*4c06356bSdh 	/* Set the HBA softstate as our private data for this unit address */
2084*4c06356bSdh 	*privp = (void *)pwp;
2085*4c06356bSdh 
2086*4c06356bSdh 	/*
2087*4c06356bSdh 	 * We are waiting on attach for this iport node, unless it is still
2088*4c06356bSdh 	 * attached. This can happen if a consumer has an outstanding open
2089*4c06356bSdh 	 * on our iport node, but the port is down.  If this is the case, we
2090*4c06356bSdh 	 * need to configure our iport here for reuse.
2091*4c06356bSdh 	 */
2092*4c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
2093*4c06356bSdh 	if (iport) {
2094*4c06356bSdh 		mutex_enter(&iport->lock);
2095*4c06356bSdh 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
2096*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: "
2097*4c06356bSdh 			    "failed to configure phys on iport [0x%p] at "
2098*4c06356bSdh 			    "unit address (%s)", __func__, (void *)iport, ua);
2099*4c06356bSdh 		}
2100*4c06356bSdh 		iport->ua_state = UA_ACTIVE;
2101*4c06356bSdh 		pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
2102*4c06356bSdh 		    &iport->nphy);
2103*4c06356bSdh 		mutex_exit(&iport->lock);
2104*4c06356bSdh 		pmcs_rele_iport(iport);
2105*4c06356bSdh 	}
2106*4c06356bSdh 
2107*4c06356bSdh }
2108*4c06356bSdh 
2109*4c06356bSdh void
2110*4c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp)
2111*4c06356bSdh {
2112*4c06356bSdh 	_NOTE(ARGUNUSED(privp));
2113*4c06356bSdh 	pmcs_hw_t	*pwp = arg;
2114*4c06356bSdh 	pmcs_iport_t	*iport;
2115*4c06356bSdh 
2116*4c06356bSdh 	mutex_enter(&pwp->lock);
2117*4c06356bSdh 	pwp->phymap_active--;
2118*4c06356bSdh 	mutex_exit(&pwp->lock);
2119*4c06356bSdh 
2120*4c06356bSdh 	if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
2121*4c06356bSdh 	    DDI_SUCCESS) {
2122*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to remove "
2123*4c06356bSdh 		    "iport handle on unit address [%s]", __func__, ua);
2124*4c06356bSdh 	} else {
2125*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active "
2126*4c06356bSdh 		    "count (%d), removed iport handle on unit address [%s]",
2127*4c06356bSdh 		    __func__, pwp->phymap_active, ua);
2128*4c06356bSdh 	}
2129*4c06356bSdh 
2130*4c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, ua);
2131*4c06356bSdh 
2132*4c06356bSdh 	if (iport == NULL) {
2133*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: failed lookup of "
2134*4c06356bSdh 		    "iport handle on unit address (%s)", __func__, ua);
2135*4c06356bSdh 		return;
2136*4c06356bSdh 	}
2137*4c06356bSdh 
2138*4c06356bSdh 	mutex_enter(&iport->lock);
2139*4c06356bSdh 	iport->ua_state = UA_INACTIVE;
2140*4c06356bSdh 	iport->portid = PMCS_IPORT_INVALID_PORT_ID;
2141*4c06356bSdh 	pmcs_remove_phy_from_iport(iport, NULL);
2142*4c06356bSdh 	mutex_exit(&iport->lock);
2143*4c06356bSdh 	pmcs_rele_iport(iport);
2144*4c06356bSdh }
2145*4c06356bSdh 
2146*4c06356bSdh /*
2147*4c06356bSdh  * Top-level discovery function
2148*4c06356bSdh  */
2149*4c06356bSdh void
2150*4c06356bSdh pmcs_discover(pmcs_hw_t *pwp)
2151*4c06356bSdh {
2152*4c06356bSdh 	pmcs_phy_t		*pptr;
2153*4c06356bSdh 	pmcs_phy_t		*root_phy;
2154*4c06356bSdh 
2155*4c06356bSdh 	DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
2156*4c06356bSdh 	    boolean_t, pwp->config_changed);
2157*4c06356bSdh 
2158*4c06356bSdh 	mutex_enter(&pwp->lock);
2159*4c06356bSdh 
2160*4c06356bSdh 	if (pwp->state != STATE_RUNNING) {
2161*4c06356bSdh 		mutex_exit(&pwp->lock);
2162*4c06356bSdh 		return;
2163*4c06356bSdh 	}
2164*4c06356bSdh 
2165*4c06356bSdh 	/* Ensure we have at least one phymap active */
2166*4c06356bSdh 	if (pwp->phymap_active == 0) {
2167*4c06356bSdh 		mutex_exit(&pwp->lock);
2168*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2169*4c06356bSdh 		    "%s: phymap inactive, exiting", __func__);
2170*4c06356bSdh 		return;
2171*4c06356bSdh 	}
2172*4c06356bSdh 
2173*4c06356bSdh 	mutex_exit(&pwp->lock);
2174*4c06356bSdh 
2175*4c06356bSdh 	/*
2176*4c06356bSdh 	 * If no iports have attached, but we have PHYs that are up, we
2177*4c06356bSdh 	 * are waiting for iport attach to complete.  Restart discovery.
2178*4c06356bSdh 	 */
2179*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
2180*4c06356bSdh 	if (!pwp->iports_attached) {
2181*4c06356bSdh 		rw_exit(&pwp->iports_lock);
2182*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2183*4c06356bSdh 		    "%s: no iports attached, retry discovery", __func__);
2184*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
2185*4c06356bSdh 		return;
2186*4c06356bSdh 	}
2187*4c06356bSdh 	rw_exit(&pwp->iports_lock);
2188*4c06356bSdh 
2189*4c06356bSdh 	mutex_enter(&pwp->config_lock);
2190*4c06356bSdh 	if (pwp->configuring) {
2191*4c06356bSdh 		mutex_exit(&pwp->config_lock);
2192*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2193*4c06356bSdh 		    "%s: configuration already in progress", __func__);
2194*4c06356bSdh 		return;
2195*4c06356bSdh 	}
2196*4c06356bSdh 
2197*4c06356bSdh 	if (pmcs_acquire_scratch(pwp, B_FALSE)) {
2198*4c06356bSdh 		mutex_exit(&pwp->config_lock);
2199*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2200*4c06356bSdh 		    "%s: cannot allocate scratch", __func__);
2201*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
2202*4c06356bSdh 		return;
2203*4c06356bSdh 	}
2204*4c06356bSdh 
2205*4c06356bSdh 	pwp->configuring = 1;
2206*4c06356bSdh 	pwp->config_changed = B_FALSE;
2207*4c06356bSdh 	mutex_exit(&pwp->config_lock);
2208*4c06356bSdh 
2209*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery begin");
2210*4c06356bSdh 
2211*4c06356bSdh 	/*
2212*4c06356bSdh 	 * The order of the following traversals is important.
2213*4c06356bSdh 	 *
2214*4c06356bSdh 	 * The first one checks for changed expanders.
2215*4c06356bSdh 	 *
2216*4c06356bSdh 	 * The second one aborts commands for dead devices and deregisters them.
2217*4c06356bSdh 	 *
2218*4c06356bSdh 	 * The third one clears the contents of dead expanders from the tree
2219*4c06356bSdh 	 *
2220*4c06356bSdh 	 * The fourth one clears now dead devices in expanders that remain.
2221*4c06356bSdh 	 */
2222*4c06356bSdh 
2223*4c06356bSdh 	/*
2224*4c06356bSdh 	 * 1. Check expanders marked changed (but not dead) to see if they still
2225*4c06356bSdh 	 * have the same number of phys and the same SAS address. Mark them,
2226*4c06356bSdh 	 * their subsidiary phys (if wide) and their descendents dead if
2227*4c06356bSdh 	 * anything has changed. Check the devices they contain to see if
2228*4c06356bSdh 	 * *they* have changed. If they've changed from type NOTHING we leave
2229*4c06356bSdh 	 * them marked changed to be configured later (picking up a new SAS
2230*4c06356bSdh 	 * address and link rate if possible). Otherwise, any change in type,
2231*4c06356bSdh 	 * SAS address or removal of target role will cause us to mark them
2232*4c06356bSdh 	 * (and their descendents) as dead (and cause any pending commands
2233*4c06356bSdh 	 * and associated devices to be removed).
2234*4c06356bSdh 	 */
2235*4c06356bSdh 	root_phy = pwp->root_phys;
2236*4c06356bSdh 	if (pmcs_check_expanders(pwp, root_phy) == B_TRUE) {
2237*4c06356bSdh 		goto out;
2238*4c06356bSdh 	}
2239*4c06356bSdh 
2240*4c06356bSdh 	/*
2241*4c06356bSdh 	 * 2. Descend the tree looking for dead devices and kill them
2242*4c06356bSdh 	 * by aborting all active commands and then deregistering them.
2243*4c06356bSdh 	 */
2244*4c06356bSdh 	if (pmcs_kill_devices(pwp, root_phy)) {
2245*4c06356bSdh 		goto out;
2246*4c06356bSdh 	}
2247*4c06356bSdh 
2248*4c06356bSdh 	/*
2249*4c06356bSdh 	 * 3. Check for dead expanders and remove their children from the tree.
2250*4c06356bSdh 	 * By the time we get here, the devices and commands for them have
2251*4c06356bSdh 	 * already been terminated and removed.
2252*4c06356bSdh 	 *
2253*4c06356bSdh 	 * We do this independent of the configuration count changing so we can
2254*4c06356bSdh 	 * free any dead device PHYs that were discovered while checking
2255*4c06356bSdh 	 * expanders. We ignore any subsidiary phys as pmcs_clear_expander
2256*4c06356bSdh 	 * will take care of those.
2257*4c06356bSdh 	 *
2258*4c06356bSdh 	 * NOTE: pmcs_clear_expander requires softstate lock
2259*4c06356bSdh 	 */
2260*4c06356bSdh 	mutex_enter(&pwp->lock);
2261*4c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
2262*4c06356bSdh 		/*
2263*4c06356bSdh 		 * Call pmcs_clear_expander for every root PHY.  It will
2264*4c06356bSdh 		 * recurse and determine which (if any) expanders actually
2265*4c06356bSdh 		 * need to be cleared.
2266*4c06356bSdh 		 */
2267*4c06356bSdh 		pmcs_lock_phy(pptr);
2268*4c06356bSdh 		pmcs_clear_expander(pwp, pptr, 0);
2269*4c06356bSdh 		pmcs_unlock_phy(pptr);
2270*4c06356bSdh 	}
2271*4c06356bSdh 	mutex_exit(&pwp->lock);
2272*4c06356bSdh 
2273*4c06356bSdh 	/*
2274*4c06356bSdh 	 * 4. Check for dead devices and nullify them. By the time we get here,
2275*4c06356bSdh 	 * the devices and commands for them have already been terminated
2276*4c06356bSdh 	 * and removed. This is different from step 2 in that this just nulls
2277*4c06356bSdh 	 * phys that are part of expanders that are still here but used to
2278*4c06356bSdh 	 * be something but are no longer something (e.g., after a pulled
2279*4c06356bSdh 	 * disk drive). Note that dead expanders had their contained phys
2280*4c06356bSdh 	 * removed from the tree- here, the expanders themselves are
2281*4c06356bSdh 	 * nullified (unless they were removed by being contained in another
2282*4c06356bSdh 	 * expander phy).
2283*4c06356bSdh 	 */
2284*4c06356bSdh 	pmcs_clear_phys(pwp, root_phy);
2285*4c06356bSdh 
2286*4c06356bSdh 	/*
2287*4c06356bSdh 	 * 5. Now check for and configure new devices.
2288*4c06356bSdh 	 */
2289*4c06356bSdh 	if (pmcs_configure_new_devices(pwp, root_phy)) {
2290*4c06356bSdh 		goto restart;
2291*4c06356bSdh 	}
2292*4c06356bSdh 
2293*4c06356bSdh out:
2294*4c06356bSdh 	DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
2295*4c06356bSdh 	    boolean_t, pwp->config_changed);
2296*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery end");
2297*4c06356bSdh 
2298*4c06356bSdh 	mutex_enter(&pwp->config_lock);
2299*4c06356bSdh 
2300*4c06356bSdh 	if (pwp->config_changed == B_FALSE) {
2301*4c06356bSdh 		/*
2302*4c06356bSdh 		 * Observation is stable, report what we currently see to
2303*4c06356bSdh 		 * the tgtmaps for delta processing. Start by setting
2304*4c06356bSdh 		 * BEGIN on all tgtmaps.
2305*4c06356bSdh 		 */
2306*4c06356bSdh 		mutex_exit(&pwp->config_lock);
2307*4c06356bSdh 		if (pmcs_report_observations(pwp) == B_FALSE) {
2308*4c06356bSdh 			goto restart;
2309*4c06356bSdh 		}
2310*4c06356bSdh 		mutex_enter(&pwp->config_lock);
2311*4c06356bSdh 	} else {
2312*4c06356bSdh 		/*
2313*4c06356bSdh 		 * If config_changed is TRUE, we need to reschedule
2314*4c06356bSdh 		 * discovery now.
2315*4c06356bSdh 		 */
2316*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2317*4c06356bSdh 		    "%s: Config has changed, will re-run discovery", __func__);
2318*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
2319*4c06356bSdh 	}
2320*4c06356bSdh 
2321*4c06356bSdh 	pmcs_release_scratch(pwp);
2322*4c06356bSdh 	pwp->configuring = 0;
2323*4c06356bSdh 	mutex_exit(&pwp->config_lock);
2324*4c06356bSdh 
2325*4c06356bSdh #ifdef DEBUG
2326*4c06356bSdh 	pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
2327*4c06356bSdh 	if (pptr != NULL) {
2328*4c06356bSdh 		if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
2329*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2330*4c06356bSdh 			    "PHY %s dead=%d changed=%d configured=%d "
2331*4c06356bSdh 			    "but no work scheduled", pptr->path, pptr->dead,
2332*4c06356bSdh 			    pptr->changed, pptr->configured);
2333*4c06356bSdh 		}
2334*4c06356bSdh 		pmcs_unlock_phy(pptr);
2335*4c06356bSdh 	}
2336*4c06356bSdh #endif
2337*4c06356bSdh 
2338*4c06356bSdh 	return;
2339*4c06356bSdh 
2340*4c06356bSdh restart:
2341*4c06356bSdh 	/* Clean up and restart discovery */
2342*4c06356bSdh 	pmcs_release_scratch(pwp);
2343*4c06356bSdh 	mutex_enter(&pwp->config_lock);
2344*4c06356bSdh 	pwp->configuring = 0;
2345*4c06356bSdh 	RESTART_DISCOVERY_LOCKED(pwp);
2346*4c06356bSdh 	mutex_exit(&pwp->config_lock);
2347*4c06356bSdh }
2348*4c06356bSdh 
2349*4c06356bSdh /*
2350*4c06356bSdh  * Return any PHY that needs to have scheduled work done.  The PHY is returned
2351*4c06356bSdh  * locked.
2352*4c06356bSdh  */
2353*4c06356bSdh static pmcs_phy_t *
2354*4c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2355*4c06356bSdh {
2356*4c06356bSdh 	pmcs_phy_t *cphyp, *pnext;
2357*4c06356bSdh 
2358*4c06356bSdh 	while (pptr) {
2359*4c06356bSdh 		pmcs_lock_phy(pptr);
2360*4c06356bSdh 
2361*4c06356bSdh 		if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
2362*4c06356bSdh 			return (pptr);
2363*4c06356bSdh 		}
2364*4c06356bSdh 
2365*4c06356bSdh 		pnext = pptr->sibling;
2366*4c06356bSdh 
2367*4c06356bSdh 		if (pptr->children) {
2368*4c06356bSdh 			cphyp = pptr->children;
2369*4c06356bSdh 			pmcs_unlock_phy(pptr);
2370*4c06356bSdh 			cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
2371*4c06356bSdh 			if (cphyp) {
2372*4c06356bSdh 				return (cphyp);
2373*4c06356bSdh 			}
2374*4c06356bSdh 		} else {
2375*4c06356bSdh 			pmcs_unlock_phy(pptr);
2376*4c06356bSdh 		}
2377*4c06356bSdh 
2378*4c06356bSdh 		pptr = pnext;
2379*4c06356bSdh 	}
2380*4c06356bSdh 
2381*4c06356bSdh 	return (NULL);
2382*4c06356bSdh }
2383*4c06356bSdh 
2384*4c06356bSdh /*
2385*4c06356bSdh  * Report current observations to SCSA.
2386*4c06356bSdh  */
2387*4c06356bSdh static boolean_t
2388*4c06356bSdh pmcs_report_observations(pmcs_hw_t *pwp)
2389*4c06356bSdh {
2390*4c06356bSdh 	pmcs_iport_t		*iport;
2391*4c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
2392*4c06356bSdh 	char			*ap;
2393*4c06356bSdh 	pmcs_phy_t		*pptr;
2394*4c06356bSdh 	uint64_t		wwn;
2395*4c06356bSdh 
2396*4c06356bSdh 	/*
2397*4c06356bSdh 	 * Observation is stable, report what we currently see to the tgtmaps
2398*4c06356bSdh 	 * for delta processing. Start by setting BEGIN on all tgtmaps.
2399*4c06356bSdh 	 */
2400*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
2401*4c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
2402*4c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
2403*4c06356bSdh 		/*
2404*4c06356bSdh 		 * Unless we have at least one phy up, skip this iport.
2405*4c06356bSdh 		 * Note we don't need to lock the iport for report_skip
2406*4c06356bSdh 		 * since it is only used here.  We are doing the skip so that
2407*4c06356bSdh 		 * the phymap and iportmap stabilization times are honored -
2408*4c06356bSdh 		 * giving us the ability to recover port operation within the
2409*4c06356bSdh 		 * stabilization time without unconfiguring targets using the
2410*4c06356bSdh 		 * port.
2411*4c06356bSdh 		 */
2412*4c06356bSdh 		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
2413*4c06356bSdh 			iport->report_skip = 1;
2414*4c06356bSdh 			continue;		/* skip set_begin */
2415*4c06356bSdh 		}
2416*4c06356bSdh 		iport->report_skip = 0;
2417*4c06356bSdh 
2418*4c06356bSdh 		tgtmap = iport->iss_tgtmap;
2419*4c06356bSdh 		ASSERT(tgtmap);
2420*4c06356bSdh 		if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
2421*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2422*4c06356bSdh 			    "%s: cannot set_begin tgtmap ", __func__);
2423*4c06356bSdh 			rw_exit(&pwp->iports_lock);
2424*4c06356bSdh 			return (B_FALSE);
2425*4c06356bSdh 		}
2426*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2427*4c06356bSdh 		    "%s: set begin on tgtmap [0x%p]", __func__,
2428*4c06356bSdh 		    (void *)tgtmap);
2429*4c06356bSdh 	}
2430*4c06356bSdh 	rw_exit(&pwp->iports_lock);
2431*4c06356bSdh 
2432*4c06356bSdh 	/*
2433*4c06356bSdh 	 * Now, cycle through all levels of all phys and report
2434*4c06356bSdh 	 * observations into their respective tgtmaps.
2435*4c06356bSdh 	 */
2436*4c06356bSdh 	pptr = pwp->root_phys;
2437*4c06356bSdh 
2438*4c06356bSdh 	while (pptr) {
2439*4c06356bSdh 		pmcs_lock_phy(pptr);
2440*4c06356bSdh 
2441*4c06356bSdh 		/*
2442*4c06356bSdh 		 * Skip PHYs that have nothing attached or are dead.
2443*4c06356bSdh 		 */
2444*4c06356bSdh 		if ((pptr->dtype == NOTHING) || pptr->dead) {
2445*4c06356bSdh 			pmcs_unlock_phy(pptr);
2446*4c06356bSdh 			pptr = pptr->sibling;
2447*4c06356bSdh 			continue;
2448*4c06356bSdh 		}
2449*4c06356bSdh 
2450*4c06356bSdh 		if (pptr->changed) {
2451*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2452*4c06356bSdh 			    "%s: oops, PHY %s changed; restart discovery",
2453*4c06356bSdh 			    __func__, pptr->path);
2454*4c06356bSdh 			pmcs_unlock_phy(pptr);
2455*4c06356bSdh 			return (B_FALSE);
2456*4c06356bSdh 		}
2457*4c06356bSdh 
2458*4c06356bSdh 		/*
2459*4c06356bSdh 		 * Get the iport for this root PHY, then call the helper
2460*4c06356bSdh 		 * to report observations for this iport's targets
2461*4c06356bSdh 		 */
2462*4c06356bSdh 		iport = pmcs_get_iport_by_phy(pwp, pptr);
2463*4c06356bSdh 		if (iport == NULL) {
2464*4c06356bSdh 			/* No iport for this tgt */
2465*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2466*4c06356bSdh 			    "%s: no iport for this target",
2467*4c06356bSdh 			    __func__);
2468*4c06356bSdh 			pmcs_unlock_phy(pptr);
2469*4c06356bSdh 			pptr = pptr->sibling;
2470*4c06356bSdh 			continue;
2471*4c06356bSdh 		}
2472*4c06356bSdh 
2473*4c06356bSdh 		if (!iport->report_skip) {
2474*4c06356bSdh 			if (pmcs_report_iport_observations(
2475*4c06356bSdh 			    pwp, iport, pptr) == B_FALSE) {
2476*4c06356bSdh 				pmcs_rele_iport(iport);
2477*4c06356bSdh 				pmcs_unlock_phy(pptr);
2478*4c06356bSdh 				return (B_FALSE);
2479*4c06356bSdh 			}
2480*4c06356bSdh 		}
2481*4c06356bSdh 		pmcs_rele_iport(iport);
2482*4c06356bSdh 		pmcs_unlock_phy(pptr);
2483*4c06356bSdh 		pptr = pptr->sibling;
2484*4c06356bSdh 	}
2485*4c06356bSdh 
2486*4c06356bSdh 	/*
2487*4c06356bSdh 	 * The observation is complete, end sets. Note we will skip any
2488*4c06356bSdh 	 * iports that are active, but have no PHYs in them (i.e. awaiting
2489*4c06356bSdh 	 * unconfigure). Set to restart discovery if we find this.
2490*4c06356bSdh 	 */
2491*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_READER);
2492*4c06356bSdh 	for (iport = list_head(&pwp->iports);
2493*4c06356bSdh 	    iport != NULL;
2494*4c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
2495*4c06356bSdh 
2496*4c06356bSdh 		if (iport->report_skip)
2497*4c06356bSdh 			continue;		/* skip set_end */
2498*4c06356bSdh 
2499*4c06356bSdh 		tgtmap = iport->iss_tgtmap;
2500*4c06356bSdh 		ASSERT(tgtmap);
2501*4c06356bSdh 		if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
2502*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2503*4c06356bSdh 			    "%s: cannot set_end tgtmap ", __func__);
2504*4c06356bSdh 			rw_exit(&pwp->iports_lock);
2505*4c06356bSdh 			return (B_FALSE);
2506*4c06356bSdh 		}
2507*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2508*4c06356bSdh 		    "%s: set end on tgtmap [0x%p]", __func__,
2509*4c06356bSdh 		    (void *)tgtmap);
2510*4c06356bSdh 	}
2511*4c06356bSdh 
2512*4c06356bSdh 	/*
2513*4c06356bSdh 	 * Now that discovery is complete, set up the necessary
2514*4c06356bSdh 	 * DDI properties on each iport node.
2515*4c06356bSdh 	 */
2516*4c06356bSdh 	for (iport = list_head(&pwp->iports); iport != NULL;
2517*4c06356bSdh 	    iport = list_next(&pwp->iports, iport)) {
2518*4c06356bSdh 		/* Set up the DDI properties on each phy */
2519*4c06356bSdh 		pmcs_smhba_set_phy_props(iport);
2520*4c06356bSdh 
2521*4c06356bSdh 		/* Set up the 'attached-port' property on the iport */
2522*4c06356bSdh 		ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
2523*4c06356bSdh 		mutex_enter(&iport->lock);
2524*4c06356bSdh 		pptr = iport->pptr;
2525*4c06356bSdh 		mutex_exit(&iport->lock);
2526*4c06356bSdh 		if (pptr == NULL) {
2527*4c06356bSdh 			/*
2528*4c06356bSdh 			 * This iport is down, but has not been
2529*4c06356bSdh 			 * removed from our list (unconfigured).
2530*4c06356bSdh 			 * Set our value to '0'.
2531*4c06356bSdh 			 */
2532*4c06356bSdh 			(void) snprintf(ap, 1, "%s", "0");
2533*4c06356bSdh 		} else {
2534*4c06356bSdh 			/* Otherwise, set it to remote phy's wwn */
2535*4c06356bSdh 			pmcs_lock_phy(pptr);
2536*4c06356bSdh 			wwn = pmcs_barray2wwn(pptr->sas_address);
2537*4c06356bSdh 			(void) scsi_wwn_to_wwnstr(wwn, 1, ap);
2538*4c06356bSdh 			pmcs_unlock_phy(pptr);
2539*4c06356bSdh 		}
2540*4c06356bSdh 		if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
2541*4c06356bSdh 		    SCSI_ADDR_PROP_ATTACHED_PORT,  ap) != DDI_SUCCESS) {
2542*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to "
2543*4c06356bSdh 			    "set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
2544*4c06356bSdh 			    __func__);
2545*4c06356bSdh 		}
2546*4c06356bSdh 		kmem_free(ap, PMCS_MAX_UA_SIZE);
2547*4c06356bSdh 	}
2548*4c06356bSdh 	rw_exit(&pwp->iports_lock);
2549*4c06356bSdh 
2550*4c06356bSdh 	return (B_TRUE);
2551*4c06356bSdh }
2552*4c06356bSdh 
2553*4c06356bSdh /*
2554*4c06356bSdh  * Report observations into a particular iport's target map
2555*4c06356bSdh  *
2556*4c06356bSdh  * Called with phyp (and all descendents) locked
2557*4c06356bSdh  */
2558*4c06356bSdh static boolean_t
2559*4c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
2560*4c06356bSdh     pmcs_phy_t *phyp)
2561*4c06356bSdh {
2562*4c06356bSdh 	pmcs_phy_t		*lphyp;
2563*4c06356bSdh 	scsi_hba_tgtmap_t	*tgtmap;
2564*4c06356bSdh 	scsi_tgtmap_tgt_type_t	tgt_type;
2565*4c06356bSdh 	char			*ua;
2566*4c06356bSdh 	uint64_t		wwn;
2567*4c06356bSdh 
2568*4c06356bSdh 	tgtmap = iport->iss_tgtmap;
2569*4c06356bSdh 	ASSERT(tgtmap);
2570*4c06356bSdh 
2571*4c06356bSdh 	lphyp = phyp;
2572*4c06356bSdh 	while (lphyp) {
2573*4c06356bSdh 		switch (lphyp->dtype) {
2574*4c06356bSdh 		default:		/* Skip unknown PHYs. */
2575*4c06356bSdh 			/* for non-root phys, skip to sibling */
2576*4c06356bSdh 			goto next_phy;
2577*4c06356bSdh 
2578*4c06356bSdh 		case SATA:
2579*4c06356bSdh 		case SAS:
2580*4c06356bSdh 			tgt_type = SCSI_TGT_SCSI_DEVICE;
2581*4c06356bSdh 			break;
2582*4c06356bSdh 
2583*4c06356bSdh 		case EXPANDER:
2584*4c06356bSdh 			tgt_type = SCSI_TGT_SMP_DEVICE;
2585*4c06356bSdh 			break;
2586*4c06356bSdh 		}
2587*4c06356bSdh 
2588*4c06356bSdh 		if (lphyp->dead) {
2589*4c06356bSdh 			goto next_phy;
2590*4c06356bSdh 		}
2591*4c06356bSdh 
2592*4c06356bSdh 		wwn = pmcs_barray2wwn(lphyp->sas_address);
2593*4c06356bSdh 		ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
2594*4c06356bSdh 
2595*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2596*4c06356bSdh 		    "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
2597*4c06356bSdh 		    ua, (void *)tgtmap, (void*)lphyp);
2598*4c06356bSdh 
2599*4c06356bSdh 		if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
2600*4c06356bSdh 		    DDI_SUCCESS) {
2601*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
2602*4c06356bSdh 			    "%s: failed to add address %s", __func__, ua);
2603*4c06356bSdh 			scsi_free_wwnstr(ua);
2604*4c06356bSdh 			return (B_FALSE);
2605*4c06356bSdh 		}
2606*4c06356bSdh 		scsi_free_wwnstr(ua);
2607*4c06356bSdh 
2608*4c06356bSdh 		if (lphyp->children) {
2609*4c06356bSdh 			if (pmcs_report_iport_observations(pwp, iport,
2610*4c06356bSdh 			    lphyp->children) == B_FALSE) {
2611*4c06356bSdh 				return (B_FALSE);
2612*4c06356bSdh 			}
2613*4c06356bSdh 		}
2614*4c06356bSdh 
2615*4c06356bSdh 		/* for non-root phys, report siblings too */
2616*4c06356bSdh next_phy:
2617*4c06356bSdh 		if (IS_ROOT_PHY(lphyp)) {
2618*4c06356bSdh 			lphyp = NULL;
2619*4c06356bSdh 		} else {
2620*4c06356bSdh 			lphyp = lphyp->sibling;
2621*4c06356bSdh 		}
2622*4c06356bSdh 	}
2623*4c06356bSdh 
2624*4c06356bSdh 	return (B_TRUE);
2625*4c06356bSdh }
2626*4c06356bSdh 
2627*4c06356bSdh /*
2628*4c06356bSdh  * Check for and configure new devices.
2629*4c06356bSdh  *
2630*4c06356bSdh  * If the changed device is a SATA device, add a SATA device.
2631*4c06356bSdh  *
2632*4c06356bSdh  * If the changed device is a SAS device, add a SAS device.
2633*4c06356bSdh  *
2634*4c06356bSdh  * If the changed device is an EXPANDER device, do a REPORT
2635*4c06356bSdh  * GENERAL SMP command to find out the number of contained phys.
2636*4c06356bSdh  *
2637*4c06356bSdh  * For each number of contained phys, allocate a phy, do a
2638*4c06356bSdh  * DISCOVERY SMP command to find out what kind of device it
2639*4c06356bSdh  * is and add it to the linked list of phys on the *next* level.
2640*4c06356bSdh  *
2641*4c06356bSdh  * NOTE: pptr passed in by the caller will be a root PHY
2642*4c06356bSdh  */
2643*4c06356bSdh static int
2644*4c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2645*4c06356bSdh {
2646*4c06356bSdh 	int rval = 0;
2647*4c06356bSdh 	pmcs_iport_t *iport;
2648*4c06356bSdh 	pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
2649*4c06356bSdh 
2650*4c06356bSdh 	/*
2651*4c06356bSdh 	 * First, walk through each PHY at this level
2652*4c06356bSdh 	 */
2653*4c06356bSdh 	while (pptr) {
2654*4c06356bSdh 		pmcs_lock_phy(pptr);
2655*4c06356bSdh 		pnext = pptr->sibling;
2656*4c06356bSdh 
2657*4c06356bSdh 		/*
2658*4c06356bSdh 		 * Set the new dtype if it has changed
2659*4c06356bSdh 		 */
2660*4c06356bSdh 		if ((pptr->pend_dtype != NEW) &&
2661*4c06356bSdh 		    (pptr->pend_dtype != pptr->dtype)) {
2662*4c06356bSdh 			pptr->dtype = pptr->pend_dtype;
2663*4c06356bSdh 		}
2664*4c06356bSdh 
2665*4c06356bSdh 		if (pptr->changed == 0 || pptr->dead || pptr->configured) {
2666*4c06356bSdh 			goto next_phy;
2667*4c06356bSdh 		}
2668*4c06356bSdh 
2669*4c06356bSdh 		/*
2670*4c06356bSdh 		 * Confirm that this target's iport is configured
2671*4c06356bSdh 		 */
2672*4c06356bSdh 		root_phy = pmcs_get_root_phy(pptr);
2673*4c06356bSdh 		iport = pmcs_get_iport_by_phy(pwp, root_phy);
2674*4c06356bSdh 		if (iport == NULL) {
2675*4c06356bSdh 			/* No iport for this tgt, restart */
2676*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2677*4c06356bSdh 			    "%s: iport not yet configured, "
2678*4c06356bSdh 			    "retry discovery", __func__);
2679*4c06356bSdh 			pnext = NULL;
2680*4c06356bSdh 			rval = -1;
2681*4c06356bSdh 			goto next_phy;
2682*4c06356bSdh 		}
2683*4c06356bSdh 
2684*4c06356bSdh 		switch (pptr->dtype) {
2685*4c06356bSdh 		case NOTHING:
2686*4c06356bSdh 			pptr->changed = 0;
2687*4c06356bSdh 			break;
2688*4c06356bSdh 		case SATA:
2689*4c06356bSdh 		case SAS:
2690*4c06356bSdh 			pptr->iport = iport;
2691*4c06356bSdh 			pmcs_new_tport(pwp, pptr);
2692*4c06356bSdh 			break;
2693*4c06356bSdh 		case EXPANDER:
2694*4c06356bSdh 			pmcs_configure_expander(pwp, pptr, iport);
2695*4c06356bSdh 			break;
2696*4c06356bSdh 		}
2697*4c06356bSdh 		pmcs_rele_iport(iport);
2698*4c06356bSdh 
2699*4c06356bSdh 		mutex_enter(&pwp->config_lock);
2700*4c06356bSdh 		if (pwp->config_changed) {
2701*4c06356bSdh 			mutex_exit(&pwp->config_lock);
2702*4c06356bSdh 			pnext = NULL;
2703*4c06356bSdh 			goto next_phy;
2704*4c06356bSdh 		}
2705*4c06356bSdh 		mutex_exit(&pwp->config_lock);
2706*4c06356bSdh 
2707*4c06356bSdh next_phy:
2708*4c06356bSdh 		pmcs_unlock_phy(pptr);
2709*4c06356bSdh 		pptr = pnext;
2710*4c06356bSdh 	}
2711*4c06356bSdh 
2712*4c06356bSdh 	if (rval != 0) {
2713*4c06356bSdh 		return (rval);
2714*4c06356bSdh 	}
2715*4c06356bSdh 
2716*4c06356bSdh 	/*
2717*4c06356bSdh 	 * Now walk through each PHY again, recalling ourselves if they
2718*4c06356bSdh 	 * have children
2719*4c06356bSdh 	 */
2720*4c06356bSdh 	pptr = orig_pptr;
2721*4c06356bSdh 	while (pptr) {
2722*4c06356bSdh 		pmcs_lock_phy(pptr);
2723*4c06356bSdh 		pnext = pptr->sibling;
2724*4c06356bSdh 		pchild = pptr->children;
2725*4c06356bSdh 		pmcs_unlock_phy(pptr);
2726*4c06356bSdh 
2727*4c06356bSdh 		if (pchild) {
2728*4c06356bSdh 			rval = pmcs_configure_new_devices(pwp, pchild);
2729*4c06356bSdh 			if (rval != 0) {
2730*4c06356bSdh 				break;
2731*4c06356bSdh 			}
2732*4c06356bSdh 		}
2733*4c06356bSdh 
2734*4c06356bSdh 		pptr = pnext;
2735*4c06356bSdh 	}
2736*4c06356bSdh 
2737*4c06356bSdh 	return (rval);
2738*4c06356bSdh }
2739*4c06356bSdh 
2740*4c06356bSdh /*
2741*4c06356bSdh  * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
2742*4c06356bSdh  * mark them all as not changed.
2743*4c06356bSdh  *
2744*4c06356bSdh  * Called with parent PHY locked.
2745*4c06356bSdh  */
2746*4c06356bSdh void
2747*4c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
2748*4c06356bSdh     int level)
2749*4c06356bSdh {
2750*4c06356bSdh 	pmcs_phy_t *pptr;
2751*4c06356bSdh 
2752*4c06356bSdh 	if (level == 0) {
2753*4c06356bSdh 		if (changed) {
2754*4c06356bSdh 			PHY_CHANGED(pwp, parent);
2755*4c06356bSdh 		} else {
2756*4c06356bSdh 			parent->changed = 0;
2757*4c06356bSdh 		}
2758*4c06356bSdh 		if (parent->dtype == EXPANDER && parent->level) {
2759*4c06356bSdh 			parent->width = 1;
2760*4c06356bSdh 		}
2761*4c06356bSdh 		if (parent->children) {
2762*4c06356bSdh 			pmcs_set_changed(pwp, parent->children, changed,
2763*4c06356bSdh 			    level + 1);
2764*4c06356bSdh 		}
2765*4c06356bSdh 	} else {
2766*4c06356bSdh 		pptr = parent;
2767*4c06356bSdh 		while (pptr) {
2768*4c06356bSdh 			if (changed) {
2769*4c06356bSdh 				PHY_CHANGED(pwp, pptr);
2770*4c06356bSdh 			} else {
2771*4c06356bSdh 				pptr->changed = 0;
2772*4c06356bSdh 			}
2773*4c06356bSdh 			if (pptr->dtype == EXPANDER && pptr->level) {
2774*4c06356bSdh 				pptr->width = 1;
2775*4c06356bSdh 			}
2776*4c06356bSdh 			if (pptr->children) {
2777*4c06356bSdh 				pmcs_set_changed(pwp, pptr->children, changed,
2778*4c06356bSdh 				    level + 1);
2779*4c06356bSdh 			}
2780*4c06356bSdh 			pptr = pptr->sibling;
2781*4c06356bSdh 		}
2782*4c06356bSdh 	}
2783*4c06356bSdh }
2784*4c06356bSdh 
2785*4c06356bSdh /*
2786*4c06356bSdh  * Take the passed phy mark it and its descendants as dead.
2787*4c06356bSdh  * Fire up reconfiguration to abort commands and bury it.
2788*4c06356bSdh  *
2789*4c06356bSdh  * Called with the parent PHY locked.
2790*4c06356bSdh  */
2791*4c06356bSdh void
2792*4c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
2793*4c06356bSdh {
2794*4c06356bSdh 	pmcs_phy_t *pptr = parent;
2795*4c06356bSdh 
2796*4c06356bSdh 	while (pptr) {
2797*4c06356bSdh 		pptr->link_rate = 0;
2798*4c06356bSdh 		pptr->abort_sent = 0;
2799*4c06356bSdh 		pptr->abort_pending = 1;
2800*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
2801*4c06356bSdh 		pptr->need_rl_ext = 0;
2802*4c06356bSdh 
2803*4c06356bSdh 		if (pptr->dead == 0) {
2804*4c06356bSdh 			PHY_CHANGED(pwp, pptr);
2805*4c06356bSdh 			RESTART_DISCOVERY(pwp);
2806*4c06356bSdh 		}
2807*4c06356bSdh 
2808*4c06356bSdh 		pptr->dead = 1;
2809*4c06356bSdh 
2810*4c06356bSdh 		if (pptr->children) {
2811*4c06356bSdh 			pmcs_kill_changed(pwp, pptr->children, level + 1);
2812*4c06356bSdh 		}
2813*4c06356bSdh 
2814*4c06356bSdh 		/*
2815*4c06356bSdh 		 * Only kill siblings at level > 0
2816*4c06356bSdh 		 */
2817*4c06356bSdh 		if (level == 0) {
2818*4c06356bSdh 			return;
2819*4c06356bSdh 		}
2820*4c06356bSdh 
2821*4c06356bSdh 		pptr = pptr->sibling;
2822*4c06356bSdh 	}
2823*4c06356bSdh }
2824*4c06356bSdh 
2825*4c06356bSdh /*
2826*4c06356bSdh  * Go through every PHY and clear any that are dead (unless they're expanders)
2827*4c06356bSdh  */
2828*4c06356bSdh static void
2829*4c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2830*4c06356bSdh {
2831*4c06356bSdh 	pmcs_phy_t *pnext, *phyp;
2832*4c06356bSdh 
2833*4c06356bSdh 	phyp = pptr;
2834*4c06356bSdh 	while (phyp) {
2835*4c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
2836*4c06356bSdh 			pmcs_lock_phy(phyp);
2837*4c06356bSdh 		}
2838*4c06356bSdh 
2839*4c06356bSdh 		if ((phyp->dtype != EXPANDER) && phyp->dead) {
2840*4c06356bSdh 			pmcs_clear_phy(pwp, phyp);
2841*4c06356bSdh 		}
2842*4c06356bSdh 
2843*4c06356bSdh 		if (phyp->children) {
2844*4c06356bSdh 			pmcs_clear_phys(pwp, phyp->children);
2845*4c06356bSdh 		}
2846*4c06356bSdh 
2847*4c06356bSdh 		pnext = phyp->sibling;
2848*4c06356bSdh 
2849*4c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
2850*4c06356bSdh 			pmcs_unlock_phy(phyp);
2851*4c06356bSdh 		}
2852*4c06356bSdh 
2853*4c06356bSdh 		phyp = pnext;
2854*4c06356bSdh 	}
2855*4c06356bSdh }
2856*4c06356bSdh 
2857*4c06356bSdh /*
2858*4c06356bSdh  * Clear volatile parts of a phy.  Called with PHY locked.
2859*4c06356bSdh  */
2860*4c06356bSdh void
2861*4c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2862*4c06356bSdh {
2863*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path);
2864*4c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
2865*4c06356bSdh 	/* keep sibling */
2866*4c06356bSdh 	/* keep children */
2867*4c06356bSdh 	/* keep parent */
2868*4c06356bSdh 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
2869*4c06356bSdh 	/* keep hw_event_ack */
2870*4c06356bSdh 	pptr->ncphy = 0;
2871*4c06356bSdh 	/* keep phynum */
2872*4c06356bSdh 	pptr->width = 0;
2873*4c06356bSdh 	pptr->ds_recovery_retries = 0;
2874*4c06356bSdh 	/* keep dtype */
2875*4c06356bSdh 	pptr->config_stop = 0;
2876*4c06356bSdh 	pptr->spinup_hold = 0;
2877*4c06356bSdh 	pptr->atdt = 0;
2878*4c06356bSdh 	/* keep portid */
2879*4c06356bSdh 	pptr->link_rate = 0;
2880*4c06356bSdh 	pptr->valid_device_id = 0;
2881*4c06356bSdh 	pptr->abort_sent = 0;
2882*4c06356bSdh 	pptr->abort_pending = 0;
2883*4c06356bSdh 	pptr->need_rl_ext = 0;
2884*4c06356bSdh 	pptr->subsidiary = 0;
2885*4c06356bSdh 	pptr->configured = 0;
2886*4c06356bSdh 	/* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
2887*4c06356bSdh 	/* XXX: What about directly attached disks? */
2888*4c06356bSdh 	if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
2889*4c06356bSdh 		pptr->dead = 1;
2890*4c06356bSdh 	pptr->changed = 0;
2891*4c06356bSdh 	/* keep SAS address */
2892*4c06356bSdh 	/* keep path */
2893*4c06356bSdh 	/* keep ref_count */
2894*4c06356bSdh 	/* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
2895*4c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
2896*4c06356bSdh 		pptr->iport = NULL;
2897*4c06356bSdh 	}
2898*4c06356bSdh }
2899*4c06356bSdh 
2900*4c06356bSdh /*
2901*4c06356bSdh  * Allocate softstate for this target if there isn't already one.  If there
2902*4c06356bSdh  * is, just redo our internal configuration.  If it is actually "new", we'll
2903*4c06356bSdh  * soon get a tran_tgt_init for it.
2904*4c06356bSdh  *
2905*4c06356bSdh  * Called with PHY locked.
2906*4c06356bSdh  */
2907*4c06356bSdh static void
2908*4c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2909*4c06356bSdh {
2910*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: phy 0x%p @ %s", __func__,
2911*4c06356bSdh 	    (void *)pptr, pptr->path);
2912*4c06356bSdh 
2913*4c06356bSdh 	if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
2914*4c06356bSdh 		/*
2915*4c06356bSdh 		 * If the config failed, mark the PHY as changed.
2916*4c06356bSdh 		 */
2917*4c06356bSdh 		PHY_CHANGED(pwp, pptr);
2918*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2919*4c06356bSdh 		    "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
2920*4c06356bSdh 		    (void *)pptr);
2921*4c06356bSdh 		return;
2922*4c06356bSdh 	}
2923*4c06356bSdh 
2924*4c06356bSdh 	/* Mark PHY as no longer changed */
2925*4c06356bSdh 	pptr->changed = 0;
2926*4c06356bSdh 
2927*4c06356bSdh 	/*
2928*4c06356bSdh 	 * If the PHY has no target pointer, see if there's a dead PHY that
2929*4c06356bSdh 	 * matches.
2930*4c06356bSdh 	 */
2931*4c06356bSdh 	if (pptr->target == NULL) {
2932*4c06356bSdh 		pmcs_reap_dead_phy(pptr);
2933*4c06356bSdh 	}
2934*4c06356bSdh 
2935*4c06356bSdh 	/*
2936*4c06356bSdh 	 * Only assign the device if there is a target for this PHY with a
2937*4c06356bSdh 	 * matching SAS address.  If an iport is disconnected from one piece
2938*4c06356bSdh 	 * of storage and connected to another within the iport stabilization
2939*4c06356bSdh 	 * time, we can get the PHY/target mismatch situation.
2940*4c06356bSdh 	 *
2941*4c06356bSdh 	 * Otherwise, it'll get done in tran_tgt_init.
2942*4c06356bSdh 	 */
2943*4c06356bSdh 	if (pptr->target) {
2944*4c06356bSdh 		mutex_enter(&pptr->target->statlock);
2945*4c06356bSdh 		if (pmcs_phy_target_match(pptr) == B_FALSE) {
2946*4c06356bSdh 			mutex_exit(&pptr->target->statlock);
2947*4c06356bSdh 			if (!IS_ROOT_PHY(pptr)) {
2948*4c06356bSdh 				pmcs_dec_phy_ref_count(pptr);
2949*4c06356bSdh 			}
2950*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2951*4c06356bSdh 			    "%s: Not assigning existing tgt %p for PHY %p "
2952*4c06356bSdh 			    "(WWN mismatch)", __func__, (void *)pptr->target,
2953*4c06356bSdh 			    (void *)pptr);
2954*4c06356bSdh 			pptr->target = NULL;
2955*4c06356bSdh 			return;
2956*4c06356bSdh 		}
2957*4c06356bSdh 
2958*4c06356bSdh 		if (!pmcs_assign_device(pwp, pptr->target)) {
2959*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2960*4c06356bSdh 			    "%s: pmcs_assign_device failed for target 0x%p",
2961*4c06356bSdh 			    __func__, (void *)pptr->target);
2962*4c06356bSdh 		}
2963*4c06356bSdh 		mutex_exit(&pptr->target->statlock);
2964*4c06356bSdh 	}
2965*4c06356bSdh }
2966*4c06356bSdh 
2967*4c06356bSdh /*
2968*4c06356bSdh  * Called with PHY lock held.
2969*4c06356bSdh  */
2970*4c06356bSdh static boolean_t
2971*4c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2972*4c06356bSdh {
2973*4c06356bSdh 	char *dtype;
2974*4c06356bSdh 
2975*4c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
2976*4c06356bSdh 
2977*4c06356bSdh 	/*
2978*4c06356bSdh 	 * Mark this device as no longer changed.
2979*4c06356bSdh 	 */
2980*4c06356bSdh 	pptr->changed = 0;
2981*4c06356bSdh 
2982*4c06356bSdh 	/*
2983*4c06356bSdh 	 * If we don't have a device handle, get one.
2984*4c06356bSdh 	 */
2985*4c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
2986*4c06356bSdh 		return (B_FALSE);
2987*4c06356bSdh 	}
2988*4c06356bSdh 
2989*4c06356bSdh 	pptr->configured = 1;
2990*4c06356bSdh 
2991*4c06356bSdh 	switch (pptr->dtype) {
2992*4c06356bSdh 	case SAS:
2993*4c06356bSdh 		dtype = "SAS";
2994*4c06356bSdh 		break;
2995*4c06356bSdh 	case SATA:
2996*4c06356bSdh 		dtype = "SATA";
2997*4c06356bSdh 		break;
2998*4c06356bSdh 	case EXPANDER:
2999*4c06356bSdh 		dtype = "SMP";
3000*4c06356bSdh 		break;
3001*4c06356bSdh 	default:
3002*4c06356bSdh 		dtype = "???";
3003*4c06356bSdh 	}
3004*4c06356bSdh 
3005*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "config_dev: %s dev %s "
3006*4c06356bSdh 	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
3007*4c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
3008*4c06356bSdh 
3009*4c06356bSdh 	return (B_TRUE);
3010*4c06356bSdh }
3011*4c06356bSdh 
3012*4c06356bSdh /*
3013*4c06356bSdh  * Called with PHY locked
3014*4c06356bSdh  */
3015*4c06356bSdh static void
3016*4c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
3017*4c06356bSdh {
3018*4c06356bSdh 	pmcs_phy_t *ctmp, *clist = NULL, *cnext;
3019*4c06356bSdh 	int result, i, nphy = 0;
3020*4c06356bSdh 	boolean_t root_phy = B_FALSE;
3021*4c06356bSdh 
3022*4c06356bSdh 	ASSERT(iport);
3023*4c06356bSdh 
3024*4c06356bSdh 	/*
3025*4c06356bSdh 	 * Step 1- clear our "changed" bit. If we need to retry/restart due
3026*4c06356bSdh 	 * to resource shortages, we'll set it again. While we're doing
3027*4c06356bSdh 	 * configuration, other events may set it again as well.  If the PHY
3028*4c06356bSdh 	 * is a root PHY and is currently marked as having changed, reset the
3029*4c06356bSdh 	 * config_stop timer as well.
3030*4c06356bSdh 	 */
3031*4c06356bSdh 	if (IS_ROOT_PHY(pptr) && pptr->changed) {
3032*4c06356bSdh 		pptr->config_stop = ddi_get_lbolt() +
3033*4c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
3034*4c06356bSdh 	}
3035*4c06356bSdh 	pptr->changed = 0;
3036*4c06356bSdh 
3037*4c06356bSdh 	/*
3038*4c06356bSdh 	 * Step 2- make sure we don't overflow
3039*4c06356bSdh 	 */
3040*4c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
3041*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_WARN,
3042*4c06356bSdh 		    "%s: SAS expansion tree too deep", __func__);
3043*4c06356bSdh 		return;
3044*4c06356bSdh 	}
3045*4c06356bSdh 
3046*4c06356bSdh 	/*
3047*4c06356bSdh 	 * Step 3- Check if this expander is part of a wide phy that has
3048*4c06356bSdh 	 * already been configured.
3049*4c06356bSdh 	 *
3050*4c06356bSdh 	 * This is known by checking this level for another EXPANDER device
3051*4c06356bSdh 	 * with the same SAS address and isn't already marked as a subsidiary
3052*4c06356bSdh 	 * phy and a parent whose SAS address is the same as our SAS address
3053*4c06356bSdh 	 * (if there are parents).
3054*4c06356bSdh 	 */
3055*4c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
3056*4c06356bSdh 		/*
3057*4c06356bSdh 		 * No need to lock the parent here because we're in discovery
3058*4c06356bSdh 		 * and the only time a PHY's children pointer can change is
3059*4c06356bSdh 		 * in discovery; either in pmcs_clear_expander (which has
3060*4c06356bSdh 		 * already been called) or here, down below.  Plus, trying to
3061*4c06356bSdh 		 * grab the parent's lock here can cause deadlock.
3062*4c06356bSdh 		 */
3063*4c06356bSdh 		ctmp = pptr->parent->children;
3064*4c06356bSdh 	} else {
3065*4c06356bSdh 		ctmp = pwp->root_phys;
3066*4c06356bSdh 		root_phy = B_TRUE;
3067*4c06356bSdh 	}
3068*4c06356bSdh 
3069*4c06356bSdh 	while (ctmp) {
3070*4c06356bSdh 		/*
3071*4c06356bSdh 		 * If we've checked all PHYs up to pptr, we stop. Otherwise,
3072*4c06356bSdh 		 * we'll be checking for a primary PHY with a higher PHY
3073*4c06356bSdh 		 * number than pptr, which will never happen.  The primary
3074*4c06356bSdh 		 * PHY on non-root expanders will ALWAYS be the lowest
3075*4c06356bSdh 		 * numbered PHY.
3076*4c06356bSdh 		 */
3077*4c06356bSdh 		if (ctmp == pptr) {
3078*4c06356bSdh 			break;
3079*4c06356bSdh 		}
3080*4c06356bSdh 
3081*4c06356bSdh 		/*
3082*4c06356bSdh 		 * If pptr and ctmp are root PHYs, just grab the mutex on
3083*4c06356bSdh 		 * ctmp.  No need to lock the entire tree.  If they are not
3084*4c06356bSdh 		 * root PHYs, there is no need to lock since a non-root PHY's
3085*4c06356bSdh 		 * SAS address and other characteristics can only change in
3086*4c06356bSdh 		 * discovery anyway.
3087*4c06356bSdh 		 */
3088*4c06356bSdh 		if (root_phy) {
3089*4c06356bSdh 			mutex_enter(&ctmp->phy_lock);
3090*4c06356bSdh 		}
3091*4c06356bSdh 
3092*4c06356bSdh 		if (ctmp->dtype == EXPANDER && ctmp->width &&
3093*4c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
3094*4c06356bSdh 			int widephy = 0;
3095*4c06356bSdh 			/*
3096*4c06356bSdh 			 * If these phys are not root PHYs, compare their SAS
3097*4c06356bSdh 			 * addresses too.
3098*4c06356bSdh 			 */
3099*4c06356bSdh 			if (!root_phy) {
3100*4c06356bSdh 				if (memcmp(ctmp->parent->sas_address,
3101*4c06356bSdh 				    pptr->parent->sas_address, 8) == 0) {
3102*4c06356bSdh 					widephy = 1;
3103*4c06356bSdh 				}
3104*4c06356bSdh 			} else {
3105*4c06356bSdh 				widephy = 1;
3106*4c06356bSdh 			}
3107*4c06356bSdh 			if (widephy) {
3108*4c06356bSdh 				ctmp->width++;
3109*4c06356bSdh 				pptr->subsidiary = 1;
3110*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: PHY "
3111*4c06356bSdh 				    "%s part of wide PHY %s (now %d wide)",
3112*4c06356bSdh 				    __func__, pptr->path, ctmp->path,
3113*4c06356bSdh 				    ctmp->width);
3114*4c06356bSdh 				if (root_phy) {
3115*4c06356bSdh 					mutex_exit(&ctmp->phy_lock);
3116*4c06356bSdh 				}
3117*4c06356bSdh 				return;
3118*4c06356bSdh 			}
3119*4c06356bSdh 		}
3120*4c06356bSdh 
3121*4c06356bSdh 		cnext = ctmp->sibling;
3122*4c06356bSdh 		if (root_phy) {
3123*4c06356bSdh 			mutex_exit(&ctmp->phy_lock);
3124*4c06356bSdh 		}
3125*4c06356bSdh 		ctmp = cnext;
3126*4c06356bSdh 	}
3127*4c06356bSdh 
3128*4c06356bSdh 	/*
3129*4c06356bSdh 	 * Step 4- If we don't have a device handle, get one.  Since this
3130*4c06356bSdh 	 * is the primary PHY, make sure subsidiary is cleared.
3131*4c06356bSdh 	 */
3132*4c06356bSdh 	pptr->subsidiary = 0;
3133*4c06356bSdh 	if (pmcs_get_device_handle(pwp, pptr)) {
3134*4c06356bSdh 		goto out;
3135*4c06356bSdh 	}
3136*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Config expander %s "
3137*4c06356bSdh 	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
3138*4c06356bSdh 	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
3139*4c06356bSdh 
3140*4c06356bSdh 	/*
3141*4c06356bSdh 	 * Step 5- figure out how many phys are in this expander.
3142*4c06356bSdh 	 */
3143*4c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
3144*4c06356bSdh 	if (nphy <= 0) {
3145*4c06356bSdh 		if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
3146*4c06356bSdh 			PHY_CHANGED(pwp, pptr);
3147*4c06356bSdh 			RESTART_DISCOVERY(pwp);
3148*4c06356bSdh 		} else {
3149*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3150*4c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
3151*4c06356bSdh 			    pptr->path);
3152*4c06356bSdh 			pptr->config_stop = 0;
3153*4c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
3154*4c06356bSdh 		}
3155*4c06356bSdh 		goto out;
3156*4c06356bSdh 	}
3157*4c06356bSdh 
3158*4c06356bSdh 	/*
3159*4c06356bSdh 	 * Step 6- Allocate a list of phys for this expander and figure out
3160*4c06356bSdh 	 * what each one is.
3161*4c06356bSdh 	 */
3162*4c06356bSdh 	for (i = 0; i < nphy; i++) {
3163*4c06356bSdh 		ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
3164*4c06356bSdh 		bzero(ctmp, sizeof (pmcs_phy_t));
3165*4c06356bSdh 		ctmp->device_id = PMCS_INVALID_DEVICE_ID;
3166*4c06356bSdh 		ctmp->sibling = clist;
3167*4c06356bSdh 		ctmp->pend_dtype = NEW;	/* Init pending dtype */
3168*4c06356bSdh 		ctmp->config_stop = ddi_get_lbolt() +
3169*4c06356bSdh 		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
3170*4c06356bSdh 		clist = ctmp;
3171*4c06356bSdh 	}
3172*4c06356bSdh 
3173*4c06356bSdh 	mutex_enter(&pwp->config_lock);
3174*4c06356bSdh 	if (pwp->config_changed) {
3175*4c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
3176*4c06356bSdh 		mutex_exit(&pwp->config_lock);
3177*4c06356bSdh 		/*
3178*4c06356bSdh 		 * Clean up the newly allocated PHYs and return
3179*4c06356bSdh 		 */
3180*4c06356bSdh 		while (clist) {
3181*4c06356bSdh 			ctmp = clist->sibling;
3182*4c06356bSdh 			kmem_cache_free(pwp->phy_cache, clist);
3183*4c06356bSdh 			clist = ctmp;
3184*4c06356bSdh 		}
3185*4c06356bSdh 		return;
3186*4c06356bSdh 	}
3187*4c06356bSdh 	mutex_exit(&pwp->config_lock);
3188*4c06356bSdh 
3189*4c06356bSdh 	/*
3190*4c06356bSdh 	 * Step 7- Now fill in the rest of the static portions of the phy.
3191*4c06356bSdh 	 */
3192*4c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
3193*4c06356bSdh 		ctmp->parent = pptr;
3194*4c06356bSdh 		ctmp->pwp = pwp;
3195*4c06356bSdh 		ctmp->level = pptr->level+1;
3196*4c06356bSdh 		ctmp->portid = pptr->portid;
3197*4c06356bSdh 		if (ctmp->tolerates_sas2) {
3198*4c06356bSdh 			ASSERT(i < SAS2_PHYNUM_MAX);
3199*4c06356bSdh 			ctmp->phynum = i & SAS2_PHYNUM_MASK;
3200*4c06356bSdh 		} else {
3201*4c06356bSdh 			ASSERT(i < SAS_PHYNUM_MAX);
3202*4c06356bSdh 			ctmp->phynum = i & SAS_PHYNUM_MASK;
3203*4c06356bSdh 		}
3204*4c06356bSdh 		pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
3205*4c06356bSdh 		pmcs_lock_phy(ctmp);
3206*4c06356bSdh 	}
3207*4c06356bSdh 
3208*4c06356bSdh 	/*
3209*4c06356bSdh 	 * Step 8- Discover things about each phy in the expander.
3210*4c06356bSdh 	 */
3211*4c06356bSdh 	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
3212*4c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, ctmp);
3213*4c06356bSdh 		if (result <= 0) {
3214*4c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
3215*4c06356bSdh 				PHY_CHANGED(pwp, pptr);
3216*4c06356bSdh 				RESTART_DISCOVERY(pwp);
3217*4c06356bSdh 			} else {
3218*4c06356bSdh 				pptr->config_stop = 0;
3219*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3220*4c06356bSdh 				    "%s: Retries exhausted for %s, killing",
3221*4c06356bSdh 				    __func__, pptr->path);
3222*4c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
3223*4c06356bSdh 			}
3224*4c06356bSdh 			goto out;
3225*4c06356bSdh 		}
3226*4c06356bSdh 
3227*4c06356bSdh 		/* Set pend_dtype to dtype for 1st time initialization */
3228*4c06356bSdh 		ctmp->pend_dtype = ctmp->dtype;
3229*4c06356bSdh 	}
3230*4c06356bSdh 
3231*4c06356bSdh 	/*
3232*4c06356bSdh 	 * Step 9- Install the new list on the next level. There should be
3233*4c06356bSdh 	 * no children pointer on this PHY.  If there is, we'd need to know
3234*4c06356bSdh 	 * how it happened (The expander suddenly got more PHYs?).
3235*4c06356bSdh 	 */
3236*4c06356bSdh 	ASSERT(pptr->children == NULL);
3237*4c06356bSdh 	if (pptr->children != NULL) {
3238*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Already child PHYs attached "
3239*4c06356bSdh 		    " to PHY %s: This should never happen", __func__,
3240*4c06356bSdh 		    pptr->path);
3241*4c06356bSdh 		goto out;
3242*4c06356bSdh 	} else {
3243*4c06356bSdh 		pptr->children = clist;
3244*4c06356bSdh 	}
3245*4c06356bSdh 
3246*4c06356bSdh 	clist = NULL;
3247*4c06356bSdh 	pptr->ncphy = nphy;
3248*4c06356bSdh 	pptr->configured = 1;
3249*4c06356bSdh 
3250*4c06356bSdh 	/*
3251*4c06356bSdh 	 * We only set width if we're greater than level 0.
3252*4c06356bSdh 	 */
3253*4c06356bSdh 	if (pptr->level) {
3254*4c06356bSdh 		pptr->width = 1;
3255*4c06356bSdh 	}
3256*4c06356bSdh 
3257*4c06356bSdh 	/*
3258*4c06356bSdh 	 * Now tell the rest of the world about us, as an SMP node.
3259*4c06356bSdh 	 */
3260*4c06356bSdh 	pptr->iport = iport;
3261*4c06356bSdh 	pmcs_new_tport(pwp, pptr);
3262*4c06356bSdh 
3263*4c06356bSdh out:
3264*4c06356bSdh 	while (clist) {
3265*4c06356bSdh 		ctmp = clist->sibling;
3266*4c06356bSdh 		pmcs_unlock_phy(clist);
3267*4c06356bSdh 		kmem_cache_free(pwp->phy_cache, clist);
3268*4c06356bSdh 		clist = ctmp;
3269*4c06356bSdh 	}
3270*4c06356bSdh }
3271*4c06356bSdh 
3272*4c06356bSdh /*
3273*4c06356bSdh  * 2. Check expanders marked changed (but not dead) to see if they still have
3274*4c06356bSdh  * the same number of phys and the same SAS address. Mark them, their subsidiary
3275*4c06356bSdh  * phys (if wide) and their descendents dead if anything has changed. Check the
3276*4c06356bSdh  * the devices they contain to see if *they* have changed. If they've changed
3277*4c06356bSdh  * from type NOTHING we leave them marked changed to be configured later
3278*4c06356bSdh  * (picking up a new SAS address and link rate if possible). Otherwise, any
3279*4c06356bSdh  * change in type, SAS address or removal of target role will cause us to
3280*4c06356bSdh  * mark them (and their descendents) as dead and cause any pending commands
3281*4c06356bSdh  * and associated devices to be removed.
3282*4c06356bSdh  *
3283*4c06356bSdh  * Called with PHY (pptr) locked.
3284*4c06356bSdh  */
3285*4c06356bSdh 
3286*4c06356bSdh static void
3287*4c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
3288*4c06356bSdh {
3289*4c06356bSdh 	int nphy, result;
3290*4c06356bSdh 	pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
3291*4c06356bSdh 	boolean_t kill_changed, changed;
3292*4c06356bSdh 
3293*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3294*4c06356bSdh 	    "%s: check %s", __func__, pptr->path);
3295*4c06356bSdh 
3296*4c06356bSdh 	/*
3297*4c06356bSdh 	 * Step 1: Mark phy as not changed. We will mark it changed if we need
3298*4c06356bSdh 	 * to retry.
3299*4c06356bSdh 	 */
3300*4c06356bSdh 	pptr->changed = 0;
3301*4c06356bSdh 
3302*4c06356bSdh 	/*
3303*4c06356bSdh 	 * Reset the config_stop time. Although we're not actually configuring
3304*4c06356bSdh 	 * anything here, we do want some indication of when to give up trying
3305*4c06356bSdh 	 * if we can't communicate with the expander.
3306*4c06356bSdh 	 */
3307*4c06356bSdh 	pptr->config_stop = ddi_get_lbolt() +
3308*4c06356bSdh 	    drv_usectohz(PMCS_MAX_CONFIG_TIME);
3309*4c06356bSdh 
3310*4c06356bSdh 	/*
3311*4c06356bSdh 	 * Step 2: Figure out how many phys are in this expander. If
3312*4c06356bSdh 	 * pmcs_expander_get_nphy returns 0 we ran out of resources,
3313*4c06356bSdh 	 * so reschedule and try later. If it returns another error,
3314*4c06356bSdh 	 * just return.
3315*4c06356bSdh 	 */
3316*4c06356bSdh 	nphy = pmcs_expander_get_nphy(pwp, pptr);
3317*4c06356bSdh 	if (nphy <= 0) {
3318*4c06356bSdh 		if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
3319*4c06356bSdh 			PHY_CHANGED(pwp, pptr);
3320*4c06356bSdh 			RESTART_DISCOVERY(pwp);
3321*4c06356bSdh 		} else {
3322*4c06356bSdh 			pptr->config_stop = 0;
3323*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3324*4c06356bSdh 			    "%s: Retries exhausted for %s, killing", __func__,
3325*4c06356bSdh 			    pptr->path);
3326*4c06356bSdh 			pmcs_kill_changed(pwp, pptr, 0);
3327*4c06356bSdh 		}
3328*4c06356bSdh 		return;
3329*4c06356bSdh 	}
3330*4c06356bSdh 
3331*4c06356bSdh 	/*
3332*4c06356bSdh 	 * Step 3: If the number of phys don't agree, kill the old sub-tree.
3333*4c06356bSdh 	 */
3334*4c06356bSdh 	if (nphy != pptr->ncphy) {
3335*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3336*4c06356bSdh 		    "%s: number of contained phys for %s changed from %d to %d",
3337*4c06356bSdh 		    __func__, pptr->path, pptr->ncphy, nphy);
3338*4c06356bSdh 		/*
3339*4c06356bSdh 		 * Force a rescan of this expander after dead contents
3340*4c06356bSdh 		 * are cleared and removed.
3341*4c06356bSdh 		 */
3342*4c06356bSdh 		pmcs_kill_changed(pwp, pptr, 0);
3343*4c06356bSdh 		return;
3344*4c06356bSdh 	}
3345*4c06356bSdh 
3346*4c06356bSdh 	/*
3347*4c06356bSdh 	 * Step 4: if we're at the bottom of the stack, we're done
3348*4c06356bSdh 	 * (we can't have any levels below us)
3349*4c06356bSdh 	 */
3350*4c06356bSdh 	if (pptr->level == PMCS_MAX_XPND-1) {
3351*4c06356bSdh 		return;
3352*4c06356bSdh 	}
3353*4c06356bSdh 
3354*4c06356bSdh 	/*
3355*4c06356bSdh 	 * Step 5: Discover things about each phy in this expander.  We do
3356*4c06356bSdh 	 * this by walking the current list of contained phys and doing a
3357*4c06356bSdh 	 * content discovery for it to a local phy.
3358*4c06356bSdh 	 */
3359*4c06356bSdh 	ctmp = pptr->children;
3360*4c06356bSdh 	ASSERT(ctmp);
3361*4c06356bSdh 	if (ctmp == NULL) {
3362*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3363*4c06356bSdh 		    "%s: No children attached to expander @ %s?", __func__,
3364*4c06356bSdh 		    pptr->path);
3365*4c06356bSdh 		return;
3366*4c06356bSdh 	}
3367*4c06356bSdh 
3368*4c06356bSdh 	while (ctmp) {
3369*4c06356bSdh 		/*
3370*4c06356bSdh 		 * Allocate a local PHY to contain the proposed new contents
3371*4c06356bSdh 		 * and link it to the rest of the local PHYs so that they
3372*4c06356bSdh 		 * can all be freed later.
3373*4c06356bSdh 		 */
3374*4c06356bSdh 		local = pmcs_clone_phy(ctmp);
3375*4c06356bSdh 
3376*4c06356bSdh 		if (local_list == NULL) {
3377*4c06356bSdh 			local_list = local;
3378*4c06356bSdh 			local_tail = local;
3379*4c06356bSdh 		} else {
3380*4c06356bSdh 			local_tail->sibling = local;
3381*4c06356bSdh 			local_tail = local;
3382*4c06356bSdh 		}
3383*4c06356bSdh 
3384*4c06356bSdh 		/*
3385*4c06356bSdh 		 * Need to lock the local PHY since pmcs_expander_content_
3386*4c06356bSdh 		 * discovery may call pmcs_clear_phy on it, which expects
3387*4c06356bSdh 		 * the PHY to be locked.
3388*4c06356bSdh 		 */
3389*4c06356bSdh 		pmcs_lock_phy(local);
3390*4c06356bSdh 		result = pmcs_expander_content_discover(pwp, pptr, local);
3391*4c06356bSdh 		pmcs_unlock_phy(local);
3392*4c06356bSdh 		if (result <= 0) {
3393*4c06356bSdh 			if (ddi_get_lbolt() < pptr->config_stop) {
3394*4c06356bSdh 				PHY_CHANGED(pwp, pptr);
3395*4c06356bSdh 				RESTART_DISCOVERY(pwp);
3396*4c06356bSdh 			} else {
3397*4c06356bSdh 				pptr->config_stop = 0;
3398*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3399*4c06356bSdh 				    "%s: Retries exhausted for %s, killing",
3400*4c06356bSdh 				    __func__, pptr->path);
3401*4c06356bSdh 				pmcs_kill_changed(pwp, pptr, 0);
3402*4c06356bSdh 			}
3403*4c06356bSdh 
3404*4c06356bSdh 			/*
3405*4c06356bSdh 			 * Release all the local PHYs that we allocated.
3406*4c06356bSdh 			 */
3407*4c06356bSdh 			pmcs_free_phys(pwp, local_list);
3408*4c06356bSdh 			return;
3409*4c06356bSdh 		}
3410*4c06356bSdh 
3411*4c06356bSdh 		ctmp = ctmp->sibling;
3412*4c06356bSdh 	}
3413*4c06356bSdh 
3414*4c06356bSdh 	/*
3415*4c06356bSdh 	 * Step 6: Compare the local PHY's contents to our current PHY.  If
3416*4c06356bSdh 	 * there are changes, take the appropriate action.
3417*4c06356bSdh 	 * This is done in two steps (step 5 above, and 6 here) so that if we
3418*4c06356bSdh 	 * have to bail during this process (e.g. pmcs_expander_content_discover
3419*4c06356bSdh 	 * fails), we haven't actually changed the state of any of the real
3420*4c06356bSdh 	 * PHYs.  Next time we come through here, we'll be starting over from
3421*4c06356bSdh 	 * scratch.  This keeps us from marking a changed PHY as no longer
3422*4c06356bSdh 	 * changed, but then having to bail only to come back next time and
3423*4c06356bSdh 	 * think that the PHY hadn't changed.  If this were to happen, we
3424*4c06356bSdh 	 * would fail to properly configure the device behind this PHY.
3425*4c06356bSdh 	 */
3426*4c06356bSdh 	local = local_list;
3427*4c06356bSdh 	ctmp = pptr->children;
3428*4c06356bSdh 
3429*4c06356bSdh 	while (ctmp) {
3430*4c06356bSdh 		changed = B_FALSE;
3431*4c06356bSdh 		kill_changed = B_FALSE;
3432*4c06356bSdh 
3433*4c06356bSdh 		/*
3434*4c06356bSdh 		 * We set local to local_list prior to this loop so that we
3435*4c06356bSdh 		 * can simply walk the local_list while we walk this list.  The
3436*4c06356bSdh 		 * two lists should be completely in sync.
3437*4c06356bSdh 		 *
3438*4c06356bSdh 		 * Clear the changed flag here.
3439*4c06356bSdh 		 */
3440*4c06356bSdh 		ctmp->changed = 0;
3441*4c06356bSdh 
3442*4c06356bSdh 		if (ctmp->dtype != local->dtype) {
3443*4c06356bSdh 			if (ctmp->dtype != NOTHING) {
3444*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s "
3445*4c06356bSdh 				    "type changed from %s to %s (killing)",
3446*4c06356bSdh 				    __func__, ctmp->path, PHY_TYPE(ctmp),
3447*4c06356bSdh 				    PHY_TYPE(local));
3448*4c06356bSdh 				/*
3449*4c06356bSdh 				 * Force a rescan of this expander after dead
3450*4c06356bSdh 				 * contents are cleared and removed.
3451*4c06356bSdh 				 */
3452*4c06356bSdh 				changed = B_TRUE;
3453*4c06356bSdh 				kill_changed = B_TRUE;
3454*4c06356bSdh 			} else {
3455*4c06356bSdh 				changed = B_TRUE;
3456*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3457*4c06356bSdh 				    "%s: %s type changed from NOTHING to %s",
3458*4c06356bSdh 				    __func__, ctmp->path, PHY_TYPE(local));
3459*4c06356bSdh 			}
3460*4c06356bSdh 
3461*4c06356bSdh 		} else if (ctmp->atdt != local->atdt) {
3462*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s attached "
3463*4c06356bSdh 			    "device type changed from %d to %d (killing)",
3464*4c06356bSdh 			    __func__, ctmp->path, ctmp->atdt, local->atdt);
3465*4c06356bSdh 			/*
3466*4c06356bSdh 			 * Force a rescan of this expander after dead
3467*4c06356bSdh 			 * contents are cleared and removed.
3468*4c06356bSdh 			 */
3469*4c06356bSdh 			changed = B_TRUE;
3470*4c06356bSdh 
3471*4c06356bSdh 			if (local->atdt == 0) {
3472*4c06356bSdh 				kill_changed = B_TRUE;
3473*4c06356bSdh 			}
3474*4c06356bSdh 		} else if (ctmp->link_rate != local->link_rate) {
3475*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_INFO, "%s: %s changed speed from"
3476*4c06356bSdh 			    " %s to %s", __func__, ctmp->path,
3477*4c06356bSdh 			    pmcs_get_rate(ctmp->link_rate),
3478*4c06356bSdh 			    pmcs_get_rate(local->link_rate));
3479*4c06356bSdh 			/* If the speed changed from invalid, force rescan */
3480*4c06356bSdh 			if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
3481*4c06356bSdh 				changed = B_TRUE;
3482*4c06356bSdh 				RESTART_DISCOVERY(pwp);
3483*4c06356bSdh 			} else {
3484*4c06356bSdh 				/* Just update to the new link rate */
3485*4c06356bSdh 				ctmp->link_rate = local->link_rate;
3486*4c06356bSdh 			}
3487*4c06356bSdh 
3488*4c06356bSdh 			if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
3489*4c06356bSdh 				kill_changed = B_TRUE;
3490*4c06356bSdh 			}
3491*4c06356bSdh 		} else if (memcmp(ctmp->sas_address, local->sas_address,
3492*4c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
3493*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: SASAddr "
3494*4c06356bSdh 			    "for %s changed from " SAS_ADDR_FMT " to "
3495*4c06356bSdh 			    SAS_ADDR_FMT " (kill old tree)", __func__,
3496*4c06356bSdh 			    ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
3497*4c06356bSdh 			    SAS_ADDR_PRT(local->sas_address));
3498*4c06356bSdh 			/*
3499*4c06356bSdh 			 * Force a rescan of this expander after dead
3500*4c06356bSdh 			 * contents are cleared and removed.
3501*4c06356bSdh 			 */
3502*4c06356bSdh 			changed = B_TRUE;
3503*4c06356bSdh 		} else {
3504*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3505*4c06356bSdh 			    "%s: %s looks the same (type %s)",
3506*4c06356bSdh 			    __func__, ctmp->path, PHY_TYPE(ctmp));
3507*4c06356bSdh 			/*
3508*4c06356bSdh 			 * If EXPANDER, still mark it changed so we
3509*4c06356bSdh 			 * re-evaluate its contents.  If it's not an expander,
3510*4c06356bSdh 			 * but it hasn't been configured, also mark it as
3511*4c06356bSdh 			 * changed so that it will undergo configuration.
3512*4c06356bSdh 			 */
3513*4c06356bSdh 			if (ctmp->dtype == EXPANDER) {
3514*4c06356bSdh 				changed = B_TRUE;
3515*4c06356bSdh 			} else if ((ctmp->dtype != NOTHING) &&
3516*4c06356bSdh 			    !ctmp->configured) {
3517*4c06356bSdh 				ctmp->changed = 1;
3518*4c06356bSdh 			} else {
3519*4c06356bSdh 				/* It simply hasn't changed */
3520*4c06356bSdh 				ctmp->changed = 0;
3521*4c06356bSdh 			}
3522*4c06356bSdh 		}
3523*4c06356bSdh 
3524*4c06356bSdh 		/*
3525*4c06356bSdh 		 * If the PHY changed, call pmcs_kill_changed if indicated,
3526*4c06356bSdh 		 * update its contents to reflect its current state and mark it
3527*4c06356bSdh 		 * as changed.
3528*4c06356bSdh 		 */
3529*4c06356bSdh 		if (changed) {
3530*4c06356bSdh 			/*
3531*4c06356bSdh 			 * pmcs_kill_changed will mark the PHY as changed, so
3532*4c06356bSdh 			 * only do PHY_CHANGED if we did not do kill_changed.
3533*4c06356bSdh 			 */
3534*4c06356bSdh 			if (kill_changed) {
3535*4c06356bSdh 				pmcs_kill_changed(pwp, ctmp, 0);
3536*4c06356bSdh 			} else {
3537*4c06356bSdh 				/*
3538*4c06356bSdh 				 * If we're not killing the device, it's not
3539*4c06356bSdh 				 * dead.  Mark the PHY as changed.
3540*4c06356bSdh 				 */
3541*4c06356bSdh 				PHY_CHANGED(pwp, ctmp);
3542*4c06356bSdh 
3543*4c06356bSdh 				if (ctmp->dead) {
3544*4c06356bSdh 					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3545*4c06356bSdh 					    "%s: Unmarking PHY %s dead, "
3546*4c06356bSdh 					    "restarting discovery",
3547*4c06356bSdh 					    __func__, ctmp->path);
3548*4c06356bSdh 					ctmp->dead = 0;
3549*4c06356bSdh 					RESTART_DISCOVERY(pwp);
3550*4c06356bSdh 				}
3551*4c06356bSdh 			}
3552*4c06356bSdh 
3553*4c06356bSdh 			/*
3554*4c06356bSdh 			 * If the dtype of this PHY is now NOTHING, mark it as
3555*4c06356bSdh 			 * unconfigured.  Set pend_dtype to what the new dtype
3556*4c06356bSdh 			 * is.  It'll get updated at the end of the discovery
3557*4c06356bSdh 			 * process.
3558*4c06356bSdh 			 */
3559*4c06356bSdh 			if (local->dtype == NOTHING) {
3560*4c06356bSdh 				bzero(ctmp->sas_address,
3561*4c06356bSdh 				    sizeof (local->sas_address));
3562*4c06356bSdh 				ctmp->atdt = 0;
3563*4c06356bSdh 				ctmp->link_rate = 0;
3564*4c06356bSdh 				ctmp->pend_dtype = NOTHING;
3565*4c06356bSdh 				ctmp->configured = 0;
3566*4c06356bSdh 			} else {
3567*4c06356bSdh 				(void) memcpy(ctmp->sas_address,
3568*4c06356bSdh 				    local->sas_address,
3569*4c06356bSdh 				    sizeof (local->sas_address));
3570*4c06356bSdh 				ctmp->atdt = local->atdt;
3571*4c06356bSdh 				ctmp->link_rate = local->link_rate;
3572*4c06356bSdh 				ctmp->pend_dtype = local->dtype;
3573*4c06356bSdh 			}
3574*4c06356bSdh 		}
3575*4c06356bSdh 
3576*4c06356bSdh 		local = local->sibling;
3577*4c06356bSdh 		ctmp = ctmp->sibling;
3578*4c06356bSdh 	}
3579*4c06356bSdh 
3580*4c06356bSdh 	/*
3581*4c06356bSdh 	 * If we got to here, that means we were able to see all the PHYs
3582*4c06356bSdh 	 * and we can now update all of the real PHYs with the information
3583*4c06356bSdh 	 * we got on the local PHYs.  Once that's done, free all the local
3584*4c06356bSdh 	 * PHYs.
3585*4c06356bSdh 	 */
3586*4c06356bSdh 
3587*4c06356bSdh 	pmcs_free_phys(pwp, local_list);
3588*4c06356bSdh }
3589*4c06356bSdh 
3590*4c06356bSdh /*
3591*4c06356bSdh  * Top level routine to check expanders.  We call pmcs_check_expander for
3592*4c06356bSdh  * each expander.  Since we're not doing any configuration right now, it
3593*4c06356bSdh  * doesn't matter if this is breadth-first.
3594*4c06356bSdh  */
3595*4c06356bSdh static boolean_t
3596*4c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
3597*4c06356bSdh {
3598*4c06356bSdh 	pmcs_phy_t *phyp, *pnext, *pchild;
3599*4c06356bSdh 	boolean_t config_changed = B_FALSE;
3600*4c06356bSdh 
3601*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path);
3602*4c06356bSdh 
3603*4c06356bSdh 	/*
3604*4c06356bSdh 	 * Check each expander at this level
3605*4c06356bSdh 	 */
3606*4c06356bSdh 	phyp = pptr;
3607*4c06356bSdh 	while (phyp && !config_changed) {
3608*4c06356bSdh 		pmcs_lock_phy(phyp);
3609*4c06356bSdh 
3610*4c06356bSdh 		if ((phyp->dtype == EXPANDER) && phyp->changed &&
3611*4c06356bSdh 		    !phyp->dead && !phyp->subsidiary &&
3612*4c06356bSdh 		    phyp->configured) {
3613*4c06356bSdh 			pmcs_check_expander(pwp, phyp);
3614*4c06356bSdh 		}
3615*4c06356bSdh 
3616*4c06356bSdh 		pnext = phyp->sibling;
3617*4c06356bSdh 		pmcs_unlock_phy(phyp);
3618*4c06356bSdh 
3619*4c06356bSdh 		mutex_enter(&pwp->config_lock);
3620*4c06356bSdh 		config_changed = pwp->config_changed;
3621*4c06356bSdh 		mutex_exit(&pwp->config_lock);
3622*4c06356bSdh 
3623*4c06356bSdh 		phyp = pnext;
3624*4c06356bSdh 	}
3625*4c06356bSdh 
3626*4c06356bSdh 	if (config_changed) {
3627*4c06356bSdh 		return (config_changed);
3628*4c06356bSdh 	}
3629*4c06356bSdh 
3630*4c06356bSdh 	/*
3631*4c06356bSdh 	 * Now check the children
3632*4c06356bSdh 	 */
3633*4c06356bSdh 	phyp = pptr;
3634*4c06356bSdh 	while (phyp && !config_changed) {
3635*4c06356bSdh 		pmcs_lock_phy(phyp);
3636*4c06356bSdh 		pnext = phyp->sibling;
3637*4c06356bSdh 		pchild = phyp->children;
3638*4c06356bSdh 		pmcs_unlock_phy(phyp);
3639*4c06356bSdh 
3640*4c06356bSdh 		if (pchild) {
3641*4c06356bSdh 			(void) pmcs_check_expanders(pwp, pchild);
3642*4c06356bSdh 		}
3643*4c06356bSdh 
3644*4c06356bSdh 		mutex_enter(&pwp->config_lock);
3645*4c06356bSdh 		config_changed = pwp->config_changed;
3646*4c06356bSdh 		mutex_exit(&pwp->config_lock);
3647*4c06356bSdh 
3648*4c06356bSdh 		phyp = pnext;
3649*4c06356bSdh 	}
3650*4c06356bSdh 
3651*4c06356bSdh 	/*
3652*4c06356bSdh 	 * We're done
3653*4c06356bSdh 	 */
3654*4c06356bSdh 	return (config_changed);
3655*4c06356bSdh }
3656*4c06356bSdh 
3657*4c06356bSdh /*
3658*4c06356bSdh  * Called with softstate and PHY locked
3659*4c06356bSdh  */
3660*4c06356bSdh static void
3661*4c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
3662*4c06356bSdh {
3663*4c06356bSdh 	pmcs_phy_t *ctmp;
3664*4c06356bSdh 
3665*4c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
3666*4c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
3667*4c06356bSdh 	ASSERT(pptr->level < PMCS_MAX_XPND - 1);
3668*4c06356bSdh 
3669*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: checking %s", __func__,
3670*4c06356bSdh 	    pptr->path);
3671*4c06356bSdh 
3672*4c06356bSdh 	ctmp = pptr->children;
3673*4c06356bSdh 	while (ctmp) {
3674*4c06356bSdh 		/*
3675*4c06356bSdh 		 * If the expander is dead, mark its children dead
3676*4c06356bSdh 		 */
3677*4c06356bSdh 		if (pptr->dead) {
3678*4c06356bSdh 			ctmp->dead = 1;
3679*4c06356bSdh 		}
3680*4c06356bSdh 		if (ctmp->dtype == EXPANDER) {
3681*4c06356bSdh 			pmcs_clear_expander(pwp, ctmp, level + 1);
3682*4c06356bSdh 		}
3683*4c06356bSdh 		ctmp = ctmp->sibling;
3684*4c06356bSdh 	}
3685*4c06356bSdh 
3686*4c06356bSdh 	/*
3687*4c06356bSdh 	 * If this expander is not dead, we're done here.
3688*4c06356bSdh 	 */
3689*4c06356bSdh 	if (!pptr->dead) {
3690*4c06356bSdh 		return;
3691*4c06356bSdh 	}
3692*4c06356bSdh 
3693*4c06356bSdh 	/*
3694*4c06356bSdh 	 * Now snip out the list of children below us and release them
3695*4c06356bSdh 	 */
3696*4c06356bSdh 	ctmp = pptr->children;
3697*4c06356bSdh 	while (ctmp) {
3698*4c06356bSdh 		pmcs_phy_t *nxt = ctmp->sibling;
3699*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3700*4c06356bSdh 		    "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
3701*4c06356bSdh 		    (void *)ctmp, ctmp->path, ctmp->ref_count);
3702*4c06356bSdh 		/*
3703*4c06356bSdh 		 * Put this PHY on the dead PHY list for the watchdog to
3704*4c06356bSdh 		 * clean up after any outstanding work has completed.
3705*4c06356bSdh 		 */
3706*4c06356bSdh 		mutex_enter(&pwp->dead_phylist_lock);
3707*4c06356bSdh 		ctmp->dead_next = pwp->dead_phys;
3708*4c06356bSdh 		pwp->dead_phys = ctmp;
3709*4c06356bSdh 		mutex_exit(&pwp->dead_phylist_lock);
3710*4c06356bSdh 		pmcs_unlock_phy(ctmp);
3711*4c06356bSdh 		ctmp = nxt;
3712*4c06356bSdh 	}
3713*4c06356bSdh 
3714*4c06356bSdh 	pptr->children = NULL;
3715*4c06356bSdh 
3716*4c06356bSdh 	/*
3717*4c06356bSdh 	 * Clear subsidiary phys as well.  Getting the parent's PHY lock
3718*4c06356bSdh 	 * is only necessary if level == 0 since otherwise the parent is
3719*4c06356bSdh 	 * already locked.
3720*4c06356bSdh 	 */
3721*4c06356bSdh 	if (!IS_ROOT_PHY(pptr)) {
3722*4c06356bSdh 		if (level == 0) {
3723*4c06356bSdh 			mutex_enter(&pptr->parent->phy_lock);
3724*4c06356bSdh 		}
3725*4c06356bSdh 		ctmp = pptr->parent->children;
3726*4c06356bSdh 		if (level == 0) {
3727*4c06356bSdh 			mutex_exit(&pptr->parent->phy_lock);
3728*4c06356bSdh 		}
3729*4c06356bSdh 	} else {
3730*4c06356bSdh 		ctmp = pwp->root_phys;
3731*4c06356bSdh 	}
3732*4c06356bSdh 
3733*4c06356bSdh 	while (ctmp) {
3734*4c06356bSdh 		if (ctmp == pptr) {
3735*4c06356bSdh 			ctmp = ctmp->sibling;
3736*4c06356bSdh 			continue;
3737*4c06356bSdh 		}
3738*4c06356bSdh 		/*
3739*4c06356bSdh 		 * We only need to lock subsidiary PHYs on the level 0
3740*4c06356bSdh 		 * expander.  Any children of that expander, subsidiaries or
3741*4c06356bSdh 		 * not, will already be locked.
3742*4c06356bSdh 		 */
3743*4c06356bSdh 		if (level == 0) {
3744*4c06356bSdh 			pmcs_lock_phy(ctmp);
3745*4c06356bSdh 		}
3746*4c06356bSdh 		if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
3747*4c06356bSdh 		    memcmp(ctmp->sas_address, pptr->sas_address,
3748*4c06356bSdh 		    sizeof (ctmp->sas_address)) != 0) {
3749*4c06356bSdh 			if (level == 0) {
3750*4c06356bSdh 				pmcs_unlock_phy(ctmp);
3751*4c06356bSdh 			}
3752*4c06356bSdh 			ctmp = ctmp->sibling;
3753*4c06356bSdh 			continue;
3754*4c06356bSdh 		}
3755*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: subsidiary %s",
3756*4c06356bSdh 		    __func__, ctmp->path);
3757*4c06356bSdh 		pmcs_clear_phy(pwp, ctmp);
3758*4c06356bSdh 		if (level == 0) {
3759*4c06356bSdh 			pmcs_unlock_phy(ctmp);
3760*4c06356bSdh 		}
3761*4c06356bSdh 		ctmp = ctmp->sibling;
3762*4c06356bSdh 	}
3763*4c06356bSdh 
3764*4c06356bSdh 	pmcs_clear_phy(pwp, pptr);
3765*4c06356bSdh }
3766*4c06356bSdh 
3767*4c06356bSdh /*
3768*4c06356bSdh  * Called with PHY locked and with scratch acquired. We return 0 if
3769*4c06356bSdh  * we fail to allocate resources or notice that the configuration
3770*4c06356bSdh  * count changed while we were running the command. We return
3771*4c06356bSdh  * less than zero if we had an I/O error or received an unsupported
3772*4c06356bSdh  * configuration. Otherwise we return the number of phys in the
3773*4c06356bSdh  * expander.
3774*4c06356bSdh  */
3775*4c06356bSdh #define	DFM(m, y) if (m == NULL) m = y
3776*4c06356bSdh static int
3777*4c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
3778*4c06356bSdh {
3779*4c06356bSdh 	struct pmcwork *pwrk;
3780*4c06356bSdh 	char buf[64];
3781*4c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
3782*4c06356bSdh 	smp_response_frame_t *srf;
3783*4c06356bSdh 	smp_report_general_resp_t *srgr;
3784*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
3785*4c06356bSdh 	int result;
3786*4c06356bSdh 
3787*4c06356bSdh 	ival = 0x40001100;
3788*4c06356bSdh again:
3789*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
3790*4c06356bSdh 	if (pwrk == NULL) {
3791*4c06356bSdh 		result = 0;
3792*4c06356bSdh 		goto out;
3793*4c06356bSdh 	}
3794*4c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
3795*4c06356bSdh 	pwrk->arg = pwp->scratch;
3796*4c06356bSdh 	pwrk->dtype = pptr->dtype;
3797*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
3798*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
3799*4c06356bSdh 	if (ptr == NULL) {
3800*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
3801*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: GET_IQ_ENTRY failed",
3802*4c06356bSdh 		    __func__);
3803*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
3804*4c06356bSdh 		result = 0;
3805*4c06356bSdh 		goto out;
3806*4c06356bSdh 	}
3807*4c06356bSdh 
3808*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
3809*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
3810*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
3811*4c06356bSdh 	msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
3812*4c06356bSdh 	/*
3813*4c06356bSdh 	 * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
3814*4c06356bSdh 	 */
3815*4c06356bSdh 	msg[4] = BE_32(ival);
3816*4c06356bSdh 	msg[5] = 0;
3817*4c06356bSdh 	msg[6] = 0;
3818*4c06356bSdh 	msg[7] = 0;
3819*4c06356bSdh 	msg[8] = 0;
3820*4c06356bSdh 	msg[9] = 0;
3821*4c06356bSdh 	msg[10] = 0;
3822*4c06356bSdh 	msg[11] = 0;
3823*4c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
3824*4c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
3825*4c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
3826*4c06356bSdh 	msg[15] = 0;
3827*4c06356bSdh 
3828*4c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
3829*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
3830*4c06356bSdh 	htag = pwrk->htag;
3831*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
3832*4c06356bSdh 
3833*4c06356bSdh 	pmcs_unlock_phy(pptr);
3834*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
3835*4c06356bSdh 	pmcs_lock_phy(pptr);
3836*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
3837*4c06356bSdh 
3838*4c06356bSdh 	mutex_enter(&pwp->config_lock);
3839*4c06356bSdh 	if (pwp->config_changed) {
3840*4c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
3841*4c06356bSdh 		mutex_exit(&pwp->config_lock);
3842*4c06356bSdh 		result = 0;
3843*4c06356bSdh 		goto out;
3844*4c06356bSdh 	}
3845*4c06356bSdh 	mutex_exit(&pwp->config_lock);
3846*4c06356bSdh 
3847*4c06356bSdh 	if (result) {
3848*4c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
3849*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3850*4c06356bSdh 		    "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
3851*4c06356bSdh 		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
3852*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3853*4c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
3854*4c06356bSdh 			    __func__, htag);
3855*4c06356bSdh 		} else {
3856*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3857*4c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
3858*4c06356bSdh 			    __func__, htag);
3859*4c06356bSdh 		}
3860*4c06356bSdh 		result = 0;
3861*4c06356bSdh 		goto out;
3862*4c06356bSdh 	}
3863*4c06356bSdh 	ptr = (void *)pwp->scratch;
3864*4c06356bSdh 	status = LE_32(ptr[2]);
3865*4c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
3866*4c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
3867*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
3868*4c06356bSdh 		    "%s: over/underflow", __func__);
3869*4c06356bSdh 		status = PMCOUT_STATUS_OK;
3870*4c06356bSdh 	}
3871*4c06356bSdh 	srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
3872*4c06356bSdh 	srgr = (smp_report_general_resp_t *)
3873*4c06356bSdh 	    &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
3874*4c06356bSdh 
3875*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
3876*4c06356bSdh 		char *nag = NULL;
3877*4c06356bSdh 		(void) snprintf(buf, sizeof (buf),
3878*4c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
3879*4c06356bSdh 		switch (status) {
3880*4c06356bSdh 		case PMCOUT_STATUS_IO_PORT_IN_RESET:
3881*4c06356bSdh 			DFM(nag, "I/O Port In Reset");
3882*4c06356bSdh 			/* FALLTHROUGH */
3883*4c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
3884*4c06356bSdh 			DFM(nag, "Hardware Timeout");
3885*4c06356bSdh 			/* FALLTHROUGH */
3886*4c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
3887*4c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
3888*4c06356bSdh 			/* FALLTHROUGH */
3889*4c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
3890*4c06356bSdh 			DFM(nag, "PHY Not Ready");
3891*4c06356bSdh 			/* FALLTHROUGH */
3892*4c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
3893*4c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
3894*4c06356bSdh 			/* FALLTHROUGH */
3895*4c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
3896*4c06356bSdh 			DFM(nag, "Open Retry Timeout");
3897*4c06356bSdh 			/* FALLTHROUGH */
3898*4c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
3899*4c06356bSdh 			DFM(nag, "Response Connection Error");
3900*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
3901*4c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
3902*4c06356bSdh 			    __func__, pptr->path, nag);
3903*4c06356bSdh 			break;
3904*4c06356bSdh 
3905*4c06356bSdh 		/*
3906*4c06356bSdh 		 * For the IO_DS_NON_OPERATIONAL case, we need to kick off
3907*4c06356bSdh 		 * device state recovery and return 0 so that the caller
3908*4c06356bSdh 		 * doesn't assume this expander is dead for good.
3909*4c06356bSdh 		 */
3910*4c06356bSdh 		case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
3911*4c06356bSdh 			pmcs_xscsi_t *xp = pptr->target;
3912*4c06356bSdh 
3913*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
3914*4c06356bSdh 			    "%s: expander %s device state non-operational",
3915*4c06356bSdh 			    __func__, pptr->path);
3916*4c06356bSdh 
3917*4c06356bSdh 			if (xp == NULL) {
3918*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
3919*4c06356bSdh 				    "%s: No target to do DS recovery for PHY "
3920*4c06356bSdh 				    "%p (%s), attempting PHY hard reset",
3921*4c06356bSdh 				    __func__, (void *)pptr, pptr->path);
3922*4c06356bSdh 				(void) pmcs_reset_phy(pwp, pptr,
3923*4c06356bSdh 				    PMCS_PHYOP_HARD_RESET);
3924*4c06356bSdh 				break;
3925*4c06356bSdh 			}
3926*4c06356bSdh 
3927*4c06356bSdh 			mutex_enter(&xp->statlock);
3928*4c06356bSdh 			pmcs_start_dev_state_recovery(xp, pptr);
3929*4c06356bSdh 			mutex_exit(&xp->statlock);
3930*4c06356bSdh 			break;
3931*4c06356bSdh 		}
3932*4c06356bSdh 
3933*4c06356bSdh 		default:
3934*4c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
3935*4c06356bSdh 			result = -EIO;
3936*4c06356bSdh 			break;
3937*4c06356bSdh 		}
3938*4c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
3939*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
3940*4c06356bSdh 		    "%s: bad response frame type 0x%x",
3941*4c06356bSdh 		    __func__, srf->srf_frame_type);
3942*4c06356bSdh 		result = -EINVAL;
3943*4c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
3944*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x",
3945*4c06356bSdh 		    __func__, srf->srf_function);
3946*4c06356bSdh 		result = -EINVAL;
3947*4c06356bSdh 	} else if (srf->srf_result != 0) {
3948*4c06356bSdh 		/*
3949*4c06356bSdh 		 * Check to see if we have a value of 3 for failure and
3950*4c06356bSdh 		 * whether we were using a SAS2.0 allocation length value
3951*4c06356bSdh 		 * and retry without it.
3952*4c06356bSdh 		 */
3953*4c06356bSdh 		if (srf->srf_result == 3 && (ival & 0xff00)) {
3954*4c06356bSdh 			ival &= ~0xff00;
3955*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
3956*4c06356bSdh 			    "%s: err 0x%x with SAS2 request- retry with SAS1",
3957*4c06356bSdh 			    __func__, srf->srf_result);
3958*4c06356bSdh 			goto again;
3959*4c06356bSdh 		}
3960*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response 0x%x",
3961*4c06356bSdh 		    __func__, srf->srf_result);
3962*4c06356bSdh 		result = -EINVAL;
3963*4c06356bSdh 	} else if (srgr->srgr_configuring) {
3964*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
3965*4c06356bSdh 		    "%s: expander at phy %s is still configuring",
3966*4c06356bSdh 		    __func__, pptr->path);
3967*4c06356bSdh 		result = 0;
3968*4c06356bSdh 	} else {
3969*4c06356bSdh 		result = srgr->srgr_number_of_phys;
3970*4c06356bSdh 		if (ival & 0xff00) {
3971*4c06356bSdh 			pptr->tolerates_sas2 = 1;
3972*4c06356bSdh 		}
3973*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
3974*4c06356bSdh 		    "%s has %d phys and %s SAS2", pptr->path, result,
3975*4c06356bSdh 		    pptr->tolerates_sas2? "tolerates" : "does not tolerate");
3976*4c06356bSdh 	}
3977*4c06356bSdh out:
3978*4c06356bSdh 	return (result);
3979*4c06356bSdh }
3980*4c06356bSdh 
3981*4c06356bSdh /*
3982*4c06356bSdh  * Called with expander locked (and thus, pptr) as well as all PHYs up to
3983*4c06356bSdh  * the root, and scratch acquired. Return 0 if we fail to allocate resources
3984*4c06356bSdh  * or notice that the configuration changed while we were running the command.
3985*4c06356bSdh  *
3986*4c06356bSdh  * We return less than zero if we had an I/O error or received an
3987*4c06356bSdh  * unsupported configuration.
3988*4c06356bSdh  */
3989*4c06356bSdh static int
3990*4c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
3991*4c06356bSdh     pmcs_phy_t *pptr)
3992*4c06356bSdh {
3993*4c06356bSdh 	struct pmcwork *pwrk;
3994*4c06356bSdh 	char buf[64];
3995*4c06356bSdh 	uint8_t sas_address[8];
3996*4c06356bSdh 	uint8_t att_sas_address[8];
3997*4c06356bSdh 	smp_response_frame_t *srf;
3998*4c06356bSdh 	smp_discover_resp_t *sdr;
3999*4c06356bSdh 	const uint_t rdoff = 0x100;	/* returned data offset */
4000*4c06356bSdh 	uint8_t *roff;
4001*4c06356bSdh 	uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
4002*4c06356bSdh 	int result;
4003*4c06356bSdh 	uint8_t	ini_support;
4004*4c06356bSdh 	uint8_t	tgt_support;
4005*4c06356bSdh 
4006*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
4007*4c06356bSdh 	if (pwrk == NULL) {
4008*4c06356bSdh 		result = 0;
4009*4c06356bSdh 		goto out;
4010*4c06356bSdh 	}
4011*4c06356bSdh 	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
4012*4c06356bSdh 	pwrk->arg = pwp->scratch;
4013*4c06356bSdh 	pwrk->dtype = expander->dtype;
4014*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
4015*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
4016*4c06356bSdh 	msg[2] = LE_32(expander->device_id);
4017*4c06356bSdh 	msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
4018*4c06356bSdh 	    SMP_INDIRECT_RESPONSE);
4019*4c06356bSdh 	/*
4020*4c06356bSdh 	 * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
4021*4c06356bSdh 	 */
4022*4c06356bSdh 	if (expander->tolerates_sas2) {
4023*4c06356bSdh 		msg[4] = BE_32(0x40101B00);
4024*4c06356bSdh 	} else {
4025*4c06356bSdh 		msg[4] = BE_32(0x40100000);
4026*4c06356bSdh 	}
4027*4c06356bSdh 	msg[5] = 0;
4028*4c06356bSdh 	msg[6] = BE_32((pptr->phynum << 16));
4029*4c06356bSdh 	msg[7] = 0;
4030*4c06356bSdh 	msg[8] = 0;
4031*4c06356bSdh 	msg[9] = 0;
4032*4c06356bSdh 	msg[10] = 0;
4033*4c06356bSdh 	msg[11] = 0;
4034*4c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
4035*4c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
4036*4c06356bSdh 	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
4037*4c06356bSdh 	msg[15] = 0;
4038*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4039*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4040*4c06356bSdh 	if (ptr == NULL) {
4041*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4042*4c06356bSdh 		result = 0;
4043*4c06356bSdh 		goto out;
4044*4c06356bSdh 	}
4045*4c06356bSdh 
4046*4c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
4047*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
4048*4c06356bSdh 	htag = pwrk->htag;
4049*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4050*4c06356bSdh 
4051*4c06356bSdh 	/*
4052*4c06356bSdh 	 * Drop PHY lock while waiting so other completions aren't potentially
4053*4c06356bSdh 	 * blocked.
4054*4c06356bSdh 	 */
4055*4c06356bSdh 	pmcs_unlock_phy(expander);
4056*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
4057*4c06356bSdh 	pmcs_lock_phy(expander);
4058*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
4059*4c06356bSdh 
4060*4c06356bSdh 	mutex_enter(&pwp->config_lock);
4061*4c06356bSdh 	if (pwp->config_changed) {
4062*4c06356bSdh 		RESTART_DISCOVERY_LOCKED(pwp);
4063*4c06356bSdh 		mutex_exit(&pwp->config_lock);
4064*4c06356bSdh 		result = 0;
4065*4c06356bSdh 		goto out;
4066*4c06356bSdh 	}
4067*4c06356bSdh 	mutex_exit(&pwp->config_lock);
4068*4c06356bSdh 
4069*4c06356bSdh 	if (result) {
4070*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, pmcs_timeo, __func__);
4071*4c06356bSdh 		if (pmcs_abort(pwp, expander, htag, 0, 0)) {
4072*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
4073*4c06356bSdh 			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
4074*4c06356bSdh 			    __func__, htag);
4075*4c06356bSdh 		} else {
4076*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
4077*4c06356bSdh 			    "%s: Issuing SMP ABORT for htag 0x%08x",
4078*4c06356bSdh 			    __func__, htag);
4079*4c06356bSdh 		}
4080*4c06356bSdh 		result = -ETIMEDOUT;
4081*4c06356bSdh 		goto out;
4082*4c06356bSdh 	}
4083*4c06356bSdh 	ptr = (void *)pwp->scratch;
4084*4c06356bSdh 	/*
4085*4c06356bSdh 	 * Point roff to the DMA offset for returned data
4086*4c06356bSdh 	 */
4087*4c06356bSdh 	roff = pwp->scratch;
4088*4c06356bSdh 	roff += rdoff;
4089*4c06356bSdh 	srf = (smp_response_frame_t *)roff;
4090*4c06356bSdh 	sdr = (smp_discover_resp_t *)(roff+4);
4091*4c06356bSdh 	status = LE_32(ptr[2]);
4092*4c06356bSdh 	if (status == PMCOUT_STATUS_UNDERFLOW ||
4093*4c06356bSdh 	    status == PMCOUT_STATUS_OVERFLOW) {
4094*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
4095*4c06356bSdh 		    "%s: over/underflow", __func__);
4096*4c06356bSdh 		status = PMCOUT_STATUS_OK;
4097*4c06356bSdh 	}
4098*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
4099*4c06356bSdh 		char *nag = NULL;
4100*4c06356bSdh 		(void) snprintf(buf, sizeof (buf),
4101*4c06356bSdh 		    "%s: SMP op failed (0x%x)", __func__, status);
4102*4c06356bSdh 		switch (status) {
4103*4c06356bSdh 		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
4104*4c06356bSdh 			DFM(nag, "Hardware Timeout");
4105*4c06356bSdh 			/* FALLTHROUGH */
4106*4c06356bSdh 		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
4107*4c06356bSdh 			DFM(nag, "Internal SMP Resource Failure");
4108*4c06356bSdh 			/* FALLTHROUGH */
4109*4c06356bSdh 		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
4110*4c06356bSdh 			DFM(nag, "PHY Not Ready");
4111*4c06356bSdh 			/* FALLTHROUGH */
4112*4c06356bSdh 		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
4113*4c06356bSdh 			DFM(nag, "Connection Rate Not Supported");
4114*4c06356bSdh 			/* FALLTHROUGH */
4115*4c06356bSdh 		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
4116*4c06356bSdh 			DFM(nag, "Open Retry Timeout");
4117*4c06356bSdh 			/* FALLTHROUGH */
4118*4c06356bSdh 		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
4119*4c06356bSdh 			DFM(nag, "Response Connection Error");
4120*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
4121*4c06356bSdh 			    "%s: expander %s SMP operation failed (%s)",
4122*4c06356bSdh 			    __func__, pptr->path, nag);
4123*4c06356bSdh 			break;
4124*4c06356bSdh 		default:
4125*4c06356bSdh 			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
4126*4c06356bSdh 			result = -EIO;
4127*4c06356bSdh 			break;
4128*4c06356bSdh 		}
4129*4c06356bSdh 		goto out;
4130*4c06356bSdh 	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
4131*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4132*4c06356bSdh 		    "%s: bad response frame type 0x%x",
4133*4c06356bSdh 		    __func__, srf->srf_frame_type);
4134*4c06356bSdh 		result = -EINVAL;
4135*4c06356bSdh 		goto out;
4136*4c06356bSdh 	} else if (srf->srf_function != SMP_FUNC_DISCOVER) {
4137*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x",
4138*4c06356bSdh 		    __func__, srf->srf_function);
4139*4c06356bSdh 		result = -EINVAL;
4140*4c06356bSdh 		goto out;
4141*4c06356bSdh 	} else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
4142*4c06356bSdh 		result = pmcs_smp_function_result(pwp, srf);
4143*4c06356bSdh 		/* Need not fail if PHY is Vacant */
4144*4c06356bSdh 		if (result != SMP_RES_PHY_VACANT) {
4145*4c06356bSdh 			result = -EINVAL;
4146*4c06356bSdh 			goto out;
4147*4c06356bSdh 		}
4148*4c06356bSdh 	}
4149*4c06356bSdh 
4150*4c06356bSdh 	ini_support = (sdr->sdr_attached_sata_host |
4151*4c06356bSdh 	    (sdr->sdr_attached_smp_initiator << 1) |
4152*4c06356bSdh 	    (sdr->sdr_attached_stp_initiator << 2) |
4153*4c06356bSdh 	    (sdr->sdr_attached_ssp_initiator << 3));
4154*4c06356bSdh 
4155*4c06356bSdh 	tgt_support = (sdr->sdr_attached_sata_device |
4156*4c06356bSdh 	    (sdr->sdr_attached_smp_target << 1) |
4157*4c06356bSdh 	    (sdr->sdr_attached_stp_target << 2) |
4158*4c06356bSdh 	    (sdr->sdr_attached_ssp_target << 3));
4159*4c06356bSdh 
4160*4c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
4161*4c06356bSdh 	pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
4162*4c06356bSdh 
4163*4c06356bSdh 	switch (sdr->sdr_attached_device_type) {
4164*4c06356bSdh 	case SAS_IF_DTYPE_ENDPOINT:
4165*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
4166*4c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
4167*4c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
4168*4c06356bSdh 		    pptr->path,
4169*4c06356bSdh 		    sdr->sdr_attached_device_type,
4170*4c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
4171*4c06356bSdh 		    ini_support,
4172*4c06356bSdh 		    tgt_support,
4173*4c06356bSdh 		    SAS_ADDR_PRT(sas_address),
4174*4c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
4175*4c06356bSdh 		    sdr->sdr_attached_phy_identifier);
4176*4c06356bSdh 
4177*4c06356bSdh 		if (sdr->sdr_attached_sata_device ||
4178*4c06356bSdh 		    sdr->sdr_attached_stp_target) {
4179*4c06356bSdh 			pptr->dtype = SATA;
4180*4c06356bSdh 		} else if (sdr->sdr_attached_ssp_target) {
4181*4c06356bSdh 			pptr->dtype = SAS;
4182*4c06356bSdh 		} else if (tgt_support || ini_support) {
4183*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s has "
4184*4c06356bSdh 			    "tgt support=%x init support=(%x)",
4185*4c06356bSdh 			    __func__, pptr->path, tgt_support, ini_support);
4186*4c06356bSdh 		}
4187*4c06356bSdh 		break;
4188*4c06356bSdh 	case SAS_IF_DTYPE_EDGE:
4189*4c06356bSdh 	case SAS_IF_DTYPE_FANOUT:
4190*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
4191*4c06356bSdh 		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
4192*4c06356bSdh 		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
4193*4c06356bSdh 		    pptr->path,
4194*4c06356bSdh 		    sdr->sdr_attached_device_type,
4195*4c06356bSdh 		    sdr->sdr_negotiated_logical_link_rate,
4196*4c06356bSdh 		    ini_support,
4197*4c06356bSdh 		    tgt_support,
4198*4c06356bSdh 		    SAS_ADDR_PRT(sas_address),
4199*4c06356bSdh 		    SAS_ADDR_PRT(att_sas_address),
4200*4c06356bSdh 		    sdr->sdr_attached_phy_identifier);
4201*4c06356bSdh 		if (sdr->sdr_attached_smp_target) {
4202*4c06356bSdh 			/*
4203*4c06356bSdh 			 * Avoid configuring phys that just point back
4204*4c06356bSdh 			 * at a parent phy
4205*4c06356bSdh 			 */
4206*4c06356bSdh 			if (expander->parent &&
4207*4c06356bSdh 			    memcmp(expander->parent->sas_address,
4208*4c06356bSdh 			    att_sas_address,
4209*4c06356bSdh 			    sizeof (expander->parent->sas_address)) == 0) {
4210*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG3,
4211*4c06356bSdh 				    "%s: skipping port back to parent "
4212*4c06356bSdh 				    "expander (%s)", __func__, pptr->path);
4213*4c06356bSdh 				pptr->dtype = NOTHING;
4214*4c06356bSdh 				break;
4215*4c06356bSdh 			}
4216*4c06356bSdh 			pptr->dtype = EXPANDER;
4217*4c06356bSdh 
4218*4c06356bSdh 		} else if (tgt_support || ini_support) {
4219*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s has "
4220*4c06356bSdh 			    "tgt support=%x init support=(%x)",
4221*4c06356bSdh 			    pptr->path, tgt_support, ini_support);
4222*4c06356bSdh 			pptr->dtype = EXPANDER;
4223*4c06356bSdh 		}
4224*4c06356bSdh 		break;
4225*4c06356bSdh 	default:
4226*4c06356bSdh 		pptr->dtype = NOTHING;
4227*4c06356bSdh 		break;
4228*4c06356bSdh 	}
4229*4c06356bSdh 	if (pptr->dtype != NOTHING) {
4230*4c06356bSdh 		pmcs_phy_t *ctmp;
4231*4c06356bSdh 
4232*4c06356bSdh 		/*
4233*4c06356bSdh 		 * If the attached device is a SATA device and the expander
4234*4c06356bSdh 		 * is (possibly) a SAS2 compliant expander, check for whether
4235*4c06356bSdh 		 * there is a NAA=5 WWN field starting at this offset and
4236*4c06356bSdh 		 * use that for the SAS Address for this device.
4237*4c06356bSdh 		 */
4238*4c06356bSdh 		if (expander->tolerates_sas2 && pptr->dtype == SATA &&
4239*4c06356bSdh 		    (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == 0x5) {
4240*4c06356bSdh 			(void) memcpy(pptr->sas_address,
4241*4c06356bSdh 			    &roff[SAS_ATTACHED_NAME_OFFSET], 8);
4242*4c06356bSdh 		} else {
4243*4c06356bSdh 			(void) memcpy(pptr->sas_address, att_sas_address, 8);
4244*4c06356bSdh 		}
4245*4c06356bSdh 		pptr->atdt = (sdr->sdr_attached_device_type);
4246*4c06356bSdh 		/*
4247*4c06356bSdh 		 * Now run up from the expander's parent up to the top to
4248*4c06356bSdh 		 * make sure we only use the least common link_rate.
4249*4c06356bSdh 		 */
4250*4c06356bSdh 		for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
4251*4c06356bSdh 			if (ctmp->link_rate <
4252*4c06356bSdh 			    sdr->sdr_negotiated_logical_link_rate) {
4253*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
4254*4c06356bSdh 				    "%s: derating link rate from %x to %x due "
4255*4c06356bSdh 				    "to %s being slower", pptr->path,
4256*4c06356bSdh 				    sdr->sdr_negotiated_logical_link_rate,
4257*4c06356bSdh 				    ctmp->link_rate,
4258*4c06356bSdh 				    ctmp->path);
4259*4c06356bSdh 				sdr->sdr_negotiated_logical_link_rate =
4260*4c06356bSdh 				    ctmp->link_rate;
4261*4c06356bSdh 			}
4262*4c06356bSdh 		}
4263*4c06356bSdh 		pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
4264*4c06356bSdh 		pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
4265*4c06356bSdh 		pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
4266*4c06356bSdh 		pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
4267*4c06356bSdh 		pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
4268*4c06356bSdh 		PHY_CHANGED(pwp, pptr);
4269*4c06356bSdh 	} else {
4270*4c06356bSdh 		pmcs_clear_phy(pwp, pptr);
4271*4c06356bSdh 	}
4272*4c06356bSdh 	result = 1;
4273*4c06356bSdh out:
4274*4c06356bSdh 	return (result);
4275*4c06356bSdh }
4276*4c06356bSdh 
4277*4c06356bSdh /*
4278*4c06356bSdh  * Get a work structure and assign it a tag with type and serial number
4279*4c06356bSdh  * If a structure is returned, it is returned locked.
4280*4c06356bSdh  */
4281*4c06356bSdh pmcwork_t *
4282*4c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
4283*4c06356bSdh {
4284*4c06356bSdh 	pmcwork_t *p;
4285*4c06356bSdh 	uint16_t snum;
4286*4c06356bSdh 	uint32_t off;
4287*4c06356bSdh 
4288*4c06356bSdh 	mutex_enter(&pwp->wfree_lock);
4289*4c06356bSdh 	p = STAILQ_FIRST(&pwp->wf);
4290*4c06356bSdh 	if (p == NULL) {
4291*4c06356bSdh 		/*
4292*4c06356bSdh 		 * If we couldn't get a work structure, it's time to bite
4293*4c06356bSdh 		 * the bullet, grab the pfree_lock and copy over all the
4294*4c06356bSdh 		 * work structures from the pending free list to the actual
4295*4c06356bSdh 		 * free list.  This shouldn't happen all that often.
4296*4c06356bSdh 		 */
4297*4c06356bSdh 		mutex_enter(&pwp->pfree_lock);
4298*4c06356bSdh 		pwp->wf.stqh_first = pwp->pf.stqh_first;
4299*4c06356bSdh 		pwp->wf.stqh_last = pwp->pf.stqh_last;
4300*4c06356bSdh 		STAILQ_INIT(&pwp->pf);
4301*4c06356bSdh 		mutex_exit(&pwp->pfree_lock);
4302*4c06356bSdh 
4303*4c06356bSdh 		p = STAILQ_FIRST(&pwp->wf);
4304*4c06356bSdh 		if (p == NULL) {
4305*4c06356bSdh 			mutex_exit(&pwp->wfree_lock);
4306*4c06356bSdh 			return (NULL);
4307*4c06356bSdh 		}
4308*4c06356bSdh 	}
4309*4c06356bSdh 	STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
4310*4c06356bSdh 	snum = pwp->wserno++;
4311*4c06356bSdh 	mutex_exit(&pwp->wfree_lock);
4312*4c06356bSdh 
4313*4c06356bSdh 	off = p - pwp->work;
4314*4c06356bSdh 
4315*4c06356bSdh 	mutex_enter(&p->lock);
4316*4c06356bSdh 	ASSERT(p->state == PMCS_WORK_STATE_NIL);
4317*4c06356bSdh 	ASSERT(p->htag == PMCS_TAG_FREE);
4318*4c06356bSdh 	p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
4319*4c06356bSdh 	p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
4320*4c06356bSdh 	p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
4321*4c06356bSdh 	p->start = gethrtime();
4322*4c06356bSdh 	p->state = PMCS_WORK_STATE_READY;
4323*4c06356bSdh 	p->ssp_event = 0;
4324*4c06356bSdh 	p->dead = 0;
4325*4c06356bSdh 
4326*4c06356bSdh 	if (phyp) {
4327*4c06356bSdh 		p->phy = phyp;
4328*4c06356bSdh 		pmcs_inc_phy_ref_count(phyp);
4329*4c06356bSdh 	}
4330*4c06356bSdh 
4331*4c06356bSdh 	return (p);
4332*4c06356bSdh }
4333*4c06356bSdh 
4334*4c06356bSdh /*
4335*4c06356bSdh  * Called with pwrk lock held.  Returned with lock released.
4336*4c06356bSdh  */
4337*4c06356bSdh void
4338*4c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
4339*4c06356bSdh {
4340*4c06356bSdh 	ASSERT(p != NULL);
4341*4c06356bSdh 	ASSERT(mutex_owned(&p->lock));
4342*4c06356bSdh 
4343*4c06356bSdh #ifdef DEBUG
4344*4c06356bSdh 	p->last_ptr = p->ptr;
4345*4c06356bSdh 	p->last_arg = p->arg;
4346*4c06356bSdh 	p->last_phy = p->phy;
4347*4c06356bSdh 	p->last_xp = p->xp;
4348*4c06356bSdh 	p->last_htag = p->htag;
4349*4c06356bSdh 	p->last_state = p->state;
4350*4c06356bSdh #endif
4351*4c06356bSdh 	p->finish = gethrtime();
4352*4c06356bSdh 
4353*4c06356bSdh 	if (p->phy) {
4354*4c06356bSdh 		pmcs_dec_phy_ref_count(p->phy);
4355*4c06356bSdh 	}
4356*4c06356bSdh 
4357*4c06356bSdh 	p->state = PMCS_WORK_STATE_NIL;
4358*4c06356bSdh 	p->htag = PMCS_TAG_FREE;
4359*4c06356bSdh 	p->xp = NULL;
4360*4c06356bSdh 	p->ptr = NULL;
4361*4c06356bSdh 	p->arg = NULL;
4362*4c06356bSdh 	p->phy = NULL;
4363*4c06356bSdh 	p->timer = 0;
4364*4c06356bSdh 	mutex_exit(&p->lock);
4365*4c06356bSdh 
4366*4c06356bSdh 	if (mutex_tryenter(&pwp->wfree_lock) == 0) {
4367*4c06356bSdh 		mutex_enter(&pwp->pfree_lock);
4368*4c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->pf, p, next);
4369*4c06356bSdh 		mutex_exit(&pwp->pfree_lock);
4370*4c06356bSdh 	} else {
4371*4c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, p, next);
4372*4c06356bSdh 		mutex_exit(&pwp->wfree_lock);
4373*4c06356bSdh 	}
4374*4c06356bSdh }
4375*4c06356bSdh 
4376*4c06356bSdh /*
4377*4c06356bSdh  * Find a work structure based upon a tag and make sure that the tag
4378*4c06356bSdh  * serial number matches the work structure we've found.
4379*4c06356bSdh  * If a structure is found, its lock is held upon return.
4380*4c06356bSdh  */
4381*4c06356bSdh pmcwork_t *
4382*4c06356bSdh pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag)
4383*4c06356bSdh {
4384*4c06356bSdh 	pmcwork_t *p;
4385*4c06356bSdh 	uint32_t idx = PMCS_TAG_INDEX(htag);
4386*4c06356bSdh 
4387*4c06356bSdh 	p = &pwp->work[idx];
4388*4c06356bSdh 
4389*4c06356bSdh 	mutex_enter(&p->lock);
4390*4c06356bSdh 	if (p->htag == htag) {
4391*4c06356bSdh 		return (p);
4392*4c06356bSdh 	}
4393*4c06356bSdh 	mutex_exit(&p->lock);
4394*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "INDEX 0x%x HTAG 0x%x got p->htag 0x%x",
4395*4c06356bSdh 	    idx, htag, p->htag);
4396*4c06356bSdh 	return (NULL);
4397*4c06356bSdh }
4398*4c06356bSdh 
4399*4c06356bSdh /*
4400*4c06356bSdh  * Issue an abort for a command or for all commands.
4401*4c06356bSdh  *
4402*4c06356bSdh  * Since this can be called from interrupt context,
4403*4c06356bSdh  * we don't wait for completion if wait is not set.
4404*4c06356bSdh  *
4405*4c06356bSdh  * Called with PHY lock held.
4406*4c06356bSdh  */
4407*4c06356bSdh int
4408*4c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
4409*4c06356bSdh     int wait)
4410*4c06356bSdh {
4411*4c06356bSdh 	pmcwork_t *pwrk;
4412*4c06356bSdh 	pmcs_xscsi_t *tgt;
4413*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
4414*4c06356bSdh 	int result, abt_type;
4415*4c06356bSdh 	uint32_t abt_htag, status;
4416*4c06356bSdh 
4417*4c06356bSdh 	if (pptr->abort_all_start) {
4418*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ABORT_ALL for (%s) already"
4419*4c06356bSdh 		    " in progress.", __func__, pptr->path);
4420*4c06356bSdh 		return (EBUSY);
4421*4c06356bSdh 	}
4422*4c06356bSdh 
4423*4c06356bSdh 	switch (pptr->dtype) {
4424*4c06356bSdh 	case SAS:
4425*4c06356bSdh 		abt_type = PMCIN_SSP_ABORT;
4426*4c06356bSdh 		break;
4427*4c06356bSdh 	case SATA:
4428*4c06356bSdh 		abt_type = PMCIN_SATA_ABORT;
4429*4c06356bSdh 		break;
4430*4c06356bSdh 	case EXPANDER:
4431*4c06356bSdh 		abt_type = PMCIN_SMP_ABORT;
4432*4c06356bSdh 		break;
4433*4c06356bSdh 	default:
4434*4c06356bSdh 		return (0);
4435*4c06356bSdh 	}
4436*4c06356bSdh 
4437*4c06356bSdh 	pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
4438*4c06356bSdh 	    pptr);
4439*4c06356bSdh 
4440*4c06356bSdh 	if (pwrk == NULL) {
4441*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
4442*4c06356bSdh 		return (ENOMEM);
4443*4c06356bSdh 	}
4444*4c06356bSdh 
4445*4c06356bSdh 	pwrk->dtype = pptr->dtype;
4446*4c06356bSdh 	if (wait) {
4447*4c06356bSdh 		pwrk->arg = msg;
4448*4c06356bSdh 	}
4449*4c06356bSdh 	if (pptr->valid_device_id == 0) {
4450*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
4451*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__);
4452*4c06356bSdh 		return (ENODEV);
4453*4c06356bSdh 	}
4454*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
4455*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
4456*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
4457*4c06356bSdh 	if (all_cmds) {
4458*4c06356bSdh 		msg[3] = 0;
4459*4c06356bSdh 		msg[4] = LE_32(1);
4460*4c06356bSdh 		pwrk->ptr = NULL;
4461*4c06356bSdh 		pptr->abort_all_start = gethrtime();
4462*4c06356bSdh 	} else {
4463*4c06356bSdh 		msg[3] = LE_32(tag);
4464*4c06356bSdh 		msg[4] = 0;
4465*4c06356bSdh 		pwrk->ptr = &tag;
4466*4c06356bSdh 	}
4467*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4468*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4469*4c06356bSdh 	if (ptr == NULL) {
4470*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4471*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
4472*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
4473*4c06356bSdh 		return (ENOMEM);
4474*4c06356bSdh 	}
4475*4c06356bSdh 
4476*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 5);
4477*4c06356bSdh 	if (all_cmds) {
4478*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4479*4c06356bSdh 		    "%s: aborting all commands for %s device %s. (htag=0x%x)",
4480*4c06356bSdh 		    __func__, pmcs_get_typename(pptr->dtype), pptr->path,
4481*4c06356bSdh 		    msg[1]);
4482*4c06356bSdh 	} else {
4483*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4484*4c06356bSdh 		    "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
4485*4c06356bSdh 		    __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
4486*4c06356bSdh 		    msg[1]);
4487*4c06356bSdh 	}
4488*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
4489*4c06356bSdh 
4490*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4491*4c06356bSdh 	if (!wait) {
4492*4c06356bSdh 		mutex_exit(&pwrk->lock);
4493*4c06356bSdh 		return (0);
4494*4c06356bSdh 	}
4495*4c06356bSdh 
4496*4c06356bSdh 	abt_htag = pwrk->htag;
4497*4c06356bSdh 	pmcs_unlock_phy(pwrk->phy);
4498*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
4499*4c06356bSdh 	pmcs_lock_phy(pwrk->phy);
4500*4c06356bSdh 
4501*4c06356bSdh 	tgt = pwrk->xp;
4502*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
4503*4c06356bSdh 
4504*4c06356bSdh 	if (tgt != NULL) {
4505*4c06356bSdh 		mutex_enter(&tgt->aqlock);
4506*4c06356bSdh 		if (!STAILQ_EMPTY(&tgt->aq)) {
4507*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
4508*4c06356bSdh 			    "%s: Abort complete (result=0x%x), but "
4509*4c06356bSdh 			    "aq not empty (tgt 0x%p), waiting",
4510*4c06356bSdh 			    __func__, result, (void *)tgt);
4511*4c06356bSdh 			cv_wait(&tgt->abort_cv, &tgt->aqlock);
4512*4c06356bSdh 		}
4513*4c06356bSdh 		mutex_exit(&tgt->aqlock);
4514*4c06356bSdh 	}
4515*4c06356bSdh 
4516*4c06356bSdh 	if (all_cmds) {
4517*4c06356bSdh 		pptr->abort_all_start = 0;
4518*4c06356bSdh 		cv_signal(&pptr->abort_all_cv);
4519*4c06356bSdh 	}
4520*4c06356bSdh 
4521*4c06356bSdh 	if (result) {
4522*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4523*4c06356bSdh 		    "%s: Abort (htag 0x%08x) request timed out",
4524*4c06356bSdh 		    __func__, abt_htag);
4525*4c06356bSdh 		if (tgt != NULL) {
4526*4c06356bSdh 			mutex_enter(&tgt->statlock);
4527*4c06356bSdh 			if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
4528*4c06356bSdh 			    (tgt->dev_state !=
4529*4c06356bSdh 			    PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
4530*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
4531*4c06356bSdh 				    "%s: Trying DS error recovery for tgt 0x%p",
4532*4c06356bSdh 				    __func__, (void *)tgt);
4533*4c06356bSdh 				(void) pmcs_send_err_recovery_cmd(pwp,
4534*4c06356bSdh 				    PMCS_DEVICE_STATE_IN_RECOVERY, tgt);
4535*4c06356bSdh 			}
4536*4c06356bSdh 			mutex_exit(&tgt->statlock);
4537*4c06356bSdh 		}
4538*4c06356bSdh 		return (ETIMEDOUT);
4539*4c06356bSdh 	}
4540*4c06356bSdh 
4541*4c06356bSdh 	status = LE_32(msg[2]);
4542*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
4543*4c06356bSdh 		/*
4544*4c06356bSdh 		 * The only non-success status are IO_NOT_VALID &
4545*4c06356bSdh 		 * IO_ABORT_IN_PROGRESS.
4546*4c06356bSdh 		 * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
4547*4c06356bSdh 		 * status is of concern and this duplicate cmd status can
4548*4c06356bSdh 		 * be ignored.
4549*4c06356bSdh 		 * If IO_NOT_VALID, that's not an error per-se.
4550*4c06356bSdh 		 * For abort of single I/O complete the command anyway.
4551*4c06356bSdh 		 * If, however, we were aborting all, that is a problem
4552*4c06356bSdh 		 * as IO_NOT_VALID really means that the IO or device is
4553*4c06356bSdh 		 * not there. So, discovery process will take of the cleanup.
4554*4c06356bSdh 		 */
4555*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: abort result 0x%x",
4556*4c06356bSdh 		    __func__, LE_32(msg[2]));
4557*4c06356bSdh 		if (all_cmds) {
4558*4c06356bSdh 			PHY_CHANGED(pwp, pptr);
4559*4c06356bSdh 			RESTART_DISCOVERY(pwp);
4560*4c06356bSdh 		} else {
4561*4c06356bSdh 			return (EINVAL);
4562*4c06356bSdh 		}
4563*4c06356bSdh 
4564*4c06356bSdh 		return (0);
4565*4c06356bSdh 	}
4566*4c06356bSdh 
4567*4c06356bSdh 	if (tgt != NULL) {
4568*4c06356bSdh 		mutex_enter(&tgt->statlock);
4569*4c06356bSdh 		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
4570*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
4571*4c06356bSdh 			    "%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
4572*4c06356bSdh 			    __func__, (void *)tgt);
4573*4c06356bSdh 			(void) pmcs_send_err_recovery_cmd(pwp,
4574*4c06356bSdh 			    PMCS_DEVICE_STATE_OPERATIONAL, tgt);
4575*4c06356bSdh 		}
4576*4c06356bSdh 		mutex_exit(&tgt->statlock);
4577*4c06356bSdh 	}
4578*4c06356bSdh 
4579*4c06356bSdh 	return (0);
4580*4c06356bSdh }
4581*4c06356bSdh 
4582*4c06356bSdh /*
4583*4c06356bSdh  * Issue a task management function to an SSP device.
4584*4c06356bSdh  *
4585*4c06356bSdh  * Called with PHY lock held.
4586*4c06356bSdh  * statlock CANNOT be held upon entry.
4587*4c06356bSdh  */
4588*4c06356bSdh int
4589*4c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
4590*4c06356bSdh     uint64_t lun, uint32_t *response)
4591*4c06356bSdh {
4592*4c06356bSdh 	int result, ds;
4593*4c06356bSdh 	uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
4594*4c06356bSdh 	sas_ssp_rsp_iu_t *rptr = (void *)local;
4595*4c06356bSdh 	static const uint8_t ssp_rsp_evec[] = {
4596*4c06356bSdh 		0x58, 0x61, 0x56, 0x72, 0x00
4597*4c06356bSdh 	};
4598*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
4599*4c06356bSdh 	struct pmcwork *pwrk;
4600*4c06356bSdh 	pmcs_xscsi_t *xp;
4601*4c06356bSdh 
4602*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
4603*4c06356bSdh 	if (pwrk == NULL) {
4604*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
4605*4c06356bSdh 		return (ENOMEM);
4606*4c06356bSdh 	}
4607*4c06356bSdh 	/*
4608*4c06356bSdh 	 * NB: We use the PMCS_OQ_GENERAL outbound queue
4609*4c06356bSdh 	 * NB: so as to not get entangled in normal I/O
4610*4c06356bSdh 	 * NB: processing.
4611*4c06356bSdh 	 */
4612*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
4613*4c06356bSdh 	    PMCIN_SSP_INI_TM_START));
4614*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
4615*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
4616*4c06356bSdh 	if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
4617*4c06356bSdh 		msg[3] = LE_32(tag);
4618*4c06356bSdh 	} else {
4619*4c06356bSdh 		msg[3] = 0;
4620*4c06356bSdh 	}
4621*4c06356bSdh 	msg[4] = LE_32(tmf);
4622*4c06356bSdh 	msg[5] = BE_32((uint32_t)lun);
4623*4c06356bSdh 	msg[6] = BE_32((uint32_t)(lun >> 32));
4624*4c06356bSdh 	msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
4625*4c06356bSdh 
4626*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4627*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4628*4c06356bSdh 	if (ptr == NULL) {
4629*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4630*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
4631*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
4632*4c06356bSdh 		return (ENOMEM);
4633*4c06356bSdh 	}
4634*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 7);
4635*4c06356bSdh 	pwrk->arg = msg;
4636*4c06356bSdh 	pwrk->dtype = pptr->dtype;
4637*4c06356bSdh 
4638*4c06356bSdh 	xp = pptr->target;
4639*4c06356bSdh 	if (xp != NULL) {
4640*4c06356bSdh 		mutex_enter(&xp->statlock);
4641*4c06356bSdh 		if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
4642*4c06356bSdh 			mutex_exit(&xp->statlock);
4643*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4644*4c06356bSdh 			pmcs_pwork(pwp, pwrk);
4645*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Not sending '%s'"
4646*4c06356bSdh 			    " because DS is '%s'", __func__, pmcs_tmf2str(tmf),
4647*4c06356bSdh 			    pmcs_status_str
4648*4c06356bSdh 			    (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
4649*4c06356bSdh 			return (EIO);
4650*4c06356bSdh 		}
4651*4c06356bSdh 		mutex_exit(&xp->statlock);
4652*4c06356bSdh 	}
4653*4c06356bSdh 
4654*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG,
4655*4c06356bSdh 	    "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
4656*4c06356bSdh 	    pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
4657*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
4658*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4659*4c06356bSdh 
4660*4c06356bSdh 	pmcs_unlock_phy(pptr);
4661*4c06356bSdh 	/*
4662*4c06356bSdh 	 * This is a command sent to the target device, so it can take
4663*4c06356bSdh 	 * significant amount of time to complete when path & device is busy.
4664*4c06356bSdh 	 * Set a timeout to 20 seconds
4665*4c06356bSdh 	 */
4666*4c06356bSdh 	WAIT_FOR(pwrk, 20000, result);
4667*4c06356bSdh 	pmcs_lock_phy(pptr);
4668*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
4669*4c06356bSdh 
4670*4c06356bSdh 	if (result) {
4671*4c06356bSdh 		if (xp == NULL) {
4672*4c06356bSdh 			return (ETIMEDOUT);
4673*4c06356bSdh 		}
4674*4c06356bSdh 
4675*4c06356bSdh 		mutex_enter(&xp->statlock);
4676*4c06356bSdh 		pmcs_start_dev_state_recovery(xp, pptr);
4677*4c06356bSdh 		mutex_exit(&xp->statlock);
4678*4c06356bSdh 		return (ETIMEDOUT);
4679*4c06356bSdh 	}
4680*4c06356bSdh 
4681*4c06356bSdh 	status = LE_32(msg[2]);
4682*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
4683*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4684*4c06356bSdh 		    "%s: status %s for TMF %s action to %s, lun %llu",
4685*4c06356bSdh 		    __func__, pmcs_status_str(status),  pmcs_tmf2str(tmf),
4686*4c06356bSdh 		    pptr->path, (unsigned long long) lun);
4687*4c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
4688*4c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
4689*4c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
4690*4c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
4691*4c06356bSdh 		} else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
4692*4c06356bSdh 			/*
4693*4c06356bSdh 			 * If the status is IN_RECOVERY, it's an indication
4694*4c06356bSdh 			 * that it's now time for us to request to have the
4695*4c06356bSdh 			 * device state set to OPERATIONAL since we're the ones
4696*4c06356bSdh 			 * that requested recovery to begin with.
4697*4c06356bSdh 			 */
4698*4c06356bSdh 			ds = PMCS_DEVICE_STATE_OPERATIONAL;
4699*4c06356bSdh 		} else {
4700*4c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
4701*4c06356bSdh 		}
4702*4c06356bSdh 		if (xp != NULL) {
4703*4c06356bSdh 			mutex_enter(&xp->statlock);
4704*4c06356bSdh 			if (xp->dev_state != ds) {
4705*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
4706*4c06356bSdh 				    "%s: Sending err recovery cmd"
4707*4c06356bSdh 				    " for tgt 0x%p (status = %s)",
4708*4c06356bSdh 				    __func__, (void *)xp,
4709*4c06356bSdh 				    pmcs_status_str(status));
4710*4c06356bSdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds, xp);
4711*4c06356bSdh 			}
4712*4c06356bSdh 			mutex_exit(&xp->statlock);
4713*4c06356bSdh 		}
4714*4c06356bSdh 		return (EIO);
4715*4c06356bSdh 	} else {
4716*4c06356bSdh 		ds = PMCS_DEVICE_STATE_OPERATIONAL;
4717*4c06356bSdh 		if (xp != NULL) {
4718*4c06356bSdh 			mutex_enter(&xp->statlock);
4719*4c06356bSdh 			if (xp->dev_state != ds) {
4720*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
4721*4c06356bSdh 				    "%s: Sending err recovery cmd"
4722*4c06356bSdh 				    " for tgt 0x%p (status = %s)",
4723*4c06356bSdh 				    __func__, (void *)xp,
4724*4c06356bSdh 				    pmcs_status_str(status));
4725*4c06356bSdh 				(void) pmcs_send_err_recovery_cmd(pwp, ds, xp);
4726*4c06356bSdh 			}
4727*4c06356bSdh 			mutex_exit(&xp->statlock);
4728*4c06356bSdh 		}
4729*4c06356bSdh 	}
4730*4c06356bSdh 	if (LE_32(msg[3]) == 0) {
4731*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "TMF completed with no response");
4732*4c06356bSdh 		return (EIO);
4733*4c06356bSdh 	}
4734*4c06356bSdh 	pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
4735*4c06356bSdh 	xd = (uint8_t *)(&msg[5]);
4736*4c06356bSdh 	xd += SAS_RSP_HDR_SIZE;
4737*4c06356bSdh 	if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
4738*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4739*4c06356bSdh 		    "%s: TMF response not RESPONSE DATA (0x%x)",
4740*4c06356bSdh 		    __func__, rptr->datapres);
4741*4c06356bSdh 		return (EIO);
4742*4c06356bSdh 	}
4743*4c06356bSdh 	if (rptr->response_data_length != 4) {
4744*4c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
4745*4c06356bSdh 		    "Bad SAS RESPONSE DATA LENGTH", msg);
4746*4c06356bSdh 		return (EIO);
4747*4c06356bSdh 	}
4748*4c06356bSdh 	(void) memcpy(&status, xd, sizeof (uint32_t));
4749*4c06356bSdh 	status = BE_32(status);
4750*4c06356bSdh 	if (response != NULL)
4751*4c06356bSdh 		*response = status;
4752*4c06356bSdh 	/*
4753*4c06356bSdh 	 * The status is actually in the low-order byte.  The upper three
4754*4c06356bSdh 	 * bytes contain additional information for the TMFs that support them.
4755*4c06356bSdh 	 * However, at this time we do not issue any of those.  In the other
4756*4c06356bSdh 	 * cases, the upper three bytes are supposed to be 0, but it appears
4757*4c06356bSdh 	 * they aren't always.  Just mask them off.
4758*4c06356bSdh 	 */
4759*4c06356bSdh 	switch (status & 0xff) {
4760*4c06356bSdh 	case SAS_RSP_TMF_COMPLETE:
4761*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF complete", __func__);
4762*4c06356bSdh 		result = 0;
4763*4c06356bSdh 		break;
4764*4c06356bSdh 	case SAS_RSP_TMF_SUCCEEDED:
4765*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF succeeded", __func__);
4766*4c06356bSdh 		result = 0;
4767*4c06356bSdh 		break;
4768*4c06356bSdh 	case SAS_RSP_INVALID_FRAME:
4769*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4770*4c06356bSdh 		    "%s: TMF returned INVALID FRAME", __func__);
4771*4c06356bSdh 		result = EIO;
4772*4c06356bSdh 		break;
4773*4c06356bSdh 	case SAS_RSP_TMF_NOT_SUPPORTED:
4774*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4775*4c06356bSdh 		    "%s: TMF returned TMF NOT SUPPORTED", __func__);
4776*4c06356bSdh 		result = EIO;
4777*4c06356bSdh 		break;
4778*4c06356bSdh 	case SAS_RSP_TMF_FAILED:
4779*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4780*4c06356bSdh 		    "%s: TMF returned TMF FAILED", __func__);
4781*4c06356bSdh 		result = EIO;
4782*4c06356bSdh 		break;
4783*4c06356bSdh 	case SAS_RSP_TMF_INCORRECT_LUN:
4784*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4785*4c06356bSdh 		    "%s: TMF returned INCORRECT LUN", __func__);
4786*4c06356bSdh 		result = EIO;
4787*4c06356bSdh 		break;
4788*4c06356bSdh 	case SAS_RSP_OVERLAPPED_OIPTTA:
4789*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4790*4c06356bSdh 		    "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
4791*4c06356bSdh 		    "ATTEMPTED", __func__);
4792*4c06356bSdh 		result = EIO;
4793*4c06356bSdh 		break;
4794*4c06356bSdh 	default:
4795*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
4796*4c06356bSdh 		    "%s: TMF returned unknown code 0x%x", __func__, status);
4797*4c06356bSdh 		result = EIO;
4798*4c06356bSdh 		break;
4799*4c06356bSdh 	}
4800*4c06356bSdh 	return (result);
4801*4c06356bSdh }
4802*4c06356bSdh 
4803*4c06356bSdh /*
4804*4c06356bSdh  * Called with PHY lock held and scratch acquired
4805*4c06356bSdh  */
4806*4c06356bSdh int
4807*4c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
4808*4c06356bSdh {
4809*4c06356bSdh 	const char *utag_fail_fmt = "%s: untagged NCQ command failure";
4810*4c06356bSdh 	const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
4811*4c06356bSdh 	uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
4812*4c06356bSdh 	uint8_t *fp = pwp->scratch, ds;
4813*4c06356bSdh 	fis_t fis;
4814*4c06356bSdh 	pmcwork_t *pwrk;
4815*4c06356bSdh 	pmcs_xscsi_t *tgt;
4816*4c06356bSdh 
4817*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
4818*4c06356bSdh 	if (pwrk == NULL) {
4819*4c06356bSdh 		return (ENOMEM);
4820*4c06356bSdh 	}
4821*4c06356bSdh 	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
4822*4c06356bSdh 	    PMCIN_SATA_HOST_IO_START));
4823*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
4824*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
4825*4c06356bSdh 	msg[3] = LE_32(512);
4826*4c06356bSdh 	msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
4827*4c06356bSdh 	msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
4828*4c06356bSdh 	msg[6] = LE_32(0x10);
4829*4c06356bSdh 	msg[8] = LE_32(1);
4830*4c06356bSdh 	msg[9] = 0;
4831*4c06356bSdh 	msg[10] = 0;
4832*4c06356bSdh 	msg[11] = 0;
4833*4c06356bSdh 	msg[12] = LE_32(DWORD0(pwp->scratch_dma));
4834*4c06356bSdh 	msg[13] = LE_32(DWORD1(pwp->scratch_dma));
4835*4c06356bSdh 	msg[14] = LE_32(512);
4836*4c06356bSdh 	msg[15] = 0;
4837*4c06356bSdh 
4838*4c06356bSdh 	pwrk->arg = msg;
4839*4c06356bSdh 	pwrk->dtype = pptr->dtype;
4840*4c06356bSdh 
4841*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4842*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4843*4c06356bSdh 	if (ptr == NULL) {
4844*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
4845*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
4846*4c06356bSdh 		return (ENOMEM);
4847*4c06356bSdh 	}
4848*4c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
4849*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
4850*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
4851*4c06356bSdh 
4852*4c06356bSdh 	pmcs_unlock_phy(pptr);
4853*4c06356bSdh 	WAIT_FOR(pwrk, 250, result);
4854*4c06356bSdh 	pmcs_lock_phy(pptr);
4855*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
4856*4c06356bSdh 
4857*4c06356bSdh 	if (result) {
4858*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, pmcs_timeo, __func__);
4859*4c06356bSdh 		return (EIO);
4860*4c06356bSdh 	}
4861*4c06356bSdh 	status = LE_32(msg[2]);
4862*4c06356bSdh 	if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
4863*4c06356bSdh 		tgt = pptr->target;
4864*4c06356bSdh 		if (tgt == NULL) {
4865*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
4866*4c06356bSdh 			    "%s: cannot find target for phy 0x%p for "
4867*4c06356bSdh 			    "dev state recovery", __func__, (void *)pptr);
4868*4c06356bSdh 			return (EIO);
4869*4c06356bSdh 		}
4870*4c06356bSdh 
4871*4c06356bSdh 		mutex_enter(&tgt->statlock);
4872*4c06356bSdh 
4873*4c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
4874*4c06356bSdh 		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
4875*4c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
4876*4c06356bSdh 		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
4877*4c06356bSdh 			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
4878*4c06356bSdh 		} else {
4879*4c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
4880*4c06356bSdh 		}
4881*4c06356bSdh 		if (tgt->dev_state != ds) {
4882*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Trying SATA DS Error"
4883*4c06356bSdh 			    " Recovery for tgt(0x%p) for status(%s)",
4884*4c06356bSdh 			    __func__, (void *)tgt, pmcs_status_str(status));
4885*4c06356bSdh 			(void) pmcs_send_err_recovery_cmd(pwp, ds, tgt);
4886*4c06356bSdh 		}
4887*4c06356bSdh 
4888*4c06356bSdh 		mutex_exit(&tgt->statlock);
4889*4c06356bSdh 		return (EIO);
4890*4c06356bSdh 	}
4891*4c06356bSdh 	fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
4892*4c06356bSdh 	fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
4893*4c06356bSdh 	fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
4894*4c06356bSdh 	fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
4895*4c06356bSdh 	fis[4] = 0;
4896*4c06356bSdh 	if (fp[0] & 0x80) {
4897*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, utag_fail_fmt, __func__);
4898*4c06356bSdh 	} else {
4899*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, tag_fail_fmt, __func__,
4900*4c06356bSdh 		    fp[0] & 0x1f);
4901*4c06356bSdh 	}
4902*4c06356bSdh 	pmcs_fis_dump(pwp, fis);
4903*4c06356bSdh 	pptr->need_rl_ext = 0;
4904*4c06356bSdh 	return (0);
4905*4c06356bSdh }
4906*4c06356bSdh 
4907*4c06356bSdh /*
4908*4c06356bSdh  * Transform a structure from CPU to Device endian format, or
4909*4c06356bSdh  * vice versa, based upon a transformation vector.
4910*4c06356bSdh  *
4911*4c06356bSdh  * A transformation vector is an array of bytes, each byte
4912*4c06356bSdh  * of which is defined thusly:
4913*4c06356bSdh  *
4914*4c06356bSdh  *  bit 7: from CPU to desired endian, otherwise from desired endian
4915*4c06356bSdh  *	   to CPU format
4916*4c06356bSdh  *  bit 6: Big Endian, else Little Endian
4917*4c06356bSdh  *  bits 5-4:
4918*4c06356bSdh  *       00 Undefined
4919*4c06356bSdh  *       01 One Byte quantities
4920*4c06356bSdh  *       02 Two Byte quantities
4921*4c06356bSdh  *       03 Four Byte quantities
4922*4c06356bSdh  *
4923*4c06356bSdh  *  bits 3-0:
4924*4c06356bSdh  *       00 Undefined
4925*4c06356bSdh  *       Number of quantities to transform
4926*4c06356bSdh  *
4927*4c06356bSdh  * The vector is terminated by a 0 value.
4928*4c06356bSdh  */
4929*4c06356bSdh 
4930*4c06356bSdh void
4931*4c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
4932*4c06356bSdh     const uint8_t *xfvec)
4933*4c06356bSdh {
4934*4c06356bSdh 	uint8_t c, *out = orig_out, *in = orig_in;
4935*4c06356bSdh 
4936*4c06356bSdh 	if (xfvec == NULL) {
4937*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null xfvec", __func__);
4938*4c06356bSdh 		return;
4939*4c06356bSdh 	}
4940*4c06356bSdh 	if (out == NULL) {
4941*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null out", __func__);
4942*4c06356bSdh 		return;
4943*4c06356bSdh 	}
4944*4c06356bSdh 	if (in == NULL) {
4945*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null in", __func__);
4946*4c06356bSdh 		return;
4947*4c06356bSdh 	}
4948*4c06356bSdh 	while ((c = *xfvec++) != 0) {
4949*4c06356bSdh 		int nbyt = (c & 0xf);
4950*4c06356bSdh 		int size = (c >> 4) & 0x3;
4951*4c06356bSdh 		int bige = (c >> 4) & 0x4;
4952*4c06356bSdh 
4953*4c06356bSdh 		switch (size) {
4954*4c06356bSdh 		case 1:
4955*4c06356bSdh 		{
4956*4c06356bSdh 			while (nbyt-- > 0) {
4957*4c06356bSdh 				*out++ = *in++;
4958*4c06356bSdh 			}
4959*4c06356bSdh 			break;
4960*4c06356bSdh 		}
4961*4c06356bSdh 		case 2:
4962*4c06356bSdh 		{
4963*4c06356bSdh 			uint16_t tmp;
4964*4c06356bSdh 			while (nbyt-- > 0) {
4965*4c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint16_t));
4966*4c06356bSdh 				if (bige) {
4967*4c06356bSdh 					tmp = BE_16(tmp);
4968*4c06356bSdh 				} else {
4969*4c06356bSdh 					tmp = LE_16(tmp);
4970*4c06356bSdh 				}
4971*4c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint16_t));
4972*4c06356bSdh 				out += sizeof (uint16_t);
4973*4c06356bSdh 				in += sizeof (uint16_t);
4974*4c06356bSdh 			}
4975*4c06356bSdh 			break;
4976*4c06356bSdh 		}
4977*4c06356bSdh 		case 3:
4978*4c06356bSdh 		{
4979*4c06356bSdh 			uint32_t tmp;
4980*4c06356bSdh 			while (nbyt-- > 0) {
4981*4c06356bSdh 				(void) memcpy(&tmp, in, sizeof (uint32_t));
4982*4c06356bSdh 				if (bige) {
4983*4c06356bSdh 					tmp = BE_32(tmp);
4984*4c06356bSdh 				} else {
4985*4c06356bSdh 					tmp = LE_32(tmp);
4986*4c06356bSdh 				}
4987*4c06356bSdh 				(void) memcpy(out, &tmp, sizeof (uint32_t));
4988*4c06356bSdh 				out += sizeof (uint32_t);
4989*4c06356bSdh 				in += sizeof (uint32_t);
4990*4c06356bSdh 			}
4991*4c06356bSdh 			break;
4992*4c06356bSdh 		}
4993*4c06356bSdh 		default:
4994*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad size", __func__);
4995*4c06356bSdh 			return;
4996*4c06356bSdh 		}
4997*4c06356bSdh 	}
4998*4c06356bSdh }
4999*4c06356bSdh 
5000*4c06356bSdh const char *
5001*4c06356bSdh pmcs_get_rate(unsigned int linkrt)
5002*4c06356bSdh {
5003*4c06356bSdh 	const char *rate;
5004*4c06356bSdh 	switch (linkrt) {
5005*4c06356bSdh 	case SAS_LINK_RATE_1_5GBIT:
5006*4c06356bSdh 		rate = "1.5";
5007*4c06356bSdh 		break;
5008*4c06356bSdh 	case SAS_LINK_RATE_3GBIT:
5009*4c06356bSdh 		rate = "3.0";
5010*4c06356bSdh 		break;
5011*4c06356bSdh 	case SAS_LINK_RATE_6GBIT:
5012*4c06356bSdh 		rate = "6.0";
5013*4c06356bSdh 		break;
5014*4c06356bSdh 	default:
5015*4c06356bSdh 		rate = "???";
5016*4c06356bSdh 		break;
5017*4c06356bSdh 	}
5018*4c06356bSdh 	return (rate);
5019*4c06356bSdh }
5020*4c06356bSdh 
5021*4c06356bSdh const char *
5022*4c06356bSdh pmcs_get_typename(pmcs_dtype_t type)
5023*4c06356bSdh {
5024*4c06356bSdh 	switch (type) {
5025*4c06356bSdh 	case NOTHING:
5026*4c06356bSdh 		return ("NIL");
5027*4c06356bSdh 	case SATA:
5028*4c06356bSdh 		return ("SATA");
5029*4c06356bSdh 	case SAS:
5030*4c06356bSdh 		return ("SSP");
5031*4c06356bSdh 	case EXPANDER:
5032*4c06356bSdh 		return ("EXPANDER");
5033*4c06356bSdh 	}
5034*4c06356bSdh 	return ("????");
5035*4c06356bSdh }
5036*4c06356bSdh 
5037*4c06356bSdh const char *
5038*4c06356bSdh pmcs_tmf2str(int tmf)
5039*4c06356bSdh {
5040*4c06356bSdh 	switch (tmf) {
5041*4c06356bSdh 	case SAS_ABORT_TASK:
5042*4c06356bSdh 		return ("Abort Task");
5043*4c06356bSdh 	case SAS_ABORT_TASK_SET:
5044*4c06356bSdh 		return ("Abort Task Set");
5045*4c06356bSdh 	case SAS_CLEAR_TASK_SET:
5046*4c06356bSdh 		return ("Clear Task Set");
5047*4c06356bSdh 	case SAS_LOGICAL_UNIT_RESET:
5048*4c06356bSdh 		return ("Logical Unit Reset");
5049*4c06356bSdh 	case SAS_I_T_NEXUS_RESET:
5050*4c06356bSdh 		return ("I_T Nexus Reset");
5051*4c06356bSdh 	case SAS_CLEAR_ACA:
5052*4c06356bSdh 		return ("Clear ACA");
5053*4c06356bSdh 	case SAS_QUERY_TASK:
5054*4c06356bSdh 		return ("Query Task");
5055*4c06356bSdh 	case SAS_QUERY_TASK_SET:
5056*4c06356bSdh 		return ("Query Task Set");
5057*4c06356bSdh 	case SAS_QUERY_UNIT_ATTENTION:
5058*4c06356bSdh 		return ("Query Unit Attention");
5059*4c06356bSdh 	default:
5060*4c06356bSdh 		return ("Unknown");
5061*4c06356bSdh 	}
5062*4c06356bSdh }
5063*4c06356bSdh 
5064*4c06356bSdh const char *
5065*4c06356bSdh pmcs_status_str(uint32_t status)
5066*4c06356bSdh {
5067*4c06356bSdh 	switch (status) {
5068*4c06356bSdh 	case PMCOUT_STATUS_OK:
5069*4c06356bSdh 		return ("OK");
5070*4c06356bSdh 	case PMCOUT_STATUS_ABORTED:
5071*4c06356bSdh 		return ("ABORTED");
5072*4c06356bSdh 	case PMCOUT_STATUS_OVERFLOW:
5073*4c06356bSdh 		return ("OVERFLOW");
5074*4c06356bSdh 	case PMCOUT_STATUS_UNDERFLOW:
5075*4c06356bSdh 		return ("UNDERFLOW");
5076*4c06356bSdh 	case PMCOUT_STATUS_FAILED:
5077*4c06356bSdh 		return ("FAILED");
5078*4c06356bSdh 	case PMCOUT_STATUS_ABORT_RESET:
5079*4c06356bSdh 		return ("ABORT_RESET");
5080*4c06356bSdh 	case PMCOUT_STATUS_IO_NOT_VALID:
5081*4c06356bSdh 		return ("IO_NOT_VALID");
5082*4c06356bSdh 	case PMCOUT_STATUS_NO_DEVICE:
5083*4c06356bSdh 		return ("NO_DEVICE");
5084*4c06356bSdh 	case PMCOUT_STATUS_ILLEGAL_PARAMETER:
5085*4c06356bSdh 		return ("ILLEGAL_PARAMETER");
5086*4c06356bSdh 	case PMCOUT_STATUS_LINK_FAILURE:
5087*4c06356bSdh 		return ("LINK_FAILURE");
5088*4c06356bSdh 	case PMCOUT_STATUS_PROG_ERROR:
5089*4c06356bSdh 		return ("PROG_ERROR");
5090*4c06356bSdh 	case PMCOUT_STATUS_EDC_IN_ERROR:
5091*4c06356bSdh 		return ("EDC_IN_ERROR");
5092*4c06356bSdh 	case PMCOUT_STATUS_EDC_OUT_ERROR:
5093*4c06356bSdh 		return ("EDC_OUT_ERROR");
5094*4c06356bSdh 	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
5095*4c06356bSdh 		return ("ERROR_HW_TIMEOUT");
5096*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_BREAK:
5097*4c06356bSdh 		return ("XFER_ERR_BREAK");
5098*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
5099*4c06356bSdh 		return ("XFER_ERR_PHY_NOT_READY");
5100*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
5101*4c06356bSdh 		return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
5102*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
5103*4c06356bSdh 		return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
5104*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
5105*4c06356bSdh 		return ("OPEN_CNX_ERROR_BREAK");
5106*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
5107*4c06356bSdh 		return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
5108*4c06356bSdh 	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
5109*4c06356bSdh 		return ("OPENCNX_ERROR_BAD_DESTINATION");
5110*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
5111*4c06356bSdh 		return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
5112*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
5113*4c06356bSdh 		return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
5114*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
5115*4c06356bSdh 		return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
5116*4c06356bSdh 	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR:
5117*4c06356bSdh 		return ("OPEN_CNX_ERROR_UNKNOWN_EROOR");
5118*4c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
5119*4c06356bSdh 		return ("IO_XFER_ERROR_NAK_RECEIVED");
5120*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
5121*4c06356bSdh 		return ("XFER_ERROR_ACK_NAK_TIMEOUT");
5122*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
5123*4c06356bSdh 		return ("XFER_ERROR_PEER_ABORTED");
5124*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
5125*4c06356bSdh 		return ("XFER_ERROR_RX_FRAME");
5126*4c06356bSdh 	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
5127*4c06356bSdh 		return ("IO_XFER_ERROR_DMA");
5128*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
5129*4c06356bSdh 		return ("XFER_ERROR_CREDIT_TIMEOUT");
5130*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
5131*4c06356bSdh 		return ("XFER_ERROR_SATA_LINK_TIMEOUT");
5132*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_SATA:
5133*4c06356bSdh 		return ("XFER_ERROR_SATA");
5134*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
5135*4c06356bSdh 		return ("XFER_ERROR_REJECTED_NCQ_MODE");
5136*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
5137*4c06356bSdh 		return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
5138*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
5139*4c06356bSdh 		return ("XFER_ERROR_ABORTED_NCQ_MODE");
5140*4c06356bSdh 	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
5141*4c06356bSdh 		return ("IO_XFER_OPEN_RETRY_TIMEOUT");
5142*4c06356bSdh 	case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
5143*4c06356bSdh 		return ("SMP_RESP_CONNECTION_ERROR");
5144*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
5145*4c06356bSdh 		return ("XFER_ERROR_UNEXPECTED_PHASE");
5146*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
5147*4c06356bSdh 		return ("XFER_ERROR_RDY_OVERRUN");
5148*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
5149*4c06356bSdh 		return ("XFER_ERROR_RDY_NOT_EXPECTED");
5150*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
5151*4c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
5152*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
5153*4c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
5154*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
5155*4c06356bSdh 		return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
5156*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
5157*4c06356bSdh 		return ("XFER_ERROR_OFFSET_MISMATCH");
5158*4c06356bSdh 	case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
5159*4c06356bSdh 		return ("XFER_ERROR_ZERO_DATA_LEN");
5160*4c06356bSdh 	case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
5161*4c06356bSdh 		return ("XFER_CMD_FRAME_ISSUED");
5162*4c06356bSdh 	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
5163*4c06356bSdh 		return ("ERROR_INTERNAL_SMP_RESOURCE");
5164*4c06356bSdh 	case PMCOUT_STATUS_IO_PORT_IN_RESET:
5165*4c06356bSdh 		return ("IO_PORT_IN_RESET");
5166*4c06356bSdh 	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
5167*4c06356bSdh 		return ("DEVICE STATE NON-OPERATIONAL");
5168*4c06356bSdh 	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
5169*4c06356bSdh 		return ("DEVICE STATE IN RECOVERY");
5170*4c06356bSdh 	default:
5171*4c06356bSdh 		return (NULL);
5172*4c06356bSdh 	}
5173*4c06356bSdh }
5174*4c06356bSdh 
5175*4c06356bSdh uint64_t
5176*4c06356bSdh pmcs_barray2wwn(uint8_t ba[8])
5177*4c06356bSdh {
5178*4c06356bSdh 	uint64_t result = 0;
5179*4c06356bSdh 	int i;
5180*4c06356bSdh 
5181*4c06356bSdh 	for (i = 0; i < 8; i++) {
5182*4c06356bSdh 		result <<= 8;
5183*4c06356bSdh 		result |= ba[i];
5184*4c06356bSdh 	}
5185*4c06356bSdh 	return (result);
5186*4c06356bSdh }
5187*4c06356bSdh 
5188*4c06356bSdh void
5189*4c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
5190*4c06356bSdh {
5191*4c06356bSdh 	int i;
5192*4c06356bSdh 	for (i = 0; i < 8; i++) {
5193*4c06356bSdh 		ba[7 - i] = wwn & 0xff;
5194*4c06356bSdh 		wwn >>= 8;
5195*4c06356bSdh 	}
5196*4c06356bSdh }
5197*4c06356bSdh 
5198*4c06356bSdh void
5199*4c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp)
5200*4c06356bSdh {
5201*4c06356bSdh 	const char *fwsupport;
5202*4c06356bSdh 	switch (PMCS_FW_TYPE(pwp)) {
5203*4c06356bSdh 	case PMCS_FW_TYPE_RELEASED:
5204*4c06356bSdh 		fwsupport = "Released";
5205*4c06356bSdh 		break;
5206*4c06356bSdh 	case PMCS_FW_TYPE_DEVELOPMENT:
5207*4c06356bSdh 		fwsupport = "Development";
5208*4c06356bSdh 		break;
5209*4c06356bSdh 	case PMCS_FW_TYPE_ALPHA:
5210*4c06356bSdh 		fwsupport = "Alpha";
5211*4c06356bSdh 		break;
5212*4c06356bSdh 	case PMCS_FW_TYPE_BETA:
5213*4c06356bSdh 		fwsupport = "Beta";
5214*4c06356bSdh 		break;
5215*4c06356bSdh 	default:
5216*4c06356bSdh 		fwsupport = "Special";
5217*4c06356bSdh 		break;
5218*4c06356bSdh 	}
5219*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO,
5220*4c06356bSdh 	    "Chip Revision: %c; F/W Revision %x.%x.%x %s", 'A' + pwp->chiprev,
5221*4c06356bSdh 	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
5222*4c06356bSdh 	    fwsupport);
5223*4c06356bSdh }
5224*4c06356bSdh 
5225*4c06356bSdh void
5226*4c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
5227*4c06356bSdh {
5228*4c06356bSdh 	if (pptr->parent) {
5229*4c06356bSdh 		pmcs_phy_name(pwp, pptr->parent, obuf, olen);
5230*4c06356bSdh 		(void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
5231*4c06356bSdh 	} else {
5232*4c06356bSdh 		(void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
5233*4c06356bSdh 	}
5234*4c06356bSdh }
5235*4c06356bSdh 
5236*4c06356bSdh /*
5237*4c06356bSdh  * Implementation for pmcs_find_phy_by_devid.
5238*4c06356bSdh  * If the PHY is found, it is returned locked.
5239*4c06356bSdh  */
5240*4c06356bSdh static pmcs_phy_t *
5241*4c06356bSdh pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id)
5242*4c06356bSdh {
5243*4c06356bSdh 	pmcs_phy_t *match, *cphyp, *nphyp;
5244*4c06356bSdh 
5245*4c06356bSdh 	ASSERT(!mutex_owned(&phyp->phy_lock));
5246*4c06356bSdh 
5247*4c06356bSdh 	while (phyp) {
5248*4c06356bSdh 		pmcs_lock_phy(phyp);
5249*4c06356bSdh 
5250*4c06356bSdh 		if ((phyp->valid_device_id) && (phyp->device_id == device_id)) {
5251*4c06356bSdh 			return (phyp);
5252*4c06356bSdh 		}
5253*4c06356bSdh 		if (phyp->children) {
5254*4c06356bSdh 			cphyp = phyp->children;
5255*4c06356bSdh 			pmcs_unlock_phy(phyp);
5256*4c06356bSdh 			match = pmcs_find_phy_by_devid_impl(cphyp, device_id);
5257*4c06356bSdh 			if (match) {
5258*4c06356bSdh 				ASSERT(mutex_owned(&match->phy_lock));
5259*4c06356bSdh 				return (match);
5260*4c06356bSdh 			}
5261*4c06356bSdh 			pmcs_lock_phy(phyp);
5262*4c06356bSdh 		}
5263*4c06356bSdh 
5264*4c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
5265*4c06356bSdh 			pmcs_unlock_phy(phyp);
5266*4c06356bSdh 			phyp = NULL;
5267*4c06356bSdh 		} else {
5268*4c06356bSdh 			nphyp = phyp->sibling;
5269*4c06356bSdh 			pmcs_unlock_phy(phyp);
5270*4c06356bSdh 			phyp = nphyp;
5271*4c06356bSdh 		}
5272*4c06356bSdh 	}
5273*4c06356bSdh 
5274*4c06356bSdh 	return (NULL);
5275*4c06356bSdh }
5276*4c06356bSdh 
5277*4c06356bSdh /*
5278*4c06356bSdh  * If the PHY is found, it is returned locked
5279*4c06356bSdh  */
5280*4c06356bSdh pmcs_phy_t *
5281*4c06356bSdh pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id)
5282*4c06356bSdh {
5283*4c06356bSdh 	pmcs_phy_t *phyp, *match = NULL;
5284*4c06356bSdh 
5285*4c06356bSdh 	phyp = pwp->root_phys;
5286*4c06356bSdh 
5287*4c06356bSdh 	while (phyp) {
5288*4c06356bSdh 		match = pmcs_find_phy_by_devid_impl(phyp, device_id);
5289*4c06356bSdh 		if (match) {
5290*4c06356bSdh 			ASSERT(mutex_owned(&match->phy_lock));
5291*4c06356bSdh 			return (match);
5292*4c06356bSdh 		}
5293*4c06356bSdh 		phyp = phyp->sibling;
5294*4c06356bSdh 	}
5295*4c06356bSdh 
5296*4c06356bSdh 	return (NULL);
5297*4c06356bSdh }
5298*4c06356bSdh 
5299*4c06356bSdh /*
5300*4c06356bSdh  * This function is called as a sanity check to ensure that a newly registered
5301*4c06356bSdh  * PHY doesn't have a device_id that exists with another registered PHY.
5302*4c06356bSdh  */
5303*4c06356bSdh static boolean_t
5304*4c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
5305*4c06356bSdh {
5306*4c06356bSdh 	pmcs_phy_t *pptr;
5307*4c06356bSdh 	boolean_t rval;
5308*4c06356bSdh 
5309*4c06356bSdh 	pptr = parent;
5310*4c06356bSdh 
5311*4c06356bSdh 	while (pptr) {
5312*4c06356bSdh 		if (pptr->valid_device_id && (pptr != phyp) &&
5313*4c06356bSdh 		    (pptr->device_id == device_id)) {
5314*4c06356bSdh 			pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG,
5315*4c06356bSdh 			    "%s: phy %s already exists as %s with "
5316*4c06356bSdh 			    "device id 0x%x", __func__, phyp->path,
5317*4c06356bSdh 			    pptr->path, device_id);
5318*4c06356bSdh 			return (B_FALSE);
5319*4c06356bSdh 		}
5320*4c06356bSdh 
5321*4c06356bSdh 		if (pptr->children) {
5322*4c06356bSdh 			rval = pmcs_validate_devid(pptr->children, phyp,
5323*4c06356bSdh 			    device_id);
5324*4c06356bSdh 			if (rval == B_FALSE) {
5325*4c06356bSdh 				return (rval);
5326*4c06356bSdh 			}
5327*4c06356bSdh 		}
5328*4c06356bSdh 
5329*4c06356bSdh 		pptr = pptr->sibling;
5330*4c06356bSdh 	}
5331*4c06356bSdh 
5332*4c06356bSdh 	/* This PHY and device_id are valid */
5333*4c06356bSdh 	return (B_TRUE);
5334*4c06356bSdh }
5335*4c06356bSdh 
5336*4c06356bSdh /*
5337*4c06356bSdh  * If the PHY is found, it is returned locked
5338*4c06356bSdh  */
5339*4c06356bSdh static pmcs_phy_t *
5340*4c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
5341*4c06356bSdh {
5342*4c06356bSdh 	pmcs_phy_t *matched_phy, *cphyp, *nphyp;
5343*4c06356bSdh 
5344*4c06356bSdh 	ASSERT(!mutex_owned(&phyp->phy_lock));
5345*4c06356bSdh 
5346*4c06356bSdh 	while (phyp) {
5347*4c06356bSdh 		pmcs_lock_phy(phyp);
5348*4c06356bSdh 
5349*4c06356bSdh 		if (phyp->valid_device_id) {
5350*4c06356bSdh 			if (memcmp(phyp->sas_address, wwn, 8) == 0) {
5351*4c06356bSdh 				return (phyp);
5352*4c06356bSdh 			}
5353*4c06356bSdh 		}
5354*4c06356bSdh 
5355*4c06356bSdh 		if (phyp->children) {
5356*4c06356bSdh 			cphyp = phyp->children;
5357*4c06356bSdh 			pmcs_unlock_phy(phyp);
5358*4c06356bSdh 			matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
5359*4c06356bSdh 			if (matched_phy) {
5360*4c06356bSdh 				ASSERT(mutex_owned(&matched_phy->phy_lock));
5361*4c06356bSdh 				return (matched_phy);
5362*4c06356bSdh 			}
5363*4c06356bSdh 			pmcs_lock_phy(phyp);
5364*4c06356bSdh 		}
5365*4c06356bSdh 
5366*4c06356bSdh 		/*
5367*4c06356bSdh 		 * Only iterate through non-root PHYs
5368*4c06356bSdh 		 */
5369*4c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
5370*4c06356bSdh 			pmcs_unlock_phy(phyp);
5371*4c06356bSdh 			phyp = NULL;
5372*4c06356bSdh 		} else {
5373*4c06356bSdh 			nphyp = phyp->sibling;
5374*4c06356bSdh 			pmcs_unlock_phy(phyp);
5375*4c06356bSdh 			phyp = nphyp;
5376*4c06356bSdh 		}
5377*4c06356bSdh 	}
5378*4c06356bSdh 
5379*4c06356bSdh 	return (NULL);
5380*4c06356bSdh }
5381*4c06356bSdh 
5382*4c06356bSdh pmcs_phy_t *
5383*4c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
5384*4c06356bSdh {
5385*4c06356bSdh 	uint8_t ebstr[8];
5386*4c06356bSdh 	pmcs_phy_t *pptr, *matched_phy;
5387*4c06356bSdh 
5388*4c06356bSdh 	pmcs_wwn2barray(wwn, ebstr);
5389*4c06356bSdh 
5390*4c06356bSdh 	pptr = pwp->root_phys;
5391*4c06356bSdh 	while (pptr) {
5392*4c06356bSdh 		matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
5393*4c06356bSdh 		if (matched_phy) {
5394*4c06356bSdh 			ASSERT(mutex_owned(&matched_phy->phy_lock));
5395*4c06356bSdh 			return (matched_phy);
5396*4c06356bSdh 		}
5397*4c06356bSdh 
5398*4c06356bSdh 		pptr = pptr->sibling;
5399*4c06356bSdh 	}
5400*4c06356bSdh 
5401*4c06356bSdh 	return (NULL);
5402*4c06356bSdh }
5403*4c06356bSdh 
5404*4c06356bSdh 
5405*4c06356bSdh /*
5406*4c06356bSdh  * pmcs_find_phy_by_sas_address
5407*4c06356bSdh  *
5408*4c06356bSdh  * Find a PHY that both matches "sas_addr" and is on "iport".
5409*4c06356bSdh  * If a matching PHY is found, it is returned locked.
5410*4c06356bSdh  */
5411*4c06356bSdh pmcs_phy_t *
5412*4c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
5413*4c06356bSdh     pmcs_phy_t *root, char *sas_addr)
5414*4c06356bSdh {
5415*4c06356bSdh 	int ua_form = 1;
5416*4c06356bSdh 	uint64_t wwn;
5417*4c06356bSdh 	char addr[PMCS_MAX_UA_SIZE];
5418*4c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pchild;
5419*4c06356bSdh 
5420*4c06356bSdh 	if (root == NULL) {
5421*4c06356bSdh 		pptr = pwp->root_phys;
5422*4c06356bSdh 	} else {
5423*4c06356bSdh 		pptr = root;
5424*4c06356bSdh 	}
5425*4c06356bSdh 
5426*4c06356bSdh 	while (pptr) {
5427*4c06356bSdh 		pmcs_lock_phy(pptr);
5428*4c06356bSdh 		/*
5429*4c06356bSdh 		 * If the PHY is dead or does not have a valid device ID,
5430*4c06356bSdh 		 * skip it.
5431*4c06356bSdh 		 */
5432*4c06356bSdh 		if ((pptr->dead) || (!pptr->valid_device_id)) {
5433*4c06356bSdh 			goto next_phy;
5434*4c06356bSdh 		}
5435*4c06356bSdh 
5436*4c06356bSdh 		if (pptr->iport != iport) {
5437*4c06356bSdh 			goto next_phy;
5438*4c06356bSdh 		}
5439*4c06356bSdh 
5440*4c06356bSdh 		wwn = pmcs_barray2wwn(pptr->sas_address);
5441*4c06356bSdh 		(void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
5442*4c06356bSdh 		if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
5443*4c06356bSdh 			return (pptr);
5444*4c06356bSdh 		}
5445*4c06356bSdh 
5446*4c06356bSdh 		if (pptr->children) {
5447*4c06356bSdh 			pchild = pptr->children;
5448*4c06356bSdh 			pmcs_unlock_phy(pptr);
5449*4c06356bSdh 			pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
5450*4c06356bSdh 			    sas_addr);
5451*4c06356bSdh 			if (pnext) {
5452*4c06356bSdh 				return (pnext);
5453*4c06356bSdh 			}
5454*4c06356bSdh 			pmcs_lock_phy(pptr);
5455*4c06356bSdh 		}
5456*4c06356bSdh 
5457*4c06356bSdh next_phy:
5458*4c06356bSdh 		pnext = pptr->sibling;
5459*4c06356bSdh 		pmcs_unlock_phy(pptr);
5460*4c06356bSdh 		pptr = pnext;
5461*4c06356bSdh 	}
5462*4c06356bSdh 
5463*4c06356bSdh 	return (NULL);
5464*4c06356bSdh }
5465*4c06356bSdh 
5466*4c06356bSdh void
5467*4c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
5468*4c06356bSdh {
5469*4c06356bSdh 	switch (fis[0] & 0xff) {
5470*4c06356bSdh 	case FIS_REG_H2DEV:
5471*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER HOST TO DEVICE: "
5472*4c06356bSdh 		    "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
5473*4c06356bSdh 		    "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
5474*4c06356bSdh 		    BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
5475*4c06356bSdh 		    (unsigned long long)
5476*4c06356bSdh 		    (((uint64_t)fis[2] & 0x00ffffff) << 24 |
5477*4c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
5478*4c06356bSdh 		break;
5479*4c06356bSdh 	case FIS_REG_D2H:
5480*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER DEVICE TO HOST: Stat"
5481*4c06356bSdh 		    "us=0x%02x Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
5482*4c06356bSdh 		    BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
5483*4c06356bSdh 		    (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
5484*4c06356bSdh 		    ((uint64_t)fis[1] & 0x00ffffff)));
5485*4c06356bSdh 		break;
5486*4c06356bSdh 	default:
5487*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS: 0x%08x 0x%08x 0x%08x 0x%08x "
5488*4c06356bSdh 		    "0x%08x 0x%08x 0x%08x",
5489*4c06356bSdh 		    fis[0], fis[1], fis[2], fis[3], fis[4], fis[5], fis[6]);
5490*4c06356bSdh 		break;
5491*4c06356bSdh 	}
5492*4c06356bSdh }
5493*4c06356bSdh 
5494*4c06356bSdh void
5495*4c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
5496*4c06356bSdh {
5497*4c06356bSdh 	uint32_t *mb = arg;
5498*4c06356bSdh 	size_t i;
5499*4c06356bSdh 
5500*4c06356bSdh 	pmcs_prt(pwp, level, msg);
5501*4c06356bSdh 	for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
5502*4c06356bSdh 		pmcs_prt(pwp, level, "Offset %2lu: 0x%08x 0x%08x 0x%08"
5503*4c06356bSdh 		    "x 0x%08x", i * sizeof (uint32_t), LE_32(mb[i]),
5504*4c06356bSdh 		    LE_32(mb[i+1]), LE_32(mb[i+2]),
5505*4c06356bSdh 		    LE_32(mb[i+3]));
5506*4c06356bSdh 	}
5507*4c06356bSdh }
5508*4c06356bSdh 
5509*4c06356bSdh /*
5510*4c06356bSdh  * If phyp == NULL we're being called from the worker thread, in which
5511*4c06356bSdh  * case we need to check all the PHYs.  In this case, the softstate lock
5512*4c06356bSdh  * will be held.
5513*4c06356bSdh  * If phyp is non-NULL, just issue the spinup release for the specified PHY
5514*4c06356bSdh  * (which will already be locked).
5515*4c06356bSdh  */
5516*4c06356bSdh void
5517*4c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
5518*4c06356bSdh {
5519*4c06356bSdh 	uint32_t *msg;
5520*4c06356bSdh 	struct pmcwork *pwrk;
5521*4c06356bSdh 	pmcs_phy_t *tphyp;
5522*4c06356bSdh 
5523*4c06356bSdh 	if (phyp != NULL) {
5524*4c06356bSdh 		ASSERT(mutex_owned(&phyp->phy_lock));
5525*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
5526*4c06356bSdh 		    "%s: Issuing spinup release only for PHY %s", __func__,
5527*4c06356bSdh 		    phyp->path);
5528*4c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5529*4c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5530*4c06356bSdh 		if (msg == NULL || (pwrk =
5531*4c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
5532*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5533*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
5534*4c06356bSdh 			return;
5535*4c06356bSdh 		}
5536*4c06356bSdh 
5537*4c06356bSdh 		phyp->spinup_hold = 0;
5538*4c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
5539*4c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
5540*4c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
5541*4c06356bSdh 		msg[1] = LE_32(pwrk->htag);
5542*4c06356bSdh 		msg[2] = LE_32((0x10 << 8) | phyp->phynum);
5543*4c06356bSdh 
5544*4c06356bSdh 		pwrk->dtype = phyp->dtype;
5545*4c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5546*4c06356bSdh 		mutex_exit(&pwrk->lock);
5547*4c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5548*4c06356bSdh 		return;
5549*4c06356bSdh 	}
5550*4c06356bSdh 
5551*4c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
5552*4c06356bSdh 
5553*4c06356bSdh 	tphyp = pwp->root_phys;
5554*4c06356bSdh 	while (tphyp) {
5555*4c06356bSdh 		pmcs_lock_phy(tphyp);
5556*4c06356bSdh 		if (tphyp->spinup_hold == 0) {
5557*4c06356bSdh 			pmcs_unlock_phy(tphyp);
5558*4c06356bSdh 			tphyp = tphyp->sibling;
5559*4c06356bSdh 			continue;
5560*4c06356bSdh 		}
5561*4c06356bSdh 
5562*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
5563*4c06356bSdh 		    "%s: Issuing spinup release for PHY %s", __func__,
5564*4c06356bSdh 		    phyp->path);
5565*4c06356bSdh 
5566*4c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5567*4c06356bSdh 		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5568*4c06356bSdh 		if (msg == NULL || (pwrk =
5569*4c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
5570*4c06356bSdh 			pmcs_unlock_phy(tphyp);
5571*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5572*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
5573*4c06356bSdh 			break;
5574*4c06356bSdh 		}
5575*4c06356bSdh 
5576*4c06356bSdh 		tphyp->spinup_hold = 0;
5577*4c06356bSdh 		bzero(msg, PMCS_QENTRY_SIZE);
5578*4c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
5579*4c06356bSdh 		    PMCIN_LOCAL_PHY_CONTROL));
5580*4c06356bSdh 		msg[1] = LE_32(pwrk->htag);
5581*4c06356bSdh 		msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
5582*4c06356bSdh 
5583*4c06356bSdh 		pwrk->dtype = phyp->dtype;
5584*4c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5585*4c06356bSdh 		mutex_exit(&pwrk->lock);
5586*4c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5587*4c06356bSdh 		pmcs_unlock_phy(tphyp);
5588*4c06356bSdh 
5589*4c06356bSdh 		tphyp = tphyp->sibling;
5590*4c06356bSdh 	}
5591*4c06356bSdh }
5592*4c06356bSdh 
5593*4c06356bSdh /*
5594*4c06356bSdh  * Abort commands on dead PHYs and deregister them as well as removing
5595*4c06356bSdh  * the associated targets.
5596*4c06356bSdh  */
5597*4c06356bSdh static int
5598*4c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
5599*4c06356bSdh {
5600*4c06356bSdh 	pmcs_phy_t *pnext, *pchild;
5601*4c06356bSdh 	boolean_t remove_device;
5602*4c06356bSdh 	int rval = 0;
5603*4c06356bSdh 
5604*4c06356bSdh 	while (phyp) {
5605*4c06356bSdh 		pmcs_lock_phy(phyp);
5606*4c06356bSdh 		pchild = phyp->children;
5607*4c06356bSdh 		pnext = phyp->sibling;
5608*4c06356bSdh 		pmcs_unlock_phy(phyp);
5609*4c06356bSdh 
5610*4c06356bSdh 		if (pchild) {
5611*4c06356bSdh 			rval = pmcs_kill_devices(pwp, pchild);
5612*4c06356bSdh 			if (rval) {
5613*4c06356bSdh 				return (rval);
5614*4c06356bSdh 			}
5615*4c06356bSdh 		}
5616*4c06356bSdh 
5617*4c06356bSdh 		/*
5618*4c06356bSdh 		 * pmcs_remove_device requires the softstate lock.
5619*4c06356bSdh 		 */
5620*4c06356bSdh 		mutex_enter(&pwp->lock);
5621*4c06356bSdh 		pmcs_lock_phy(phyp);
5622*4c06356bSdh 		if (phyp->dead && phyp->valid_device_id) {
5623*4c06356bSdh 			remove_device = B_TRUE;
5624*4c06356bSdh 		} else {
5625*4c06356bSdh 			remove_device = B_FALSE;
5626*4c06356bSdh 		}
5627*4c06356bSdh 
5628*4c06356bSdh 		if (remove_device) {
5629*4c06356bSdh 			pmcs_remove_device(pwp, phyp);
5630*4c06356bSdh 			mutex_exit(&pwp->lock);
5631*4c06356bSdh 
5632*4c06356bSdh 			rval = pmcs_kill_device(pwp, phyp);
5633*4c06356bSdh 
5634*4c06356bSdh 			if (rval) {
5635*4c06356bSdh 				pmcs_unlock_phy(phyp);
5636*4c06356bSdh 				return (rval);
5637*4c06356bSdh 			}
5638*4c06356bSdh 		} else {
5639*4c06356bSdh 			mutex_exit(&pwp->lock);
5640*4c06356bSdh 		}
5641*4c06356bSdh 
5642*4c06356bSdh 		pmcs_unlock_phy(phyp);
5643*4c06356bSdh 		phyp = pnext;
5644*4c06356bSdh 	}
5645*4c06356bSdh 
5646*4c06356bSdh 	return (rval);
5647*4c06356bSdh }
5648*4c06356bSdh 
5649*4c06356bSdh /*
5650*4c06356bSdh  * Called with PHY locked
5651*4c06356bSdh  */
5652*4c06356bSdh int
5653*4c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
5654*4c06356bSdh {
5655*4c06356bSdh 	int r, result;
5656*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
5657*4c06356bSdh 	struct pmcwork *pwrk;
5658*4c06356bSdh 	pmcs_xscsi_t *tgt;
5659*4c06356bSdh 
5660*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, "kill %s device @ %s",
5661*4c06356bSdh 	    pmcs_get_typename(pptr->dtype), pptr->path);
5662*4c06356bSdh 
5663*4c06356bSdh 	/*
5664*4c06356bSdh 	 * There may be an outstanding ABORT_ALL running, which we wouldn't
5665*4c06356bSdh 	 * know just by checking abort_pending.  We can, however, check
5666*4c06356bSdh 	 * abort_all_start.  If it's non-zero, there is one, and we'll just
5667*4c06356bSdh 	 * sit here and wait for it to complete.  If we don't, we'll remove
5668*4c06356bSdh 	 * the device while there are still commands pending.
5669*4c06356bSdh 	 */
5670*4c06356bSdh 	if (pptr->abort_all_start) {
5671*4c06356bSdh 		while (pptr->abort_all_start) {
5672*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
5673*4c06356bSdh 			    "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
5674*4c06356bSdh 			    __func__, (void *)pptr);
5675*4c06356bSdh 			cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
5676*4c06356bSdh 		}
5677*4c06356bSdh 	} else if (pptr->abort_pending) {
5678*4c06356bSdh 		r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
5679*4c06356bSdh 
5680*4c06356bSdh 		if (r) {
5681*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
5682*4c06356bSdh 			    "%s: ABORT_ALL returned non-zero status (%d) for "
5683*4c06356bSdh 			    "PHY 0x%p", __func__, r, (void *)pptr);
5684*4c06356bSdh 			return (r);
5685*4c06356bSdh 		}
5686*4c06356bSdh 		pptr->abort_pending = 0;
5687*4c06356bSdh 	}
5688*4c06356bSdh 
5689*4c06356bSdh 	/*
5690*4c06356bSdh 	 * Now that everything is aborted from the chip's perspective (or even
5691*4c06356bSdh 	 * if it is not), flush out the wait queue.  We won't flush the active
5692*4c06356bSdh 	 * queue since it is possible that abort completions may follow after
5693*4c06356bSdh 	 * the notification that the abort all has completed.
5694*4c06356bSdh 	 */
5695*4c06356bSdh 	tgt = pptr->target;
5696*4c06356bSdh 	if (tgt) {
5697*4c06356bSdh 		mutex_enter(&tgt->statlock);
5698*4c06356bSdh 		pmcs_flush_target_queues(pwp, tgt, PMCS_TGT_WAIT_QUEUE);
5699*4c06356bSdh 		mutex_exit(&tgt->statlock);
5700*4c06356bSdh 	}
5701*4c06356bSdh 
5702*4c06356bSdh 	if (pptr->valid_device_id == 0) {
5703*4c06356bSdh 		return (0);
5704*4c06356bSdh 	}
5705*4c06356bSdh 
5706*4c06356bSdh 	if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
5707*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
5708*4c06356bSdh 		return (ENOMEM);
5709*4c06356bSdh 	}
5710*4c06356bSdh 	pwrk->arg = msg;
5711*4c06356bSdh 	pwrk->dtype = pptr->dtype;
5712*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
5713*4c06356bSdh 	    PMCIN_DEREGISTER_DEVICE_HANDLE));
5714*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
5715*4c06356bSdh 	msg[2] = LE_32(pptr->device_id);
5716*4c06356bSdh 
5717*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5718*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5719*4c06356bSdh 	if (ptr == NULL) {
5720*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5721*4c06356bSdh 		mutex_exit(&pwrk->lock);
5722*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
5723*4c06356bSdh 		return (ENOMEM);
5724*4c06356bSdh 	}
5725*4c06356bSdh 
5726*4c06356bSdh 	COPY_MESSAGE(ptr, msg, 3);
5727*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
5728*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5729*4c06356bSdh 
5730*4c06356bSdh 	pmcs_unlock_phy(pptr);
5731*4c06356bSdh 	WAIT_FOR(pwrk, 250, result);
5732*4c06356bSdh 	pmcs_lock_phy(pptr);
5733*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
5734*4c06356bSdh 
5735*4c06356bSdh 	if (result) {
5736*4c06356bSdh 		return (ETIMEDOUT);
5737*4c06356bSdh 	}
5738*4c06356bSdh 	status = LE_32(msg[2]);
5739*4c06356bSdh 	if (status != PMCOUT_STATUS_OK) {
5740*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
5741*4c06356bSdh 		    "%s: status 0x%x when trying to deregister device %s",
5742*4c06356bSdh 		    __func__, status, pptr->path);
5743*4c06356bSdh 	}
5744*4c06356bSdh 
5745*4c06356bSdh 	pptr->device_id = PMCS_INVALID_DEVICE_ID;
5746*4c06356bSdh 	PHY_CHANGED(pwp, pptr);
5747*4c06356bSdh 	RESTART_DISCOVERY(pwp);
5748*4c06356bSdh 	pptr->valid_device_id = 0;
5749*4c06356bSdh 	return (0);
5750*4c06356bSdh }
5751*4c06356bSdh 
5752*4c06356bSdh /*
5753*4c06356bSdh  * Acknowledge the SAS h/w events that need acknowledgement.
5754*4c06356bSdh  * This is only needed for first level PHYs.
5755*4c06356bSdh  */
5756*4c06356bSdh void
5757*4c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp)
5758*4c06356bSdh {
5759*4c06356bSdh 	uint32_t msg[PMCS_MSG_SIZE], *ptr;
5760*4c06356bSdh 	struct pmcwork *pwrk;
5761*4c06356bSdh 	pmcs_phy_t *pptr;
5762*4c06356bSdh 
5763*4c06356bSdh 	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
5764*4c06356bSdh 		pmcs_lock_phy(pptr);
5765*4c06356bSdh 		if (pptr->hw_event_ack == 0) {
5766*4c06356bSdh 			pmcs_unlock_phy(pptr);
5767*4c06356bSdh 			continue;
5768*4c06356bSdh 		}
5769*4c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5770*4c06356bSdh 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5771*4c06356bSdh 
5772*4c06356bSdh 		if ((ptr == NULL) || (pwrk =
5773*4c06356bSdh 		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
5774*4c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
5775*4c06356bSdh 			pmcs_unlock_phy(pptr);
5776*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
5777*4c06356bSdh 			break;
5778*4c06356bSdh 		}
5779*4c06356bSdh 
5780*4c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
5781*4c06356bSdh 		    PMCIN_SAW_HW_EVENT_ACK));
5782*4c06356bSdh 		msg[1] = LE_32(pwrk->htag);
5783*4c06356bSdh 		msg[2] = LE_32(pptr->hw_event_ack);
5784*4c06356bSdh 
5785*4c06356bSdh 		mutex_exit(&pwrk->lock);
5786*4c06356bSdh 		pwrk->dtype = pptr->dtype;
5787*4c06356bSdh 		pptr->hw_event_ack = 0;
5788*4c06356bSdh 		COPY_MESSAGE(ptr, msg, 3);
5789*4c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
5790*4c06356bSdh 		pmcs_unlock_phy(pptr);
5791*4c06356bSdh 	}
5792*4c06356bSdh }
5793*4c06356bSdh 
5794*4c06356bSdh /*
5795*4c06356bSdh  * Load DMA
5796*4c06356bSdh  */
5797*4c06356bSdh int
5798*4c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
5799*4c06356bSdh {
5800*4c06356bSdh 	ddi_dma_cookie_t *sg;
5801*4c06356bSdh 	pmcs_dmachunk_t *tc;
5802*4c06356bSdh 	pmcs_dmasgl_t *sgl, *prior;
5803*4c06356bSdh 	int seg, tsc;
5804*4c06356bSdh 	uint64_t sgl_addr;
5805*4c06356bSdh 
5806*4c06356bSdh 	/*
5807*4c06356bSdh 	 * If we have no data segments, we're done.
5808*4c06356bSdh 	 */
5809*4c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 0) {
5810*4c06356bSdh 		return (0);
5811*4c06356bSdh 	}
5812*4c06356bSdh 
5813*4c06356bSdh 	/*
5814*4c06356bSdh 	 * Get the S/G list pointer.
5815*4c06356bSdh 	 */
5816*4c06356bSdh 	sg = CMD2PKT(sp)->pkt_cookies;
5817*4c06356bSdh 
5818*4c06356bSdh 	/*
5819*4c06356bSdh 	 * If we only have one dma segment, we can directly address that
5820*4c06356bSdh 	 * data within the Inbound message itself.
5821*4c06356bSdh 	 */
5822*4c06356bSdh 	if (CMD2PKT(sp)->pkt_numcookies == 1) {
5823*4c06356bSdh 		msg[12] = LE_32(DWORD0(sg->dmac_laddress));
5824*4c06356bSdh 		msg[13] = LE_32(DWORD1(sg->dmac_laddress));
5825*4c06356bSdh 		msg[14] = LE_32(sg->dmac_size);
5826*4c06356bSdh 		msg[15] = 0;
5827*4c06356bSdh 		return (0);
5828*4c06356bSdh 	}
5829*4c06356bSdh 
5830*4c06356bSdh 	/*
5831*4c06356bSdh 	 * Otherwise, we'll need one or more external S/G list chunks.
5832*4c06356bSdh 	 * Get the first one and its dma address into the Inbound message.
5833*4c06356bSdh 	 */
5834*4c06356bSdh 	mutex_enter(&pwp->dma_lock);
5835*4c06356bSdh 	tc = pwp->dma_freelist;
5836*4c06356bSdh 	if (tc == NULL) {
5837*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
5838*4c06356bSdh 		mutex_exit(&pwp->dma_lock);
5839*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: out of SG lists", __func__);
5840*4c06356bSdh 		return (-1);
5841*4c06356bSdh 	}
5842*4c06356bSdh 	pwp->dma_freelist = tc->nxt;
5843*4c06356bSdh 	mutex_exit(&pwp->dma_lock);
5844*4c06356bSdh 
5845*4c06356bSdh 	tc->nxt = NULL;
5846*4c06356bSdh 	sp->cmd_clist = tc;
5847*4c06356bSdh 	sgl = tc->chunks;
5848*4c06356bSdh 	(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
5849*4c06356bSdh 	sgl_addr = tc->addr;
5850*4c06356bSdh 	msg[12] = LE_32(DWORD0(sgl_addr));
5851*4c06356bSdh 	msg[13] = LE_32(DWORD1(sgl_addr));
5852*4c06356bSdh 	msg[14] = 0;
5853*4c06356bSdh 	msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
5854*4c06356bSdh 
5855*4c06356bSdh 	prior = sgl;
5856*4c06356bSdh 	tsc = 0;
5857*4c06356bSdh 
5858*4c06356bSdh 	for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
5859*4c06356bSdh 		/*
5860*4c06356bSdh 		 * If the current segment count for this chunk is one less than
5861*4c06356bSdh 		 * the number s/g lists per chunk and we have more than one seg
5862*4c06356bSdh 		 * to go, we need another chunk. Get it, and make sure that the
5863*4c06356bSdh 		 * tail end of the the previous chunk points the new chunk
5864*4c06356bSdh 		 * (if remembering an offset can be called 'pointing to').
5865*4c06356bSdh 		 *
5866*4c06356bSdh 		 * Note that we can store the offset into our command area that
5867*4c06356bSdh 		 * represents the new chunk in the length field of the part
5868*4c06356bSdh 		 * that points the PMC chip at the next chunk- the PMC chip
5869*4c06356bSdh 		 * ignores this field when the EXTENSION bit is set.
5870*4c06356bSdh 		 *
5871*4c06356bSdh 		 * This is required for dma unloads later.
5872*4c06356bSdh 		 */
5873*4c06356bSdh 		if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
5874*4c06356bSdh 		    seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
5875*4c06356bSdh 			mutex_enter(&pwp->dma_lock);
5876*4c06356bSdh 			tc = pwp->dma_freelist;
5877*4c06356bSdh 			if (tc == NULL) {
5878*4c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
5879*4c06356bSdh 				mutex_exit(&pwp->dma_lock);
5880*4c06356bSdh 				pmcs_dma_unload(pwp, sp);
5881*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG2,
5882*4c06356bSdh 				    "%s: out of SG lists", __func__);
5883*4c06356bSdh 				return (-1);
5884*4c06356bSdh 			}
5885*4c06356bSdh 			pwp->dma_freelist = tc->nxt;
5886*4c06356bSdh 			tc->nxt = sp->cmd_clist;
5887*4c06356bSdh 			mutex_exit(&pwp->dma_lock);
5888*4c06356bSdh 
5889*4c06356bSdh 			sp->cmd_clist = tc;
5890*4c06356bSdh 			(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
5891*4c06356bSdh 			sgl = tc->chunks;
5892*4c06356bSdh 			sgl_addr = tc->addr;
5893*4c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglal =
5894*4c06356bSdh 			    LE_32(DWORD0(sgl_addr));
5895*4c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglah =
5896*4c06356bSdh 			    LE_32(DWORD1(sgl_addr));
5897*4c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
5898*4c06356bSdh 			prior[PMCS_SGL_NCHUNKS-1].flags =
5899*4c06356bSdh 			    LE_32(PMCS_DMASGL_EXTENSION);
5900*4c06356bSdh 			prior = sgl;
5901*4c06356bSdh 			tsc = 0;
5902*4c06356bSdh 		}
5903*4c06356bSdh 		sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
5904*4c06356bSdh 		sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
5905*4c06356bSdh 		sgl[tsc].sglen = LE_32(sg->dmac_size);
5906*4c06356bSdh 		sgl[tsc++].flags = 0;
5907*4c06356bSdh 		sg++;
5908*4c06356bSdh 	}
5909*4c06356bSdh 	return (0);
5910*4c06356bSdh }
5911*4c06356bSdh 
5912*4c06356bSdh /*
5913*4c06356bSdh  * Unload DMA
5914*4c06356bSdh  */
5915*4c06356bSdh void
5916*4c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
5917*4c06356bSdh {
5918*4c06356bSdh 	pmcs_dmachunk_t *cp;
5919*4c06356bSdh 
5920*4c06356bSdh 	mutex_enter(&pwp->dma_lock);
5921*4c06356bSdh 	while ((cp = sp->cmd_clist) != NULL) {
5922*4c06356bSdh 		sp->cmd_clist = cp->nxt;
5923*4c06356bSdh 		cp->nxt = pwp->dma_freelist;
5924*4c06356bSdh 		pwp->dma_freelist = cp;
5925*4c06356bSdh 	}
5926*4c06356bSdh 	mutex_exit(&pwp->dma_lock);
5927*4c06356bSdh }
5928*4c06356bSdh 
5929*4c06356bSdh /*
5930*4c06356bSdh  * Take a chunk of consistent memory that has just been allocated and inserted
5931*4c06356bSdh  * into the cip indices and prepare it for DMA chunk usage and add it to the
5932*4c06356bSdh  * freelist.
5933*4c06356bSdh  *
5934*4c06356bSdh  * Called with dma_lock locked (except during attach when it's unnecessary)
5935*4c06356bSdh  */
5936*4c06356bSdh void
5937*4c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
5938*4c06356bSdh     pmcs_chunk_t *pchunk, unsigned long lim)
5939*4c06356bSdh {
5940*4c06356bSdh 	unsigned long off, n;
5941*4c06356bSdh 	pmcs_dmachunk_t *np = dcp;
5942*4c06356bSdh 	pmcs_chunk_t *tmp_chunk;
5943*4c06356bSdh 
5944*4c06356bSdh 	if (pwp->dma_chunklist == NULL) {
5945*4c06356bSdh 		pwp->dma_chunklist = pchunk;
5946*4c06356bSdh 	} else {
5947*4c06356bSdh 		tmp_chunk = pwp->dma_chunklist;
5948*4c06356bSdh 		while (tmp_chunk->next) {
5949*4c06356bSdh 			tmp_chunk = tmp_chunk->next;
5950*4c06356bSdh 		}
5951*4c06356bSdh 		tmp_chunk->next = pchunk;
5952*4c06356bSdh 	}
5953*4c06356bSdh 
5954*4c06356bSdh 	/*
5955*4c06356bSdh 	 * Install offsets into chunk lists.
5956*4c06356bSdh 	 */
5957*4c06356bSdh 	for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
5958*4c06356bSdh 		np->chunks = (void *)&pchunk->addrp[off];
5959*4c06356bSdh 		np->addr = pchunk->dma_addr + off;
5960*4c06356bSdh 		np->acc_handle = pchunk->acc_handle;
5961*4c06356bSdh 		np->dma_handle = pchunk->dma_handle;
5962*4c06356bSdh 		if ((off + PMCS_SGL_CHUNKSZ) < lim) {
5963*4c06356bSdh 			np = np->nxt;
5964*4c06356bSdh 		}
5965*4c06356bSdh 	}
5966*4c06356bSdh 	np->nxt = pwp->dma_freelist;
5967*4c06356bSdh 	pwp->dma_freelist = dcp;
5968*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
5969*4c06356bSdh 	    "added %lu DMA chunks ", n);
5970*4c06356bSdh }
5971*4c06356bSdh 
5972*4c06356bSdh /*
5973*4c06356bSdh  * Change the value of the interrupt coalescing timer.  This is done currently
5974*4c06356bSdh  * only for I/O completions.  If we're using the "auto clear" feature, it can
5975*4c06356bSdh  * be turned back on when interrupt coalescing is turned off and must be
5976*4c06356bSdh  * turned off when the coalescing timer is on.
5977*4c06356bSdh  * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value.  As long
5978*4c06356bSdh  * as that's true, we don't need to distinguish between them.
5979*4c06356bSdh  */
5980*4c06356bSdh 
5981*4c06356bSdh void
5982*4c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
5983*4c06356bSdh {
5984*4c06356bSdh 	if (adj == DECREASE_TIMER) {
5985*4c06356bSdh 		/* If the timer is already off, nothing to do. */
5986*4c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
5987*4c06356bSdh 			return;
5988*4c06356bSdh 		}
5989*4c06356bSdh 
5990*4c06356bSdh 		pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
5991*4c06356bSdh 
5992*4c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer == 0) {
5993*4c06356bSdh 			/* Disable the timer */
5994*4c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
5995*4c06356bSdh 
5996*4c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
5997*4c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
5998*4c06356bSdh 				    pwp->odb_auto_clear);
5999*4c06356bSdh 			}
6000*4c06356bSdh 
6001*4c06356bSdh 			pwp->io_intr_coal.timer_on = B_FALSE;
6002*4c06356bSdh 			pwp->io_intr_coal.max_io_completions = B_FALSE;
6003*4c06356bSdh 			pwp->io_intr_coal.num_intrs = 0;
6004*4c06356bSdh 			pwp->io_intr_coal.int_cleared = B_FALSE;
6005*4c06356bSdh 			pwp->io_intr_coal.num_io_completions = 0;
6006*4c06356bSdh 
6007*4c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
6008*4c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
6009*4c06356bSdh 		} else {
6010*4c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
6011*4c06356bSdh 			    pwp->io_intr_coal.intr_coal_timer);
6012*4c06356bSdh 		}
6013*4c06356bSdh 	} else {
6014*4c06356bSdh 		/*
6015*4c06356bSdh 		 * If the timer isn't on yet, do the setup for it now.
6016*4c06356bSdh 		 */
6017*4c06356bSdh 		if (pwp->io_intr_coal.timer_on == B_FALSE) {
6018*4c06356bSdh 			/* If auto clear is being used, turn it off. */
6019*4c06356bSdh 			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
6020*4c06356bSdh 				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
6021*4c06356bSdh 				    (pwp->odb_auto_clear &
6022*4c06356bSdh 				    ~(1 << PMCS_MSIX_IODONE)));
6023*4c06356bSdh 			}
6024*4c06356bSdh 
6025*4c06356bSdh 			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
6026*4c06356bSdh 			    (1 << PMCS_MSIX_IODONE));
6027*4c06356bSdh 			pwp->io_intr_coal.timer_on = B_TRUE;
6028*4c06356bSdh 			pwp->io_intr_coal.intr_coal_timer =
6029*4c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
6030*4c06356bSdh 
6031*4c06356bSdh 			DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
6032*4c06356bSdh 			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
6033*4c06356bSdh 		} else {
6034*4c06356bSdh 			pwp->io_intr_coal.intr_coal_timer +=
6035*4c06356bSdh 			    PMCS_COAL_TIMER_GRAN;
6036*4c06356bSdh 		}
6037*4c06356bSdh 
6038*4c06356bSdh 		if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
6039*4c06356bSdh 			pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
6040*4c06356bSdh 		}
6041*4c06356bSdh 
6042*4c06356bSdh 		pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
6043*4c06356bSdh 		    pwp->io_intr_coal.intr_coal_timer);
6044*4c06356bSdh 	}
6045*4c06356bSdh 
6046*4c06356bSdh 	/*
6047*4c06356bSdh 	 * Adjust the interrupt threshold based on the current timer value
6048*4c06356bSdh 	 */
6049*4c06356bSdh 	pwp->io_intr_coal.intr_threshold =
6050*4c06356bSdh 	    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
6051*4c06356bSdh 	    (pwp->io_intr_coal.intr_latency +
6052*4c06356bSdh 	    (pwp->io_intr_coal.intr_coal_timer * 1000)));
6053*4c06356bSdh }
6054*4c06356bSdh 
6055*4c06356bSdh /*
6056*4c06356bSdh  * Register Access functions
6057*4c06356bSdh  */
6058*4c06356bSdh uint32_t
6059*4c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
6060*4c06356bSdh {
6061*4c06356bSdh 	uint32_t iqci;
6062*4c06356bSdh 
6063*4c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
6064*4c06356bSdh 	    DDI_SUCCESS) {
6065*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
6066*4c06356bSdh 		    __func__);
6067*4c06356bSdh 	}
6068*4c06356bSdh 
6069*4c06356bSdh 	iqci = LE_32(
6070*4c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
6071*4c06356bSdh 
6072*4c06356bSdh 	return (iqci);
6073*4c06356bSdh }
6074*4c06356bSdh 
6075*4c06356bSdh uint32_t
6076*4c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
6077*4c06356bSdh {
6078*4c06356bSdh 	uint32_t oqpi;
6079*4c06356bSdh 
6080*4c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
6081*4c06356bSdh 	    DDI_SUCCESS) {
6082*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
6083*4c06356bSdh 		    __func__);
6084*4c06356bSdh 	}
6085*4c06356bSdh 
6086*4c06356bSdh 	oqpi = LE_32(
6087*4c06356bSdh 	    ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
6088*4c06356bSdh 
6089*4c06356bSdh 	return (oqpi);
6090*4c06356bSdh }
6091*4c06356bSdh 
6092*4c06356bSdh uint32_t
6093*4c06356bSdh pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint32_t off)
6094*4c06356bSdh {
6095*4c06356bSdh 	uint32_t rv, newaxil, oldaxil;
6096*4c06356bSdh 
6097*4c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
6098*4c06356bSdh 	off &= GSM_BASE_MASK;
6099*4c06356bSdh 	mutex_enter(&pwp->axil_lock);
6100*4c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
6101*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
6102*4c06356bSdh 	ddi_put32(pwp->top_acc_handle,
6103*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
6104*4c06356bSdh 	drv_usecwait(10);
6105*4c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
6106*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6107*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed");
6108*4c06356bSdh 	}
6109*4c06356bSdh 	rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
6110*4c06356bSdh 	ddi_put32(pwp->top_acc_handle,
6111*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
6112*4c06356bSdh 	drv_usecwait(10);
6113*4c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
6114*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6115*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed");
6116*4c06356bSdh 	}
6117*4c06356bSdh 	mutex_exit(&pwp->axil_lock);
6118*4c06356bSdh 	return (rv);
6119*4c06356bSdh }
6120*4c06356bSdh 
6121*4c06356bSdh void
6122*4c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6123*4c06356bSdh {
6124*4c06356bSdh 	uint32_t newaxil, oldaxil;
6125*4c06356bSdh 
6126*4c06356bSdh 	newaxil = off & ~GSM_BASE_MASK;
6127*4c06356bSdh 	off &= GSM_BASE_MASK;
6128*4c06356bSdh 	mutex_enter(&pwp->axil_lock);
6129*4c06356bSdh 	oldaxil = ddi_get32(pwp->top_acc_handle,
6130*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
6131*4c06356bSdh 	ddi_put32(pwp->top_acc_handle,
6132*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
6133*4c06356bSdh 	drv_usecwait(10);
6134*4c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
6135*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
6136*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed");
6137*4c06356bSdh 	}
6138*4c06356bSdh 	ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
6139*4c06356bSdh 	ddi_put32(pwp->top_acc_handle,
6140*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
6141*4c06356bSdh 	drv_usecwait(10);
6142*4c06356bSdh 	if (ddi_get32(pwp->top_acc_handle,
6143*4c06356bSdh 	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
6144*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed");
6145*4c06356bSdh 	}
6146*4c06356bSdh 	mutex_exit(&pwp->axil_lock);
6147*4c06356bSdh }
6148*4c06356bSdh 
6149*4c06356bSdh uint32_t
6150*4c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
6151*4c06356bSdh {
6152*4c06356bSdh 	switch (off) {
6153*4c06356bSdh 	case PMCS_SPC_RESET:
6154*4c06356bSdh 	case PMCS_SPC_BOOT_STRAP:
6155*4c06356bSdh 	case PMCS_SPC_DEVICE_ID:
6156*4c06356bSdh 	case PMCS_DEVICE_REVISION:
6157*4c06356bSdh 		off = pmcs_rd_gsm_reg(pwp, off);
6158*4c06356bSdh 		break;
6159*4c06356bSdh 	default:
6160*4c06356bSdh 		off = ddi_get32(pwp->top_acc_handle,
6161*4c06356bSdh 		    &pwp->top_regs[off >> 2]);
6162*4c06356bSdh 		break;
6163*4c06356bSdh 	}
6164*4c06356bSdh 	return (off);
6165*4c06356bSdh }
6166*4c06356bSdh 
6167*4c06356bSdh void
6168*4c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6169*4c06356bSdh {
6170*4c06356bSdh 	switch (off) {
6171*4c06356bSdh 	case PMCS_SPC_RESET:
6172*4c06356bSdh 	case PMCS_DEVICE_REVISION:
6173*4c06356bSdh 		pmcs_wr_gsm_reg(pwp, off, val);
6174*4c06356bSdh 		break;
6175*4c06356bSdh 	default:
6176*4c06356bSdh 		ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
6177*4c06356bSdh 		break;
6178*4c06356bSdh 	}
6179*4c06356bSdh }
6180*4c06356bSdh 
6181*4c06356bSdh uint32_t
6182*4c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
6183*4c06356bSdh {
6184*4c06356bSdh 	return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
6185*4c06356bSdh }
6186*4c06356bSdh 
6187*4c06356bSdh uint32_t
6188*4c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
6189*4c06356bSdh {
6190*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6191*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
6192*4c06356bSdh }
6193*4c06356bSdh 
6194*4c06356bSdh uint32_t
6195*4c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
6196*4c06356bSdh {
6197*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6198*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
6199*4c06356bSdh }
6200*4c06356bSdh 
6201*4c06356bSdh uint32_t
6202*4c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
6203*4c06356bSdh {
6204*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6205*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
6206*4c06356bSdh }
6207*4c06356bSdh 
6208*4c06356bSdh uint32_t
6209*4c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
6210*4c06356bSdh {
6211*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6212*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
6213*4c06356bSdh }
6214*4c06356bSdh 
6215*4c06356bSdh uint32_t
6216*4c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
6217*4c06356bSdh {
6218*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6219*4c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
6220*4c06356bSdh }
6221*4c06356bSdh 
6222*4c06356bSdh uint32_t
6223*4c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
6224*4c06356bSdh {
6225*4c06356bSdh 	return (ddi_get32(pwp->mpi_acc_handle,
6226*4c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
6227*4c06356bSdh }
6228*4c06356bSdh 
6229*4c06356bSdh void
6230*4c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6231*4c06356bSdh {
6232*4c06356bSdh 	ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
6233*4c06356bSdh }
6234*4c06356bSdh 
6235*4c06356bSdh void
6236*4c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6237*4c06356bSdh {
6238*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6239*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
6240*4c06356bSdh }
6241*4c06356bSdh 
6242*4c06356bSdh void
6243*4c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6244*4c06356bSdh {
6245*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6246*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
6247*4c06356bSdh }
6248*4c06356bSdh 
6249*4c06356bSdh void
6250*4c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6251*4c06356bSdh {
6252*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6253*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
6254*4c06356bSdh }
6255*4c06356bSdh 
6256*4c06356bSdh void
6257*4c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
6258*4c06356bSdh {
6259*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6260*4c06356bSdh 	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
6261*4c06356bSdh }
6262*4c06356bSdh 
6263*4c06356bSdh void
6264*4c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
6265*4c06356bSdh {
6266*4c06356bSdh 	((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
6267*4c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
6268*4c06356bSdh 	    DDI_SUCCESS) {
6269*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
6270*4c06356bSdh 		    __func__);
6271*4c06356bSdh 	}
6272*4c06356bSdh }
6273*4c06356bSdh 
6274*4c06356bSdh void
6275*4c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
6276*4c06356bSdh {
6277*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6278*4c06356bSdh 	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
6279*4c06356bSdh }
6280*4c06356bSdh 
6281*4c06356bSdh void
6282*4c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
6283*4c06356bSdh {
6284*4c06356bSdh 	ddi_put32(pwp->mpi_acc_handle,
6285*4c06356bSdh 	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
6286*4c06356bSdh }
6287*4c06356bSdh 
6288*4c06356bSdh void
6289*4c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
6290*4c06356bSdh {
6291*4c06356bSdh 	((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
6292*4c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
6293*4c06356bSdh 	    DDI_SUCCESS) {
6294*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
6295*4c06356bSdh 		    __func__);
6296*4c06356bSdh 	}
6297*4c06356bSdh }
6298*4c06356bSdh 
6299*4c06356bSdh /*
6300*4c06356bSdh  * Check the status value of an outbound IOMB and report anything bad
6301*4c06356bSdh  */
6302*4c06356bSdh 
6303*4c06356bSdh void
6304*4c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
6305*4c06356bSdh {
6306*4c06356bSdh 	uint16_t 	opcode;
6307*4c06356bSdh 	int		offset;
6308*4c06356bSdh 
6309*4c06356bSdh 	if (iomb == NULL) {
6310*4c06356bSdh 		return;
6311*4c06356bSdh 	}
6312*4c06356bSdh 
6313*4c06356bSdh 	opcode = LE_32(iomb[0]) & 0xfff;
6314*4c06356bSdh 
6315*4c06356bSdh 	switch (opcode) {
6316*4c06356bSdh 		/*
6317*4c06356bSdh 		 * The following have no status field, so ignore them
6318*4c06356bSdh 		 */
6319*4c06356bSdh 	case PMCOUT_ECHO:
6320*4c06356bSdh 	case PMCOUT_SAS_HW_EVENT:
6321*4c06356bSdh 	case PMCOUT_GET_DEVICE_HANDLE:
6322*4c06356bSdh 	case PMCOUT_SATA_EVENT:
6323*4c06356bSdh 	case PMCOUT_SSP_EVENT:
6324*4c06356bSdh 	case PMCOUT_DEVICE_HANDLE_ARRIVED:
6325*4c06356bSdh 	case PMCOUT_SMP_REQUEST_RECEIVED:
6326*4c06356bSdh 	case PMCOUT_GPIO:
6327*4c06356bSdh 	case PMCOUT_GPIO_EVENT:
6328*4c06356bSdh 	case PMCOUT_GET_TIME_STAMP:
6329*4c06356bSdh 	case PMCOUT_SKIP_ENTRIES:
6330*4c06356bSdh 	case PMCOUT_GET_NVMD_DATA:	/* Actually lower 16 bits of word 3 */
6331*4c06356bSdh 	case PMCOUT_SET_NVMD_DATA:	/* but ignore - we don't use these */
6332*4c06356bSdh 	case PMCOUT_DEVICE_HANDLE_REMOVED:
6333*4c06356bSdh 	case PMCOUT_SSP_REQUEST_RECEIVED:
6334*4c06356bSdh 		return;
6335*4c06356bSdh 
6336*4c06356bSdh 	case PMCOUT_GENERAL_EVENT:
6337*4c06356bSdh 		offset = 1;
6338*4c06356bSdh 		break;
6339*4c06356bSdh 
6340*4c06356bSdh 	case PMCOUT_SSP_COMPLETION:
6341*4c06356bSdh 	case PMCOUT_SMP_COMPLETION:
6342*4c06356bSdh 	case PMCOUT_DEVICE_REGISTRATION:
6343*4c06356bSdh 	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
6344*4c06356bSdh 	case PMCOUT_SATA_COMPLETION:
6345*4c06356bSdh 	case PMCOUT_DEVICE_INFO:
6346*4c06356bSdh 	case PMCOUT_FW_FLASH_UPDATE:
6347*4c06356bSdh 	case PMCOUT_SSP_ABORT:
6348*4c06356bSdh 	case PMCOUT_SATA_ABORT:
6349*4c06356bSdh 	case PMCOUT_SAS_DIAG_MODE_START_END:
6350*4c06356bSdh 	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
6351*4c06356bSdh 	case PMCOUT_SMP_ABORT:
6352*4c06356bSdh 	case PMCOUT_SET_DEVICE_STATE:
6353*4c06356bSdh 	case PMCOUT_GET_DEVICE_STATE:
6354*4c06356bSdh 	case PMCOUT_SET_DEVICE_INFO:
6355*4c06356bSdh 		offset = 2;
6356*4c06356bSdh 		break;
6357*4c06356bSdh 
6358*4c06356bSdh 	case PMCOUT_LOCAL_PHY_CONTROL:
6359*4c06356bSdh 	case PMCOUT_SAS_DIAG_EXECUTE:
6360*4c06356bSdh 	case PMCOUT_PORT_CONTROL:
6361*4c06356bSdh 		offset = 3;
6362*4c06356bSdh 		break;
6363*4c06356bSdh 
6364*4c06356bSdh 	case PMCOUT_GET_INFO:
6365*4c06356bSdh 	case PMCOUT_GET_VPD:
6366*4c06356bSdh 	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
6367*4c06356bSdh 	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
6368*4c06356bSdh 	case PMCOUT_SET_VPD:
6369*4c06356bSdh 	case PMCOUT_TWI:
6370*4c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
6371*4c06356bSdh 		    "Got response for deprecated opcode", iomb);
6372*4c06356bSdh 		return;
6373*4c06356bSdh 
6374*4c06356bSdh 	default:
6375*4c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
6376*4c06356bSdh 		    "Got response for unknown opcode", iomb);
6377*4c06356bSdh 		return;
6378*4c06356bSdh 	}
6379*4c06356bSdh 
6380*4c06356bSdh 	if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
6381*4c06356bSdh 		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
6382*4c06356bSdh 		    "bad status on TAG_TYPE_NONE command", iomb);
6383*4c06356bSdh 	}
6384*4c06356bSdh }
6385*4c06356bSdh 
6386*4c06356bSdh /*
6387*4c06356bSdh  * Called with statlock held
6388*4c06356bSdh  */
6389*4c06356bSdh void
6390*4c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
6391*4c06356bSdh {
6392*4c06356bSdh 	_NOTE(ARGUNUSED(pwp));
6393*4c06356bSdh 
6394*4c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
6395*4c06356bSdh 	ASSERT(xp->dying);
6396*4c06356bSdh 
6397*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device 0x%p is gone.", __func__,
6398*4c06356bSdh 	    (void *)xp);
6399*4c06356bSdh 
6400*4c06356bSdh 	/*
6401*4c06356bSdh 	 * Clear the dip now.  This keeps pmcs_rem_old_devices from attempting
6402*4c06356bSdh 	 * to call us on the same device while we're still flushing queues.
6403*4c06356bSdh 	 * The only side effect is we can no longer update SM-HBA properties,
6404*4c06356bSdh 	 * but this device is going away anyway, so no matter.
6405*4c06356bSdh 	 */
6406*4c06356bSdh 	xp->dip = NULL;
6407*4c06356bSdh 
6408*4c06356bSdh 	/*
6409*4c06356bSdh 	 * Flush all target queues
6410*4c06356bSdh 	 */
6411*4c06356bSdh 	pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
6412*4c06356bSdh 
6413*4c06356bSdh 	xp->special_running = 0;
6414*4c06356bSdh 	xp->recovering = 0;
6415*4c06356bSdh 	xp->recover_wait = 0;
6416*4c06356bSdh 	xp->draining = 0;
6417*4c06356bSdh 	xp->dying = 0;
6418*4c06356bSdh 	xp->new = 0;
6419*4c06356bSdh 	xp->assigned = 0;
6420*4c06356bSdh 	xp->dev_state = 0;
6421*4c06356bSdh 	xp->tagmap = 0;
6422*4c06356bSdh 	xp->dev_gone = 1;
6423*4c06356bSdh 	xp->event_recovery = 0;
6424*4c06356bSdh 	xp->dtype = NOTHING;
6425*4c06356bSdh 	xp->wq_recovery_tail = NULL;
6426*4c06356bSdh 	/* Don't clear xp->phy */
6427*4c06356bSdh 	/* Don't clear xp->actv_cnt */
6428*4c06356bSdh }
6429*4c06356bSdh 
6430*4c06356bSdh static int
6431*4c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
6432*4c06356bSdh {
6433*4c06356bSdh 	int result = srf->srf_result;
6434*4c06356bSdh 
6435*4c06356bSdh 	switch (result) {
6436*4c06356bSdh 	case SMP_RES_UNKNOWN_FUNCTION:
6437*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6438*4c06356bSdh 		    "Function Result: Unknown SMP Function(0x%x)",
6439*4c06356bSdh 		    __func__, result);
6440*4c06356bSdh 		break;
6441*4c06356bSdh 	case SMP_RES_FUNCTION_FAILED:
6442*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6443*4c06356bSdh 		    "Function Result: SMP Function Failed(0x%x)",
6444*4c06356bSdh 		    __func__, result);
6445*4c06356bSdh 		break;
6446*4c06356bSdh 	case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
6447*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6448*4c06356bSdh 		    "Function Result: Invalid Request Frame Length(0x%x)",
6449*4c06356bSdh 		    __func__, result);
6450*4c06356bSdh 		break;
6451*4c06356bSdh 	case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
6452*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6453*4c06356bSdh 		    "Function Result: Incomplete Descriptor List(0x%x)",
6454*4c06356bSdh 		    __func__, result);
6455*4c06356bSdh 		break;
6456*4c06356bSdh 	case SMP_RES_PHY_DOES_NOT_EXIST:
6457*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6458*4c06356bSdh 		    "Function Result: PHY does not exist(0x%x)",
6459*4c06356bSdh 		    __func__, result);
6460*4c06356bSdh 		break;
6461*4c06356bSdh 	case SMP_RES_PHY_VACANT:
6462*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6463*4c06356bSdh 		    "Function Result: PHY Vacant(0x%x)",
6464*4c06356bSdh 		    __func__, result);
6465*4c06356bSdh 		break;
6466*4c06356bSdh 	default:
6467*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
6468*4c06356bSdh 		    "Function Result: (0x%x)",
6469*4c06356bSdh 		    __func__, result);
6470*4c06356bSdh 		break;
6471*4c06356bSdh 	}
6472*4c06356bSdh 
6473*4c06356bSdh 	return (result);
6474*4c06356bSdh }
6475*4c06356bSdh 
6476*4c06356bSdh /*
6477*4c06356bSdh  * Do all the repetitive stuff necessary to setup for DMA
6478*4c06356bSdh  *
6479*4c06356bSdh  * pwp: Used for dip
6480*4c06356bSdh  * dma_attr: ddi_dma_attr_t to use for the mapping
6481*4c06356bSdh  * acch: ddi_acc_handle_t to use for the mapping
6482*4c06356bSdh  * dmah: ddi_dma_handle_t to use
6483*4c06356bSdh  * length: Amount of memory for mapping
6484*4c06356bSdh  * kvp: Pointer filled in with kernel virtual address on successful return
6485*4c06356bSdh  * dma_addr: Pointer filled in with DMA address on successful return
6486*4c06356bSdh  */
6487*4c06356bSdh boolean_t
6488*4c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
6489*4c06356bSdh     ddi_dma_handle_t *dmah, size_t length, caddr_t *kvp, uint64_t *dma_addr)
6490*4c06356bSdh {
6491*4c06356bSdh 	dev_info_t		*dip = pwp->dip;
6492*4c06356bSdh 	ddi_dma_cookie_t	cookie;
6493*4c06356bSdh 	size_t			real_length;
6494*4c06356bSdh 	uint_t			ddma_flag = DDI_DMA_CONSISTENT;
6495*4c06356bSdh 	uint_t			ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
6496*4c06356bSdh 	uint_t			cookie_cnt;
6497*4c06356bSdh 	ddi_device_acc_attr_t	mattr = {
6498*4c06356bSdh 		DDI_DEVICE_ATTR_V0,
6499*4c06356bSdh 		DDI_NEVERSWAP_ACC,
6500*4c06356bSdh 		DDI_STRICTORDER_ACC,
6501*4c06356bSdh 		DDI_DEFAULT_ACC
6502*4c06356bSdh 	};
6503*4c06356bSdh 
6504*4c06356bSdh 	*acch = NULL;
6505*4c06356bSdh 	*dmah = NULL;
6506*4c06356bSdh 
6507*4c06356bSdh 	if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
6508*4c06356bSdh 	    DDI_SUCCESS) {
6509*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA handle");
6510*4c06356bSdh 		return (B_FALSE);
6511*4c06356bSdh 	}
6512*4c06356bSdh 
6513*4c06356bSdh 	if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
6514*4c06356bSdh 	    NULL, kvp, &real_length, acch) != DDI_SUCCESS) {
6515*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA mem");
6516*4c06356bSdh 		ddi_dma_free_handle(dmah);
6517*4c06356bSdh 		*dmah = NULL;
6518*4c06356bSdh 		return (B_FALSE);
6519*4c06356bSdh 	}
6520*4c06356bSdh 
6521*4c06356bSdh 	if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvp, real_length,
6522*4c06356bSdh 	    ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
6523*4c06356bSdh 	    != DDI_DMA_MAPPED) {
6524*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to bind DMA");
6525*4c06356bSdh 		ddi_dma_free_handle(dmah);
6526*4c06356bSdh 		ddi_dma_mem_free(acch);
6527*4c06356bSdh 		*dmah = NULL;
6528*4c06356bSdh 		*acch = NULL;
6529*4c06356bSdh 		return (B_FALSE);
6530*4c06356bSdh 	}
6531*4c06356bSdh 
6532*4c06356bSdh 	if (cookie_cnt != 1) {
6533*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Multiple cookies");
6534*4c06356bSdh 		if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
6535*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed at "
6536*4c06356bSdh 			    "%s():%d", __func__, __LINE__);
6537*4c06356bSdh 		}
6538*4c06356bSdh 		ddi_dma_free_handle(dmah);
6539*4c06356bSdh 		ddi_dma_mem_free(acch);
6540*4c06356bSdh 		*dmah = NULL;
6541*4c06356bSdh 		*acch = NULL;
6542*4c06356bSdh 		return (B_FALSE);
6543*4c06356bSdh 	}
6544*4c06356bSdh 
6545*4c06356bSdh 	*dma_addr = cookie.dmac_laddress;
6546*4c06356bSdh 
6547*4c06356bSdh 	return (B_TRUE);
6548*4c06356bSdh }
6549*4c06356bSdh 
6550*4c06356bSdh /*
6551*4c06356bSdh  * Flush requested queues for a particular target.  Called with statlock held
6552*4c06356bSdh  */
6553*4c06356bSdh void
6554*4c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
6555*4c06356bSdh {
6556*4c06356bSdh 	pmcs_cmd_t	*sp;
6557*4c06356bSdh 	pmcwork_t	*pwrk;
6558*4c06356bSdh 
6559*4c06356bSdh 	ASSERT(pwp != NULL);
6560*4c06356bSdh 	ASSERT(tgt != NULL);
6561*4c06356bSdh 
6562*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG,
6563*4c06356bSdh 	    "%s: Flushing queues (%d) for target 0x%p", __func__,
6564*4c06356bSdh 	    queues, (void *)tgt);
6565*4c06356bSdh 
6566*4c06356bSdh 	/*
6567*4c06356bSdh 	 * Commands on the wait queue (or the special queue below) don't have
6568*4c06356bSdh 	 * work structures associated with them.
6569*4c06356bSdh 	 */
6570*4c06356bSdh 	if (queues & PMCS_TGT_WAIT_QUEUE) {
6571*4c06356bSdh 		mutex_enter(&tgt->wqlock);
6572*4c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
6573*4c06356bSdh 			STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
6574*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
6575*4c06356bSdh 			    "%s: Removing cmd 0x%p from wq for target 0x%p",
6576*4c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
6577*4c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
6578*4c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
6579*4c06356bSdh 			mutex_exit(&tgt->wqlock);
6580*4c06356bSdh 			pmcs_dma_unload(pwp, sp);
6581*4c06356bSdh 			mutex_enter(&pwp->cq_lock);
6582*4c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6583*4c06356bSdh 			mutex_exit(&pwp->cq_lock);
6584*4c06356bSdh 			mutex_enter(&tgt->wqlock);
6585*4c06356bSdh 		}
6586*4c06356bSdh 		mutex_exit(&tgt->wqlock);
6587*4c06356bSdh 	}
6588*4c06356bSdh 
6589*4c06356bSdh 	/*
6590*4c06356bSdh 	 * Commands on the active queue will have work structures associated
6591*4c06356bSdh 	 * with them.
6592*4c06356bSdh 	 */
6593*4c06356bSdh 	if (queues & PMCS_TGT_ACTIVE_QUEUE) {
6594*4c06356bSdh 		mutex_enter(&tgt->aqlock);
6595*4c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->aq)) != NULL) {
6596*4c06356bSdh 			STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
6597*4c06356bSdh 			pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
6598*4c06356bSdh 			mutex_exit(&tgt->aqlock);
6599*4c06356bSdh 			mutex_exit(&tgt->statlock);
6600*4c06356bSdh 			/*
6601*4c06356bSdh 			 * If we found a work structure, mark it as dead
6602*4c06356bSdh 			 * and complete it
6603*4c06356bSdh 			 */
6604*4c06356bSdh 			if (pwrk != NULL) {
6605*4c06356bSdh 				pwrk->dead = 1;
6606*4c06356bSdh 				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
6607*4c06356bSdh 				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
6608*4c06356bSdh 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
6609*4c06356bSdh 			}
6610*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
6611*4c06356bSdh 			    "%s: Removing cmd 0x%p from aq for target 0x%p",
6612*4c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
6613*4c06356bSdh 			pmcs_dma_unload(pwp, sp);
6614*4c06356bSdh 			mutex_enter(&pwp->cq_lock);
6615*4c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6616*4c06356bSdh 			mutex_exit(&pwp->cq_lock);
6617*4c06356bSdh 			mutex_enter(&tgt->aqlock);
6618*4c06356bSdh 			mutex_enter(&tgt->statlock);
6619*4c06356bSdh 		}
6620*4c06356bSdh 		mutex_exit(&tgt->aqlock);
6621*4c06356bSdh 	}
6622*4c06356bSdh 
6623*4c06356bSdh 	if (queues & PMCS_TGT_SPECIAL_QUEUE) {
6624*4c06356bSdh 		while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
6625*4c06356bSdh 			STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
6626*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
6627*4c06356bSdh 			    "%s: Removing cmd 0x%p from sq for target 0x%p",
6628*4c06356bSdh 			    __func__, (void *)sp, (void *)tgt);
6629*4c06356bSdh 			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
6630*4c06356bSdh 			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
6631*4c06356bSdh 			pmcs_dma_unload(pwp, sp);
6632*4c06356bSdh 			mutex_enter(&pwp->cq_lock);
6633*4c06356bSdh 			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6634*4c06356bSdh 			mutex_exit(&pwp->cq_lock);
6635*4c06356bSdh 		}
6636*4c06356bSdh 	}
6637*4c06356bSdh }
6638*4c06356bSdh 
6639*4c06356bSdh void
6640*4c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
6641*4c06356bSdh     size_t amt)
6642*4c06356bSdh {
6643*4c06356bSdh 	switch (PMCS_TAG_TYPE(pwrk->htag)) {
6644*4c06356bSdh 	case PMCS_TAG_TYPE_CBACK:
6645*4c06356bSdh 	{
6646*4c06356bSdh 		pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
6647*4c06356bSdh 		(*callback)(pwp, pwrk, iomb);
6648*4c06356bSdh 		break;
6649*4c06356bSdh 	}
6650*4c06356bSdh 	case PMCS_TAG_TYPE_WAIT:
6651*4c06356bSdh 		if (pwrk->arg && iomb && amt) {
6652*4c06356bSdh 			(void) memcpy(pwrk->arg, iomb, amt);
6653*4c06356bSdh 		}
6654*4c06356bSdh 		cv_signal(&pwrk->sleep_cv);
6655*4c06356bSdh 		mutex_exit(&pwrk->lock);
6656*4c06356bSdh 		break;
6657*4c06356bSdh 	case PMCS_TAG_TYPE_NONE:
6658*4c06356bSdh #ifdef DEBUG
6659*4c06356bSdh 		pmcs_check_iomb_status(pwp, iomb);
6660*4c06356bSdh #endif
6661*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6662*4c06356bSdh 		break;
6663*4c06356bSdh 	default:
6664*4c06356bSdh 		/*
6665*4c06356bSdh 		 * We will leak a structure here if we don't know
6666*4c06356bSdh 		 * what happened
6667*4c06356bSdh 		 */
6668*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unknown PMCS_TAG_TYPE (%x)",
6669*4c06356bSdh 		    __func__, PMCS_TAG_TYPE(pwrk->htag));
6670*4c06356bSdh 		break;
6671*4c06356bSdh 	}
6672*4c06356bSdh }
6673*4c06356bSdh 
6674*4c06356bSdh /*
6675*4c06356bSdh  * Determine if iport still has targets. During detach(9E), if SCSA is
6676*4c06356bSdh  * successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
6677*4c06356bSdh  * this should always return B_FALSE.
6678*4c06356bSdh  */
6679*4c06356bSdh boolean_t
6680*4c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
6681*4c06356bSdh {
6682*4c06356bSdh 	pmcs_xscsi_t *xp;
6683*4c06356bSdh 	int i;
6684*4c06356bSdh 
6685*4c06356bSdh 	mutex_enter(&pwp->lock);
6686*4c06356bSdh 
6687*4c06356bSdh 	if (!pwp->targets || !pwp->max_dev) {
6688*4c06356bSdh 		mutex_exit(&pwp->lock);
6689*4c06356bSdh 		return (B_FALSE);
6690*4c06356bSdh 	}
6691*4c06356bSdh 
6692*4c06356bSdh 	for (i = 0; i < pwp->max_dev; i++) {
6693*4c06356bSdh 		xp = pwp->targets[i];
6694*4c06356bSdh 		if ((xp == NULL) || (xp->phy == NULL) ||
6695*4c06356bSdh 		    (xp->phy->iport != iport)) {
6696*4c06356bSdh 			continue;
6697*4c06356bSdh 		}
6698*4c06356bSdh 
6699*4c06356bSdh 		mutex_exit(&pwp->lock);
6700*4c06356bSdh 		return (B_TRUE);
6701*4c06356bSdh 	}
6702*4c06356bSdh 
6703*4c06356bSdh 	mutex_exit(&pwp->lock);
6704*4c06356bSdh 	return (B_FALSE);
6705*4c06356bSdh }
6706*4c06356bSdh 
6707*4c06356bSdh /*
6708*4c06356bSdh  * Called with softstate lock held
6709*4c06356bSdh  */
6710*4c06356bSdh void
6711*4c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target)
6712*4c06356bSdh {
6713*4c06356bSdh 	pmcs_hw_t *pwp = target->pwp;
6714*4c06356bSdh 	pmcs_iport_t *iport;
6715*4c06356bSdh 
6716*4c06356bSdh 	ASSERT(pwp);
6717*4c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
6718*4c06356bSdh 
6719*4c06356bSdh 	if (!target->ua) {
6720*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
6721*4c06356bSdh 		    "%s: target %p iport addres is null",
6722*4c06356bSdh 		    __func__, (void *)target);
6723*4c06356bSdh 	}
6724*4c06356bSdh 
6725*4c06356bSdh 	iport = pmcs_get_iport_by_ua(pwp, target->ua);
6726*4c06356bSdh 	if (iport == NULL) {
6727*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
6728*4c06356bSdh 		    "%s: no iport associated with tgt(0x%p)",
6729*4c06356bSdh 		    __func__, (void *)target);
6730*4c06356bSdh 		return;
6731*4c06356bSdh 	}
6732*4c06356bSdh 
6733*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
6734*4c06356bSdh 	    "%s: free target %p", __func__, (void *)target);
6735*4c06356bSdh 	if (target->ua) {
6736*4c06356bSdh 		strfree(target->ua);
6737*4c06356bSdh 	}
6738*4c06356bSdh 
6739*4c06356bSdh 	mutex_destroy(&target->wqlock);
6740*4c06356bSdh 	mutex_destroy(&target->aqlock);
6741*4c06356bSdh 	mutex_destroy(&target->statlock);
6742*4c06356bSdh 	cv_destroy(&target->reset_cv);
6743*4c06356bSdh 	cv_destroy(&target->abort_cv);
6744*4c06356bSdh 	ddi_soft_state_bystr_fini(&target->lun_sstate);
6745*4c06356bSdh 	ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
6746*4c06356bSdh 	pmcs_rele_iport(iport);
6747*4c06356bSdh }
6748*4c06356bSdh 
6749*4c06356bSdh /*
6750*4c06356bSdh  * Get device state.  Called with statlock and PHY lock held.
6751*4c06356bSdh  */
6752*4c06356bSdh int
6753*4c06356bSdh pmcs_get_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t *ds)
6754*4c06356bSdh {
6755*4c06356bSdh 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
6756*4c06356bSdh 	int result;
6757*4c06356bSdh 	struct pmcwork *pwrk;
6758*4c06356bSdh 	pmcs_phy_t *phyp;
6759*4c06356bSdh 
6760*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: tgt(0x%p)", __func__, (void *)xp);
6761*4c06356bSdh 	if (xp == NULL) {
6762*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is NULL", __func__);
6763*4c06356bSdh 		return (-1);
6764*4c06356bSdh 	}
6765*4c06356bSdh 
6766*4c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
6767*4c06356bSdh 	phyp = xp->phy;
6768*4c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
6769*4c06356bSdh 
6770*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
6771*4c06356bSdh 	if (pwrk == NULL) {
6772*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
6773*4c06356bSdh 		return (-1);
6774*4c06356bSdh 	}
6775*4c06356bSdh 	pwrk->arg = msg;
6776*4c06356bSdh 	pwrk->dtype = phyp->dtype;
6777*4c06356bSdh 
6778*4c06356bSdh 	if (phyp->valid_device_id == 0) {
6779*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6780*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__);
6781*4c06356bSdh 		return (-1);
6782*4c06356bSdh 	}
6783*4c06356bSdh 	htag = pwrk->htag;
6784*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
6785*4c06356bSdh 	    PMCIN_GET_DEVICE_STATE));
6786*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
6787*4c06356bSdh 	msg[2] = LE_32(phyp->device_id);
6788*4c06356bSdh 
6789*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6790*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6791*4c06356bSdh 	if (ptr == NULL) {
6792*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6793*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6794*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
6795*4c06356bSdh 		return (-1);
6796*4c06356bSdh 	}
6797*4c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
6798*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6799*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6800*4c06356bSdh 	mutex_exit(&xp->statlock);
6801*4c06356bSdh 	pmcs_unlock_phy(phyp);
6802*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
6803*4c06356bSdh 	pmcs_lock_phy(phyp);
6804*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
6805*4c06356bSdh 	mutex_enter(&xp->statlock);
6806*4c06356bSdh 
6807*4c06356bSdh 	if (result) {
6808*4c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
6809*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cmd timed out, returning ",
6810*4c06356bSdh 		    __func__);
6811*4c06356bSdh 		return (-1);
6812*4c06356bSdh 	}
6813*4c06356bSdh 	if (LE_32(msg[2]) == 0) {
6814*4c06356bSdh 		*ds = (uint8_t)(LE_32(msg[4]));
6815*4c06356bSdh 		if (*ds !=  xp->dev_state) {
6816*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6817*4c06356bSdh 			    "%s: retrieved_ds=0x%x, target_ds=0x%x", __func__,
6818*4c06356bSdh 			    *ds, xp->dev_state);
6819*4c06356bSdh 		}
6820*4c06356bSdh 		return (0);
6821*4c06356bSdh 	} else {
6822*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6823*4c06356bSdh 		    "%s: cmd failed Status(0x%x), returning ", __func__,
6824*4c06356bSdh 		    LE_32(msg[2]));
6825*4c06356bSdh 		return (-1);
6826*4c06356bSdh 	}
6827*4c06356bSdh }
6828*4c06356bSdh 
6829*4c06356bSdh /*
6830*4c06356bSdh  * Set device state.  Called with target's statlock and PHY lock held.
6831*4c06356bSdh  */
6832*4c06356bSdh int
6833*4c06356bSdh pmcs_set_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t ds)
6834*4c06356bSdh {
6835*4c06356bSdh 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
6836*4c06356bSdh 	int result;
6837*4c06356bSdh 	uint8_t pds, nds;
6838*4c06356bSdh 	struct pmcwork *pwrk;
6839*4c06356bSdh 	pmcs_phy_t *phyp;
6840*4c06356bSdh 
6841*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds(0x%x), tgt(0x%p)",
6842*4c06356bSdh 	    __func__, ds, (void *)xp);
6843*4c06356bSdh 	if (xp == NULL) {
6844*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is Null", __func__);
6845*4c06356bSdh 		return (-1);
6846*4c06356bSdh 	}
6847*4c06356bSdh 
6848*4c06356bSdh 	phyp = xp->phy;
6849*4c06356bSdh 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
6850*4c06356bSdh 	if (pwrk == NULL) {
6851*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
6852*4c06356bSdh 		return (-1);
6853*4c06356bSdh 	}
6854*4c06356bSdh 	if (phyp == NULL) {
6855*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6856*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null",
6857*4c06356bSdh 		    __func__);
6858*4c06356bSdh 		return (-1);
6859*4c06356bSdh 	}
6860*4c06356bSdh 	if (phyp->valid_device_id == 0) {
6861*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6862*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6863*4c06356bSdh 		    "%s: Invalid DeviceID", __func__);
6864*4c06356bSdh 		return (-1);
6865*4c06356bSdh 	}
6866*4c06356bSdh 	pwrk->arg = msg;
6867*4c06356bSdh 	pwrk->dtype = phyp->dtype;
6868*4c06356bSdh 	htag = pwrk->htag;
6869*4c06356bSdh 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
6870*4c06356bSdh 	    PMCIN_SET_DEVICE_STATE));
6871*4c06356bSdh 	msg[1] = LE_32(pwrk->htag);
6872*4c06356bSdh 	msg[2] = LE_32(phyp->device_id);
6873*4c06356bSdh 	msg[3] = LE_32(ds);
6874*4c06356bSdh 
6875*4c06356bSdh 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6876*4c06356bSdh 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6877*4c06356bSdh 	if (ptr == NULL) {
6878*4c06356bSdh 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6879*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
6880*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
6881*4c06356bSdh 		return (-1);
6882*4c06356bSdh 	}
6883*4c06356bSdh 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
6884*4c06356bSdh 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6885*4c06356bSdh 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6886*4c06356bSdh 
6887*4c06356bSdh 	mutex_exit(&xp->statlock);
6888*4c06356bSdh 	pmcs_unlock_phy(phyp);
6889*4c06356bSdh 	WAIT_FOR(pwrk, 1000, result);
6890*4c06356bSdh 	pmcs_lock_phy(phyp);
6891*4c06356bSdh 	pmcs_pwork(pwp, pwrk);
6892*4c06356bSdh 	mutex_enter(&xp->statlock);
6893*4c06356bSdh 
6894*4c06356bSdh 	if (result) {
6895*4c06356bSdh 		pmcs_timed_out(pwp, htag, __func__);
6896*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6897*4c06356bSdh 		    "%s: cmd timed out, returning", __func__);
6898*4c06356bSdh 		return (-1);
6899*4c06356bSdh 	}
6900*4c06356bSdh 	if (LE_32(msg[2]) == 0) {
6901*4c06356bSdh 		pds = (uint8_t)(LE_32(msg[4]) >> 4);
6902*4c06356bSdh 		nds = (uint8_t)(LE_32(msg[4]) & 0x0000000f);
6903*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: previous_ds=0x%x, "
6904*4c06356bSdh 		    "new_ds=0x%x", __func__, pds, nds);
6905*4c06356bSdh 		xp->dev_state = nds;
6906*4c06356bSdh 		return (0);
6907*4c06356bSdh 	} else {
6908*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6909*4c06356bSdh 		    "%s: cmd failed Status(0x%x), returning ", __func__,
6910*4c06356bSdh 		    LE_32(msg[2]));
6911*4c06356bSdh 		return (-1);
6912*4c06356bSdh 	}
6913*4c06356bSdh }
6914*4c06356bSdh 
6915*4c06356bSdh void
6916*4c06356bSdh pmcs_dev_state_recovery(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
6917*4c06356bSdh {
6918*4c06356bSdh 	uint8_t	ds;
6919*4c06356bSdh 	int rc;
6920*4c06356bSdh 	pmcs_xscsi_t *tgt;
6921*4c06356bSdh 	pmcs_phy_t *pptr, *pnext, *pchild;
6922*4c06356bSdh 
6923*4c06356bSdh 	/*
6924*4c06356bSdh 	 * First time, check to see if we're already performing recovery
6925*4c06356bSdh 	 */
6926*4c06356bSdh 	if (phyp == NULL) {
6927*4c06356bSdh 		mutex_enter(&pwp->lock);
6928*4c06356bSdh 		if (pwp->ds_err_recovering) {
6929*4c06356bSdh 			mutex_exit(&pwp->lock);
6930*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
6931*4c06356bSdh 			return;
6932*4c06356bSdh 		}
6933*4c06356bSdh 
6934*4c06356bSdh 		pwp->ds_err_recovering = 1;
6935*4c06356bSdh 		pptr = pwp->root_phys;
6936*4c06356bSdh 		mutex_exit(&pwp->lock);
6937*4c06356bSdh 	} else {
6938*4c06356bSdh 		pptr = phyp;
6939*4c06356bSdh 	}
6940*4c06356bSdh 
6941*4c06356bSdh 	while (pptr) {
6942*4c06356bSdh 		/*
6943*4c06356bSdh 		 * Since ds_err_recovering is set, we can be assured these
6944*4c06356bSdh 		 * PHYs won't disappear on us while we do this.
6945*4c06356bSdh 		 */
6946*4c06356bSdh 		pmcs_lock_phy(pptr);
6947*4c06356bSdh 		pchild = pptr->children;
6948*4c06356bSdh 		pnext = pptr->sibling;
6949*4c06356bSdh 		pmcs_unlock_phy(pptr);
6950*4c06356bSdh 
6951*4c06356bSdh 		if (pchild) {
6952*4c06356bSdh 			pmcs_dev_state_recovery(pwp, pchild);
6953*4c06356bSdh 		}
6954*4c06356bSdh 
6955*4c06356bSdh 		tgt = NULL;
6956*4c06356bSdh 		pmcs_lock_phy(pptr);
6957*4c06356bSdh 
6958*4c06356bSdh 		if (pptr->dead) {
6959*4c06356bSdh 			goto next_phy;
6960*4c06356bSdh 		}
6961*4c06356bSdh 
6962*4c06356bSdh 		tgt = pptr->target;
6963*4c06356bSdh 		if (tgt == NULL) {
6964*4c06356bSdh 			if (pptr->dtype != NOTHING) {
6965*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG2,
6966*4c06356bSdh 				    "%s: no target for DS error recovery for "
6967*4c06356bSdh 				    "PHY 0x%p", __func__, (void *)pptr);
6968*4c06356bSdh 			}
6969*4c06356bSdh 			goto next_phy;
6970*4c06356bSdh 		}
6971*4c06356bSdh 
6972*4c06356bSdh 		mutex_enter(&tgt->statlock);
6973*4c06356bSdh 
6974*4c06356bSdh 		if (tgt->recover_wait == 0) {
6975*4c06356bSdh 			goto next_phy;
6976*4c06356bSdh 		}
6977*4c06356bSdh 
6978*4c06356bSdh 		if (tgt->dying) {
6979*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
6980*4c06356bSdh 			    "%s: Not doing DS recovery on dying target %p",
6981*4c06356bSdh 			    __func__, (void *)tgt);
6982*4c06356bSdh 			goto next_phy;
6983*4c06356bSdh 		}
6984*4c06356bSdh 
6985*4c06356bSdh 		/*
6986*4c06356bSdh 		 * Step 1: Put the device into the IN_RECOVERY state
6987*4c06356bSdh 		 */
6988*4c06356bSdh 		rc = pmcs_get_dev_state(pwp, tgt, &ds);
6989*4c06356bSdh 		if (rc != 0) {
6990*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
6991*4c06356bSdh 			    "%s: pmcs_get_dev_state on PHY %s "
6992*4c06356bSdh 			    "failed (rc=%d)",
6993*4c06356bSdh 			    __func__, pptr->path, rc);
6994*4c06356bSdh 
6995*4c06356bSdh 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
6996*4c06356bSdh 			    __func__, __LINE__, "pmcs_get_dev_state");
6997*4c06356bSdh 
6998*4c06356bSdh 			goto next_phy;
6999*4c06356bSdh 		}
7000*4c06356bSdh 
7001*4c06356bSdh 		if (tgt->dev_state == ds) {
7002*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7003*4c06356bSdh 			    "%s: Target 0x%p already IN_RECOVERY", __func__,
7004*4c06356bSdh 			    (void *)tgt);
7005*4c06356bSdh 		} else {
7006*4c06356bSdh 			tgt->dev_state = ds;
7007*4c06356bSdh 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
7008*4c06356bSdh 			rc = pmcs_send_err_recovery_cmd(pwp, ds, tgt);
7009*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7010*4c06356bSdh 			    "%s: pmcs_send_err_recovery_cmd "
7011*4c06356bSdh 			    "result(%d) tgt(0x%p) ds(0x%x) tgt->ds(0x%x)",
7012*4c06356bSdh 			    __func__, rc, (void *)tgt, ds, tgt->dev_state);
7013*4c06356bSdh 
7014*4c06356bSdh 			if (rc) {
7015*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
7016*4c06356bSdh 				    "%s: pmcs_send_err_recovery_cmd to PHY %s "
7017*4c06356bSdh 				    "failed (rc=%d)",
7018*4c06356bSdh 				    __func__, pptr->path, rc);
7019*4c06356bSdh 
7020*4c06356bSdh 				pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
7021*4c06356bSdh 				    __func__, __LINE__,
7022*4c06356bSdh 				    "pmcs_send_err_recovery_cmd");
7023*4c06356bSdh 
7024*4c06356bSdh 				goto next_phy;
7025*4c06356bSdh 			}
7026*4c06356bSdh 		}
7027*4c06356bSdh 
7028*4c06356bSdh 		/*
7029*4c06356bSdh 		 * Step 2: Perform a hard reset on the PHY
7030*4c06356bSdh 		 */
7031*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7032*4c06356bSdh 		    "%s: Issue HARD_RESET to PHY %s", __func__, pptr->path);
7033*4c06356bSdh 		/*
7034*4c06356bSdh 		 * Must release statlock here because pmcs_reset_phy will
7035*4c06356bSdh 		 * drop and reacquire the PHY lock.
7036*4c06356bSdh 		 */
7037*4c06356bSdh 		mutex_exit(&tgt->statlock);
7038*4c06356bSdh 		rc = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_HARD_RESET);
7039*4c06356bSdh 		mutex_enter(&tgt->statlock);
7040*4c06356bSdh 		if (rc) {
7041*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
7042*4c06356bSdh 			    "%s: HARD_RESET to PHY %s failed (rc=%d)",
7043*4c06356bSdh 			    __func__, pptr->path, rc);
7044*4c06356bSdh 
7045*4c06356bSdh 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
7046*4c06356bSdh 			    __func__, __LINE__, "HARD_RESET");
7047*4c06356bSdh 
7048*4c06356bSdh 			goto next_phy;
7049*4c06356bSdh 		}
7050*4c06356bSdh 
7051*4c06356bSdh 		/*
7052*4c06356bSdh 		 * Step 3: Abort all I/Os to the device
7053*4c06356bSdh 		 */
7054*4c06356bSdh 		if (pptr->abort_all_start) {
7055*4c06356bSdh 			while (pptr->abort_all_start) {
7056*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
7057*4c06356bSdh 				    "%s: Waiting for outstanding ABORT_ALL on "
7058*4c06356bSdh 				    "PHY 0x%p", __func__, (void *)pptr);
7059*4c06356bSdh 				cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
7060*4c06356bSdh 			}
7061*4c06356bSdh 		} else {
7062*4c06356bSdh 			mutex_exit(&tgt->statlock);
7063*4c06356bSdh 			rc = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
7064*4c06356bSdh 			mutex_enter(&tgt->statlock);
7065*4c06356bSdh 			if (rc != 0) {
7066*4c06356bSdh 				pptr->abort_pending = 1;
7067*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
7068*4c06356bSdh 				    "%s: pmcs_abort to PHY %s failed (rc=%d)",
7069*4c06356bSdh 				    __func__, pptr->path, rc);
7070*4c06356bSdh 
7071*4c06356bSdh 				pmcs_handle_ds_recovery_error(pptr, tgt,
7072*4c06356bSdh 				    pwp, __func__, __LINE__, "pmcs_abort");
7073*4c06356bSdh 
7074*4c06356bSdh 				goto next_phy;
7075*4c06356bSdh 			}
7076*4c06356bSdh 		}
7077*4c06356bSdh 
7078*4c06356bSdh 		/*
7079*4c06356bSdh 		 * Step 4: Set the device back to OPERATIONAL state
7080*4c06356bSdh 		 */
7081*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7082*4c06356bSdh 		    "%s: Set PHY/tgt 0x%p/0x%p to OPERATIONAL state",
7083*4c06356bSdh 		    __func__, (void *)pptr, (void *)tgt);
7084*4c06356bSdh 		rc = pmcs_set_dev_state(pwp, tgt,
7085*4c06356bSdh 		    PMCS_DEVICE_STATE_OPERATIONAL);
7086*4c06356bSdh 		if (rc == 0) {
7087*4c06356bSdh 			tgt->recover_wait = 0;
7088*4c06356bSdh 			pptr->ds_recovery_retries = 0;
7089*4c06356bSdh 			/*
7090*4c06356bSdh 			 * Don't bother to run the work queues if the PHY
7091*4c06356bSdh 			 * is dead.
7092*4c06356bSdh 			 */
7093*4c06356bSdh 			if (tgt->phy && !tgt->phy->dead) {
7094*4c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
7095*4c06356bSdh 				(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
7096*4c06356bSdh 				    pwp, DDI_NOSLEEP);
7097*4c06356bSdh 			}
7098*4c06356bSdh 		} else {
7099*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7100*4c06356bSdh 			    "%s: Failed to SET tgt 0x%p to OPERATIONAL state",
7101*4c06356bSdh 			    __func__, (void *)tgt);
7102*4c06356bSdh 
7103*4c06356bSdh 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
7104*4c06356bSdh 			    __func__, __LINE__, "SET tgt to OPERATIONAL state");
7105*4c06356bSdh 
7106*4c06356bSdh 			goto next_phy;
7107*4c06356bSdh 		}
7108*4c06356bSdh 
7109*4c06356bSdh next_phy:
7110*4c06356bSdh 		if (tgt) {
7111*4c06356bSdh 			mutex_exit(&tgt->statlock);
7112*4c06356bSdh 		}
7113*4c06356bSdh 		pmcs_unlock_phy(pptr);
7114*4c06356bSdh 		pptr = pnext;
7115*4c06356bSdh 	}
7116*4c06356bSdh 
7117*4c06356bSdh 	/*
7118*4c06356bSdh 	 * Only clear ds_err_recovering if we're exiting for good and not
7119*4c06356bSdh 	 * just unwinding from recursion
7120*4c06356bSdh 	 */
7121*4c06356bSdh 	if (phyp == NULL) {
7122*4c06356bSdh 		mutex_enter(&pwp->lock);
7123*4c06356bSdh 		pwp->ds_err_recovering = 0;
7124*4c06356bSdh 		mutex_exit(&pwp->lock);
7125*4c06356bSdh 	}
7126*4c06356bSdh }
7127*4c06356bSdh 
7128*4c06356bSdh /*
7129*4c06356bSdh  * Called with target's statlock and PHY lock held.
7130*4c06356bSdh  */
7131*4c06356bSdh int
7132*4c06356bSdh pmcs_send_err_recovery_cmd(pmcs_hw_t *pwp, uint8_t dev_state, pmcs_xscsi_t *tgt)
7133*4c06356bSdh {
7134*4c06356bSdh 	pmcs_phy_t *pptr;
7135*4c06356bSdh 	int rc = -1;
7136*4c06356bSdh 
7137*4c06356bSdh 	ASSERT(tgt != NULL);
7138*4c06356bSdh 	ASSERT(mutex_owned(&tgt->statlock));
7139*4c06356bSdh 
7140*4c06356bSdh 	if (tgt->recovering) {
7141*4c06356bSdh 		return (0);
7142*4c06356bSdh 	}
7143*4c06356bSdh 
7144*4c06356bSdh 	tgt->recovering = 1;
7145*4c06356bSdh 	pptr = tgt->phy;
7146*4c06356bSdh 
7147*4c06356bSdh 	if (pptr == NULL) {
7148*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null",
7149*4c06356bSdh 		    __func__);
7150*4c06356bSdh 		return (-1);
7151*4c06356bSdh 	}
7152*4c06356bSdh 
7153*4c06356bSdh 	ASSERT(mutex_owned(&pptr->phy_lock));
7154*4c06356bSdh 
7155*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds: 0x%x, tgt ds(0x%x)",
7156*4c06356bSdh 	    __func__, dev_state, tgt->dev_state);
7157*4c06356bSdh 
7158*4c06356bSdh 	switch (dev_state) {
7159*4c06356bSdh 	case PMCS_DEVICE_STATE_IN_RECOVERY:
7160*4c06356bSdh 		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
7161*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7162*4c06356bSdh 			    "%s: Target 0x%p already IN_RECOVERY", __func__,
7163*4c06356bSdh 			    (void *)tgt);
7164*4c06356bSdh 			rc = 0;	/* This is not an error */
7165*4c06356bSdh 			goto no_action;
7166*4c06356bSdh 		}
7167*4c06356bSdh 
7168*4c06356bSdh 		rc = pmcs_set_dev_state(pwp, tgt,
7169*4c06356bSdh 		    PMCS_DEVICE_STATE_IN_RECOVERY);
7170*4c06356bSdh 		if (rc != 0) {
7171*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7172*4c06356bSdh 			    "%s(1): Failed to SET tgt(0x%p) to _IN_RECOVERY",
7173*4c06356bSdh 			    __func__, (void *)tgt);
7174*4c06356bSdh 		}
7175*4c06356bSdh 
7176*4c06356bSdh 		break;
7177*4c06356bSdh 
7178*4c06356bSdh 	case PMCS_DEVICE_STATE_OPERATIONAL:
7179*4c06356bSdh 		if (tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) {
7180*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7181*4c06356bSdh 			    "%s: Target 0x%p not ready to go OPERATIONAL",
7182*4c06356bSdh 			    __func__, (void *)tgt);
7183*4c06356bSdh 			goto no_action;
7184*4c06356bSdh 		}
7185*4c06356bSdh 
7186*4c06356bSdh 		rc = pmcs_set_dev_state(pwp, tgt,
7187*4c06356bSdh 		    PMCS_DEVICE_STATE_OPERATIONAL);
7188*4c06356bSdh 		tgt->reset_success = 1;
7189*4c06356bSdh 		if (rc != 0) {
7190*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7191*4c06356bSdh 			    "%s(2): Failed to SET tgt(0x%p) to OPERATIONAL",
7192*4c06356bSdh 			    __func__, (void *)tgt);
7193*4c06356bSdh 			tgt->reset_success = 0;
7194*4c06356bSdh 		}
7195*4c06356bSdh 
7196*4c06356bSdh 		break;
7197*4c06356bSdh 
7198*4c06356bSdh 	case PMCS_DEVICE_STATE_NON_OPERATIONAL:
7199*4c06356bSdh 		PHY_CHANGED(pwp, pptr);
7200*4c06356bSdh 		RESTART_DISCOVERY(pwp);
7201*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7202*4c06356bSdh 		    "%s: Device at %s is non-operational",
7203*4c06356bSdh 		    __func__, pptr->path);
7204*4c06356bSdh 		tgt->dev_state = PMCS_DEVICE_STATE_NON_OPERATIONAL;
7205*4c06356bSdh 		rc = 0;
7206*4c06356bSdh 
7207*4c06356bSdh 		break;
7208*4c06356bSdh 
7209*4c06356bSdh 	default:
7210*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
7211*4c06356bSdh 		    "%s: Invalid state requested (%d)", __func__,
7212*4c06356bSdh 		    dev_state);
7213*4c06356bSdh 		break;
7214*4c06356bSdh 
7215*4c06356bSdh 	}
7216*4c06356bSdh 
7217*4c06356bSdh no_action:
7218*4c06356bSdh 	tgt->recovering = 0;
7219*4c06356bSdh 	return (rc);
7220*4c06356bSdh }
7221*4c06356bSdh 
7222*4c06356bSdh /*
7223*4c06356bSdh  * pmcs_lock_phy_impl
7224*4c06356bSdh  *
7225*4c06356bSdh  * This function is what does the actual work for pmcs_lock_phy.  It will
7226*4c06356bSdh  * lock all PHYs from phyp down in a top-down fashion.
7227*4c06356bSdh  *
7228*4c06356bSdh  * Locking notes:
7229*4c06356bSdh  * 1. level starts from 0 for the PHY ("parent") that's passed in.  It is
7230*4c06356bSdh  * not a reflection of the actual level of the PHY in the SAS topology.
7231*4c06356bSdh  * 2. If parent is an expander, then parent is locked along with all its
7232*4c06356bSdh  * descendents.
7233*4c06356bSdh  * 3. Expander subsidiary PHYs at level 0 are not locked.  It is the
7234*4c06356bSdh  * responsibility of the caller to individually lock expander subsidiary PHYs
7235*4c06356bSdh  * at level 0 if necessary.
7236*4c06356bSdh  * 4. Siblings at level 0 are not traversed due to the possibility that we're
7237*4c06356bSdh  * locking a PHY on the dead list.  The siblings could be pointing to invalid
7238*4c06356bSdh  * PHYs.  We don't lock siblings at level 0 anyway.
7239*4c06356bSdh  */
7240*4c06356bSdh static void
7241*4c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
7242*4c06356bSdh {
7243*4c06356bSdh 	pmcs_phy_t *tphyp;
7244*4c06356bSdh 
7245*4c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
7246*4c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
7247*4c06356bSdh 
7248*4c06356bSdh 	/*
7249*4c06356bSdh 	 * Start walking the PHYs.
7250*4c06356bSdh 	 */
7251*4c06356bSdh 	tphyp = phyp;
7252*4c06356bSdh 	while (tphyp) {
7253*4c06356bSdh 		/*
7254*4c06356bSdh 		 * If we're at the top level, only lock ourselves.  For anything
7255*4c06356bSdh 		 * at level > 0, traverse children while locking everything.
7256*4c06356bSdh 		 */
7257*4c06356bSdh 		if ((level > 0) || (tphyp == phyp)) {
7258*4c06356bSdh 			pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7259*4c06356bSdh 			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
7260*4c06356bSdh 			    __func__, (void *)tphyp, (void *)tphyp->parent,
7261*4c06356bSdh 			    tphyp->path, level);
7262*4c06356bSdh 			mutex_enter(&tphyp->phy_lock);
7263*4c06356bSdh 
7264*4c06356bSdh 			if (tphyp->children) {
7265*4c06356bSdh 				pmcs_lock_phy_impl(tphyp->children, level + 1);
7266*4c06356bSdh 			}
7267*4c06356bSdh 		}
7268*4c06356bSdh 
7269*4c06356bSdh 		if (level == 0) {
7270*4c06356bSdh 			return;
7271*4c06356bSdh 		}
7272*4c06356bSdh 
7273*4c06356bSdh 		tphyp = tphyp->sibling;
7274*4c06356bSdh 	}
7275*4c06356bSdh }
7276*4c06356bSdh 
7277*4c06356bSdh /*
7278*4c06356bSdh  * pmcs_lock_phy
7279*4c06356bSdh  *
7280*4c06356bSdh  * This function is responsible for locking a PHY and all its descendents
7281*4c06356bSdh  */
7282*4c06356bSdh void
7283*4c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp)
7284*4c06356bSdh {
7285*4c06356bSdh #ifdef DEBUG
7286*4c06356bSdh 	char *callername = NULL;
7287*4c06356bSdh 	ulong_t off;
7288*4c06356bSdh 
7289*4c06356bSdh 	ASSERT(phyp != NULL);
7290*4c06356bSdh 
7291*4c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
7292*4c06356bSdh 
7293*4c06356bSdh 	if (callername == NULL) {
7294*4c06356bSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7295*4c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
7296*4c06356bSdh 		    (void *)phyp, phyp->path);
7297*4c06356bSdh 	} else {
7298*4c06356bSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7299*4c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
7300*4c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
7301*4c06356bSdh 	}
7302*4c06356bSdh #else
7303*4c06356bSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7304*4c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
7305*4c06356bSdh #endif
7306*4c06356bSdh 	pmcs_lock_phy_impl(phyp, 0);
7307*4c06356bSdh }
7308*4c06356bSdh 
7309*4c06356bSdh /*
7310*4c06356bSdh  * pmcs_unlock_phy_impl
7311*4c06356bSdh  *
7312*4c06356bSdh  * Unlock all PHYs from phyp down in a bottom-up fashion.
7313*4c06356bSdh  */
7314*4c06356bSdh static void
7315*4c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
7316*4c06356bSdh {
7317*4c06356bSdh 	pmcs_phy_t *phy_next;
7318*4c06356bSdh 
7319*4c06356bSdh 	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
7320*4c06356bSdh 	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
7321*4c06356bSdh 
7322*4c06356bSdh 	/*
7323*4c06356bSdh 	 * Recurse down to the bottom PHYs
7324*4c06356bSdh 	 */
7325*4c06356bSdh 	if (level == 0) {
7326*4c06356bSdh 		if (phyp->children) {
7327*4c06356bSdh 			pmcs_unlock_phy_impl(phyp->children, level + 1);
7328*4c06356bSdh 		}
7329*4c06356bSdh 	} else {
7330*4c06356bSdh 		phy_next = phyp;
7331*4c06356bSdh 		while (phy_next) {
7332*4c06356bSdh 			if (phy_next->children) {
7333*4c06356bSdh 				pmcs_unlock_phy_impl(phy_next->children,
7334*4c06356bSdh 				    level + 1);
7335*4c06356bSdh 			}
7336*4c06356bSdh 			phy_next = phy_next->sibling;
7337*4c06356bSdh 		}
7338*4c06356bSdh 	}
7339*4c06356bSdh 
7340*4c06356bSdh 	/*
7341*4c06356bSdh 	 * Iterate through PHYs unlocking all at level > 0 as well the top PHY
7342*4c06356bSdh 	 */
7343*4c06356bSdh 	phy_next = phyp;
7344*4c06356bSdh 	while (phy_next) {
7345*4c06356bSdh 		if ((level > 0) || (phy_next == phyp)) {
7346*4c06356bSdh 			pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7347*4c06356bSdh 			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
7348*4c06356bSdh 			    __func__, (void *)phy_next,
7349*4c06356bSdh 			    (void *)phy_next->parent, phy_next->path, level);
7350*4c06356bSdh 			mutex_exit(&phy_next->phy_lock);
7351*4c06356bSdh 		}
7352*4c06356bSdh 
7353*4c06356bSdh 		if (level == 0) {
7354*4c06356bSdh 			return;
7355*4c06356bSdh 		}
7356*4c06356bSdh 
7357*4c06356bSdh 		phy_next = phy_next->sibling;
7358*4c06356bSdh 	}
7359*4c06356bSdh }
7360*4c06356bSdh 
7361*4c06356bSdh /*
7362*4c06356bSdh  * pmcs_unlock_phy
7363*4c06356bSdh  *
7364*4c06356bSdh  * Unlock a PHY and all its descendents
7365*4c06356bSdh  */
7366*4c06356bSdh void
7367*4c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp)
7368*4c06356bSdh {
7369*4c06356bSdh #ifdef DEBUG
7370*4c06356bSdh 	char *callername = NULL;
7371*4c06356bSdh 	ulong_t off;
7372*4c06356bSdh 
7373*4c06356bSdh 	ASSERT(phyp != NULL);
7374*4c06356bSdh 
7375*4c06356bSdh 	callername = modgetsymname((uintptr_t)caller(), &off);
7376*4c06356bSdh 
7377*4c06356bSdh 	if (callername == NULL) {
7378*4c06356bSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7379*4c06356bSdh 		    "%s: PHY 0x%p path %s caller: unknown", __func__,
7380*4c06356bSdh 		    (void *)phyp, phyp->path);
7381*4c06356bSdh 	} else {
7382*4c06356bSdh 		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7383*4c06356bSdh 		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
7384*4c06356bSdh 		    (void *)phyp, phyp->path, callername, off);
7385*4c06356bSdh 	}
7386*4c06356bSdh #else
7387*4c06356bSdh 	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
7388*4c06356bSdh 	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
7389*4c06356bSdh #endif
7390*4c06356bSdh 	pmcs_unlock_phy_impl(phyp, 0);
7391*4c06356bSdh }
7392*4c06356bSdh 
7393*4c06356bSdh /*
7394*4c06356bSdh  * pmcs_get_root_phy
7395*4c06356bSdh  *
7396*4c06356bSdh  * For a given phy pointer return its root phy.
7397*4c06356bSdh  * The caller must be holding the lock on every PHY from phyp up to the root.
7398*4c06356bSdh  */
7399*4c06356bSdh pmcs_phy_t *
7400*4c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp)
7401*4c06356bSdh {
7402*4c06356bSdh 	ASSERT(phyp);
7403*4c06356bSdh 
7404*4c06356bSdh 	while (phyp) {
7405*4c06356bSdh 		if (IS_ROOT_PHY(phyp)) {
7406*4c06356bSdh 			break;
7407*4c06356bSdh 		}
7408*4c06356bSdh 		phyp = phyp->parent;
7409*4c06356bSdh 	}
7410*4c06356bSdh 
7411*4c06356bSdh 	return (phyp);
7412*4c06356bSdh }
7413*4c06356bSdh 
7414*4c06356bSdh /*
7415*4c06356bSdh  * pmcs_free_dma_chunklist
7416*4c06356bSdh  *
7417*4c06356bSdh  * Free DMA S/G chunk list
7418*4c06356bSdh  */
7419*4c06356bSdh void
7420*4c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
7421*4c06356bSdh {
7422*4c06356bSdh 	pmcs_chunk_t	*pchunk;
7423*4c06356bSdh 
7424*4c06356bSdh 	while (pwp->dma_chunklist) {
7425*4c06356bSdh 		pchunk = pwp->dma_chunklist;
7426*4c06356bSdh 		pwp->dma_chunklist = pwp->dma_chunklist->next;
7427*4c06356bSdh 		if (pchunk->dma_handle) {
7428*4c06356bSdh 			if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
7429*4c06356bSdh 			    DDI_SUCCESS) {
7430*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed"
7431*4c06356bSdh 				    " at %s():%d", __func__, __LINE__);
7432*4c06356bSdh 			}
7433*4c06356bSdh 			ddi_dma_free_handle(&pchunk->dma_handle);
7434*4c06356bSdh 			ddi_dma_mem_free(&pchunk->acc_handle);
7435*4c06356bSdh 		}
7436*4c06356bSdh 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
7437*4c06356bSdh 	}
7438*4c06356bSdh }
7439*4c06356bSdh 
7440*4c06356bSdh 
7441*4c06356bSdh /*
7442*4c06356bSdh  * Start ssp event recovery. We have to schedule recovery operation because
7443*4c06356bSdh  * it involves sending multiple commands to device and we should not do it
7444*4c06356bSdh  * in the interrupt context.
7445*4c06356bSdh  * If it is failure of a recovery command, let the recovery thread deal with it.
7446*4c06356bSdh  * Called with pmcwork lock held.
7447*4c06356bSdh  */
7448*4c06356bSdh 
7449*4c06356bSdh void
7450*4c06356bSdh pmcs_start_ssp_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
7451*4c06356bSdh     size_t amt)
7452*4c06356bSdh {
7453*4c06356bSdh 	pmcs_xscsi_t *tgt = pwrk->xp;
7454*4c06356bSdh 	uint32_t event = LE_32(iomb[2]);
7455*4c06356bSdh 	pmcs_phy_t *pptr = pwrk->phy;
7456*4c06356bSdh 	uint32_t tag;
7457*4c06356bSdh 
7458*4c06356bSdh 	if (tgt != NULL) {
7459*4c06356bSdh 		mutex_enter(&tgt->statlock);
7460*4c06356bSdh 		if (tgt->dying || !tgt->assigned) {
7461*4c06356bSdh 			if (pptr) {
7462*4c06356bSdh 				pmcs_dec_phy_ref_count(pptr);
7463*4c06356bSdh 			}
7464*4c06356bSdh 			pptr = NULL;
7465*4c06356bSdh 			pwrk->phy = NULL;
7466*4c06356bSdh 		}
7467*4c06356bSdh 		mutex_exit(&tgt->statlock);
7468*4c06356bSdh 	}
7469*4c06356bSdh 	if (pptr == NULL) {
7470*4c06356bSdh 		/*
7471*4c06356bSdh 		 * No target or dying target.Need to run RE-DISCOVERY here.
7472*4c06356bSdh 		 */
7473*4c06356bSdh 		if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
7474*4c06356bSdh 			pwrk->state = PMCS_WORK_STATE_INTR;
7475*4c06356bSdh 		}
7476*4c06356bSdh 		/*
7477*4c06356bSdh 		 * Although we cannot mark phy to force abort nor mark phy
7478*4c06356bSdh 		 * as changed, killing of a target would take care of aborting
7479*4c06356bSdh 		 * commands for the device.
7480*4c06356bSdh 		 */
7481*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: No valid target for event "
7482*4c06356bSdh 		    "processing found. Scheduling RECONFIGURE",  __func__);
7483*4c06356bSdh 		pmcs_pwork(pwp, pwrk);
7484*4c06356bSdh 		RESTART_DISCOVERY(pwp);
7485*4c06356bSdh 		return;
7486*4c06356bSdh 	} else {
7487*4c06356bSdh 		pmcs_lock_phy(pptr);
7488*4c06356bSdh 		mutex_enter(&tgt->statlock);
7489*4c06356bSdh 		if (event == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
7490*4c06356bSdh 			if (tgt->dev_state !=
7491*4c06356bSdh 			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
7492*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device at "
7493*4c06356bSdh 				    "%s is non-operational", __func__,
7494*4c06356bSdh 				    pptr->path);
7495*4c06356bSdh 				tgt->dev_state =
7496*4c06356bSdh 				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
7497*4c06356bSdh 			}
7498*4c06356bSdh 			pptr->abort_pending = 1;
7499*4c06356bSdh 			mutex_exit(&tgt->statlock);
7500*4c06356bSdh 			pmcs_unlock_phy(pptr);
7501*4c06356bSdh 			mutex_exit(&pwrk->lock);
7502*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7503*4c06356bSdh 			RESTART_DISCOVERY(pwp);
7504*4c06356bSdh 			return;
7505*4c06356bSdh 		}
7506*4c06356bSdh 
7507*4c06356bSdh 		/*
7508*4c06356bSdh 		 * If this command is run in WAIT mode, it is a failing recovery
7509*4c06356bSdh 		 * command. If so, just wake up recovery thread waiting for
7510*4c06356bSdh 		 * command completion.
7511*4c06356bSdh 		 */
7512*4c06356bSdh 		tag = PMCS_TAG_TYPE(pwrk->htag);
7513*4c06356bSdh 		if (tag == PMCS_TAG_TYPE_WAIT) {
7514*4c06356bSdh 			pwrk->htag |= PMCS_TAG_DONE;
7515*4c06356bSdh 			if (pwrk->arg && amt) {
7516*4c06356bSdh 				(void) memcpy(pwrk->arg, iomb, amt);
7517*4c06356bSdh 			}
7518*4c06356bSdh 			cv_signal(&pwrk->sleep_cv);
7519*4c06356bSdh 			mutex_exit(&tgt->statlock);
7520*4c06356bSdh 			pmcs_unlock_phy(pptr);
7521*4c06356bSdh 			mutex_exit(&pwrk->lock);
7522*4c06356bSdh 			return;
7523*4c06356bSdh 		}
7524*4c06356bSdh 
7525*4c06356bSdh 		/*
7526*4c06356bSdh 		 * To recover from primary failures,
7527*4c06356bSdh 		 * we need to schedule handling events recovery.
7528*4c06356bSdh 		 */
7529*4c06356bSdh 		tgt->event_recovery = 1;
7530*4c06356bSdh 		mutex_exit(&tgt->statlock);
7531*4c06356bSdh 		pmcs_unlock_phy(pptr);
7532*4c06356bSdh 		pwrk->ssp_event = event;
7533*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
7534*4c06356bSdh 		    "%s: Scheduling SSP event recovery for tgt(0x%p) "
7535*4c06356bSdh 		    "pwrk(%p) tag(0x%x)", __func__, (void *)tgt, (void *)pwrk,
7536*4c06356bSdh 		    pwrk->htag);
7537*4c06356bSdh 		mutex_exit(&pwrk->lock);
7538*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_SSP_EVT_RECOVERY);
7539*4c06356bSdh 	}
7540*4c06356bSdh 
7541*4c06356bSdh 	/* Work cannot be completed until event recovery is completed. */
7542*4c06356bSdh }
7543*4c06356bSdh 
7544*4c06356bSdh /*
7545*4c06356bSdh  * SSP target event recovery
7546*4c06356bSdh  * Entered with a phy lock held
7547*4c06356bSdh  * Pwrk lock is not needed - pwrk is on the target aq and no other thread
7548*4c06356bSdh  * will do anything with it until this thread starts the chain of recovery.
7549*4c06356bSdh  * Statlock may be acquired and released.
7550*4c06356bSdh  */
7551*4c06356bSdh 
7552*4c06356bSdh void
7553*4c06356bSdh pmcs_tgt_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk)
7554*4c06356bSdh {
7555*4c06356bSdh 	pmcs_phy_t *pptr = pwrk->phy;
7556*4c06356bSdh 	pmcs_cmd_t *sp = pwrk->arg;
7557*4c06356bSdh 	pmcs_lun_t *lun = sp->cmd_lun;
7558*4c06356bSdh 	pmcs_xscsi_t *tgt = pwrk->xp;
7559*4c06356bSdh 	uint32_t event;
7560*4c06356bSdh 	uint32_t htag;
7561*4c06356bSdh 	uint32_t status;
7562*4c06356bSdh 	uint8_t dstate;
7563*4c06356bSdh 	int rv;
7564*4c06356bSdh 
7565*4c06356bSdh 	ASSERT(pwrk->arg != NULL);
7566*4c06356bSdh 	ASSERT(pwrk->xp != NULL);
7567*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: event recovery for "
7568*4c06356bSdh 	    "target 0x%p", __func__, (void *)pwrk->xp);
7569*4c06356bSdh 	htag = pwrk->htag;
7570*4c06356bSdh 	event = pwrk->ssp_event;
7571*4c06356bSdh 	pwrk->ssp_event = 0xffffffff;
7572*4c06356bSdh 	if (event == PMCOUT_STATUS_XFER_ERR_BREAK ||
7573*4c06356bSdh 	    event == PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY ||
7574*4c06356bSdh 	    event == PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT) {
7575*4c06356bSdh 		/* Command may be still pending on device */
7576*4c06356bSdh 		rv = pmcs_ssp_tmf(pwp, pptr, SAS_QUERY_TASK, htag,
7577*4c06356bSdh 		    lun->lun_num, &status);
7578*4c06356bSdh 		if (rv != 0) {
7579*4c06356bSdh 			goto out;
7580*4c06356bSdh 		}
7581*4c06356bSdh 		if (status == SAS_RSP_TMF_COMPLETE) {
7582*4c06356bSdh 			/* Command NOT pending on a device */
7583*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
7584*4c06356bSdh 			    "%s: No pending command for tgt 0x%p",
7585*4c06356bSdh 			    __func__, (void *)tgt);
7586*4c06356bSdh 			/* Nothing more to do, just abort it on chip */
7587*4c06356bSdh 			htag = 0;
7588*4c06356bSdh 		}
7589*4c06356bSdh 	}
7590*4c06356bSdh 	/*
7591*4c06356bSdh 	 * All other events left the command pending in the host
7592*4c06356bSdh 	 * Send abort task and abort it on the chip
7593*4c06356bSdh 	 */
7594*4c06356bSdh 	if (htag != 0) {
7595*4c06356bSdh 		if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, htag,
7596*4c06356bSdh 		    lun->lun_num, &status))
7597*4c06356bSdh 			goto out;
7598*4c06356bSdh 	}
7599*4c06356bSdh 	(void) pmcs_abort(pwp, pptr, pwrk->htag, 0, 1);
7600*4c06356bSdh 	/*
7601*4c06356bSdh 	 * Abort either took care of work completion, or put device in
7602*4c06356bSdh 	 * a recovery state
7603*4c06356bSdh 	 */
7604*4c06356bSdh 	return;
7605*4c06356bSdh out:
7606*4c06356bSdh 	/* Abort failed, do full device recovery */
7607*4c06356bSdh 	mutex_enter(&tgt->statlock);
7608*4c06356bSdh 	if (!pmcs_get_dev_state(pwp, tgt, &dstate))
7609*4c06356bSdh 		tgt->dev_state = dstate;
7610*4c06356bSdh 
7611*4c06356bSdh 	if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
7612*4c06356bSdh 	    (tgt->dev_state != PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
7613*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
7614*4c06356bSdh 		    "%s: Setting IN_RECOVERY for tgt 0x%p",
7615*4c06356bSdh 		    __func__, (void *)tgt);
7616*4c06356bSdh 		(void) pmcs_send_err_recovery_cmd(pwp,
7617*4c06356bSdh 		    PMCS_DEVICE_STATE_IN_RECOVERY, tgt);
7618*4c06356bSdh 	}
7619*4c06356bSdh 	mutex_exit(&tgt->statlock);
7620*4c06356bSdh }
7621*4c06356bSdh 
7622*4c06356bSdh /*
7623*4c06356bSdh  * SSP event recovery task.
7624*4c06356bSdh  */
7625*4c06356bSdh void
7626*4c06356bSdh pmcs_ssp_event_recovery(pmcs_hw_t *pwp)
7627*4c06356bSdh {
7628*4c06356bSdh 	int idx;
7629*4c06356bSdh 	pmcs_xscsi_t *tgt;
7630*4c06356bSdh 	pmcs_cmd_t *cp;
7631*4c06356bSdh 	pmcwork_t *pwrk;
7632*4c06356bSdh 	pmcs_phy_t *pphy;
7633*4c06356bSdh 	int er_flag;
7634*4c06356bSdh 	uint32_t idxpwrk;
7635*4c06356bSdh 
7636*4c06356bSdh restart:
7637*4c06356bSdh 	for (idx = 0; idx < pwp->max_dev; idx++) {
7638*4c06356bSdh 		mutex_enter(&pwp->lock);
7639*4c06356bSdh 		tgt = pwp->targets[idx];
7640*4c06356bSdh 		mutex_exit(&pwp->lock);
7641*4c06356bSdh 		if (tgt != NULL) {
7642*4c06356bSdh 			mutex_enter(&tgt->statlock);
7643*4c06356bSdh 			if (tgt->dying || !tgt->assigned) {
7644*4c06356bSdh 				mutex_exit(&tgt->statlock);
7645*4c06356bSdh 				continue;
7646*4c06356bSdh 			}
7647*4c06356bSdh 			pphy = tgt->phy;
7648*4c06356bSdh 			er_flag = tgt->event_recovery;
7649*4c06356bSdh 			mutex_exit(&tgt->statlock);
7650*4c06356bSdh 			if (pphy != NULL && er_flag != 0) {
7651*4c06356bSdh 				pmcs_lock_phy(pphy);
7652*4c06356bSdh 				mutex_enter(&tgt->statlock);
7653*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
7654*4c06356bSdh 				    "%s: found target(0x%p)", __func__,
7655*4c06356bSdh 				    (void *) tgt);
7656*4c06356bSdh 
7657*4c06356bSdh 				/* Check what cmd expects recovery */
7658*4c06356bSdh 				mutex_enter(&tgt->aqlock);
7659*4c06356bSdh 				STAILQ_FOREACH(cp, &tgt->aq, cmd_next) {
7660*4c06356bSdh 					/*
7661*4c06356bSdh 					 * Since work structure is on this
7662*4c06356bSdh 					 * target aq, and only this thread
7663*4c06356bSdh 					 * is accessing it now, we do not need
7664*4c06356bSdh 					 * to lock it
7665*4c06356bSdh 					 */
7666*4c06356bSdh 					idxpwrk = PMCS_TAG_INDEX(cp->cmd_tag);
7667*4c06356bSdh 					pwrk = &pwp->work[idxpwrk];
7668*4c06356bSdh 					if (pwrk->htag != cp->cmd_tag) {
7669*4c06356bSdh 						/*
7670*4c06356bSdh 						 * aq may contain TMF commands,
7671*4c06356bSdh 						 * so we may not find work
7672*4c06356bSdh 						 * structure with htag
7673*4c06356bSdh 						 */
7674*4c06356bSdh 						break;
7675*4c06356bSdh 					}
7676*4c06356bSdh 					if (pwrk->ssp_event != 0 &&
7677*4c06356bSdh 					    pwrk->ssp_event !=
7678*4c06356bSdh 					    PMCS_REC_EVENT) {
7679*4c06356bSdh 						pmcs_prt(pwp,
7680*4c06356bSdh 						    PMCS_PRT_DEBUG,
7681*4c06356bSdh 						    "%s: pwrk(%p) ctag(0x%x)",
7682*4c06356bSdh 						    __func__, (void *) pwrk,
7683*4c06356bSdh 						    cp->cmd_tag);
7684*4c06356bSdh 						mutex_exit(&tgt->aqlock);
7685*4c06356bSdh 						mutex_exit(&tgt->statlock);
7686*4c06356bSdh 						pmcs_tgt_event_recovery(
7687*4c06356bSdh 						    pwp, pwrk);
7688*4c06356bSdh 						/*
7689*4c06356bSdh 						 * We dropped statlock, so
7690*4c06356bSdh 						 * restart scanning from scratch
7691*4c06356bSdh 						 */
7692*4c06356bSdh 						pmcs_unlock_phy(pphy);
7693*4c06356bSdh 						goto restart;
7694*4c06356bSdh 					}
7695*4c06356bSdh 				}
7696*4c06356bSdh 				mutex_exit(&tgt->aqlock);
7697*4c06356bSdh 				tgt->event_recovery = 0;
7698*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG,
7699*4c06356bSdh 				    "%s: end of SSP event recovery for "
7700*4c06356bSdh 				    "target(0x%p)", __func__, (void *) tgt);
7701*4c06356bSdh 				mutex_exit(&tgt->statlock);
7702*4c06356bSdh 				pmcs_unlock_phy(pphy);
7703*4c06356bSdh 			}
7704*4c06356bSdh 		}
7705*4c06356bSdh 	}
7706*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG,
7707*4c06356bSdh 	    "%s: end of SSP event recovery for pwp(0x%p)", __func__,
7708*4c06356bSdh 	    (void *) pwp);
7709*4c06356bSdh }
7710*4c06356bSdh 
7711*4c06356bSdh /*ARGSUSED2*/
7712*4c06356bSdh int
7713*4c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags)
7714*4c06356bSdh {
7715*4c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
7716*4c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
7717*4c06356bSdh 
7718*4c06356bSdh 	mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
7719*4c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
7720*4c06356bSdh 	cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
7721*4c06356bSdh 	return (0);
7722*4c06356bSdh }
7723*4c06356bSdh 
7724*4c06356bSdh /*ARGSUSED1*/
7725*4c06356bSdh void
7726*4c06356bSdh pmcs_phy_destructor(void *buf, void *arg)
7727*4c06356bSdh {
7728*4c06356bSdh 	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
7729*4c06356bSdh 
7730*4c06356bSdh 	cv_destroy(&phyp->abort_all_cv);
7731*4c06356bSdh 	mutex_destroy(&phyp->phy_lock);
7732*4c06356bSdh }
7733*4c06356bSdh 
7734*4c06356bSdh /*
7735*4c06356bSdh  * Free all PHYs from the kmem_cache starting at phyp as well as everything
7736*4c06356bSdh  * on the dead_phys list.
7737*4c06356bSdh  *
7738*4c06356bSdh  * NOTE: This function does not free root PHYs as they are not allocated
7739*4c06356bSdh  * from the kmem_cache.
7740*4c06356bSdh  *
7741*4c06356bSdh  * No PHY locks are acquired as this should only be called during DDI_DETACH
7742*4c06356bSdh  * or soft reset (while pmcs interrupts are disabled).
7743*4c06356bSdh  */
7744*4c06356bSdh void
7745*4c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
7746*4c06356bSdh {
7747*4c06356bSdh 	pmcs_phy_t *tphyp, *nphyp;
7748*4c06356bSdh 
7749*4c06356bSdh 	if (phyp == NULL) {
7750*4c06356bSdh 		return;
7751*4c06356bSdh 	}
7752*4c06356bSdh 
7753*4c06356bSdh 	tphyp = phyp;
7754*4c06356bSdh 	while (tphyp) {
7755*4c06356bSdh 		nphyp = tphyp->sibling;
7756*4c06356bSdh 
7757*4c06356bSdh 		if (tphyp->children) {
7758*4c06356bSdh 			pmcs_free_all_phys(pwp, tphyp->children);
7759*4c06356bSdh 			tphyp->children = NULL;
7760*4c06356bSdh 		}
7761*4c06356bSdh 		if (!IS_ROOT_PHY(tphyp)) {
7762*4c06356bSdh 			kmem_cache_free(pwp->phy_cache, tphyp);
7763*4c06356bSdh 		}
7764*4c06356bSdh 
7765*4c06356bSdh 		tphyp = nphyp;
7766*4c06356bSdh 	}
7767*4c06356bSdh 
7768*4c06356bSdh 	tphyp = pwp->dead_phys;
7769*4c06356bSdh 	while (tphyp) {
7770*4c06356bSdh 		nphyp = tphyp->sibling;
7771*4c06356bSdh 		kmem_cache_free(pwp->phy_cache, tphyp);
7772*4c06356bSdh 		tphyp = nphyp;
7773*4c06356bSdh 	}
7774*4c06356bSdh 	pwp->dead_phys = NULL;
7775*4c06356bSdh }
7776*4c06356bSdh 
7777*4c06356bSdh /*
7778*4c06356bSdh  * Free a list of PHYs linked together by the sibling pointer back to the
7779*4c06356bSdh  * kmem cache from whence they came.  This function does not recurse, so the
7780*4c06356bSdh  * caller must ensure there are no children.
7781*4c06356bSdh  */
7782*4c06356bSdh void
7783*4c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
7784*4c06356bSdh {
7785*4c06356bSdh 	pmcs_phy_t *next_phy;
7786*4c06356bSdh 
7787*4c06356bSdh 	while (phyp) {
7788*4c06356bSdh 		next_phy = phyp->sibling;
7789*4c06356bSdh 		ASSERT(!mutex_owned(&phyp->phy_lock));
7790*4c06356bSdh 		kmem_cache_free(pwp->phy_cache, phyp);
7791*4c06356bSdh 		phyp = next_phy;
7792*4c06356bSdh 	}
7793*4c06356bSdh }
7794*4c06356bSdh 
7795*4c06356bSdh /*
7796*4c06356bSdh  * Make a copy of an existing PHY structure.  This is used primarily in
7797*4c06356bSdh  * discovery to compare the contents of an existing PHY with what gets
7798*4c06356bSdh  * reported back by an expander.
7799*4c06356bSdh  *
7800*4c06356bSdh  * This function must not be called from any context where sleeping is
7801*4c06356bSdh  * not possible.
7802*4c06356bSdh  *
7803*4c06356bSdh  * The new PHY is returned unlocked.
7804*4c06356bSdh  */
7805*4c06356bSdh static pmcs_phy_t *
7806*4c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy)
7807*4c06356bSdh {
7808*4c06356bSdh 	pmcs_phy_t *local;
7809*4c06356bSdh 
7810*4c06356bSdh 	local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
7811*4c06356bSdh 
7812*4c06356bSdh 	/*
7813*4c06356bSdh 	 * Go ahead and just copy everything...
7814*4c06356bSdh 	 */
7815*4c06356bSdh 	*local = *orig_phy;
7816*4c06356bSdh 
7817*4c06356bSdh 	/*
7818*4c06356bSdh 	 * But the following must be set appropriately for this copy
7819*4c06356bSdh 	 */
7820*4c06356bSdh 	local->sibling = NULL;
7821*4c06356bSdh 	local->children = NULL;
7822*4c06356bSdh 	mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
7823*4c06356bSdh 	    DDI_INTR_PRI(orig_phy->pwp->intr_pri));
7824*4c06356bSdh 
7825*4c06356bSdh 	return (local);
7826*4c06356bSdh }
7827*4c06356bSdh 
7828*4c06356bSdh int
7829*4c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle)
7830*4c06356bSdh {
7831*4c06356bSdh 	ddi_fm_error_t de;
7832*4c06356bSdh 
7833*4c06356bSdh 	if (handle == NULL) {
7834*4c06356bSdh 		return (DDI_FAILURE);
7835*4c06356bSdh 	}
7836*4c06356bSdh 	ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
7837*4c06356bSdh 	return (de.fme_status);
7838*4c06356bSdh }
7839*4c06356bSdh 
7840*4c06356bSdh int
7841*4c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle)
7842*4c06356bSdh {
7843*4c06356bSdh 	ddi_fm_error_t de;
7844*4c06356bSdh 
7845*4c06356bSdh 	if (handle == NULL) {
7846*4c06356bSdh 		return (DDI_FAILURE);
7847*4c06356bSdh 	}
7848*4c06356bSdh 	ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
7849*4c06356bSdh 	return (de.fme_status);
7850*4c06356bSdh }
7851*4c06356bSdh 
7852*4c06356bSdh 
7853*4c06356bSdh void
7854*4c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
7855*4c06356bSdh {
7856*4c06356bSdh 	uint64_t ena;
7857*4c06356bSdh 	char buf[FM_MAX_CLASS];
7858*4c06356bSdh 
7859*4c06356bSdh 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
7860*4c06356bSdh 	ena = fm_ena_generate(0, FM_ENA_FMT1);
7861*4c06356bSdh 	if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
7862*4c06356bSdh 		ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
7863*4c06356bSdh 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
7864*4c06356bSdh 	}
7865*4c06356bSdh }
7866*4c06356bSdh 
7867*4c06356bSdh int
7868*4c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
7869*4c06356bSdh {
7870*4c06356bSdh 	pmcs_chunk_t *pchunk;
7871*4c06356bSdh 	int i;
7872*4c06356bSdh 
7873*4c06356bSdh 	/* check all acc & dma handles allocated in attach */
7874*4c06356bSdh 	if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
7875*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
7876*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
7877*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
7878*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
7879*4c06356bSdh 		goto check_failed;
7880*4c06356bSdh 	}
7881*4c06356bSdh 
7882*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
7883*4c06356bSdh 		if ((pmcs_check_dma_handle(
7884*4c06356bSdh 		    pwp->iqp_handles[i]) != DDI_SUCCESS) ||
7885*4c06356bSdh 		    (pmcs_check_acc_handle(
7886*4c06356bSdh 		    pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
7887*4c06356bSdh 			goto check_failed;
7888*4c06356bSdh 		}
7889*4c06356bSdh 	}
7890*4c06356bSdh 
7891*4c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
7892*4c06356bSdh 		if ((pmcs_check_dma_handle(
7893*4c06356bSdh 		    pwp->oqp_handles[i]) != DDI_SUCCESS) ||
7894*4c06356bSdh 		    (pmcs_check_acc_handle(
7895*4c06356bSdh 		    pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
7896*4c06356bSdh 			goto check_failed;
7897*4c06356bSdh 		}
7898*4c06356bSdh 	}
7899*4c06356bSdh 
7900*4c06356bSdh 	if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
7901*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
7902*4c06356bSdh 		goto check_failed;
7903*4c06356bSdh 	}
7904*4c06356bSdh 
7905*4c06356bSdh 	if (pwp->fwlog &&
7906*4c06356bSdh 	    ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
7907*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
7908*4c06356bSdh 		goto check_failed;
7909*4c06356bSdh 	}
7910*4c06356bSdh 
7911*4c06356bSdh 	if (pwp->regdump_hndl && pwp->regdump_acchdl &&
7912*4c06356bSdh 	    ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
7913*4c06356bSdh 	    (pmcs_check_acc_handle(pwp->regdump_acchdl)
7914*4c06356bSdh 	    != DDI_SUCCESS))) {
7915*4c06356bSdh 		goto check_failed;
7916*4c06356bSdh 	}
7917*4c06356bSdh 
7918*4c06356bSdh 
7919*4c06356bSdh 	pchunk = pwp->dma_chunklist;
7920*4c06356bSdh 	while (pchunk) {
7921*4c06356bSdh 		if ((pmcs_check_acc_handle(pchunk->acc_handle)
7922*4c06356bSdh 		    != DDI_SUCCESS) ||
7923*4c06356bSdh 		    (pmcs_check_dma_handle(pchunk->dma_handle)
7924*4c06356bSdh 		    != DDI_SUCCESS)) {
7925*4c06356bSdh 			goto check_failed;
7926*4c06356bSdh 		}
7927*4c06356bSdh 		pchunk = pchunk->next;
7928*4c06356bSdh 	}
7929*4c06356bSdh 
7930*4c06356bSdh 	return (0);
7931*4c06356bSdh 
7932*4c06356bSdh check_failed:
7933*4c06356bSdh 
7934*4c06356bSdh 	return (1);
7935*4c06356bSdh }
7936*4c06356bSdh 
7937*4c06356bSdh /*
7938*4c06356bSdh  * pmcs_handle_dead_phys
7939*4c06356bSdh  *
7940*4c06356bSdh  * If the PHY has no outstanding work associated with it, remove it from
7941*4c06356bSdh  * the dead PHY list and free it.
7942*4c06356bSdh  *
7943*4c06356bSdh  * If pwp->ds_err_recovering or pwp->configuring is set, don't run.
7944*4c06356bSdh  * This keeps routines that need to submit work to the chip from having to
7945*4c06356bSdh  * hold PHY locks to ensure that PHYs don't disappear while they do their work.
7946*4c06356bSdh  */
7947*4c06356bSdh void
7948*4c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp)
7949*4c06356bSdh {
7950*4c06356bSdh 	pmcs_phy_t *phyp, *nphyp, *pphyp;
7951*4c06356bSdh 
7952*4c06356bSdh 	mutex_enter(&pwp->lock);
7953*4c06356bSdh 	mutex_enter(&pwp->config_lock);
7954*4c06356bSdh 
7955*4c06356bSdh 	if (pwp->configuring | pwp->ds_err_recovering) {
7956*4c06356bSdh 		mutex_exit(&pwp->config_lock);
7957*4c06356bSdh 		mutex_exit(&pwp->lock);
7958*4c06356bSdh 		return;
7959*4c06356bSdh 	}
7960*4c06356bSdh 
7961*4c06356bSdh 	/*
7962*4c06356bSdh 	 * Check every PHY in the dead PHY list
7963*4c06356bSdh 	 */
7964*4c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
7965*4c06356bSdh 	phyp = pwp->dead_phys;
7966*4c06356bSdh 	pphyp = NULL;	/* Set previous PHY to NULL */
7967*4c06356bSdh 
7968*4c06356bSdh 	while (phyp != NULL) {
7969*4c06356bSdh 		pmcs_lock_phy(phyp);
7970*4c06356bSdh 		ASSERT(phyp->dead);
7971*4c06356bSdh 
7972*4c06356bSdh 		nphyp = phyp->dead_next;
7973*4c06356bSdh 
7974*4c06356bSdh 		/*
7975*4c06356bSdh 		 * Check for outstanding work
7976*4c06356bSdh 		 */
7977*4c06356bSdh 		if (phyp->ref_count > 0) {
7978*4c06356bSdh 			pmcs_unlock_phy(phyp);
7979*4c06356bSdh 			pphyp = phyp;	/* This PHY becomes "previous" */
7980*4c06356bSdh 		} else if (phyp->target) {
7981*4c06356bSdh 			pmcs_unlock_phy(phyp);
7982*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
7983*4c06356bSdh 			    "%s: Not freeing PHY 0x%p: target 0x%p is not free",
7984*4c06356bSdh 			    __func__, (void *)phyp, (void *)phyp->target);
7985*4c06356bSdh 			pphyp = phyp;
7986*4c06356bSdh 		} else {
7987*4c06356bSdh 			/*
7988*4c06356bSdh 			 * No outstanding work or target references. Remove it
7989*4c06356bSdh 			 * from the list and free it
7990*4c06356bSdh 			 */
7991*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
7992*4c06356bSdh 			    "%s: Freeing inactive dead PHY 0x%p @ %s "
7993*4c06356bSdh 			    "target = 0x%p", __func__, (void *)phyp,
7994*4c06356bSdh 			    phyp->path, (void *)phyp->target);
7995*4c06356bSdh 			/*
7996*4c06356bSdh 			 * If pphyp is NULL, then phyp was the head of the list,
7997*4c06356bSdh 			 * so just reset the head to nphyp. Otherwise, the
7998*4c06356bSdh 			 * previous PHY will now point to nphyp (the next PHY)
7999*4c06356bSdh 			 */
8000*4c06356bSdh 			if (pphyp == NULL) {
8001*4c06356bSdh 				pwp->dead_phys = nphyp;
8002*4c06356bSdh 			} else {
8003*4c06356bSdh 				pphyp->dead_next = nphyp;
8004*4c06356bSdh 			}
8005*4c06356bSdh 			/*
8006*4c06356bSdh 			 * If the target still points to this PHY, remove
8007*4c06356bSdh 			 * that linkage now.
8008*4c06356bSdh 			 */
8009*4c06356bSdh 			if (phyp->target) {
8010*4c06356bSdh 				mutex_enter(&phyp->target->statlock);
8011*4c06356bSdh 				if (phyp->target->phy == phyp) {
8012*4c06356bSdh 					phyp->target->phy = NULL;
8013*4c06356bSdh 				}
8014*4c06356bSdh 				mutex_exit(&phyp->target->statlock);
8015*4c06356bSdh 			}
8016*4c06356bSdh 			kmem_cache_free(pwp->phy_cache, phyp);
8017*4c06356bSdh 		}
8018*4c06356bSdh 
8019*4c06356bSdh 		phyp = nphyp;
8020*4c06356bSdh 	}
8021*4c06356bSdh 
8022*4c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
8023*4c06356bSdh 	mutex_exit(&pwp->config_lock);
8024*4c06356bSdh 	mutex_exit(&pwp->lock);
8025*4c06356bSdh }
8026*4c06356bSdh 
8027*4c06356bSdh void
8028*4c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
8029*4c06356bSdh {
8030*4c06356bSdh 	atomic_inc_32(&phyp->ref_count);
8031*4c06356bSdh }
8032*4c06356bSdh 
8033*4c06356bSdh void
8034*4c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
8035*4c06356bSdh {
8036*4c06356bSdh 	ASSERT(phyp->ref_count != 0);
8037*4c06356bSdh 	atomic_dec_32(&phyp->ref_count);
8038*4c06356bSdh }
8039*4c06356bSdh 
8040*4c06356bSdh /*
8041*4c06356bSdh  * pmcs_reap_dead_phy
8042*4c06356bSdh  *
8043*4c06356bSdh  * This function is called from pmcs_new_tport when we have a PHY
8044*4c06356bSdh  * without a target pointer.  It's possible in that case that this PHY
8045*4c06356bSdh  * may have a "brother" on the dead_phys list.  That is, it may be the same as
8046*4c06356bSdh  * this one but with a different root PHY number (e.g. pp05 vs. pp04).  If
8047*4c06356bSdh  * that's the case, update the dead PHY and this new PHY.  If that's not the
8048*4c06356bSdh  * case, we should get a tran_tgt_init on this after it's reported to SCSA.
8049*4c06356bSdh  *
8050*4c06356bSdh  * Called with PHY locked.
8051*4c06356bSdh  */
8052*4c06356bSdh static void
8053*4c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp)
8054*4c06356bSdh {
8055*4c06356bSdh 	pmcs_hw_t *pwp = phyp->pwp;
8056*4c06356bSdh 	pmcs_phy_t *ctmp;
8057*4c06356bSdh 
8058*4c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
8059*4c06356bSdh 
8060*4c06356bSdh 	/*
8061*4c06356bSdh 	 * Check the dead PHYs list
8062*4c06356bSdh 	 */
8063*4c06356bSdh 	mutex_enter(&pwp->dead_phylist_lock);
8064*4c06356bSdh 	ctmp = pwp->dead_phys;
8065*4c06356bSdh 	while (ctmp) {
8066*4c06356bSdh 		if ((ctmp->iport != phyp->iport) ||
8067*4c06356bSdh 		    (memcmp((void *)&ctmp->sas_address[0],
8068*4c06356bSdh 		    (void *)&phyp->sas_address[0], 8))) {
8069*4c06356bSdh 			ctmp = ctmp->dead_next;
8070*4c06356bSdh 			continue;
8071*4c06356bSdh 		}
8072*4c06356bSdh 
8073*4c06356bSdh 		/*
8074*4c06356bSdh 		 * Same SAS address on same iport.  Now check to see if
8075*4c06356bSdh 		 * the PHY path is the same with the possible exception
8076*4c06356bSdh 		 * of the root PHY number.
8077*4c06356bSdh 		 * The "5" is the string length of "pp00."
8078*4c06356bSdh 		 */
8079*4c06356bSdh 		if ((strnlen(phyp->path, 5) >= 5) &&
8080*4c06356bSdh 		    (strnlen(ctmp->path, 5) >= 5)) {
8081*4c06356bSdh 			if (memcmp((void *)&phyp->path[5],
8082*4c06356bSdh 			    (void *)&ctmp->path[5],
8083*4c06356bSdh 			    strnlen(phyp->path, 32) - 5) == 0) {
8084*4c06356bSdh 				break;
8085*4c06356bSdh 			}
8086*4c06356bSdh 		}
8087*4c06356bSdh 
8088*4c06356bSdh 		ctmp = ctmp->dead_next;
8089*4c06356bSdh 	}
8090*4c06356bSdh 	mutex_exit(&pwp->dead_phylist_lock);
8091*4c06356bSdh 
8092*4c06356bSdh 	/*
8093*4c06356bSdh 	 * Found a match.  Remove the target linkage and drop the
8094*4c06356bSdh 	 * ref count on the old PHY.  Then, increment the ref count
8095*4c06356bSdh 	 * on the new PHY to compensate.
8096*4c06356bSdh 	 */
8097*4c06356bSdh 	if (ctmp) {
8098*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
8099*4c06356bSdh 		    "%s: Found match in dead PHY list for new PHY %s",
8100*4c06356bSdh 		    __func__, phyp->path);
8101*4c06356bSdh 		if (ctmp->target) {
8102*4c06356bSdh 			/*
8103*4c06356bSdh 			 * If there is a pointer to the target in the dead
8104*4c06356bSdh 			 * PHY, and that PHY's ref_count drops to 0, we can
8105*4c06356bSdh 			 * clear the target linkage now.  If the PHY's
8106*4c06356bSdh 			 * ref_count is > 1, then there may be multiple
8107*4c06356bSdh 			 * LUNs still remaining, so leave the linkage.
8108*4c06356bSdh 			 */
8109*4c06356bSdh 			pmcs_inc_phy_ref_count(phyp);
8110*4c06356bSdh 			pmcs_dec_phy_ref_count(ctmp);
8111*4c06356bSdh 			phyp->target = ctmp->target;
8112*4c06356bSdh 			/*
8113*4c06356bSdh 			 * Update the target's linkage as well
8114*4c06356bSdh 			 */
8115*4c06356bSdh 			mutex_enter(&phyp->target->statlock);
8116*4c06356bSdh 			phyp->target->phy = phyp;
8117*4c06356bSdh 			phyp->target->dtype = phyp->dtype;
8118*4c06356bSdh 			mutex_exit(&phyp->target->statlock);
8119*4c06356bSdh 
8120*4c06356bSdh 			if (ctmp->ref_count == 0) {
8121*4c06356bSdh 				ctmp->target = NULL;
8122*4c06356bSdh 			}
8123*4c06356bSdh 		}
8124*4c06356bSdh 	}
8125*4c06356bSdh }
8126*4c06356bSdh 
8127*4c06356bSdh /*
8128*4c06356bSdh  * Called with iport lock held
8129*4c06356bSdh  */
8130*4c06356bSdh void
8131*4c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
8132*4c06356bSdh {
8133*4c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
8134*4c06356bSdh 	ASSERT(phyp);
8135*4c06356bSdh 	ASSERT(!list_link_active(&phyp->list_node));
8136*4c06356bSdh 	iport->nphy++;
8137*4c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
8138*4c06356bSdh 	    &iport->nphy);
8139*4c06356bSdh 	list_insert_tail(&iport->phys, phyp);
8140*4c06356bSdh 	mutex_enter(&iport->refcnt_lock);
8141*4c06356bSdh 	iport->refcnt++;
8142*4c06356bSdh 	mutex_exit(&iport->refcnt_lock);
8143*4c06356bSdh }
8144*4c06356bSdh 
8145*4c06356bSdh /*
8146*4c06356bSdh  * Called with the iport lock held
8147*4c06356bSdh  */
8148*4c06356bSdh void
8149*4c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
8150*4c06356bSdh {
8151*4c06356bSdh 	pmcs_phy_t *pptr, *next_pptr;
8152*4c06356bSdh 
8153*4c06356bSdh 	ASSERT(mutex_owned(&iport->lock));
8154*4c06356bSdh 
8155*4c06356bSdh 	/*
8156*4c06356bSdh 	 * If phyp is NULL, remove all PHYs from the iport
8157*4c06356bSdh 	 */
8158*4c06356bSdh 	if (phyp == NULL) {
8159*4c06356bSdh 		for (pptr = list_head(&iport->phys); pptr != NULL;
8160*4c06356bSdh 		    pptr = next_pptr) {
8161*4c06356bSdh 			next_pptr = list_next(&iport->phys, pptr);
8162*4c06356bSdh 			mutex_enter(&pptr->phy_lock);
8163*4c06356bSdh 			pptr->iport = NULL;
8164*4c06356bSdh 			mutex_exit(&pptr->phy_lock);
8165*4c06356bSdh 			pmcs_rele_iport(iport);
8166*4c06356bSdh 			list_remove(&iport->phys, pptr);
8167*4c06356bSdh 		}
8168*4c06356bSdh 		iport->nphy = 0;
8169*4c06356bSdh 		return;
8170*4c06356bSdh 	}
8171*4c06356bSdh 
8172*4c06356bSdh 	ASSERT(phyp);
8173*4c06356bSdh 	ASSERT(iport->nphy > 0);
8174*4c06356bSdh 	ASSERT(list_link_active(&phyp->list_node));
8175*4c06356bSdh 	iport->nphy--;
8176*4c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
8177*4c06356bSdh 	    &iport->nphy);
8178*4c06356bSdh 	list_remove(&iport->phys, phyp);
8179*4c06356bSdh 	pmcs_rele_iport(iport);
8180*4c06356bSdh }
8181*4c06356bSdh 
8182*4c06356bSdh /*
8183*4c06356bSdh  * This function checks to see if the target pointed to by phyp is still
8184*4c06356bSdh  * correct.  This is done by comparing the target's unit address with the
8185*4c06356bSdh  * SAS address in phyp.
8186*4c06356bSdh  *
8187*4c06356bSdh  * Called with PHY locked and target statlock held
8188*4c06356bSdh  */
8189*4c06356bSdh static boolean_t
8190*4c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp)
8191*4c06356bSdh {
8192*4c06356bSdh 	uint64_t wwn;
8193*4c06356bSdh 	char unit_address[PMCS_MAX_UA_SIZE];
8194*4c06356bSdh 	boolean_t rval = B_FALSE;
8195*4c06356bSdh 
8196*4c06356bSdh 	ASSERT(phyp);
8197*4c06356bSdh 	ASSERT(phyp->target);
8198*4c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
8199*4c06356bSdh 	ASSERT(mutex_owned(&phyp->target->statlock));
8200*4c06356bSdh 
8201*4c06356bSdh 	wwn = pmcs_barray2wwn(phyp->sas_address);
8202*4c06356bSdh 	(void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
8203*4c06356bSdh 
8204*4c06356bSdh 	if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
8205*4c06356bSdh 	    strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
8206*4c06356bSdh 		rval = B_TRUE;
8207*4c06356bSdh 	}
8208*4c06356bSdh 
8209*4c06356bSdh 	return (rval);
8210*4c06356bSdh }
8211*4c06356bSdh 
8212*4c06356bSdh void
8213*4c06356bSdh pmcs_start_dev_state_recovery(pmcs_xscsi_t *xp, pmcs_phy_t *phyp)
8214*4c06356bSdh {
8215*4c06356bSdh 	ASSERT(mutex_owned(&xp->statlock));
8216*4c06356bSdh 	ASSERT(xp->pwp != NULL);
8217*4c06356bSdh 
8218*4c06356bSdh 	if (xp->recover_wait == 0) {
8219*4c06356bSdh 		pmcs_prt(xp->pwp, PMCS_PRT_DEBUG_DEV_STATE,
8220*4c06356bSdh 		    "%s: Start ds_recovery for tgt 0x%p/PHY 0x%p (%s)",
8221*4c06356bSdh 		    __func__, (void *)xp, (void *)phyp, phyp->path);
8222*4c06356bSdh 		xp->recover_wait = 1;
8223*4c06356bSdh 
8224*4c06356bSdh 		/*
8225*4c06356bSdh 		 * Rather than waiting for the watchdog timer, we'll
8226*4c06356bSdh 		 * kick it right now.
8227*4c06356bSdh 		 */
8228*4c06356bSdh 		SCHEDULE_WORK(xp->pwp, PMCS_WORK_DS_ERR_RECOVERY);
8229*4c06356bSdh 		(void) ddi_taskq_dispatch(xp->pwp->tq, pmcs_worker, xp->pwp,
8230*4c06356bSdh 		    DDI_NOSLEEP);
8231*4c06356bSdh 	}
8232*4c06356bSdh }
8233*4c06356bSdh 
8234*4c06356bSdh /*
8235*4c06356bSdh  * Increment the phy ds error retry count.
8236*4c06356bSdh  * If too many retries, mark phy dead and restart discovery;
8237*4c06356bSdh  * otherwise schedule ds recovery.
8238*4c06356bSdh  */
8239*4c06356bSdh static void
8240*4c06356bSdh pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp, pmcs_xscsi_t *tgt,
8241*4c06356bSdh     pmcs_hw_t *pwp, const char *func_name, int line, char *reason_string)
8242*4c06356bSdh {
8243*4c06356bSdh 	ASSERT(mutex_owned(&phyp->phy_lock));
8244*4c06356bSdh 
8245*4c06356bSdh 	phyp->ds_recovery_retries++;
8246*4c06356bSdh 
8247*4c06356bSdh 	if (phyp->ds_recovery_retries > PMCS_MAX_DS_RECOVERY_RETRIES) {
8248*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
8249*4c06356bSdh 		    "%s: retry limit reached after %s to PHY %s failed",
8250*4c06356bSdh 		    func_name, reason_string, phyp->path);
8251*4c06356bSdh 		tgt->recover_wait = 0;
8252*4c06356bSdh 		phyp->dead = 1;
8253*4c06356bSdh 		PHY_CHANGED_AT_LOCATION(pwp, phyp, func_name, line);
8254*4c06356bSdh 		RESTART_DISCOVERY(pwp);
8255*4c06356bSdh 	} else {
8256*4c06356bSdh 		SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
8257*4c06356bSdh 	}
8258*4c06356bSdh }
8259