xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_attach.c (revision 12462:6a2cdc3dccf5)
110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM  * CDDL HEADER START
310696SDavid.Hollister@Sun.COM  *
410696SDavid.Hollister@Sun.COM  * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM  * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM  * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM  *
810696SDavid.Hollister@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM  * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM  * and limitations under the License.
1210696SDavid.Hollister@Sun.COM  *
1310696SDavid.Hollister@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM  *
1910696SDavid.Hollister@Sun.COM  * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM  */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2310696SDavid.Hollister@Sun.COM  */
2410696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
2510699SDavid.Hollister@Sun.COM 
2610699SDavid.Hollister@Sun.COM #define	PMCS_DRIVER_VERSION	"pmcs HBA device driver"
2710696SDavid.Hollister@Sun.COM 
2810696SDavid.Hollister@Sun.COM static	char	*pmcs_driver_rev = PMCS_DRIVER_VERSION;
2910696SDavid.Hollister@Sun.COM 
3010696SDavid.Hollister@Sun.COM /*
3110696SDavid.Hollister@Sun.COM  * Non-DDI Compliant stuff
3210696SDavid.Hollister@Sun.COM  */
3310696SDavid.Hollister@Sun.COM extern char hw_serial[];
3410696SDavid.Hollister@Sun.COM 
3510696SDavid.Hollister@Sun.COM /*
3610696SDavid.Hollister@Sun.COM  * Global driver data
3710696SDavid.Hollister@Sun.COM  */
3810696SDavid.Hollister@Sun.COM void *pmcs_softc_state = NULL;
3910696SDavid.Hollister@Sun.COM void *pmcs_iport_softstate = NULL;
4010696SDavid.Hollister@Sun.COM 
4110696SDavid.Hollister@Sun.COM /*
4210696SDavid.Hollister@Sun.COM  * Tracing and Logging info
4310696SDavid.Hollister@Sun.COM  */
4410696SDavid.Hollister@Sun.COM pmcs_tbuf_t *pmcs_tbuf = NULL;
4510696SDavid.Hollister@Sun.COM uint32_t pmcs_tbuf_num_elems = 0;
4610696SDavid.Hollister@Sun.COM pmcs_tbuf_t *pmcs_tbuf_ptr;
4710696SDavid.Hollister@Sun.COM uint32_t pmcs_tbuf_idx = 0;
4810696SDavid.Hollister@Sun.COM boolean_t pmcs_tbuf_wrap = B_FALSE;
4912120SDavid.Hollister@Sun.COM kmutex_t pmcs_trace_lock;
5010696SDavid.Hollister@Sun.COM 
5110696SDavid.Hollister@Sun.COM /*
5210696SDavid.Hollister@Sun.COM  * If pmcs_force_syslog value is non-zero, all messages put in the trace log
5310696SDavid.Hollister@Sun.COM  * will also be sent to system log.
5410696SDavid.Hollister@Sun.COM  */
5510696SDavid.Hollister@Sun.COM int pmcs_force_syslog = 0;
5610696SDavid.Hollister@Sun.COM int pmcs_console = 0;
5710696SDavid.Hollister@Sun.COM 
5810696SDavid.Hollister@Sun.COM /*
5910696SDavid.Hollister@Sun.COM  * External References
6010696SDavid.Hollister@Sun.COM  */
6110696SDavid.Hollister@Sun.COM extern int ncpus_online;
6210696SDavid.Hollister@Sun.COM 
6310696SDavid.Hollister@Sun.COM /*
6410696SDavid.Hollister@Sun.COM  * Local static data
6510696SDavid.Hollister@Sun.COM  */
6610696SDavid.Hollister@Sun.COM static int fwlog_level = 3;
6710696SDavid.Hollister@Sun.COM static int physpeed = PHY_LINK_ALL;
6810696SDavid.Hollister@Sun.COM static int phymode = PHY_LM_AUTO;
6910696SDavid.Hollister@Sun.COM static int block_mask = 0;
7012207SChris.Horne@Sun.COM static int phymap_stable_usec = 3 * MICROSEC;
7112207SChris.Horne@Sun.COM static int iportmap_stable_usec = 2 * MICROSEC;
7212207SChris.Horne@Sun.COM static int iportmap_csync_usec = 20 * MICROSEC;
7310696SDavid.Hollister@Sun.COM 
7410696SDavid.Hollister@Sun.COM #ifdef DEBUG
7510696SDavid.Hollister@Sun.COM static int debug_mask = 1;
7610696SDavid.Hollister@Sun.COM #else
7710696SDavid.Hollister@Sun.COM static int debug_mask = 0;
7810696SDavid.Hollister@Sun.COM #endif
7910696SDavid.Hollister@Sun.COM 
8010696SDavid.Hollister@Sun.COM #ifdef DISABLE_MSIX
8110696SDavid.Hollister@Sun.COM static int disable_msix = 1;
8210696SDavid.Hollister@Sun.COM #else
8310696SDavid.Hollister@Sun.COM static int disable_msix = 0;
8410696SDavid.Hollister@Sun.COM #endif
8510696SDavid.Hollister@Sun.COM 
8610696SDavid.Hollister@Sun.COM #ifdef DISABLE_MSI
8710696SDavid.Hollister@Sun.COM static int disable_msi = 1;
8810696SDavid.Hollister@Sun.COM #else
8910696SDavid.Hollister@Sun.COM static int disable_msi = 0;
9010696SDavid.Hollister@Sun.COM #endif
9110696SDavid.Hollister@Sun.COM 
9212399SChris.Horne@Sun.COM /*
9312399SChris.Horne@Sun.COM  * DEBUG: testing: allow detach with an active port:
9412399SChris.Horne@Sun.COM  *
9512399SChris.Horne@Sun.COM  * # echo 'detach_driver_unconfig/W 10'		| mdb -kw
9612399SChris.Horne@Sun.COM  * # echo 'scsi_hba_bus_unconfig_remove/W 1'	| mdb -kw
9712399SChris.Horne@Sun.COM  * # echo 'pmcs`detach_with_active_port/W 1'	| mdb -kw
9812399SChris.Horne@Sun.COM  * # modunload -i <pmcs_driver_index>
9912399SChris.Horne@Sun.COM  */
10012399SChris.Horne@Sun.COM static int detach_with_active_port = 0;
10112399SChris.Horne@Sun.COM 
10210696SDavid.Hollister@Sun.COM static uint16_t maxqdepth = 0xfffe;
10310696SDavid.Hollister@Sun.COM 
10410696SDavid.Hollister@Sun.COM /*
10510696SDavid.Hollister@Sun.COM  * Local prototypes
10610696SDavid.Hollister@Sun.COM  */
10710696SDavid.Hollister@Sun.COM static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t);
10810696SDavid.Hollister@Sun.COM static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t);
10910696SDavid.Hollister@Sun.COM static int pmcs_unattach(pmcs_hw_t *);
11010696SDavid.Hollister@Sun.COM static int pmcs_iport_unattach(pmcs_iport_t *);
11110696SDavid.Hollister@Sun.COM static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long);
11210696SDavid.Hollister@Sun.COM static void pmcs_watchdog(void *);
11310696SDavid.Hollister@Sun.COM static int pmcs_setup_intr(pmcs_hw_t *);
11410696SDavid.Hollister@Sun.COM static int pmcs_teardown_intr(pmcs_hw_t *);
11510696SDavid.Hollister@Sun.COM 
11610696SDavid.Hollister@Sun.COM static uint_t pmcs_nonio_ix(caddr_t, caddr_t);
11710696SDavid.Hollister@Sun.COM static uint_t pmcs_general_ix(caddr_t, caddr_t);
11810696SDavid.Hollister@Sun.COM static uint_t pmcs_event_ix(caddr_t, caddr_t);
11910696SDavid.Hollister@Sun.COM static uint_t pmcs_iodone_ix(caddr_t, caddr_t);
12010696SDavid.Hollister@Sun.COM static uint_t pmcs_fatal_ix(caddr_t, caddr_t);
12110696SDavid.Hollister@Sun.COM static uint_t pmcs_all_intr(caddr_t, caddr_t);
12210696SDavid.Hollister@Sun.COM static int pmcs_quiesce(dev_info_t *dip);
12310696SDavid.Hollister@Sun.COM static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *);
12410696SDavid.Hollister@Sun.COM 
12512077Ssrikanth.suravajhala@oracle.com static void pmcs_create_all_phy_stats(pmcs_iport_t *);
12610696SDavid.Hollister@Sun.COM int pmcs_update_phy_stats(kstat_t *, int);
12710696SDavid.Hollister@Sun.COM 
12810696SDavid.Hollister@Sun.COM static void pmcs_fm_fini(pmcs_hw_t *pwp);
12910696SDavid.Hollister@Sun.COM static void pmcs_fm_init(pmcs_hw_t *pwp);
13010696SDavid.Hollister@Sun.COM static int pmcs_fm_error_cb(dev_info_t *dip,
13110696SDavid.Hollister@Sun.COM     ddi_fm_error_t *err, const void *impl_data);
13210696SDavid.Hollister@Sun.COM 
13310696SDavid.Hollister@Sun.COM /*
13410696SDavid.Hollister@Sun.COM  * Local configuration data
13510696SDavid.Hollister@Sun.COM  */
13610696SDavid.Hollister@Sun.COM static struct dev_ops pmcs_ops = {
13710696SDavid.Hollister@Sun.COM 	DEVO_REV,		/* devo_rev, */
13810696SDavid.Hollister@Sun.COM 	0,			/* refcnt */
13910696SDavid.Hollister@Sun.COM 	ddi_no_info,		/* info */
14010696SDavid.Hollister@Sun.COM 	nulldev,		/* identify */
14110696SDavid.Hollister@Sun.COM 	nulldev,		/* probe */
14210696SDavid.Hollister@Sun.COM 	pmcs_attach,		/* attach */
14310696SDavid.Hollister@Sun.COM 	pmcs_detach,		/* detach */
14410696SDavid.Hollister@Sun.COM 	nodev,			/* reset */
14510696SDavid.Hollister@Sun.COM 	NULL,			/* driver operations */
14610696SDavid.Hollister@Sun.COM 	NULL,			/* bus operations */
14710696SDavid.Hollister@Sun.COM 	ddi_power,		/* power management */
14810696SDavid.Hollister@Sun.COM 	pmcs_quiesce		/* quiesce */
14910696SDavid.Hollister@Sun.COM };
15010696SDavid.Hollister@Sun.COM 
15110696SDavid.Hollister@Sun.COM static struct modldrv modldrv = {
15210696SDavid.Hollister@Sun.COM 	&mod_driverops,
15310696SDavid.Hollister@Sun.COM 	PMCS_DRIVER_VERSION,
15410696SDavid.Hollister@Sun.COM 	&pmcs_ops,	/* driver ops */
15510696SDavid.Hollister@Sun.COM };
15610696SDavid.Hollister@Sun.COM static struct modlinkage modlinkage = {
15710696SDavid.Hollister@Sun.COM 	MODREV_1, &modldrv, NULL
15810696SDavid.Hollister@Sun.COM };
15910696SDavid.Hollister@Sun.COM 
16010696SDavid.Hollister@Sun.COM const ddi_dma_attr_t pmcs_dattr = {
16110696SDavid.Hollister@Sun.COM 	DMA_ATTR_V0,			/* dma_attr version	*/
16210696SDavid.Hollister@Sun.COM 	0x0000000000000000ull,		/* dma_attr_addr_lo	*/
16310696SDavid.Hollister@Sun.COM 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_addr_hi	*/
16410696SDavid.Hollister@Sun.COM 	0x00000000FFFFFFFFull,		/* dma_attr_count_max	*/
16510696SDavid.Hollister@Sun.COM 	0x0000000000000001ull,		/* dma_attr_align	*/
16610696SDavid.Hollister@Sun.COM 	0x00000078,			/* dma_attr_burstsizes	*/
16710696SDavid.Hollister@Sun.COM 	0x00000001,			/* dma_attr_minxfer	*/
16810696SDavid.Hollister@Sun.COM 	0x00000000FFFFFFFFull,		/* dma_attr_maxxfer	*/
16910696SDavid.Hollister@Sun.COM 	0x00000000FFFFFFFFull,		/* dma_attr_seg		*/
17010696SDavid.Hollister@Sun.COM 	1,				/* dma_attr_sgllen 	*/
17110696SDavid.Hollister@Sun.COM 	512,				/* dma_attr_granular 	*/
17210696SDavid.Hollister@Sun.COM 	0				/* dma_attr_flags 	*/
17310696SDavid.Hollister@Sun.COM };
17410696SDavid.Hollister@Sun.COM 
17510696SDavid.Hollister@Sun.COM static ddi_device_acc_attr_t rattr = {
17611236SStephen.Hanson@Sun.COM 	DDI_DEVICE_ATTR_V1,
17710696SDavid.Hollister@Sun.COM 	DDI_STRUCTURE_LE_ACC,
17810696SDavid.Hollister@Sun.COM 	DDI_STRICTORDER_ACC,
17910696SDavid.Hollister@Sun.COM 	DDI_DEFAULT_ACC
18010696SDavid.Hollister@Sun.COM };
18110696SDavid.Hollister@Sun.COM 
18210696SDavid.Hollister@Sun.COM 
18310696SDavid.Hollister@Sun.COM /*
18410696SDavid.Hollister@Sun.COM  * Attach/Detach functions
18510696SDavid.Hollister@Sun.COM  */
18610696SDavid.Hollister@Sun.COM 
18710696SDavid.Hollister@Sun.COM int
_init(void)18810696SDavid.Hollister@Sun.COM _init(void)
18910696SDavid.Hollister@Sun.COM {
19010696SDavid.Hollister@Sun.COM 	int ret;
19110696SDavid.Hollister@Sun.COM 
19210696SDavid.Hollister@Sun.COM 	ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1);
19310696SDavid.Hollister@Sun.COM 	if (ret != 0) {
19410696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "?soft state init failed for pmcs");
19510696SDavid.Hollister@Sun.COM 		return (ret);
19610696SDavid.Hollister@Sun.COM 	}
19710696SDavid.Hollister@Sun.COM 
19810696SDavid.Hollister@Sun.COM 	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
19910696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs");
20010696SDavid.Hollister@Sun.COM 		ddi_soft_state_fini(&pmcs_softc_state);
20110696SDavid.Hollister@Sun.COM 		return (ret);
20210696SDavid.Hollister@Sun.COM 	}
20310696SDavid.Hollister@Sun.COM 
20410696SDavid.Hollister@Sun.COM 	/*
20510696SDavid.Hollister@Sun.COM 	 * Allocate soft state for iports
20610696SDavid.Hollister@Sun.COM 	 */
20710696SDavid.Hollister@Sun.COM 	ret = ddi_soft_state_init(&pmcs_iport_softstate,
20810696SDavid.Hollister@Sun.COM 	    sizeof (pmcs_iport_t), 2);
20910696SDavid.Hollister@Sun.COM 	if (ret != 0) {
21010696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "?iport soft state init failed for pmcs");
21110696SDavid.Hollister@Sun.COM 		ddi_soft_state_fini(&pmcs_softc_state);
21210696SDavid.Hollister@Sun.COM 		return (ret);
21310696SDavid.Hollister@Sun.COM 	}
21410696SDavid.Hollister@Sun.COM 
21510696SDavid.Hollister@Sun.COM 	ret = mod_install(&modlinkage);
21610696SDavid.Hollister@Sun.COM 	if (ret != 0) {
21710696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret);
21810696SDavid.Hollister@Sun.COM 		scsi_hba_fini(&modlinkage);
21910696SDavid.Hollister@Sun.COM 		ddi_soft_state_fini(&pmcs_iport_softstate);
22010696SDavid.Hollister@Sun.COM 		ddi_soft_state_fini(&pmcs_softc_state);
22110696SDavid.Hollister@Sun.COM 		return (ret);
22210696SDavid.Hollister@Sun.COM 	}
22310696SDavid.Hollister@Sun.COM 
22410696SDavid.Hollister@Sun.COM 	/* Initialize the global trace lock */
22510696SDavid.Hollister@Sun.COM 	mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL);
22610696SDavid.Hollister@Sun.COM 
22710696SDavid.Hollister@Sun.COM 	return (0);
22810696SDavid.Hollister@Sun.COM }
22910696SDavid.Hollister@Sun.COM 
23010696SDavid.Hollister@Sun.COM int
_fini(void)23110696SDavid.Hollister@Sun.COM _fini(void)
23210696SDavid.Hollister@Sun.COM {
23310696SDavid.Hollister@Sun.COM 	int ret;
23410696SDavid.Hollister@Sun.COM 	if ((ret = mod_remove(&modlinkage)) != 0) {
23510696SDavid.Hollister@Sun.COM 		return (ret);
23610696SDavid.Hollister@Sun.COM 	}
23710696SDavid.Hollister@Sun.COM 	scsi_hba_fini(&modlinkage);
23810696SDavid.Hollister@Sun.COM 
23910696SDavid.Hollister@Sun.COM 	/* Free pmcs log buffer and destroy the global lock */
24010696SDavid.Hollister@Sun.COM 	if (pmcs_tbuf) {
24110696SDavid.Hollister@Sun.COM 		kmem_free(pmcs_tbuf,
24210696SDavid.Hollister@Sun.COM 		    pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t));
24310696SDavid.Hollister@Sun.COM 		pmcs_tbuf = NULL;
24410696SDavid.Hollister@Sun.COM 	}
24510696SDavid.Hollister@Sun.COM 	mutex_destroy(&pmcs_trace_lock);
24610696SDavid.Hollister@Sun.COM 
24710696SDavid.Hollister@Sun.COM 	ddi_soft_state_fini(&pmcs_iport_softstate);
24810696SDavid.Hollister@Sun.COM 	ddi_soft_state_fini(&pmcs_softc_state);
24910696SDavid.Hollister@Sun.COM 	return (0);
25010696SDavid.Hollister@Sun.COM }
25110696SDavid.Hollister@Sun.COM 
25210696SDavid.Hollister@Sun.COM int
_info(struct modinfo * modinfop)25310696SDavid.Hollister@Sun.COM _info(struct modinfo *modinfop)
25410696SDavid.Hollister@Sun.COM {
25510696SDavid.Hollister@Sun.COM 	return (mod_info(&modlinkage, modinfop));
25610696SDavid.Hollister@Sun.COM }
25710696SDavid.Hollister@Sun.COM 
25810696SDavid.Hollister@Sun.COM static int
pmcs_iport_attach(dev_info_t * dip)25910696SDavid.Hollister@Sun.COM pmcs_iport_attach(dev_info_t *dip)
26010696SDavid.Hollister@Sun.COM {
26110696SDavid.Hollister@Sun.COM 	pmcs_iport_t		*iport;
26210696SDavid.Hollister@Sun.COM 	pmcs_hw_t		*pwp;
26310696SDavid.Hollister@Sun.COM 	scsi_hba_tran_t		*tran;
26410696SDavid.Hollister@Sun.COM 	void			*ua_priv = NULL;
26510696SDavid.Hollister@Sun.COM 	char			*iport_ua;
26610696SDavid.Hollister@Sun.COM 	char			*init_port;
26710696SDavid.Hollister@Sun.COM 	int			hba_inst;
26810696SDavid.Hollister@Sun.COM 	int			inst;
26910696SDavid.Hollister@Sun.COM 
27010696SDavid.Hollister@Sun.COM 	hba_inst = ddi_get_instance(ddi_get_parent(dip));
27110696SDavid.Hollister@Sun.COM 	inst = ddi_get_instance(dip);
27210696SDavid.Hollister@Sun.COM 
27310696SDavid.Hollister@Sun.COM 	pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst);
27410696SDavid.Hollister@Sun.COM 	if (pwp == NULL) {
27511693SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "%s: No HBA softstate for instance %d",
27610696SDavid.Hollister@Sun.COM 		    __func__, inst);
27710696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
27810696SDavid.Hollister@Sun.COM 	}
27910696SDavid.Hollister@Sun.COM 
28010696SDavid.Hollister@Sun.COM 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
28110696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
28210696SDavid.Hollister@Sun.COM 	}
28310696SDavid.Hollister@Sun.COM 
28410696SDavid.Hollister@Sun.COM 	if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) {
28511048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
28610696SDavid.Hollister@Sun.COM 		    "%s: invoked with NULL unit address, inst (%d)",
28710696SDavid.Hollister@Sun.COM 		    __func__, inst);
28810696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
28910696SDavid.Hollister@Sun.COM 	}
29010696SDavid.Hollister@Sun.COM 
29110696SDavid.Hollister@Sun.COM 	if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) {
29211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
29310696SDavid.Hollister@Sun.COM 		    "Failed to alloc soft state for iport %d", inst);
29410696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
29510696SDavid.Hollister@Sun.COM 	}
29610696SDavid.Hollister@Sun.COM 
29710696SDavid.Hollister@Sun.COM 	iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
29810696SDavid.Hollister@Sun.COM 	if (iport == NULL) {
29911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
30010696SDavid.Hollister@Sun.COM 		    "cannot get iport soft state");
30110696SDavid.Hollister@Sun.COM 		goto iport_attach_fail1;
30210696SDavid.Hollister@Sun.COM 	}
30310696SDavid.Hollister@Sun.COM 
30410696SDavid.Hollister@Sun.COM 	mutex_init(&iport->lock, NULL, MUTEX_DRIVER,
30510696SDavid.Hollister@Sun.COM 	    DDI_INTR_PRI(pwp->intr_pri));
30610696SDavid.Hollister@Sun.COM 	cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL);
30711267SJesse.Butler@Sun.COM 	cv_init(&iport->smp_cv, NULL, CV_DEFAULT, NULL);
30810696SDavid.Hollister@Sun.COM 	mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER,
30910696SDavid.Hollister@Sun.COM 	    DDI_INTR_PRI(pwp->intr_pri));
31011267SJesse.Butler@Sun.COM 	mutex_init(&iport->smp_lock, NULL, MUTEX_DRIVER,
31111267SJesse.Butler@Sun.COM 	    DDI_INTR_PRI(pwp->intr_pri));
31210696SDavid.Hollister@Sun.COM 
31310696SDavid.Hollister@Sun.COM 	/* Set some data on the iport handle */
31410696SDavid.Hollister@Sun.COM 	iport->dip = dip;
31510696SDavid.Hollister@Sun.COM 	iport->pwp = pwp;
31610696SDavid.Hollister@Sun.COM 
31710696SDavid.Hollister@Sun.COM 	/* Dup the UA into the iport handle */
31810696SDavid.Hollister@Sun.COM 	iport->ua = strdup(iport_ua);
31910696SDavid.Hollister@Sun.COM 
32010696SDavid.Hollister@Sun.COM 	tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
32110696SDavid.Hollister@Sun.COM 	tran->tran_hba_private = iport;
32210696SDavid.Hollister@Sun.COM 
32310696SDavid.Hollister@Sun.COM 	list_create(&iport->phys, sizeof (pmcs_phy_t),
32410696SDavid.Hollister@Sun.COM 	    offsetof(pmcs_phy_t, list_node));
32510696SDavid.Hollister@Sun.COM 
32610696SDavid.Hollister@Sun.COM 	/*
32710696SDavid.Hollister@Sun.COM 	 * If our unit address is active in the phymap, configure our
32810696SDavid.Hollister@Sun.COM 	 * iport's phylist.
32910696SDavid.Hollister@Sun.COM 	 */
33010696SDavid.Hollister@Sun.COM 	mutex_enter(&iport->lock);
33110696SDavid.Hollister@Sun.COM 	ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua);
33210696SDavid.Hollister@Sun.COM 	if (ua_priv) {
33310696SDavid.Hollister@Sun.COM 		/* Non-NULL private data indicates the unit address is active */
33410696SDavid.Hollister@Sun.COM 		iport->ua_state = UA_ACTIVE;
33510696SDavid.Hollister@Sun.COM 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
33611048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
33711048SDavid.Hollister@Sun.COM 			    "%s: failed to "
33810696SDavid.Hollister@Sun.COM 			    "configure phys on iport handle (0x%p), "
33910696SDavid.Hollister@Sun.COM 			    " unit address [%s]", __func__,
34010696SDavid.Hollister@Sun.COM 			    (void *)iport, iport_ua);
34110696SDavid.Hollister@Sun.COM 			mutex_exit(&iport->lock);
34210696SDavid.Hollister@Sun.COM 			goto iport_attach_fail2;
34310696SDavid.Hollister@Sun.COM 		}
34410696SDavid.Hollister@Sun.COM 	} else {
34510696SDavid.Hollister@Sun.COM 		iport->ua_state = UA_INACTIVE;
34610696SDavid.Hollister@Sun.COM 	}
34710696SDavid.Hollister@Sun.COM 	mutex_exit(&iport->lock);
34810696SDavid.Hollister@Sun.COM 
34910696SDavid.Hollister@Sun.COM 	/* Allocate string-based soft state pool for targets */
35010696SDavid.Hollister@Sun.COM 	iport->tgt_sstate = NULL;
35110696SDavid.Hollister@Sun.COM 	if (ddi_soft_state_bystr_init(&iport->tgt_sstate,
35210696SDavid.Hollister@Sun.COM 	    sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) {
35311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
35410696SDavid.Hollister@Sun.COM 		    "cannot get iport tgt soft state");
35510696SDavid.Hollister@Sun.COM 		goto iport_attach_fail2;
35610696SDavid.Hollister@Sun.COM 	}
35710696SDavid.Hollister@Sun.COM 
35810696SDavid.Hollister@Sun.COM 	/* Create this iport's target map */
35910696SDavid.Hollister@Sun.COM 	if (pmcs_iport_tgtmap_create(iport) == B_FALSE) {
36011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
36110696SDavid.Hollister@Sun.COM 		    "Failed to create tgtmap on iport %d", inst);
36210696SDavid.Hollister@Sun.COM 		goto iport_attach_fail3;
36310696SDavid.Hollister@Sun.COM 	}
36410696SDavid.Hollister@Sun.COM 
36510696SDavid.Hollister@Sun.COM 	/* Set up the 'initiator-port' DDI property on this iport */
36610696SDavid.Hollister@Sun.COM 	init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
36710696SDavid.Hollister@Sun.COM 	if (pwp->separate_ports) {
36811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
36911048SDavid.Hollister@Sun.COM 		    "%s: separate ports not supported", __func__);
37010696SDavid.Hollister@Sun.COM 	} else {
37110696SDavid.Hollister@Sun.COM 		/* Set initiator-port value to the HBA's base WWN */
37210696SDavid.Hollister@Sun.COM 		(void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1,
37310696SDavid.Hollister@Sun.COM 		    init_port);
37410696SDavid.Hollister@Sun.COM 	}
37511090SDavid.Hollister@Sun.COM 
37611090SDavid.Hollister@Sun.COM 	mutex_enter(&iport->lock);
37710696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING,
37810696SDavid.Hollister@Sun.COM 	    SCSI_ADDR_PROP_INITIATOR_PORT, init_port);
37910696SDavid.Hollister@Sun.COM 	kmem_free(init_port, PMCS_MAX_UA_SIZE);
38010696SDavid.Hollister@Sun.COM 
38110696SDavid.Hollister@Sun.COM 	/* Set up a 'num-phys' DDI property for the iport node */
38210696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
38310696SDavid.Hollister@Sun.COM 	    &iport->nphy);
38411090SDavid.Hollister@Sun.COM 	mutex_exit(&iport->lock);
38510696SDavid.Hollister@Sun.COM 
38610696SDavid.Hollister@Sun.COM 	/* Create kstats for each of the phys in this port */
38712077Ssrikanth.suravajhala@oracle.com 	pmcs_create_all_phy_stats(iport);
38810696SDavid.Hollister@Sun.COM 
38910696SDavid.Hollister@Sun.COM 	/*
39010696SDavid.Hollister@Sun.COM 	 * Insert this iport handle into our list and set
39110696SDavid.Hollister@Sun.COM 	 * iports_attached on the HBA node.
39210696SDavid.Hollister@Sun.COM 	 */
39310696SDavid.Hollister@Sun.COM 	rw_enter(&pwp->iports_lock, RW_WRITER);
39410696SDavid.Hollister@Sun.COM 	ASSERT(!list_link_active(&iport->list_node));
39510696SDavid.Hollister@Sun.COM 	list_insert_tail(&pwp->iports, iport);
39610696SDavid.Hollister@Sun.COM 	pwp->iports_attached = 1;
39710696SDavid.Hollister@Sun.COM 	pwp->num_iports++;
39810696SDavid.Hollister@Sun.COM 	rw_exit(&pwp->iports_lock);
39910696SDavid.Hollister@Sun.COM 
40011048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
40111048SDavid.Hollister@Sun.COM 	    "iport%d attached", inst);
40210696SDavid.Hollister@Sun.COM 	ddi_report_dev(dip);
40310696SDavid.Hollister@Sun.COM 	return (DDI_SUCCESS);
40410696SDavid.Hollister@Sun.COM 
40510696SDavid.Hollister@Sun.COM 	/* teardown and fail */
40610696SDavid.Hollister@Sun.COM iport_attach_fail3:
40710696SDavid.Hollister@Sun.COM 	ddi_soft_state_bystr_fini(&iport->tgt_sstate);
40810696SDavid.Hollister@Sun.COM iport_attach_fail2:
40910696SDavid.Hollister@Sun.COM 	list_destroy(&iport->phys);
41010696SDavid.Hollister@Sun.COM 	strfree(iport->ua);
41110696SDavid.Hollister@Sun.COM 	mutex_destroy(&iport->refcnt_lock);
41211267SJesse.Butler@Sun.COM 	mutex_destroy(&iport->smp_lock);
41310696SDavid.Hollister@Sun.COM 	cv_destroy(&iport->refcnt_cv);
41411267SJesse.Butler@Sun.COM 	cv_destroy(&iport->smp_cv);
41510696SDavid.Hollister@Sun.COM 	mutex_destroy(&iport->lock);
41610696SDavid.Hollister@Sun.COM iport_attach_fail1:
41710696SDavid.Hollister@Sun.COM 	ddi_soft_state_free(pmcs_iport_softstate, inst);
41810696SDavid.Hollister@Sun.COM 	return (DDI_FAILURE);
41910696SDavid.Hollister@Sun.COM }
42010696SDavid.Hollister@Sun.COM 
42110696SDavid.Hollister@Sun.COM static int
pmcs_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)42210696SDavid.Hollister@Sun.COM pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
42310696SDavid.Hollister@Sun.COM {
42410696SDavid.Hollister@Sun.COM 	scsi_hba_tran_t *tran;
42510696SDavid.Hollister@Sun.COM 	char chiprev, *fwsupport, hw_rev[24], fw_rev[24];
42610696SDavid.Hollister@Sun.COM 	off_t set3size;
42710696SDavid.Hollister@Sun.COM 	int inst, i;
42810696SDavid.Hollister@Sun.COM 	int sm_hba = 1;
42910696SDavid.Hollister@Sun.COM 	int protocol = 0;
43010696SDavid.Hollister@Sun.COM 	int num_phys = 0;
43110696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp;
43210696SDavid.Hollister@Sun.COM 	pmcs_phy_t *phyp;
43310696SDavid.Hollister@Sun.COM 	uint32_t num_threads;
43410696SDavid.Hollister@Sun.COM 	char buf[64];
43511694SDavid.Hollister@Sun.COM 	char *fwl_file;
43610696SDavid.Hollister@Sun.COM 
43710696SDavid.Hollister@Sun.COM 	switch (cmd) {
43810696SDavid.Hollister@Sun.COM 	case DDI_ATTACH:
43910696SDavid.Hollister@Sun.COM 		break;
44010696SDavid.Hollister@Sun.COM 
44110696SDavid.Hollister@Sun.COM 	case DDI_PM_RESUME:
44210696SDavid.Hollister@Sun.COM 	case DDI_RESUME:
44310696SDavid.Hollister@Sun.COM 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
44410696SDavid.Hollister@Sun.COM 		if (!tran) {
44510696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
44610696SDavid.Hollister@Sun.COM 		}
44710696SDavid.Hollister@Sun.COM 		/* No DDI_?_RESUME on iport nodes */
44810696SDavid.Hollister@Sun.COM 		if (scsi_hba_iport_unit_address(dip) != NULL) {
44910696SDavid.Hollister@Sun.COM 			return (DDI_SUCCESS);
45010696SDavid.Hollister@Sun.COM 		}
45110696SDavid.Hollister@Sun.COM 		pwp = TRAN2PMC(tran);
45210696SDavid.Hollister@Sun.COM 		if (pwp == NULL) {
45310696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
45410696SDavid.Hollister@Sun.COM 		}
45510696SDavid.Hollister@Sun.COM 
45610696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
45710696SDavid.Hollister@Sun.COM 		pwp->suspended = 0;
45810696SDavid.Hollister@Sun.COM 		if (pwp->tq) {
45910696SDavid.Hollister@Sun.COM 			ddi_taskq_resume(pwp->tq);
46010696SDavid.Hollister@Sun.COM 		}
46110696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
46210696SDavid.Hollister@Sun.COM 		return (DDI_SUCCESS);
46310696SDavid.Hollister@Sun.COM 
46410696SDavid.Hollister@Sun.COM 	default:
46510696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
46610696SDavid.Hollister@Sun.COM 	}
46710696SDavid.Hollister@Sun.COM 
46810696SDavid.Hollister@Sun.COM 	/*
46910696SDavid.Hollister@Sun.COM 	 * If this is an iport node, invoke iport attach.
47010696SDavid.Hollister@Sun.COM 	 */
47110696SDavid.Hollister@Sun.COM 	if (scsi_hba_iport_unit_address(dip) != NULL) {
47210696SDavid.Hollister@Sun.COM 		return (pmcs_iport_attach(dip));
47310696SDavid.Hollister@Sun.COM 	}
47410696SDavid.Hollister@Sun.COM 
47510696SDavid.Hollister@Sun.COM 	/*
47610696SDavid.Hollister@Sun.COM 	 * From here on is attach for the HBA node
47710696SDavid.Hollister@Sun.COM 	 */
47810696SDavid.Hollister@Sun.COM 
47910696SDavid.Hollister@Sun.COM #ifdef	DEBUG
48010696SDavid.Hollister@Sun.COM 	/*
48110696SDavid.Hollister@Sun.COM 	 * Check to see if this unit is to be disabled.  We can't disable
48210696SDavid.Hollister@Sun.COM 	 * on a per-iport node.  It's either the entire HBA or nothing.
48310696SDavid.Hollister@Sun.COM 	 */
48410696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
48510696SDavid.Hollister@Sun.COM 	    "disable-instance-%d", ddi_get_instance(dip));
48610696SDavid.Hollister@Sun.COM 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
48710696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) {
48810696SDavid.Hollister@Sun.COM 		cmn_err(CE_NOTE, "pmcs%d: disabled by configuration",
48910696SDavid.Hollister@Sun.COM 		    ddi_get_instance(dip));
49010696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
49110696SDavid.Hollister@Sun.COM 	}
49210696SDavid.Hollister@Sun.COM #endif
49310696SDavid.Hollister@Sun.COM 
49410696SDavid.Hollister@Sun.COM 	/*
49510696SDavid.Hollister@Sun.COM 	 * Allocate softstate
49610696SDavid.Hollister@Sun.COM 	 */
49710696SDavid.Hollister@Sun.COM 	inst = ddi_get_instance(dip);
49810696SDavid.Hollister@Sun.COM 	if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) {
49910696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst);
50010696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
50110696SDavid.Hollister@Sun.COM 	}
50210696SDavid.Hollister@Sun.COM 
50310696SDavid.Hollister@Sun.COM 	pwp = ddi_get_soft_state(pmcs_softc_state, inst);
50410696SDavid.Hollister@Sun.COM 	if (pwp == NULL) {
50510696SDavid.Hollister@Sun.COM 		cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst);
50610696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
50710696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
50810696SDavid.Hollister@Sun.COM 	}
50910696SDavid.Hollister@Sun.COM 	pwp->dip = dip;
51010696SDavid.Hollister@Sun.COM 	STAILQ_INIT(&pwp->dq);
51110696SDavid.Hollister@Sun.COM 	STAILQ_INIT(&pwp->cq);
51210696SDavid.Hollister@Sun.COM 	STAILQ_INIT(&pwp->wf);
51310696SDavid.Hollister@Sun.COM 	STAILQ_INIT(&pwp->pf);
51412078SJesse.Butler@Sun.COM 
51510696SDavid.Hollister@Sun.COM 	/*
51612078SJesse.Butler@Sun.COM 	 * Create the list for iports and init its lock.
51710696SDavid.Hollister@Sun.COM 	 */
51810696SDavid.Hollister@Sun.COM 	list_create(&pwp->iports, sizeof (pmcs_iport_t),
51910696SDavid.Hollister@Sun.COM 	    offsetof(pmcs_iport_t, list_node));
52012078SJesse.Butler@Sun.COM 	rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL);
52110696SDavid.Hollister@Sun.COM 
52210696SDavid.Hollister@Sun.COM 	pwp->state = STATE_PROBING;
52310696SDavid.Hollister@Sun.COM 
52410696SDavid.Hollister@Sun.COM 	/*
52510696SDavid.Hollister@Sun.COM 	 * Get driver.conf properties
52610696SDavid.Hollister@Sun.COM 	 */
52710696SDavid.Hollister@Sun.COM 	pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
52810696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask",
52910696SDavid.Hollister@Sun.COM 	    debug_mask);
53010696SDavid.Hollister@Sun.COM 	pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
53110696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask",
53210696SDavid.Hollister@Sun.COM 	    block_mask);
53310696SDavid.Hollister@Sun.COM 	pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
53410696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed);
53510696SDavid.Hollister@Sun.COM 	pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
53610696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode);
53710696SDavid.Hollister@Sun.COM 	pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
53810696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level);
53910696SDavid.Hollister@Sun.COM 	if (pwp->fwlog > PMCS_FWLOG_MAX) {
54010696SDavid.Hollister@Sun.COM 		pwp->fwlog = PMCS_FWLOG_MAX;
54110696SDavid.Hollister@Sun.COM 	}
54211694SDavid.Hollister@Sun.COM 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "pmcs-fwlogfile",
54311694SDavid.Hollister@Sun.COM 	    &fwl_file) == DDI_SUCCESS)) {
54411694SDavid.Hollister@Sun.COM 		if (snprintf(pwp->fwlogfile_aap1, MAXPATHLEN, "%s%d-aap1.0",
54511694SDavid.Hollister@Sun.COM 		    fwl_file, ddi_get_instance(dip)) > MAXPATHLEN) {
54611694SDavid.Hollister@Sun.COM 			pwp->fwlogfile_aap1[0] = '\0';
54711694SDavid.Hollister@Sun.COM 			pwp->fwlogfile_iop[0] = '\0';
54811694SDavid.Hollister@Sun.COM 		} else if (snprintf(pwp->fwlogfile_iop, MAXPATHLEN,
54911694SDavid.Hollister@Sun.COM 		    "%s%d-iop.0", fwl_file,
55011694SDavid.Hollister@Sun.COM 		    ddi_get_instance(dip)) > MAXPATHLEN) {
55111694SDavid.Hollister@Sun.COM 			pwp->fwlogfile_aap1[0] = '\0';
55211694SDavid.Hollister@Sun.COM 			pwp->fwlogfile_iop[0] = '\0';
55311694SDavid.Hollister@Sun.COM 		}
55411694SDavid.Hollister@Sun.COM 		ddi_prop_free(fwl_file);
55511694SDavid.Hollister@Sun.COM 	} else {
55611694SDavid.Hollister@Sun.COM 		pwp->fwlogfile_aap1[0] = '\0';
55711694SDavid.Hollister@Sun.COM 		pwp->fwlogfile_iop[0] = '\0';
55811694SDavid.Hollister@Sun.COM 	}
55910696SDavid.Hollister@Sun.COM 
56012060SDavid.Hollister@Sun.COM 	pwp->open_retry_interval = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
56112060SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-open-retry-interval",
56212060SDavid.Hollister@Sun.COM 	    OPEN_RETRY_INTERVAL_DEF);
56312060SDavid.Hollister@Sun.COM 	if (pwp->open_retry_interval > OPEN_RETRY_INTERVAL_MAX) {
56412060SDavid.Hollister@Sun.COM 		pwp->open_retry_interval = OPEN_RETRY_INTERVAL_MAX;
56512060SDavid.Hollister@Sun.COM 	}
56612060SDavid.Hollister@Sun.COM 
56710696SDavid.Hollister@Sun.COM 	mutex_enter(&pmcs_trace_lock);
56810696SDavid.Hollister@Sun.COM 	if (pmcs_tbuf == NULL) {
56910696SDavid.Hollister@Sun.COM 		/* Allocate trace buffer */
57010696SDavid.Hollister@Sun.COM 		pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
57110696SDavid.Hollister@Sun.COM 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems",
57210696SDavid.Hollister@Sun.COM 		    PMCS_TBUF_NUM_ELEMS_DEF);
57310696SDavid.Hollister@Sun.COM 		if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) ||
57410696SDavid.Hollister@Sun.COM 		    (pmcs_tbuf_num_elems == 0)) {
57510696SDavid.Hollister@Sun.COM 			pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF;
57610696SDavid.Hollister@Sun.COM 		}
57710696SDavid.Hollister@Sun.COM 
57810696SDavid.Hollister@Sun.COM 		pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems *
57910696SDavid.Hollister@Sun.COM 		    sizeof (pmcs_tbuf_t), KM_SLEEP);
58010696SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr = pmcs_tbuf;
58110696SDavid.Hollister@Sun.COM 		pmcs_tbuf_idx = 0;
58210696SDavid.Hollister@Sun.COM 	}
58310696SDavid.Hollister@Sun.COM 	mutex_exit(&pmcs_trace_lock);
58410696SDavid.Hollister@Sun.COM 
58511694SDavid.Hollister@Sun.COM 	if (pwp->fwlog && strlen(pwp->fwlogfile_aap1) > 0) {
58611694SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
58711694SDavid.Hollister@Sun.COM 		    "%s: firmware event log files: %s, %s", __func__,
58811694SDavid.Hollister@Sun.COM 		    pwp->fwlogfile_aap1, pwp->fwlogfile_iop);
58911694SDavid.Hollister@Sun.COM 		pwp->fwlog_file = 1;
59011694SDavid.Hollister@Sun.COM 	} else {
59111694SDavid.Hollister@Sun.COM 		if (pwp->fwlog == 0) {
59211694SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
59311694SDavid.Hollister@Sun.COM 			    "%s: No firmware event log will be written "
59411694SDavid.Hollister@Sun.COM 			    "(event log disabled)", __func__);
59511694SDavid.Hollister@Sun.COM 		} else {
59611694SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
59711694SDavid.Hollister@Sun.COM 			    "%s: No firmware event log will be written "
59811694SDavid.Hollister@Sun.COM 			    "(no filename configured - too long?)", __func__);
59911694SDavid.Hollister@Sun.COM 		}
60011694SDavid.Hollister@Sun.COM 		pwp->fwlog_file = 0;
60111694SDavid.Hollister@Sun.COM 	}
60211694SDavid.Hollister@Sun.COM 
60310696SDavid.Hollister@Sun.COM 	disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
60410696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix",
60510696SDavid.Hollister@Sun.COM 	    disable_msix);
60610696SDavid.Hollister@Sun.COM 	disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
60710696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi",
60810696SDavid.Hollister@Sun.COM 	    disable_msi);
60910696SDavid.Hollister@Sun.COM 	maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
61010696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth);
61110696SDavid.Hollister@Sun.COM 	pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
61210696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0);
61310696SDavid.Hollister@Sun.COM 	if (pwp->fw_force_update == 0) {
61410696SDavid.Hollister@Sun.COM 		pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
61510696SDavid.Hollister@Sun.COM 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
61610696SDavid.Hollister@Sun.COM 		    "pmcs-fw-disable-update", 0);
61710696SDavid.Hollister@Sun.COM 	}
61810696SDavid.Hollister@Sun.COM 	pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
61910696SDavid.Hollister@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries",
62010696SDavid.Hollister@Sun.COM 	    PMCS_NQENTRY);
62110696SDavid.Hollister@Sun.COM 
62210696SDavid.Hollister@Sun.COM 	/*
62310696SDavid.Hollister@Sun.COM 	 * Initialize FMA
62410696SDavid.Hollister@Sun.COM 	 */
62510696SDavid.Hollister@Sun.COM 	pwp->dev_acc_attr = pwp->reg_acc_attr = rattr;
62610696SDavid.Hollister@Sun.COM 	pwp->iqp_dma_attr = pwp->oqp_dma_attr =
62710696SDavid.Hollister@Sun.COM 	    pwp->regdump_dma_attr = pwp->cip_dma_attr =
62810696SDavid.Hollister@Sun.COM 	    pwp->fwlog_dma_attr = pmcs_dattr;
62910696SDavid.Hollister@Sun.COM 	pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip,
63010696SDavid.Hollister@Sun.COM 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable",
63110696SDavid.Hollister@Sun.COM 	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
63210696SDavid.Hollister@Sun.COM 	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
63310696SDavid.Hollister@Sun.COM 	pmcs_fm_init(pwp);
63410696SDavid.Hollister@Sun.COM 
63510696SDavid.Hollister@Sun.COM 	/*
63610696SDavid.Hollister@Sun.COM 	 * Map registers
63710696SDavid.Hollister@Sun.COM 	 */
63810696SDavid.Hollister@Sun.COM 	if (pci_config_setup(dip, &pwp->pci_acc_handle)) {
63911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
64011048SDavid.Hollister@Sun.COM 		    "pci config setup failed");
64110696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
64210696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
64310696SDavid.Hollister@Sun.COM 	}
64410696SDavid.Hollister@Sun.COM 
64510696SDavid.Hollister@Sun.COM 	/*
64610696SDavid.Hollister@Sun.COM 	 * Get the size of register set 3.
64710696SDavid.Hollister@Sun.COM 	 */
64810696SDavid.Hollister@Sun.COM 	if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) {
64911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
65010696SDavid.Hollister@Sun.COM 		    "unable to get size of register set %d", PMCS_REGSET_3);
65110696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
65210696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
65310696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
65410696SDavid.Hollister@Sun.COM 	}
65510696SDavid.Hollister@Sun.COM 
65610696SDavid.Hollister@Sun.COM 	/*
65710696SDavid.Hollister@Sun.COM 	 * Map registers
65810696SDavid.Hollister@Sun.COM 	 */
65910696SDavid.Hollister@Sun.COM 	pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
66010696SDavid.Hollister@Sun.COM 
66110696SDavid.Hollister@Sun.COM 	if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs,
66210696SDavid.Hollister@Sun.COM 	    0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) {
66311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
66410696SDavid.Hollister@Sun.COM 		    "failed to map Message Unit registers");
66510696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
66610696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
66710696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
66810696SDavid.Hollister@Sun.COM 	}
66910696SDavid.Hollister@Sun.COM 
67010696SDavid.Hollister@Sun.COM 	if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs,
67110696SDavid.Hollister@Sun.COM 	    0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) {
67211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
67311048SDavid.Hollister@Sun.COM 		    "failed to map TOP registers");
67410696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->msg_acc_handle);
67510696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
67610696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
67710696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
67810696SDavid.Hollister@Sun.COM 	}
67910696SDavid.Hollister@Sun.COM 
68010696SDavid.Hollister@Sun.COM 	if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs,
68110696SDavid.Hollister@Sun.COM 	    0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) {
68211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
68311048SDavid.Hollister@Sun.COM 		    "failed to map GSM registers");
68410696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->top_acc_handle);
68510696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->msg_acc_handle);
68610696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
68710696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
68810696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
68910696SDavid.Hollister@Sun.COM 	}
69010696SDavid.Hollister@Sun.COM 
69110696SDavid.Hollister@Sun.COM 	if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs,
69210696SDavid.Hollister@Sun.COM 	    0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) {
69311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
69411048SDavid.Hollister@Sun.COM 		    "failed to map MPI registers");
69510696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->top_acc_handle);
69610696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->gsm_acc_handle);
69710696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->msg_acc_handle);
69810696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
69910696SDavid.Hollister@Sun.COM 		ddi_soft_state_free(pmcs_softc_state, inst);
70010696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
70110696SDavid.Hollister@Sun.COM 	}
70210696SDavid.Hollister@Sun.COM 	pwp->mpibar =
70310696SDavid.Hollister@Sun.COM 	    (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size;
70410696SDavid.Hollister@Sun.COM 
70510696SDavid.Hollister@Sun.COM 	/*
70610696SDavid.Hollister@Sun.COM 	 * Make sure we can support this card.
70710696SDavid.Hollister@Sun.COM 	 */
70810696SDavid.Hollister@Sun.COM 	pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION);
70910696SDavid.Hollister@Sun.COM 
71010696SDavid.Hollister@Sun.COM 	switch (pwp->chiprev) {
71110696SDavid.Hollister@Sun.COM 	case PMCS_PM8001_REV_A:
71210696SDavid.Hollister@Sun.COM 	case PMCS_PM8001_REV_B:
71311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
71410696SDavid.Hollister@Sun.COM 		    "Rev A/B Card no longer supported");
71510696SDavid.Hollister@Sun.COM 		goto failure;
71610696SDavid.Hollister@Sun.COM 	case PMCS_PM8001_REV_C:
71710696SDavid.Hollister@Sun.COM 		break;
71810696SDavid.Hollister@Sun.COM 	default:
71911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
72010696SDavid.Hollister@Sun.COM 		    "Unknown chip revision (%d)", pwp->chiprev);
72110696SDavid.Hollister@Sun.COM 		goto failure;
72210696SDavid.Hollister@Sun.COM 	}
72310696SDavid.Hollister@Sun.COM 
72410696SDavid.Hollister@Sun.COM 	/*
72510696SDavid.Hollister@Sun.COM 	 * Allocate DMA addressable area for Inbound and Outbound Queue indices
72610696SDavid.Hollister@Sun.COM 	 * that the chip needs to access plus a space for scratch usage
72710696SDavid.Hollister@Sun.COM 	 */
72810696SDavid.Hollister@Sun.COM 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
72910696SDavid.Hollister@Sun.COM 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls,
73010696SDavid.Hollister@Sun.COM 	    &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip,
73110696SDavid.Hollister@Sun.COM 	    &pwp->ciaddr) == B_FALSE) {
73211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
73310696SDavid.Hollister@Sun.COM 		    "Failed to setup DMA for index/scratch");
73410696SDavid.Hollister@Sun.COM 		goto failure;
73510696SDavid.Hollister@Sun.COM 	}
73610696SDavid.Hollister@Sun.COM 
73710696SDavid.Hollister@Sun.COM 	bzero(pwp->cip, ptob(1));
73810696SDavid.Hollister@Sun.COM 	pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE];
73910696SDavid.Hollister@Sun.COM 	pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE;
74010696SDavid.Hollister@Sun.COM 
74110696SDavid.Hollister@Sun.COM 	/*
74210696SDavid.Hollister@Sun.COM 	 * Allocate DMA S/G list chunks
74310696SDavid.Hollister@Sun.COM 	 */
74410696SDavid.Hollister@Sun.COM 	(void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES);
74510696SDavid.Hollister@Sun.COM 
74610696SDavid.Hollister@Sun.COM 	/*
74710696SDavid.Hollister@Sun.COM 	 * Allocate a DMA addressable area for the firmware log (if needed)
74810696SDavid.Hollister@Sun.COM 	 */
74910696SDavid.Hollister@Sun.COM 	if (pwp->fwlog) {
75010696SDavid.Hollister@Sun.COM 		/*
75110696SDavid.Hollister@Sun.COM 		 * Align to event log header and entry size
75210696SDavid.Hollister@Sun.COM 		 */
75310696SDavid.Hollister@Sun.COM 		pwp->fwlog_dma_attr.dma_attr_align = 32;
75410696SDavid.Hollister@Sun.COM 		if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr,
75510696SDavid.Hollister@Sun.COM 		    &pwp->fwlog_acchdl,
75610696SDavid.Hollister@Sun.COM 		    &pwp->fwlog_hndl, PMCS_FWLOG_SIZE,
75710696SDavid.Hollister@Sun.COM 		    (caddr_t *)&pwp->fwlogp,
75810696SDavid.Hollister@Sun.COM 		    &pwp->fwaddr) == B_FALSE) {
75911048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
76010696SDavid.Hollister@Sun.COM 			    "Failed to setup DMA for fwlog area");
76110696SDavid.Hollister@Sun.COM 			pwp->fwlog = 0;
76210696SDavid.Hollister@Sun.COM 		} else {
76310696SDavid.Hollister@Sun.COM 			bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
76411694SDavid.Hollister@Sun.COM 			pwp->fwlogp_aap1 = (pmcs_fw_event_hdr_t *)pwp->fwlogp;
76511694SDavid.Hollister@Sun.COM 			pwp->fwlogp_iop = (pmcs_fw_event_hdr_t *)((void *)
76611694SDavid.Hollister@Sun.COM 			    ((caddr_t)pwp->fwlogp + (PMCS_FWLOG_SIZE / 2)));
76710696SDavid.Hollister@Sun.COM 		}
76810696SDavid.Hollister@Sun.COM 	}
76910696SDavid.Hollister@Sun.COM 
77010696SDavid.Hollister@Sun.COM 	if (pwp->flash_chunk_addr == NULL) {
77110696SDavid.Hollister@Sun.COM 		pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE;
77210696SDavid.Hollister@Sun.COM 		if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr,
77310696SDavid.Hollister@Sun.COM 		    &pwp->regdump_acchdl,
77410696SDavid.Hollister@Sun.COM 		    &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE,
77510696SDavid.Hollister@Sun.COM 		    (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) ==
77610696SDavid.Hollister@Sun.COM 		    B_FALSE) {
77711048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
77810696SDavid.Hollister@Sun.COM 			    "Failed to setup DMA for register dump area");
77910696SDavid.Hollister@Sun.COM 			goto failure;
78010696SDavid.Hollister@Sun.COM 		}
78110696SDavid.Hollister@Sun.COM 		bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE);
78210696SDavid.Hollister@Sun.COM 	}
78310696SDavid.Hollister@Sun.COM 
78410696SDavid.Hollister@Sun.COM 	/*
78510696SDavid.Hollister@Sun.COM 	 * More bits of local initialization...
78610696SDavid.Hollister@Sun.COM 	 */
78710696SDavid.Hollister@Sun.COM 	pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0);
78810696SDavid.Hollister@Sun.COM 	if (pwp->tq == NULL) {
78911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
79011048SDavid.Hollister@Sun.COM 		    "unable to create worker taskq");
79110696SDavid.Hollister@Sun.COM 		goto failure;
79210696SDavid.Hollister@Sun.COM 	}
79310696SDavid.Hollister@Sun.COM 
79410696SDavid.Hollister@Sun.COM 	/*
79510696SDavid.Hollister@Sun.COM 	 * Cache of structures for dealing with I/O completion callbacks.
79610696SDavid.Hollister@Sun.COM 	 */
79710696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst);
79810696SDavid.Hollister@Sun.COM 	pwp->iocomp_cb_cache = kmem_cache_create(buf,
79910696SDavid.Hollister@Sun.COM 	    sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0);
80010696SDavid.Hollister@Sun.COM 
80110696SDavid.Hollister@Sun.COM 	/*
80210696SDavid.Hollister@Sun.COM 	 * Cache of PHY structures
80310696SDavid.Hollister@Sun.COM 	 */
80410696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst);
80510696SDavid.Hollister@Sun.COM 	pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8,
80610696SDavid.Hollister@Sun.COM 	    pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp,
80710696SDavid.Hollister@Sun.COM 	    NULL, 0);
80810696SDavid.Hollister@Sun.COM 
80910696SDavid.Hollister@Sun.COM 	/*
81010696SDavid.Hollister@Sun.COM 	 * Allocate space for the I/O completion threads
81110696SDavid.Hollister@Sun.COM 	 */
81210696SDavid.Hollister@Sun.COM 	num_threads = ncpus_online;
81310696SDavid.Hollister@Sun.COM 	if (num_threads > PMCS_MAX_CQ_THREADS) {
81410696SDavid.Hollister@Sun.COM 		num_threads = PMCS_MAX_CQ_THREADS;
81510696SDavid.Hollister@Sun.COM 	}
81610696SDavid.Hollister@Sun.COM 
81710696SDavid.Hollister@Sun.COM 	pwp->cq_info.cq_threads = num_threads;
81812399SChris.Horne@Sun.COM 	pwp->cq_info.cq_thr_info = kmem_zalloc(
81912399SChris.Horne@Sun.COM 	    sizeof (pmcs_cq_thr_info_t) * pwp->cq_info.cq_threads, KM_SLEEP);
82010696SDavid.Hollister@Sun.COM 	pwp->cq_info.cq_next_disp_thr = 0;
82110696SDavid.Hollister@Sun.COM 	pwp->cq_info.cq_stop = B_FALSE;
82210696SDavid.Hollister@Sun.COM 
82310696SDavid.Hollister@Sun.COM 	/*
82410696SDavid.Hollister@Sun.COM 	 * Set the quantum value in clock ticks for the I/O interrupt
82510696SDavid.Hollister@Sun.COM 	 * coalescing timer.
82610696SDavid.Hollister@Sun.COM 	 */
82710696SDavid.Hollister@Sun.COM 	pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS);
82810696SDavid.Hollister@Sun.COM 
82910696SDavid.Hollister@Sun.COM 	/*
83010696SDavid.Hollister@Sun.COM 	 * We have a delicate dance here. We need to set up
83110696SDavid.Hollister@Sun.COM 	 * interrupts so we know how to set up some OQC
83210696SDavid.Hollister@Sun.COM 	 * tables. However, while we're setting up table
83310696SDavid.Hollister@Sun.COM 	 * access, we may need to flash new firmware and
83410696SDavid.Hollister@Sun.COM 	 * reset the card, which will take some finessing.
83510696SDavid.Hollister@Sun.COM 	 */
83610696SDavid.Hollister@Sun.COM 
83710696SDavid.Hollister@Sun.COM 	/*
83810696SDavid.Hollister@Sun.COM 	 * Set up interrupts here.
83910696SDavid.Hollister@Sun.COM 	 */
84010696SDavid.Hollister@Sun.COM 	switch (pmcs_setup_intr(pwp)) {
84110696SDavid.Hollister@Sun.COM 	case 0:
84210696SDavid.Hollister@Sun.COM 		break;
84310696SDavid.Hollister@Sun.COM 	case EIO:
84410696SDavid.Hollister@Sun.COM 		pwp->stuck = 1;
84510696SDavid.Hollister@Sun.COM 		/* FALLTHROUGH */
84610696SDavid.Hollister@Sun.COM 	default:
84710696SDavid.Hollister@Sun.COM 		goto failure;
84810696SDavid.Hollister@Sun.COM 	}
84910696SDavid.Hollister@Sun.COM 
85010696SDavid.Hollister@Sun.COM 	/*
85110696SDavid.Hollister@Sun.COM 	 * Set these up now becuase they are used to initialize the OQC tables.
85210696SDavid.Hollister@Sun.COM 	 *
85310696SDavid.Hollister@Sun.COM 	 * If we have MSI or MSI-X interrupts set up and we have enough
85410696SDavid.Hollister@Sun.COM 	 * vectors for each OQ, the Outbound Queue vectors can all be the
85510696SDavid.Hollister@Sun.COM 	 * same as the appropriate interrupt routine will have been called
85610696SDavid.Hollister@Sun.COM 	 * and the doorbell register automatically cleared.
85710696SDavid.Hollister@Sun.COM 	 * This keeps us from having to check the Outbound Doorbell register
85810696SDavid.Hollister@Sun.COM 	 * when the routines for these interrupts are called.
85910696SDavid.Hollister@Sun.COM 	 *
86010696SDavid.Hollister@Sun.COM 	 * If we have Legacy INT-X interrupts set up or we didn't have enough
86110696SDavid.Hollister@Sun.COM 	 * MSI/MSI-X vectors to uniquely identify each OQ, we point these
86210696SDavid.Hollister@Sun.COM 	 * vectors to the bits we would like to have set in the Outbound
86310696SDavid.Hollister@Sun.COM 	 * Doorbell register because pmcs_all_intr will read the doorbell
86410696SDavid.Hollister@Sun.COM 	 * register to find out why we have an interrupt and write the
86510696SDavid.Hollister@Sun.COM 	 * corresponding 'clear' bit for that interrupt.
86610696SDavid.Hollister@Sun.COM 	 */
86710696SDavid.Hollister@Sun.COM 
86810696SDavid.Hollister@Sun.COM 	switch (pwp->intr_cnt) {
86910696SDavid.Hollister@Sun.COM 	case 1:
87010696SDavid.Hollister@Sun.COM 		/*
87110696SDavid.Hollister@Sun.COM 		 * Only one vector, so we must check all OQs for MSI.  For
87210696SDavid.Hollister@Sun.COM 		 * INT-X, there's only one vector anyway, so we can just
87310696SDavid.Hollister@Sun.COM 		 * use the outbound queue bits to keep from having to
87410696SDavid.Hollister@Sun.COM 		 * check each queue for each interrupt.
87510696SDavid.Hollister@Sun.COM 		 */
87610696SDavid.Hollister@Sun.COM 		if (pwp->int_type == PMCS_INT_FIXED) {
87710696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
87810696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
87910696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
88010696SDavid.Hollister@Sun.COM 		} else {
88110696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
88210696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE;
88310696SDavid.Hollister@Sun.COM 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE;
88410696SDavid.Hollister@Sun.COM 		}
88510696SDavid.Hollister@Sun.COM 		break;
88610696SDavid.Hollister@Sun.COM 	case 2:
88710696SDavid.Hollister@Sun.COM 		/* With 2, we can at least isolate IODONE */
88810696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
88910696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
89010696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL;
89110696SDavid.Hollister@Sun.COM 		break;
89210696SDavid.Hollister@Sun.COM 	case 4:
89310696SDavid.Hollister@Sun.COM 		/* With 4 vectors, everybody gets one */
89410696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
89510696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
89610696SDavid.Hollister@Sun.COM 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
89710696SDavid.Hollister@Sun.COM 		break;
89810696SDavid.Hollister@Sun.COM 	}
89910696SDavid.Hollister@Sun.COM 
90010696SDavid.Hollister@Sun.COM 	/*
90110696SDavid.Hollister@Sun.COM 	 * Do the first part of setup
90210696SDavid.Hollister@Sun.COM 	 */
90310696SDavid.Hollister@Sun.COM 	if (pmcs_setup(pwp)) {
90410696SDavid.Hollister@Sun.COM 		goto failure;
90510696SDavid.Hollister@Sun.COM 	}
90610696SDavid.Hollister@Sun.COM 	pmcs_report_fwversion(pwp);
90710696SDavid.Hollister@Sun.COM 
90810696SDavid.Hollister@Sun.COM 	/*
90910696SDavid.Hollister@Sun.COM 	 * Now do some additonal allocations based upon information
91010696SDavid.Hollister@Sun.COM 	 * gathered during MPI setup.
91110696SDavid.Hollister@Sun.COM 	 */
91210696SDavid.Hollister@Sun.COM 	pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP);
91310696SDavid.Hollister@Sun.COM 	ASSERT(pwp->nphy < SAS2_PHYNUM_MAX);
91410696SDavid.Hollister@Sun.COM 	phyp = pwp->root_phys;
91510696SDavid.Hollister@Sun.COM 	for (i = 0; i < pwp->nphy; i++) {
91610696SDavid.Hollister@Sun.COM 		if (i < pwp->nphy-1) {
91710696SDavid.Hollister@Sun.COM 			phyp->sibling = (phyp + 1);
91810696SDavid.Hollister@Sun.COM 		}
91910696SDavid.Hollister@Sun.COM 		mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
92010696SDavid.Hollister@Sun.COM 		    DDI_INTR_PRI(pwp->intr_pri));
92110696SDavid.Hollister@Sun.COM 		phyp->phynum = i & SAS2_PHYNUM_MASK;
92210696SDavid.Hollister@Sun.COM 		pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path));
92310696SDavid.Hollister@Sun.COM 		phyp->pwp = pwp;
92410696SDavid.Hollister@Sun.COM 		phyp->device_id = PMCS_INVALID_DEVICE_ID;
92511347SRamana.Srikanth@Sun.COM 		phyp->portid = PMCS_PHY_INVALID_PORT_ID;
92610696SDavid.Hollister@Sun.COM 		phyp++;
92710696SDavid.Hollister@Sun.COM 	}
92810696SDavid.Hollister@Sun.COM 
92910696SDavid.Hollister@Sun.COM 	pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP);
93012258Ssrikanth.suravajhala@oracle.com 	for (i = 0; i < pwp->max_cmd; i++) {
93110696SDavid.Hollister@Sun.COM 		pmcwork_t *pwrk = &pwp->work[i];
93210696SDavid.Hollister@Sun.COM 		mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER,
93310696SDavid.Hollister@Sun.COM 		    DDI_INTR_PRI(pwp->intr_pri));
93410696SDavid.Hollister@Sun.COM 		cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL);
93510696SDavid.Hollister@Sun.COM 		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
93610696SDavid.Hollister@Sun.COM 
93710696SDavid.Hollister@Sun.COM 	}
93810696SDavid.Hollister@Sun.COM 	pwp->targets = (pmcs_xscsi_t **)
93910696SDavid.Hollister@Sun.COM 	    kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP);
94010696SDavid.Hollister@Sun.COM 
94110696SDavid.Hollister@Sun.COM 	pwp->iqpt = (pmcs_iqp_trace_t *)
94210696SDavid.Hollister@Sun.COM 	    kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP);
94310696SDavid.Hollister@Sun.COM 	pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP);
94410696SDavid.Hollister@Sun.COM 	pwp->iqpt->curpos = pwp->iqpt->head;
94510696SDavid.Hollister@Sun.COM 	pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE;
94610696SDavid.Hollister@Sun.COM 
94710696SDavid.Hollister@Sun.COM 	/*
94810696SDavid.Hollister@Sun.COM 	 * Start MPI communication.
94910696SDavid.Hollister@Sun.COM 	 */
95010696SDavid.Hollister@Sun.COM 	if (pmcs_start_mpi(pwp)) {
95110696SDavid.Hollister@Sun.COM 		if (pmcs_soft_reset(pwp, B_FALSE)) {
95210696SDavid.Hollister@Sun.COM 			goto failure;
95310696SDavid.Hollister@Sun.COM 		}
95411692SJesse.Butler@Sun.COM 		pwp->last_reset_reason = PMCS_LAST_RST_ATTACH;
95510696SDavid.Hollister@Sun.COM 	}
95610696SDavid.Hollister@Sun.COM 
95710696SDavid.Hollister@Sun.COM 	/*
95810696SDavid.Hollister@Sun.COM 	 * Do some initial acceptance tests.
95910696SDavid.Hollister@Sun.COM 	 * This tests interrupts and queues.
96010696SDavid.Hollister@Sun.COM 	 */
96110696SDavid.Hollister@Sun.COM 	if (pmcs_echo_test(pwp)) {
96210696SDavid.Hollister@Sun.COM 		goto failure;
96310696SDavid.Hollister@Sun.COM 	}
96410696SDavid.Hollister@Sun.COM 
96510696SDavid.Hollister@Sun.COM 	/* Read VPD - if it exists */
96610696SDavid.Hollister@Sun.COM 	if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) {
96711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
96811048SDavid.Hollister@Sun.COM 		    "%s: Unable to read VPD: "
96910696SDavid.Hollister@Sun.COM 		    "attempting to fabricate", __func__);
97010696SDavid.Hollister@Sun.COM 		/*
97110696SDavid.Hollister@Sun.COM 		 * When we release, this must goto failure and the call
97210696SDavid.Hollister@Sun.COM 		 * to pmcs_fabricate_wwid is removed.
97310696SDavid.Hollister@Sun.COM 		 */
97410696SDavid.Hollister@Sun.COM 		/* goto failure; */
97510696SDavid.Hollister@Sun.COM 		if (!pmcs_fabricate_wwid(pwp)) {
97610696SDavid.Hollister@Sun.COM 			goto failure;
97710696SDavid.Hollister@Sun.COM 		}
97810696SDavid.Hollister@Sun.COM 	}
97910696SDavid.Hollister@Sun.COM 
98010696SDavid.Hollister@Sun.COM 	/*
98110696SDavid.Hollister@Sun.COM 	 * We're now officially running
98210696SDavid.Hollister@Sun.COM 	 */
98310696SDavid.Hollister@Sun.COM 	pwp->state = STATE_RUNNING;
98410696SDavid.Hollister@Sun.COM 
98510696SDavid.Hollister@Sun.COM 	/*
98610696SDavid.Hollister@Sun.COM 	 * Check firmware versions and load new firmware
98710696SDavid.Hollister@Sun.COM 	 * if needed and reset.
98810696SDavid.Hollister@Sun.COM 	 */
98910696SDavid.Hollister@Sun.COM 	if (pmcs_firmware_update(pwp)) {
99011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
99111048SDavid.Hollister@Sun.COM 		    "%s: Firmware update failed", __func__);
99210696SDavid.Hollister@Sun.COM 		goto failure;
99310696SDavid.Hollister@Sun.COM 	}
99410696SDavid.Hollister@Sun.COM 
99510696SDavid.Hollister@Sun.COM 	/*
99610696SDavid.Hollister@Sun.COM 	 * Create completion threads.
99710696SDavid.Hollister@Sun.COM 	 */
99810696SDavid.Hollister@Sun.COM 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
99910696SDavid.Hollister@Sun.COM 		pwp->cq_info.cq_thr_info[i].cq_pwp = pwp;
100010696SDavid.Hollister@Sun.COM 		pwp->cq_info.cq_thr_info[i].cq_thread =
100110696SDavid.Hollister@Sun.COM 		    thread_create(NULL, 0, pmcs_scsa_cq_run,
100210696SDavid.Hollister@Sun.COM 		    &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri);
100310696SDavid.Hollister@Sun.COM 	}
100410696SDavid.Hollister@Sun.COM 
100510696SDavid.Hollister@Sun.COM 	/*
100610696SDavid.Hollister@Sun.COM 	 * Create one thread to deal with the updating of the interrupt
100710696SDavid.Hollister@Sun.COM 	 * coalescing timer.
100810696SDavid.Hollister@Sun.COM 	 */
100910696SDavid.Hollister@Sun.COM 	pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal,
101010696SDavid.Hollister@Sun.COM 	    pwp, 0, &p0, TS_RUN, minclsyspri);
101110696SDavid.Hollister@Sun.COM 
101210696SDavid.Hollister@Sun.COM 	/*
101310696SDavid.Hollister@Sun.COM 	 * Kick off the watchdog
101410696SDavid.Hollister@Sun.COM 	 */
101510696SDavid.Hollister@Sun.COM 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
101610696SDavid.Hollister@Sun.COM 	    drv_usectohz(PMCS_WATCH_INTERVAL));
101710696SDavid.Hollister@Sun.COM 	/*
101810696SDavid.Hollister@Sun.COM 	 * Do the SCSI attachment code (before starting phys)
101910696SDavid.Hollister@Sun.COM 	 */
102010696SDavid.Hollister@Sun.COM 	if (pmcs_scsa_init(pwp, &pmcs_dattr)) {
102110696SDavid.Hollister@Sun.COM 		goto failure;
102210696SDavid.Hollister@Sun.COM 	}
102310696SDavid.Hollister@Sun.COM 	pwp->hba_attached = 1;
102410696SDavid.Hollister@Sun.COM 
102510696SDavid.Hollister@Sun.COM 	/* Check all acc & dma handles allocated in attach */
102610696SDavid.Hollister@Sun.COM 	if (pmcs_check_acc_dma_handle(pwp)) {
102710696SDavid.Hollister@Sun.COM 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
102810696SDavid.Hollister@Sun.COM 		goto failure;
102910696SDavid.Hollister@Sun.COM 	}
103010696SDavid.Hollister@Sun.COM 
103110696SDavid.Hollister@Sun.COM 	/*
103212399SChris.Horne@Sun.COM 	 * Create the iportmap for this HBA instance
103312399SChris.Horne@Sun.COM 	 */
103412399SChris.Horne@Sun.COM 	if (scsi_hba_iportmap_create(dip, iportmap_csync_usec,
103512399SChris.Horne@Sun.COM 	    iportmap_stable_usec, &pwp->hss_iportmap) != DDI_SUCCESS) {
103612399SChris.Horne@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
103712399SChris.Horne@Sun.COM 		    "%s: pmcs%d iportmap_create failed", __func__, inst);
103812399SChris.Horne@Sun.COM 		goto failure;
103912399SChris.Horne@Sun.COM 	}
104012399SChris.Horne@Sun.COM 	ASSERT(pwp->hss_iportmap);
104112399SChris.Horne@Sun.COM 
104212399SChris.Horne@Sun.COM 	/*
104310696SDavid.Hollister@Sun.COM 	 * Create the phymap for this HBA instance
104410696SDavid.Hollister@Sun.COM 	 */
104512207SChris.Horne@Sun.COM 	if (sas_phymap_create(dip, phymap_stable_usec, PHYMAP_MODE_SIMPLE, NULL,
104610696SDavid.Hollister@Sun.COM 	    pwp, pmcs_phymap_activate, pmcs_phymap_deactivate,
104710696SDavid.Hollister@Sun.COM 	    &pwp->hss_phymap) != DDI_SUCCESS) {
104811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
104911048SDavid.Hollister@Sun.COM 		    "%s: pmcs%d phymap_create failed", __func__, inst);
105010696SDavid.Hollister@Sun.COM 		goto failure;
105110696SDavid.Hollister@Sun.COM 	}
105210696SDavid.Hollister@Sun.COM 	ASSERT(pwp->hss_phymap);
105310696SDavid.Hollister@Sun.COM 
105410696SDavid.Hollister@Sun.COM 	/*
105510696SDavid.Hollister@Sun.COM 	 * Start the PHYs.
105610696SDavid.Hollister@Sun.COM 	 */
105710696SDavid.Hollister@Sun.COM 	if (pmcs_start_phys(pwp)) {
105810696SDavid.Hollister@Sun.COM 		goto failure;
105910696SDavid.Hollister@Sun.COM 	}
106010696SDavid.Hollister@Sun.COM 
106110696SDavid.Hollister@Sun.COM 	/*
106210696SDavid.Hollister@Sun.COM 	 * From this point on, we can't fail.
106310696SDavid.Hollister@Sun.COM 	 */
106410696SDavid.Hollister@Sun.COM 	ddi_report_dev(dip);
106510696SDavid.Hollister@Sun.COM 
106610696SDavid.Hollister@Sun.COM 	/* SM-HBA */
106710696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED,
106810696SDavid.Hollister@Sun.COM 	    &sm_hba);
106910696SDavid.Hollister@Sun.COM 
107010696SDavid.Hollister@Sun.COM 	/* SM-HBA */
107110696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION,
107210696SDavid.Hollister@Sun.COM 	    pmcs_driver_rev);
107310696SDavid.Hollister@Sun.COM 
107410696SDavid.Hollister@Sun.COM 	/* SM-HBA */
107510696SDavid.Hollister@Sun.COM 	chiprev = 'A' + pwp->chiprev;
107610696SDavid.Hollister@Sun.COM 	(void) snprintf(hw_rev, 2, "%s", &chiprev);
107710696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION,
107810696SDavid.Hollister@Sun.COM 	    hw_rev);
107910696SDavid.Hollister@Sun.COM 
108010696SDavid.Hollister@Sun.COM 	/* SM-HBA */
108110696SDavid.Hollister@Sun.COM 	switch (PMCS_FW_TYPE(pwp)) {
108210696SDavid.Hollister@Sun.COM 	case PMCS_FW_TYPE_RELEASED:
108310696SDavid.Hollister@Sun.COM 		fwsupport = "Released";
108410696SDavid.Hollister@Sun.COM 		break;
108510696SDavid.Hollister@Sun.COM 	case PMCS_FW_TYPE_DEVELOPMENT:
108610696SDavid.Hollister@Sun.COM 		fwsupport = "Development";
108710696SDavid.Hollister@Sun.COM 		break;
108810696SDavid.Hollister@Sun.COM 	case PMCS_FW_TYPE_ALPHA:
108910696SDavid.Hollister@Sun.COM 		fwsupport = "Alpha";
109010696SDavid.Hollister@Sun.COM 		break;
109110696SDavid.Hollister@Sun.COM 	case PMCS_FW_TYPE_BETA:
109210696SDavid.Hollister@Sun.COM 		fwsupport = "Beta";
109310696SDavid.Hollister@Sun.COM 		break;
109410696SDavid.Hollister@Sun.COM 	default:
109510696SDavid.Hollister@Sun.COM 		fwsupport = "Special";
109610696SDavid.Hollister@Sun.COM 		break;
109710696SDavid.Hollister@Sun.COM 	}
109810696SDavid.Hollister@Sun.COM 	(void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s",
109910696SDavid.Hollister@Sun.COM 	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
110010696SDavid.Hollister@Sun.COM 	    fwsupport);
110110696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION,
110210696SDavid.Hollister@Sun.COM 	    fw_rev);
110310696SDavid.Hollister@Sun.COM 
110410696SDavid.Hollister@Sun.COM 	/* SM-HBA */
110510696SDavid.Hollister@Sun.COM 	num_phys = pwp->nphy;
110610696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA,
110710696SDavid.Hollister@Sun.COM 	    &num_phys);
110810696SDavid.Hollister@Sun.COM 
110910696SDavid.Hollister@Sun.COM 	/* SM-HBA */
111010696SDavid.Hollister@Sun.COM 	protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
111110696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL,
111210696SDavid.Hollister@Sun.COM 	    &protocol);
111310696SDavid.Hollister@Sun.COM 
111412060SDavid.Hollister@Sun.COM 	/* Receptacle properties (FMA) */
111512060SDavid.Hollister@Sun.COM 	pwp->recept_labels[0] = PMCS_RECEPT_LABEL_0;
111612060SDavid.Hollister@Sun.COM 	pwp->recept_pm[0] = PMCS_RECEPT_PM_0;
111712060SDavid.Hollister@Sun.COM 	pwp->recept_labels[1] = PMCS_RECEPT_LABEL_1;
111812060SDavid.Hollister@Sun.COM 	pwp->recept_pm[1] = PMCS_RECEPT_PM_1;
111912060SDavid.Hollister@Sun.COM 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
112012060SDavid.Hollister@Sun.COM 	    SCSI_HBA_PROP_RECEPTACLE_LABEL, &pwp->recept_labels[0],
112112060SDavid.Hollister@Sun.COM 	    PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) {
112212060SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
112312060SDavid.Hollister@Sun.COM 		    "%s: failed to create %s property", __func__,
112412060SDavid.Hollister@Sun.COM 		    "receptacle-label");
112512060SDavid.Hollister@Sun.COM 	}
112612120SDavid.Hollister@Sun.COM 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
112712060SDavid.Hollister@Sun.COM 	    SCSI_HBA_PROP_RECEPTACLE_PM, &pwp->recept_pm[0],
112812060SDavid.Hollister@Sun.COM 	    PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) {
112912060SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
113012060SDavid.Hollister@Sun.COM 		    "%s: failed to create %s property", __func__,
113112060SDavid.Hollister@Sun.COM 		    "receptacle-pm");
113212060SDavid.Hollister@Sun.COM 	}
113312060SDavid.Hollister@Sun.COM 
113410696SDavid.Hollister@Sun.COM 	return (DDI_SUCCESS);
113510696SDavid.Hollister@Sun.COM 
113610696SDavid.Hollister@Sun.COM failure:
113710696SDavid.Hollister@Sun.COM 	if (pmcs_unattach(pwp)) {
113810696SDavid.Hollister@Sun.COM 		pwp->stuck = 1;
113910696SDavid.Hollister@Sun.COM 	}
114010696SDavid.Hollister@Sun.COM 	return (DDI_FAILURE);
114110696SDavid.Hollister@Sun.COM }
114210696SDavid.Hollister@Sun.COM 
114310696SDavid.Hollister@Sun.COM int
pmcs_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)114410696SDavid.Hollister@Sun.COM pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
114510696SDavid.Hollister@Sun.COM {
114610696SDavid.Hollister@Sun.COM 	int inst = ddi_get_instance(dip);
114710696SDavid.Hollister@Sun.COM 	pmcs_iport_t	*iport = NULL;
114810696SDavid.Hollister@Sun.COM 	pmcs_hw_t	*pwp = NULL;
114910696SDavid.Hollister@Sun.COM 	scsi_hba_tran_t	*tran;
115010696SDavid.Hollister@Sun.COM 
115110696SDavid.Hollister@Sun.COM 	if (scsi_hba_iport_unit_address(dip) != NULL) {
115210696SDavid.Hollister@Sun.COM 		/* iport node */
115310696SDavid.Hollister@Sun.COM 		iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
115410696SDavid.Hollister@Sun.COM 		ASSERT(iport);
115510696SDavid.Hollister@Sun.COM 		if (iport == NULL) {
115610696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
115710696SDavid.Hollister@Sun.COM 		}
115810696SDavid.Hollister@Sun.COM 		pwp = iport->pwp;
115910696SDavid.Hollister@Sun.COM 	} else {
116010696SDavid.Hollister@Sun.COM 		/* hba node */
116110696SDavid.Hollister@Sun.COM 		pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst);
116210696SDavid.Hollister@Sun.COM 		ASSERT(pwp);
116310696SDavid.Hollister@Sun.COM 		if (pwp == NULL) {
116410696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
116510696SDavid.Hollister@Sun.COM 		}
116610696SDavid.Hollister@Sun.COM 	}
116710696SDavid.Hollister@Sun.COM 	switch (cmd) {
116810696SDavid.Hollister@Sun.COM 	case DDI_DETACH:
116910696SDavid.Hollister@Sun.COM 		if (iport) {
117010696SDavid.Hollister@Sun.COM 			/* iport detach */
117110696SDavid.Hollister@Sun.COM 			if (pmcs_iport_unattach(iport)) {
117210696SDavid.Hollister@Sun.COM 				return (DDI_FAILURE);
117310696SDavid.Hollister@Sun.COM 			}
117411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
117511048SDavid.Hollister@Sun.COM 			    "iport%d detached", inst);
117610696SDavid.Hollister@Sun.COM 			return (DDI_SUCCESS);
117710696SDavid.Hollister@Sun.COM 		} else {
117810696SDavid.Hollister@Sun.COM 			/* HBA detach */
117910696SDavid.Hollister@Sun.COM 			if (pmcs_unattach(pwp)) {
118010696SDavid.Hollister@Sun.COM 				return (DDI_FAILURE);
118110696SDavid.Hollister@Sun.COM 			}
118210696SDavid.Hollister@Sun.COM 			return (DDI_SUCCESS);
118310696SDavid.Hollister@Sun.COM 		}
118410696SDavid.Hollister@Sun.COM 
118510696SDavid.Hollister@Sun.COM 	case DDI_SUSPEND:
118610696SDavid.Hollister@Sun.COM 	case DDI_PM_SUSPEND:
118710696SDavid.Hollister@Sun.COM 		/* No DDI_SUSPEND on iport nodes */
118810696SDavid.Hollister@Sun.COM 		if (iport) {
118910696SDavid.Hollister@Sun.COM 			return (DDI_SUCCESS);
119010696SDavid.Hollister@Sun.COM 		}
119110696SDavid.Hollister@Sun.COM 
119210696SDavid.Hollister@Sun.COM 		if (pwp->stuck) {
119310696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
119410696SDavid.Hollister@Sun.COM 		}
119510696SDavid.Hollister@Sun.COM 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
119610696SDavid.Hollister@Sun.COM 		if (!tran) {
119710696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
119810696SDavid.Hollister@Sun.COM 		}
119910696SDavid.Hollister@Sun.COM 
120010696SDavid.Hollister@Sun.COM 		pwp = TRAN2PMC(tran);
120110696SDavid.Hollister@Sun.COM 		if (pwp == NULL) {
120210696SDavid.Hollister@Sun.COM 			return (DDI_FAILURE);
120310696SDavid.Hollister@Sun.COM 		}
120410696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
120510696SDavid.Hollister@Sun.COM 		if (pwp->tq) {
120610696SDavid.Hollister@Sun.COM 			ddi_taskq_suspend(pwp->tq);
120710696SDavid.Hollister@Sun.COM 		}
120810696SDavid.Hollister@Sun.COM 		pwp->suspended = 1;
120910696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
121011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PMC8X6G suspending");
121110696SDavid.Hollister@Sun.COM 		return (DDI_SUCCESS);
121210696SDavid.Hollister@Sun.COM 
121310696SDavid.Hollister@Sun.COM 	default:
121410696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
121510696SDavid.Hollister@Sun.COM 	}
121610696SDavid.Hollister@Sun.COM }
121710696SDavid.Hollister@Sun.COM 
121810696SDavid.Hollister@Sun.COM static int
pmcs_iport_unattach(pmcs_iport_t * iport)121910696SDavid.Hollister@Sun.COM pmcs_iport_unattach(pmcs_iport_t *iport)
122010696SDavid.Hollister@Sun.COM {
122110696SDavid.Hollister@Sun.COM 	pmcs_hw_t	*pwp = iport->pwp;
122210696SDavid.Hollister@Sun.COM 
122310696SDavid.Hollister@Sun.COM 	/*
122410696SDavid.Hollister@Sun.COM 	 * First, check if there are still any configured targets on this
122510696SDavid.Hollister@Sun.COM 	 * iport.  If so, we fail detach.
122610696SDavid.Hollister@Sun.COM 	 */
122710696SDavid.Hollister@Sun.COM 	if (pmcs_iport_has_targets(pwp, iport)) {
122811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
122911048SDavid.Hollister@Sun.COM 		    "iport%d detach failure: iport has targets (luns)",
123011048SDavid.Hollister@Sun.COM 		    ddi_get_instance(iport->dip));
123110696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
123210696SDavid.Hollister@Sun.COM 	}
123310696SDavid.Hollister@Sun.COM 
123410696SDavid.Hollister@Sun.COM 	/*
123510696SDavid.Hollister@Sun.COM 	 * Remove this iport from our list if it is inactive in the phymap.
123610696SDavid.Hollister@Sun.COM 	 */
123710696SDavid.Hollister@Sun.COM 	rw_enter(&pwp->iports_lock, RW_WRITER);
123810696SDavid.Hollister@Sun.COM 	mutex_enter(&iport->lock);
123910696SDavid.Hollister@Sun.COM 
124012399SChris.Horne@Sun.COM 	if ((iport->ua_state == UA_ACTIVE) &&
124112399SChris.Horne@Sun.COM 	    (detach_with_active_port == 0)) {
124210696SDavid.Hollister@Sun.COM 		mutex_exit(&iport->lock);
124310696SDavid.Hollister@Sun.COM 		rw_exit(&pwp->iports_lock);
124411048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
124511048SDavid.Hollister@Sun.COM 		    "iport%d detach failure: "
124610696SDavid.Hollister@Sun.COM 		    "iport unit address active in phymap",
124710696SDavid.Hollister@Sun.COM 		    ddi_get_instance(iport->dip));
124810696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
124910696SDavid.Hollister@Sun.COM 	}
125010696SDavid.Hollister@Sun.COM 
125110696SDavid.Hollister@Sun.COM 	/* If it's our only iport, clear iports_attached */
125210696SDavid.Hollister@Sun.COM 	ASSERT(pwp->num_iports >= 1);
125310696SDavid.Hollister@Sun.COM 	if (--pwp->num_iports == 0) {
125410696SDavid.Hollister@Sun.COM 		pwp->iports_attached = 0;
125510696SDavid.Hollister@Sun.COM 	}
125610696SDavid.Hollister@Sun.COM 
125710696SDavid.Hollister@Sun.COM 	ASSERT(list_link_active(&iport->list_node));
125810696SDavid.Hollister@Sun.COM 	list_remove(&pwp->iports, iport);
125910696SDavid.Hollister@Sun.COM 	rw_exit(&pwp->iports_lock);
126010696SDavid.Hollister@Sun.COM 
126110696SDavid.Hollister@Sun.COM 	/*
126210696SDavid.Hollister@Sun.COM 	 * We have removed the iport handle from the HBA's iports list,
126310696SDavid.Hollister@Sun.COM 	 * there will be no new references to it. Two things must be
126410696SDavid.Hollister@Sun.COM 	 * guarded against here.  First, we could have PHY up events,
126510696SDavid.Hollister@Sun.COM 	 * adding themselves to the iport->phys list and grabbing ref's
126610696SDavid.Hollister@Sun.COM 	 * on our iport handle.  Second, we could have existing references
126710696SDavid.Hollister@Sun.COM 	 * to this iport handle from a point in time prior to the list
126810696SDavid.Hollister@Sun.COM 	 * removal above.
126910696SDavid.Hollister@Sun.COM 	 *
127010696SDavid.Hollister@Sun.COM 	 * So first, destroy the phys list. Remove any phys that have snuck
127110696SDavid.Hollister@Sun.COM 	 * in after the phymap deactivate, dropping the refcnt accordingly.
127210696SDavid.Hollister@Sun.COM 	 * If these PHYs are still up if and when the phymap reactivates
127310696SDavid.Hollister@Sun.COM 	 * (i.e. when this iport reattaches), we'll populate the list with
127410696SDavid.Hollister@Sun.COM 	 * them and bump the refcnt back up.
127510696SDavid.Hollister@Sun.COM 	 */
127610696SDavid.Hollister@Sun.COM 	pmcs_remove_phy_from_iport(iport, NULL);
127710696SDavid.Hollister@Sun.COM 	ASSERT(list_is_empty(&iport->phys));
127810696SDavid.Hollister@Sun.COM 	list_destroy(&iport->phys);
127910696SDavid.Hollister@Sun.COM 	mutex_exit(&iport->lock);
128010696SDavid.Hollister@Sun.COM 
128110696SDavid.Hollister@Sun.COM 	/*
128210696SDavid.Hollister@Sun.COM 	 * Second, wait for any other references to this iport to be
128310696SDavid.Hollister@Sun.COM 	 * dropped, then continue teardown.
128410696SDavid.Hollister@Sun.COM 	 */
128510696SDavid.Hollister@Sun.COM 	mutex_enter(&iport->refcnt_lock);
128610696SDavid.Hollister@Sun.COM 	while (iport->refcnt != 0) {
128710696SDavid.Hollister@Sun.COM 		cv_wait(&iport->refcnt_cv, &iport->refcnt_lock);
128810696SDavid.Hollister@Sun.COM 	}
128910696SDavid.Hollister@Sun.COM 	mutex_exit(&iport->refcnt_lock);
129010696SDavid.Hollister@Sun.COM 
129110696SDavid.Hollister@Sun.COM 
129210696SDavid.Hollister@Sun.COM 	/* Destroy the iport target map */
129310696SDavid.Hollister@Sun.COM 	if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) {
129410696SDavid.Hollister@Sun.COM 		return (DDI_FAILURE);
129510696SDavid.Hollister@Sun.COM 	}
129610696SDavid.Hollister@Sun.COM 
129710696SDavid.Hollister@Sun.COM 	/* Free the tgt soft state */
129810696SDavid.Hollister@Sun.COM 	if (iport->tgt_sstate != NULL) {
129910696SDavid.Hollister@Sun.COM 		ddi_soft_state_bystr_fini(&iport->tgt_sstate);
130010696SDavid.Hollister@Sun.COM 	}
130110696SDavid.Hollister@Sun.COM 
130210696SDavid.Hollister@Sun.COM 	/* Free our unit address string */
130310696SDavid.Hollister@Sun.COM 	strfree(iport->ua);
130410696SDavid.Hollister@Sun.COM 
130510696SDavid.Hollister@Sun.COM 	/* Finish teardown and free the softstate */
130610696SDavid.Hollister@Sun.COM 	mutex_destroy(&iport->refcnt_lock);
130711267SJesse.Butler@Sun.COM 	mutex_destroy(&iport->smp_lock);
130810696SDavid.Hollister@Sun.COM 	ASSERT(iport->refcnt == 0);
130910696SDavid.Hollister@Sun.COM 	cv_destroy(&iport->refcnt_cv);
131011267SJesse.Butler@Sun.COM 	cv_destroy(&iport->smp_cv);
131110696SDavid.Hollister@Sun.COM 	mutex_destroy(&iport->lock);
131210696SDavid.Hollister@Sun.COM 	ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip));
131310696SDavid.Hollister@Sun.COM 
131410696SDavid.Hollister@Sun.COM 	return (DDI_SUCCESS);
131510696SDavid.Hollister@Sun.COM }
131610696SDavid.Hollister@Sun.COM 
131710696SDavid.Hollister@Sun.COM static int
pmcs_unattach(pmcs_hw_t * pwp)131810696SDavid.Hollister@Sun.COM pmcs_unattach(pmcs_hw_t *pwp)
131910696SDavid.Hollister@Sun.COM {
132010696SDavid.Hollister@Sun.COM 	int i;
132110696SDavid.Hollister@Sun.COM 	enum pwpstate curstate;
132210696SDavid.Hollister@Sun.COM 	pmcs_cq_thr_info_t *cqti;
132310696SDavid.Hollister@Sun.COM 
132410696SDavid.Hollister@Sun.COM 	/*
132510696SDavid.Hollister@Sun.COM 	 * Tear down the interrupt infrastructure.
132610696SDavid.Hollister@Sun.COM 	 */
132710696SDavid.Hollister@Sun.COM 	if (pmcs_teardown_intr(pwp)) {
132810696SDavid.Hollister@Sun.COM 		pwp->stuck = 1;
132910696SDavid.Hollister@Sun.COM 	}
133010696SDavid.Hollister@Sun.COM 	pwp->intr_cnt = 0;
133110696SDavid.Hollister@Sun.COM 
133210696SDavid.Hollister@Sun.COM 	/*
133310696SDavid.Hollister@Sun.COM 	 * Grab a lock, if initted, to set state.
133410696SDavid.Hollister@Sun.COM 	 */
133510696SDavid.Hollister@Sun.COM 	if (pwp->locks_initted) {
133610696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
133710696SDavid.Hollister@Sun.COM 		if (pwp->state != STATE_DEAD) {
133810696SDavid.Hollister@Sun.COM 			pwp->state = STATE_UNPROBING;
133910696SDavid.Hollister@Sun.COM 		}
134010696SDavid.Hollister@Sun.COM 		curstate = pwp->state;
134110696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
134210696SDavid.Hollister@Sun.COM 
134310696SDavid.Hollister@Sun.COM 		/*
134410696SDavid.Hollister@Sun.COM 		 * Stop the I/O completion threads.
134510696SDavid.Hollister@Sun.COM 		 */
134610696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->cq_lock);
134710696SDavid.Hollister@Sun.COM 		pwp->cq_info.cq_stop = B_TRUE;
134810696SDavid.Hollister@Sun.COM 		for (i = 0; i < pwp->cq_info.cq_threads; i++) {
134910696SDavid.Hollister@Sun.COM 			if (pwp->cq_info.cq_thr_info[i].cq_thread) {
135010696SDavid.Hollister@Sun.COM 				cqti = &pwp->cq_info.cq_thr_info[i];
135110696SDavid.Hollister@Sun.COM 				mutex_enter(&cqti->cq_thr_lock);
135210696SDavid.Hollister@Sun.COM 				cv_signal(&cqti->cq_cv);
135310696SDavid.Hollister@Sun.COM 				mutex_exit(&cqti->cq_thr_lock);
135410696SDavid.Hollister@Sun.COM 				mutex_exit(&pwp->cq_lock);
135510696SDavid.Hollister@Sun.COM 				thread_join(cqti->cq_thread->t_did);
135610696SDavid.Hollister@Sun.COM 				mutex_enter(&pwp->cq_lock);
135710696SDavid.Hollister@Sun.COM 			}
135810696SDavid.Hollister@Sun.COM 		}
135910696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->cq_lock);
136012399SChris.Horne@Sun.COM 		kmem_free(pwp->cq_info.cq_thr_info,
136112399SChris.Horne@Sun.COM 		    sizeof (pmcs_cq_thr_info_t) * pwp->cq_info.cq_threads);
136210696SDavid.Hollister@Sun.COM 
136310696SDavid.Hollister@Sun.COM 		/*
136410696SDavid.Hollister@Sun.COM 		 * Stop the interrupt coalescing timer thread
136510696SDavid.Hollister@Sun.COM 		 */
136610696SDavid.Hollister@Sun.COM 		if (pwp->ict_thread) {
136710696SDavid.Hollister@Sun.COM 			mutex_enter(&pwp->ict_lock);
136810696SDavid.Hollister@Sun.COM 			pwp->io_intr_coal.stop_thread = B_TRUE;
136910696SDavid.Hollister@Sun.COM 			cv_signal(&pwp->ict_cv);
137010696SDavid.Hollister@Sun.COM 			mutex_exit(&pwp->ict_lock);
137110696SDavid.Hollister@Sun.COM 			thread_join(pwp->ict_thread->t_did);
137210696SDavid.Hollister@Sun.COM 		}
137310696SDavid.Hollister@Sun.COM 	} else {
137410696SDavid.Hollister@Sun.COM 		if (pwp->state != STATE_DEAD) {
137510696SDavid.Hollister@Sun.COM 			pwp->state = STATE_UNPROBING;
137610696SDavid.Hollister@Sun.COM 		}
137710696SDavid.Hollister@Sun.COM 		curstate = pwp->state;
137810696SDavid.Hollister@Sun.COM 	}
137910696SDavid.Hollister@Sun.COM 
138010696SDavid.Hollister@Sun.COM 	/*
138110696SDavid.Hollister@Sun.COM 	 * Make sure that any pending watchdog won't
138210696SDavid.Hollister@Sun.COM 	 * be called from this point on out.
138310696SDavid.Hollister@Sun.COM 	 */
138410696SDavid.Hollister@Sun.COM 	(void) untimeout(pwp->wdhandle);
138510696SDavid.Hollister@Sun.COM 	/*
138610696SDavid.Hollister@Sun.COM 	 * After the above action, the watchdog
138710696SDavid.Hollister@Sun.COM 	 * timer that starts up the worker task
138810696SDavid.Hollister@Sun.COM 	 * may trigger but will exit immediately
138910696SDavid.Hollister@Sun.COM 	 * on triggering.
139010696SDavid.Hollister@Sun.COM 	 *
139110696SDavid.Hollister@Sun.COM 	 * Now that this is done, we can destroy
139210696SDavid.Hollister@Sun.COM 	 * the task queue, which will wait if we're
139310696SDavid.Hollister@Sun.COM 	 * running something on it.
139410696SDavid.Hollister@Sun.COM 	 */
139510696SDavid.Hollister@Sun.COM 	if (pwp->tq) {
139610696SDavid.Hollister@Sun.COM 		ddi_taskq_destroy(pwp->tq);
139710696SDavid.Hollister@Sun.COM 		pwp->tq = NULL;
139810696SDavid.Hollister@Sun.COM 	}
139910696SDavid.Hollister@Sun.COM 
140010696SDavid.Hollister@Sun.COM 	pmcs_fm_fini(pwp);
140110696SDavid.Hollister@Sun.COM 
140210696SDavid.Hollister@Sun.COM 	if (pwp->hba_attached) {
140310696SDavid.Hollister@Sun.COM 		(void) scsi_hba_detach(pwp->dip);
140410696SDavid.Hollister@Sun.COM 		pwp->hba_attached = 0;
140510696SDavid.Hollister@Sun.COM 	}
140610696SDavid.Hollister@Sun.COM 
140710696SDavid.Hollister@Sun.COM 	/*
140810696SDavid.Hollister@Sun.COM 	 * If the chip hasn't been marked dead, shut it down now
140910696SDavid.Hollister@Sun.COM 	 * to bring it back to a known state without attempting
141010696SDavid.Hollister@Sun.COM 	 * a soft reset.
141110696SDavid.Hollister@Sun.COM 	 */
141210696SDavid.Hollister@Sun.COM 	if (curstate != STATE_DEAD && pwp->locks_initted) {
141310696SDavid.Hollister@Sun.COM 		/*
141410696SDavid.Hollister@Sun.COM 		 * De-register all registered devices
141510696SDavid.Hollister@Sun.COM 		 */
141610696SDavid.Hollister@Sun.COM 		pmcs_deregister_devices(pwp, pwp->root_phys);
141710696SDavid.Hollister@Sun.COM 
141810696SDavid.Hollister@Sun.COM 		/*
141910696SDavid.Hollister@Sun.COM 		 * Stop all the phys.
142010696SDavid.Hollister@Sun.COM 		 */
142110696SDavid.Hollister@Sun.COM 		pmcs_stop_phys(pwp);
142210696SDavid.Hollister@Sun.COM 
142310696SDavid.Hollister@Sun.COM 		/*
142410696SDavid.Hollister@Sun.COM 		 * Shut Down Message Passing
142510696SDavid.Hollister@Sun.COM 		 */
142610696SDavid.Hollister@Sun.COM 		(void) pmcs_stop_mpi(pwp);
142710696SDavid.Hollister@Sun.COM 
142810696SDavid.Hollister@Sun.COM 		/*
142910696SDavid.Hollister@Sun.COM 		 * Reset chip
143010696SDavid.Hollister@Sun.COM 		 */
143110696SDavid.Hollister@Sun.COM 		(void) pmcs_soft_reset(pwp, B_FALSE);
143211692SJesse.Butler@Sun.COM 		pwp->last_reset_reason = PMCS_LAST_RST_DETACH;
143310696SDavid.Hollister@Sun.COM 	}
143410696SDavid.Hollister@Sun.COM 
143510696SDavid.Hollister@Sun.COM 	/*
143610696SDavid.Hollister@Sun.COM 	 * Turn off interrupts on the chip
143710696SDavid.Hollister@Sun.COM 	 */
143810696SDavid.Hollister@Sun.COM 	if (pwp->mpi_acc_handle) {
143910696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
144010696SDavid.Hollister@Sun.COM 	}
144110696SDavid.Hollister@Sun.COM 
144212399SChris.Horne@Sun.COM 	if (pwp->hss_phymap != NULL) {
144312399SChris.Horne@Sun.COM 		/* Destroy the phymap */
144412399SChris.Horne@Sun.COM 		sas_phymap_destroy(pwp->hss_phymap);
144512399SChris.Horne@Sun.COM 	}
144612399SChris.Horne@Sun.COM 
144712078SJesse.Butler@Sun.COM 	if (pwp->hss_iportmap != NULL) {
144812078SJesse.Butler@Sun.COM 		/* Destroy the iportmap */
144912078SJesse.Butler@Sun.COM 		scsi_hba_iportmap_destroy(pwp->hss_iportmap);
145012078SJesse.Butler@Sun.COM 	}
145112078SJesse.Butler@Sun.COM 
145212078SJesse.Butler@Sun.COM 	/* Destroy the iports lock and list */
145312078SJesse.Butler@Sun.COM 	rw_destroy(&pwp->iports_lock);
145412078SJesse.Butler@Sun.COM 	ASSERT(list_is_empty(&pwp->iports));
145512078SJesse.Butler@Sun.COM 	list_destroy(&pwp->iports);
145612078SJesse.Butler@Sun.COM 
145710696SDavid.Hollister@Sun.COM 	/*
145810696SDavid.Hollister@Sun.COM 	 * Free DMA handles and associated consistent memory
145910696SDavid.Hollister@Sun.COM 	 */
146010696SDavid.Hollister@Sun.COM 	if (pwp->regdump_hndl) {
146110696SDavid.Hollister@Sun.COM 		if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) {
146211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
146311048SDavid.Hollister@Sun.COM 			    "Condition check failed "
146410696SDavid.Hollister@Sun.COM 			    "at %s():%d", __func__, __LINE__);
146510696SDavid.Hollister@Sun.COM 		}
146610696SDavid.Hollister@Sun.COM 		ddi_dma_free_handle(&pwp->regdump_hndl);
146710696SDavid.Hollister@Sun.COM 		ddi_dma_mem_free(&pwp->regdump_acchdl);
146810696SDavid.Hollister@Sun.COM 		pwp->regdump_hndl = 0;
146910696SDavid.Hollister@Sun.COM 	}
147010696SDavid.Hollister@Sun.COM 	if (pwp->fwlog_hndl) {
147110696SDavid.Hollister@Sun.COM 		if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) {
147211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
147311048SDavid.Hollister@Sun.COM 			    "Condition check failed "
147410696SDavid.Hollister@Sun.COM 			    "at %s():%d", __func__, __LINE__);
147510696SDavid.Hollister@Sun.COM 		}
147610696SDavid.Hollister@Sun.COM 		ddi_dma_free_handle(&pwp->fwlog_hndl);
147710696SDavid.Hollister@Sun.COM 		ddi_dma_mem_free(&pwp->fwlog_acchdl);
147810696SDavid.Hollister@Sun.COM 		pwp->fwlog_hndl = 0;
147910696SDavid.Hollister@Sun.COM 	}
148010696SDavid.Hollister@Sun.COM 	if (pwp->cip_handles) {
148110696SDavid.Hollister@Sun.COM 		if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) {
148211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
148311048SDavid.Hollister@Sun.COM 			    "Condition check failed "
148410696SDavid.Hollister@Sun.COM 			    "at %s():%d", __func__, __LINE__);
148510696SDavid.Hollister@Sun.COM 		}
148610696SDavid.Hollister@Sun.COM 		ddi_dma_free_handle(&pwp->cip_handles);
148710696SDavid.Hollister@Sun.COM 		ddi_dma_mem_free(&pwp->cip_acchdls);
148810696SDavid.Hollister@Sun.COM 		pwp->cip_handles = 0;
148910696SDavid.Hollister@Sun.COM 	}
149010696SDavid.Hollister@Sun.COM 	for (i = 0; i < PMCS_NOQ; i++) {
149110696SDavid.Hollister@Sun.COM 		if (pwp->oqp_handles[i]) {
149210696SDavid.Hollister@Sun.COM 			if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) !=
149310696SDavid.Hollister@Sun.COM 			    DDI_SUCCESS) {
149411048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
149511048SDavid.Hollister@Sun.COM 				    "Condition check failed at %s():%d",
149611048SDavid.Hollister@Sun.COM 				    __func__, __LINE__);
149710696SDavid.Hollister@Sun.COM 			}
149810696SDavid.Hollister@Sun.COM 			ddi_dma_free_handle(&pwp->oqp_handles[i]);
149910696SDavid.Hollister@Sun.COM 			ddi_dma_mem_free(&pwp->oqp_acchdls[i]);
150010696SDavid.Hollister@Sun.COM 			pwp->oqp_handles[i] = 0;
150110696SDavid.Hollister@Sun.COM 		}
150210696SDavid.Hollister@Sun.COM 	}
150310696SDavid.Hollister@Sun.COM 	for (i = 0; i < PMCS_NIQ; i++) {
150410696SDavid.Hollister@Sun.COM 		if (pwp->iqp_handles[i]) {
150510696SDavid.Hollister@Sun.COM 			if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) !=
150610696SDavid.Hollister@Sun.COM 			    DDI_SUCCESS) {
150711048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
150811048SDavid.Hollister@Sun.COM 				    "Condition check failed at %s():%d",
150911048SDavid.Hollister@Sun.COM 				    __func__, __LINE__);
151010696SDavid.Hollister@Sun.COM 			}
151110696SDavid.Hollister@Sun.COM 			ddi_dma_free_handle(&pwp->iqp_handles[i]);
151210696SDavid.Hollister@Sun.COM 			ddi_dma_mem_free(&pwp->iqp_acchdls[i]);
151310696SDavid.Hollister@Sun.COM 			pwp->iqp_handles[i] = 0;
151410696SDavid.Hollister@Sun.COM 		}
151510696SDavid.Hollister@Sun.COM 	}
151610696SDavid.Hollister@Sun.COM 
151710696SDavid.Hollister@Sun.COM 	pmcs_free_dma_chunklist(pwp);
151810696SDavid.Hollister@Sun.COM 
151910696SDavid.Hollister@Sun.COM 	/*
152010696SDavid.Hollister@Sun.COM 	 * Unmap registers and destroy access handles
152110696SDavid.Hollister@Sun.COM 	 */
152210696SDavid.Hollister@Sun.COM 	if (pwp->mpi_acc_handle) {
152310696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->mpi_acc_handle);
152410696SDavid.Hollister@Sun.COM 		pwp->mpi_acc_handle = 0;
152510696SDavid.Hollister@Sun.COM 	}
152610696SDavid.Hollister@Sun.COM 	if (pwp->top_acc_handle) {
152710696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->top_acc_handle);
152810696SDavid.Hollister@Sun.COM 		pwp->top_acc_handle = 0;
152910696SDavid.Hollister@Sun.COM 	}
153010696SDavid.Hollister@Sun.COM 	if (pwp->gsm_acc_handle) {
153110696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->gsm_acc_handle);
153210696SDavid.Hollister@Sun.COM 		pwp->gsm_acc_handle = 0;
153310696SDavid.Hollister@Sun.COM 	}
153410696SDavid.Hollister@Sun.COM 	if (pwp->msg_acc_handle) {
153510696SDavid.Hollister@Sun.COM 		ddi_regs_map_free(&pwp->msg_acc_handle);
153610696SDavid.Hollister@Sun.COM 		pwp->msg_acc_handle = 0;
153710696SDavid.Hollister@Sun.COM 	}
153810696SDavid.Hollister@Sun.COM 	if (pwp->pci_acc_handle) {
153910696SDavid.Hollister@Sun.COM 		pci_config_teardown(&pwp->pci_acc_handle);
154010696SDavid.Hollister@Sun.COM 		pwp->pci_acc_handle = 0;
154110696SDavid.Hollister@Sun.COM 	}
154210696SDavid.Hollister@Sun.COM 
154310696SDavid.Hollister@Sun.COM 	/*
154410696SDavid.Hollister@Sun.COM 	 * Do memory allocation cleanup.
154510696SDavid.Hollister@Sun.COM 	 */
154610696SDavid.Hollister@Sun.COM 	while (pwp->dma_freelist) {
154710696SDavid.Hollister@Sun.COM 		pmcs_dmachunk_t *this = pwp->dma_freelist;
154810696SDavid.Hollister@Sun.COM 		pwp->dma_freelist = this->nxt;
154910696SDavid.Hollister@Sun.COM 		kmem_free(this, sizeof (pmcs_dmachunk_t));
155010696SDavid.Hollister@Sun.COM 	}
155110696SDavid.Hollister@Sun.COM 
155210696SDavid.Hollister@Sun.COM 	/*
155310696SDavid.Hollister@Sun.COM 	 * Free pools
155410696SDavid.Hollister@Sun.COM 	 */
155510696SDavid.Hollister@Sun.COM 	if (pwp->iocomp_cb_cache) {
155610696SDavid.Hollister@Sun.COM 		kmem_cache_destroy(pwp->iocomp_cb_cache);
155710696SDavid.Hollister@Sun.COM 	}
155810696SDavid.Hollister@Sun.COM 
155910696SDavid.Hollister@Sun.COM 	/*
156010696SDavid.Hollister@Sun.COM 	 * Free all PHYs (at level > 0), then free the cache
156110696SDavid.Hollister@Sun.COM 	 */
156210696SDavid.Hollister@Sun.COM 	pmcs_free_all_phys(pwp, pwp->root_phys);
156310696SDavid.Hollister@Sun.COM 	if (pwp->phy_cache) {
156410696SDavid.Hollister@Sun.COM 		kmem_cache_destroy(pwp->phy_cache);
156510696SDavid.Hollister@Sun.COM 	}
156610696SDavid.Hollister@Sun.COM 
156710696SDavid.Hollister@Sun.COM 	/*
156810696SDavid.Hollister@Sun.COM 	 * Free root PHYs
156910696SDavid.Hollister@Sun.COM 	 */
157010696SDavid.Hollister@Sun.COM 	if (pwp->root_phys) {
157110696SDavid.Hollister@Sun.COM 		pmcs_phy_t *phyp = pwp->root_phys;
157210696SDavid.Hollister@Sun.COM 		for (i = 0; i < pwp->nphy; i++) {
157310696SDavid.Hollister@Sun.COM 			mutex_destroy(&phyp->phy_lock);
157410696SDavid.Hollister@Sun.COM 			phyp = phyp->sibling;
157510696SDavid.Hollister@Sun.COM 		}
157610696SDavid.Hollister@Sun.COM 		kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t));
157710696SDavid.Hollister@Sun.COM 		pwp->root_phys = NULL;
157810696SDavid.Hollister@Sun.COM 		pwp->nphy = 0;
157910696SDavid.Hollister@Sun.COM 	}
158010696SDavid.Hollister@Sun.COM 
158110696SDavid.Hollister@Sun.COM 	/* Free the targets list */
158210696SDavid.Hollister@Sun.COM 	if (pwp->targets) {
158310696SDavid.Hollister@Sun.COM 		kmem_free(pwp->targets,
158410696SDavid.Hollister@Sun.COM 		    sizeof (pmcs_xscsi_t *) * pwp->max_dev);
158510696SDavid.Hollister@Sun.COM 	}
158610696SDavid.Hollister@Sun.COM 
158710696SDavid.Hollister@Sun.COM 	/*
158810696SDavid.Hollister@Sun.COM 	 * Free work structures
158910696SDavid.Hollister@Sun.COM 	 */
159010696SDavid.Hollister@Sun.COM 
159110696SDavid.Hollister@Sun.COM 	if (pwp->work && pwp->max_cmd) {
159212258Ssrikanth.suravajhala@oracle.com 		for (i = 0; i < pwp->max_cmd; i++) {
159310696SDavid.Hollister@Sun.COM 			pmcwork_t *pwrk = &pwp->work[i];
159410696SDavid.Hollister@Sun.COM 			mutex_destroy(&pwrk->lock);
159510696SDavid.Hollister@Sun.COM 			cv_destroy(&pwrk->sleep_cv);
159610696SDavid.Hollister@Sun.COM 		}
159710696SDavid.Hollister@Sun.COM 		kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
159810696SDavid.Hollister@Sun.COM 		pwp->work = NULL;
159910696SDavid.Hollister@Sun.COM 		pwp->max_cmd = 0;
160010696SDavid.Hollister@Sun.COM 	}
160110696SDavid.Hollister@Sun.COM 
160210696SDavid.Hollister@Sun.COM 	/*
160310696SDavid.Hollister@Sun.COM 	 * Do last property and SCSA cleanup
160410696SDavid.Hollister@Sun.COM 	 */
160512399SChris.Horne@Sun.COM 	if (pwp->smp_tran) {
160612399SChris.Horne@Sun.COM 		smp_hba_tran_free(pwp->smp_tran);
160712399SChris.Horne@Sun.COM 		pwp->smp_tran = NULL;
160812399SChris.Horne@Sun.COM 	}
160910696SDavid.Hollister@Sun.COM 	if (pwp->tran) {
161010696SDavid.Hollister@Sun.COM 		scsi_hba_tran_free(pwp->tran);
161110696SDavid.Hollister@Sun.COM 		pwp->tran = NULL;
161210696SDavid.Hollister@Sun.COM 	}
161310696SDavid.Hollister@Sun.COM 	if (pwp->reset_notify_listf) {
161410696SDavid.Hollister@Sun.COM 		scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf);
161510696SDavid.Hollister@Sun.COM 		pwp->reset_notify_listf = NULL;
161610696SDavid.Hollister@Sun.COM 	}
161710696SDavid.Hollister@Sun.COM 	ddi_prop_remove_all(pwp->dip);
161810696SDavid.Hollister@Sun.COM 	if (pwp->stuck) {
161910696SDavid.Hollister@Sun.COM 		return (-1);
162010696SDavid.Hollister@Sun.COM 	}
162110696SDavid.Hollister@Sun.COM 
162210696SDavid.Hollister@Sun.COM 	/* Free register dump area if allocated */
162310696SDavid.Hollister@Sun.COM 	if (pwp->regdumpp) {
162410696SDavid.Hollister@Sun.COM 		kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE);
162510696SDavid.Hollister@Sun.COM 		pwp->regdumpp = NULL;
162610696SDavid.Hollister@Sun.COM 	}
162710696SDavid.Hollister@Sun.COM 	if (pwp->iqpt && pwp->iqpt->head) {
162810696SDavid.Hollister@Sun.COM 		kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE);
162910696SDavid.Hollister@Sun.COM 		pwp->iqpt->head = pwp->iqpt->curpos = NULL;
163010696SDavid.Hollister@Sun.COM 	}
163110696SDavid.Hollister@Sun.COM 	if (pwp->iqpt) {
163210696SDavid.Hollister@Sun.COM 		kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t));
163310696SDavid.Hollister@Sun.COM 		pwp->iqpt = NULL;
163410696SDavid.Hollister@Sun.COM 	}
163510696SDavid.Hollister@Sun.COM 
163612399SChris.Horne@Sun.COM 	/* Destroy pwp's lock */
163712399SChris.Horne@Sun.COM 	if (pwp->locks_initted) {
163812399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->lock);
163912399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->dma_lock);
164012399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->axil_lock);
164112399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->cq_lock);
164212399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->config_lock);
164312399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->ict_lock);
164412399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->wfree_lock);
164512399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->pfree_lock);
164612399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->dead_phylist_lock);
164712399SChris.Horne@Sun.COM #ifdef	DEBUG
164812399SChris.Horne@Sun.COM 		mutex_destroy(&pwp->dbglock);
164912399SChris.Horne@Sun.COM #endif
165012399SChris.Horne@Sun.COM 		cv_destroy(&pwp->config_cv);
165112399SChris.Horne@Sun.COM 		cv_destroy(&pwp->ict_cv);
165212399SChris.Horne@Sun.COM 		cv_destroy(&pwp->drain_cv);
165312399SChris.Horne@Sun.COM 		pwp->locks_initted = 0;
165412399SChris.Horne@Sun.COM 	}
165512399SChris.Horne@Sun.COM 
165610696SDavid.Hollister@Sun.COM 	ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip));
165710696SDavid.Hollister@Sun.COM 	return (0);
165810696SDavid.Hollister@Sun.COM }
165910696SDavid.Hollister@Sun.COM 
166010696SDavid.Hollister@Sun.COM /*
166110696SDavid.Hollister@Sun.COM  * quiesce (9E) entry point
166210696SDavid.Hollister@Sun.COM  *
166310696SDavid.Hollister@Sun.COM  * This function is called when the system is single-threaded at high PIL
166410696SDavid.Hollister@Sun.COM  * with preemption disabled. Therefore, the function must not block/wait/sleep.
166510696SDavid.Hollister@Sun.COM  *
166610696SDavid.Hollister@Sun.COM  * Returns DDI_SUCCESS or DDI_FAILURE.
166710696SDavid.Hollister@Sun.COM  *
166810696SDavid.Hollister@Sun.COM  */
166910696SDavid.Hollister@Sun.COM static int
pmcs_quiesce(dev_info_t * dip)167010696SDavid.Hollister@Sun.COM pmcs_quiesce(dev_info_t *dip)
167110696SDavid.Hollister@Sun.COM {
167210696SDavid.Hollister@Sun.COM 	pmcs_hw_t	*pwp;
167310696SDavid.Hollister@Sun.COM 	scsi_hba_tran_t	*tran;
167410696SDavid.Hollister@Sun.COM 
167510696SDavid.Hollister@Sun.COM 	if ((tran = ddi_get_driver_private(dip)) == NULL)
167610696SDavid.Hollister@Sun.COM 		return (DDI_SUCCESS);
167710696SDavid.Hollister@Sun.COM 
167810696SDavid.Hollister@Sun.COM 	/* No quiesce necessary on a per-iport basis */
167910696SDavid.Hollister@Sun.COM 	if (scsi_hba_iport_unit_address(dip) != NULL) {
168010696SDavid.Hollister@Sun.COM 		return (DDI_SUCCESS);
168110696SDavid.Hollister@Sun.COM 	}
168210696SDavid.Hollister@Sun.COM 
168310696SDavid.Hollister@Sun.COM 	if ((pwp = TRAN2PMC(tran)) == NULL)
168410696SDavid.Hollister@Sun.COM 		return (DDI_SUCCESS);
168510696SDavid.Hollister@Sun.COM 
168610696SDavid.Hollister@Sun.COM 	/* Stop MPI & Reset chip (no need to re-initialize) */
168710696SDavid.Hollister@Sun.COM 	(void) pmcs_stop_mpi(pwp);
168810696SDavid.Hollister@Sun.COM 	(void) pmcs_soft_reset(pwp, B_TRUE);
168911692SJesse.Butler@Sun.COM 	pwp->last_reset_reason = PMCS_LAST_RST_QUIESCE;
169010696SDavid.Hollister@Sun.COM 
169110696SDavid.Hollister@Sun.COM 	return (DDI_SUCCESS);
169210696SDavid.Hollister@Sun.COM }
169310696SDavid.Hollister@Sun.COM 
169410696SDavid.Hollister@Sun.COM /*
169510696SDavid.Hollister@Sun.COM  * Called with xp->statlock and PHY lock and scratch acquired.
169610696SDavid.Hollister@Sun.COM  */
169710696SDavid.Hollister@Sun.COM static int
pmcs_add_sata_device(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)169810696SDavid.Hollister@Sun.COM pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
169910696SDavid.Hollister@Sun.COM {
170010696SDavid.Hollister@Sun.COM 	ata_identify_t *ati;
170110696SDavid.Hollister@Sun.COM 	int result, i;
170210696SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr;
170310696SDavid.Hollister@Sun.COM 	uint16_t *a;
170410696SDavid.Hollister@Sun.COM 	union {
170510696SDavid.Hollister@Sun.COM 		uint8_t nsa[8];
170610696SDavid.Hollister@Sun.COM 		uint16_t nsb[4];
170710696SDavid.Hollister@Sun.COM 	} u;
170810696SDavid.Hollister@Sun.COM 
170910696SDavid.Hollister@Sun.COM 	/*
171010696SDavid.Hollister@Sun.COM 	 * Safe defaults - use only if this target is brand new (i.e. doesn't
171110696SDavid.Hollister@Sun.COM 	 * already have these settings configured)
171210696SDavid.Hollister@Sun.COM 	 */
171310696SDavid.Hollister@Sun.COM 	if (xp->capacity == 0) {
171410696SDavid.Hollister@Sun.COM 		xp->capacity = (uint64_t)-1;
171510696SDavid.Hollister@Sun.COM 		xp->ca = 1;
171610696SDavid.Hollister@Sun.COM 		xp->qdepth = 1;
171710696SDavid.Hollister@Sun.COM 		xp->pio = 1;
171810696SDavid.Hollister@Sun.COM 	}
171910696SDavid.Hollister@Sun.COM 
172010696SDavid.Hollister@Sun.COM 	pptr = xp->phy;
172110696SDavid.Hollister@Sun.COM 
172210696SDavid.Hollister@Sun.COM 	/*
172310696SDavid.Hollister@Sun.COM 	 * We only try and issue an IDENTIFY for first level
172410696SDavid.Hollister@Sun.COM 	 * (direct attached) devices. We don't try and
172510696SDavid.Hollister@Sun.COM 	 * set other quirks here (this will happen later,
172610696SDavid.Hollister@Sun.COM 	 * if the device is fully configured)
172710696SDavid.Hollister@Sun.COM 	 */
172810696SDavid.Hollister@Sun.COM 	if (pptr->level) {
172910696SDavid.Hollister@Sun.COM 		return (0);
173010696SDavid.Hollister@Sun.COM 	}
173110696SDavid.Hollister@Sun.COM 
173210696SDavid.Hollister@Sun.COM 	mutex_exit(&xp->statlock);
173310696SDavid.Hollister@Sun.COM 	result = pmcs_sata_identify(pwp, pptr);
173410696SDavid.Hollister@Sun.COM 	mutex_enter(&xp->statlock);
173510696SDavid.Hollister@Sun.COM 
173610696SDavid.Hollister@Sun.COM 	if (result) {
173710696SDavid.Hollister@Sun.COM 		return (result);
173810696SDavid.Hollister@Sun.COM 	}
173910696SDavid.Hollister@Sun.COM 	ati = pwp->scratch;
174010696SDavid.Hollister@Sun.COM 	a = &ati->word108;
174110696SDavid.Hollister@Sun.COM 	for (i = 0; i < 4; i++) {
174210696SDavid.Hollister@Sun.COM 		u.nsb[i] = ddi_swap16(*a++);
174310696SDavid.Hollister@Sun.COM 	}
174410696SDavid.Hollister@Sun.COM 
174510696SDavid.Hollister@Sun.COM 	/*
174610696SDavid.Hollister@Sun.COM 	 * Check the returned data for being a valid (NAA=5) WWN.
174710696SDavid.Hollister@Sun.COM 	 * If so, use that and override the SAS address we were
174810696SDavid.Hollister@Sun.COM 	 * given at Link Up time.
174910696SDavid.Hollister@Sun.COM 	 */
175010696SDavid.Hollister@Sun.COM 	if ((u.nsa[0] >> 4) == 5) {
175110696SDavid.Hollister@Sun.COM 		(void) memcpy(pptr->sas_address, u.nsa, 8);
175210696SDavid.Hollister@Sun.COM 	}
175311048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
175411048SDavid.Hollister@Sun.COM 	    "%s: %s has SAS ADDRESS " SAS_ADDR_FMT,
175510696SDavid.Hollister@Sun.COM 	    __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address));
175610696SDavid.Hollister@Sun.COM 	return (0);
175710696SDavid.Hollister@Sun.COM }
175810696SDavid.Hollister@Sun.COM 
175910696SDavid.Hollister@Sun.COM /*
176010696SDavid.Hollister@Sun.COM  * Called with PHY lock and target statlock held and scratch acquired
176110696SDavid.Hollister@Sun.COM  */
176210696SDavid.Hollister@Sun.COM static boolean_t
pmcs_add_new_device(pmcs_hw_t * pwp,pmcs_xscsi_t * target)176310696SDavid.Hollister@Sun.COM pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target)
176410696SDavid.Hollister@Sun.COM {
176510696SDavid.Hollister@Sun.COM 	ASSERT(target != NULL);
176611048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, "%s: target = 0x%p",
176710696SDavid.Hollister@Sun.COM 	    __func__, (void *) target);
176810696SDavid.Hollister@Sun.COM 
176910696SDavid.Hollister@Sun.COM 	switch (target->phy->dtype) {
177010696SDavid.Hollister@Sun.COM 	case SATA:
177110696SDavid.Hollister@Sun.COM 		if (pmcs_add_sata_device(pwp, target) != 0) {
177211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, target->phy,
177311048SDavid.Hollister@Sun.COM 			    target, "%s: add_sata_device failed for tgt 0x%p",
177410696SDavid.Hollister@Sun.COM 			    __func__, (void *) target);
177510696SDavid.Hollister@Sun.COM 			return (B_FALSE);
177610696SDavid.Hollister@Sun.COM 		}
177710696SDavid.Hollister@Sun.COM 		break;
177810696SDavid.Hollister@Sun.COM 	case SAS:
177910696SDavid.Hollister@Sun.COM 		target->qdepth = maxqdepth;
178010696SDavid.Hollister@Sun.COM 		break;
178110696SDavid.Hollister@Sun.COM 	case EXPANDER:
178210696SDavid.Hollister@Sun.COM 		target->qdepth = 1;
178310696SDavid.Hollister@Sun.COM 		break;
178410696SDavid.Hollister@Sun.COM 	}
178510696SDavid.Hollister@Sun.COM 
178610696SDavid.Hollister@Sun.COM 	target->new = 0;
178710696SDavid.Hollister@Sun.COM 	target->assigned = 1;
178810696SDavid.Hollister@Sun.COM 	target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
178910696SDavid.Hollister@Sun.COM 	target->dtype = target->phy->dtype;
179010696SDavid.Hollister@Sun.COM 
179110696SDavid.Hollister@Sun.COM 	/*
179210696SDavid.Hollister@Sun.COM 	 * Set the PHY's config stop time to 0.  This is one of the final
179310696SDavid.Hollister@Sun.COM 	 * stops along the config path, so we're indicating that we
179410696SDavid.Hollister@Sun.COM 	 * successfully configured the PHY.
179510696SDavid.Hollister@Sun.COM 	 */
179610696SDavid.Hollister@Sun.COM 	target->phy->config_stop = 0;
179710696SDavid.Hollister@Sun.COM 
179810696SDavid.Hollister@Sun.COM 	return (B_TRUE);
179910696SDavid.Hollister@Sun.COM }
180010696SDavid.Hollister@Sun.COM 
180110696SDavid.Hollister@Sun.COM void
pmcs_worker(void * arg)180210696SDavid.Hollister@Sun.COM pmcs_worker(void *arg)
180310696SDavid.Hollister@Sun.COM {
180410696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = arg;
180510696SDavid.Hollister@Sun.COM 	ulong_t work_flags;
180610696SDavid.Hollister@Sun.COM 
180710696SDavid.Hollister@Sun.COM 	DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t,
180810696SDavid.Hollister@Sun.COM 	    pwp->config_changed);
180910696SDavid.Hollister@Sun.COM 
181010696SDavid.Hollister@Sun.COM 	if (pwp->state != STATE_RUNNING) {
181110696SDavid.Hollister@Sun.COM 		return;
181210696SDavid.Hollister@Sun.COM 	}
181310696SDavid.Hollister@Sun.COM 
181410696SDavid.Hollister@Sun.COM 	work_flags = atomic_swap_ulong(&pwp->work_flags, 0);
181510696SDavid.Hollister@Sun.COM 
181611847SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_DUMP_REGS) {
181711847SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
181811847SDavid.Hollister@Sun.COM 		pmcs_register_dump_int(pwp);
181911847SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
182011847SDavid.Hollister@Sun.COM 	}
182111847SDavid.Hollister@Sun.COM 
182210696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) {
182310696SDavid.Hollister@Sun.COM 		pmcs_ack_events(pwp);
182410696SDavid.Hollister@Sun.COM 	}
182510696SDavid.Hollister@Sun.COM 
182610696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) {
182710696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
182810696SDavid.Hollister@Sun.COM 		pmcs_spinup_release(pwp, NULL);
182910696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
183010696SDavid.Hollister@Sun.COM 	}
183110696SDavid.Hollister@Sun.COM 
183210696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) {
183310696SDavid.Hollister@Sun.COM 		pmcs_ssp_event_recovery(pwp);
183410696SDavid.Hollister@Sun.COM 	}
183510696SDavid.Hollister@Sun.COM 
183610696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) {
183710696SDavid.Hollister@Sun.COM 		pmcs_dev_state_recovery(pwp, NULL);
183810696SDavid.Hollister@Sun.COM 	}
183910696SDavid.Hollister@Sun.COM 
184011347SRamana.Srikanth@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_DEREGISTER_DEV) {
184111347SRamana.Srikanth@Sun.COM 		pmcs_deregister_device_work(pwp, NULL);
184211347SRamana.Srikanth@Sun.COM 	}
184311347SRamana.Srikanth@Sun.COM 
184410696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_DISCOVER) {
184510696SDavid.Hollister@Sun.COM 		pmcs_discover(pwp);
184610696SDavid.Hollister@Sun.COM 	}
184710696SDavid.Hollister@Sun.COM 
184810696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) {
184910696SDavid.Hollister@Sun.COM 		if (pmcs_abort_handler(pwp)) {
185010696SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
185110696SDavid.Hollister@Sun.COM 		}
185210696SDavid.Hollister@Sun.COM 	}
185310696SDavid.Hollister@Sun.COM 
185410696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_SATA_RUN) {
185510696SDavid.Hollister@Sun.COM 		pmcs_sata_work(pwp);
185610696SDavid.Hollister@Sun.COM 	}
185710696SDavid.Hollister@Sun.COM 
185810696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) {
185910696SDavid.Hollister@Sun.COM 		pmcs_scsa_wq_run(pwp);
186010696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
186110696SDavid.Hollister@Sun.COM 		PMCS_CQ_RUN(pwp);
186210696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
186310696SDavid.Hollister@Sun.COM 	}
186410696SDavid.Hollister@Sun.COM 
186510696SDavid.Hollister@Sun.COM 	if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) {
186610696SDavid.Hollister@Sun.COM 		if (pmcs_add_more_chunks(pwp,
186710696SDavid.Hollister@Sun.COM 		    ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) {
186810696SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
186910696SDavid.Hollister@Sun.COM 		} else {
187010696SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
187110696SDavid.Hollister@Sun.COM 		}
187210696SDavid.Hollister@Sun.COM 	}
187310696SDavid.Hollister@Sun.COM }
187410696SDavid.Hollister@Sun.COM 
187510696SDavid.Hollister@Sun.COM static int
pmcs_add_more_chunks(pmcs_hw_t * pwp,unsigned long nsize)187610696SDavid.Hollister@Sun.COM pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize)
187710696SDavid.Hollister@Sun.COM {
187810696SDavid.Hollister@Sun.COM 	pmcs_dmachunk_t *dc;
187910696SDavid.Hollister@Sun.COM 	unsigned long dl;
188010696SDavid.Hollister@Sun.COM 	pmcs_chunk_t	*pchunk = NULL;
188110696SDavid.Hollister@Sun.COM 
188210696SDavid.Hollister@Sun.COM 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
188310696SDavid.Hollister@Sun.COM 
188410696SDavid.Hollister@Sun.COM 	pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP);
188510696SDavid.Hollister@Sun.COM 	if (pchunk == NULL) {
188611048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
188710696SDavid.Hollister@Sun.COM 		    "Not enough memory for DMA chunks");
188810696SDavid.Hollister@Sun.COM 		return (-1);
188910696SDavid.Hollister@Sun.COM 	}
189010696SDavid.Hollister@Sun.COM 
189110696SDavid.Hollister@Sun.COM 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle,
189210696SDavid.Hollister@Sun.COM 	    &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp,
189310696SDavid.Hollister@Sun.COM 	    &pchunk->dma_addr) == B_FALSE) {
189411048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
189511048SDavid.Hollister@Sun.COM 		    "Failed to setup DMA for chunks");
189610696SDavid.Hollister@Sun.COM 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
189710696SDavid.Hollister@Sun.COM 		return (-1);
189810696SDavid.Hollister@Sun.COM 	}
189910696SDavid.Hollister@Sun.COM 
190010696SDavid.Hollister@Sun.COM 	if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) ||
190110696SDavid.Hollister@Sun.COM 	    (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) {
190210696SDavid.Hollister@Sun.COM 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
190310696SDavid.Hollister@Sun.COM 		return (-1);
190410696SDavid.Hollister@Sun.COM 	}
190510696SDavid.Hollister@Sun.COM 
190610696SDavid.Hollister@Sun.COM 	bzero(pchunk->addrp, nsize);
190710696SDavid.Hollister@Sun.COM 	dc = NULL;
190810696SDavid.Hollister@Sun.COM 	for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) {
190910696SDavid.Hollister@Sun.COM 		pmcs_dmachunk_t *tmp;
191010696SDavid.Hollister@Sun.COM 		tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP);
191110696SDavid.Hollister@Sun.COM 		tmp->nxt = dc;
191210696SDavid.Hollister@Sun.COM 		dc = tmp;
191310696SDavid.Hollister@Sun.COM 	}
191410696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->dma_lock);
191510696SDavid.Hollister@Sun.COM 	pmcs_idma_chunks(pwp, dc, pchunk, nsize);
191610696SDavid.Hollister@Sun.COM 	pwp->nchunks++;
191710696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->dma_lock);
191810696SDavid.Hollister@Sun.COM 	return (0);
191910696SDavid.Hollister@Sun.COM }
192010696SDavid.Hollister@Sun.COM 
192111692SJesse.Butler@Sun.COM static void
pmcs_check_forward_progress(pmcs_hw_t * pwp)192211692SJesse.Butler@Sun.COM pmcs_check_forward_progress(pmcs_hw_t *pwp)
192311692SJesse.Butler@Sun.COM {
192411832SJesse.Butler@Sun.COM 	pmcwork_t	*wrkp;
192511832SJesse.Butler@Sun.COM 	uint32_t	*iqp;
192611692SJesse.Butler@Sun.COM 	uint32_t	cur_iqci;
192711832SJesse.Butler@Sun.COM 	uint32_t	cur_work_idx;
192811692SJesse.Butler@Sun.COM 	uint32_t	cur_msgu_tick;
192911692SJesse.Butler@Sun.COM 	uint32_t	cur_iop_tick;
193011692SJesse.Butler@Sun.COM 	int 		i;
193111692SJesse.Butler@Sun.COM 
193211692SJesse.Butler@Sun.COM 	mutex_enter(&pwp->lock);
193311692SJesse.Butler@Sun.COM 
193411692SJesse.Butler@Sun.COM 	if (pwp->state == STATE_IN_RESET) {
193511692SJesse.Butler@Sun.COM 		mutex_exit(&pwp->lock);
193611692SJesse.Butler@Sun.COM 		return;
193711692SJesse.Butler@Sun.COM 	}
193811692SJesse.Butler@Sun.COM 
193911832SJesse.Butler@Sun.COM 	/*
194011832SJesse.Butler@Sun.COM 	 * Ensure that inbound work is getting picked up.  First, check to
194111832SJesse.Butler@Sun.COM 	 * see if new work has been posted.  If it has, ensure that the
194211832SJesse.Butler@Sun.COM 	 * work is moving forward by checking the consumer index and the
194311832SJesse.Butler@Sun.COM 	 * last_htag for the work being processed against what we saw last
194411832SJesse.Butler@Sun.COM 	 * time.  Note: we use the work structure's 'last_htag' because at
194511832SJesse.Butler@Sun.COM 	 * any given moment it could be freed back, thus clearing 'htag'
194611832SJesse.Butler@Sun.COM 	 * and setting 'last_htag' (see pmcs_pwork).
194711832SJesse.Butler@Sun.COM 	 */
194811692SJesse.Butler@Sun.COM 	for (i = 0; i < PMCS_NIQ; i++) {
194911692SJesse.Butler@Sun.COM 		cur_iqci = pmcs_rd_iqci(pwp, i);
195011832SJesse.Butler@Sun.COM 		iqp = &pwp->iqp[i][cur_iqci * (PMCS_QENTRY_SIZE >> 2)];
195111832SJesse.Butler@Sun.COM 		cur_work_idx = PMCS_TAG_INDEX(LE_32(*(iqp+1)));
195211832SJesse.Butler@Sun.COM 		wrkp = &pwp->work[cur_work_idx];
195311692SJesse.Butler@Sun.COM 		if (cur_iqci == pwp->shadow_iqpi[i]) {
195411692SJesse.Butler@Sun.COM 			pwp->last_iqci[i] = cur_iqci;
195511832SJesse.Butler@Sun.COM 			pwp->last_htag[i] = wrkp->last_htag;
195611692SJesse.Butler@Sun.COM 			continue;
195711692SJesse.Butler@Sun.COM 		}
195811832SJesse.Butler@Sun.COM 		if ((cur_iqci == pwp->last_iqci[i]) &&
195911832SJesse.Butler@Sun.COM 		    (wrkp->last_htag == pwp->last_htag[i])) {
196011692SJesse.Butler@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
196111692SJesse.Butler@Sun.COM 			    "Inbound Queue stall detected, issuing reset");
196211692SJesse.Butler@Sun.COM 			goto hot_reset;
196311692SJesse.Butler@Sun.COM 		}
196411692SJesse.Butler@Sun.COM 		pwp->last_iqci[i] = cur_iqci;
196511832SJesse.Butler@Sun.COM 		pwp->last_htag[i] = wrkp->last_htag;
196611692SJesse.Butler@Sun.COM 	}
196711692SJesse.Butler@Sun.COM 
196811832SJesse.Butler@Sun.COM 	/*
196911832SJesse.Butler@Sun.COM 	 * Check heartbeat on both the MSGU and IOP.  It is unlikely that
197011832SJesse.Butler@Sun.COM 	 * we'd ever fail here, as the inbound queue monitoring code above
197111832SJesse.Butler@Sun.COM 	 * would detect a stall due to either of these elements being
197211832SJesse.Butler@Sun.COM 	 * stalled, but we might as well keep an eye on them.
197311832SJesse.Butler@Sun.COM 	 */
197411692SJesse.Butler@Sun.COM 	cur_msgu_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK);
197511692SJesse.Butler@Sun.COM 	if (cur_msgu_tick == pwp->last_msgu_tick) {
197611692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
197711692SJesse.Butler@Sun.COM 		    "Stall detected on MSGU, issuing reset");
197811692SJesse.Butler@Sun.COM 		goto hot_reset;
197911692SJesse.Butler@Sun.COM 	}
198011692SJesse.Butler@Sun.COM 	pwp->last_msgu_tick = cur_msgu_tick;
198111692SJesse.Butler@Sun.COM 
198211692SJesse.Butler@Sun.COM 	cur_iop_tick  = pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK);
198311692SJesse.Butler@Sun.COM 	if (cur_iop_tick == pwp->last_iop_tick) {
198411692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
198511692SJesse.Butler@Sun.COM 		    "Stall detected on IOP, issuing reset");
198611692SJesse.Butler@Sun.COM 		goto hot_reset;
198711692SJesse.Butler@Sun.COM 	}
198811692SJesse.Butler@Sun.COM 	pwp->last_iop_tick = cur_iop_tick;
198911692SJesse.Butler@Sun.COM 
199011692SJesse.Butler@Sun.COM 	mutex_exit(&pwp->lock);
199111692SJesse.Butler@Sun.COM 	return;
199211692SJesse.Butler@Sun.COM 
199311692SJesse.Butler@Sun.COM hot_reset:
199411692SJesse.Butler@Sun.COM 	pwp->state = STATE_DEAD;
199511692SJesse.Butler@Sun.COM 	/*
199611692SJesse.Butler@Sun.COM 	 * We've detected a stall. Attempt to recover service via hot
199711692SJesse.Butler@Sun.COM 	 * reset. In case of failure, pmcs_hot_reset() will handle the
199811692SJesse.Butler@Sun.COM 	 * failure and issue any required FM notifications.
199911692SJesse.Butler@Sun.COM 	 * See pmcs_subr.c for more details.
200011692SJesse.Butler@Sun.COM 	 */
200111692SJesse.Butler@Sun.COM 	if (pmcs_hot_reset(pwp)) {
200211692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
200311692SJesse.Butler@Sun.COM 		    "%s: hot reset failure", __func__);
200411692SJesse.Butler@Sun.COM 	} else {
200511692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
200611692SJesse.Butler@Sun.COM 		    "%s: hot reset complete", __func__);
200711692SJesse.Butler@Sun.COM 		pwp->last_reset_reason = PMCS_LAST_RST_STALL;
200811692SJesse.Butler@Sun.COM 	}
200911692SJesse.Butler@Sun.COM 	mutex_exit(&pwp->lock);
201011692SJesse.Butler@Sun.COM }
201110696SDavid.Hollister@Sun.COM 
201210696SDavid.Hollister@Sun.COM static void
pmcs_check_commands(pmcs_hw_t * pwp)201310696SDavid.Hollister@Sun.COM pmcs_check_commands(pmcs_hw_t *pwp)
201410696SDavid.Hollister@Sun.COM {
201510696SDavid.Hollister@Sun.COM 	pmcs_cmd_t *sp;
201610696SDavid.Hollister@Sun.COM 	size_t amt;
201710696SDavid.Hollister@Sun.COM 	char path[32];
201810696SDavid.Hollister@Sun.COM 	pmcwork_t *pwrk;
201910696SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *target;
202010696SDavid.Hollister@Sun.COM 	pmcs_phy_t *phyp;
202111347SRamana.Srikanth@Sun.COM 	int rval;
202210696SDavid.Hollister@Sun.COM 
202310696SDavid.Hollister@Sun.COM 	for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) {
202410696SDavid.Hollister@Sun.COM 		mutex_enter(&pwrk->lock);
202510696SDavid.Hollister@Sun.COM 
202610696SDavid.Hollister@Sun.COM 		/*
202710696SDavid.Hollister@Sun.COM 		 * If the command isn't active, we can't be timing it still.
202810696SDavid.Hollister@Sun.COM 		 * Active means the tag is not free and the state is "on chip".
202910696SDavid.Hollister@Sun.COM 		 */
203010696SDavid.Hollister@Sun.COM 		if (!PMCS_COMMAND_ACTIVE(pwrk)) {
203110696SDavid.Hollister@Sun.COM 			mutex_exit(&pwrk->lock);
203210696SDavid.Hollister@Sun.COM 			continue;
203310696SDavid.Hollister@Sun.COM 		}
203410696SDavid.Hollister@Sun.COM 
203510696SDavid.Hollister@Sun.COM 		/*
203610696SDavid.Hollister@Sun.COM 		 * No timer active for this command.
203710696SDavid.Hollister@Sun.COM 		 */
203810696SDavid.Hollister@Sun.COM 		if (pwrk->timer == 0) {
203910696SDavid.Hollister@Sun.COM 			mutex_exit(&pwrk->lock);
204010696SDavid.Hollister@Sun.COM 			continue;
204110696SDavid.Hollister@Sun.COM 		}
204210696SDavid.Hollister@Sun.COM 
204310696SDavid.Hollister@Sun.COM 		/*
204410696SDavid.Hollister@Sun.COM 		 * Knock off bits for the time interval.
204510696SDavid.Hollister@Sun.COM 		 */
204610696SDavid.Hollister@Sun.COM 		if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) {
204710696SDavid.Hollister@Sun.COM 			pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL);
204810696SDavid.Hollister@Sun.COM 		} else {
204910696SDavid.Hollister@Sun.COM 			pwrk->timer = 0;
205010696SDavid.Hollister@Sun.COM 		}
205110696SDavid.Hollister@Sun.COM 		if (pwrk->timer > 0) {
205210696SDavid.Hollister@Sun.COM 			mutex_exit(&pwrk->lock);
205310696SDavid.Hollister@Sun.COM 			continue;
205410696SDavid.Hollister@Sun.COM 		}
205510696SDavid.Hollister@Sun.COM 
205610696SDavid.Hollister@Sun.COM 		/*
205710696SDavid.Hollister@Sun.COM 		 * The command has now officially timed out.
205810696SDavid.Hollister@Sun.COM 		 * Get the path for it. If it doesn't have
205910696SDavid.Hollister@Sun.COM 		 * a phy pointer any more, it's really dead
206010696SDavid.Hollister@Sun.COM 		 * and can just be put back on the free list.
206110696SDavid.Hollister@Sun.COM 		 * There should *not* be any commands associated
206210696SDavid.Hollister@Sun.COM 		 * with it any more.
206310696SDavid.Hollister@Sun.COM 		 */
206410696SDavid.Hollister@Sun.COM 		if (pwrk->phy == NULL) {
206511048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
206610696SDavid.Hollister@Sun.COM 			    "dead command with gone phy being recycled");
206710696SDavid.Hollister@Sun.COM 			ASSERT(pwrk->xp == NULL);
206810696SDavid.Hollister@Sun.COM 			pmcs_pwork(pwp, pwrk);
206910696SDavid.Hollister@Sun.COM 			continue;
207010696SDavid.Hollister@Sun.COM 		}
207110696SDavid.Hollister@Sun.COM 		amt = sizeof (path);
207210696SDavid.Hollister@Sun.COM 		amt = min(sizeof (pwrk->phy->path), amt);
207310696SDavid.Hollister@Sun.COM 		(void) memcpy(path, pwrk->phy->path, amt);
207410696SDavid.Hollister@Sun.COM 
207510696SDavid.Hollister@Sun.COM 		/*
207610696SDavid.Hollister@Sun.COM 		 * If this is a non-SCSA command, stop here. Eventually
207710696SDavid.Hollister@Sun.COM 		 * we might do something with non-SCSA commands here-
207810696SDavid.Hollister@Sun.COM 		 * but so far their timeout mechanisms are handled in
207910696SDavid.Hollister@Sun.COM 		 * the WAIT_FOR macro.
208010696SDavid.Hollister@Sun.COM 		 */
208110696SDavid.Hollister@Sun.COM 		if (pwrk->xp == NULL) {
208211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
208310696SDavid.Hollister@Sun.COM 			    "%s: non-SCSA cmd tag 0x%x timed out",
208410696SDavid.Hollister@Sun.COM 			    path, pwrk->htag);
208510696SDavid.Hollister@Sun.COM 			mutex_exit(&pwrk->lock);
208610696SDavid.Hollister@Sun.COM 			continue;
208710696SDavid.Hollister@Sun.COM 		}
208810696SDavid.Hollister@Sun.COM 
208910696SDavid.Hollister@Sun.COM 		sp = pwrk->arg;
209010696SDavid.Hollister@Sun.COM 		ASSERT(sp != NULL);
209110696SDavid.Hollister@Sun.COM 
209210696SDavid.Hollister@Sun.COM 		/*
209310696SDavid.Hollister@Sun.COM 		 * Mark it as timed out.
209410696SDavid.Hollister@Sun.COM 		 */
209510696SDavid.Hollister@Sun.COM 		CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT;
209610696SDavid.Hollister@Sun.COM 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
209710696SDavid.Hollister@Sun.COM #ifdef	DEBUG
209811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp,
209910696SDavid.Hollister@Sun.COM 		    "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d",
210010696SDavid.Hollister@Sun.COM 		    path, pwrk->htag, pwrk->state, pwrk->onwire);
210110696SDavid.Hollister@Sun.COM #else
210211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp,
210310696SDavid.Hollister@Sun.COM 		    "%s: SCSA cmd tag 0x%x timed out (state %x)",
210410696SDavid.Hollister@Sun.COM 		    path, pwrk->htag, pwrk->state);
210510696SDavid.Hollister@Sun.COM #endif
210610696SDavid.Hollister@Sun.COM 		/*
210710696SDavid.Hollister@Sun.COM 		 * Mark the work structure as timed out.
210810696SDavid.Hollister@Sun.COM 		 */
210910696SDavid.Hollister@Sun.COM 		pwrk->state = PMCS_WORK_STATE_TIMED_OUT;
211010696SDavid.Hollister@Sun.COM 		phyp = pwrk->phy;
211110696SDavid.Hollister@Sun.COM 		target = pwrk->xp;
2112*12462Sjesse.butler@oracle.com 		ASSERT(target != NULL);
211310696SDavid.Hollister@Sun.COM 		mutex_exit(&pwrk->lock);
211410696SDavid.Hollister@Sun.COM 
211510696SDavid.Hollister@Sun.COM 		pmcs_lock_phy(phyp);
211610696SDavid.Hollister@Sun.COM 		mutex_enter(&target->statlock);
211710696SDavid.Hollister@Sun.COM 
211810696SDavid.Hollister@Sun.COM 		/*
211910696SDavid.Hollister@Sun.COM 		 * No point attempting recovery if the device is gone
212010696SDavid.Hollister@Sun.COM 		 */
212111090SDavid.Hollister@Sun.COM 		if (target->dev_gone) {
212210696SDavid.Hollister@Sun.COM 			mutex_exit(&target->statlock);
212310696SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(phyp);
212411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
212510696SDavid.Hollister@Sun.COM 			    "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE "
212610696SDavid.Hollister@Sun.COM 			    "for htag 0x%08x", __func__,
212711090SDavid.Hollister@Sun.COM 			    (void *)target, pwrk->htag);
212810696SDavid.Hollister@Sun.COM 			mutex_enter(&pwrk->lock);
212910696SDavid.Hollister@Sun.COM 			if (!PMCS_COMMAND_DONE(pwrk)) {
213010696SDavid.Hollister@Sun.COM 				/* Complete this command here */
213111048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
213211048SDavid.Hollister@Sun.COM 				    "%s: Completing cmd (htag 0x%08x) "
213310696SDavid.Hollister@Sun.COM 				    "anyway", __func__, pwrk->htag);
213410696SDavid.Hollister@Sun.COM 				pwrk->dead = 1;
213510696SDavid.Hollister@Sun.COM 				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
213610696SDavid.Hollister@Sun.COM 				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
213710696SDavid.Hollister@Sun.COM 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
213810696SDavid.Hollister@Sun.COM 			} else {
213910696SDavid.Hollister@Sun.COM 				mutex_exit(&pwrk->lock);
214010696SDavid.Hollister@Sun.COM 			}
214110696SDavid.Hollister@Sun.COM 			continue;
214210696SDavid.Hollister@Sun.COM 		}
214310696SDavid.Hollister@Sun.COM 
214411347SRamana.Srikanth@Sun.COM 		mutex_exit(&target->statlock);
214511347SRamana.Srikanth@Sun.COM 		rval = pmcs_abort(pwp, phyp, pwrk->htag, 0, 1);
214611347SRamana.Srikanth@Sun.COM 		if (rval) {
214711347SRamana.Srikanth@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
214811347SRamana.Srikanth@Sun.COM 			    "%s: Bad status (%d) on abort of HTAG 0x%08x",
214911347SRamana.Srikanth@Sun.COM 			    __func__, rval, pwrk->htag);
215010696SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(phyp);
215111347SRamana.Srikanth@Sun.COM 			mutex_enter(&pwrk->lock);
215211347SRamana.Srikanth@Sun.COM 			if (!PMCS_COMMAND_DONE(pwrk)) {
215311347SRamana.Srikanth@Sun.COM 				/* Complete this command here */
215411347SRamana.Srikanth@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
215511347SRamana.Srikanth@Sun.COM 				    "%s: Completing cmd (htag 0x%08x) "
215611347SRamana.Srikanth@Sun.COM 				    "anyway", __func__, pwrk->htag);
215711347SRamana.Srikanth@Sun.COM 				if (target->dev_gone) {
215811347SRamana.Srikanth@Sun.COM 					pwrk->dead = 1;
215911347SRamana.Srikanth@Sun.COM 					CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
216011347SRamana.Srikanth@Sun.COM 					CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
216111347SRamana.Srikanth@Sun.COM 				}
216211347SRamana.Srikanth@Sun.COM 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
216311347SRamana.Srikanth@Sun.COM 			} else {
216411347SRamana.Srikanth@Sun.COM 				mutex_exit(&pwrk->lock);
216511347SRamana.Srikanth@Sun.COM 			}
216611347SRamana.Srikanth@Sun.COM 			pmcs_lock_phy(phyp);
216711347SRamana.Srikanth@Sun.COM 			/*
216811347SRamana.Srikanth@Sun.COM 			 * No need to reschedule ABORT if we get any other
216911347SRamana.Srikanth@Sun.COM 			 * status
217011347SRamana.Srikanth@Sun.COM 			 */
217111347SRamana.Srikanth@Sun.COM 			if (rval == ENOMEM) {
217211347SRamana.Srikanth@Sun.COM 				phyp->abort_sent = 0;
217311347SRamana.Srikanth@Sun.COM 				phyp->abort_pending = 1;
217411347SRamana.Srikanth@Sun.COM 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
217511347SRamana.Srikanth@Sun.COM 			}
217610696SDavid.Hollister@Sun.COM 		}
217710696SDavid.Hollister@Sun.COM 		pmcs_unlock_phy(phyp);
217810696SDavid.Hollister@Sun.COM 	}
217910696SDavid.Hollister@Sun.COM 	/*
218010696SDavid.Hollister@Sun.COM 	 * Run any completions that may have been queued up.
218110696SDavid.Hollister@Sun.COM 	 */
218210696SDavid.Hollister@Sun.COM 	PMCS_CQ_RUN(pwp);
218310696SDavid.Hollister@Sun.COM }
218410696SDavid.Hollister@Sun.COM 
218510696SDavid.Hollister@Sun.COM static void
pmcs_watchdog(void * arg)218610696SDavid.Hollister@Sun.COM pmcs_watchdog(void *arg)
218710696SDavid.Hollister@Sun.COM {
218810696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = arg;
218910696SDavid.Hollister@Sun.COM 
219010696SDavid.Hollister@Sun.COM 	DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t,
219110696SDavid.Hollister@Sun.COM 	    pwp->config_changed);
219210696SDavid.Hollister@Sun.COM 
219311601SDavid.Hollister@Sun.COM 	/*
219411692SJesse.Butler@Sun.COM 	 * Check forward progress on the chip
219511692SJesse.Butler@Sun.COM 	 */
219611692SJesse.Butler@Sun.COM 	if (++pwp->watchdog_count == PMCS_FWD_PROG_TRIGGER) {
219711692SJesse.Butler@Sun.COM 		pwp->watchdog_count = 0;
219811692SJesse.Butler@Sun.COM 		pmcs_check_forward_progress(pwp);
219911692SJesse.Butler@Sun.COM 	}
220011692SJesse.Butler@Sun.COM 
220111692SJesse.Butler@Sun.COM 	/*
220211601SDavid.Hollister@Sun.COM 	 * Check to see if we need to kick discovery off again
220311601SDavid.Hollister@Sun.COM 	 */
220411601SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->config_lock);
220511601SDavid.Hollister@Sun.COM 	if (pwp->config_restart &&
220611601SDavid.Hollister@Sun.COM 	    (ddi_get_lbolt() >= pwp->config_restart_time)) {
220711601SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
220811601SDavid.Hollister@Sun.COM 		    "%s: Timer expired for re-enumeration: Start discovery",
220911601SDavid.Hollister@Sun.COM 		    __func__);
221011601SDavid.Hollister@Sun.COM 		pwp->config_restart = B_FALSE;
221111601SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
221211601SDavid.Hollister@Sun.COM 	}
221311601SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->config_lock);
221411601SDavid.Hollister@Sun.COM 
221510696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->lock);
221610696SDavid.Hollister@Sun.COM 	if (pwp->state != STATE_RUNNING) {
221710696SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
221810696SDavid.Hollister@Sun.COM 		return;
221910696SDavid.Hollister@Sun.COM 	}
222010696SDavid.Hollister@Sun.COM 
222110696SDavid.Hollister@Sun.COM 	if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) {
222210696SDavid.Hollister@Sun.COM 		if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp,
222310696SDavid.Hollister@Sun.COM 		    DDI_NOSLEEP) != DDI_SUCCESS) {
222411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
222510696SDavid.Hollister@Sun.COM 			    "Could not dispatch to worker thread");
222610696SDavid.Hollister@Sun.COM 		}
222710696SDavid.Hollister@Sun.COM 	}
222810696SDavid.Hollister@Sun.COM 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
222910696SDavid.Hollister@Sun.COM 	    drv_usectohz(PMCS_WATCH_INTERVAL));
223011692SJesse.Butler@Sun.COM 
223110696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->lock);
223211692SJesse.Butler@Sun.COM 
223310696SDavid.Hollister@Sun.COM 	pmcs_check_commands(pwp);
223410696SDavid.Hollister@Sun.COM 	pmcs_handle_dead_phys(pwp);
223510696SDavid.Hollister@Sun.COM }
223610696SDavid.Hollister@Sun.COM 
223710696SDavid.Hollister@Sun.COM static int
pmcs_remove_ihandlers(pmcs_hw_t * pwp,int icnt)223810696SDavid.Hollister@Sun.COM pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt)
223910696SDavid.Hollister@Sun.COM {
224010696SDavid.Hollister@Sun.COM 	int i, r, rslt = 0;
224110696SDavid.Hollister@Sun.COM 	for (i = 0; i < icnt; i++) {
224210696SDavid.Hollister@Sun.COM 		r = ddi_intr_remove_handler(pwp->ih_table[i]);
224310696SDavid.Hollister@Sun.COM 		if (r == DDI_SUCCESS) {
224410696SDavid.Hollister@Sun.COM 			continue;
224510696SDavid.Hollister@Sun.COM 		}
224611048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
224710696SDavid.Hollister@Sun.COM 		    "%s: unable to remove interrupt handler %d", __func__, i);
224810696SDavid.Hollister@Sun.COM 		rslt = -1;
224910696SDavid.Hollister@Sun.COM 		break;
225010696SDavid.Hollister@Sun.COM 	}
225110696SDavid.Hollister@Sun.COM 	return (rslt);
225210696SDavid.Hollister@Sun.COM }
225310696SDavid.Hollister@Sun.COM 
225410696SDavid.Hollister@Sun.COM static int
pmcs_disable_intrs(pmcs_hw_t * pwp,int icnt)225510696SDavid.Hollister@Sun.COM pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt)
225610696SDavid.Hollister@Sun.COM {
225710696SDavid.Hollister@Sun.COM 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
225810696SDavid.Hollister@Sun.COM 		int r = ddi_intr_block_disable(&pwp->ih_table[0],
225910696SDavid.Hollister@Sun.COM 		    pwp->intr_cnt);
226010696SDavid.Hollister@Sun.COM 		if (r != DDI_SUCCESS) {
226111048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
226210696SDavid.Hollister@Sun.COM 			    "unable to disable interrupt block");
226310696SDavid.Hollister@Sun.COM 			return (-1);
226410696SDavid.Hollister@Sun.COM 		}
226510696SDavid.Hollister@Sun.COM 	} else {
226610696SDavid.Hollister@Sun.COM 		int i;
226710696SDavid.Hollister@Sun.COM 		for (i = 0; i < icnt; i++) {
226810696SDavid.Hollister@Sun.COM 			if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) {
226910696SDavid.Hollister@Sun.COM 				continue;
227010696SDavid.Hollister@Sun.COM 			}
227111048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
227210696SDavid.Hollister@Sun.COM 			    "unable to disable interrupt %d", i);
227310696SDavid.Hollister@Sun.COM 			return (-1);
227410696SDavid.Hollister@Sun.COM 		}
227510696SDavid.Hollister@Sun.COM 	}
227610696SDavid.Hollister@Sun.COM 	return (0);
227710696SDavid.Hollister@Sun.COM }
227810696SDavid.Hollister@Sun.COM 
227910696SDavid.Hollister@Sun.COM static int
pmcs_free_intrs(pmcs_hw_t * pwp,int icnt)228010696SDavid.Hollister@Sun.COM pmcs_free_intrs(pmcs_hw_t *pwp, int icnt)
228110696SDavid.Hollister@Sun.COM {
228210696SDavid.Hollister@Sun.COM 	int i;
228310696SDavid.Hollister@Sun.COM 	for (i = 0; i < icnt; i++) {
228410696SDavid.Hollister@Sun.COM 		if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) {
228510696SDavid.Hollister@Sun.COM 			continue;
228610696SDavid.Hollister@Sun.COM 		}
228711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
228811048SDavid.Hollister@Sun.COM 		    "unable to free interrupt %d", i);
228910696SDavid.Hollister@Sun.COM 		return (-1);
229010696SDavid.Hollister@Sun.COM 	}
229110696SDavid.Hollister@Sun.COM 	kmem_free(pwp->ih_table, pwp->ih_table_size);
229210696SDavid.Hollister@Sun.COM 	pwp->ih_table_size = 0;
229310696SDavid.Hollister@Sun.COM 	return (0);
229410696SDavid.Hollister@Sun.COM }
229510696SDavid.Hollister@Sun.COM 
229610696SDavid.Hollister@Sun.COM /*
229710696SDavid.Hollister@Sun.COM  * Try to set up interrupts of type "type" with a minimum number of interrupts
229810696SDavid.Hollister@Sun.COM  * of "min".
229910696SDavid.Hollister@Sun.COM  */
230010696SDavid.Hollister@Sun.COM static void
pmcs_setup_intr_impl(pmcs_hw_t * pwp,int type,int min)230110696SDavid.Hollister@Sun.COM pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min)
230210696SDavid.Hollister@Sun.COM {
230310696SDavid.Hollister@Sun.COM 	int rval, avail, count, actual, max;
230410696SDavid.Hollister@Sun.COM 
230510696SDavid.Hollister@Sun.COM 	rval = ddi_intr_get_nintrs(pwp->dip, type, &count);
230610696SDavid.Hollister@Sun.COM 	if ((rval != DDI_SUCCESS) || (count < min)) {
230711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
230810696SDavid.Hollister@Sun.COM 		    "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d",
230910696SDavid.Hollister@Sun.COM 		    __func__, type, rval, count, min);
231010696SDavid.Hollister@Sun.COM 		return;
231110696SDavid.Hollister@Sun.COM 	}
231210696SDavid.Hollister@Sun.COM 
231311048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
231410696SDavid.Hollister@Sun.COM 	    "%s: nintrs = %d for type: %d", __func__, count, type);
231510696SDavid.Hollister@Sun.COM 
231610696SDavid.Hollister@Sun.COM 	rval = ddi_intr_get_navail(pwp->dip, type, &avail);
231710696SDavid.Hollister@Sun.COM 	if ((rval != DDI_SUCCESS) || (avail < min)) {
231811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
231910696SDavid.Hollister@Sun.COM 		    "%s: get_navail failed; type: %d rc: %d avail: %d min: %d",
232010696SDavid.Hollister@Sun.COM 		    __func__, type, rval, avail, min);
232110696SDavid.Hollister@Sun.COM 		return;
232210696SDavid.Hollister@Sun.COM 	}
232310696SDavid.Hollister@Sun.COM 
232411048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
232510696SDavid.Hollister@Sun.COM 	    "%s: navail = %d for type: %d", __func__, avail, type);
232610696SDavid.Hollister@Sun.COM 
232710696SDavid.Hollister@Sun.COM 	pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t);
232810696SDavid.Hollister@Sun.COM 	pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP);
232910696SDavid.Hollister@Sun.COM 
233010696SDavid.Hollister@Sun.COM 	switch (type) {
233110696SDavid.Hollister@Sun.COM 	case DDI_INTR_TYPE_MSIX:
233210696SDavid.Hollister@Sun.COM 		pwp->int_type = PMCS_INT_MSIX;
233310696SDavid.Hollister@Sun.COM 		max = PMCS_MAX_MSIX;
233410696SDavid.Hollister@Sun.COM 		break;
233510696SDavid.Hollister@Sun.COM 	case DDI_INTR_TYPE_MSI:
233610696SDavid.Hollister@Sun.COM 		pwp->int_type = PMCS_INT_MSI;
233710696SDavid.Hollister@Sun.COM 		max = PMCS_MAX_MSI;
233810696SDavid.Hollister@Sun.COM 		break;
233910696SDavid.Hollister@Sun.COM 	case DDI_INTR_TYPE_FIXED:
234010696SDavid.Hollister@Sun.COM 	default:
234110696SDavid.Hollister@Sun.COM 		pwp->int_type = PMCS_INT_FIXED;
234210696SDavid.Hollister@Sun.COM 		max = PMCS_MAX_FIXED;
234310696SDavid.Hollister@Sun.COM 		break;
234410696SDavid.Hollister@Sun.COM 	}
234510696SDavid.Hollister@Sun.COM 
234610696SDavid.Hollister@Sun.COM 	rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual,
234710696SDavid.Hollister@Sun.COM 	    DDI_INTR_ALLOC_NORMAL);
234810696SDavid.Hollister@Sun.COM 	if (rval != DDI_SUCCESS) {
234911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
235010696SDavid.Hollister@Sun.COM 		    "%s: ddi_intr_alloc failed; type: %d rc: %d",
235110696SDavid.Hollister@Sun.COM 		    __func__, type, rval);
235210696SDavid.Hollister@Sun.COM 		kmem_free(pwp->ih_table, pwp->ih_table_size);
235310696SDavid.Hollister@Sun.COM 		pwp->ih_table = NULL;
235410696SDavid.Hollister@Sun.COM 		pwp->ih_table_size = 0;
235510696SDavid.Hollister@Sun.COM 		pwp->intr_cnt = 0;
235610696SDavid.Hollister@Sun.COM 		pwp->int_type = PMCS_INT_NONE;
235710696SDavid.Hollister@Sun.COM 		return;
235810696SDavid.Hollister@Sun.COM 	}
235910696SDavid.Hollister@Sun.COM 
236010696SDavid.Hollister@Sun.COM 	pwp->intr_cnt = actual;
236110696SDavid.Hollister@Sun.COM }
236210696SDavid.Hollister@Sun.COM 
236310696SDavid.Hollister@Sun.COM /*
236410696SDavid.Hollister@Sun.COM  * Set up interrupts.
236510696SDavid.Hollister@Sun.COM  * We return one of three values:
236610696SDavid.Hollister@Sun.COM  *
236710696SDavid.Hollister@Sun.COM  * 0 - success
236810696SDavid.Hollister@Sun.COM  * EAGAIN - failure to set up interrupts
236910696SDavid.Hollister@Sun.COM  * EIO - "" + we're now stuck partly enabled
237010696SDavid.Hollister@Sun.COM  *
237110696SDavid.Hollister@Sun.COM  * If EIO is returned, we can't unload the driver.
237210696SDavid.Hollister@Sun.COM  */
237310696SDavid.Hollister@Sun.COM static int
pmcs_setup_intr(pmcs_hw_t * pwp)237410696SDavid.Hollister@Sun.COM pmcs_setup_intr(pmcs_hw_t *pwp)
237510696SDavid.Hollister@Sun.COM {
237610696SDavid.Hollister@Sun.COM 	int i, r, itypes, oqv_count;
237710696SDavid.Hollister@Sun.COM 	ddi_intr_handler_t **iv_table;
237810696SDavid.Hollister@Sun.COM 	size_t iv_table_size;
237910696SDavid.Hollister@Sun.COM 	uint_t pri;
238010696SDavid.Hollister@Sun.COM 
238110696SDavid.Hollister@Sun.COM 	if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) {
238211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
238311048SDavid.Hollister@Sun.COM 		    "cannot get interrupt types");
238410696SDavid.Hollister@Sun.COM 		return (EAGAIN);
238510696SDavid.Hollister@Sun.COM 	}
238610696SDavid.Hollister@Sun.COM 
238710696SDavid.Hollister@Sun.COM 	if (disable_msix) {
238810696SDavid.Hollister@Sun.COM 		itypes &= ~DDI_INTR_TYPE_MSIX;
238910696SDavid.Hollister@Sun.COM 	}
239010696SDavid.Hollister@Sun.COM 	if (disable_msi) {
239110696SDavid.Hollister@Sun.COM 		itypes &= ~DDI_INTR_TYPE_MSI;
239210696SDavid.Hollister@Sun.COM 	}
239310696SDavid.Hollister@Sun.COM 
239410696SDavid.Hollister@Sun.COM 	/*
239510696SDavid.Hollister@Sun.COM 	 * We won't know what firmware we're running until we call pmcs_setup,
239610696SDavid.Hollister@Sun.COM 	 * and we can't call pmcs_setup until we establish interrupts.
239710696SDavid.Hollister@Sun.COM 	 */
239810696SDavid.Hollister@Sun.COM 
239910696SDavid.Hollister@Sun.COM 	pwp->int_type = PMCS_INT_NONE;
240010696SDavid.Hollister@Sun.COM 
240110696SDavid.Hollister@Sun.COM 	/*
240210696SDavid.Hollister@Sun.COM 	 * We want PMCS_MAX_MSIX vectors for MSI-X.  Anything less would be
240310696SDavid.Hollister@Sun.COM 	 * uncivilized.
240410696SDavid.Hollister@Sun.COM 	 */
240510696SDavid.Hollister@Sun.COM 	if (itypes & DDI_INTR_TYPE_MSIX) {
240610696SDavid.Hollister@Sun.COM 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX);
240710696SDavid.Hollister@Sun.COM 		if (pwp->int_type == PMCS_INT_MSIX) {
240810696SDavid.Hollister@Sun.COM 			itypes = 0;
240910696SDavid.Hollister@Sun.COM 		}
241010696SDavid.Hollister@Sun.COM 	}
241110696SDavid.Hollister@Sun.COM 
241210696SDavid.Hollister@Sun.COM 	if (itypes & DDI_INTR_TYPE_MSI) {
241310696SDavid.Hollister@Sun.COM 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1);
241410696SDavid.Hollister@Sun.COM 		if (pwp->int_type == PMCS_INT_MSI) {
241510696SDavid.Hollister@Sun.COM 			itypes = 0;
241610696SDavid.Hollister@Sun.COM 		}
241710696SDavid.Hollister@Sun.COM 	}
241810696SDavid.Hollister@Sun.COM 
241910696SDavid.Hollister@Sun.COM 	if (itypes & DDI_INTR_TYPE_FIXED) {
242010696SDavid.Hollister@Sun.COM 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1);
242110696SDavid.Hollister@Sun.COM 		if (pwp->int_type == PMCS_INT_FIXED) {
242210696SDavid.Hollister@Sun.COM 			itypes = 0;
242310696SDavid.Hollister@Sun.COM 		}
242410696SDavid.Hollister@Sun.COM 	}
242510696SDavid.Hollister@Sun.COM 
242610696SDavid.Hollister@Sun.COM 	if (pwp->intr_cnt == 0) {
242711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
242811048SDavid.Hollister@Sun.COM 		    "No interrupts available");
242910696SDavid.Hollister@Sun.COM 		return (EAGAIN);
243010696SDavid.Hollister@Sun.COM 	}
243110696SDavid.Hollister@Sun.COM 
243210696SDavid.Hollister@Sun.COM 	iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt;
243310696SDavid.Hollister@Sun.COM 	iv_table = kmem_alloc(iv_table_size, KM_SLEEP);
243410696SDavid.Hollister@Sun.COM 
243510696SDavid.Hollister@Sun.COM 	/*
243610696SDavid.Hollister@Sun.COM 	 * Get iblock cookie and add handlers.
243710696SDavid.Hollister@Sun.COM 	 */
243810696SDavid.Hollister@Sun.COM 	switch (pwp->intr_cnt) {
243910696SDavid.Hollister@Sun.COM 	case 1:
244010696SDavid.Hollister@Sun.COM 		iv_table[0] = pmcs_all_intr;
244110696SDavid.Hollister@Sun.COM 		break;
244210696SDavid.Hollister@Sun.COM 	case 2:
244310696SDavid.Hollister@Sun.COM 		iv_table[0] = pmcs_iodone_ix;
244410696SDavid.Hollister@Sun.COM 		iv_table[1] = pmcs_nonio_ix;
244510696SDavid.Hollister@Sun.COM 		break;
244610696SDavid.Hollister@Sun.COM 	case 4:
244710696SDavid.Hollister@Sun.COM 		iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix;
244810696SDavid.Hollister@Sun.COM 		iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix;
244910696SDavid.Hollister@Sun.COM 		iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix;
245010696SDavid.Hollister@Sun.COM 		iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix;
245110696SDavid.Hollister@Sun.COM 		break;
245210696SDavid.Hollister@Sun.COM 	default:
245311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
245410696SDavid.Hollister@Sun.COM 		    "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt);
245510696SDavid.Hollister@Sun.COM 		kmem_free(iv_table, iv_table_size);
245610696SDavid.Hollister@Sun.COM 		return (EAGAIN);
245710696SDavid.Hollister@Sun.COM 	}
245810696SDavid.Hollister@Sun.COM 
245910696SDavid.Hollister@Sun.COM 	for (i = 0; i < pwp->intr_cnt; i++) {
246010696SDavid.Hollister@Sun.COM 		r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i],
246110696SDavid.Hollister@Sun.COM 		    (caddr_t)pwp, NULL);
246210696SDavid.Hollister@Sun.COM 		if (r != DDI_SUCCESS) {
246310696SDavid.Hollister@Sun.COM 			kmem_free(iv_table, iv_table_size);
246410696SDavid.Hollister@Sun.COM 			if (pmcs_remove_ihandlers(pwp, i)) {
246510696SDavid.Hollister@Sun.COM 				return (EIO);
246610696SDavid.Hollister@Sun.COM 			}
246710696SDavid.Hollister@Sun.COM 			if (pmcs_free_intrs(pwp, i)) {
246810696SDavid.Hollister@Sun.COM 				return (EIO);
246910696SDavid.Hollister@Sun.COM 			}
247010696SDavid.Hollister@Sun.COM 			pwp->intr_cnt = 0;
247110696SDavid.Hollister@Sun.COM 			return (EAGAIN);
247210696SDavid.Hollister@Sun.COM 		}
247310696SDavid.Hollister@Sun.COM 	}
247410696SDavid.Hollister@Sun.COM 
247510696SDavid.Hollister@Sun.COM 	kmem_free(iv_table, iv_table_size);
247610696SDavid.Hollister@Sun.COM 
247710696SDavid.Hollister@Sun.COM 	if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) {
247811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
247911048SDavid.Hollister@Sun.COM 		    "unable to get int capabilities");
248010696SDavid.Hollister@Sun.COM 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
248110696SDavid.Hollister@Sun.COM 			return (EIO);
248210696SDavid.Hollister@Sun.COM 		}
248310696SDavid.Hollister@Sun.COM 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
248410696SDavid.Hollister@Sun.COM 			return (EIO);
248510696SDavid.Hollister@Sun.COM 		}
248610696SDavid.Hollister@Sun.COM 		pwp->intr_cnt = 0;
248710696SDavid.Hollister@Sun.COM 		return (EAGAIN);
248810696SDavid.Hollister@Sun.COM 	}
248910696SDavid.Hollister@Sun.COM 
249010696SDavid.Hollister@Sun.COM 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
249110696SDavid.Hollister@Sun.COM 		r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt);
249210696SDavid.Hollister@Sun.COM 		if (r != DDI_SUCCESS) {
249311048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
249411048SDavid.Hollister@Sun.COM 			    "intr blk enable failed");
249510696SDavid.Hollister@Sun.COM 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
249610696SDavid.Hollister@Sun.COM 				return (EIO);
249710696SDavid.Hollister@Sun.COM 			}
249810696SDavid.Hollister@Sun.COM 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
249910696SDavid.Hollister@Sun.COM 				return (EIO);
250010696SDavid.Hollister@Sun.COM 			}
250110696SDavid.Hollister@Sun.COM 			pwp->intr_cnt = 0;
250210696SDavid.Hollister@Sun.COM 			return (EFAULT);
250310696SDavid.Hollister@Sun.COM 		}
250410696SDavid.Hollister@Sun.COM 	} else {
250510696SDavid.Hollister@Sun.COM 		for (i = 0; i < pwp->intr_cnt; i++) {
250610696SDavid.Hollister@Sun.COM 			r = ddi_intr_enable(pwp->ih_table[i]);
250710696SDavid.Hollister@Sun.COM 			if (r == DDI_SUCCESS) {
250810696SDavid.Hollister@Sun.COM 				continue;
250910696SDavid.Hollister@Sun.COM 			}
251011048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
251110696SDavid.Hollister@Sun.COM 			    "unable to enable interrupt %d", i);
251210696SDavid.Hollister@Sun.COM 			if (pmcs_disable_intrs(pwp, i)) {
251310696SDavid.Hollister@Sun.COM 				return (EIO);
251410696SDavid.Hollister@Sun.COM 			}
251510696SDavid.Hollister@Sun.COM 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
251610696SDavid.Hollister@Sun.COM 				return (EIO);
251710696SDavid.Hollister@Sun.COM 			}
251810696SDavid.Hollister@Sun.COM 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
251910696SDavid.Hollister@Sun.COM 				return (EIO);
252010696SDavid.Hollister@Sun.COM 			}
252110696SDavid.Hollister@Sun.COM 			pwp->intr_cnt = 0;
252210696SDavid.Hollister@Sun.COM 			return (EAGAIN);
252310696SDavid.Hollister@Sun.COM 		}
252410696SDavid.Hollister@Sun.COM 	}
252510696SDavid.Hollister@Sun.COM 
252610696SDavid.Hollister@Sun.COM 	/*
252710696SDavid.Hollister@Sun.COM 	 * Set up locks.
252810696SDavid.Hollister@Sun.COM 	 */
252910696SDavid.Hollister@Sun.COM 	if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) {
253011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
253110696SDavid.Hollister@Sun.COM 		    "unable to get interrupt priority");
253210696SDavid.Hollister@Sun.COM 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
253310696SDavid.Hollister@Sun.COM 			return (EIO);
253410696SDavid.Hollister@Sun.COM 		}
253510696SDavid.Hollister@Sun.COM 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
253610696SDavid.Hollister@Sun.COM 			return (EIO);
253710696SDavid.Hollister@Sun.COM 		}
253810696SDavid.Hollister@Sun.COM 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
253910696SDavid.Hollister@Sun.COM 			return (EIO);
254010696SDavid.Hollister@Sun.COM 		}
254110696SDavid.Hollister@Sun.COM 		pwp->intr_cnt = 0;
254210696SDavid.Hollister@Sun.COM 		return (EAGAIN);
254310696SDavid.Hollister@Sun.COM 	}
254410696SDavid.Hollister@Sun.COM 
254510696SDavid.Hollister@Sun.COM 	pwp->locks_initted = 1;
254610696SDavid.Hollister@Sun.COM 	pwp->intr_pri = pri;
254710696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
254810696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
254910696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255010696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255110696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255210696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255310696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255410696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255510696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER,
255610696SDavid.Hollister@Sun.COM 	    DDI_INTR_PRI(pri));
255710696SDavid.Hollister@Sun.COM #ifdef	DEBUG
255810696SDavid.Hollister@Sun.COM 	mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
255910696SDavid.Hollister@Sun.COM #endif
256010696SDavid.Hollister@Sun.COM 	cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL);
256110696SDavid.Hollister@Sun.COM 	cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL);
256212078SJesse.Butler@Sun.COM 	cv_init(&pwp->config_cv, NULL, CV_DRIVER, NULL);
256310696SDavid.Hollister@Sun.COM 	for (i = 0; i < PMCS_NIQ; i++) {
256410696SDavid.Hollister@Sun.COM 		mutex_init(&pwp->iqp_lock[i], NULL,
256510696SDavid.Hollister@Sun.COM 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
256610696SDavid.Hollister@Sun.COM 	}
256710696SDavid.Hollister@Sun.COM 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
256810696SDavid.Hollister@Sun.COM 		mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL,
256910696SDavid.Hollister@Sun.COM 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
257010696SDavid.Hollister@Sun.COM 		cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL,
257110696SDavid.Hollister@Sun.COM 		    CV_DRIVER, NULL);
257210696SDavid.Hollister@Sun.COM 	}
257310696SDavid.Hollister@Sun.COM 
257411048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%d %s interrup%s configured",
257510696SDavid.Hollister@Sun.COM 	    pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" :
257610696SDavid.Hollister@Sun.COM 	    ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"),
257710696SDavid.Hollister@Sun.COM 	    pwp->intr_cnt == 1? "t" : "ts");
257810696SDavid.Hollister@Sun.COM 
257910696SDavid.Hollister@Sun.COM 
258010696SDavid.Hollister@Sun.COM 	/*
258110696SDavid.Hollister@Sun.COM 	 * Enable Interrupts
258210696SDavid.Hollister@Sun.COM 	 */
258310696SDavid.Hollister@Sun.COM 	if (pwp->intr_cnt > PMCS_NOQ) {
258410696SDavid.Hollister@Sun.COM 		oqv_count = pwp->intr_cnt;
258510696SDavid.Hollister@Sun.COM 	} else {
258610696SDavid.Hollister@Sun.COM 		oqv_count = PMCS_NOQ;
258710696SDavid.Hollister@Sun.COM 	}
258810696SDavid.Hollister@Sun.COM 	for (pri = 0xffffffff, i = 0; i < oqv_count; i++) {
258910696SDavid.Hollister@Sun.COM 		pri ^= (1 << i);
259010696SDavid.Hollister@Sun.COM 	}
259110696SDavid.Hollister@Sun.COM 
259210696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->lock);
259310696SDavid.Hollister@Sun.COM 	pwp->intr_mask = pri;
259410696SDavid.Hollister@Sun.COM 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
259510696SDavid.Hollister@Sun.COM 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
259610696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->lock);
259710696SDavid.Hollister@Sun.COM 
259810696SDavid.Hollister@Sun.COM 	return (0);
259910696SDavid.Hollister@Sun.COM }
260010696SDavid.Hollister@Sun.COM 
260110696SDavid.Hollister@Sun.COM static int
pmcs_teardown_intr(pmcs_hw_t * pwp)260210696SDavid.Hollister@Sun.COM pmcs_teardown_intr(pmcs_hw_t *pwp)
260310696SDavid.Hollister@Sun.COM {
260410696SDavid.Hollister@Sun.COM 	if (pwp->intr_cnt) {
260510696SDavid.Hollister@Sun.COM 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
260610696SDavid.Hollister@Sun.COM 			return (EIO);
260710696SDavid.Hollister@Sun.COM 		}
260810696SDavid.Hollister@Sun.COM 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
260910696SDavid.Hollister@Sun.COM 			return (EIO);
261010696SDavid.Hollister@Sun.COM 		}
261110696SDavid.Hollister@Sun.COM 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
261210696SDavid.Hollister@Sun.COM 			return (EIO);
261310696SDavid.Hollister@Sun.COM 		}
261410696SDavid.Hollister@Sun.COM 		pwp->intr_cnt = 0;
261510696SDavid.Hollister@Sun.COM 	}
261610696SDavid.Hollister@Sun.COM 	return (0);
261710696SDavid.Hollister@Sun.COM }
261810696SDavid.Hollister@Sun.COM 
261910696SDavid.Hollister@Sun.COM static uint_t
pmcs_general_ix(caddr_t arg1,caddr_t arg2)262010696SDavid.Hollister@Sun.COM pmcs_general_ix(caddr_t arg1, caddr_t arg2)
262110696SDavid.Hollister@Sun.COM {
262210696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
262310696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
262410696SDavid.Hollister@Sun.COM 	pmcs_general_intr(pwp);
262510696SDavid.Hollister@Sun.COM 	return (DDI_INTR_CLAIMED);
262610696SDavid.Hollister@Sun.COM }
262710696SDavid.Hollister@Sun.COM 
262810696SDavid.Hollister@Sun.COM static uint_t
pmcs_event_ix(caddr_t arg1,caddr_t arg2)262910696SDavid.Hollister@Sun.COM pmcs_event_ix(caddr_t arg1, caddr_t arg2)
263010696SDavid.Hollister@Sun.COM {
263110696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
263210696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
263310696SDavid.Hollister@Sun.COM 	pmcs_event_intr(pwp);
263410696SDavid.Hollister@Sun.COM 	return (DDI_INTR_CLAIMED);
263510696SDavid.Hollister@Sun.COM }
263610696SDavid.Hollister@Sun.COM 
263710696SDavid.Hollister@Sun.COM static uint_t
pmcs_iodone_ix(caddr_t arg1,caddr_t arg2)263810696SDavid.Hollister@Sun.COM pmcs_iodone_ix(caddr_t arg1, caddr_t arg2)
263910696SDavid.Hollister@Sun.COM {
264010696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
264110696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
264210696SDavid.Hollister@Sun.COM 
264310696SDavid.Hollister@Sun.COM 	/*
264410696SDavid.Hollister@Sun.COM 	 * It's possible that if we just turned interrupt coalescing off
264510696SDavid.Hollister@Sun.COM 	 * (and thus, re-enabled auto clear for interrupts on the I/O outbound
264610696SDavid.Hollister@Sun.COM 	 * queue) that there was an interrupt already pending.  We use
264710696SDavid.Hollister@Sun.COM 	 * io_intr_coal.int_cleared to ensure that we still drop in here and
264810696SDavid.Hollister@Sun.COM 	 * clear the appropriate interrupt bit one last time.
264910696SDavid.Hollister@Sun.COM 	 */
265010696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->ict_lock);
265110696SDavid.Hollister@Sun.COM 	if (pwp->io_intr_coal.timer_on ||
265210696SDavid.Hollister@Sun.COM 	    (pwp->io_intr_coal.int_cleared == B_FALSE)) {
265310696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
265410696SDavid.Hollister@Sun.COM 		    (1 << PMCS_OQ_IODONE));
265510696SDavid.Hollister@Sun.COM 		pwp->io_intr_coal.int_cleared = B_TRUE;
265610696SDavid.Hollister@Sun.COM 	}
265710696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->ict_lock);
265810696SDavid.Hollister@Sun.COM 
265910696SDavid.Hollister@Sun.COM 	pmcs_iodone_intr(pwp);
266010696SDavid.Hollister@Sun.COM 
266110696SDavid.Hollister@Sun.COM 	return (DDI_INTR_CLAIMED);
266210696SDavid.Hollister@Sun.COM }
266310696SDavid.Hollister@Sun.COM 
266410696SDavid.Hollister@Sun.COM static uint_t
pmcs_fatal_ix(caddr_t arg1,caddr_t arg2)266510696SDavid.Hollister@Sun.COM pmcs_fatal_ix(caddr_t arg1, caddr_t arg2)
266610696SDavid.Hollister@Sun.COM {
266710696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
266810696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
266910696SDavid.Hollister@Sun.COM 	pmcs_fatal_handler(pwp);
267010696SDavid.Hollister@Sun.COM 	return (DDI_INTR_CLAIMED);
267110696SDavid.Hollister@Sun.COM }
267210696SDavid.Hollister@Sun.COM 
267310696SDavid.Hollister@Sun.COM static uint_t
pmcs_nonio_ix(caddr_t arg1,caddr_t arg2)267410696SDavid.Hollister@Sun.COM pmcs_nonio_ix(caddr_t arg1, caddr_t arg2)
267510696SDavid.Hollister@Sun.COM {
267610696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
267710696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (void *)arg1;
267810696SDavid.Hollister@Sun.COM 	uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
267910696SDavid.Hollister@Sun.COM 
268010696SDavid.Hollister@Sun.COM 	/*
268110696SDavid.Hollister@Sun.COM 	 * Check for Fatal Interrupts
268210696SDavid.Hollister@Sun.COM 	 */
268310696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
268410696SDavid.Hollister@Sun.COM 		pmcs_fatal_handler(pwp);
268510696SDavid.Hollister@Sun.COM 		return (DDI_INTR_CLAIMED);
268610696SDavid.Hollister@Sun.COM 	}
268710696SDavid.Hollister@Sun.COM 
268810696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
268910696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
269010696SDavid.Hollister@Sun.COM 		    (1 << PMCS_OQ_GENERAL));
269110696SDavid.Hollister@Sun.COM 		pmcs_general_intr(pwp);
269210696SDavid.Hollister@Sun.COM 		pmcs_event_intr(pwp);
269310696SDavid.Hollister@Sun.COM 	}
269410696SDavid.Hollister@Sun.COM 
269510696SDavid.Hollister@Sun.COM 	return (DDI_INTR_CLAIMED);
269610696SDavid.Hollister@Sun.COM }
269710696SDavid.Hollister@Sun.COM 
269810696SDavid.Hollister@Sun.COM static uint_t
pmcs_all_intr(caddr_t arg1,caddr_t arg2)269910696SDavid.Hollister@Sun.COM pmcs_all_intr(caddr_t arg1, caddr_t arg2)
270010696SDavid.Hollister@Sun.COM {
270110696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(arg2));
270210696SDavid.Hollister@Sun.COM 	pmcs_hw_t *pwp = (void *) arg1;
270310696SDavid.Hollister@Sun.COM 	uint32_t obdb;
270410696SDavid.Hollister@Sun.COM 	int handled = 0;
270510696SDavid.Hollister@Sun.COM 
270610696SDavid.Hollister@Sun.COM 	obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
270710696SDavid.Hollister@Sun.COM 
270810696SDavid.Hollister@Sun.COM 	/*
270910696SDavid.Hollister@Sun.COM 	 * Check for Fatal Interrupts
271010696SDavid.Hollister@Sun.COM 	 */
271110696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
271210696SDavid.Hollister@Sun.COM 		pmcs_fatal_handler(pwp);
271310696SDavid.Hollister@Sun.COM 		return (DDI_INTR_CLAIMED);
271410696SDavid.Hollister@Sun.COM 	}
271510696SDavid.Hollister@Sun.COM 
271610696SDavid.Hollister@Sun.COM 	/*
271710696SDavid.Hollister@Sun.COM 	 * Check for Outbound Queue service needed
271810696SDavid.Hollister@Sun.COM 	 */
271910696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_OQ_IODONE)) {
272010696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
272110696SDavid.Hollister@Sun.COM 		    (1 << PMCS_OQ_IODONE));
272210696SDavid.Hollister@Sun.COM 		obdb ^= (1 << PMCS_OQ_IODONE);
272310696SDavid.Hollister@Sun.COM 		handled++;
272410696SDavid.Hollister@Sun.COM 		pmcs_iodone_intr(pwp);
272510696SDavid.Hollister@Sun.COM 	}
272610696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
272710696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
272810696SDavid.Hollister@Sun.COM 		    (1 << PMCS_OQ_GENERAL));
272910696SDavid.Hollister@Sun.COM 		obdb ^= (1 << PMCS_OQ_GENERAL);
273010696SDavid.Hollister@Sun.COM 		handled++;
273110696SDavid.Hollister@Sun.COM 		pmcs_general_intr(pwp);
273210696SDavid.Hollister@Sun.COM 	}
273310696SDavid.Hollister@Sun.COM 	if (obdb & (1 << PMCS_OQ_EVENTS)) {
273410696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
273510696SDavid.Hollister@Sun.COM 		    (1 << PMCS_OQ_EVENTS));
273610696SDavid.Hollister@Sun.COM 		obdb ^= (1 << PMCS_OQ_EVENTS);
273710696SDavid.Hollister@Sun.COM 		handled++;
273810696SDavid.Hollister@Sun.COM 		pmcs_event_intr(pwp);
273910696SDavid.Hollister@Sun.COM 	}
274010696SDavid.Hollister@Sun.COM 	if (obdb) {
274111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
274210696SDavid.Hollister@Sun.COM 		    "interrupt bits not handled (0x%x)", obdb);
274310696SDavid.Hollister@Sun.COM 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb);
274410696SDavid.Hollister@Sun.COM 		handled++;
274510696SDavid.Hollister@Sun.COM 	}
274610696SDavid.Hollister@Sun.COM 	if (pwp->int_type == PMCS_INT_MSI) {
274710696SDavid.Hollister@Sun.COM 		handled++;
274810696SDavid.Hollister@Sun.COM 	}
274910696SDavid.Hollister@Sun.COM 	return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
275010696SDavid.Hollister@Sun.COM }
275110696SDavid.Hollister@Sun.COM 
275210696SDavid.Hollister@Sun.COM void
pmcs_fatal_handler(pmcs_hw_t * pwp)275310696SDavid.Hollister@Sun.COM pmcs_fatal_handler(pmcs_hw_t *pwp)
275410696SDavid.Hollister@Sun.COM {
275511048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, "Fatal Interrupt caught");
275611692SJesse.Butler@Sun.COM 
275710696SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->lock);
275810696SDavid.Hollister@Sun.COM 	pwp->state = STATE_DEAD;
275911692SJesse.Butler@Sun.COM 
276011692SJesse.Butler@Sun.COM 	/*
276111692SJesse.Butler@Sun.COM 	 * Attempt a hot reset. In case of failure, pmcs_hot_reset() will
276211692SJesse.Butler@Sun.COM 	 * handle the failure and issue any required FM notifications.
276311692SJesse.Butler@Sun.COM 	 * See pmcs_subr.c for more details.
276411692SJesse.Butler@Sun.COM 	 */
276511692SJesse.Butler@Sun.COM 	if (pmcs_hot_reset(pwp)) {
276611692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
276711692SJesse.Butler@Sun.COM 		    "%s: hot reset failure", __func__);
276811692SJesse.Butler@Sun.COM 	} else {
276911692SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
277011692SJesse.Butler@Sun.COM 		    "%s: hot reset complete", __func__);
277111692SJesse.Butler@Sun.COM 		pwp->last_reset_reason = PMCS_LAST_RST_FATAL_ERROR;
277211692SJesse.Butler@Sun.COM 	}
277310696SDavid.Hollister@Sun.COM 	mutex_exit(&pwp->lock);
277410696SDavid.Hollister@Sun.COM }
277510696SDavid.Hollister@Sun.COM 
277610696SDavid.Hollister@Sun.COM /*
277710696SDavid.Hollister@Sun.COM  * Called with PHY lock and target statlock held and scratch acquired.
277810696SDavid.Hollister@Sun.COM  */
277910696SDavid.Hollister@Sun.COM boolean_t
pmcs_assign_device(pmcs_hw_t * pwp,pmcs_xscsi_t * tgt)278010696SDavid.Hollister@Sun.COM pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
278110696SDavid.Hollister@Sun.COM {
278210696SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr = tgt->phy;
278310696SDavid.Hollister@Sun.COM 
278410696SDavid.Hollister@Sun.COM 	switch (pptr->dtype) {
278510696SDavid.Hollister@Sun.COM 	case SAS:
278610696SDavid.Hollister@Sun.COM 	case EXPANDER:
278710696SDavid.Hollister@Sun.COM 		break;
278810696SDavid.Hollister@Sun.COM 	case SATA:
278910696SDavid.Hollister@Sun.COM 		tgt->ca = 1;
279010696SDavid.Hollister@Sun.COM 		break;
279110696SDavid.Hollister@Sun.COM 	default:
279211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
279310696SDavid.Hollister@Sun.COM 		    "%s: Target %p has PHY %p with invalid dtype",
279410696SDavid.Hollister@Sun.COM 		    __func__, (void *)tgt, (void *)pptr);
279510696SDavid.Hollister@Sun.COM 		return (B_FALSE);
279610696SDavid.Hollister@Sun.COM 	}
279710696SDavid.Hollister@Sun.COM 
279810696SDavid.Hollister@Sun.COM 	tgt->new = 1;
279910696SDavid.Hollister@Sun.COM 	tgt->dev_gone = 0;
280010696SDavid.Hollister@Sun.COM 	tgt->recover_wait = 0;
280110696SDavid.Hollister@Sun.COM 
280211048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
280310696SDavid.Hollister@Sun.COM 	    "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__,
280410696SDavid.Hollister@Sun.COM 	    pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
280510696SDavid.Hollister@Sun.COM 
280610696SDavid.Hollister@Sun.COM 	if (pmcs_add_new_device(pwp, tgt) != B_TRUE) {
280711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
280810696SDavid.Hollister@Sun.COM 		    "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__,
280910696SDavid.Hollister@Sun.COM 		    tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
281010696SDavid.Hollister@Sun.COM 		mutex_destroy(&tgt->statlock);
281110696SDavid.Hollister@Sun.COM 		mutex_destroy(&tgt->wqlock);
281210696SDavid.Hollister@Sun.COM 		mutex_destroy(&tgt->aqlock);
281310696SDavid.Hollister@Sun.COM 		return (B_FALSE);
281410696SDavid.Hollister@Sun.COM 	}
281510696SDavid.Hollister@Sun.COM 
281610696SDavid.Hollister@Sun.COM 	return (B_TRUE);
281710696SDavid.Hollister@Sun.COM }
281810696SDavid.Hollister@Sun.COM 
281910696SDavid.Hollister@Sun.COM /*
282010696SDavid.Hollister@Sun.COM  * Called with softstate lock held
282110696SDavid.Hollister@Sun.COM  */
282210696SDavid.Hollister@Sun.COM void
pmcs_remove_device(pmcs_hw_t * pwp,pmcs_phy_t * pptr)282310696SDavid.Hollister@Sun.COM pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
282410696SDavid.Hollister@Sun.COM {
282510696SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *xp;
282610696SDavid.Hollister@Sun.COM 	unsigned int vtgt;
282710696SDavid.Hollister@Sun.COM 
282810696SDavid.Hollister@Sun.COM 	ASSERT(mutex_owned(&pwp->lock));
282910696SDavid.Hollister@Sun.COM 
283010696SDavid.Hollister@Sun.COM 	for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) {
283110696SDavid.Hollister@Sun.COM 		xp = pwp->targets[vtgt];
283210696SDavid.Hollister@Sun.COM 		if (xp == NULL) {
283310696SDavid.Hollister@Sun.COM 			continue;
283410696SDavid.Hollister@Sun.COM 		}
283510696SDavid.Hollister@Sun.COM 
283610696SDavid.Hollister@Sun.COM 		mutex_enter(&xp->statlock);
283710696SDavid.Hollister@Sun.COM 		if (xp->phy == pptr) {
283810696SDavid.Hollister@Sun.COM 			if (xp->new) {
283910696SDavid.Hollister@Sun.COM 				xp->new = 0;
284011048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
284110696SDavid.Hollister@Sun.COM 				    "cancel config of vtgt %u", vtgt);
284210696SDavid.Hollister@Sun.COM 			} else {
284310755SJesse.Butler@Sun.COM 				pmcs_clear_xp(pwp, xp);
284411048SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
284510755SJesse.Butler@Sun.COM 				    "Removed tgt 0x%p vtgt %u",
284610696SDavid.Hollister@Sun.COM 				    (void *)xp, vtgt);
284710696SDavid.Hollister@Sun.COM 			}
284810696SDavid.Hollister@Sun.COM 			mutex_exit(&xp->statlock);
284910696SDavid.Hollister@Sun.COM 			break;
285010696SDavid.Hollister@Sun.COM 		}
285110696SDavid.Hollister@Sun.COM 		mutex_exit(&xp->statlock);
285210696SDavid.Hollister@Sun.COM 	}
285310696SDavid.Hollister@Sun.COM }
285410696SDavid.Hollister@Sun.COM 
285510696SDavid.Hollister@Sun.COM void
pmcs_prt_impl(pmcs_hw_t * pwp,pmcs_prt_level_t level,pmcs_phy_t * phyp,pmcs_xscsi_t * target,const char * fmt,...)285611048SDavid.Hollister@Sun.COM pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level,
285711048SDavid.Hollister@Sun.COM     pmcs_phy_t *phyp, pmcs_xscsi_t *target, const char *fmt, ...)
285810696SDavid.Hollister@Sun.COM {
285910696SDavid.Hollister@Sun.COM 	va_list	ap;
286010696SDavid.Hollister@Sun.COM 	int written = 0;
286110696SDavid.Hollister@Sun.COM 	char *ptr;
286210696SDavid.Hollister@Sun.COM 	uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1;
286310696SDavid.Hollister@Sun.COM 	boolean_t system_log;
286410696SDavid.Hollister@Sun.COM 	int system_log_level;
286512120SDavid.Hollister@Sun.COM 	hrtime_t hrtimestamp;
286610696SDavid.Hollister@Sun.COM 
286710696SDavid.Hollister@Sun.COM 	switch (level) {
286810696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_DEVEL:
286910696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_DEV_STATE:
287010696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_PHY_LOCKING:
287110696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_SCSI_STATUS:
287210696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_UNDERFLOW:
287310696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_CONFIG:
287410696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_IPORT:
287510696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG_MAP:
287610696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG3:
287710696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG2:
287810696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG1:
287910696SDavid.Hollister@Sun.COM 	case PMCS_PRT_DEBUG:
288010696SDavid.Hollister@Sun.COM 		system_log = B_FALSE;
288110696SDavid.Hollister@Sun.COM 		break;
288210696SDavid.Hollister@Sun.COM 	case PMCS_PRT_INFO:
288310696SDavid.Hollister@Sun.COM 		system_log = B_TRUE;
288410696SDavid.Hollister@Sun.COM 		system_log_level = CE_CONT;
288510696SDavid.Hollister@Sun.COM 		break;
288610696SDavid.Hollister@Sun.COM 	case PMCS_PRT_WARN:
288710696SDavid.Hollister@Sun.COM 		system_log = B_TRUE;
288810696SDavid.Hollister@Sun.COM 		system_log_level = CE_NOTE;
288910696SDavid.Hollister@Sun.COM 		break;
289010696SDavid.Hollister@Sun.COM 	case PMCS_PRT_ERR:
289110696SDavid.Hollister@Sun.COM 		system_log = B_TRUE;
289210696SDavid.Hollister@Sun.COM 		system_log_level = CE_WARN;
289310696SDavid.Hollister@Sun.COM 		break;
289410696SDavid.Hollister@Sun.COM 	default:
289510696SDavid.Hollister@Sun.COM 		return;
289610696SDavid.Hollister@Sun.COM 	}
289710696SDavid.Hollister@Sun.COM 
289810696SDavid.Hollister@Sun.COM 	mutex_enter(&pmcs_trace_lock);
289912120SDavid.Hollister@Sun.COM 	hrtimestamp = gethrtime();
290010696SDavid.Hollister@Sun.COM 	gethrestime(&pmcs_tbuf_ptr->timestamp);
290112120SDavid.Hollister@Sun.COM 
290212120SDavid.Hollister@Sun.COM 	if (pwp->fw_timestamp != 0) {
290312120SDavid.Hollister@Sun.COM 		/* Calculate the approximate firmware time stamp... */
290412120SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->fw_timestamp = pwp->fw_timestamp +
290512120SDavid.Hollister@Sun.COM 		    ((hrtimestamp - pwp->hrtimestamp) / PMCS_FWLOG_TIMER_DIV);
290612120SDavid.Hollister@Sun.COM 	} else {
290712120SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->fw_timestamp = 0;
290812120SDavid.Hollister@Sun.COM 	}
290912120SDavid.Hollister@Sun.COM 
291010696SDavid.Hollister@Sun.COM 	ptr = pmcs_tbuf_ptr->buf;
291111048SDavid.Hollister@Sun.COM 
291211048SDavid.Hollister@Sun.COM 	/*
291311048SDavid.Hollister@Sun.COM 	 * Store the pertinent PHY and target information if there is any
291411048SDavid.Hollister@Sun.COM 	 */
291511048SDavid.Hollister@Sun.COM 	if (target == NULL) {
291611048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->target_num = PMCS_INVALID_TARGET_NUM;
291711048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->target_ua[0] = '\0';
291811048SDavid.Hollister@Sun.COM 	} else {
291911048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->target_num = target->target_num;
292011048SDavid.Hollister@Sun.COM 		(void) strncpy(pmcs_tbuf_ptr->target_ua, target->ua,
292111048SDavid.Hollister@Sun.COM 		    PMCS_TBUF_UA_MAX_SIZE);
292211048SDavid.Hollister@Sun.COM 	}
292311048SDavid.Hollister@Sun.COM 
292411048SDavid.Hollister@Sun.COM 	if (phyp == NULL) {
292511048SDavid.Hollister@Sun.COM 		(void) memset(pmcs_tbuf_ptr->phy_sas_address, 0, 8);
292611048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->phy_path[0] = '\0';
292711048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->phy_dtype = NOTHING;
292811048SDavid.Hollister@Sun.COM 	} else {
292911048SDavid.Hollister@Sun.COM 		(void) memcpy(pmcs_tbuf_ptr->phy_sas_address,
293011048SDavid.Hollister@Sun.COM 		    phyp->sas_address, 8);
293111048SDavid.Hollister@Sun.COM 		(void) strncpy(pmcs_tbuf_ptr->phy_path, phyp->path, 32);
293211048SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->phy_dtype = phyp->dtype;
293311048SDavid.Hollister@Sun.COM 	}
293411048SDavid.Hollister@Sun.COM 
293510696SDavid.Hollister@Sun.COM 	written += snprintf(ptr, elem_size, "pmcs%d:%d: ",
293610696SDavid.Hollister@Sun.COM 	    ddi_get_instance(pwp->dip), level);
293710696SDavid.Hollister@Sun.COM 	ptr += strlen(ptr);
293810696SDavid.Hollister@Sun.COM 	va_start(ap, fmt);
293910696SDavid.Hollister@Sun.COM 	written += vsnprintf(ptr, elem_size - written, fmt, ap);
294010696SDavid.Hollister@Sun.COM 	va_end(ap);
294110696SDavid.Hollister@Sun.COM 	if (written > elem_size - 1) {
294210696SDavid.Hollister@Sun.COM 		/* Indicate truncation */
294310696SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr->buf[elem_size - 1] = '+';
294410696SDavid.Hollister@Sun.COM 	}
294510696SDavid.Hollister@Sun.COM 	if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) {
294610696SDavid.Hollister@Sun.COM 		pmcs_tbuf_ptr = pmcs_tbuf;
294710696SDavid.Hollister@Sun.COM 		pmcs_tbuf_wrap = B_TRUE;
294810696SDavid.Hollister@Sun.COM 		pmcs_tbuf_idx = 0;
294910696SDavid.Hollister@Sun.COM 	} else {
295010696SDavid.Hollister@Sun.COM 		++pmcs_tbuf_ptr;
295110696SDavid.Hollister@Sun.COM 	}
295210696SDavid.Hollister@Sun.COM 	mutex_exit(&pmcs_trace_lock);
295310696SDavid.Hollister@Sun.COM 
295410696SDavid.Hollister@Sun.COM 	/*
295510696SDavid.Hollister@Sun.COM 	 * When pmcs_force_syslog in non-zero, everything goes also
295610696SDavid.Hollister@Sun.COM 	 * to syslog, at CE_CONT level.
295710696SDavid.Hollister@Sun.COM 	 */
295810696SDavid.Hollister@Sun.COM 	if (pmcs_force_syslog) {
295910696SDavid.Hollister@Sun.COM 		system_log = B_TRUE;
296010696SDavid.Hollister@Sun.COM 		system_log_level = CE_CONT;
296110696SDavid.Hollister@Sun.COM 	}
296210696SDavid.Hollister@Sun.COM 
296310696SDavid.Hollister@Sun.COM 	/*
296410696SDavid.Hollister@Sun.COM 	 * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also
296510696SDavid.Hollister@Sun.COM 	 * goes to syslog.
296610696SDavid.Hollister@Sun.COM 	 */
296710696SDavid.Hollister@Sun.COM 	if (system_log) {
296810696SDavid.Hollister@Sun.COM 		char local[196];
296910696SDavid.Hollister@Sun.COM 
297010696SDavid.Hollister@Sun.COM 		switch (system_log_level) {
297110696SDavid.Hollister@Sun.COM 		case CE_CONT:
297210696SDavid.Hollister@Sun.COM 			(void) snprintf(local, sizeof (local), "%sINFO: ",
297310696SDavid.Hollister@Sun.COM 			    pmcs_console ? "" : "?");
297410696SDavid.Hollister@Sun.COM 			break;
297510696SDavid.Hollister@Sun.COM 		case CE_NOTE:
297610696SDavid.Hollister@Sun.COM 		case CE_WARN:
297710696SDavid.Hollister@Sun.COM 			local[0] = 0;
297810696SDavid.Hollister@Sun.COM 			break;
297910696SDavid.Hollister@Sun.COM 		default:
298010696SDavid.Hollister@Sun.COM 			return;
298110696SDavid.Hollister@Sun.COM 		}
298210696SDavid.Hollister@Sun.COM 
298310696SDavid.Hollister@Sun.COM 		ptr = local;
298410696SDavid.Hollister@Sun.COM 		ptr += strlen(local);
298510696SDavid.Hollister@Sun.COM 		(void) snprintf(ptr, (sizeof (local)) -
298610696SDavid.Hollister@Sun.COM 		    ((size_t)ptr - (size_t)local), "pmcs%d: ",
298710696SDavid.Hollister@Sun.COM 		    ddi_get_instance(pwp->dip));
298810696SDavid.Hollister@Sun.COM 		ptr += strlen(ptr);
298910696SDavid.Hollister@Sun.COM 		va_start(ap, fmt);
299010696SDavid.Hollister@Sun.COM 		(void) vsnprintf(ptr,
299110696SDavid.Hollister@Sun.COM 		    (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap);
299210696SDavid.Hollister@Sun.COM 		va_end(ap);
299310696SDavid.Hollister@Sun.COM 		if (level == CE_CONT) {
299410696SDavid.Hollister@Sun.COM 			(void) strlcat(local, "\n", sizeof (local));
299510696SDavid.Hollister@Sun.COM 		}
299610696SDavid.Hollister@Sun.COM 		cmn_err(system_log_level, local);
299710696SDavid.Hollister@Sun.COM 	}
299810696SDavid.Hollister@Sun.COM 
299910696SDavid.Hollister@Sun.COM }
300010696SDavid.Hollister@Sun.COM 
300110696SDavid.Hollister@Sun.COM /*
300210696SDavid.Hollister@Sun.COM  * pmcs_acquire_scratch
300310696SDavid.Hollister@Sun.COM  *
300410696SDavid.Hollister@Sun.COM  * If "wait" is true, the caller will wait until it can acquire the scratch.
300510696SDavid.Hollister@Sun.COM  * This implies the caller needs to be in a context where spinning for an
300610696SDavid.Hollister@Sun.COM  * indeterminate amount of time is acceptable.
300710696SDavid.Hollister@Sun.COM  */
300810696SDavid.Hollister@Sun.COM int
pmcs_acquire_scratch(pmcs_hw_t * pwp,boolean_t wait)300910696SDavid.Hollister@Sun.COM pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait)
301010696SDavid.Hollister@Sun.COM {
301110696SDavid.Hollister@Sun.COM 	int rval;
301210696SDavid.Hollister@Sun.COM 
301310696SDavid.Hollister@Sun.COM 	if (!wait) {
301410696SDavid.Hollister@Sun.COM 		return (atomic_swap_8(&pwp->scratch_locked, 1));
301510696SDavid.Hollister@Sun.COM 	}
301610696SDavid.Hollister@Sun.COM 
301710696SDavid.Hollister@Sun.COM 	/*
301810696SDavid.Hollister@Sun.COM 	 * Caller will wait for scratch.
301910696SDavid.Hollister@Sun.COM 	 */
302010696SDavid.Hollister@Sun.COM 	while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) {
302110696SDavid.Hollister@Sun.COM 		drv_usecwait(100);
302210696SDavid.Hollister@Sun.COM 	}
302310696SDavid.Hollister@Sun.COM 
302410696SDavid.Hollister@Sun.COM 	return (rval);
302510696SDavid.Hollister@Sun.COM }
302610696SDavid.Hollister@Sun.COM 
302710696SDavid.Hollister@Sun.COM void
pmcs_release_scratch(pmcs_hw_t * pwp)302810696SDavid.Hollister@Sun.COM pmcs_release_scratch(pmcs_hw_t *pwp)
302910696SDavid.Hollister@Sun.COM {
303010696SDavid.Hollister@Sun.COM 	pwp->scratch_locked = 0;
303110696SDavid.Hollister@Sun.COM }
303210696SDavid.Hollister@Sun.COM 
303312077Ssrikanth.suravajhala@oracle.com /* Called with iport_lock and phy lock held */
303412077Ssrikanth.suravajhala@oracle.com void
pmcs_create_one_phy_stats(pmcs_iport_t * iport,pmcs_phy_t * phyp)303512077Ssrikanth.suravajhala@oracle.com pmcs_create_one_phy_stats(pmcs_iport_t *iport, pmcs_phy_t *phyp)
303610696SDavid.Hollister@Sun.COM {
303710696SDavid.Hollister@Sun.COM 	sas_phy_stats_t		*ps;
303810696SDavid.Hollister@Sun.COM 	pmcs_hw_t		*pwp;
303910696SDavid.Hollister@Sun.COM 	int			ndata;
304010696SDavid.Hollister@Sun.COM 	char			ks_name[KSTAT_STRLEN];
304110696SDavid.Hollister@Sun.COM 
304212077Ssrikanth.suravajhala@oracle.com 	ASSERT(mutex_owned(&iport->lock));
304312077Ssrikanth.suravajhala@oracle.com 	pwp = iport->pwp;
304412077Ssrikanth.suravajhala@oracle.com 	ASSERT(pwp != NULL);
304512077Ssrikanth.suravajhala@oracle.com 	ASSERT(mutex_owned(&phyp->phy_lock));
304612077Ssrikanth.suravajhala@oracle.com 
304712077Ssrikanth.suravajhala@oracle.com 	if (phyp->phy_stats != NULL) {
304812077Ssrikanth.suravajhala@oracle.com 		/*
304912077Ssrikanth.suravajhala@oracle.com 		 * Delete existing kstats with name containing
305012077Ssrikanth.suravajhala@oracle.com 		 * old iport instance# and allow creation of
305112077Ssrikanth.suravajhala@oracle.com 		 * new kstats with new iport instance# in the name.
305212077Ssrikanth.suravajhala@oracle.com 		 */
305312077Ssrikanth.suravajhala@oracle.com 		kstat_delete(phyp->phy_stats);
305412077Ssrikanth.suravajhala@oracle.com 	}
305512077Ssrikanth.suravajhala@oracle.com 
305612077Ssrikanth.suravajhala@oracle.com 	ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t));
305712077Ssrikanth.suravajhala@oracle.com 
305812077Ssrikanth.suravajhala@oracle.com 	(void) snprintf(ks_name, sizeof (ks_name),
305912077Ssrikanth.suravajhala@oracle.com 	    "%s.%llx.%d.%d", ddi_driver_name(iport->dip),
306012077Ssrikanth.suravajhala@oracle.com 	    (longlong_t)pwp->sas_wwns[0],
306112077Ssrikanth.suravajhala@oracle.com 	    ddi_get_instance(iport->dip), phyp->phynum);
306212077Ssrikanth.suravajhala@oracle.com 
306312077Ssrikanth.suravajhala@oracle.com 	phyp->phy_stats = kstat_create("pmcs",
306412077Ssrikanth.suravajhala@oracle.com 	    ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS,
306512077Ssrikanth.suravajhala@oracle.com 	    KSTAT_TYPE_NAMED, ndata, 0);
306612077Ssrikanth.suravajhala@oracle.com 
306712077Ssrikanth.suravajhala@oracle.com 	if (phyp->phy_stats == NULL) {
306812077Ssrikanth.suravajhala@oracle.com 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
306912077Ssrikanth.suravajhala@oracle.com 		    "%s: Failed to create %s kstats for PHY(0x%p) at %s",
307012077Ssrikanth.suravajhala@oracle.com 		    __func__, ks_name, (void *)phyp, phyp->path);
307112258Ssrikanth.suravajhala@oracle.com 		return;
307212077Ssrikanth.suravajhala@oracle.com 	}
307312077Ssrikanth.suravajhala@oracle.com 
307412077Ssrikanth.suravajhala@oracle.com 	ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
307512077Ssrikanth.suravajhala@oracle.com 
307612077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->seconds_since_last_reset,
307712077Ssrikanth.suravajhala@oracle.com 	    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
307812077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->tx_frames,
307912077Ssrikanth.suravajhala@oracle.com 	    "TxFrames", KSTAT_DATA_ULONGLONG);
308012077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->rx_frames,
308112077Ssrikanth.suravajhala@oracle.com 	    "RxFrames", KSTAT_DATA_ULONGLONG);
308212077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->tx_words,
308312077Ssrikanth.suravajhala@oracle.com 	    "TxWords", KSTAT_DATA_ULONGLONG);
308412077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->rx_words,
308512077Ssrikanth.suravajhala@oracle.com 	    "RxWords", KSTAT_DATA_ULONGLONG);
308612077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->invalid_dword_count,
308712077Ssrikanth.suravajhala@oracle.com 	    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
308812077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->running_disparity_error_count,
308912077Ssrikanth.suravajhala@oracle.com 	    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
309012077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->loss_of_dword_sync_count,
309112077Ssrikanth.suravajhala@oracle.com 	    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
309212077Ssrikanth.suravajhala@oracle.com 	kstat_named_init(&ps->phy_reset_problem_count,
309312077Ssrikanth.suravajhala@oracle.com 	    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
309412077Ssrikanth.suravajhala@oracle.com 
309512077Ssrikanth.suravajhala@oracle.com 	phyp->phy_stats->ks_private = phyp;
309612077Ssrikanth.suravajhala@oracle.com 	phyp->phy_stats->ks_update = pmcs_update_phy_stats;
309712077Ssrikanth.suravajhala@oracle.com 	kstat_install(phyp->phy_stats);
309812077Ssrikanth.suravajhala@oracle.com }
309912077Ssrikanth.suravajhala@oracle.com 
310012077Ssrikanth.suravajhala@oracle.com static void
pmcs_create_all_phy_stats(pmcs_iport_t * iport)310112077Ssrikanth.suravajhala@oracle.com pmcs_create_all_phy_stats(pmcs_iport_t *iport)
310212077Ssrikanth.suravajhala@oracle.com {
310312077Ssrikanth.suravajhala@oracle.com 	pmcs_hw_t		*pwp;
310412077Ssrikanth.suravajhala@oracle.com 	pmcs_phy_t		*phyp;
310512077Ssrikanth.suravajhala@oracle.com 
310610696SDavid.Hollister@Sun.COM 	ASSERT(iport != NULL);
310710696SDavid.Hollister@Sun.COM 	pwp = iport->pwp;
310810696SDavid.Hollister@Sun.COM 	ASSERT(pwp != NULL);
310910696SDavid.Hollister@Sun.COM 
311010696SDavid.Hollister@Sun.COM 	mutex_enter(&iport->lock);
311110696SDavid.Hollister@Sun.COM 
311210696SDavid.Hollister@Sun.COM 	for (phyp = list_head(&iport->phys);
311310696SDavid.Hollister@Sun.COM 	    phyp != NULL;
311410696SDavid.Hollister@Sun.COM 	    phyp = list_next(&iport->phys, phyp)) {
311510696SDavid.Hollister@Sun.COM 
311612077Ssrikanth.suravajhala@oracle.com 		mutex_enter(&phyp->phy_lock);
311712077Ssrikanth.suravajhala@oracle.com 		pmcs_create_one_phy_stats(iport, phyp);
311812077Ssrikanth.suravajhala@oracle.com 		mutex_exit(&phyp->phy_lock);
311910696SDavid.Hollister@Sun.COM 	}
312010696SDavid.Hollister@Sun.COM 
312110696SDavid.Hollister@Sun.COM 	mutex_exit(&iport->lock);
312210696SDavid.Hollister@Sun.COM }
312310696SDavid.Hollister@Sun.COM 
312410696SDavid.Hollister@Sun.COM int
pmcs_update_phy_stats(kstat_t * ks,int rw)312510696SDavid.Hollister@Sun.COM pmcs_update_phy_stats(kstat_t *ks, int rw)
312610696SDavid.Hollister@Sun.COM {
312710696SDavid.Hollister@Sun.COM 	int		val, ret = DDI_FAILURE;
312810696SDavid.Hollister@Sun.COM 	pmcs_phy_t	*pptr = (pmcs_phy_t *)ks->ks_private;
312910696SDavid.Hollister@Sun.COM 	pmcs_hw_t	*pwp = pptr->pwp;
313010696SDavid.Hollister@Sun.COM 	sas_phy_stats_t	*ps = ks->ks_data;
313110696SDavid.Hollister@Sun.COM 
313210696SDavid.Hollister@Sun.COM 	_NOTE(ARGUNUSED(rw));
313310696SDavid.Hollister@Sun.COM 	ASSERT((pptr != NULL) && (pwp != NULL));
313410696SDavid.Hollister@Sun.COM 
313510696SDavid.Hollister@Sun.COM 	/*
313610696SDavid.Hollister@Sun.COM 	 * We just want to lock against other invocations of kstat;
313710696SDavid.Hollister@Sun.COM 	 * we don't need to pmcs_lock_phy() for this.
313810696SDavid.Hollister@Sun.COM 	 */
313910696SDavid.Hollister@Sun.COM 	mutex_enter(&pptr->phy_lock);
314010696SDavid.Hollister@Sun.COM 
314110696SDavid.Hollister@Sun.COM 	/* Get Stats from Chip */
314210696SDavid.Hollister@Sun.COM 	val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum);
314310696SDavid.Hollister@Sun.COM 	if (val == DDI_FAILURE)
314410696SDavid.Hollister@Sun.COM 		goto fail;
314510696SDavid.Hollister@Sun.COM 	ps->invalid_dword_count.value.ull = (unsigned long long)val;
314610696SDavid.Hollister@Sun.COM 
314710696SDavid.Hollister@Sun.COM 	val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum);
314810696SDavid.Hollister@Sun.COM 	if (val == DDI_FAILURE)
314910696SDavid.Hollister@Sun.COM 		goto fail;
315010696SDavid.Hollister@Sun.COM 	ps->running_disparity_error_count.value.ull = (unsigned long long)val;
315110696SDavid.Hollister@Sun.COM 
315210696SDavid.Hollister@Sun.COM 	val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum);
315310696SDavid.Hollister@Sun.COM 	if (val == DDI_FAILURE)
315410696SDavid.Hollister@Sun.COM 		goto fail;
315510696SDavid.Hollister@Sun.COM 	ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val;
315610696SDavid.Hollister@Sun.COM 
315710696SDavid.Hollister@Sun.COM 	val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum);
315810696SDavid.Hollister@Sun.COM 	if (val == DDI_FAILURE)
315910696SDavid.Hollister@Sun.COM 		goto fail;
316010696SDavid.Hollister@Sun.COM 	ps->phy_reset_problem_count.value.ull = (unsigned long long)val;
316110696SDavid.Hollister@Sun.COM 
316210696SDavid.Hollister@Sun.COM 	ret = DDI_SUCCESS;
316310696SDavid.Hollister@Sun.COM fail:
316410696SDavid.Hollister@Sun.COM 	mutex_exit(&pptr->phy_lock);
316510696SDavid.Hollister@Sun.COM 	return (ret);
316610696SDavid.Hollister@Sun.COM }
316710696SDavid.Hollister@Sun.COM 
316810696SDavid.Hollister@Sun.COM /*ARGSUSED*/
316910696SDavid.Hollister@Sun.COM static int
pmcs_fm_error_cb(dev_info_t * dip,ddi_fm_error_t * err,const void * impl_data)317010696SDavid.Hollister@Sun.COM pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
317110696SDavid.Hollister@Sun.COM {
317210696SDavid.Hollister@Sun.COM 	/*
317310696SDavid.Hollister@Sun.COM 	 * as the driver can always deal with an error in any dma or
317410696SDavid.Hollister@Sun.COM 	 * access handle, we can just return the fme_status value.
317510696SDavid.Hollister@Sun.COM 	 */
317610696SDavid.Hollister@Sun.COM 	pci_ereport_post(dip, err, NULL);
317710696SDavid.Hollister@Sun.COM 	return (err->fme_status);
317810696SDavid.Hollister@Sun.COM }
317910696SDavid.Hollister@Sun.COM 
318010696SDavid.Hollister@Sun.COM static void
pmcs_fm_init(pmcs_hw_t * pwp)318110696SDavid.Hollister@Sun.COM pmcs_fm_init(pmcs_hw_t *pwp)
318210696SDavid.Hollister@Sun.COM {
318310696SDavid.Hollister@Sun.COM 	ddi_iblock_cookie_t	fm_ibc;
318410696SDavid.Hollister@Sun.COM 
318510696SDavid.Hollister@Sun.COM 	/* Only register with IO Fault Services if we have some capability */
318610696SDavid.Hollister@Sun.COM 	if (pwp->fm_capabilities) {
318710696SDavid.Hollister@Sun.COM 		/* Adjust access and dma attributes for FMA */
318811236SStephen.Hanson@Sun.COM 		pwp->reg_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
318910696SDavid.Hollister@Sun.COM 		pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
319010696SDavid.Hollister@Sun.COM 		pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
319110696SDavid.Hollister@Sun.COM 		pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
319210696SDavid.Hollister@Sun.COM 		pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
319310696SDavid.Hollister@Sun.COM 
319410696SDavid.Hollister@Sun.COM 		/*
319510696SDavid.Hollister@Sun.COM 		 * Register capabilities with IO Fault Services.
319610696SDavid.Hollister@Sun.COM 		 */
319710696SDavid.Hollister@Sun.COM 		ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc);
319810696SDavid.Hollister@Sun.COM 
319910696SDavid.Hollister@Sun.COM 		/*
320010696SDavid.Hollister@Sun.COM 		 * Initialize pci ereport capabilities if ereport
320110696SDavid.Hollister@Sun.COM 		 * capable (should always be.)
320210696SDavid.Hollister@Sun.COM 		 */
320310696SDavid.Hollister@Sun.COM 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
320410696SDavid.Hollister@Sun.COM 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
320510696SDavid.Hollister@Sun.COM 			pci_ereport_setup(pwp->dip);
320610696SDavid.Hollister@Sun.COM 		}
320710696SDavid.Hollister@Sun.COM 
320810696SDavid.Hollister@Sun.COM 		/*
320910696SDavid.Hollister@Sun.COM 		 * Register error callback if error callback capable.
321010696SDavid.Hollister@Sun.COM 		 */
321110696SDavid.Hollister@Sun.COM 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
321210696SDavid.Hollister@Sun.COM 			ddi_fm_handler_register(pwp->dip,
321310696SDavid.Hollister@Sun.COM 			    pmcs_fm_error_cb, (void *) pwp);
321410696SDavid.Hollister@Sun.COM 		}
321510696SDavid.Hollister@Sun.COM 	}
321610696SDavid.Hollister@Sun.COM }
321710696SDavid.Hollister@Sun.COM 
321810696SDavid.Hollister@Sun.COM static void
pmcs_fm_fini(pmcs_hw_t * pwp)321910696SDavid.Hollister@Sun.COM pmcs_fm_fini(pmcs_hw_t *pwp)
322010696SDavid.Hollister@Sun.COM {
322110696SDavid.Hollister@Sun.COM 	/* Only unregister FMA capabilities if registered */
322210696SDavid.Hollister@Sun.COM 	if (pwp->fm_capabilities) {
322310696SDavid.Hollister@Sun.COM 		/*
322410696SDavid.Hollister@Sun.COM 		 * Un-register error callback if error callback capable.
322510696SDavid.Hollister@Sun.COM 		 */
322610696SDavid.Hollister@Sun.COM 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
322710696SDavid.Hollister@Sun.COM 			ddi_fm_handler_unregister(pwp->dip);
322810696SDavid.Hollister@Sun.COM 		}
322910696SDavid.Hollister@Sun.COM 
323010696SDavid.Hollister@Sun.COM 		/*
323110696SDavid.Hollister@Sun.COM 		 * Release any resources allocated by pci_ereport_setup()
323210696SDavid.Hollister@Sun.COM 		 */
323310696SDavid.Hollister@Sun.COM 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
323410696SDavid.Hollister@Sun.COM 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
323510696SDavid.Hollister@Sun.COM 			pci_ereport_teardown(pwp->dip);
323610696SDavid.Hollister@Sun.COM 		}
323710696SDavid.Hollister@Sun.COM 
323810696SDavid.Hollister@Sun.COM 		/* Unregister from IO Fault Services */
323910696SDavid.Hollister@Sun.COM 		ddi_fm_fini(pwp->dip);
324010696SDavid.Hollister@Sun.COM 
324110696SDavid.Hollister@Sun.COM 		/* Adjust access and dma attributes for FMA */
324211236SStephen.Hanson@Sun.COM 		pwp->reg_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC;
324310696SDavid.Hollister@Sun.COM 		pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
324410696SDavid.Hollister@Sun.COM 		pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
324510696SDavid.Hollister@Sun.COM 		pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
324610696SDavid.Hollister@Sun.COM 		pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
324710696SDavid.Hollister@Sun.COM 	}
324810696SDavid.Hollister@Sun.COM }
324910696SDavid.Hollister@Sun.COM 
325010696SDavid.Hollister@Sun.COM static boolean_t
pmcs_fabricate_wwid(pmcs_hw_t * pwp)325110696SDavid.Hollister@Sun.COM pmcs_fabricate_wwid(pmcs_hw_t *pwp)
325210696SDavid.Hollister@Sun.COM {
325310696SDavid.Hollister@Sun.COM 	char *cp, c;
325410696SDavid.Hollister@Sun.COM 	uint64_t adr;
325510696SDavid.Hollister@Sun.COM 	int i;
325610696SDavid.Hollister@Sun.COM 
325710696SDavid.Hollister@Sun.COM 	cp = &c;
325810696SDavid.Hollister@Sun.COM 	(void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr);
325911267SJesse.Butler@Sun.COM 
326010696SDavid.Hollister@Sun.COM 	if (adr == 0) {
326111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
326210696SDavid.Hollister@Sun.COM 		    "%s: No serial number available to fabricate WWN",
326310696SDavid.Hollister@Sun.COM 		    __func__);
326411267SJesse.Butler@Sun.COM 
326511267SJesse.Butler@Sun.COM 		adr = (uint64_t)gethrtime();
326610696SDavid.Hollister@Sun.COM 	}
326711267SJesse.Butler@Sun.COM 
326810696SDavid.Hollister@Sun.COM 	adr <<= 8;
326910696SDavid.Hollister@Sun.COM 	adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52);
327010696SDavid.Hollister@Sun.COM 	adr |= (5ULL << 60);
327111267SJesse.Butler@Sun.COM 
327210696SDavid.Hollister@Sun.COM 	for (i = 0; i < PMCS_MAX_PORTS; i++) {
327310696SDavid.Hollister@Sun.COM 		pwp->sas_wwns[i] = adr + i;
327410696SDavid.Hollister@Sun.COM 	}
327510696SDavid.Hollister@Sun.COM 
327610696SDavid.Hollister@Sun.COM 	return (B_TRUE);
327710696SDavid.Hollister@Sun.COM }
3278