xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_attach.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 #include <sys/scsi/adapters/pmcs/pmcs.h>
26*4c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs_bldrev.h>
27*4c06356bSdh 
28*4c06356bSdh #define	PMCS_DRIVER_VERSION	"PMC-Sierra HBA Driver "\
29*4c06356bSdh 	PMCS_BUILD_VERSION
30*4c06356bSdh 
31*4c06356bSdh static	char	*pmcs_driver_rev = PMCS_DRIVER_VERSION;
32*4c06356bSdh 
33*4c06356bSdh /*
34*4c06356bSdh  * Non-DDI Compliant stuff
35*4c06356bSdh  */
36*4c06356bSdh extern char hw_serial[];
37*4c06356bSdh 
38*4c06356bSdh /*
39*4c06356bSdh  * Global driver data
40*4c06356bSdh  */
41*4c06356bSdh void *pmcs_softc_state = NULL;
42*4c06356bSdh void *pmcs_iport_softstate = NULL;
43*4c06356bSdh 
44*4c06356bSdh /*
45*4c06356bSdh  * Tracing and Logging info
46*4c06356bSdh  */
47*4c06356bSdh pmcs_tbuf_t *pmcs_tbuf = NULL;
48*4c06356bSdh uint32_t pmcs_tbuf_num_elems = 0;
49*4c06356bSdh pmcs_tbuf_t *pmcs_tbuf_ptr;
50*4c06356bSdh uint32_t pmcs_tbuf_idx = 0;
51*4c06356bSdh boolean_t pmcs_tbuf_wrap = B_FALSE;
52*4c06356bSdh static kmutex_t pmcs_trace_lock;
53*4c06356bSdh 
54*4c06356bSdh /*
55*4c06356bSdh  * If pmcs_force_syslog value is non-zero, all messages put in the trace log
56*4c06356bSdh  * will also be sent to system log.
57*4c06356bSdh  */
58*4c06356bSdh int pmcs_force_syslog = 0;
59*4c06356bSdh int pmcs_console = 0;
60*4c06356bSdh 
61*4c06356bSdh /*
62*4c06356bSdh  * External References
63*4c06356bSdh  */
64*4c06356bSdh extern int ncpus_online;
65*4c06356bSdh 
66*4c06356bSdh /*
67*4c06356bSdh  * Local static data
68*4c06356bSdh  */
69*4c06356bSdh static int fwlog_level = 3;
70*4c06356bSdh static int physpeed = PHY_LINK_ALL;
71*4c06356bSdh static int phymode = PHY_LM_AUTO;
72*4c06356bSdh static int block_mask = 0;
73*4c06356bSdh static int phymap_usec = 3 * MICROSEC;
74*4c06356bSdh static int iportmap_usec = 2 * MICROSEC;
75*4c06356bSdh 
76*4c06356bSdh #ifdef DEBUG
77*4c06356bSdh static int debug_mask = 1;
78*4c06356bSdh #else
79*4c06356bSdh static int debug_mask = 0;
80*4c06356bSdh #endif
81*4c06356bSdh 
82*4c06356bSdh #ifdef DISABLE_MSIX
83*4c06356bSdh static int disable_msix = 1;
84*4c06356bSdh #else
85*4c06356bSdh static int disable_msix = 0;
86*4c06356bSdh #endif
87*4c06356bSdh 
88*4c06356bSdh #ifdef DISABLE_MSI
89*4c06356bSdh static int disable_msi = 1;
90*4c06356bSdh #else
91*4c06356bSdh static int disable_msi = 0;
92*4c06356bSdh #endif
93*4c06356bSdh 
94*4c06356bSdh static uint16_t maxqdepth = 0xfffe;
95*4c06356bSdh 
96*4c06356bSdh /*
97*4c06356bSdh  * Local prototypes
98*4c06356bSdh  */
99*4c06356bSdh static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t);
100*4c06356bSdh static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t);
101*4c06356bSdh static int pmcs_unattach(pmcs_hw_t *);
102*4c06356bSdh static int pmcs_iport_unattach(pmcs_iport_t *);
103*4c06356bSdh static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long);
104*4c06356bSdh static void pmcs_watchdog(void *);
105*4c06356bSdh static int pmcs_setup_intr(pmcs_hw_t *);
106*4c06356bSdh static int pmcs_teardown_intr(pmcs_hw_t *);
107*4c06356bSdh 
108*4c06356bSdh static uint_t pmcs_nonio_ix(caddr_t, caddr_t);
109*4c06356bSdh static uint_t pmcs_general_ix(caddr_t, caddr_t);
110*4c06356bSdh static uint_t pmcs_event_ix(caddr_t, caddr_t);
111*4c06356bSdh static uint_t pmcs_iodone_ix(caddr_t, caddr_t);
112*4c06356bSdh static uint_t pmcs_fatal_ix(caddr_t, caddr_t);
113*4c06356bSdh static uint_t pmcs_all_intr(caddr_t, caddr_t);
114*4c06356bSdh static int pmcs_quiesce(dev_info_t *dip);
115*4c06356bSdh static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *);
116*4c06356bSdh 
117*4c06356bSdh static void pmcs_create_phy_stats(pmcs_iport_t *);
118*4c06356bSdh int pmcs_update_phy_stats(kstat_t *, int);
119*4c06356bSdh static void pmcs_destroy_phy_stats(pmcs_iport_t *);
120*4c06356bSdh 
121*4c06356bSdh static void pmcs_fm_fini(pmcs_hw_t *pwp);
122*4c06356bSdh static void pmcs_fm_init(pmcs_hw_t *pwp);
123*4c06356bSdh static int pmcs_fm_error_cb(dev_info_t *dip,
124*4c06356bSdh     ddi_fm_error_t *err, const void *impl_data);
125*4c06356bSdh 
126*4c06356bSdh /*
127*4c06356bSdh  * Local configuration data
128*4c06356bSdh  */
129*4c06356bSdh static struct dev_ops pmcs_ops = {
130*4c06356bSdh 	DEVO_REV,		/* devo_rev, */
131*4c06356bSdh 	0,			/* refcnt */
132*4c06356bSdh 	ddi_no_info,		/* info */
133*4c06356bSdh 	nulldev,		/* identify */
134*4c06356bSdh 	nulldev,		/* probe */
135*4c06356bSdh 	pmcs_attach,		/* attach */
136*4c06356bSdh 	pmcs_detach,		/* detach */
137*4c06356bSdh 	nodev,			/* reset */
138*4c06356bSdh 	NULL,			/* driver operations */
139*4c06356bSdh 	NULL,			/* bus operations */
140*4c06356bSdh 	ddi_power,		/* power management */
141*4c06356bSdh 	pmcs_quiesce		/* quiesce */
142*4c06356bSdh };
143*4c06356bSdh 
144*4c06356bSdh static struct modldrv modldrv = {
145*4c06356bSdh 	&mod_driverops,
146*4c06356bSdh 	PMCS_DRIVER_VERSION,
147*4c06356bSdh 	&pmcs_ops,	/* driver ops */
148*4c06356bSdh };
149*4c06356bSdh static struct modlinkage modlinkage = {
150*4c06356bSdh 	MODREV_1, &modldrv, NULL
151*4c06356bSdh };
152*4c06356bSdh 
153*4c06356bSdh const ddi_dma_attr_t pmcs_dattr = {
154*4c06356bSdh 	DMA_ATTR_V0,			/* dma_attr version	*/
155*4c06356bSdh 	0x0000000000000000ull,		/* dma_attr_addr_lo	*/
156*4c06356bSdh 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_addr_hi	*/
157*4c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_count_max	*/
158*4c06356bSdh 	0x0000000000000001ull,		/* dma_attr_align	*/
159*4c06356bSdh 	0x00000078,			/* dma_attr_burstsizes	*/
160*4c06356bSdh 	0x00000001,			/* dma_attr_minxfer	*/
161*4c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_maxxfer	*/
162*4c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_seg		*/
163*4c06356bSdh 	1,				/* dma_attr_sgllen 	*/
164*4c06356bSdh 	512,				/* dma_attr_granular 	*/
165*4c06356bSdh 	0				/* dma_attr_flags 	*/
166*4c06356bSdh };
167*4c06356bSdh 
168*4c06356bSdh static ddi_device_acc_attr_t rattr = {
169*4c06356bSdh 	DDI_DEVICE_ATTR_V0,
170*4c06356bSdh 	DDI_STRUCTURE_LE_ACC,
171*4c06356bSdh 	DDI_STRICTORDER_ACC,
172*4c06356bSdh 	DDI_DEFAULT_ACC
173*4c06356bSdh };
174*4c06356bSdh 
175*4c06356bSdh 
176*4c06356bSdh /*
177*4c06356bSdh  * Attach/Detach functions
178*4c06356bSdh  */
179*4c06356bSdh 
180*4c06356bSdh int
181*4c06356bSdh _init(void)
182*4c06356bSdh {
183*4c06356bSdh 	int ret;
184*4c06356bSdh 
185*4c06356bSdh 	ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1);
186*4c06356bSdh 	if (ret != 0) {
187*4c06356bSdh 		cmn_err(CE_WARN, "?soft state init failed for pmcs");
188*4c06356bSdh 		return (ret);
189*4c06356bSdh 	}
190*4c06356bSdh 
191*4c06356bSdh 	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
192*4c06356bSdh 		cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs");
193*4c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
194*4c06356bSdh 		return (ret);
195*4c06356bSdh 	}
196*4c06356bSdh 
197*4c06356bSdh 	/*
198*4c06356bSdh 	 * Allocate soft state for iports
199*4c06356bSdh 	 */
200*4c06356bSdh 	ret = ddi_soft_state_init(&pmcs_iport_softstate,
201*4c06356bSdh 	    sizeof (pmcs_iport_t), 2);
202*4c06356bSdh 	if (ret != 0) {
203*4c06356bSdh 		cmn_err(CE_WARN, "?iport soft state init failed for pmcs");
204*4c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
205*4c06356bSdh 		return (ret);
206*4c06356bSdh 	}
207*4c06356bSdh 
208*4c06356bSdh 	ret = mod_install(&modlinkage);
209*4c06356bSdh 	if (ret != 0) {
210*4c06356bSdh 		cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret);
211*4c06356bSdh 		scsi_hba_fini(&modlinkage);
212*4c06356bSdh 		ddi_soft_state_fini(&pmcs_iport_softstate);
213*4c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
214*4c06356bSdh 		return (ret);
215*4c06356bSdh 	}
216*4c06356bSdh 
217*4c06356bSdh 	/* Initialize the global trace lock */
218*4c06356bSdh 	mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL);
219*4c06356bSdh 
220*4c06356bSdh 	return (0);
221*4c06356bSdh }
222*4c06356bSdh 
223*4c06356bSdh int
224*4c06356bSdh _fini(void)
225*4c06356bSdh {
226*4c06356bSdh 	int ret;
227*4c06356bSdh 	if ((ret = mod_remove(&modlinkage)) != 0) {
228*4c06356bSdh 		return (ret);
229*4c06356bSdh 	}
230*4c06356bSdh 	scsi_hba_fini(&modlinkage);
231*4c06356bSdh 
232*4c06356bSdh 	/* Free pmcs log buffer and destroy the global lock */
233*4c06356bSdh 	if (pmcs_tbuf) {
234*4c06356bSdh 		kmem_free(pmcs_tbuf,
235*4c06356bSdh 		    pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t));
236*4c06356bSdh 		pmcs_tbuf = NULL;
237*4c06356bSdh 	}
238*4c06356bSdh 	mutex_destroy(&pmcs_trace_lock);
239*4c06356bSdh 
240*4c06356bSdh 	ddi_soft_state_fini(&pmcs_iport_softstate);
241*4c06356bSdh 	ddi_soft_state_fini(&pmcs_softc_state);
242*4c06356bSdh 	return (0);
243*4c06356bSdh }
244*4c06356bSdh 
245*4c06356bSdh int
246*4c06356bSdh _info(struct modinfo *modinfop)
247*4c06356bSdh {
248*4c06356bSdh 	return (mod_info(&modlinkage, modinfop));
249*4c06356bSdh }
250*4c06356bSdh 
251*4c06356bSdh static int
252*4c06356bSdh pmcs_iport_attach(dev_info_t *dip)
253*4c06356bSdh {
254*4c06356bSdh 	pmcs_iport_t		*iport;
255*4c06356bSdh 	pmcs_hw_t		*pwp;
256*4c06356bSdh 	scsi_hba_tran_t		*tran;
257*4c06356bSdh 	void			*ua_priv = NULL;
258*4c06356bSdh 	char			*iport_ua;
259*4c06356bSdh 	char			*init_port;
260*4c06356bSdh 	int			hba_inst;
261*4c06356bSdh 	int			inst;
262*4c06356bSdh 
263*4c06356bSdh 	hba_inst = ddi_get_instance(ddi_get_parent(dip));
264*4c06356bSdh 	inst = ddi_get_instance(dip);
265*4c06356bSdh 
266*4c06356bSdh 	pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst);
267*4c06356bSdh 	if (pwp == NULL) {
268*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
269*4c06356bSdh 		    "%s: iport%d attach invoked with NULL parent (HBA) node)",
270*4c06356bSdh 		    __func__, inst);
271*4c06356bSdh 		return (DDI_FAILURE);
272*4c06356bSdh 	}
273*4c06356bSdh 
274*4c06356bSdh 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
275*4c06356bSdh 		return (DDI_FAILURE);
276*4c06356bSdh 	}
277*4c06356bSdh 
278*4c06356bSdh 	if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) {
279*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
280*4c06356bSdh 		    "%s: invoked with NULL unit address, inst (%d)",
281*4c06356bSdh 		    __func__, inst);
282*4c06356bSdh 		return (DDI_FAILURE);
283*4c06356bSdh 	}
284*4c06356bSdh 
285*4c06356bSdh 	if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) {
286*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
287*4c06356bSdh 		    "Failed to alloc soft state for iport %d", inst);
288*4c06356bSdh 		return (DDI_FAILURE);
289*4c06356bSdh 	}
290*4c06356bSdh 
291*4c06356bSdh 	iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
292*4c06356bSdh 	if (iport == NULL) {
293*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
294*4c06356bSdh 		    "cannot get iport soft state");
295*4c06356bSdh 		goto iport_attach_fail1;
296*4c06356bSdh 	}
297*4c06356bSdh 
298*4c06356bSdh 	mutex_init(&iport->lock, NULL, MUTEX_DRIVER,
299*4c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
300*4c06356bSdh 	cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL);
301*4c06356bSdh 	mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER,
302*4c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
303*4c06356bSdh 
304*4c06356bSdh 	/* Set some data on the iport handle */
305*4c06356bSdh 	iport->dip = dip;
306*4c06356bSdh 	iport->pwp = pwp;
307*4c06356bSdh 
308*4c06356bSdh 	/* Dup the UA into the iport handle */
309*4c06356bSdh 	iport->ua = strdup(iport_ua);
310*4c06356bSdh 
311*4c06356bSdh 	tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
312*4c06356bSdh 	tran->tran_hba_private = iport;
313*4c06356bSdh 
314*4c06356bSdh 	list_create(&iport->phys, sizeof (pmcs_phy_t),
315*4c06356bSdh 	    offsetof(pmcs_phy_t, list_node));
316*4c06356bSdh 
317*4c06356bSdh 	/*
318*4c06356bSdh 	 * If our unit address is active in the phymap, configure our
319*4c06356bSdh 	 * iport's phylist.
320*4c06356bSdh 	 */
321*4c06356bSdh 	mutex_enter(&iport->lock);
322*4c06356bSdh 	ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua);
323*4c06356bSdh 	if (ua_priv) {
324*4c06356bSdh 		/* Non-NULL private data indicates the unit address is active */
325*4c06356bSdh 		iport->ua_state = UA_ACTIVE;
326*4c06356bSdh 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
327*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: failed to "
328*4c06356bSdh 			    "configure phys on iport handle (0x%p), "
329*4c06356bSdh 			    " unit address [%s]", __func__,
330*4c06356bSdh 			    (void *)iport, iport_ua);
331*4c06356bSdh 			mutex_exit(&iport->lock);
332*4c06356bSdh 			goto iport_attach_fail2;
333*4c06356bSdh 		}
334*4c06356bSdh 	} else {
335*4c06356bSdh 		iport->ua_state = UA_INACTIVE;
336*4c06356bSdh 	}
337*4c06356bSdh 	mutex_exit(&iport->lock);
338*4c06356bSdh 
339*4c06356bSdh 	/* Allocate string-based soft state pool for targets */
340*4c06356bSdh 	iport->tgt_sstate = NULL;
341*4c06356bSdh 	if (ddi_soft_state_bystr_init(&iport->tgt_sstate,
342*4c06356bSdh 	    sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) {
343*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
344*4c06356bSdh 		    "cannot get iport tgt soft state");
345*4c06356bSdh 		goto iport_attach_fail2;
346*4c06356bSdh 	}
347*4c06356bSdh 
348*4c06356bSdh 	/* Create this iport's target map */
349*4c06356bSdh 	if (pmcs_iport_tgtmap_create(iport) == B_FALSE) {
350*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
351*4c06356bSdh 		    "Failed to create tgtmap on iport %d", inst);
352*4c06356bSdh 		goto iport_attach_fail3;
353*4c06356bSdh 	}
354*4c06356bSdh 
355*4c06356bSdh 	/* Set up the 'initiator-port' DDI property on this iport */
356*4c06356bSdh 	init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
357*4c06356bSdh 	if (pwp->separate_ports) {
358*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: separate ports not "
359*4c06356bSdh 		    "supported", __func__);
360*4c06356bSdh 	} else {
361*4c06356bSdh 		/* Set initiator-port value to the HBA's base WWN */
362*4c06356bSdh 		(void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1,
363*4c06356bSdh 		    init_port);
364*4c06356bSdh 	}
365*4c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING,
366*4c06356bSdh 	    SCSI_ADDR_PROP_INITIATOR_PORT, init_port);
367*4c06356bSdh 	kmem_free(init_port, PMCS_MAX_UA_SIZE);
368*4c06356bSdh 
369*4c06356bSdh 	/* Set up a 'num-phys' DDI property for the iport node */
370*4c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
371*4c06356bSdh 	    &iport->nphy);
372*4c06356bSdh 
373*4c06356bSdh 	/* Create kstats for each of the phys in this port */
374*4c06356bSdh 	pmcs_create_phy_stats(iport);
375*4c06356bSdh 
376*4c06356bSdh 	/*
377*4c06356bSdh 	 * Insert this iport handle into our list and set
378*4c06356bSdh 	 * iports_attached on the HBA node.
379*4c06356bSdh 	 */
380*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_WRITER);
381*4c06356bSdh 	ASSERT(!list_link_active(&iport->list_node));
382*4c06356bSdh 	list_insert_tail(&pwp->iports, iport);
383*4c06356bSdh 	pwp->iports_attached = 1;
384*4c06356bSdh 	pwp->num_iports++;
385*4c06356bSdh 	rw_exit(&pwp->iports_lock);
386*4c06356bSdh 
387*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d attached", inst);
388*4c06356bSdh 	ddi_report_dev(dip);
389*4c06356bSdh 	return (DDI_SUCCESS);
390*4c06356bSdh 
391*4c06356bSdh 	/* teardown and fail */
392*4c06356bSdh iport_attach_fail3:
393*4c06356bSdh 	ddi_soft_state_bystr_fini(&iport->tgt_sstate);
394*4c06356bSdh iport_attach_fail2:
395*4c06356bSdh 	list_destroy(&iport->phys);
396*4c06356bSdh 	strfree(iport->ua);
397*4c06356bSdh 	mutex_destroy(&iport->refcnt_lock);
398*4c06356bSdh 	cv_destroy(&iport->refcnt_cv);
399*4c06356bSdh 	mutex_destroy(&iport->lock);
400*4c06356bSdh iport_attach_fail1:
401*4c06356bSdh 	ddi_soft_state_free(pmcs_iport_softstate, inst);
402*4c06356bSdh 	return (DDI_FAILURE);
403*4c06356bSdh }
404*4c06356bSdh 
405*4c06356bSdh static int
406*4c06356bSdh pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
407*4c06356bSdh {
408*4c06356bSdh 	scsi_hba_tran_t *tran;
409*4c06356bSdh 	char chiprev, *fwsupport, hw_rev[24], fw_rev[24];
410*4c06356bSdh 	off_t set3size;
411*4c06356bSdh 	int inst, i;
412*4c06356bSdh 	int sm_hba = 1;
413*4c06356bSdh 	int protocol = 0;
414*4c06356bSdh 	int num_phys = 0;
415*4c06356bSdh 	pmcs_hw_t *pwp;
416*4c06356bSdh 	pmcs_phy_t *phyp;
417*4c06356bSdh 	uint32_t num_threads;
418*4c06356bSdh 	char buf[64];
419*4c06356bSdh 
420*4c06356bSdh 	switch (cmd) {
421*4c06356bSdh 	case DDI_ATTACH:
422*4c06356bSdh 		break;
423*4c06356bSdh 
424*4c06356bSdh 	case DDI_PM_RESUME:
425*4c06356bSdh 	case DDI_RESUME:
426*4c06356bSdh 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
427*4c06356bSdh 		if (!tran) {
428*4c06356bSdh 			return (DDI_FAILURE);
429*4c06356bSdh 		}
430*4c06356bSdh 		/* No DDI_?_RESUME on iport nodes */
431*4c06356bSdh 		if (scsi_hba_iport_unit_address(dip) != NULL) {
432*4c06356bSdh 			return (DDI_SUCCESS);
433*4c06356bSdh 		}
434*4c06356bSdh 		pwp = TRAN2PMC(tran);
435*4c06356bSdh 		if (pwp == NULL) {
436*4c06356bSdh 			return (DDI_FAILURE);
437*4c06356bSdh 		}
438*4c06356bSdh 
439*4c06356bSdh 		mutex_enter(&pwp->lock);
440*4c06356bSdh 		pwp->suspended = 0;
441*4c06356bSdh 		if (pwp->tq) {
442*4c06356bSdh 			ddi_taskq_resume(pwp->tq);
443*4c06356bSdh 		}
444*4c06356bSdh 		mutex_exit(&pwp->lock);
445*4c06356bSdh 		return (DDI_SUCCESS);
446*4c06356bSdh 
447*4c06356bSdh 	default:
448*4c06356bSdh 		return (DDI_FAILURE);
449*4c06356bSdh 	}
450*4c06356bSdh 
451*4c06356bSdh 	/*
452*4c06356bSdh 	 * If this is an iport node, invoke iport attach.
453*4c06356bSdh 	 */
454*4c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
455*4c06356bSdh 		return (pmcs_iport_attach(dip));
456*4c06356bSdh 	}
457*4c06356bSdh 
458*4c06356bSdh 	/*
459*4c06356bSdh 	 * From here on is attach for the HBA node
460*4c06356bSdh 	 */
461*4c06356bSdh 
462*4c06356bSdh #ifdef	DEBUG
463*4c06356bSdh 	/*
464*4c06356bSdh 	 * Check to see if this unit is to be disabled.  We can't disable
465*4c06356bSdh 	 * on a per-iport node.  It's either the entire HBA or nothing.
466*4c06356bSdh 	 */
467*4c06356bSdh 	(void) snprintf(buf, sizeof (buf),
468*4c06356bSdh 	    "disable-instance-%d", ddi_get_instance(dip));
469*4c06356bSdh 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
470*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) {
471*4c06356bSdh 		cmn_err(CE_NOTE, "pmcs%d: disabled by configuration",
472*4c06356bSdh 		    ddi_get_instance(dip));
473*4c06356bSdh 		return (DDI_FAILURE);
474*4c06356bSdh 	}
475*4c06356bSdh #endif
476*4c06356bSdh 
477*4c06356bSdh 	/*
478*4c06356bSdh 	 * Allocate softstate
479*4c06356bSdh 	 */
480*4c06356bSdh 	inst = ddi_get_instance(dip);
481*4c06356bSdh 	if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) {
482*4c06356bSdh 		cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst);
483*4c06356bSdh 		return (DDI_FAILURE);
484*4c06356bSdh 	}
485*4c06356bSdh 
486*4c06356bSdh 	pwp = ddi_get_soft_state(pmcs_softc_state, inst);
487*4c06356bSdh 	if (pwp == NULL) {
488*4c06356bSdh 		cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst);
489*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
490*4c06356bSdh 		return (DDI_FAILURE);
491*4c06356bSdh 	}
492*4c06356bSdh 	pwp->dip = dip;
493*4c06356bSdh 	STAILQ_INIT(&pwp->dq);
494*4c06356bSdh 	STAILQ_INIT(&pwp->cq);
495*4c06356bSdh 	STAILQ_INIT(&pwp->wf);
496*4c06356bSdh 	STAILQ_INIT(&pwp->pf);
497*4c06356bSdh 	/*
498*4c06356bSdh 	 * Create the list for iports
499*4c06356bSdh 	 */
500*4c06356bSdh 	list_create(&pwp->iports, sizeof (pmcs_iport_t),
501*4c06356bSdh 	    offsetof(pmcs_iport_t, list_node));
502*4c06356bSdh 
503*4c06356bSdh 	pwp->state = STATE_PROBING;
504*4c06356bSdh 
505*4c06356bSdh 	/*
506*4c06356bSdh 	 * Get driver.conf properties
507*4c06356bSdh 	 */
508*4c06356bSdh 	pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
509*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask",
510*4c06356bSdh 	    debug_mask);
511*4c06356bSdh 	pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
512*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask",
513*4c06356bSdh 	    block_mask);
514*4c06356bSdh 	pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
515*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed);
516*4c06356bSdh 	pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
517*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode);
518*4c06356bSdh 	pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
519*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level);
520*4c06356bSdh 	if (pwp->fwlog > PMCS_FWLOG_MAX) {
521*4c06356bSdh 		pwp->fwlog = PMCS_FWLOG_MAX;
522*4c06356bSdh 	}
523*4c06356bSdh 
524*4c06356bSdh 	mutex_enter(&pmcs_trace_lock);
525*4c06356bSdh 	if (pmcs_tbuf == NULL) {
526*4c06356bSdh 		/* Allocate trace buffer */
527*4c06356bSdh 		pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
528*4c06356bSdh 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems",
529*4c06356bSdh 		    PMCS_TBUF_NUM_ELEMS_DEF);
530*4c06356bSdh 		if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) ||
531*4c06356bSdh 		    (pmcs_tbuf_num_elems == 0)) {
532*4c06356bSdh 			pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF;
533*4c06356bSdh 		}
534*4c06356bSdh 
535*4c06356bSdh 		pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems *
536*4c06356bSdh 		    sizeof (pmcs_tbuf_t), KM_SLEEP);
537*4c06356bSdh 		pmcs_tbuf_ptr = pmcs_tbuf;
538*4c06356bSdh 		pmcs_tbuf_idx = 0;
539*4c06356bSdh 	}
540*4c06356bSdh 	mutex_exit(&pmcs_trace_lock);
541*4c06356bSdh 
542*4c06356bSdh 	disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
543*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix",
544*4c06356bSdh 	    disable_msix);
545*4c06356bSdh 	disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
546*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi",
547*4c06356bSdh 	    disable_msi);
548*4c06356bSdh 	maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
549*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth);
550*4c06356bSdh 	pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
551*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0);
552*4c06356bSdh 	if (pwp->fw_force_update == 0) {
553*4c06356bSdh 		pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
554*4c06356bSdh 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
555*4c06356bSdh 		    "pmcs-fw-disable-update", 0);
556*4c06356bSdh 	}
557*4c06356bSdh 	pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
558*4c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries",
559*4c06356bSdh 	    PMCS_NQENTRY);
560*4c06356bSdh 
561*4c06356bSdh 	/*
562*4c06356bSdh 	 * Initialize FMA
563*4c06356bSdh 	 */
564*4c06356bSdh 	pwp->dev_acc_attr = pwp->reg_acc_attr = rattr;
565*4c06356bSdh 	pwp->iqp_dma_attr = pwp->oqp_dma_attr =
566*4c06356bSdh 	    pwp->regdump_dma_attr = pwp->cip_dma_attr =
567*4c06356bSdh 	    pwp->fwlog_dma_attr = pmcs_dattr;
568*4c06356bSdh 	pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip,
569*4c06356bSdh 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable",
570*4c06356bSdh 	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
571*4c06356bSdh 	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
572*4c06356bSdh 	pmcs_fm_init(pwp);
573*4c06356bSdh 
574*4c06356bSdh 	/*
575*4c06356bSdh 	 * Map registers
576*4c06356bSdh 	 */
577*4c06356bSdh 	if (pci_config_setup(dip, &pwp->pci_acc_handle)) {
578*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, "pci config setup failed");
579*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
580*4c06356bSdh 		return (DDI_FAILURE);
581*4c06356bSdh 	}
582*4c06356bSdh 
583*4c06356bSdh 	/*
584*4c06356bSdh 	 * Get the size of register set 3.
585*4c06356bSdh 	 */
586*4c06356bSdh 	if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) {
587*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
588*4c06356bSdh 		    "unable to get size of register set %d", PMCS_REGSET_3);
589*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
590*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
591*4c06356bSdh 		return (DDI_FAILURE);
592*4c06356bSdh 	}
593*4c06356bSdh 
594*4c06356bSdh 	/*
595*4c06356bSdh 	 * Map registers
596*4c06356bSdh 	 */
597*4c06356bSdh 	pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
598*4c06356bSdh 
599*4c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs,
600*4c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) {
601*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
602*4c06356bSdh 		    "failed to map Message Unit registers");
603*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
604*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
605*4c06356bSdh 		return (DDI_FAILURE);
606*4c06356bSdh 	}
607*4c06356bSdh 
608*4c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs,
609*4c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) {
610*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map TOP registers");
611*4c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
612*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
613*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
614*4c06356bSdh 		return (DDI_FAILURE);
615*4c06356bSdh 	}
616*4c06356bSdh 
617*4c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs,
618*4c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) {
619*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map GSM registers");
620*4c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
621*4c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
622*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
623*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
624*4c06356bSdh 		return (DDI_FAILURE);
625*4c06356bSdh 	}
626*4c06356bSdh 
627*4c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs,
628*4c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) {
629*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map MPI registers");
630*4c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
631*4c06356bSdh 		ddi_regs_map_free(&pwp->gsm_acc_handle);
632*4c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
633*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
634*4c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
635*4c06356bSdh 		return (DDI_FAILURE);
636*4c06356bSdh 	}
637*4c06356bSdh 	pwp->mpibar =
638*4c06356bSdh 	    (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size;
639*4c06356bSdh 
640*4c06356bSdh 	/*
641*4c06356bSdh 	 * Make sure we can support this card.
642*4c06356bSdh 	 */
643*4c06356bSdh 	pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION);
644*4c06356bSdh 
645*4c06356bSdh 	switch (pwp->chiprev) {
646*4c06356bSdh 	case PMCS_PM8001_REV_A:
647*4c06356bSdh 	case PMCS_PM8001_REV_B:
648*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR,
649*4c06356bSdh 		    "Rev A/B Card no longer supported");
650*4c06356bSdh 		goto failure;
651*4c06356bSdh 	case PMCS_PM8001_REV_C:
652*4c06356bSdh 		break;
653*4c06356bSdh 	default:
654*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR,
655*4c06356bSdh 		    "Unknown chip revision (%d)", pwp->chiprev);
656*4c06356bSdh 		goto failure;
657*4c06356bSdh 	}
658*4c06356bSdh 
659*4c06356bSdh 	/*
660*4c06356bSdh 	 * Allocate DMA addressable area for Inbound and Outbound Queue indices
661*4c06356bSdh 	 * that the chip needs to access plus a space for scratch usage
662*4c06356bSdh 	 */
663*4c06356bSdh 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
664*4c06356bSdh 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls,
665*4c06356bSdh 	    &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip,
666*4c06356bSdh 	    &pwp->ciaddr) == B_FALSE) {
667*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
668*4c06356bSdh 		    "Failed to setup DMA for index/scratch");
669*4c06356bSdh 		goto failure;
670*4c06356bSdh 	}
671*4c06356bSdh 
672*4c06356bSdh 	bzero(pwp->cip, ptob(1));
673*4c06356bSdh 	pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE];
674*4c06356bSdh 	pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE;
675*4c06356bSdh 
676*4c06356bSdh 	/*
677*4c06356bSdh 	 * Allocate DMA S/G list chunks
678*4c06356bSdh 	 */
679*4c06356bSdh 	(void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES);
680*4c06356bSdh 
681*4c06356bSdh 	/*
682*4c06356bSdh 	 * Allocate a DMA addressable area for the firmware log (if needed)
683*4c06356bSdh 	 */
684*4c06356bSdh 	if (pwp->fwlog) {
685*4c06356bSdh 		/*
686*4c06356bSdh 		 * Align to event log header and entry size
687*4c06356bSdh 		 */
688*4c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_align = 32;
689*4c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr,
690*4c06356bSdh 		    &pwp->fwlog_acchdl,
691*4c06356bSdh 		    &pwp->fwlog_hndl, PMCS_FWLOG_SIZE,
692*4c06356bSdh 		    (caddr_t *)&pwp->fwlogp,
693*4c06356bSdh 		    &pwp->fwaddr) == B_FALSE) {
694*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
695*4c06356bSdh 			    "Failed to setup DMA for fwlog area");
696*4c06356bSdh 			pwp->fwlog = 0;
697*4c06356bSdh 		} else {
698*4c06356bSdh 			bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
699*4c06356bSdh 		}
700*4c06356bSdh 	}
701*4c06356bSdh 
702*4c06356bSdh 	if (pwp->flash_chunk_addr == NULL) {
703*4c06356bSdh 		pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE;
704*4c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr,
705*4c06356bSdh 		    &pwp->regdump_acchdl,
706*4c06356bSdh 		    &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE,
707*4c06356bSdh 		    (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) ==
708*4c06356bSdh 		    B_FALSE) {
709*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
710*4c06356bSdh 			    "Failed to setup DMA for register dump area");
711*4c06356bSdh 			goto failure;
712*4c06356bSdh 		}
713*4c06356bSdh 		bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE);
714*4c06356bSdh 	}
715*4c06356bSdh 
716*4c06356bSdh 	/*
717*4c06356bSdh 	 * More bits of local initialization...
718*4c06356bSdh 	 */
719*4c06356bSdh 	pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0);
720*4c06356bSdh 	if (pwp->tq == NULL) {
721*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to create worker taskq");
722*4c06356bSdh 		goto failure;
723*4c06356bSdh 	}
724*4c06356bSdh 
725*4c06356bSdh 	/*
726*4c06356bSdh 	 * Cache of structures for dealing with I/O completion callbacks.
727*4c06356bSdh 	 */
728*4c06356bSdh 	(void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst);
729*4c06356bSdh 	pwp->iocomp_cb_cache = kmem_cache_create(buf,
730*4c06356bSdh 	    sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0);
731*4c06356bSdh 
732*4c06356bSdh 	/*
733*4c06356bSdh 	 * Cache of PHY structures
734*4c06356bSdh 	 */
735*4c06356bSdh 	(void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst);
736*4c06356bSdh 	pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8,
737*4c06356bSdh 	    pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp,
738*4c06356bSdh 	    NULL, 0);
739*4c06356bSdh 
740*4c06356bSdh 	/*
741*4c06356bSdh 	 * Allocate space for the I/O completion threads
742*4c06356bSdh 	 */
743*4c06356bSdh 	num_threads = ncpus_online;
744*4c06356bSdh 	if (num_threads > PMCS_MAX_CQ_THREADS) {
745*4c06356bSdh 		num_threads = PMCS_MAX_CQ_THREADS;
746*4c06356bSdh 	}
747*4c06356bSdh 
748*4c06356bSdh 	pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) *
749*4c06356bSdh 	    num_threads, KM_SLEEP);
750*4c06356bSdh 	pwp->cq_info.cq_threads = num_threads;
751*4c06356bSdh 	pwp->cq_info.cq_next_disp_thr = 0;
752*4c06356bSdh 	pwp->cq_info.cq_stop = B_FALSE;
753*4c06356bSdh 
754*4c06356bSdh 	/*
755*4c06356bSdh 	 * Set the quantum value in clock ticks for the I/O interrupt
756*4c06356bSdh 	 * coalescing timer.
757*4c06356bSdh 	 */
758*4c06356bSdh 	pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS);
759*4c06356bSdh 
760*4c06356bSdh 	/*
761*4c06356bSdh 	 * We have a delicate dance here. We need to set up
762*4c06356bSdh 	 * interrupts so we know how to set up some OQC
763*4c06356bSdh 	 * tables. However, while we're setting up table
764*4c06356bSdh 	 * access, we may need to flash new firmware and
765*4c06356bSdh 	 * reset the card, which will take some finessing.
766*4c06356bSdh 	 */
767*4c06356bSdh 
768*4c06356bSdh 	/*
769*4c06356bSdh 	 * Set up interrupts here.
770*4c06356bSdh 	 */
771*4c06356bSdh 	switch (pmcs_setup_intr(pwp)) {
772*4c06356bSdh 	case 0:
773*4c06356bSdh 		break;
774*4c06356bSdh 	case EIO:
775*4c06356bSdh 		pwp->stuck = 1;
776*4c06356bSdh 		/* FALLTHROUGH */
777*4c06356bSdh 	default:
778*4c06356bSdh 		goto failure;
779*4c06356bSdh 	}
780*4c06356bSdh 
781*4c06356bSdh 	/*
782*4c06356bSdh 	 * Set these up now becuase they are used to initialize the OQC tables.
783*4c06356bSdh 	 *
784*4c06356bSdh 	 * If we have MSI or MSI-X interrupts set up and we have enough
785*4c06356bSdh 	 * vectors for each OQ, the Outbound Queue vectors can all be the
786*4c06356bSdh 	 * same as the appropriate interrupt routine will have been called
787*4c06356bSdh 	 * and the doorbell register automatically cleared.
788*4c06356bSdh 	 * This keeps us from having to check the Outbound Doorbell register
789*4c06356bSdh 	 * when the routines for these interrupts are called.
790*4c06356bSdh 	 *
791*4c06356bSdh 	 * If we have Legacy INT-X interrupts set up or we didn't have enough
792*4c06356bSdh 	 * MSI/MSI-X vectors to uniquely identify each OQ, we point these
793*4c06356bSdh 	 * vectors to the bits we would like to have set in the Outbound
794*4c06356bSdh 	 * Doorbell register because pmcs_all_intr will read the doorbell
795*4c06356bSdh 	 * register to find out why we have an interrupt and write the
796*4c06356bSdh 	 * corresponding 'clear' bit for that interrupt.
797*4c06356bSdh 	 */
798*4c06356bSdh 
799*4c06356bSdh 	switch (pwp->intr_cnt) {
800*4c06356bSdh 	case 1:
801*4c06356bSdh 		/*
802*4c06356bSdh 		 * Only one vector, so we must check all OQs for MSI.  For
803*4c06356bSdh 		 * INT-X, there's only one vector anyway, so we can just
804*4c06356bSdh 		 * use the outbound queue bits to keep from having to
805*4c06356bSdh 		 * check each queue for each interrupt.
806*4c06356bSdh 		 */
807*4c06356bSdh 		if (pwp->int_type == PMCS_INT_FIXED) {
808*4c06356bSdh 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
809*4c06356bSdh 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
810*4c06356bSdh 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
811*4c06356bSdh 		} else {
812*4c06356bSdh 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
813*4c06356bSdh 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE;
814*4c06356bSdh 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE;
815*4c06356bSdh 		}
816*4c06356bSdh 		break;
817*4c06356bSdh 	case 2:
818*4c06356bSdh 		/* With 2, we can at least isolate IODONE */
819*4c06356bSdh 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
820*4c06356bSdh 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
821*4c06356bSdh 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL;
822*4c06356bSdh 		break;
823*4c06356bSdh 	case 4:
824*4c06356bSdh 		/* With 4 vectors, everybody gets one */
825*4c06356bSdh 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
826*4c06356bSdh 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
827*4c06356bSdh 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
828*4c06356bSdh 		break;
829*4c06356bSdh 	}
830*4c06356bSdh 
831*4c06356bSdh 	/*
832*4c06356bSdh 	 * Do the first part of setup
833*4c06356bSdh 	 */
834*4c06356bSdh 	if (pmcs_setup(pwp)) {
835*4c06356bSdh 		goto failure;
836*4c06356bSdh 	}
837*4c06356bSdh 	pmcs_report_fwversion(pwp);
838*4c06356bSdh 
839*4c06356bSdh 	/*
840*4c06356bSdh 	 * Now do some additonal allocations based upon information
841*4c06356bSdh 	 * gathered during MPI setup.
842*4c06356bSdh 	 */
843*4c06356bSdh 	pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP);
844*4c06356bSdh 	ASSERT(pwp->nphy < SAS2_PHYNUM_MAX);
845*4c06356bSdh 	phyp = pwp->root_phys;
846*4c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
847*4c06356bSdh 		if (i < pwp->nphy-1) {
848*4c06356bSdh 			phyp->sibling = (phyp + 1);
849*4c06356bSdh 		}
850*4c06356bSdh 		mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
851*4c06356bSdh 		    DDI_INTR_PRI(pwp->intr_pri));
852*4c06356bSdh 		phyp->phynum = i & SAS2_PHYNUM_MASK;
853*4c06356bSdh 		pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path));
854*4c06356bSdh 		phyp->pwp = pwp;
855*4c06356bSdh 		phyp->device_id = PMCS_INVALID_DEVICE_ID;
856*4c06356bSdh 		phyp++;
857*4c06356bSdh 	}
858*4c06356bSdh 
859*4c06356bSdh 	pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP);
860*4c06356bSdh 	for (i = 0; i < pwp->max_cmd - 1; i++) {
861*4c06356bSdh 		pmcwork_t *pwrk = &pwp->work[i];
862*4c06356bSdh 		mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER,
863*4c06356bSdh 		    DDI_INTR_PRI(pwp->intr_pri));
864*4c06356bSdh 		cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL);
865*4c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
866*4c06356bSdh 
867*4c06356bSdh 	}
868*4c06356bSdh 	pwp->targets = (pmcs_xscsi_t **)
869*4c06356bSdh 	    kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP);
870*4c06356bSdh 
871*4c06356bSdh 	pwp->iqpt = (pmcs_iqp_trace_t *)
872*4c06356bSdh 	    kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP);
873*4c06356bSdh 	pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP);
874*4c06356bSdh 	pwp->iqpt->curpos = pwp->iqpt->head;
875*4c06356bSdh 	pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE;
876*4c06356bSdh 
877*4c06356bSdh 	/*
878*4c06356bSdh 	 * Start MPI communication.
879*4c06356bSdh 	 */
880*4c06356bSdh 	if (pmcs_start_mpi(pwp)) {
881*4c06356bSdh 		if (pmcs_soft_reset(pwp, B_FALSE)) {
882*4c06356bSdh 			goto failure;
883*4c06356bSdh 		}
884*4c06356bSdh 	}
885*4c06356bSdh 
886*4c06356bSdh 	/*
887*4c06356bSdh 	 * Do some initial acceptance tests.
888*4c06356bSdh 	 * This tests interrupts and queues.
889*4c06356bSdh 	 */
890*4c06356bSdh 	if (pmcs_echo_test(pwp)) {
891*4c06356bSdh 		goto failure;
892*4c06356bSdh 	}
893*4c06356bSdh 
894*4c06356bSdh 	/* Read VPD - if it exists */
895*4c06356bSdh 	if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) {
896*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unable to read VPD: "
897*4c06356bSdh 		    "attempting to fabricate", __func__);
898*4c06356bSdh 		/*
899*4c06356bSdh 		 * When we release, this must goto failure and the call
900*4c06356bSdh 		 * to pmcs_fabricate_wwid is removed.
901*4c06356bSdh 		 */
902*4c06356bSdh 		/* goto failure; */
903*4c06356bSdh 		if (!pmcs_fabricate_wwid(pwp)) {
904*4c06356bSdh 			goto failure;
905*4c06356bSdh 		}
906*4c06356bSdh 	}
907*4c06356bSdh 
908*4c06356bSdh 	/*
909*4c06356bSdh 	 * We're now officially running
910*4c06356bSdh 	 */
911*4c06356bSdh 	pwp->state = STATE_RUNNING;
912*4c06356bSdh 
913*4c06356bSdh 	/*
914*4c06356bSdh 	 * Check firmware versions and load new firmware
915*4c06356bSdh 	 * if needed and reset.
916*4c06356bSdh 	 */
917*4c06356bSdh 	if (pmcs_firmware_update(pwp)) {
918*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, "%s: Firmware update failed",
919*4c06356bSdh 		    __func__);
920*4c06356bSdh 		goto failure;
921*4c06356bSdh 	}
922*4c06356bSdh 
923*4c06356bSdh 	/*
924*4c06356bSdh 	 * Create completion threads.
925*4c06356bSdh 	 */
926*4c06356bSdh 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
927*4c06356bSdh 		pwp->cq_info.cq_thr_info[i].cq_pwp = pwp;
928*4c06356bSdh 		pwp->cq_info.cq_thr_info[i].cq_thread =
929*4c06356bSdh 		    thread_create(NULL, 0, pmcs_scsa_cq_run,
930*4c06356bSdh 		    &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri);
931*4c06356bSdh 	}
932*4c06356bSdh 
933*4c06356bSdh 	/*
934*4c06356bSdh 	 * Create one thread to deal with the updating of the interrupt
935*4c06356bSdh 	 * coalescing timer.
936*4c06356bSdh 	 */
937*4c06356bSdh 	pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal,
938*4c06356bSdh 	    pwp, 0, &p0, TS_RUN, minclsyspri);
939*4c06356bSdh 
940*4c06356bSdh 	/*
941*4c06356bSdh 	 * Kick off the watchdog
942*4c06356bSdh 	 */
943*4c06356bSdh 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
944*4c06356bSdh 	    drv_usectohz(PMCS_WATCH_INTERVAL));
945*4c06356bSdh 	/*
946*4c06356bSdh 	 * Do the SCSI attachment code (before starting phys)
947*4c06356bSdh 	 */
948*4c06356bSdh 	if (pmcs_scsa_init(pwp, &pmcs_dattr)) {
949*4c06356bSdh 		goto failure;
950*4c06356bSdh 	}
951*4c06356bSdh 	pwp->hba_attached = 1;
952*4c06356bSdh 
953*4c06356bSdh 	/*
954*4c06356bSdh 	 * Initialize the rwlock for the iport elements.
955*4c06356bSdh 	 */
956*4c06356bSdh 	rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL);
957*4c06356bSdh 
958*4c06356bSdh 	/* Check all acc & dma handles allocated in attach */
959*4c06356bSdh 	if (pmcs_check_acc_dma_handle(pwp)) {
960*4c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
961*4c06356bSdh 		goto failure;
962*4c06356bSdh 	}
963*4c06356bSdh 
964*4c06356bSdh 	/*
965*4c06356bSdh 	 * Create the phymap for this HBA instance
966*4c06356bSdh 	 */
967*4c06356bSdh 	if (sas_phymap_create(dip, phymap_usec, PHYMAP_MODE_SIMPLE, NULL,
968*4c06356bSdh 	    pwp, pmcs_phymap_activate, pmcs_phymap_deactivate,
969*4c06356bSdh 	    &pwp->hss_phymap) != DDI_SUCCESS) {
970*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: pmcs%d phymap_create failed",
971*4c06356bSdh 		    __func__, inst);
972*4c06356bSdh 		goto failure;
973*4c06356bSdh 	}
974*4c06356bSdh 	ASSERT(pwp->hss_phymap);
975*4c06356bSdh 
976*4c06356bSdh 	/*
977*4c06356bSdh 	 * Create the iportmap for this HBA instance
978*4c06356bSdh 	 */
979*4c06356bSdh 	if (scsi_hba_iportmap_create(dip, iportmap_usec, pwp->nphy,
980*4c06356bSdh 	    &pwp->hss_iportmap) != DDI_SUCCESS) {
981*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: pmcs%d iportmap_create "
982*4c06356bSdh 		    "failed", __func__, inst);
983*4c06356bSdh 		goto failure;
984*4c06356bSdh 	}
985*4c06356bSdh 	ASSERT(pwp->hss_iportmap);
986*4c06356bSdh 
987*4c06356bSdh 	/*
988*4c06356bSdh 	 * Start the PHYs.
989*4c06356bSdh 	 */
990*4c06356bSdh 	if (pmcs_start_phys(pwp)) {
991*4c06356bSdh 		goto failure;
992*4c06356bSdh 	}
993*4c06356bSdh 
994*4c06356bSdh 	/*
995*4c06356bSdh 	 * From this point on, we can't fail.
996*4c06356bSdh 	 */
997*4c06356bSdh 	ddi_report_dev(dip);
998*4c06356bSdh 
999*4c06356bSdh 	/* SM-HBA */
1000*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED,
1001*4c06356bSdh 	    &sm_hba);
1002*4c06356bSdh 
1003*4c06356bSdh 	/* SM-HBA */
1004*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION,
1005*4c06356bSdh 	    pmcs_driver_rev);
1006*4c06356bSdh 
1007*4c06356bSdh 	/* SM-HBA */
1008*4c06356bSdh 	chiprev = 'A' + pwp->chiprev;
1009*4c06356bSdh 	(void) snprintf(hw_rev, 2, "%s", &chiprev);
1010*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION,
1011*4c06356bSdh 	    hw_rev);
1012*4c06356bSdh 
1013*4c06356bSdh 	/* SM-HBA */
1014*4c06356bSdh 	switch (PMCS_FW_TYPE(pwp)) {
1015*4c06356bSdh 	case PMCS_FW_TYPE_RELEASED:
1016*4c06356bSdh 		fwsupport = "Released";
1017*4c06356bSdh 		break;
1018*4c06356bSdh 	case PMCS_FW_TYPE_DEVELOPMENT:
1019*4c06356bSdh 		fwsupport = "Development";
1020*4c06356bSdh 		break;
1021*4c06356bSdh 	case PMCS_FW_TYPE_ALPHA:
1022*4c06356bSdh 		fwsupport = "Alpha";
1023*4c06356bSdh 		break;
1024*4c06356bSdh 	case PMCS_FW_TYPE_BETA:
1025*4c06356bSdh 		fwsupport = "Beta";
1026*4c06356bSdh 		break;
1027*4c06356bSdh 	default:
1028*4c06356bSdh 		fwsupport = "Special";
1029*4c06356bSdh 		break;
1030*4c06356bSdh 	}
1031*4c06356bSdh 	(void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s",
1032*4c06356bSdh 	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
1033*4c06356bSdh 	    fwsupport);
1034*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION,
1035*4c06356bSdh 	    fw_rev);
1036*4c06356bSdh 
1037*4c06356bSdh 	/* SM-HBA */
1038*4c06356bSdh 	num_phys = pwp->nphy;
1039*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA,
1040*4c06356bSdh 	    &num_phys);
1041*4c06356bSdh 
1042*4c06356bSdh 	/* SM-HBA */
1043*4c06356bSdh 	protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
1044*4c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL,
1045*4c06356bSdh 	    &protocol);
1046*4c06356bSdh 
1047*4c06356bSdh 	return (DDI_SUCCESS);
1048*4c06356bSdh 
1049*4c06356bSdh failure:
1050*4c06356bSdh 	if (pmcs_unattach(pwp)) {
1051*4c06356bSdh 		pwp->stuck = 1;
1052*4c06356bSdh 	}
1053*4c06356bSdh 	return (DDI_FAILURE);
1054*4c06356bSdh }
1055*4c06356bSdh 
1056*4c06356bSdh int
1057*4c06356bSdh pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1058*4c06356bSdh {
1059*4c06356bSdh 	int inst = ddi_get_instance(dip);
1060*4c06356bSdh 	pmcs_iport_t	*iport = NULL;
1061*4c06356bSdh 	pmcs_hw_t	*pwp = NULL;
1062*4c06356bSdh 	scsi_hba_tran_t	*tran;
1063*4c06356bSdh 
1064*4c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
1065*4c06356bSdh 		/* iport node */
1066*4c06356bSdh 		iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
1067*4c06356bSdh 		ASSERT(iport);
1068*4c06356bSdh 		if (iport == NULL) {
1069*4c06356bSdh 			return (DDI_FAILURE);
1070*4c06356bSdh 		}
1071*4c06356bSdh 		pwp = iport->pwp;
1072*4c06356bSdh 	} else {
1073*4c06356bSdh 		/* hba node */
1074*4c06356bSdh 		pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst);
1075*4c06356bSdh 		ASSERT(pwp);
1076*4c06356bSdh 		if (pwp == NULL) {
1077*4c06356bSdh 			return (DDI_FAILURE);
1078*4c06356bSdh 		}
1079*4c06356bSdh 	}
1080*4c06356bSdh 
1081*4c06356bSdh 	switch (cmd) {
1082*4c06356bSdh 	case DDI_DETACH:
1083*4c06356bSdh 		if (iport) {
1084*4c06356bSdh 			/* iport detach */
1085*4c06356bSdh 			if (pmcs_iport_unattach(iport)) {
1086*4c06356bSdh 				return (DDI_FAILURE);
1087*4c06356bSdh 			}
1088*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "iport%d detached", inst);
1089*4c06356bSdh 			return (DDI_SUCCESS);
1090*4c06356bSdh 		} else {
1091*4c06356bSdh 			/* HBA detach */
1092*4c06356bSdh 			if (pmcs_unattach(pwp)) {
1093*4c06356bSdh 				return (DDI_FAILURE);
1094*4c06356bSdh 			}
1095*4c06356bSdh 			return (DDI_SUCCESS);
1096*4c06356bSdh 		}
1097*4c06356bSdh 
1098*4c06356bSdh 	case DDI_SUSPEND:
1099*4c06356bSdh 	case DDI_PM_SUSPEND:
1100*4c06356bSdh 		/* No DDI_SUSPEND on iport nodes */
1101*4c06356bSdh 		if (iport) {
1102*4c06356bSdh 			return (DDI_SUCCESS);
1103*4c06356bSdh 		}
1104*4c06356bSdh 
1105*4c06356bSdh 		if (pwp->stuck) {
1106*4c06356bSdh 			return (DDI_FAILURE);
1107*4c06356bSdh 		}
1108*4c06356bSdh 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
1109*4c06356bSdh 		if (!tran) {
1110*4c06356bSdh 			return (DDI_FAILURE);
1111*4c06356bSdh 		}
1112*4c06356bSdh 
1113*4c06356bSdh 		pwp = TRAN2PMC(tran);
1114*4c06356bSdh 		if (pwp == NULL) {
1115*4c06356bSdh 			return (DDI_FAILURE);
1116*4c06356bSdh 		}
1117*4c06356bSdh 		mutex_enter(&pwp->lock);
1118*4c06356bSdh 		if (pwp->tq) {
1119*4c06356bSdh 			ddi_taskq_suspend(pwp->tq);
1120*4c06356bSdh 		}
1121*4c06356bSdh 		pwp->suspended = 1;
1122*4c06356bSdh 		mutex_exit(&pwp->lock);
1123*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, "PMC8X6G suspending");
1124*4c06356bSdh 		return (DDI_SUCCESS);
1125*4c06356bSdh 
1126*4c06356bSdh 	default:
1127*4c06356bSdh 		return (DDI_FAILURE);
1128*4c06356bSdh 	}
1129*4c06356bSdh }
1130*4c06356bSdh 
1131*4c06356bSdh static int
1132*4c06356bSdh pmcs_iport_unattach(pmcs_iport_t *iport)
1133*4c06356bSdh {
1134*4c06356bSdh 	pmcs_hw_t	*pwp = iport->pwp;
1135*4c06356bSdh 
1136*4c06356bSdh 	/*
1137*4c06356bSdh 	 * First, check if there are still any configured targets on this
1138*4c06356bSdh 	 * iport.  If so, we fail detach.
1139*4c06356bSdh 	 */
1140*4c06356bSdh 	if (pmcs_iport_has_targets(pwp, iport)) {
1141*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d detach failure: "
1142*4c06356bSdh 		    "iport has targets (luns)", ddi_get_instance(iport->dip));
1143*4c06356bSdh 		return (DDI_FAILURE);
1144*4c06356bSdh 	}
1145*4c06356bSdh 
1146*4c06356bSdh 	/*
1147*4c06356bSdh 	 * Remove this iport from our list if it is inactive in the phymap.
1148*4c06356bSdh 	 */
1149*4c06356bSdh 	rw_enter(&pwp->iports_lock, RW_WRITER);
1150*4c06356bSdh 	mutex_enter(&iport->lock);
1151*4c06356bSdh 
1152*4c06356bSdh 	if (iport->ua_state == UA_ACTIVE) {
1153*4c06356bSdh 		mutex_exit(&iport->lock);
1154*4c06356bSdh 		rw_exit(&pwp->iports_lock);
1155*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d detach failure: "
1156*4c06356bSdh 		    "iport unit address active in phymap",
1157*4c06356bSdh 		    ddi_get_instance(iport->dip));
1158*4c06356bSdh 		return (DDI_FAILURE);
1159*4c06356bSdh 	}
1160*4c06356bSdh 
1161*4c06356bSdh 	/* If it's our only iport, clear iports_attached */
1162*4c06356bSdh 	ASSERT(pwp->num_iports >= 1);
1163*4c06356bSdh 	if (--pwp->num_iports == 0) {
1164*4c06356bSdh 		pwp->iports_attached = 0;
1165*4c06356bSdh 	}
1166*4c06356bSdh 
1167*4c06356bSdh 	ASSERT(list_link_active(&iport->list_node));
1168*4c06356bSdh 	list_remove(&pwp->iports, iport);
1169*4c06356bSdh 	rw_exit(&pwp->iports_lock);
1170*4c06356bSdh 
1171*4c06356bSdh 	/*
1172*4c06356bSdh 	 * We have removed the iport handle from the HBA's iports list,
1173*4c06356bSdh 	 * there will be no new references to it. Two things must be
1174*4c06356bSdh 	 * guarded against here.  First, we could have PHY up events,
1175*4c06356bSdh 	 * adding themselves to the iport->phys list and grabbing ref's
1176*4c06356bSdh 	 * on our iport handle.  Second, we could have existing references
1177*4c06356bSdh 	 * to this iport handle from a point in time prior to the list
1178*4c06356bSdh 	 * removal above.
1179*4c06356bSdh 	 *
1180*4c06356bSdh 	 * So first, destroy the phys list. Remove any phys that have snuck
1181*4c06356bSdh 	 * in after the phymap deactivate, dropping the refcnt accordingly.
1182*4c06356bSdh 	 * If these PHYs are still up if and when the phymap reactivates
1183*4c06356bSdh 	 * (i.e. when this iport reattaches), we'll populate the list with
1184*4c06356bSdh 	 * them and bump the refcnt back up.
1185*4c06356bSdh 	 */
1186*4c06356bSdh 	pmcs_remove_phy_from_iport(iport, NULL);
1187*4c06356bSdh 	ASSERT(list_is_empty(&iport->phys));
1188*4c06356bSdh 	list_destroy(&iport->phys);
1189*4c06356bSdh 	mutex_exit(&iport->lock);
1190*4c06356bSdh 
1191*4c06356bSdh 	/*
1192*4c06356bSdh 	 * Second, wait for any other references to this iport to be
1193*4c06356bSdh 	 * dropped, then continue teardown.
1194*4c06356bSdh 	 */
1195*4c06356bSdh 	mutex_enter(&iport->refcnt_lock);
1196*4c06356bSdh 	while (iport->refcnt != 0) {
1197*4c06356bSdh 		cv_wait(&iport->refcnt_cv, &iport->refcnt_lock);
1198*4c06356bSdh 	}
1199*4c06356bSdh 	mutex_exit(&iport->refcnt_lock);
1200*4c06356bSdh 
1201*4c06356bSdh 	/* Delete kstats */
1202*4c06356bSdh 	pmcs_destroy_phy_stats(iport);
1203*4c06356bSdh 
1204*4c06356bSdh 	/* Destroy the iport target map */
1205*4c06356bSdh 	if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) {
1206*4c06356bSdh 		return (DDI_FAILURE);
1207*4c06356bSdh 	}
1208*4c06356bSdh 
1209*4c06356bSdh 	/* Free the tgt soft state */
1210*4c06356bSdh 	if (iport->tgt_sstate != NULL) {
1211*4c06356bSdh 		ddi_soft_state_bystr_fini(&iport->tgt_sstate);
1212*4c06356bSdh 	}
1213*4c06356bSdh 
1214*4c06356bSdh 	/* Free our unit address string */
1215*4c06356bSdh 	strfree(iport->ua);
1216*4c06356bSdh 
1217*4c06356bSdh 	/* Finish teardown and free the softstate */
1218*4c06356bSdh 	mutex_destroy(&iport->refcnt_lock);
1219*4c06356bSdh 	ASSERT(iport->refcnt == 0);
1220*4c06356bSdh 	cv_destroy(&iport->refcnt_cv);
1221*4c06356bSdh 	mutex_destroy(&iport->lock);
1222*4c06356bSdh 	ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip));
1223*4c06356bSdh 
1224*4c06356bSdh 	return (DDI_SUCCESS);
1225*4c06356bSdh }
1226*4c06356bSdh 
1227*4c06356bSdh static int
1228*4c06356bSdh pmcs_unattach(pmcs_hw_t *pwp)
1229*4c06356bSdh {
1230*4c06356bSdh 	int i;
1231*4c06356bSdh 	enum pwpstate curstate;
1232*4c06356bSdh 	pmcs_cq_thr_info_t *cqti;
1233*4c06356bSdh 
1234*4c06356bSdh 	/*
1235*4c06356bSdh 	 * Tear down the interrupt infrastructure.
1236*4c06356bSdh 	 */
1237*4c06356bSdh 	if (pmcs_teardown_intr(pwp)) {
1238*4c06356bSdh 		pwp->stuck = 1;
1239*4c06356bSdh 	}
1240*4c06356bSdh 	pwp->intr_cnt = 0;
1241*4c06356bSdh 
1242*4c06356bSdh 	/*
1243*4c06356bSdh 	 * Grab a lock, if initted, to set state.
1244*4c06356bSdh 	 */
1245*4c06356bSdh 	if (pwp->locks_initted) {
1246*4c06356bSdh 		mutex_enter(&pwp->lock);
1247*4c06356bSdh 		if (pwp->state != STATE_DEAD) {
1248*4c06356bSdh 			pwp->state = STATE_UNPROBING;
1249*4c06356bSdh 		}
1250*4c06356bSdh 		curstate = pwp->state;
1251*4c06356bSdh 		mutex_exit(&pwp->lock);
1252*4c06356bSdh 
1253*4c06356bSdh 		/*
1254*4c06356bSdh 		 * Stop the I/O completion threads.
1255*4c06356bSdh 		 */
1256*4c06356bSdh 		mutex_enter(&pwp->cq_lock);
1257*4c06356bSdh 		pwp->cq_info.cq_stop = B_TRUE;
1258*4c06356bSdh 		for (i = 0; i < pwp->cq_info.cq_threads; i++) {
1259*4c06356bSdh 			if (pwp->cq_info.cq_thr_info[i].cq_thread) {
1260*4c06356bSdh 				cqti = &pwp->cq_info.cq_thr_info[i];
1261*4c06356bSdh 				mutex_enter(&cqti->cq_thr_lock);
1262*4c06356bSdh 				cv_signal(&cqti->cq_cv);
1263*4c06356bSdh 				mutex_exit(&cqti->cq_thr_lock);
1264*4c06356bSdh 				mutex_exit(&pwp->cq_lock);
1265*4c06356bSdh 				thread_join(cqti->cq_thread->t_did);
1266*4c06356bSdh 				mutex_enter(&pwp->cq_lock);
1267*4c06356bSdh 			}
1268*4c06356bSdh 		}
1269*4c06356bSdh 		mutex_exit(&pwp->cq_lock);
1270*4c06356bSdh 
1271*4c06356bSdh 		/*
1272*4c06356bSdh 		 * Stop the interrupt coalescing timer thread
1273*4c06356bSdh 		 */
1274*4c06356bSdh 		if (pwp->ict_thread) {
1275*4c06356bSdh 			mutex_enter(&pwp->ict_lock);
1276*4c06356bSdh 			pwp->io_intr_coal.stop_thread = B_TRUE;
1277*4c06356bSdh 			cv_signal(&pwp->ict_cv);
1278*4c06356bSdh 			mutex_exit(&pwp->ict_lock);
1279*4c06356bSdh 			thread_join(pwp->ict_thread->t_did);
1280*4c06356bSdh 		}
1281*4c06356bSdh 	} else {
1282*4c06356bSdh 		if (pwp->state != STATE_DEAD) {
1283*4c06356bSdh 			pwp->state = STATE_UNPROBING;
1284*4c06356bSdh 		}
1285*4c06356bSdh 		curstate = pwp->state;
1286*4c06356bSdh 	}
1287*4c06356bSdh 
1288*4c06356bSdh 	if (&pwp->iports != NULL) {
1289*4c06356bSdh 		/* Destroy the iports lock */
1290*4c06356bSdh 		rw_destroy(&pwp->iports_lock);
1291*4c06356bSdh 		/* Destroy the iports list */
1292*4c06356bSdh 		ASSERT(list_is_empty(&pwp->iports));
1293*4c06356bSdh 		list_destroy(&pwp->iports);
1294*4c06356bSdh 	}
1295*4c06356bSdh 
1296*4c06356bSdh 	if (pwp->hss_iportmap != NULL) {
1297*4c06356bSdh 		/* Destroy the iportmap */
1298*4c06356bSdh 		scsi_hba_iportmap_destroy(pwp->hss_iportmap);
1299*4c06356bSdh 	}
1300*4c06356bSdh 
1301*4c06356bSdh 	if (pwp->hss_phymap != NULL) {
1302*4c06356bSdh 		/* Destroy the phymap */
1303*4c06356bSdh 		sas_phymap_destroy(pwp->hss_phymap);
1304*4c06356bSdh 	}
1305*4c06356bSdh 
1306*4c06356bSdh 	/*
1307*4c06356bSdh 	 * Make sure that any pending watchdog won't
1308*4c06356bSdh 	 * be called from this point on out.
1309*4c06356bSdh 	 */
1310*4c06356bSdh 	(void) untimeout(pwp->wdhandle);
1311*4c06356bSdh 	/*
1312*4c06356bSdh 	 * After the above action, the watchdog
1313*4c06356bSdh 	 * timer that starts up the worker task
1314*4c06356bSdh 	 * may trigger but will exit immediately
1315*4c06356bSdh 	 * on triggering.
1316*4c06356bSdh 	 *
1317*4c06356bSdh 	 * Now that this is done, we can destroy
1318*4c06356bSdh 	 * the task queue, which will wait if we're
1319*4c06356bSdh 	 * running something on it.
1320*4c06356bSdh 	 */
1321*4c06356bSdh 	if (pwp->tq) {
1322*4c06356bSdh 		ddi_taskq_destroy(pwp->tq);
1323*4c06356bSdh 		pwp->tq = NULL;
1324*4c06356bSdh 	}
1325*4c06356bSdh 
1326*4c06356bSdh 	pmcs_fm_fini(pwp);
1327*4c06356bSdh 
1328*4c06356bSdh 	if (pwp->hba_attached) {
1329*4c06356bSdh 		(void) scsi_hba_detach(pwp->dip);
1330*4c06356bSdh 		pwp->hba_attached = 0;
1331*4c06356bSdh 	}
1332*4c06356bSdh 
1333*4c06356bSdh 	/*
1334*4c06356bSdh 	 * If the chip hasn't been marked dead, shut it down now
1335*4c06356bSdh 	 * to bring it back to a known state without attempting
1336*4c06356bSdh 	 * a soft reset.
1337*4c06356bSdh 	 */
1338*4c06356bSdh 	if (curstate != STATE_DEAD && pwp->locks_initted) {
1339*4c06356bSdh 		/*
1340*4c06356bSdh 		 * De-register all registered devices
1341*4c06356bSdh 		 */
1342*4c06356bSdh 		pmcs_deregister_devices(pwp, pwp->root_phys);
1343*4c06356bSdh 
1344*4c06356bSdh 		/*
1345*4c06356bSdh 		 * Stop all the phys.
1346*4c06356bSdh 		 */
1347*4c06356bSdh 		pmcs_stop_phys(pwp);
1348*4c06356bSdh 
1349*4c06356bSdh 		/*
1350*4c06356bSdh 		 * Shut Down Message Passing
1351*4c06356bSdh 		 */
1352*4c06356bSdh 		(void) pmcs_stop_mpi(pwp);
1353*4c06356bSdh 
1354*4c06356bSdh 		/*
1355*4c06356bSdh 		 * Reset chip
1356*4c06356bSdh 		 */
1357*4c06356bSdh 		(void) pmcs_soft_reset(pwp, B_FALSE);
1358*4c06356bSdh 	}
1359*4c06356bSdh 
1360*4c06356bSdh 	/*
1361*4c06356bSdh 	 * Turn off interrupts on the chip
1362*4c06356bSdh 	 */
1363*4c06356bSdh 	if (pwp->mpi_acc_handle) {
1364*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
1365*4c06356bSdh 	}
1366*4c06356bSdh 
1367*4c06356bSdh 	/* Destroy pwp's lock */
1368*4c06356bSdh 	if (pwp->locks_initted) {
1369*4c06356bSdh 		mutex_destroy(&pwp->lock);
1370*4c06356bSdh 		mutex_destroy(&pwp->dma_lock);
1371*4c06356bSdh 		mutex_destroy(&pwp->axil_lock);
1372*4c06356bSdh 		mutex_destroy(&pwp->cq_lock);
1373*4c06356bSdh 		mutex_destroy(&pwp->config_lock);
1374*4c06356bSdh 		mutex_destroy(&pwp->ict_lock);
1375*4c06356bSdh 		mutex_destroy(&pwp->wfree_lock);
1376*4c06356bSdh 		mutex_destroy(&pwp->pfree_lock);
1377*4c06356bSdh 		mutex_destroy(&pwp->dead_phylist_lock);
1378*4c06356bSdh #ifdef	DEBUG
1379*4c06356bSdh 		mutex_destroy(&pwp->dbglock);
1380*4c06356bSdh #endif
1381*4c06356bSdh 		cv_destroy(&pwp->ict_cv);
1382*4c06356bSdh 		cv_destroy(&pwp->drain_cv);
1383*4c06356bSdh 		pwp->locks_initted = 0;
1384*4c06356bSdh 	}
1385*4c06356bSdh 
1386*4c06356bSdh 	/*
1387*4c06356bSdh 	 * Free DMA handles and associated consistent memory
1388*4c06356bSdh 	 */
1389*4c06356bSdh 	if (pwp->regdump_hndl) {
1390*4c06356bSdh 		if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) {
1391*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
1392*4c06356bSdh 			    "at %s():%d", __func__, __LINE__);
1393*4c06356bSdh 		}
1394*4c06356bSdh 		ddi_dma_free_handle(&pwp->regdump_hndl);
1395*4c06356bSdh 		ddi_dma_mem_free(&pwp->regdump_acchdl);
1396*4c06356bSdh 		pwp->regdump_hndl = 0;
1397*4c06356bSdh 	}
1398*4c06356bSdh 	if (pwp->fwlog_hndl) {
1399*4c06356bSdh 		if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) {
1400*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
1401*4c06356bSdh 			    "at %s():%d", __func__, __LINE__);
1402*4c06356bSdh 		}
1403*4c06356bSdh 		ddi_dma_free_handle(&pwp->fwlog_hndl);
1404*4c06356bSdh 		ddi_dma_mem_free(&pwp->fwlog_acchdl);
1405*4c06356bSdh 		pwp->fwlog_hndl = 0;
1406*4c06356bSdh 	}
1407*4c06356bSdh 	if (pwp->cip_handles) {
1408*4c06356bSdh 		if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) {
1409*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
1410*4c06356bSdh 			    "at %s():%d", __func__, __LINE__);
1411*4c06356bSdh 		}
1412*4c06356bSdh 		ddi_dma_free_handle(&pwp->cip_handles);
1413*4c06356bSdh 		ddi_dma_mem_free(&pwp->cip_acchdls);
1414*4c06356bSdh 		pwp->cip_handles = 0;
1415*4c06356bSdh 	}
1416*4c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
1417*4c06356bSdh 		if (pwp->oqp_handles[i]) {
1418*4c06356bSdh 			if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) !=
1419*4c06356bSdh 			    DDI_SUCCESS) {
1420*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
1421*4c06356bSdh 				    "failed at %s():%d", __func__, __LINE__);
1422*4c06356bSdh 			}
1423*4c06356bSdh 			ddi_dma_free_handle(&pwp->oqp_handles[i]);
1424*4c06356bSdh 			ddi_dma_mem_free(&pwp->oqp_acchdls[i]);
1425*4c06356bSdh 			pwp->oqp_handles[i] = 0;
1426*4c06356bSdh 		}
1427*4c06356bSdh 	}
1428*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
1429*4c06356bSdh 		if (pwp->iqp_handles[i]) {
1430*4c06356bSdh 			if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) !=
1431*4c06356bSdh 			    DDI_SUCCESS) {
1432*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
1433*4c06356bSdh 				    "failed at %s():%d", __func__, __LINE__);
1434*4c06356bSdh 			}
1435*4c06356bSdh 			ddi_dma_free_handle(&pwp->iqp_handles[i]);
1436*4c06356bSdh 			ddi_dma_mem_free(&pwp->iqp_acchdls[i]);
1437*4c06356bSdh 			pwp->iqp_handles[i] = 0;
1438*4c06356bSdh 		}
1439*4c06356bSdh 	}
1440*4c06356bSdh 
1441*4c06356bSdh 	pmcs_free_dma_chunklist(pwp);
1442*4c06356bSdh 
1443*4c06356bSdh 	/*
1444*4c06356bSdh 	 * Unmap registers and destroy access handles
1445*4c06356bSdh 	 */
1446*4c06356bSdh 	if (pwp->mpi_acc_handle) {
1447*4c06356bSdh 		ddi_regs_map_free(&pwp->mpi_acc_handle);
1448*4c06356bSdh 		pwp->mpi_acc_handle = 0;
1449*4c06356bSdh 	}
1450*4c06356bSdh 	if (pwp->top_acc_handle) {
1451*4c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
1452*4c06356bSdh 		pwp->top_acc_handle = 0;
1453*4c06356bSdh 	}
1454*4c06356bSdh 	if (pwp->gsm_acc_handle) {
1455*4c06356bSdh 		ddi_regs_map_free(&pwp->gsm_acc_handle);
1456*4c06356bSdh 		pwp->gsm_acc_handle = 0;
1457*4c06356bSdh 	}
1458*4c06356bSdh 	if (pwp->msg_acc_handle) {
1459*4c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
1460*4c06356bSdh 		pwp->msg_acc_handle = 0;
1461*4c06356bSdh 	}
1462*4c06356bSdh 	if (pwp->pci_acc_handle) {
1463*4c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
1464*4c06356bSdh 		pwp->pci_acc_handle = 0;
1465*4c06356bSdh 	}
1466*4c06356bSdh 
1467*4c06356bSdh 	/*
1468*4c06356bSdh 	 * Do memory allocation cleanup.
1469*4c06356bSdh 	 */
1470*4c06356bSdh 	while (pwp->dma_freelist) {
1471*4c06356bSdh 		pmcs_dmachunk_t *this = pwp->dma_freelist;
1472*4c06356bSdh 		pwp->dma_freelist = this->nxt;
1473*4c06356bSdh 		kmem_free(this, sizeof (pmcs_dmachunk_t));
1474*4c06356bSdh 	}
1475*4c06356bSdh 
1476*4c06356bSdh 	/*
1477*4c06356bSdh 	 * Free pools
1478*4c06356bSdh 	 */
1479*4c06356bSdh 	if (pwp->iocomp_cb_cache) {
1480*4c06356bSdh 		kmem_cache_destroy(pwp->iocomp_cb_cache);
1481*4c06356bSdh 	}
1482*4c06356bSdh 
1483*4c06356bSdh 	/*
1484*4c06356bSdh 	 * Free all PHYs (at level > 0), then free the cache
1485*4c06356bSdh 	 */
1486*4c06356bSdh 	pmcs_free_all_phys(pwp, pwp->root_phys);
1487*4c06356bSdh 	if (pwp->phy_cache) {
1488*4c06356bSdh 		kmem_cache_destroy(pwp->phy_cache);
1489*4c06356bSdh 	}
1490*4c06356bSdh 
1491*4c06356bSdh 	/*
1492*4c06356bSdh 	 * Free root PHYs
1493*4c06356bSdh 	 */
1494*4c06356bSdh 	if (pwp->root_phys) {
1495*4c06356bSdh 		pmcs_phy_t *phyp = pwp->root_phys;
1496*4c06356bSdh 		for (i = 0; i < pwp->nphy; i++) {
1497*4c06356bSdh 			mutex_destroy(&phyp->phy_lock);
1498*4c06356bSdh 			phyp = phyp->sibling;
1499*4c06356bSdh 		}
1500*4c06356bSdh 		kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t));
1501*4c06356bSdh 		pwp->root_phys = NULL;
1502*4c06356bSdh 		pwp->nphy = 0;
1503*4c06356bSdh 	}
1504*4c06356bSdh 
1505*4c06356bSdh 	/* Free the targets list */
1506*4c06356bSdh 	if (pwp->targets) {
1507*4c06356bSdh 		kmem_free(pwp->targets,
1508*4c06356bSdh 		    sizeof (pmcs_xscsi_t *) * pwp->max_dev);
1509*4c06356bSdh 	}
1510*4c06356bSdh 
1511*4c06356bSdh 	/*
1512*4c06356bSdh 	 * Free work structures
1513*4c06356bSdh 	 */
1514*4c06356bSdh 
1515*4c06356bSdh 	if (pwp->work && pwp->max_cmd) {
1516*4c06356bSdh 		for (i = 0; i < pwp->max_cmd - 1; i++) {
1517*4c06356bSdh 			pmcwork_t *pwrk = &pwp->work[i];
1518*4c06356bSdh 			mutex_destroy(&pwrk->lock);
1519*4c06356bSdh 			cv_destroy(&pwrk->sleep_cv);
1520*4c06356bSdh 		}
1521*4c06356bSdh 		kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
1522*4c06356bSdh 		pwp->work = NULL;
1523*4c06356bSdh 		pwp->max_cmd = 0;
1524*4c06356bSdh 	}
1525*4c06356bSdh 
1526*4c06356bSdh 	/*
1527*4c06356bSdh 	 * Do last property and SCSA cleanup
1528*4c06356bSdh 	 */
1529*4c06356bSdh 	if (pwp->tran) {
1530*4c06356bSdh 		scsi_hba_tran_free(pwp->tran);
1531*4c06356bSdh 		pwp->tran = NULL;
1532*4c06356bSdh 	}
1533*4c06356bSdh 	if (pwp->reset_notify_listf) {
1534*4c06356bSdh 		scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf);
1535*4c06356bSdh 		pwp->reset_notify_listf = NULL;
1536*4c06356bSdh 	}
1537*4c06356bSdh 	ddi_prop_remove_all(pwp->dip);
1538*4c06356bSdh 	if (pwp->stuck) {
1539*4c06356bSdh 		return (-1);
1540*4c06356bSdh 	}
1541*4c06356bSdh 
1542*4c06356bSdh 	/* Free register dump area if allocated */
1543*4c06356bSdh 	if (pwp->regdumpp) {
1544*4c06356bSdh 		kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE);
1545*4c06356bSdh 		pwp->regdumpp = NULL;
1546*4c06356bSdh 	}
1547*4c06356bSdh 	if (pwp->iqpt && pwp->iqpt->head) {
1548*4c06356bSdh 		kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE);
1549*4c06356bSdh 		pwp->iqpt->head = pwp->iqpt->curpos = NULL;
1550*4c06356bSdh 	}
1551*4c06356bSdh 	if (pwp->iqpt) {
1552*4c06356bSdh 		kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t));
1553*4c06356bSdh 		pwp->iqpt = NULL;
1554*4c06356bSdh 	}
1555*4c06356bSdh 
1556*4c06356bSdh 	ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip));
1557*4c06356bSdh 	return (0);
1558*4c06356bSdh }
1559*4c06356bSdh 
1560*4c06356bSdh /*
1561*4c06356bSdh  * quiesce (9E) entry point
1562*4c06356bSdh  *
1563*4c06356bSdh  * This function is called when the system is single-threaded at high PIL
1564*4c06356bSdh  * with preemption disabled. Therefore, the function must not block/wait/sleep.
1565*4c06356bSdh  *
1566*4c06356bSdh  * Returns DDI_SUCCESS or DDI_FAILURE.
1567*4c06356bSdh  *
1568*4c06356bSdh  */
1569*4c06356bSdh static int
1570*4c06356bSdh pmcs_quiesce(dev_info_t *dip)
1571*4c06356bSdh {
1572*4c06356bSdh 	pmcs_hw_t	*pwp;
1573*4c06356bSdh 	scsi_hba_tran_t	*tran;
1574*4c06356bSdh 
1575*4c06356bSdh 	if ((tran = ddi_get_driver_private(dip)) == NULL)
1576*4c06356bSdh 		return (DDI_SUCCESS);
1577*4c06356bSdh 
1578*4c06356bSdh 	/* No quiesce necessary on a per-iport basis */
1579*4c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
1580*4c06356bSdh 		return (DDI_SUCCESS);
1581*4c06356bSdh 	}
1582*4c06356bSdh 
1583*4c06356bSdh 	if ((pwp = TRAN2PMC(tran)) == NULL)
1584*4c06356bSdh 		return (DDI_SUCCESS);
1585*4c06356bSdh 
1586*4c06356bSdh 	/* Stop MPI & Reset chip (no need to re-initialize) */
1587*4c06356bSdh 	(void) pmcs_stop_mpi(pwp);
1588*4c06356bSdh 	(void) pmcs_soft_reset(pwp, B_TRUE);
1589*4c06356bSdh 
1590*4c06356bSdh 	return (DDI_SUCCESS);
1591*4c06356bSdh }
1592*4c06356bSdh 
1593*4c06356bSdh /*
1594*4c06356bSdh  * Called with xp->statlock and PHY lock and scratch acquired.
1595*4c06356bSdh  */
1596*4c06356bSdh static int
1597*4c06356bSdh pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
1598*4c06356bSdh {
1599*4c06356bSdh 	ata_identify_t *ati;
1600*4c06356bSdh 	int result, i;
1601*4c06356bSdh 	pmcs_phy_t *pptr;
1602*4c06356bSdh 	uint16_t *a;
1603*4c06356bSdh 	union {
1604*4c06356bSdh 		uint8_t nsa[8];
1605*4c06356bSdh 		uint16_t nsb[4];
1606*4c06356bSdh 	} u;
1607*4c06356bSdh 
1608*4c06356bSdh 	/*
1609*4c06356bSdh 	 * Safe defaults - use only if this target is brand new (i.e. doesn't
1610*4c06356bSdh 	 * already have these settings configured)
1611*4c06356bSdh 	 */
1612*4c06356bSdh 	if (xp->capacity == 0) {
1613*4c06356bSdh 		xp->capacity = (uint64_t)-1;
1614*4c06356bSdh 		xp->ca = 1;
1615*4c06356bSdh 		xp->qdepth = 1;
1616*4c06356bSdh 		xp->pio = 1;
1617*4c06356bSdh 	}
1618*4c06356bSdh 
1619*4c06356bSdh 	pptr = xp->phy;
1620*4c06356bSdh 
1621*4c06356bSdh 	/*
1622*4c06356bSdh 	 * We only try and issue an IDENTIFY for first level
1623*4c06356bSdh 	 * (direct attached) devices. We don't try and
1624*4c06356bSdh 	 * set other quirks here (this will happen later,
1625*4c06356bSdh 	 * if the device is fully configured)
1626*4c06356bSdh 	 */
1627*4c06356bSdh 	if (pptr->level) {
1628*4c06356bSdh 		return (0);
1629*4c06356bSdh 	}
1630*4c06356bSdh 
1631*4c06356bSdh 	mutex_exit(&xp->statlock);
1632*4c06356bSdh 	result = pmcs_sata_identify(pwp, pptr);
1633*4c06356bSdh 	mutex_enter(&xp->statlock);
1634*4c06356bSdh 
1635*4c06356bSdh 	if (result) {
1636*4c06356bSdh 		return (result);
1637*4c06356bSdh 	}
1638*4c06356bSdh 	ati = pwp->scratch;
1639*4c06356bSdh 	a = &ati->word108;
1640*4c06356bSdh 	for (i = 0; i < 4; i++) {
1641*4c06356bSdh 		u.nsb[i] = ddi_swap16(*a++);
1642*4c06356bSdh 	}
1643*4c06356bSdh 
1644*4c06356bSdh 	/*
1645*4c06356bSdh 	 * Check the returned data for being a valid (NAA=5) WWN.
1646*4c06356bSdh 	 * If so, use that and override the SAS address we were
1647*4c06356bSdh 	 * given at Link Up time.
1648*4c06356bSdh 	 */
1649*4c06356bSdh 	if ((u.nsa[0] >> 4) == 5) {
1650*4c06356bSdh 		(void) memcpy(pptr->sas_address, u.nsa, 8);
1651*4c06356bSdh 	}
1652*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: %s has SAS ADDRESS " SAS_ADDR_FMT,
1653*4c06356bSdh 	    __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address));
1654*4c06356bSdh 	return (0);
1655*4c06356bSdh }
1656*4c06356bSdh 
1657*4c06356bSdh /*
1658*4c06356bSdh  * Called with PHY lock and target statlock held and scratch acquired
1659*4c06356bSdh  */
1660*4c06356bSdh static boolean_t
1661*4c06356bSdh pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target)
1662*4c06356bSdh {
1663*4c06356bSdh 	ASSERT(target != NULL);
1664*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: target = 0x%p",
1665*4c06356bSdh 	    __func__, (void *) target);
1666*4c06356bSdh 
1667*4c06356bSdh 	switch (target->phy->dtype) {
1668*4c06356bSdh 	case SATA:
1669*4c06356bSdh 		if (pmcs_add_sata_device(pwp, target) != 0) {
1670*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
1671*4c06356bSdh 			    "%s: add_sata_device failed for tgt 0x%p",
1672*4c06356bSdh 			    __func__, (void *) target);
1673*4c06356bSdh 			return (B_FALSE);
1674*4c06356bSdh 		}
1675*4c06356bSdh 		break;
1676*4c06356bSdh 	case SAS:
1677*4c06356bSdh 		target->qdepth = maxqdepth;
1678*4c06356bSdh 		break;
1679*4c06356bSdh 	case EXPANDER:
1680*4c06356bSdh 		target->qdepth = 1;
1681*4c06356bSdh 		break;
1682*4c06356bSdh 	}
1683*4c06356bSdh 
1684*4c06356bSdh 	target->new = 0;
1685*4c06356bSdh 	target->assigned = 1;
1686*4c06356bSdh 	target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
1687*4c06356bSdh 	target->dtype = target->phy->dtype;
1688*4c06356bSdh 
1689*4c06356bSdh 	/*
1690*4c06356bSdh 	 * Set the PHY's config stop time to 0.  This is one of the final
1691*4c06356bSdh 	 * stops along the config path, so we're indicating that we
1692*4c06356bSdh 	 * successfully configured the PHY.
1693*4c06356bSdh 	 */
1694*4c06356bSdh 	target->phy->config_stop = 0;
1695*4c06356bSdh 
1696*4c06356bSdh 	return (B_TRUE);
1697*4c06356bSdh }
1698*4c06356bSdh 
1699*4c06356bSdh 
1700*4c06356bSdh static void
1701*4c06356bSdh pmcs_rem_old_devices(pmcs_hw_t *pwp)
1702*4c06356bSdh {
1703*4c06356bSdh 	pmcs_xscsi_t *xp;
1704*4c06356bSdh 	int i;
1705*4c06356bSdh 
1706*4c06356bSdh 	mutex_enter(&pwp->lock);
1707*4c06356bSdh 	for (i = 0; i < pwp->max_dev; i++) {
1708*4c06356bSdh 		xp = pwp->targets[i];
1709*4c06356bSdh 		if (xp == NULL) {
1710*4c06356bSdh 			continue;
1711*4c06356bSdh 		}
1712*4c06356bSdh 		mutex_exit(&pwp->lock);
1713*4c06356bSdh 
1714*4c06356bSdh 		mutex_enter(&xp->statlock);
1715*4c06356bSdh 		if (xp->dying && (xp->dip != NULL)) {
1716*4c06356bSdh 			pmcs_clear_xp(pwp, xp);
1717*4c06356bSdh 			/* Target is now gone */
1718*4c06356bSdh 		}
1719*4c06356bSdh 		mutex_exit(&xp->statlock);
1720*4c06356bSdh 		mutex_enter(&pwp->lock);
1721*4c06356bSdh 	}
1722*4c06356bSdh 	mutex_exit(&pwp->lock);
1723*4c06356bSdh }
1724*4c06356bSdh 
1725*4c06356bSdh 
1726*4c06356bSdh void
1727*4c06356bSdh pmcs_worker(void *arg)
1728*4c06356bSdh {
1729*4c06356bSdh 	pmcs_hw_t *pwp = arg;
1730*4c06356bSdh 	ulong_t work_flags;
1731*4c06356bSdh 
1732*4c06356bSdh 	DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t,
1733*4c06356bSdh 	    pwp->config_changed);
1734*4c06356bSdh 
1735*4c06356bSdh 	if (pwp->state != STATE_RUNNING) {
1736*4c06356bSdh 		return;
1737*4c06356bSdh 	}
1738*4c06356bSdh 
1739*4c06356bSdh 	work_flags = atomic_swap_ulong(&pwp->work_flags, 0);
1740*4c06356bSdh 
1741*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) {
1742*4c06356bSdh 		pmcs_ack_events(pwp);
1743*4c06356bSdh 	}
1744*4c06356bSdh 
1745*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) {
1746*4c06356bSdh 		mutex_enter(&pwp->lock);
1747*4c06356bSdh 		pmcs_spinup_release(pwp, NULL);
1748*4c06356bSdh 		mutex_exit(&pwp->lock);
1749*4c06356bSdh 	}
1750*4c06356bSdh 
1751*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) {
1752*4c06356bSdh 		pmcs_ssp_event_recovery(pwp);
1753*4c06356bSdh 	}
1754*4c06356bSdh 
1755*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) {
1756*4c06356bSdh 		pmcs_dev_state_recovery(pwp, NULL);
1757*4c06356bSdh 	}
1758*4c06356bSdh 
1759*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_REM_DEVICES) {
1760*4c06356bSdh 		pmcs_rem_old_devices(pwp);
1761*4c06356bSdh 	}
1762*4c06356bSdh 
1763*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_DISCOVER) {
1764*4c06356bSdh 		pmcs_discover(pwp);
1765*4c06356bSdh 	}
1766*4c06356bSdh 
1767*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) {
1768*4c06356bSdh 		if (pmcs_abort_handler(pwp)) {
1769*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
1770*4c06356bSdh 		}
1771*4c06356bSdh 	}
1772*4c06356bSdh 
1773*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SATA_RUN) {
1774*4c06356bSdh 		pmcs_sata_work(pwp);
1775*4c06356bSdh 	}
1776*4c06356bSdh 
1777*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) {
1778*4c06356bSdh 		pmcs_scsa_wq_run(pwp);
1779*4c06356bSdh 		mutex_enter(&pwp->lock);
1780*4c06356bSdh 		PMCS_CQ_RUN(pwp);
1781*4c06356bSdh 		mutex_exit(&pwp->lock);
1782*4c06356bSdh 	}
1783*4c06356bSdh 
1784*4c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) {
1785*4c06356bSdh 		if (pmcs_add_more_chunks(pwp,
1786*4c06356bSdh 		    ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) {
1787*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
1788*4c06356bSdh 		} else {
1789*4c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1790*4c06356bSdh 		}
1791*4c06356bSdh 	}
1792*4c06356bSdh }
1793*4c06356bSdh 
1794*4c06356bSdh static int
1795*4c06356bSdh pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize)
1796*4c06356bSdh {
1797*4c06356bSdh 	pmcs_dmachunk_t *dc;
1798*4c06356bSdh 	unsigned long dl;
1799*4c06356bSdh 	pmcs_chunk_t	*pchunk = NULL;
1800*4c06356bSdh 
1801*4c06356bSdh 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
1802*4c06356bSdh 
1803*4c06356bSdh 	pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP);
1804*4c06356bSdh 	if (pchunk == NULL) {
1805*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1806*4c06356bSdh 		    "Not enough memory for DMA chunks");
1807*4c06356bSdh 		return (-1);
1808*4c06356bSdh 	}
1809*4c06356bSdh 
1810*4c06356bSdh 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle,
1811*4c06356bSdh 	    &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp,
1812*4c06356bSdh 	    &pchunk->dma_addr) == B_FALSE) {
1813*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to setup DMA for chunks");
1814*4c06356bSdh 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
1815*4c06356bSdh 		return (-1);
1816*4c06356bSdh 	}
1817*4c06356bSdh 
1818*4c06356bSdh 	if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) ||
1819*4c06356bSdh 	    (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) {
1820*4c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
1821*4c06356bSdh 		return (-1);
1822*4c06356bSdh 	}
1823*4c06356bSdh 
1824*4c06356bSdh 	bzero(pchunk->addrp, nsize);
1825*4c06356bSdh 	dc = NULL;
1826*4c06356bSdh 	for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) {
1827*4c06356bSdh 		pmcs_dmachunk_t *tmp;
1828*4c06356bSdh 		tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP);
1829*4c06356bSdh 		tmp->nxt = dc;
1830*4c06356bSdh 		dc = tmp;
1831*4c06356bSdh 	}
1832*4c06356bSdh 	mutex_enter(&pwp->dma_lock);
1833*4c06356bSdh 	pmcs_idma_chunks(pwp, dc, pchunk, nsize);
1834*4c06356bSdh 	pwp->nchunks++;
1835*4c06356bSdh 	mutex_exit(&pwp->dma_lock);
1836*4c06356bSdh 	return (0);
1837*4c06356bSdh }
1838*4c06356bSdh 
1839*4c06356bSdh 
1840*4c06356bSdh static void
1841*4c06356bSdh pmcs_check_commands(pmcs_hw_t *pwp)
1842*4c06356bSdh {
1843*4c06356bSdh 	pmcs_cmd_t *sp;
1844*4c06356bSdh 	size_t amt;
1845*4c06356bSdh 	char path[32];
1846*4c06356bSdh 	pmcwork_t *pwrk;
1847*4c06356bSdh 	pmcs_xscsi_t *target;
1848*4c06356bSdh 	pmcs_phy_t *phyp;
1849*4c06356bSdh 
1850*4c06356bSdh 	for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) {
1851*4c06356bSdh 		mutex_enter(&pwrk->lock);
1852*4c06356bSdh 
1853*4c06356bSdh 		/*
1854*4c06356bSdh 		 * If the command isn't active, we can't be timing it still.
1855*4c06356bSdh 		 * Active means the tag is not free and the state is "on chip".
1856*4c06356bSdh 		 */
1857*4c06356bSdh 		if (!PMCS_COMMAND_ACTIVE(pwrk)) {
1858*4c06356bSdh 			mutex_exit(&pwrk->lock);
1859*4c06356bSdh 			continue;
1860*4c06356bSdh 		}
1861*4c06356bSdh 
1862*4c06356bSdh 		/*
1863*4c06356bSdh 		 * No timer active for this command.
1864*4c06356bSdh 		 */
1865*4c06356bSdh 		if (pwrk->timer == 0) {
1866*4c06356bSdh 			mutex_exit(&pwrk->lock);
1867*4c06356bSdh 			continue;
1868*4c06356bSdh 		}
1869*4c06356bSdh 
1870*4c06356bSdh 		/*
1871*4c06356bSdh 		 * Knock off bits for the time interval.
1872*4c06356bSdh 		 */
1873*4c06356bSdh 		if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) {
1874*4c06356bSdh 			pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL);
1875*4c06356bSdh 		} else {
1876*4c06356bSdh 			pwrk->timer = 0;
1877*4c06356bSdh 		}
1878*4c06356bSdh 		if (pwrk->timer > 0) {
1879*4c06356bSdh 			mutex_exit(&pwrk->lock);
1880*4c06356bSdh 			continue;
1881*4c06356bSdh 		}
1882*4c06356bSdh 
1883*4c06356bSdh 		/*
1884*4c06356bSdh 		 * The command has now officially timed out.
1885*4c06356bSdh 		 * Get the path for it. If it doesn't have
1886*4c06356bSdh 		 * a phy pointer any more, it's really dead
1887*4c06356bSdh 		 * and can just be put back on the free list.
1888*4c06356bSdh 		 * There should *not* be any commands associated
1889*4c06356bSdh 		 * with it any more.
1890*4c06356bSdh 		 */
1891*4c06356bSdh 		if (pwrk->phy == NULL) {
1892*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
1893*4c06356bSdh 			    "dead command with gone phy being recycled");
1894*4c06356bSdh 			ASSERT(pwrk->xp == NULL);
1895*4c06356bSdh 			pmcs_pwork(pwp, pwrk);
1896*4c06356bSdh 			continue;
1897*4c06356bSdh 		}
1898*4c06356bSdh 		amt = sizeof (path);
1899*4c06356bSdh 		amt = min(sizeof (pwrk->phy->path), amt);
1900*4c06356bSdh 		(void) memcpy(path, pwrk->phy->path, amt);
1901*4c06356bSdh 
1902*4c06356bSdh 		/*
1903*4c06356bSdh 		 * If this is a non-SCSA command, stop here. Eventually
1904*4c06356bSdh 		 * we might do something with non-SCSA commands here-
1905*4c06356bSdh 		 * but so far their timeout mechanisms are handled in
1906*4c06356bSdh 		 * the WAIT_FOR macro.
1907*4c06356bSdh 		 */
1908*4c06356bSdh 		if (pwrk->xp == NULL) {
1909*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
1910*4c06356bSdh 			    "%s: non-SCSA cmd tag 0x%x timed out",
1911*4c06356bSdh 			    path, pwrk->htag);
1912*4c06356bSdh 			mutex_exit(&pwrk->lock);
1913*4c06356bSdh 			continue;
1914*4c06356bSdh 		}
1915*4c06356bSdh 
1916*4c06356bSdh 		sp = pwrk->arg;
1917*4c06356bSdh 		ASSERT(sp != NULL);
1918*4c06356bSdh 
1919*4c06356bSdh 		/*
1920*4c06356bSdh 		 * Mark it as timed out.
1921*4c06356bSdh 		 */
1922*4c06356bSdh 		CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT;
1923*4c06356bSdh 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
1924*4c06356bSdh #ifdef	DEBUG
1925*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1926*4c06356bSdh 		    "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d",
1927*4c06356bSdh 		    path, pwrk->htag, pwrk->state, pwrk->onwire);
1928*4c06356bSdh #else
1929*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
1930*4c06356bSdh 		    "%s: SCSA cmd tag 0x%x timed out (state %x)",
1931*4c06356bSdh 		    path, pwrk->htag, pwrk->state);
1932*4c06356bSdh #endif
1933*4c06356bSdh 		/*
1934*4c06356bSdh 		 * Mark the work structure as timed out.
1935*4c06356bSdh 		 */
1936*4c06356bSdh 		pwrk->state = PMCS_WORK_STATE_TIMED_OUT;
1937*4c06356bSdh 		phyp = pwrk->phy;
1938*4c06356bSdh 		target = pwrk->xp;
1939*4c06356bSdh 		mutex_exit(&pwrk->lock);
1940*4c06356bSdh 
1941*4c06356bSdh 		pmcs_lock_phy(phyp);
1942*4c06356bSdh 		mutex_enter(&target->statlock);
1943*4c06356bSdh 
1944*4c06356bSdh 		/*
1945*4c06356bSdh 		 * No point attempting recovery if the device is gone
1946*4c06356bSdh 		 */
1947*4c06356bSdh 		if (pwrk->xp->dev_gone) {
1948*4c06356bSdh 			mutex_exit(&target->statlock);
1949*4c06356bSdh 			pmcs_unlock_phy(phyp);
1950*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
1951*4c06356bSdh 			    "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE "
1952*4c06356bSdh 			    "for htag 0x%08x", __func__,
1953*4c06356bSdh 			    (void *)pwrk->xp, pwrk->htag);
1954*4c06356bSdh 			mutex_enter(&pwrk->lock);
1955*4c06356bSdh 			if (!PMCS_COMMAND_DONE(pwrk)) {
1956*4c06356bSdh 				/* Complete this command here */
1957*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: "
1958*4c06356bSdh 				    "Completing cmd (htag 0x%08x) "
1959*4c06356bSdh 				    "anyway", __func__, pwrk->htag);
1960*4c06356bSdh 				pwrk->dead = 1;
1961*4c06356bSdh 				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
1962*4c06356bSdh 				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
1963*4c06356bSdh 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
1964*4c06356bSdh 			} else {
1965*4c06356bSdh 				mutex_exit(&pwrk->lock);
1966*4c06356bSdh 			}
1967*4c06356bSdh 			continue;
1968*4c06356bSdh 		}
1969*4c06356bSdh 
1970*4c06356bSdh 		/*
1971*4c06356bSdh 		 * See if we're already waiting for device state recovery
1972*4c06356bSdh 		 */
1973*4c06356bSdh 		if (target->recover_wait) {
1974*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
1975*4c06356bSdh 			    "%s: Target %p already in recovery", __func__,
1976*4c06356bSdh 			    (void *)target);
1977*4c06356bSdh 			mutex_exit(&target->statlock);
1978*4c06356bSdh 			pmcs_unlock_phy(phyp);
1979*4c06356bSdh 			continue;
1980*4c06356bSdh 		}
1981*4c06356bSdh 
1982*4c06356bSdh 		pmcs_start_dev_state_recovery(target, phyp);
1983*4c06356bSdh 		mutex_exit(&target->statlock);
1984*4c06356bSdh 		pmcs_unlock_phy(phyp);
1985*4c06356bSdh 	}
1986*4c06356bSdh 	/*
1987*4c06356bSdh 	 * Run any completions that may have been queued up.
1988*4c06356bSdh 	 */
1989*4c06356bSdh 	PMCS_CQ_RUN(pwp);
1990*4c06356bSdh }
1991*4c06356bSdh 
1992*4c06356bSdh static void
1993*4c06356bSdh pmcs_watchdog(void *arg)
1994*4c06356bSdh {
1995*4c06356bSdh 	pmcs_hw_t *pwp = arg;
1996*4c06356bSdh 
1997*4c06356bSdh 	DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t,
1998*4c06356bSdh 	    pwp->config_changed);
1999*4c06356bSdh 
2000*4c06356bSdh 	mutex_enter(&pwp->lock);
2001*4c06356bSdh 
2002*4c06356bSdh 	if (pwp->state != STATE_RUNNING) {
2003*4c06356bSdh 		mutex_exit(&pwp->lock);
2004*4c06356bSdh 		return;
2005*4c06356bSdh 	}
2006*4c06356bSdh 
2007*4c06356bSdh 	if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) {
2008*4c06356bSdh 		if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp,
2009*4c06356bSdh 		    DDI_NOSLEEP) != DDI_SUCCESS) {
2010*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2011*4c06356bSdh 			    "Could not dispatch to worker thread");
2012*4c06356bSdh 		}
2013*4c06356bSdh 	}
2014*4c06356bSdh 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
2015*4c06356bSdh 	    drv_usectohz(PMCS_WATCH_INTERVAL));
2016*4c06356bSdh 	mutex_exit(&pwp->lock);
2017*4c06356bSdh 	pmcs_check_commands(pwp);
2018*4c06356bSdh 	pmcs_handle_dead_phys(pwp);
2019*4c06356bSdh }
2020*4c06356bSdh 
2021*4c06356bSdh static int
2022*4c06356bSdh pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt)
2023*4c06356bSdh {
2024*4c06356bSdh 	int i, r, rslt = 0;
2025*4c06356bSdh 	for (i = 0; i < icnt; i++) {
2026*4c06356bSdh 		r = ddi_intr_remove_handler(pwp->ih_table[i]);
2027*4c06356bSdh 		if (r == DDI_SUCCESS) {
2028*4c06356bSdh 			continue;
2029*4c06356bSdh 		}
2030*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
2031*4c06356bSdh 		    "%s: unable to remove interrupt handler %d", __func__, i);
2032*4c06356bSdh 		rslt = -1;
2033*4c06356bSdh 		break;
2034*4c06356bSdh 	}
2035*4c06356bSdh 	return (rslt);
2036*4c06356bSdh }
2037*4c06356bSdh 
2038*4c06356bSdh static int
2039*4c06356bSdh pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt)
2040*4c06356bSdh {
2041*4c06356bSdh 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
2042*4c06356bSdh 		int r = ddi_intr_block_disable(&pwp->ih_table[0],
2043*4c06356bSdh 		    pwp->intr_cnt);
2044*4c06356bSdh 		if (r != DDI_SUCCESS) {
2045*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2046*4c06356bSdh 			    "unable to disable interrupt block");
2047*4c06356bSdh 			return (-1);
2048*4c06356bSdh 		}
2049*4c06356bSdh 	} else {
2050*4c06356bSdh 		int i;
2051*4c06356bSdh 		for (i = 0; i < icnt; i++) {
2052*4c06356bSdh 			if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) {
2053*4c06356bSdh 				continue;
2054*4c06356bSdh 			}
2055*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2056*4c06356bSdh 			    "unable to disable interrupt %d", i);
2057*4c06356bSdh 			return (-1);
2058*4c06356bSdh 		}
2059*4c06356bSdh 	}
2060*4c06356bSdh 	return (0);
2061*4c06356bSdh }
2062*4c06356bSdh 
2063*4c06356bSdh static int
2064*4c06356bSdh pmcs_free_intrs(pmcs_hw_t *pwp, int icnt)
2065*4c06356bSdh {
2066*4c06356bSdh 	int i;
2067*4c06356bSdh 	for (i = 0; i < icnt; i++) {
2068*4c06356bSdh 		if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) {
2069*4c06356bSdh 			continue;
2070*4c06356bSdh 		}
2071*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to free interrupt %d", i);
2072*4c06356bSdh 		return (-1);
2073*4c06356bSdh 	}
2074*4c06356bSdh 	kmem_free(pwp->ih_table, pwp->ih_table_size);
2075*4c06356bSdh 	pwp->ih_table_size = 0;
2076*4c06356bSdh 	return (0);
2077*4c06356bSdh }
2078*4c06356bSdh 
2079*4c06356bSdh /*
2080*4c06356bSdh  * Try to set up interrupts of type "type" with a minimum number of interrupts
2081*4c06356bSdh  * of "min".
2082*4c06356bSdh  */
2083*4c06356bSdh static void
2084*4c06356bSdh pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min)
2085*4c06356bSdh {
2086*4c06356bSdh 	int rval, avail, count, actual, max;
2087*4c06356bSdh 
2088*4c06356bSdh 	rval = ddi_intr_get_nintrs(pwp->dip, type, &count);
2089*4c06356bSdh 	if ((rval != DDI_SUCCESS) || (count < min)) {
2090*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2091*4c06356bSdh 		    "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d",
2092*4c06356bSdh 		    __func__, type, rval, count, min);
2093*4c06356bSdh 		return;
2094*4c06356bSdh 	}
2095*4c06356bSdh 
2096*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2097*4c06356bSdh 	    "%s: nintrs = %d for type: %d", __func__, count, type);
2098*4c06356bSdh 
2099*4c06356bSdh 	rval = ddi_intr_get_navail(pwp->dip, type, &avail);
2100*4c06356bSdh 	if ((rval != DDI_SUCCESS) || (avail < min)) {
2101*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2102*4c06356bSdh 		    "%s: get_navail failed; type: %d rc: %d avail: %d min: %d",
2103*4c06356bSdh 		    __func__, type, rval, avail, min);
2104*4c06356bSdh 		return;
2105*4c06356bSdh 	}
2106*4c06356bSdh 
2107*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2108*4c06356bSdh 	    "%s: navail = %d for type: %d", __func__, avail, type);
2109*4c06356bSdh 
2110*4c06356bSdh 	pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t);
2111*4c06356bSdh 	pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP);
2112*4c06356bSdh 
2113*4c06356bSdh 	switch (type) {
2114*4c06356bSdh 	case DDI_INTR_TYPE_MSIX:
2115*4c06356bSdh 		pwp->int_type = PMCS_INT_MSIX;
2116*4c06356bSdh 		max = PMCS_MAX_MSIX;
2117*4c06356bSdh 		break;
2118*4c06356bSdh 	case DDI_INTR_TYPE_MSI:
2119*4c06356bSdh 		pwp->int_type = PMCS_INT_MSI;
2120*4c06356bSdh 		max = PMCS_MAX_MSI;
2121*4c06356bSdh 		break;
2122*4c06356bSdh 	case DDI_INTR_TYPE_FIXED:
2123*4c06356bSdh 	default:
2124*4c06356bSdh 		pwp->int_type = PMCS_INT_FIXED;
2125*4c06356bSdh 		max = PMCS_MAX_FIXED;
2126*4c06356bSdh 		break;
2127*4c06356bSdh 	}
2128*4c06356bSdh 
2129*4c06356bSdh 	rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual,
2130*4c06356bSdh 	    DDI_INTR_ALLOC_NORMAL);
2131*4c06356bSdh 	if (rval != DDI_SUCCESS) {
2132*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2133*4c06356bSdh 		    "%s: ddi_intr_alloc failed; type: %d rc: %d",
2134*4c06356bSdh 		    __func__, type, rval);
2135*4c06356bSdh 		kmem_free(pwp->ih_table, pwp->ih_table_size);
2136*4c06356bSdh 		pwp->ih_table = NULL;
2137*4c06356bSdh 		pwp->ih_table_size = 0;
2138*4c06356bSdh 		pwp->intr_cnt = 0;
2139*4c06356bSdh 		pwp->int_type = PMCS_INT_NONE;
2140*4c06356bSdh 		return;
2141*4c06356bSdh 	}
2142*4c06356bSdh 
2143*4c06356bSdh 	pwp->intr_cnt = actual;
2144*4c06356bSdh }
2145*4c06356bSdh 
2146*4c06356bSdh /*
2147*4c06356bSdh  * Set up interrupts.
2148*4c06356bSdh  * We return one of three values:
2149*4c06356bSdh  *
2150*4c06356bSdh  * 0 - success
2151*4c06356bSdh  * EAGAIN - failure to set up interrupts
2152*4c06356bSdh  * EIO - "" + we're now stuck partly enabled
2153*4c06356bSdh  *
2154*4c06356bSdh  * If EIO is returned, we can't unload the driver.
2155*4c06356bSdh  */
2156*4c06356bSdh static int
2157*4c06356bSdh pmcs_setup_intr(pmcs_hw_t *pwp)
2158*4c06356bSdh {
2159*4c06356bSdh 	int i, r, itypes, oqv_count;
2160*4c06356bSdh 	ddi_intr_handler_t **iv_table;
2161*4c06356bSdh 	size_t iv_table_size;
2162*4c06356bSdh 	uint_t pri;
2163*4c06356bSdh 
2164*4c06356bSdh 	if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) {
2165*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "cannot get interrupt types");
2166*4c06356bSdh 		return (EAGAIN);
2167*4c06356bSdh 	}
2168*4c06356bSdh 
2169*4c06356bSdh 	if (disable_msix) {
2170*4c06356bSdh 		itypes &= ~DDI_INTR_TYPE_MSIX;
2171*4c06356bSdh 	}
2172*4c06356bSdh 	if (disable_msi) {
2173*4c06356bSdh 		itypes &= ~DDI_INTR_TYPE_MSI;
2174*4c06356bSdh 	}
2175*4c06356bSdh 
2176*4c06356bSdh 	/*
2177*4c06356bSdh 	 * We won't know what firmware we're running until we call pmcs_setup,
2178*4c06356bSdh 	 * and we can't call pmcs_setup until we establish interrupts.
2179*4c06356bSdh 	 */
2180*4c06356bSdh 
2181*4c06356bSdh 	pwp->int_type = PMCS_INT_NONE;
2182*4c06356bSdh 
2183*4c06356bSdh 	/*
2184*4c06356bSdh 	 * We want PMCS_MAX_MSIX vectors for MSI-X.  Anything less would be
2185*4c06356bSdh 	 * uncivilized.
2186*4c06356bSdh 	 */
2187*4c06356bSdh 	if (itypes & DDI_INTR_TYPE_MSIX) {
2188*4c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX);
2189*4c06356bSdh 		if (pwp->int_type == PMCS_INT_MSIX) {
2190*4c06356bSdh 			itypes = 0;
2191*4c06356bSdh 		}
2192*4c06356bSdh 	}
2193*4c06356bSdh 
2194*4c06356bSdh 	if (itypes & DDI_INTR_TYPE_MSI) {
2195*4c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1);
2196*4c06356bSdh 		if (pwp->int_type == PMCS_INT_MSI) {
2197*4c06356bSdh 			itypes = 0;
2198*4c06356bSdh 		}
2199*4c06356bSdh 	}
2200*4c06356bSdh 
2201*4c06356bSdh 	if (itypes & DDI_INTR_TYPE_FIXED) {
2202*4c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1);
2203*4c06356bSdh 		if (pwp->int_type == PMCS_INT_FIXED) {
2204*4c06356bSdh 			itypes = 0;
2205*4c06356bSdh 		}
2206*4c06356bSdh 	}
2207*4c06356bSdh 
2208*4c06356bSdh 	if (pwp->intr_cnt == 0) {
2209*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, "No interrupts available");
2210*4c06356bSdh 		return (EAGAIN);
2211*4c06356bSdh 	}
2212*4c06356bSdh 
2213*4c06356bSdh 	iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt;
2214*4c06356bSdh 	iv_table = kmem_alloc(iv_table_size, KM_SLEEP);
2215*4c06356bSdh 
2216*4c06356bSdh 	/*
2217*4c06356bSdh 	 * Get iblock cookie and add handlers.
2218*4c06356bSdh 	 */
2219*4c06356bSdh 	switch (pwp->intr_cnt) {
2220*4c06356bSdh 	case 1:
2221*4c06356bSdh 		iv_table[0] = pmcs_all_intr;
2222*4c06356bSdh 		break;
2223*4c06356bSdh 	case 2:
2224*4c06356bSdh 		iv_table[0] = pmcs_iodone_ix;
2225*4c06356bSdh 		iv_table[1] = pmcs_nonio_ix;
2226*4c06356bSdh 		break;
2227*4c06356bSdh 	case 4:
2228*4c06356bSdh 		iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix;
2229*4c06356bSdh 		iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix;
2230*4c06356bSdh 		iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix;
2231*4c06356bSdh 		iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix;
2232*4c06356bSdh 		break;
2233*4c06356bSdh 	default:
2234*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
2235*4c06356bSdh 		    "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt);
2236*4c06356bSdh 		kmem_free(iv_table, iv_table_size);
2237*4c06356bSdh 		return (EAGAIN);
2238*4c06356bSdh 	}
2239*4c06356bSdh 
2240*4c06356bSdh 	for (i = 0; i < pwp->intr_cnt; i++) {
2241*4c06356bSdh 		r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i],
2242*4c06356bSdh 		    (caddr_t)pwp, NULL);
2243*4c06356bSdh 		if (r != DDI_SUCCESS) {
2244*4c06356bSdh 			kmem_free(iv_table, iv_table_size);
2245*4c06356bSdh 			if (pmcs_remove_ihandlers(pwp, i)) {
2246*4c06356bSdh 				return (EIO);
2247*4c06356bSdh 			}
2248*4c06356bSdh 			if (pmcs_free_intrs(pwp, i)) {
2249*4c06356bSdh 				return (EIO);
2250*4c06356bSdh 			}
2251*4c06356bSdh 			pwp->intr_cnt = 0;
2252*4c06356bSdh 			return (EAGAIN);
2253*4c06356bSdh 		}
2254*4c06356bSdh 	}
2255*4c06356bSdh 
2256*4c06356bSdh 	kmem_free(iv_table, iv_table_size);
2257*4c06356bSdh 
2258*4c06356bSdh 	if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) {
2259*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to get int capabilities");
2260*4c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
2261*4c06356bSdh 			return (EIO);
2262*4c06356bSdh 		}
2263*4c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
2264*4c06356bSdh 			return (EIO);
2265*4c06356bSdh 		}
2266*4c06356bSdh 		pwp->intr_cnt = 0;
2267*4c06356bSdh 		return (EAGAIN);
2268*4c06356bSdh 	}
2269*4c06356bSdh 
2270*4c06356bSdh 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
2271*4c06356bSdh 		r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt);
2272*4c06356bSdh 		if (r != DDI_SUCCESS) {
2273*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, "intr blk enable failed");
2274*4c06356bSdh 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
2275*4c06356bSdh 				return (EIO);
2276*4c06356bSdh 			}
2277*4c06356bSdh 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
2278*4c06356bSdh 				return (EIO);
2279*4c06356bSdh 			}
2280*4c06356bSdh 			pwp->intr_cnt = 0;
2281*4c06356bSdh 			return (EFAULT);
2282*4c06356bSdh 		}
2283*4c06356bSdh 	} else {
2284*4c06356bSdh 		for (i = 0; i < pwp->intr_cnt; i++) {
2285*4c06356bSdh 			r = ddi_intr_enable(pwp->ih_table[i]);
2286*4c06356bSdh 			if (r == DDI_SUCCESS) {
2287*4c06356bSdh 				continue;
2288*4c06356bSdh 			}
2289*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2290*4c06356bSdh 			    "unable to enable interrupt %d", i);
2291*4c06356bSdh 			if (pmcs_disable_intrs(pwp, i)) {
2292*4c06356bSdh 				return (EIO);
2293*4c06356bSdh 			}
2294*4c06356bSdh 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
2295*4c06356bSdh 				return (EIO);
2296*4c06356bSdh 			}
2297*4c06356bSdh 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
2298*4c06356bSdh 				return (EIO);
2299*4c06356bSdh 			}
2300*4c06356bSdh 			pwp->intr_cnt = 0;
2301*4c06356bSdh 			return (EAGAIN);
2302*4c06356bSdh 		}
2303*4c06356bSdh 	}
2304*4c06356bSdh 
2305*4c06356bSdh 	/*
2306*4c06356bSdh 	 * Set up locks.
2307*4c06356bSdh 	 */
2308*4c06356bSdh 	if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) {
2309*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
2310*4c06356bSdh 		    "unable to get interrupt priority");
2311*4c06356bSdh 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
2312*4c06356bSdh 			return (EIO);
2313*4c06356bSdh 		}
2314*4c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
2315*4c06356bSdh 			return (EIO);
2316*4c06356bSdh 		}
2317*4c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
2318*4c06356bSdh 			return (EIO);
2319*4c06356bSdh 		}
2320*4c06356bSdh 		pwp->intr_cnt = 0;
2321*4c06356bSdh 		return (EAGAIN);
2322*4c06356bSdh 	}
2323*4c06356bSdh 
2324*4c06356bSdh 	pwp->locks_initted = 1;
2325*4c06356bSdh 	pwp->intr_pri = pri;
2326*4c06356bSdh 	mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2327*4c06356bSdh 	mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2328*4c06356bSdh 	mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2329*4c06356bSdh 	mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2330*4c06356bSdh 	mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2331*4c06356bSdh 	mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2332*4c06356bSdh 	mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2333*4c06356bSdh 	mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2334*4c06356bSdh 	mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER,
2335*4c06356bSdh 	    DDI_INTR_PRI(pri));
2336*4c06356bSdh #ifdef	DEBUG
2337*4c06356bSdh 	mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
2338*4c06356bSdh #endif
2339*4c06356bSdh 	cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL);
2340*4c06356bSdh 	cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL);
2341*4c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
2342*4c06356bSdh 		mutex_init(&pwp->iqp_lock[i], NULL,
2343*4c06356bSdh 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
2344*4c06356bSdh 	}
2345*4c06356bSdh 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
2346*4c06356bSdh 		mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL,
2347*4c06356bSdh 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
2348*4c06356bSdh 		cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL,
2349*4c06356bSdh 		    CV_DRIVER, NULL);
2350*4c06356bSdh 	}
2351*4c06356bSdh 
2352*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, "%d %s interrup%s configured",
2353*4c06356bSdh 	    pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" :
2354*4c06356bSdh 	    ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"),
2355*4c06356bSdh 	    pwp->intr_cnt == 1? "t" : "ts");
2356*4c06356bSdh 
2357*4c06356bSdh 
2358*4c06356bSdh 	/*
2359*4c06356bSdh 	 * Enable Interrupts
2360*4c06356bSdh 	 */
2361*4c06356bSdh 	if (pwp->intr_cnt > PMCS_NOQ) {
2362*4c06356bSdh 		oqv_count = pwp->intr_cnt;
2363*4c06356bSdh 	} else {
2364*4c06356bSdh 		oqv_count = PMCS_NOQ;
2365*4c06356bSdh 	}
2366*4c06356bSdh 	for (pri = 0xffffffff, i = 0; i < oqv_count; i++) {
2367*4c06356bSdh 		pri ^= (1 << i);
2368*4c06356bSdh 	}
2369*4c06356bSdh 
2370*4c06356bSdh 	mutex_enter(&pwp->lock);
2371*4c06356bSdh 	pwp->intr_mask = pri;
2372*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
2373*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
2374*4c06356bSdh 	mutex_exit(&pwp->lock);
2375*4c06356bSdh 
2376*4c06356bSdh 	return (0);
2377*4c06356bSdh }
2378*4c06356bSdh 
2379*4c06356bSdh static int
2380*4c06356bSdh pmcs_teardown_intr(pmcs_hw_t *pwp)
2381*4c06356bSdh {
2382*4c06356bSdh 	if (pwp->intr_cnt) {
2383*4c06356bSdh 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
2384*4c06356bSdh 			return (EIO);
2385*4c06356bSdh 		}
2386*4c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
2387*4c06356bSdh 			return (EIO);
2388*4c06356bSdh 		}
2389*4c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
2390*4c06356bSdh 			return (EIO);
2391*4c06356bSdh 		}
2392*4c06356bSdh 		pwp->intr_cnt = 0;
2393*4c06356bSdh 	}
2394*4c06356bSdh 	return (0);
2395*4c06356bSdh }
2396*4c06356bSdh 
2397*4c06356bSdh static uint_t
2398*4c06356bSdh pmcs_general_ix(caddr_t arg1, caddr_t arg2)
2399*4c06356bSdh {
2400*4c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
2401*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2402*4c06356bSdh 	pmcs_general_intr(pwp);
2403*4c06356bSdh 	return (DDI_INTR_CLAIMED);
2404*4c06356bSdh }
2405*4c06356bSdh 
2406*4c06356bSdh static uint_t
2407*4c06356bSdh pmcs_event_ix(caddr_t arg1, caddr_t arg2)
2408*4c06356bSdh {
2409*4c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
2410*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2411*4c06356bSdh 	pmcs_event_intr(pwp);
2412*4c06356bSdh 	return (DDI_INTR_CLAIMED);
2413*4c06356bSdh }
2414*4c06356bSdh 
2415*4c06356bSdh static uint_t
2416*4c06356bSdh pmcs_iodone_ix(caddr_t arg1, caddr_t arg2)
2417*4c06356bSdh {
2418*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2419*4c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
2420*4c06356bSdh 
2421*4c06356bSdh 	/*
2422*4c06356bSdh 	 * It's possible that if we just turned interrupt coalescing off
2423*4c06356bSdh 	 * (and thus, re-enabled auto clear for interrupts on the I/O outbound
2424*4c06356bSdh 	 * queue) that there was an interrupt already pending.  We use
2425*4c06356bSdh 	 * io_intr_coal.int_cleared to ensure that we still drop in here and
2426*4c06356bSdh 	 * clear the appropriate interrupt bit one last time.
2427*4c06356bSdh 	 */
2428*4c06356bSdh 	mutex_enter(&pwp->ict_lock);
2429*4c06356bSdh 	if (pwp->io_intr_coal.timer_on ||
2430*4c06356bSdh 	    (pwp->io_intr_coal.int_cleared == B_FALSE)) {
2431*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
2432*4c06356bSdh 		    (1 << PMCS_OQ_IODONE));
2433*4c06356bSdh 		pwp->io_intr_coal.int_cleared = B_TRUE;
2434*4c06356bSdh 	}
2435*4c06356bSdh 	mutex_exit(&pwp->ict_lock);
2436*4c06356bSdh 
2437*4c06356bSdh 	pmcs_iodone_intr(pwp);
2438*4c06356bSdh 
2439*4c06356bSdh 	return (DDI_INTR_CLAIMED);
2440*4c06356bSdh }
2441*4c06356bSdh 
2442*4c06356bSdh static uint_t
2443*4c06356bSdh pmcs_fatal_ix(caddr_t arg1, caddr_t arg2)
2444*4c06356bSdh {
2445*4c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
2446*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2447*4c06356bSdh 	pmcs_fatal_handler(pwp);
2448*4c06356bSdh 	return (DDI_INTR_CLAIMED);
2449*4c06356bSdh }
2450*4c06356bSdh 
2451*4c06356bSdh static uint_t
2452*4c06356bSdh pmcs_nonio_ix(caddr_t arg1, caddr_t arg2)
2453*4c06356bSdh {
2454*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2455*4c06356bSdh 	pmcs_hw_t *pwp = (void *)arg1;
2456*4c06356bSdh 	uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
2457*4c06356bSdh 
2458*4c06356bSdh 	/*
2459*4c06356bSdh 	 * Check for Fatal Interrupts
2460*4c06356bSdh 	 */
2461*4c06356bSdh 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
2462*4c06356bSdh 		pmcs_fatal_handler(pwp);
2463*4c06356bSdh 		return (DDI_INTR_CLAIMED);
2464*4c06356bSdh 	}
2465*4c06356bSdh 
2466*4c06356bSdh 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
2467*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
2468*4c06356bSdh 		    (1 << PMCS_OQ_GENERAL));
2469*4c06356bSdh 		pmcs_general_intr(pwp);
2470*4c06356bSdh 		pmcs_event_intr(pwp);
2471*4c06356bSdh 	}
2472*4c06356bSdh 
2473*4c06356bSdh 	return (DDI_INTR_CLAIMED);
2474*4c06356bSdh }
2475*4c06356bSdh 
2476*4c06356bSdh static uint_t
2477*4c06356bSdh pmcs_all_intr(caddr_t arg1, caddr_t arg2)
2478*4c06356bSdh {
2479*4c06356bSdh 	_NOTE(ARGUNUSED(arg2));
2480*4c06356bSdh 	pmcs_hw_t *pwp = (void *) arg1;
2481*4c06356bSdh 	uint32_t obdb;
2482*4c06356bSdh 	int handled = 0;
2483*4c06356bSdh 
2484*4c06356bSdh 	obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
2485*4c06356bSdh 
2486*4c06356bSdh 	/*
2487*4c06356bSdh 	 * Check for Fatal Interrupts
2488*4c06356bSdh 	 */
2489*4c06356bSdh 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
2490*4c06356bSdh 		pmcs_fatal_handler(pwp);
2491*4c06356bSdh 		return (DDI_INTR_CLAIMED);
2492*4c06356bSdh 	}
2493*4c06356bSdh 
2494*4c06356bSdh 	/*
2495*4c06356bSdh 	 * Check for Outbound Queue service needed
2496*4c06356bSdh 	 */
2497*4c06356bSdh 	if (obdb & (1 << PMCS_OQ_IODONE)) {
2498*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
2499*4c06356bSdh 		    (1 << PMCS_OQ_IODONE));
2500*4c06356bSdh 		obdb ^= (1 << PMCS_OQ_IODONE);
2501*4c06356bSdh 		handled++;
2502*4c06356bSdh 		pmcs_iodone_intr(pwp);
2503*4c06356bSdh 	}
2504*4c06356bSdh 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
2505*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
2506*4c06356bSdh 		    (1 << PMCS_OQ_GENERAL));
2507*4c06356bSdh 		obdb ^= (1 << PMCS_OQ_GENERAL);
2508*4c06356bSdh 		handled++;
2509*4c06356bSdh 		pmcs_general_intr(pwp);
2510*4c06356bSdh 	}
2511*4c06356bSdh 	if (obdb & (1 << PMCS_OQ_EVENTS)) {
2512*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
2513*4c06356bSdh 		    (1 << PMCS_OQ_EVENTS));
2514*4c06356bSdh 		obdb ^= (1 << PMCS_OQ_EVENTS);
2515*4c06356bSdh 		handled++;
2516*4c06356bSdh 		pmcs_event_intr(pwp);
2517*4c06356bSdh 	}
2518*4c06356bSdh 	if (obdb) {
2519*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
2520*4c06356bSdh 		    "interrupt bits not handled (0x%x)", obdb);
2521*4c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb);
2522*4c06356bSdh 		handled++;
2523*4c06356bSdh 	}
2524*4c06356bSdh 	if (pwp->int_type == PMCS_INT_MSI) {
2525*4c06356bSdh 		handled++;
2526*4c06356bSdh 	}
2527*4c06356bSdh 	return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2528*4c06356bSdh }
2529*4c06356bSdh 
2530*4c06356bSdh void
2531*4c06356bSdh pmcs_fatal_handler(pmcs_hw_t *pwp)
2532*4c06356bSdh {
2533*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_ERR, "Fatal Interrupt caught");
2534*4c06356bSdh 	mutex_enter(&pwp->lock);
2535*4c06356bSdh 	pwp->state = STATE_DEAD;
2536*4c06356bSdh 	pmcs_register_dump_int(pwp);
2537*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
2538*4c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
2539*4c06356bSdh 	mutex_exit(&pwp->lock);
2540*4c06356bSdh 	pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE);
2541*4c06356bSdh 	ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
2542*4c06356bSdh 
2543*4c06356bSdh #ifdef	DEBUG
2544*4c06356bSdh 	cmn_err(CE_PANIC, "PMCS Fatal Firmware Error");
2545*4c06356bSdh #endif
2546*4c06356bSdh }
2547*4c06356bSdh 
2548*4c06356bSdh /*
2549*4c06356bSdh  * Called with PHY lock and target statlock held and scratch acquired.
2550*4c06356bSdh  */
2551*4c06356bSdh boolean_t
2552*4c06356bSdh pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
2553*4c06356bSdh {
2554*4c06356bSdh 	pmcs_phy_t *pptr = tgt->phy;
2555*4c06356bSdh 
2556*4c06356bSdh 	switch (pptr->dtype) {
2557*4c06356bSdh 	case SAS:
2558*4c06356bSdh 	case EXPANDER:
2559*4c06356bSdh 		break;
2560*4c06356bSdh 	case SATA:
2561*4c06356bSdh 		tgt->ca = 1;
2562*4c06356bSdh 		break;
2563*4c06356bSdh 	default:
2564*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2565*4c06356bSdh 		    "%s: Target %p has PHY %p with invalid dtype",
2566*4c06356bSdh 		    __func__, (void *)tgt, (void *)pptr);
2567*4c06356bSdh 		return (B_FALSE);
2568*4c06356bSdh 	}
2569*4c06356bSdh 
2570*4c06356bSdh 	tgt->new = 1;
2571*4c06356bSdh 	tgt->dev_gone = 0;
2572*4c06356bSdh 	tgt->dying = 0;
2573*4c06356bSdh 	tgt->recover_wait = 0;
2574*4c06356bSdh 
2575*4c06356bSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2576*4c06356bSdh 	    "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__,
2577*4c06356bSdh 	    pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
2578*4c06356bSdh 
2579*4c06356bSdh 	if (pmcs_add_new_device(pwp, tgt) != B_TRUE) {
2580*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2581*4c06356bSdh 		    "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__,
2582*4c06356bSdh 		    tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
2583*4c06356bSdh 		mutex_destroy(&tgt->statlock);
2584*4c06356bSdh 		mutex_destroy(&tgt->wqlock);
2585*4c06356bSdh 		mutex_destroy(&tgt->aqlock);
2586*4c06356bSdh 		return (B_FALSE);
2587*4c06356bSdh 	}
2588*4c06356bSdh 
2589*4c06356bSdh 	return (B_TRUE);
2590*4c06356bSdh }
2591*4c06356bSdh 
2592*4c06356bSdh /*
2593*4c06356bSdh  * Called with softstate lock held
2594*4c06356bSdh  */
2595*4c06356bSdh void
2596*4c06356bSdh pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
2597*4c06356bSdh {
2598*4c06356bSdh 	pmcs_xscsi_t *xp;
2599*4c06356bSdh 	unsigned int vtgt;
2600*4c06356bSdh 
2601*4c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
2602*4c06356bSdh 
2603*4c06356bSdh 	for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) {
2604*4c06356bSdh 		xp = pwp->targets[vtgt];
2605*4c06356bSdh 		if (xp == NULL) {
2606*4c06356bSdh 			continue;
2607*4c06356bSdh 		}
2608*4c06356bSdh 
2609*4c06356bSdh 		mutex_enter(&xp->statlock);
2610*4c06356bSdh 		if (xp->phy == pptr) {
2611*4c06356bSdh 			if (xp->new) {
2612*4c06356bSdh 				xp->new = 0;
2613*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2614*4c06356bSdh 				    "cancel config of vtgt %u", vtgt);
2615*4c06356bSdh 			} else {
2616*4c06356bSdh 				xp->assigned = 0;
2617*4c06356bSdh 				xp->dying = 1;
2618*4c06356bSdh 				SCHEDULE_WORK(pwp, PMCS_WORK_REM_DEVICES);
2619*4c06356bSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
2620*4c06356bSdh 				    "Scheduling removal of tgt 0x%p vtgt %u",
2621*4c06356bSdh 				    (void *)xp, vtgt);
2622*4c06356bSdh 			}
2623*4c06356bSdh 			mutex_exit(&xp->statlock);
2624*4c06356bSdh 			break;
2625*4c06356bSdh 		}
2626*4c06356bSdh 		mutex_exit(&xp->statlock);
2627*4c06356bSdh 	}
2628*4c06356bSdh }
2629*4c06356bSdh 
2630*4c06356bSdh void
2631*4c06356bSdh pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level, const char *fmt, ...)
2632*4c06356bSdh {
2633*4c06356bSdh 	va_list	ap;
2634*4c06356bSdh 	int written = 0;
2635*4c06356bSdh 	char *ptr;
2636*4c06356bSdh 	uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1;
2637*4c06356bSdh 	boolean_t system_log;
2638*4c06356bSdh 	int system_log_level;
2639*4c06356bSdh 
2640*4c06356bSdh 	switch (level) {
2641*4c06356bSdh 	case PMCS_PRT_DEBUG_DEVEL:
2642*4c06356bSdh 	case PMCS_PRT_DEBUG_DEV_STATE:
2643*4c06356bSdh 	case PMCS_PRT_DEBUG_PHY_LOCKING:
2644*4c06356bSdh 	case PMCS_PRT_DEBUG_SCSI_STATUS:
2645*4c06356bSdh 	case PMCS_PRT_DEBUG_UNDERFLOW:
2646*4c06356bSdh 	case PMCS_PRT_DEBUG_CONFIG:
2647*4c06356bSdh 	case PMCS_PRT_DEBUG_IPORT:
2648*4c06356bSdh 	case PMCS_PRT_DEBUG_MAP:
2649*4c06356bSdh 	case PMCS_PRT_DEBUG3:
2650*4c06356bSdh 	case PMCS_PRT_DEBUG2:
2651*4c06356bSdh 	case PMCS_PRT_DEBUG1:
2652*4c06356bSdh 	case PMCS_PRT_DEBUG:
2653*4c06356bSdh 		system_log = B_FALSE;
2654*4c06356bSdh 		break;
2655*4c06356bSdh 	case PMCS_PRT_INFO:
2656*4c06356bSdh 		system_log = B_TRUE;
2657*4c06356bSdh 		system_log_level = CE_CONT;
2658*4c06356bSdh 		break;
2659*4c06356bSdh 	case PMCS_PRT_WARN:
2660*4c06356bSdh 		system_log = B_TRUE;
2661*4c06356bSdh 		system_log_level = CE_NOTE;
2662*4c06356bSdh 		break;
2663*4c06356bSdh 	case PMCS_PRT_ERR:
2664*4c06356bSdh 		system_log = B_TRUE;
2665*4c06356bSdh 		system_log_level = CE_WARN;
2666*4c06356bSdh 		break;
2667*4c06356bSdh 	default:
2668*4c06356bSdh 		return;
2669*4c06356bSdh 	}
2670*4c06356bSdh 
2671*4c06356bSdh 	mutex_enter(&pmcs_trace_lock);
2672*4c06356bSdh 	gethrestime(&pmcs_tbuf_ptr->timestamp);
2673*4c06356bSdh 	ptr = pmcs_tbuf_ptr->buf;
2674*4c06356bSdh 	written += snprintf(ptr, elem_size, "pmcs%d:%d: ",
2675*4c06356bSdh 	    ddi_get_instance(pwp->dip), level);
2676*4c06356bSdh 	ptr += strlen(ptr);
2677*4c06356bSdh 	va_start(ap, fmt);
2678*4c06356bSdh 	written += vsnprintf(ptr, elem_size - written, fmt, ap);
2679*4c06356bSdh 	va_end(ap);
2680*4c06356bSdh 	if (written > elem_size - 1) {
2681*4c06356bSdh 		/* Indicate truncation */
2682*4c06356bSdh 		pmcs_tbuf_ptr->buf[elem_size - 1] = '+';
2683*4c06356bSdh 	}
2684*4c06356bSdh 	if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) {
2685*4c06356bSdh 		pmcs_tbuf_ptr = pmcs_tbuf;
2686*4c06356bSdh 		pmcs_tbuf_wrap = B_TRUE;
2687*4c06356bSdh 		pmcs_tbuf_idx = 0;
2688*4c06356bSdh 	} else {
2689*4c06356bSdh 		++pmcs_tbuf_ptr;
2690*4c06356bSdh 	}
2691*4c06356bSdh 	mutex_exit(&pmcs_trace_lock);
2692*4c06356bSdh 
2693*4c06356bSdh 	/*
2694*4c06356bSdh 	 * When pmcs_force_syslog in non-zero, everything goes also
2695*4c06356bSdh 	 * to syslog, at CE_CONT level.
2696*4c06356bSdh 	 */
2697*4c06356bSdh 	if (pmcs_force_syslog) {
2698*4c06356bSdh 		system_log = B_TRUE;
2699*4c06356bSdh 		system_log_level = CE_CONT;
2700*4c06356bSdh 	}
2701*4c06356bSdh 
2702*4c06356bSdh 	/*
2703*4c06356bSdh 	 * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also
2704*4c06356bSdh 	 * goes to syslog.
2705*4c06356bSdh 	 */
2706*4c06356bSdh 	if (system_log) {
2707*4c06356bSdh 		char local[196];
2708*4c06356bSdh 
2709*4c06356bSdh 		switch (system_log_level) {
2710*4c06356bSdh 		case CE_CONT:
2711*4c06356bSdh 			(void) snprintf(local, sizeof (local), "%sINFO: ",
2712*4c06356bSdh 			    pmcs_console ? "" : "?");
2713*4c06356bSdh 			break;
2714*4c06356bSdh 		case CE_NOTE:
2715*4c06356bSdh 		case CE_WARN:
2716*4c06356bSdh 			local[0] = 0;
2717*4c06356bSdh 			break;
2718*4c06356bSdh 		default:
2719*4c06356bSdh 			return;
2720*4c06356bSdh 		}
2721*4c06356bSdh 
2722*4c06356bSdh 		ptr = local;
2723*4c06356bSdh 		ptr += strlen(local);
2724*4c06356bSdh 		(void) snprintf(ptr, (sizeof (local)) -
2725*4c06356bSdh 		    ((size_t)ptr - (size_t)local), "pmcs%d: ",
2726*4c06356bSdh 		    ddi_get_instance(pwp->dip));
2727*4c06356bSdh 		ptr += strlen(ptr);
2728*4c06356bSdh 		va_start(ap, fmt);
2729*4c06356bSdh 		(void) vsnprintf(ptr,
2730*4c06356bSdh 		    (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap);
2731*4c06356bSdh 		va_end(ap);
2732*4c06356bSdh 		if (level == CE_CONT) {
2733*4c06356bSdh 			(void) strlcat(local, "\n", sizeof (local));
2734*4c06356bSdh 		}
2735*4c06356bSdh 		cmn_err(system_log_level, local);
2736*4c06356bSdh 	}
2737*4c06356bSdh 
2738*4c06356bSdh }
2739*4c06356bSdh 
2740*4c06356bSdh /*
2741*4c06356bSdh  * pmcs_acquire_scratch
2742*4c06356bSdh  *
2743*4c06356bSdh  * If "wait" is true, the caller will wait until it can acquire the scratch.
2744*4c06356bSdh  * This implies the caller needs to be in a context where spinning for an
2745*4c06356bSdh  * indeterminate amount of time is acceptable.
2746*4c06356bSdh  */
2747*4c06356bSdh int
2748*4c06356bSdh pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait)
2749*4c06356bSdh {
2750*4c06356bSdh 	int rval;
2751*4c06356bSdh 
2752*4c06356bSdh 	if (!wait) {
2753*4c06356bSdh 		return (atomic_swap_8(&pwp->scratch_locked, 1));
2754*4c06356bSdh 	}
2755*4c06356bSdh 
2756*4c06356bSdh 	/*
2757*4c06356bSdh 	 * Caller will wait for scratch.
2758*4c06356bSdh 	 */
2759*4c06356bSdh 	while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) {
2760*4c06356bSdh 		drv_usecwait(100);
2761*4c06356bSdh 	}
2762*4c06356bSdh 
2763*4c06356bSdh 	return (rval);
2764*4c06356bSdh }
2765*4c06356bSdh 
2766*4c06356bSdh void
2767*4c06356bSdh pmcs_release_scratch(pmcs_hw_t *pwp)
2768*4c06356bSdh {
2769*4c06356bSdh 	pwp->scratch_locked = 0;
2770*4c06356bSdh }
2771*4c06356bSdh 
2772*4c06356bSdh static void
2773*4c06356bSdh pmcs_create_phy_stats(pmcs_iport_t *iport)
2774*4c06356bSdh {
2775*4c06356bSdh 	sas_phy_stats_t		*ps;
2776*4c06356bSdh 	pmcs_hw_t		*pwp;
2777*4c06356bSdh 	pmcs_phy_t		*phyp;
2778*4c06356bSdh 	int			ndata;
2779*4c06356bSdh 	char			ks_name[KSTAT_STRLEN];
2780*4c06356bSdh 
2781*4c06356bSdh 	ASSERT(iport != NULL);
2782*4c06356bSdh 	pwp = iport->pwp;
2783*4c06356bSdh 	ASSERT(pwp != NULL);
2784*4c06356bSdh 
2785*4c06356bSdh 	mutex_enter(&iport->lock);
2786*4c06356bSdh 
2787*4c06356bSdh 	for (phyp = list_head(&iport->phys);
2788*4c06356bSdh 	    phyp != NULL;
2789*4c06356bSdh 	    phyp = list_next(&iport->phys, phyp)) {
2790*4c06356bSdh 
2791*4c06356bSdh 		pmcs_lock_phy(phyp);
2792*4c06356bSdh 
2793*4c06356bSdh 		if (phyp->phy_stats != NULL) {
2794*4c06356bSdh 			pmcs_unlock_phy(phyp);
2795*4c06356bSdh 			/* We've already created this kstat instance */
2796*4c06356bSdh 			continue;
2797*4c06356bSdh 		}
2798*4c06356bSdh 
2799*4c06356bSdh 		ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t));
2800*4c06356bSdh 
2801*4c06356bSdh 		(void) snprintf(ks_name, sizeof (ks_name),
2802*4c06356bSdh 		    "%s.%llx.%d.%d", ddi_driver_name(iport->dip),
2803*4c06356bSdh 		    (longlong_t)pwp->sas_wwns[0],
2804*4c06356bSdh 		    ddi_get_instance(iport->dip), phyp->phynum);
2805*4c06356bSdh 
2806*4c06356bSdh 		phyp->phy_stats = kstat_create("pmcs",
2807*4c06356bSdh 		    ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS,
2808*4c06356bSdh 		    KSTAT_TYPE_NAMED, ndata, 0);
2809*4c06356bSdh 
2810*4c06356bSdh 		if (phyp->phy_stats == NULL) {
2811*4c06356bSdh 			pmcs_unlock_phy(phyp);
2812*4c06356bSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
2813*4c06356bSdh 			    "%s: Failed to create %s kstats", __func__,
2814*4c06356bSdh 			    ks_name);
2815*4c06356bSdh 			continue;
2816*4c06356bSdh 		}
2817*4c06356bSdh 
2818*4c06356bSdh 		ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
2819*4c06356bSdh 
2820*4c06356bSdh 		kstat_named_init(&ps->seconds_since_last_reset,
2821*4c06356bSdh 		    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
2822*4c06356bSdh 		kstat_named_init(&ps->tx_frames,
2823*4c06356bSdh 		    "TxFrames", KSTAT_DATA_ULONGLONG);
2824*4c06356bSdh 		kstat_named_init(&ps->rx_frames,
2825*4c06356bSdh 		    "RxFrames", KSTAT_DATA_ULONGLONG);
2826*4c06356bSdh 		kstat_named_init(&ps->tx_words,
2827*4c06356bSdh 		    "TxWords", KSTAT_DATA_ULONGLONG);
2828*4c06356bSdh 		kstat_named_init(&ps->rx_words,
2829*4c06356bSdh 		    "RxWords", KSTAT_DATA_ULONGLONG);
2830*4c06356bSdh 		kstat_named_init(&ps->invalid_dword_count,
2831*4c06356bSdh 		    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
2832*4c06356bSdh 		kstat_named_init(&ps->running_disparity_error_count,
2833*4c06356bSdh 		    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
2834*4c06356bSdh 		kstat_named_init(&ps->loss_of_dword_sync_count,
2835*4c06356bSdh 		    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
2836*4c06356bSdh 		kstat_named_init(&ps->phy_reset_problem_count,
2837*4c06356bSdh 		    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
2838*4c06356bSdh 
2839*4c06356bSdh 		phyp->phy_stats->ks_private = phyp;
2840*4c06356bSdh 		phyp->phy_stats->ks_update = pmcs_update_phy_stats;
2841*4c06356bSdh 		kstat_install(phyp->phy_stats);
2842*4c06356bSdh 		pmcs_unlock_phy(phyp);
2843*4c06356bSdh 	}
2844*4c06356bSdh 
2845*4c06356bSdh 	mutex_exit(&iport->lock);
2846*4c06356bSdh }
2847*4c06356bSdh 
2848*4c06356bSdh int
2849*4c06356bSdh pmcs_update_phy_stats(kstat_t *ks, int rw)
2850*4c06356bSdh {
2851*4c06356bSdh 	int		val, ret = DDI_FAILURE;
2852*4c06356bSdh 	pmcs_phy_t	*pptr = (pmcs_phy_t *)ks->ks_private;
2853*4c06356bSdh 	pmcs_hw_t	*pwp = pptr->pwp;
2854*4c06356bSdh 	sas_phy_stats_t	*ps = ks->ks_data;
2855*4c06356bSdh 
2856*4c06356bSdh 	_NOTE(ARGUNUSED(rw));
2857*4c06356bSdh 	ASSERT((pptr != NULL) && (pwp != NULL));
2858*4c06356bSdh 
2859*4c06356bSdh 	/*
2860*4c06356bSdh 	 * We just want to lock against other invocations of kstat;
2861*4c06356bSdh 	 * we don't need to pmcs_lock_phy() for this.
2862*4c06356bSdh 	 */
2863*4c06356bSdh 	mutex_enter(&pptr->phy_lock);
2864*4c06356bSdh 
2865*4c06356bSdh 	/* Get Stats from Chip */
2866*4c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum);
2867*4c06356bSdh 	if (val == DDI_FAILURE)
2868*4c06356bSdh 		goto fail;
2869*4c06356bSdh 	ps->invalid_dword_count.value.ull = (unsigned long long)val;
2870*4c06356bSdh 
2871*4c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum);
2872*4c06356bSdh 	if (val == DDI_FAILURE)
2873*4c06356bSdh 		goto fail;
2874*4c06356bSdh 	ps->running_disparity_error_count.value.ull = (unsigned long long)val;
2875*4c06356bSdh 
2876*4c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum);
2877*4c06356bSdh 	if (val == DDI_FAILURE)
2878*4c06356bSdh 		goto fail;
2879*4c06356bSdh 	ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val;
2880*4c06356bSdh 
2881*4c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum);
2882*4c06356bSdh 	if (val == DDI_FAILURE)
2883*4c06356bSdh 		goto fail;
2884*4c06356bSdh 	ps->phy_reset_problem_count.value.ull = (unsigned long long)val;
2885*4c06356bSdh 
2886*4c06356bSdh 	ret = DDI_SUCCESS;
2887*4c06356bSdh fail:
2888*4c06356bSdh 	mutex_exit(&pptr->phy_lock);
2889*4c06356bSdh 	return (ret);
2890*4c06356bSdh }
2891*4c06356bSdh 
2892*4c06356bSdh static void
2893*4c06356bSdh pmcs_destroy_phy_stats(pmcs_iport_t *iport)
2894*4c06356bSdh {
2895*4c06356bSdh 	pmcs_phy_t		*phyp;
2896*4c06356bSdh 
2897*4c06356bSdh 	ASSERT(iport != NULL);
2898*4c06356bSdh 	mutex_enter(&iport->lock);
2899*4c06356bSdh 	phyp = iport->pptr;
2900*4c06356bSdh 	if (phyp == NULL) {
2901*4c06356bSdh 		mutex_exit(&iport->lock);
2902*4c06356bSdh 		return;
2903*4c06356bSdh 	}
2904*4c06356bSdh 
2905*4c06356bSdh 	pmcs_lock_phy(phyp);
2906*4c06356bSdh 	if (phyp->phy_stats != NULL) {
2907*4c06356bSdh 		kstat_delete(phyp->phy_stats);
2908*4c06356bSdh 		phyp->phy_stats = NULL;
2909*4c06356bSdh 	}
2910*4c06356bSdh 	pmcs_unlock_phy(phyp);
2911*4c06356bSdh 
2912*4c06356bSdh 	mutex_exit(&iport->lock);
2913*4c06356bSdh }
2914*4c06356bSdh 
2915*4c06356bSdh /*ARGSUSED*/
2916*4c06356bSdh static int
2917*4c06356bSdh pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
2918*4c06356bSdh {
2919*4c06356bSdh 	/*
2920*4c06356bSdh 	 * as the driver can always deal with an error in any dma or
2921*4c06356bSdh 	 * access handle, we can just return the fme_status value.
2922*4c06356bSdh 	 */
2923*4c06356bSdh 	pci_ereport_post(dip, err, NULL);
2924*4c06356bSdh 	return (err->fme_status);
2925*4c06356bSdh }
2926*4c06356bSdh 
2927*4c06356bSdh static void
2928*4c06356bSdh pmcs_fm_init(pmcs_hw_t *pwp)
2929*4c06356bSdh {
2930*4c06356bSdh 	ddi_iblock_cookie_t	fm_ibc;
2931*4c06356bSdh 
2932*4c06356bSdh 	/* Only register with IO Fault Services if we have some capability */
2933*4c06356bSdh 	if (pwp->fm_capabilities) {
2934*4c06356bSdh 		/* Adjust access and dma attributes for FMA */
2935*4c06356bSdh 		pwp->reg_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC;
2936*4c06356bSdh 		pwp->dev_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC;
2937*4c06356bSdh 		pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2938*4c06356bSdh 		pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2939*4c06356bSdh 		pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2940*4c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2941*4c06356bSdh 
2942*4c06356bSdh 		/*
2943*4c06356bSdh 		 * Register capabilities with IO Fault Services.
2944*4c06356bSdh 		 */
2945*4c06356bSdh 		ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc);
2946*4c06356bSdh 
2947*4c06356bSdh 		/*
2948*4c06356bSdh 		 * Initialize pci ereport capabilities if ereport
2949*4c06356bSdh 		 * capable (should always be.)
2950*4c06356bSdh 		 */
2951*4c06356bSdh 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
2952*4c06356bSdh 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
2953*4c06356bSdh 			pci_ereport_setup(pwp->dip);
2954*4c06356bSdh 		}
2955*4c06356bSdh 
2956*4c06356bSdh 		/*
2957*4c06356bSdh 		 * Register error callback if error callback capable.
2958*4c06356bSdh 		 */
2959*4c06356bSdh 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
2960*4c06356bSdh 			ddi_fm_handler_register(pwp->dip,
2961*4c06356bSdh 			    pmcs_fm_error_cb, (void *) pwp);
2962*4c06356bSdh 		}
2963*4c06356bSdh 	}
2964*4c06356bSdh }
2965*4c06356bSdh 
2966*4c06356bSdh static void
2967*4c06356bSdh pmcs_fm_fini(pmcs_hw_t *pwp)
2968*4c06356bSdh {
2969*4c06356bSdh 	/* Only unregister FMA capabilities if registered */
2970*4c06356bSdh 	if (pwp->fm_capabilities) {
2971*4c06356bSdh 		/*
2972*4c06356bSdh 		 * Un-register error callback if error callback capable.
2973*4c06356bSdh 		 */
2974*4c06356bSdh 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
2975*4c06356bSdh 			ddi_fm_handler_unregister(pwp->dip);
2976*4c06356bSdh 		}
2977*4c06356bSdh 
2978*4c06356bSdh 		/*
2979*4c06356bSdh 		 * Release any resources allocated by pci_ereport_setup()
2980*4c06356bSdh 		 */
2981*4c06356bSdh 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
2982*4c06356bSdh 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
2983*4c06356bSdh 			pci_ereport_teardown(pwp->dip);
2984*4c06356bSdh 		}
2985*4c06356bSdh 
2986*4c06356bSdh 		/* Unregister from IO Fault Services */
2987*4c06356bSdh 		ddi_fm_fini(pwp->dip);
2988*4c06356bSdh 
2989*4c06356bSdh 		/* Adjust access and dma attributes for FMA */
2990*4c06356bSdh 		pwp->reg_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC;
2991*4c06356bSdh 		pwp->dev_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC;
2992*4c06356bSdh 		pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
2993*4c06356bSdh 		pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
2994*4c06356bSdh 		pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
2995*4c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
2996*4c06356bSdh 	}
2997*4c06356bSdh }
2998*4c06356bSdh 
2999*4c06356bSdh static boolean_t
3000*4c06356bSdh pmcs_fabricate_wwid(pmcs_hw_t *pwp)
3001*4c06356bSdh {
3002*4c06356bSdh 	char *cp, c;
3003*4c06356bSdh 	uint64_t adr;
3004*4c06356bSdh 	int i;
3005*4c06356bSdh 
3006*4c06356bSdh 	cp = &c;
3007*4c06356bSdh 	(void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr);
3008*4c06356bSdh 	if (adr == 0) {
3009*4c06356bSdh 		static const char foo[] = __DATE__ __TIME__;
3010*4c06356bSdh 		/* Oh, dear, we're toast */
3011*4c06356bSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
3012*4c06356bSdh 		    "%s: No serial number available to fabricate WWN",
3013*4c06356bSdh 		    __func__);
3014*4c06356bSdh 		for (i = 0; foo[i]; i++) {
3015*4c06356bSdh 			adr += foo[i];
3016*4c06356bSdh 		}
3017*4c06356bSdh 	}
3018*4c06356bSdh 	adr <<= 8;
3019*4c06356bSdh 	adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52);
3020*4c06356bSdh 	adr |= (5ULL << 60);
3021*4c06356bSdh 	for (i = 0; i < PMCS_MAX_PORTS; i++) {
3022*4c06356bSdh 		pwp->sas_wwns[i] = adr + i;
3023*4c06356bSdh 	}
3024*4c06356bSdh 
3025*4c06356bSdh 	return (B_TRUE);
3026*4c06356bSdh }
3027