xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_attach.c (revision 978d7443a924cda8208d6a10e72be89383bc7bec)
14c06356bSdh /*
24c06356bSdh  * CDDL HEADER START
34c06356bSdh  *
44c06356bSdh  * The contents of this file are subject to the terms of the
54c06356bSdh  * Common Development and Distribution License (the "License").
64c06356bSdh  * You may not use this file except in compliance with the License.
74c06356bSdh  *
84c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh  * See the License for the specific language governing permissions
114c06356bSdh  * and limitations under the License.
124c06356bSdh  *
134c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh  *
194c06356bSdh  * CDDL HEADER END
20658280b6SDavid Hollister  */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh  */
244c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
254c06356bSdh 
2647b47c8cSdh #define	PMCS_DRIVER_VERSION	"pmcs HBA device driver"
274c06356bSdh 
284c06356bSdh static	char	*pmcs_driver_rev = PMCS_DRIVER_VERSION;
294c06356bSdh 
304c06356bSdh /*
314c06356bSdh  * Non-DDI Compliant stuff
324c06356bSdh  */
334c06356bSdh extern char hw_serial[];
344c06356bSdh 
354c06356bSdh /*
364c06356bSdh  * Global driver data
374c06356bSdh  */
384c06356bSdh void *pmcs_softc_state = NULL;
394c06356bSdh void *pmcs_iport_softstate = NULL;
404c06356bSdh 
414c06356bSdh /*
424c06356bSdh  * Tracing and Logging info
434c06356bSdh  */
444c06356bSdh pmcs_tbuf_t *pmcs_tbuf = NULL;
454c06356bSdh uint32_t pmcs_tbuf_num_elems = 0;
464c06356bSdh pmcs_tbuf_t *pmcs_tbuf_ptr;
474c06356bSdh uint32_t pmcs_tbuf_idx = 0;
484c06356bSdh boolean_t pmcs_tbuf_wrap = B_FALSE;
491f81b464SDavid Hollister kmutex_t pmcs_trace_lock;
504c06356bSdh 
514c06356bSdh /*
524c06356bSdh  * If pmcs_force_syslog value is non-zero, all messages put in the trace log
534c06356bSdh  * will also be sent to system log.
544c06356bSdh  */
554c06356bSdh int pmcs_force_syslog = 0;
564c06356bSdh int pmcs_console = 0;
574c06356bSdh 
584c06356bSdh /*
594c06356bSdh  * External References
604c06356bSdh  */
614c06356bSdh extern int ncpus_online;
624c06356bSdh 
634c06356bSdh /*
644c06356bSdh  * Local static data
654c06356bSdh  */
664c06356bSdh static int fwlog_level = 3;
674c06356bSdh static int physpeed = PHY_LINK_ALL;
684c06356bSdh static int phymode = PHY_LM_AUTO;
694c06356bSdh static int block_mask = 0;
7060aabb4cSChris Horne static int phymap_stable_usec = 3 * MICROSEC;
7160aabb4cSChris Horne static int iportmap_stable_usec = 2 * MICROSEC;
7260aabb4cSChris Horne static int iportmap_csync_usec = 20 * MICROSEC;
734c06356bSdh 
744c06356bSdh #ifdef DEBUG
754c06356bSdh static int debug_mask = 1;
764c06356bSdh #else
774c06356bSdh static int debug_mask = 0;
784c06356bSdh #endif
794c06356bSdh 
804c06356bSdh #ifdef DISABLE_MSIX
814c06356bSdh static int disable_msix = 1;
824c06356bSdh #else
834c06356bSdh static int disable_msix = 0;
844c06356bSdh #endif
854c06356bSdh 
864c06356bSdh #ifdef DISABLE_MSI
874c06356bSdh static int disable_msi = 1;
884c06356bSdh #else
894c06356bSdh static int disable_msi = 0;
904c06356bSdh #endif
914c06356bSdh 
924c06356bSdh static uint16_t maxqdepth = 0xfffe;
934c06356bSdh 
944c06356bSdh /*
954c06356bSdh  * Local prototypes
964c06356bSdh  */
974c06356bSdh static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t);
984c06356bSdh static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t);
994c06356bSdh static int pmcs_unattach(pmcs_hw_t *);
1004c06356bSdh static int pmcs_iport_unattach(pmcs_iport_t *);
1014c06356bSdh static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long);
1024c06356bSdh static void pmcs_watchdog(void *);
1034c06356bSdh static int pmcs_setup_intr(pmcs_hw_t *);
1044c06356bSdh static int pmcs_teardown_intr(pmcs_hw_t *);
1054c06356bSdh 
1064c06356bSdh static uint_t pmcs_nonio_ix(caddr_t, caddr_t);
1074c06356bSdh static uint_t pmcs_general_ix(caddr_t, caddr_t);
1084c06356bSdh static uint_t pmcs_event_ix(caddr_t, caddr_t);
1094c06356bSdh static uint_t pmcs_iodone_ix(caddr_t, caddr_t);
1104c06356bSdh static uint_t pmcs_fatal_ix(caddr_t, caddr_t);
1114c06356bSdh static uint_t pmcs_all_intr(caddr_t, caddr_t);
1124c06356bSdh static int pmcs_quiesce(dev_info_t *dip);
1134c06356bSdh static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *);
1144c06356bSdh 
11535dae232SSrikanth Suravajhala static void pmcs_create_all_phy_stats(pmcs_iport_t *);
1164c06356bSdh int pmcs_update_phy_stats(kstat_t *, int);
1174c06356bSdh static void pmcs_destroy_phy_stats(pmcs_iport_t *);
1184c06356bSdh 
1194c06356bSdh static void pmcs_fm_fini(pmcs_hw_t *pwp);
1204c06356bSdh static void pmcs_fm_init(pmcs_hw_t *pwp);
1214c06356bSdh static int pmcs_fm_error_cb(dev_info_t *dip,
1224c06356bSdh     ddi_fm_error_t *err, const void *impl_data);
1234c06356bSdh 
1244c06356bSdh /*
1254c06356bSdh  * Local configuration data
1264c06356bSdh  */
1274c06356bSdh static struct dev_ops pmcs_ops = {
1284c06356bSdh 	DEVO_REV,		/* devo_rev, */
1294c06356bSdh 	0,			/* refcnt */
1304c06356bSdh 	ddi_no_info,		/* info */
1314c06356bSdh 	nulldev,		/* identify */
1324c06356bSdh 	nulldev,		/* probe */
1334c06356bSdh 	pmcs_attach,		/* attach */
1344c06356bSdh 	pmcs_detach,		/* detach */
1354c06356bSdh 	nodev,			/* reset */
1364c06356bSdh 	NULL,			/* driver operations */
1374c06356bSdh 	NULL,			/* bus operations */
1384c06356bSdh 	ddi_power,		/* power management */
1394c06356bSdh 	pmcs_quiesce		/* quiesce */
1404c06356bSdh };
1414c06356bSdh 
1424c06356bSdh static struct modldrv modldrv = {
1434c06356bSdh 	&mod_driverops,
1444c06356bSdh 	PMCS_DRIVER_VERSION,
1454c06356bSdh 	&pmcs_ops,	/* driver ops */
1464c06356bSdh };
1474c06356bSdh static struct modlinkage modlinkage = {
1484c06356bSdh 	MODREV_1, &modldrv, NULL
1494c06356bSdh };
1504c06356bSdh 
1514c06356bSdh const ddi_dma_attr_t pmcs_dattr = {
1524c06356bSdh 	DMA_ATTR_V0,			/* dma_attr version	*/
1534c06356bSdh 	0x0000000000000000ull,		/* dma_attr_addr_lo	*/
1544c06356bSdh 	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_addr_hi	*/
1554c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_count_max	*/
1564c06356bSdh 	0x0000000000000001ull,		/* dma_attr_align	*/
1574c06356bSdh 	0x00000078,			/* dma_attr_burstsizes	*/
1584c06356bSdh 	0x00000001,			/* dma_attr_minxfer	*/
1594c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_maxxfer	*/
1604c06356bSdh 	0x00000000FFFFFFFFull,		/* dma_attr_seg		*/
1614c06356bSdh 	1,				/* dma_attr_sgllen 	*/
1624c06356bSdh 	512,				/* dma_attr_granular 	*/
1634c06356bSdh 	0				/* dma_attr_flags 	*/
1644c06356bSdh };
1654c06356bSdh 
1664c06356bSdh static ddi_device_acc_attr_t rattr = {
167837c1ac4SStephen Hanson 	DDI_DEVICE_ATTR_V1,
1684c06356bSdh 	DDI_STRUCTURE_LE_ACC,
1694c06356bSdh 	DDI_STRICTORDER_ACC,
1704c06356bSdh 	DDI_DEFAULT_ACC
1714c06356bSdh };
1724c06356bSdh 
1734c06356bSdh 
1744c06356bSdh /*
1754c06356bSdh  * Attach/Detach functions
1764c06356bSdh  */
1774c06356bSdh 
1784c06356bSdh int
1794c06356bSdh _init(void)
1804c06356bSdh {
1814c06356bSdh 	int ret;
1824c06356bSdh 
1834c06356bSdh 	ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1);
1844c06356bSdh 	if (ret != 0) {
1854c06356bSdh 		cmn_err(CE_WARN, "?soft state init failed for pmcs");
1864c06356bSdh 		return (ret);
1874c06356bSdh 	}
1884c06356bSdh 
1894c06356bSdh 	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
1904c06356bSdh 		cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs");
1914c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
1924c06356bSdh 		return (ret);
1934c06356bSdh 	}
1944c06356bSdh 
1954c06356bSdh 	/*
1964c06356bSdh 	 * Allocate soft state for iports
1974c06356bSdh 	 */
1984c06356bSdh 	ret = ddi_soft_state_init(&pmcs_iport_softstate,
1994c06356bSdh 	    sizeof (pmcs_iport_t), 2);
2004c06356bSdh 	if (ret != 0) {
2014c06356bSdh 		cmn_err(CE_WARN, "?iport soft state init failed for pmcs");
2024c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
2034c06356bSdh 		return (ret);
2044c06356bSdh 	}
2054c06356bSdh 
2064c06356bSdh 	ret = mod_install(&modlinkage);
2074c06356bSdh 	if (ret != 0) {
2084c06356bSdh 		cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret);
2094c06356bSdh 		scsi_hba_fini(&modlinkage);
2104c06356bSdh 		ddi_soft_state_fini(&pmcs_iport_softstate);
2114c06356bSdh 		ddi_soft_state_fini(&pmcs_softc_state);
2124c06356bSdh 		return (ret);
2134c06356bSdh 	}
2144c06356bSdh 
2154c06356bSdh 	/* Initialize the global trace lock */
2164c06356bSdh 	mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL);
2174c06356bSdh 
2184c06356bSdh 	return (0);
2194c06356bSdh }
2204c06356bSdh 
2214c06356bSdh int
2224c06356bSdh _fini(void)
2234c06356bSdh {
2244c06356bSdh 	int ret;
2254c06356bSdh 	if ((ret = mod_remove(&modlinkage)) != 0) {
2264c06356bSdh 		return (ret);
2274c06356bSdh 	}
2284c06356bSdh 	scsi_hba_fini(&modlinkage);
2294c06356bSdh 
2304c06356bSdh 	/* Free pmcs log buffer and destroy the global lock */
2314c06356bSdh 	if (pmcs_tbuf) {
2324c06356bSdh 		kmem_free(pmcs_tbuf,
2334c06356bSdh 		    pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t));
2344c06356bSdh 		pmcs_tbuf = NULL;
2354c06356bSdh 	}
2364c06356bSdh 	mutex_destroy(&pmcs_trace_lock);
2374c06356bSdh 
2384c06356bSdh 	ddi_soft_state_fini(&pmcs_iport_softstate);
2394c06356bSdh 	ddi_soft_state_fini(&pmcs_softc_state);
2404c06356bSdh 	return (0);
2414c06356bSdh }
2424c06356bSdh 
2434c06356bSdh int
2444c06356bSdh _info(struct modinfo *modinfop)
2454c06356bSdh {
2464c06356bSdh 	return (mod_info(&modlinkage, modinfop));
2474c06356bSdh }
2484c06356bSdh 
2494c06356bSdh static int
2504c06356bSdh pmcs_iport_attach(dev_info_t *dip)
2514c06356bSdh {
2524c06356bSdh 	pmcs_iport_t		*iport;
2534c06356bSdh 	pmcs_hw_t		*pwp;
2544c06356bSdh 	scsi_hba_tran_t		*tran;
2554c06356bSdh 	void			*ua_priv = NULL;
2564c06356bSdh 	char			*iport_ua;
2574c06356bSdh 	char			*init_port;
2584c06356bSdh 	int			hba_inst;
2594c06356bSdh 	int			inst;
2604c06356bSdh 
2614c06356bSdh 	hba_inst = ddi_get_instance(ddi_get_parent(dip));
2624c06356bSdh 	inst = ddi_get_instance(dip);
2634c06356bSdh 
2644c06356bSdh 	pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst);
2654c06356bSdh 	if (pwp == NULL) {
266a25672a1SDavid Hollister 		cmn_err(CE_WARN, "%s: No HBA softstate for instance %d",
2674c06356bSdh 		    __func__, inst);
2684c06356bSdh 		return (DDI_FAILURE);
2694c06356bSdh 	}
2704c06356bSdh 
2714c06356bSdh 	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
2724c06356bSdh 		return (DDI_FAILURE);
2734c06356bSdh 	}
2744c06356bSdh 
2754c06356bSdh 	if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) {
276c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2774c06356bSdh 		    "%s: invoked with NULL unit address, inst (%d)",
2784c06356bSdh 		    __func__, inst);
2794c06356bSdh 		return (DDI_FAILURE);
2804c06356bSdh 	}
2814c06356bSdh 
2824c06356bSdh 	if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) {
283c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2844c06356bSdh 		    "Failed to alloc soft state for iport %d", inst);
2854c06356bSdh 		return (DDI_FAILURE);
2864c06356bSdh 	}
2874c06356bSdh 
2884c06356bSdh 	iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
2894c06356bSdh 	if (iport == NULL) {
290c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2914c06356bSdh 		    "cannot get iport soft state");
2924c06356bSdh 		goto iport_attach_fail1;
2934c06356bSdh 	}
2944c06356bSdh 
2954c06356bSdh 	mutex_init(&iport->lock, NULL, MUTEX_DRIVER,
2964c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
2974c06356bSdh 	cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL);
2986745c559SJesse Butler 	cv_init(&iport->smp_cv, NULL, CV_DEFAULT, NULL);
2994c06356bSdh 	mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER,
3004c06356bSdh 	    DDI_INTR_PRI(pwp->intr_pri));
3016745c559SJesse Butler 	mutex_init(&iport->smp_lock, NULL, MUTEX_DRIVER,
3026745c559SJesse Butler 	    DDI_INTR_PRI(pwp->intr_pri));
3034c06356bSdh 
3044c06356bSdh 	/* Set some data on the iport handle */
3054c06356bSdh 	iport->dip = dip;
3064c06356bSdh 	iport->pwp = pwp;
3074c06356bSdh 
3084c06356bSdh 	/* Dup the UA into the iport handle */
3094c06356bSdh 	iport->ua = strdup(iport_ua);
3104c06356bSdh 
3114c06356bSdh 	tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
3124c06356bSdh 	tran->tran_hba_private = iport;
3134c06356bSdh 
3144c06356bSdh 	list_create(&iport->phys, sizeof (pmcs_phy_t),
3154c06356bSdh 	    offsetof(pmcs_phy_t, list_node));
3164c06356bSdh 
3174c06356bSdh 	/*
3184c06356bSdh 	 * If our unit address is active in the phymap, configure our
3194c06356bSdh 	 * iport's phylist.
3204c06356bSdh 	 */
3214c06356bSdh 	mutex_enter(&iport->lock);
3224c06356bSdh 	ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua);
3234c06356bSdh 	if (ua_priv) {
3244c06356bSdh 		/* Non-NULL private data indicates the unit address is active */
3254c06356bSdh 		iport->ua_state = UA_ACTIVE;
3264c06356bSdh 		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
327c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
328c3bc407cSdh 			    "%s: failed to "
3294c06356bSdh 			    "configure phys on iport handle (0x%p), "
3304c06356bSdh 			    " unit address [%s]", __func__,
3314c06356bSdh 			    (void *)iport, iport_ua);
3324c06356bSdh 			mutex_exit(&iport->lock);
3334c06356bSdh 			goto iport_attach_fail2;
3344c06356bSdh 		}
3354c06356bSdh 	} else {
3364c06356bSdh 		iport->ua_state = UA_INACTIVE;
3374c06356bSdh 	}
3384c06356bSdh 	mutex_exit(&iport->lock);
3394c06356bSdh 
3404c06356bSdh 	/* Allocate string-based soft state pool for targets */
3414c06356bSdh 	iport->tgt_sstate = NULL;
3424c06356bSdh 	if (ddi_soft_state_bystr_init(&iport->tgt_sstate,
3434c06356bSdh 	    sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) {
344c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
3454c06356bSdh 		    "cannot get iport tgt soft state");
3464c06356bSdh 		goto iport_attach_fail2;
3474c06356bSdh 	}
3484c06356bSdh 
3494c06356bSdh 	/* Create this iport's target map */
3504c06356bSdh 	if (pmcs_iport_tgtmap_create(iport) == B_FALSE) {
351c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
3524c06356bSdh 		    "Failed to create tgtmap on iport %d", inst);
3534c06356bSdh 		goto iport_attach_fail3;
3544c06356bSdh 	}
3554c06356bSdh 
3564c06356bSdh 	/* Set up the 'initiator-port' DDI property on this iport */
3574c06356bSdh 	init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
3584c06356bSdh 	if (pwp->separate_ports) {
359c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
360c3bc407cSdh 		    "%s: separate ports not supported", __func__);
3614c06356bSdh 	} else {
3624c06356bSdh 		/* Set initiator-port value to the HBA's base WWN */
3634c06356bSdh 		(void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1,
3644c06356bSdh 		    init_port);
3654c06356bSdh 	}
366145e0143Sdh 
367145e0143Sdh 	mutex_enter(&iport->lock);
3684c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING,
3694c06356bSdh 	    SCSI_ADDR_PROP_INITIATOR_PORT, init_port);
3704c06356bSdh 	kmem_free(init_port, PMCS_MAX_UA_SIZE);
3714c06356bSdh 
3724c06356bSdh 	/* Set up a 'num-phys' DDI property for the iport node */
3734c06356bSdh 	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
3744c06356bSdh 	    &iport->nphy);
375145e0143Sdh 	mutex_exit(&iport->lock);
3764c06356bSdh 
3774c06356bSdh 	/* Create kstats for each of the phys in this port */
37835dae232SSrikanth Suravajhala 	pmcs_create_all_phy_stats(iport);
3794c06356bSdh 
3804c06356bSdh 	/*
3814c06356bSdh 	 * Insert this iport handle into our list and set
3824c06356bSdh 	 * iports_attached on the HBA node.
3834c06356bSdh 	 */
3844c06356bSdh 	rw_enter(&pwp->iports_lock, RW_WRITER);
3854c06356bSdh 	ASSERT(!list_link_active(&iport->list_node));
3864c06356bSdh 	list_insert_tail(&pwp->iports, iport);
3874c06356bSdh 	pwp->iports_attached = 1;
3884c06356bSdh 	pwp->num_iports++;
3894c06356bSdh 	rw_exit(&pwp->iports_lock);
3904c06356bSdh 
391c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
392c3bc407cSdh 	    "iport%d attached", inst);
3934c06356bSdh 	ddi_report_dev(dip);
3944c06356bSdh 	return (DDI_SUCCESS);
3954c06356bSdh 
3964c06356bSdh 	/* teardown and fail */
3974c06356bSdh iport_attach_fail3:
3984c06356bSdh 	ddi_soft_state_bystr_fini(&iport->tgt_sstate);
3994c06356bSdh iport_attach_fail2:
4004c06356bSdh 	list_destroy(&iport->phys);
4014c06356bSdh 	strfree(iport->ua);
4024c06356bSdh 	mutex_destroy(&iport->refcnt_lock);
4036745c559SJesse Butler 	mutex_destroy(&iport->smp_lock);
4044c06356bSdh 	cv_destroy(&iport->refcnt_cv);
4056745c559SJesse Butler 	cv_destroy(&iport->smp_cv);
4064c06356bSdh 	mutex_destroy(&iport->lock);
4074c06356bSdh iport_attach_fail1:
4084c06356bSdh 	ddi_soft_state_free(pmcs_iport_softstate, inst);
4094c06356bSdh 	return (DDI_FAILURE);
4104c06356bSdh }
4114c06356bSdh 
4124c06356bSdh static int
4134c06356bSdh pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4144c06356bSdh {
4154c06356bSdh 	scsi_hba_tran_t *tran;
4164c06356bSdh 	char chiprev, *fwsupport, hw_rev[24], fw_rev[24];
4174c06356bSdh 	off_t set3size;
4184c06356bSdh 	int inst, i;
4194c06356bSdh 	int sm_hba = 1;
4204c06356bSdh 	int protocol = 0;
4214c06356bSdh 	int num_phys = 0;
4224c06356bSdh 	pmcs_hw_t *pwp;
4234c06356bSdh 	pmcs_phy_t *phyp;
4244c06356bSdh 	uint32_t num_threads;
4254c06356bSdh 	char buf[64];
4269719310aSDavid Hollister 	char *fwl_file;
4274c06356bSdh 
4284c06356bSdh 	switch (cmd) {
4294c06356bSdh 	case DDI_ATTACH:
4304c06356bSdh 		break;
4314c06356bSdh 
4324c06356bSdh 	case DDI_PM_RESUME:
4334c06356bSdh 	case DDI_RESUME:
4344c06356bSdh 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
4354c06356bSdh 		if (!tran) {
4364c06356bSdh 			return (DDI_FAILURE);
4374c06356bSdh 		}
4384c06356bSdh 		/* No DDI_?_RESUME on iport nodes */
4394c06356bSdh 		if (scsi_hba_iport_unit_address(dip) != NULL) {
4404c06356bSdh 			return (DDI_SUCCESS);
4414c06356bSdh 		}
4424c06356bSdh 		pwp = TRAN2PMC(tran);
4434c06356bSdh 		if (pwp == NULL) {
4444c06356bSdh 			return (DDI_FAILURE);
4454c06356bSdh 		}
4464c06356bSdh 
4474c06356bSdh 		mutex_enter(&pwp->lock);
4484c06356bSdh 		pwp->suspended = 0;
4494c06356bSdh 		if (pwp->tq) {
4504c06356bSdh 			ddi_taskq_resume(pwp->tq);
4514c06356bSdh 		}
4524c06356bSdh 		mutex_exit(&pwp->lock);
4534c06356bSdh 		return (DDI_SUCCESS);
4544c06356bSdh 
4554c06356bSdh 	default:
4564c06356bSdh 		return (DDI_FAILURE);
4574c06356bSdh 	}
4584c06356bSdh 
4594c06356bSdh 	/*
4604c06356bSdh 	 * If this is an iport node, invoke iport attach.
4614c06356bSdh 	 */
4624c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
4634c06356bSdh 		return (pmcs_iport_attach(dip));
4644c06356bSdh 	}
4654c06356bSdh 
4664c06356bSdh 	/*
4674c06356bSdh 	 * From here on is attach for the HBA node
4684c06356bSdh 	 */
4694c06356bSdh 
4704c06356bSdh #ifdef	DEBUG
4714c06356bSdh 	/*
4724c06356bSdh 	 * Check to see if this unit is to be disabled.  We can't disable
4734c06356bSdh 	 * on a per-iport node.  It's either the entire HBA or nothing.
4744c06356bSdh 	 */
4754c06356bSdh 	(void) snprintf(buf, sizeof (buf),
4764c06356bSdh 	    "disable-instance-%d", ddi_get_instance(dip));
4774c06356bSdh 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4784c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) {
4794c06356bSdh 		cmn_err(CE_NOTE, "pmcs%d: disabled by configuration",
4804c06356bSdh 		    ddi_get_instance(dip));
4814c06356bSdh 		return (DDI_FAILURE);
4824c06356bSdh 	}
4834c06356bSdh #endif
4844c06356bSdh 
4854c06356bSdh 	/*
4864c06356bSdh 	 * Allocate softstate
4874c06356bSdh 	 */
4884c06356bSdh 	inst = ddi_get_instance(dip);
4894c06356bSdh 	if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) {
4904c06356bSdh 		cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst);
4914c06356bSdh 		return (DDI_FAILURE);
4924c06356bSdh 	}
4934c06356bSdh 
4944c06356bSdh 	pwp = ddi_get_soft_state(pmcs_softc_state, inst);
4954c06356bSdh 	if (pwp == NULL) {
4964c06356bSdh 		cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst);
4974c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
4984c06356bSdh 		return (DDI_FAILURE);
4994c06356bSdh 	}
5004c06356bSdh 	pwp->dip = dip;
5014c06356bSdh 	STAILQ_INIT(&pwp->dq);
5024c06356bSdh 	STAILQ_INIT(&pwp->cq);
5034c06356bSdh 	STAILQ_INIT(&pwp->wf);
5044c06356bSdh 	STAILQ_INIT(&pwp->pf);
50532b54db7SJesse Butler 
5064c06356bSdh 	/*
50732b54db7SJesse Butler 	 * Create the list for iports and init its lock.
5084c06356bSdh 	 */
5094c06356bSdh 	list_create(&pwp->iports, sizeof (pmcs_iport_t),
5104c06356bSdh 	    offsetof(pmcs_iport_t, list_node));
51132b54db7SJesse Butler 	rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL);
5124c06356bSdh 
5134c06356bSdh 	pwp->state = STATE_PROBING;
5144c06356bSdh 
5154c06356bSdh 	/*
5164c06356bSdh 	 * Get driver.conf properties
5174c06356bSdh 	 */
5184c06356bSdh 	pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5194c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask",
5204c06356bSdh 	    debug_mask);
5214c06356bSdh 	pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5224c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask",
5234c06356bSdh 	    block_mask);
5244c06356bSdh 	pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5254c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed);
5264c06356bSdh 	pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5274c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode);
5284c06356bSdh 	pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5294c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level);
5304c06356bSdh 	if (pwp->fwlog > PMCS_FWLOG_MAX) {
5314c06356bSdh 		pwp->fwlog = PMCS_FWLOG_MAX;
5324c06356bSdh 	}
5339719310aSDavid Hollister 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "pmcs-fwlogfile",
5349719310aSDavid Hollister 	    &fwl_file) == DDI_SUCCESS)) {
5359719310aSDavid Hollister 		if (snprintf(pwp->fwlogfile_aap1, MAXPATHLEN, "%s%d-aap1.0",
5369719310aSDavid Hollister 		    fwl_file, ddi_get_instance(dip)) > MAXPATHLEN) {
5379719310aSDavid Hollister 			pwp->fwlogfile_aap1[0] = '\0';
5389719310aSDavid Hollister 			pwp->fwlogfile_iop[0] = '\0';
5399719310aSDavid Hollister 		} else if (snprintf(pwp->fwlogfile_iop, MAXPATHLEN,
5409719310aSDavid Hollister 		    "%s%d-iop.0", fwl_file,
5419719310aSDavid Hollister 		    ddi_get_instance(dip)) > MAXPATHLEN) {
5429719310aSDavid Hollister 			pwp->fwlogfile_aap1[0] = '\0';
5439719310aSDavid Hollister 			pwp->fwlogfile_iop[0] = '\0';
5449719310aSDavid Hollister 		}
5459719310aSDavid Hollister 		ddi_prop_free(fwl_file);
5469719310aSDavid Hollister 	} else {
5479719310aSDavid Hollister 		pwp->fwlogfile_aap1[0] = '\0';
5489719310aSDavid Hollister 		pwp->fwlogfile_iop[0] = '\0';
5499719310aSDavid Hollister 	}
5504c06356bSdh 
551658280b6SDavid Hollister 	pwp->open_retry_interval = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
552658280b6SDavid Hollister 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-open-retry-interval",
553658280b6SDavid Hollister 	    OPEN_RETRY_INTERVAL_DEF);
554658280b6SDavid Hollister 	if (pwp->open_retry_interval > OPEN_RETRY_INTERVAL_MAX) {
555658280b6SDavid Hollister 		pwp->open_retry_interval = OPEN_RETRY_INTERVAL_MAX;
556658280b6SDavid Hollister 	}
557658280b6SDavid Hollister 
5584c06356bSdh 	mutex_enter(&pmcs_trace_lock);
5594c06356bSdh 	if (pmcs_tbuf == NULL) {
5604c06356bSdh 		/* Allocate trace buffer */
5614c06356bSdh 		pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5624c06356bSdh 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems",
5634c06356bSdh 		    PMCS_TBUF_NUM_ELEMS_DEF);
5644c06356bSdh 		if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) ||
5654c06356bSdh 		    (pmcs_tbuf_num_elems == 0)) {
5664c06356bSdh 			pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF;
5674c06356bSdh 		}
5684c06356bSdh 
5694c06356bSdh 		pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems *
5704c06356bSdh 		    sizeof (pmcs_tbuf_t), KM_SLEEP);
5714c06356bSdh 		pmcs_tbuf_ptr = pmcs_tbuf;
5724c06356bSdh 		pmcs_tbuf_idx = 0;
5734c06356bSdh 	}
5744c06356bSdh 	mutex_exit(&pmcs_trace_lock);
5754c06356bSdh 
5769719310aSDavid Hollister 	if (pwp->fwlog && strlen(pwp->fwlogfile_aap1) > 0) {
5779719310aSDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5789719310aSDavid Hollister 		    "%s: firmware event log files: %s, %s", __func__,
5799719310aSDavid Hollister 		    pwp->fwlogfile_aap1, pwp->fwlogfile_iop);
5809719310aSDavid Hollister 		pwp->fwlog_file = 1;
5819719310aSDavid Hollister 	} else {
5829719310aSDavid Hollister 		if (pwp->fwlog == 0) {
5839719310aSDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5849719310aSDavid Hollister 			    "%s: No firmware event log will be written "
5859719310aSDavid Hollister 			    "(event log disabled)", __func__);
5869719310aSDavid Hollister 		} else {
5879719310aSDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5889719310aSDavid Hollister 			    "%s: No firmware event log will be written "
5899719310aSDavid Hollister 			    "(no filename configured - too long?)", __func__);
5909719310aSDavid Hollister 		}
5919719310aSDavid Hollister 		pwp->fwlog_file = 0;
5929719310aSDavid Hollister 	}
5939719310aSDavid Hollister 
5944c06356bSdh 	disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5954c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix",
5964c06356bSdh 	    disable_msix);
5974c06356bSdh 	disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5984c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi",
5994c06356bSdh 	    disable_msi);
6004c06356bSdh 	maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6014c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth);
6024c06356bSdh 	pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6034c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0);
6044c06356bSdh 	if (pwp->fw_force_update == 0) {
6054c06356bSdh 		pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6064c06356bSdh 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
6074c06356bSdh 		    "pmcs-fw-disable-update", 0);
6084c06356bSdh 	}
6094c06356bSdh 	pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6104c06356bSdh 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries",
6114c06356bSdh 	    PMCS_NQENTRY);
6124c06356bSdh 
6134c06356bSdh 	/*
6144c06356bSdh 	 * Initialize FMA
6154c06356bSdh 	 */
6164c06356bSdh 	pwp->dev_acc_attr = pwp->reg_acc_attr = rattr;
6174c06356bSdh 	pwp->iqp_dma_attr = pwp->oqp_dma_attr =
6184c06356bSdh 	    pwp->regdump_dma_attr = pwp->cip_dma_attr =
6194c06356bSdh 	    pwp->fwlog_dma_attr = pmcs_dattr;
6204c06356bSdh 	pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip,
6214c06356bSdh 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable",
6224c06356bSdh 	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
6234c06356bSdh 	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
6244c06356bSdh 	pmcs_fm_init(pwp);
6254c06356bSdh 
6264c06356bSdh 	/*
6274c06356bSdh 	 * Map registers
6284c06356bSdh 	 */
6294c06356bSdh 	if (pci_config_setup(dip, &pwp->pci_acc_handle)) {
630c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
631c3bc407cSdh 		    "pci config setup failed");
6324c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6334c06356bSdh 		return (DDI_FAILURE);
6344c06356bSdh 	}
6354c06356bSdh 
6364c06356bSdh 	/*
6374c06356bSdh 	 * Get the size of register set 3.
6384c06356bSdh 	 */
6394c06356bSdh 	if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) {
640c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6414c06356bSdh 		    "unable to get size of register set %d", PMCS_REGSET_3);
6424c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
6434c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6444c06356bSdh 		return (DDI_FAILURE);
6454c06356bSdh 	}
6464c06356bSdh 
6474c06356bSdh 	/*
6484c06356bSdh 	 * Map registers
6494c06356bSdh 	 */
6504c06356bSdh 	pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
6514c06356bSdh 
6524c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs,
6534c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) {
654c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6554c06356bSdh 		    "failed to map Message Unit registers");
6564c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
6574c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6584c06356bSdh 		return (DDI_FAILURE);
6594c06356bSdh 	}
6604c06356bSdh 
6614c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs,
6624c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) {
663c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
664c3bc407cSdh 		    "failed to map TOP registers");
6654c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
6664c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
6674c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6684c06356bSdh 		return (DDI_FAILURE);
6694c06356bSdh 	}
6704c06356bSdh 
6714c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs,
6724c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) {
673c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
674c3bc407cSdh 		    "failed to map GSM registers");
6754c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
6764c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
6774c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
6784c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6794c06356bSdh 		return (DDI_FAILURE);
6804c06356bSdh 	}
6814c06356bSdh 
6824c06356bSdh 	if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs,
6834c06356bSdh 	    0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) {
684c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
685c3bc407cSdh 		    "failed to map MPI registers");
6864c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
6874c06356bSdh 		ddi_regs_map_free(&pwp->gsm_acc_handle);
6884c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
6894c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
6904c06356bSdh 		ddi_soft_state_free(pmcs_softc_state, inst);
6914c06356bSdh 		return (DDI_FAILURE);
6924c06356bSdh 	}
6934c06356bSdh 	pwp->mpibar =
6944c06356bSdh 	    (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size;
6954c06356bSdh 
6964c06356bSdh 	/*
6974c06356bSdh 	 * Make sure we can support this card.
6984c06356bSdh 	 */
6994c06356bSdh 	pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION);
7004c06356bSdh 
7014c06356bSdh 	switch (pwp->chiprev) {
7024c06356bSdh 	case PMCS_PM8001_REV_A:
7034c06356bSdh 	case PMCS_PM8001_REV_B:
704c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
7054c06356bSdh 		    "Rev A/B Card no longer supported");
7064c06356bSdh 		goto failure;
7074c06356bSdh 	case PMCS_PM8001_REV_C:
7084c06356bSdh 		break;
7094c06356bSdh 	default:
710c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
7114c06356bSdh 		    "Unknown chip revision (%d)", pwp->chiprev);
7124c06356bSdh 		goto failure;
7134c06356bSdh 	}
7144c06356bSdh 
7154c06356bSdh 	/*
7164c06356bSdh 	 * Allocate DMA addressable area for Inbound and Outbound Queue indices
7174c06356bSdh 	 * that the chip needs to access plus a space for scratch usage
7184c06356bSdh 	 */
7194c06356bSdh 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
7204c06356bSdh 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls,
7214c06356bSdh 	    &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip,
7224c06356bSdh 	    &pwp->ciaddr) == B_FALSE) {
723c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7244c06356bSdh 		    "Failed to setup DMA for index/scratch");
7254c06356bSdh 		goto failure;
7264c06356bSdh 	}
7274c06356bSdh 
7284c06356bSdh 	bzero(pwp->cip, ptob(1));
7294c06356bSdh 	pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE];
7304c06356bSdh 	pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE;
7314c06356bSdh 
7324c06356bSdh 	/*
7334c06356bSdh 	 * Allocate DMA S/G list chunks
7344c06356bSdh 	 */
7354c06356bSdh 	(void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES);
7364c06356bSdh 
7374c06356bSdh 	/*
7384c06356bSdh 	 * Allocate a DMA addressable area for the firmware log (if needed)
7394c06356bSdh 	 */
7404c06356bSdh 	if (pwp->fwlog) {
7414c06356bSdh 		/*
7424c06356bSdh 		 * Align to event log header and entry size
7434c06356bSdh 		 */
7444c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_align = 32;
7454c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr,
7464c06356bSdh 		    &pwp->fwlog_acchdl,
7474c06356bSdh 		    &pwp->fwlog_hndl, PMCS_FWLOG_SIZE,
7484c06356bSdh 		    (caddr_t *)&pwp->fwlogp,
7494c06356bSdh 		    &pwp->fwaddr) == B_FALSE) {
750c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7514c06356bSdh 			    "Failed to setup DMA for fwlog area");
7524c06356bSdh 			pwp->fwlog = 0;
7534c06356bSdh 		} else {
7544c06356bSdh 			bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
7559719310aSDavid Hollister 			pwp->fwlogp_aap1 = (pmcs_fw_event_hdr_t *)pwp->fwlogp;
7569719310aSDavid Hollister 			pwp->fwlogp_iop = (pmcs_fw_event_hdr_t *)((void *)
7579719310aSDavid Hollister 			    ((caddr_t)pwp->fwlogp + (PMCS_FWLOG_SIZE / 2)));
7584c06356bSdh 		}
7594c06356bSdh 	}
7604c06356bSdh 
7614c06356bSdh 	if (pwp->flash_chunk_addr == NULL) {
7624c06356bSdh 		pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE;
7634c06356bSdh 		if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr,
7644c06356bSdh 		    &pwp->regdump_acchdl,
7654c06356bSdh 		    &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE,
7664c06356bSdh 		    (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) ==
7674c06356bSdh 		    B_FALSE) {
768c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7694c06356bSdh 			    "Failed to setup DMA for register dump area");
7704c06356bSdh 			goto failure;
7714c06356bSdh 		}
7724c06356bSdh 		bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE);
7734c06356bSdh 	}
7744c06356bSdh 
7754c06356bSdh 	/*
7764c06356bSdh 	 * More bits of local initialization...
7774c06356bSdh 	 */
7784c06356bSdh 	pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0);
7794c06356bSdh 	if (pwp->tq == NULL) {
780c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
781c3bc407cSdh 		    "unable to create worker taskq");
7824c06356bSdh 		goto failure;
7834c06356bSdh 	}
7844c06356bSdh 
7854c06356bSdh 	/*
7864c06356bSdh 	 * Cache of structures for dealing with I/O completion callbacks.
7874c06356bSdh 	 */
7884c06356bSdh 	(void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst);
7894c06356bSdh 	pwp->iocomp_cb_cache = kmem_cache_create(buf,
7904c06356bSdh 	    sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0);
7914c06356bSdh 
7924c06356bSdh 	/*
7934c06356bSdh 	 * Cache of PHY structures
7944c06356bSdh 	 */
7954c06356bSdh 	(void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst);
7964c06356bSdh 	pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8,
7974c06356bSdh 	    pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp,
7984c06356bSdh 	    NULL, 0);
7994c06356bSdh 
8004c06356bSdh 	/*
8014c06356bSdh 	 * Allocate space for the I/O completion threads
8024c06356bSdh 	 */
8034c06356bSdh 	num_threads = ncpus_online;
8044c06356bSdh 	if (num_threads > PMCS_MAX_CQ_THREADS) {
8054c06356bSdh 		num_threads = PMCS_MAX_CQ_THREADS;
8064c06356bSdh 	}
8074c06356bSdh 
8084c06356bSdh 	pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) *
8094c06356bSdh 	    num_threads, KM_SLEEP);
8104c06356bSdh 	pwp->cq_info.cq_threads = num_threads;
8114c06356bSdh 	pwp->cq_info.cq_next_disp_thr = 0;
8124c06356bSdh 	pwp->cq_info.cq_stop = B_FALSE;
8134c06356bSdh 
8144c06356bSdh 	/*
8154c06356bSdh 	 * Set the quantum value in clock ticks for the I/O interrupt
8164c06356bSdh 	 * coalescing timer.
8174c06356bSdh 	 */
8184c06356bSdh 	pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS);
8194c06356bSdh 
8204c06356bSdh 	/*
8214c06356bSdh 	 * We have a delicate dance here. We need to set up
8224c06356bSdh 	 * interrupts so we know how to set up some OQC
8234c06356bSdh 	 * tables. However, while we're setting up table
8244c06356bSdh 	 * access, we may need to flash new firmware and
8254c06356bSdh 	 * reset the card, which will take some finessing.
8264c06356bSdh 	 */
8274c06356bSdh 
8284c06356bSdh 	/*
8294c06356bSdh 	 * Set up interrupts here.
8304c06356bSdh 	 */
8314c06356bSdh 	switch (pmcs_setup_intr(pwp)) {
8324c06356bSdh 	case 0:
8334c06356bSdh 		break;
8344c06356bSdh 	case EIO:
8354c06356bSdh 		pwp->stuck = 1;
8364c06356bSdh 		/* FALLTHROUGH */
8374c06356bSdh 	default:
8384c06356bSdh 		goto failure;
8394c06356bSdh 	}
8404c06356bSdh 
8414c06356bSdh 	/*
8424c06356bSdh 	 * Set these up now becuase they are used to initialize the OQC tables.
8434c06356bSdh 	 *
8444c06356bSdh 	 * If we have MSI or MSI-X interrupts set up and we have enough
8454c06356bSdh 	 * vectors for each OQ, the Outbound Queue vectors can all be the
8464c06356bSdh 	 * same as the appropriate interrupt routine will have been called
8474c06356bSdh 	 * and the doorbell register automatically cleared.
8484c06356bSdh 	 * This keeps us from having to check the Outbound Doorbell register
8494c06356bSdh 	 * when the routines for these interrupts are called.
8504c06356bSdh 	 *
8514c06356bSdh 	 * If we have Legacy INT-X interrupts set up or we didn't have enough
8524c06356bSdh 	 * MSI/MSI-X vectors to uniquely identify each OQ, we point these
8534c06356bSdh 	 * vectors to the bits we would like to have set in the Outbound
8544c06356bSdh 	 * Doorbell register because pmcs_all_intr will read the doorbell
8554c06356bSdh 	 * register to find out why we have an interrupt and write the
8564c06356bSdh 	 * corresponding 'clear' bit for that interrupt.
8574c06356bSdh 	 */
8584c06356bSdh 
8594c06356bSdh 	switch (pwp->intr_cnt) {
8604c06356bSdh 	case 1:
8614c06356bSdh 		/*
8624c06356bSdh 		 * Only one vector, so we must check all OQs for MSI.  For
8634c06356bSdh 		 * INT-X, there's only one vector anyway, so we can just
8644c06356bSdh 		 * use the outbound queue bits to keep from having to
8654c06356bSdh 		 * check each queue for each interrupt.
8664c06356bSdh 		 */
8674c06356bSdh 		if (pwp->int_type == PMCS_INT_FIXED) {
8684c06356bSdh 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8694c06356bSdh 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8704c06356bSdh 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
8714c06356bSdh 		} else {
8724c06356bSdh 			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8734c06356bSdh 			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE;
8744c06356bSdh 			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE;
8754c06356bSdh 		}
8764c06356bSdh 		break;
8774c06356bSdh 	case 2:
8784c06356bSdh 		/* With 2, we can at least isolate IODONE */
8794c06356bSdh 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8804c06356bSdh 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8814c06356bSdh 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL;
8824c06356bSdh 		break;
8834c06356bSdh 	case 4:
8844c06356bSdh 		/* With 4 vectors, everybody gets one */
8854c06356bSdh 		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8864c06356bSdh 		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8874c06356bSdh 		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
8884c06356bSdh 		break;
8894c06356bSdh 	}
8904c06356bSdh 
8914c06356bSdh 	/*
8924c06356bSdh 	 * Do the first part of setup
8934c06356bSdh 	 */
8944c06356bSdh 	if (pmcs_setup(pwp)) {
8954c06356bSdh 		goto failure;
8964c06356bSdh 	}
8974c06356bSdh 	pmcs_report_fwversion(pwp);
8984c06356bSdh 
8994c06356bSdh 	/*
9004c06356bSdh 	 * Now do some additonal allocations based upon information
9014c06356bSdh 	 * gathered during MPI setup.
9024c06356bSdh 	 */
9034c06356bSdh 	pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP);
9044c06356bSdh 	ASSERT(pwp->nphy < SAS2_PHYNUM_MAX);
9054c06356bSdh 	phyp = pwp->root_phys;
9064c06356bSdh 	for (i = 0; i < pwp->nphy; i++) {
9074c06356bSdh 		if (i < pwp->nphy-1) {
9084c06356bSdh 			phyp->sibling = (phyp + 1);
9094c06356bSdh 		}
9104c06356bSdh 		mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
9114c06356bSdh 		    DDI_INTR_PRI(pwp->intr_pri));
9124c06356bSdh 		phyp->phynum = i & SAS2_PHYNUM_MASK;
9134c06356bSdh 		pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path));
9144c06356bSdh 		phyp->pwp = pwp;
9154c06356bSdh 		phyp->device_id = PMCS_INVALID_DEVICE_ID;
916601c90f1SSrikanth, Ramana 		phyp->portid = PMCS_PHY_INVALID_PORT_ID;
9174c06356bSdh 		phyp++;
9184c06356bSdh 	}
9194c06356bSdh 
9204c06356bSdh 	pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP);
921*978d7443SSrikanth Suravajhala 	for (i = 0; i < pwp->max_cmd; i++) {
9224c06356bSdh 		pmcwork_t *pwrk = &pwp->work[i];
9234c06356bSdh 		mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER,
9244c06356bSdh 		    DDI_INTR_PRI(pwp->intr_pri));
9254c06356bSdh 		cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL);
9264c06356bSdh 		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
9274c06356bSdh 
9284c06356bSdh 	}
9294c06356bSdh 	pwp->targets = (pmcs_xscsi_t **)
9304c06356bSdh 	    kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP);
9314c06356bSdh 
9324c06356bSdh 	pwp->iqpt = (pmcs_iqp_trace_t *)
9334c06356bSdh 	    kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP);
9344c06356bSdh 	pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP);
9354c06356bSdh 	pwp->iqpt->curpos = pwp->iqpt->head;
9364c06356bSdh 	pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE;
9374c06356bSdh 
9384c06356bSdh 	/*
9394c06356bSdh 	 * Start MPI communication.
9404c06356bSdh 	 */
9414c06356bSdh 	if (pmcs_start_mpi(pwp)) {
9424c06356bSdh 		if (pmcs_soft_reset(pwp, B_FALSE)) {
9434c06356bSdh 			goto failure;
9444c06356bSdh 		}
9455c45adf0SJesse Butler 		pwp->last_reset_reason = PMCS_LAST_RST_ATTACH;
9464c06356bSdh 	}
9474c06356bSdh 
9484c06356bSdh 	/*
9494c06356bSdh 	 * Do some initial acceptance tests.
9504c06356bSdh 	 * This tests interrupts and queues.
9514c06356bSdh 	 */
9524c06356bSdh 	if (pmcs_echo_test(pwp)) {
9534c06356bSdh 		goto failure;
9544c06356bSdh 	}
9554c06356bSdh 
9564c06356bSdh 	/* Read VPD - if it exists */
9574c06356bSdh 	if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) {
958c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
959c3bc407cSdh 		    "%s: Unable to read VPD: "
9604c06356bSdh 		    "attempting to fabricate", __func__);
9614c06356bSdh 		/*
9624c06356bSdh 		 * When we release, this must goto failure and the call
9634c06356bSdh 		 * to pmcs_fabricate_wwid is removed.
9644c06356bSdh 		 */
9654c06356bSdh 		/* goto failure; */
9664c06356bSdh 		if (!pmcs_fabricate_wwid(pwp)) {
9674c06356bSdh 			goto failure;
9684c06356bSdh 		}
9694c06356bSdh 	}
9704c06356bSdh 
9714c06356bSdh 	/*
9724c06356bSdh 	 * We're now officially running
9734c06356bSdh 	 */
9744c06356bSdh 	pwp->state = STATE_RUNNING;
9754c06356bSdh 
9764c06356bSdh 	/*
9774c06356bSdh 	 * Check firmware versions and load new firmware
9784c06356bSdh 	 * if needed and reset.
9794c06356bSdh 	 */
9804c06356bSdh 	if (pmcs_firmware_update(pwp)) {
981c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
982c3bc407cSdh 		    "%s: Firmware update failed", __func__);
9834c06356bSdh 		goto failure;
9844c06356bSdh 	}
9854c06356bSdh 
9864c06356bSdh 	/*
9874c06356bSdh 	 * Create completion threads.
9884c06356bSdh 	 */
9894c06356bSdh 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
9904c06356bSdh 		pwp->cq_info.cq_thr_info[i].cq_pwp = pwp;
9914c06356bSdh 		pwp->cq_info.cq_thr_info[i].cq_thread =
9924c06356bSdh 		    thread_create(NULL, 0, pmcs_scsa_cq_run,
9934c06356bSdh 		    &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri);
9944c06356bSdh 	}
9954c06356bSdh 
9964c06356bSdh 	/*
9974c06356bSdh 	 * Create one thread to deal with the updating of the interrupt
9984c06356bSdh 	 * coalescing timer.
9994c06356bSdh 	 */
10004c06356bSdh 	pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal,
10014c06356bSdh 	    pwp, 0, &p0, TS_RUN, minclsyspri);
10024c06356bSdh 
10034c06356bSdh 	/*
10044c06356bSdh 	 * Kick off the watchdog
10054c06356bSdh 	 */
10064c06356bSdh 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
10074c06356bSdh 	    drv_usectohz(PMCS_WATCH_INTERVAL));
10084c06356bSdh 	/*
10094c06356bSdh 	 * Do the SCSI attachment code (before starting phys)
10104c06356bSdh 	 */
10114c06356bSdh 	if (pmcs_scsa_init(pwp, &pmcs_dattr)) {
10124c06356bSdh 		goto failure;
10134c06356bSdh 	}
10144c06356bSdh 	pwp->hba_attached = 1;
10154c06356bSdh 
10164c06356bSdh 	/* Check all acc & dma handles allocated in attach */
10174c06356bSdh 	if (pmcs_check_acc_dma_handle(pwp)) {
10184c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
10194c06356bSdh 		goto failure;
10204c06356bSdh 	}
10214c06356bSdh 
10224c06356bSdh 	/*
10234c06356bSdh 	 * Create the phymap for this HBA instance
10244c06356bSdh 	 */
102560aabb4cSChris Horne 	if (sas_phymap_create(dip, phymap_stable_usec, PHYMAP_MODE_SIMPLE, NULL,
10264c06356bSdh 	    pwp, pmcs_phymap_activate, pmcs_phymap_deactivate,
10274c06356bSdh 	    &pwp->hss_phymap) != DDI_SUCCESS) {
1028c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1029c3bc407cSdh 		    "%s: pmcs%d phymap_create failed", __func__, inst);
10304c06356bSdh 		goto failure;
10314c06356bSdh 	}
10324c06356bSdh 	ASSERT(pwp->hss_phymap);
10334c06356bSdh 
10344c06356bSdh 	/*
10354c06356bSdh 	 * Create the iportmap for this HBA instance
10364c06356bSdh 	 */
103760aabb4cSChris Horne 	if (scsi_hba_iportmap_create(dip, iportmap_csync_usec,
103860aabb4cSChris Horne 	    iportmap_stable_usec, &pwp->hss_iportmap) != DDI_SUCCESS) {
1039c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1040c3bc407cSdh 		    "%s: pmcs%d iportmap_create failed", __func__, inst);
10414c06356bSdh 		goto failure;
10424c06356bSdh 	}
10434c06356bSdh 	ASSERT(pwp->hss_iportmap);
10444c06356bSdh 
10454c06356bSdh 	/*
10464c06356bSdh 	 * Start the PHYs.
10474c06356bSdh 	 */
10484c06356bSdh 	if (pmcs_start_phys(pwp)) {
10494c06356bSdh 		goto failure;
10504c06356bSdh 	}
10514c06356bSdh 
10524c06356bSdh 	/*
10534c06356bSdh 	 * From this point on, we can't fail.
10544c06356bSdh 	 */
10554c06356bSdh 	ddi_report_dev(dip);
10564c06356bSdh 
10574c06356bSdh 	/* SM-HBA */
10584c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED,
10594c06356bSdh 	    &sm_hba);
10604c06356bSdh 
10614c06356bSdh 	/* SM-HBA */
10624c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION,
10634c06356bSdh 	    pmcs_driver_rev);
10644c06356bSdh 
10654c06356bSdh 	/* SM-HBA */
10664c06356bSdh 	chiprev = 'A' + pwp->chiprev;
10674c06356bSdh 	(void) snprintf(hw_rev, 2, "%s", &chiprev);
10684c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION,
10694c06356bSdh 	    hw_rev);
10704c06356bSdh 
10714c06356bSdh 	/* SM-HBA */
10724c06356bSdh 	switch (PMCS_FW_TYPE(pwp)) {
10734c06356bSdh 	case PMCS_FW_TYPE_RELEASED:
10744c06356bSdh 		fwsupport = "Released";
10754c06356bSdh 		break;
10764c06356bSdh 	case PMCS_FW_TYPE_DEVELOPMENT:
10774c06356bSdh 		fwsupport = "Development";
10784c06356bSdh 		break;
10794c06356bSdh 	case PMCS_FW_TYPE_ALPHA:
10804c06356bSdh 		fwsupport = "Alpha";
10814c06356bSdh 		break;
10824c06356bSdh 	case PMCS_FW_TYPE_BETA:
10834c06356bSdh 		fwsupport = "Beta";
10844c06356bSdh 		break;
10854c06356bSdh 	default:
10864c06356bSdh 		fwsupport = "Special";
10874c06356bSdh 		break;
10884c06356bSdh 	}
10894c06356bSdh 	(void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s",
10904c06356bSdh 	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
10914c06356bSdh 	    fwsupport);
10924c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION,
10934c06356bSdh 	    fw_rev);
10944c06356bSdh 
10954c06356bSdh 	/* SM-HBA */
10964c06356bSdh 	num_phys = pwp->nphy;
10974c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA,
10984c06356bSdh 	    &num_phys);
10994c06356bSdh 
11004c06356bSdh 	/* SM-HBA */
11014c06356bSdh 	protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
11024c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL,
11034c06356bSdh 	    &protocol);
11044c06356bSdh 
1105658280b6SDavid Hollister 	/* Receptacle properties (FMA) */
1106658280b6SDavid Hollister 	pwp->recept_labels[0] = PMCS_RECEPT_LABEL_0;
1107658280b6SDavid Hollister 	pwp->recept_pm[0] = PMCS_RECEPT_PM_0;
1108658280b6SDavid Hollister 	pwp->recept_labels[1] = PMCS_RECEPT_LABEL_1;
1109658280b6SDavid Hollister 	pwp->recept_pm[1] = PMCS_RECEPT_PM_1;
1110658280b6SDavid Hollister 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
1111658280b6SDavid Hollister 	    SCSI_HBA_PROP_RECEPTACLE_LABEL, &pwp->recept_labels[0],
1112658280b6SDavid Hollister 	    PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) {
1113658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1114658280b6SDavid Hollister 		    "%s: failed to create %s property", __func__,
1115658280b6SDavid Hollister 		    "receptacle-label");
1116658280b6SDavid Hollister 	}
11171f81b464SDavid Hollister 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
1118658280b6SDavid Hollister 	    SCSI_HBA_PROP_RECEPTACLE_PM, &pwp->recept_pm[0],
1119658280b6SDavid Hollister 	    PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) {
1120658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1121658280b6SDavid Hollister 		    "%s: failed to create %s property", __func__,
1122658280b6SDavid Hollister 		    "receptacle-pm");
1123658280b6SDavid Hollister 	}
1124658280b6SDavid Hollister 
11254c06356bSdh 	return (DDI_SUCCESS);
11264c06356bSdh 
11274c06356bSdh failure:
11284c06356bSdh 	if (pmcs_unattach(pwp)) {
11294c06356bSdh 		pwp->stuck = 1;
11304c06356bSdh 	}
11314c06356bSdh 	return (DDI_FAILURE);
11324c06356bSdh }
11334c06356bSdh 
11344c06356bSdh int
11354c06356bSdh pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
11364c06356bSdh {
11374c06356bSdh 	int inst = ddi_get_instance(dip);
11384c06356bSdh 	pmcs_iport_t	*iport = NULL;
11394c06356bSdh 	pmcs_hw_t	*pwp = NULL;
11404c06356bSdh 	scsi_hba_tran_t	*tran;
11414c06356bSdh 
11424c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
11434c06356bSdh 		/* iport node */
11444c06356bSdh 		iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
11454c06356bSdh 		ASSERT(iport);
11464c06356bSdh 		if (iport == NULL) {
11474c06356bSdh 			return (DDI_FAILURE);
11484c06356bSdh 		}
11494c06356bSdh 		pwp = iport->pwp;
11504c06356bSdh 	} else {
11514c06356bSdh 		/* hba node */
11524c06356bSdh 		pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst);
11534c06356bSdh 		ASSERT(pwp);
11544c06356bSdh 		if (pwp == NULL) {
11554c06356bSdh 			return (DDI_FAILURE);
11564c06356bSdh 		}
11574c06356bSdh 	}
11584c06356bSdh 
11594c06356bSdh 	switch (cmd) {
11604c06356bSdh 	case DDI_DETACH:
11614c06356bSdh 		if (iport) {
11624c06356bSdh 			/* iport detach */
11634c06356bSdh 			if (pmcs_iport_unattach(iport)) {
11644c06356bSdh 				return (DDI_FAILURE);
11654c06356bSdh 			}
1166c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1167c3bc407cSdh 			    "iport%d detached", inst);
11684c06356bSdh 			return (DDI_SUCCESS);
11694c06356bSdh 		} else {
11704c06356bSdh 			/* HBA detach */
11714c06356bSdh 			if (pmcs_unattach(pwp)) {
11724c06356bSdh 				return (DDI_FAILURE);
11734c06356bSdh 			}
11744c06356bSdh 			return (DDI_SUCCESS);
11754c06356bSdh 		}
11764c06356bSdh 
11774c06356bSdh 	case DDI_SUSPEND:
11784c06356bSdh 	case DDI_PM_SUSPEND:
11794c06356bSdh 		/* No DDI_SUSPEND on iport nodes */
11804c06356bSdh 		if (iport) {
11814c06356bSdh 			return (DDI_SUCCESS);
11824c06356bSdh 		}
11834c06356bSdh 
11844c06356bSdh 		if (pwp->stuck) {
11854c06356bSdh 			return (DDI_FAILURE);
11864c06356bSdh 		}
11874c06356bSdh 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
11884c06356bSdh 		if (!tran) {
11894c06356bSdh 			return (DDI_FAILURE);
11904c06356bSdh 		}
11914c06356bSdh 
11924c06356bSdh 		pwp = TRAN2PMC(tran);
11934c06356bSdh 		if (pwp == NULL) {
11944c06356bSdh 			return (DDI_FAILURE);
11954c06356bSdh 		}
11964c06356bSdh 		mutex_enter(&pwp->lock);
11974c06356bSdh 		if (pwp->tq) {
11984c06356bSdh 			ddi_taskq_suspend(pwp->tq);
11994c06356bSdh 		}
12004c06356bSdh 		pwp->suspended = 1;
12014c06356bSdh 		mutex_exit(&pwp->lock);
1202c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PMC8X6G suspending");
12034c06356bSdh 		return (DDI_SUCCESS);
12044c06356bSdh 
12054c06356bSdh 	default:
12064c06356bSdh 		return (DDI_FAILURE);
12074c06356bSdh 	}
12084c06356bSdh }
12094c06356bSdh 
12104c06356bSdh static int
12114c06356bSdh pmcs_iport_unattach(pmcs_iport_t *iport)
12124c06356bSdh {
12134c06356bSdh 	pmcs_hw_t	*pwp = iport->pwp;
12144c06356bSdh 
12154c06356bSdh 	/*
12164c06356bSdh 	 * First, check if there are still any configured targets on this
12174c06356bSdh 	 * iport.  If so, we fail detach.
12184c06356bSdh 	 */
12194c06356bSdh 	if (pmcs_iport_has_targets(pwp, iport)) {
1220c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
1221c3bc407cSdh 		    "iport%d detach failure: iport has targets (luns)",
1222c3bc407cSdh 		    ddi_get_instance(iport->dip));
12234c06356bSdh 		return (DDI_FAILURE);
12244c06356bSdh 	}
12254c06356bSdh 
12264c06356bSdh 	/*
12274c06356bSdh 	 * Remove this iport from our list if it is inactive in the phymap.
12284c06356bSdh 	 */
12294c06356bSdh 	rw_enter(&pwp->iports_lock, RW_WRITER);
12304c06356bSdh 	mutex_enter(&iport->lock);
12314c06356bSdh 
12324c06356bSdh 	if (iport->ua_state == UA_ACTIVE) {
12334c06356bSdh 		mutex_exit(&iport->lock);
12344c06356bSdh 		rw_exit(&pwp->iports_lock);
1235c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
1236c3bc407cSdh 		    "iport%d detach failure: "
12374c06356bSdh 		    "iport unit address active in phymap",
12384c06356bSdh 		    ddi_get_instance(iport->dip));
12394c06356bSdh 		return (DDI_FAILURE);
12404c06356bSdh 	}
12414c06356bSdh 
12424c06356bSdh 	/* If it's our only iport, clear iports_attached */
12434c06356bSdh 	ASSERT(pwp->num_iports >= 1);
12444c06356bSdh 	if (--pwp->num_iports == 0) {
12454c06356bSdh 		pwp->iports_attached = 0;
12464c06356bSdh 	}
12474c06356bSdh 
12484c06356bSdh 	ASSERT(list_link_active(&iport->list_node));
12494c06356bSdh 	list_remove(&pwp->iports, iport);
12504c06356bSdh 	rw_exit(&pwp->iports_lock);
12514c06356bSdh 
12524c06356bSdh 	/*
12534c06356bSdh 	 * We have removed the iport handle from the HBA's iports list,
12544c06356bSdh 	 * there will be no new references to it. Two things must be
12554c06356bSdh 	 * guarded against here.  First, we could have PHY up events,
12564c06356bSdh 	 * adding themselves to the iport->phys list and grabbing ref's
12574c06356bSdh 	 * on our iport handle.  Second, we could have existing references
12584c06356bSdh 	 * to this iport handle from a point in time prior to the list
12594c06356bSdh 	 * removal above.
12604c06356bSdh 	 *
12614c06356bSdh 	 * So first, destroy the phys list. Remove any phys that have snuck
12624c06356bSdh 	 * in after the phymap deactivate, dropping the refcnt accordingly.
12634c06356bSdh 	 * If these PHYs are still up if and when the phymap reactivates
12644c06356bSdh 	 * (i.e. when this iport reattaches), we'll populate the list with
12654c06356bSdh 	 * them and bump the refcnt back up.
12664c06356bSdh 	 */
12674c06356bSdh 	pmcs_remove_phy_from_iport(iport, NULL);
12684c06356bSdh 	ASSERT(list_is_empty(&iport->phys));
12694c06356bSdh 	list_destroy(&iport->phys);
12704c06356bSdh 	mutex_exit(&iport->lock);
12714c06356bSdh 
12724c06356bSdh 	/*
12734c06356bSdh 	 * Second, wait for any other references to this iport to be
12744c06356bSdh 	 * dropped, then continue teardown.
12754c06356bSdh 	 */
12764c06356bSdh 	mutex_enter(&iport->refcnt_lock);
12774c06356bSdh 	while (iport->refcnt != 0) {
12784c06356bSdh 		cv_wait(&iport->refcnt_cv, &iport->refcnt_lock);
12794c06356bSdh 	}
12804c06356bSdh 	mutex_exit(&iport->refcnt_lock);
12814c06356bSdh 
12824c06356bSdh 	/* Delete kstats */
12834c06356bSdh 	pmcs_destroy_phy_stats(iport);
12844c06356bSdh 
12854c06356bSdh 	/* Destroy the iport target map */
12864c06356bSdh 	if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) {
12874c06356bSdh 		return (DDI_FAILURE);
12884c06356bSdh 	}
12894c06356bSdh 
12904c06356bSdh 	/* Free the tgt soft state */
12914c06356bSdh 	if (iport->tgt_sstate != NULL) {
12924c06356bSdh 		ddi_soft_state_bystr_fini(&iport->tgt_sstate);
12934c06356bSdh 	}
12944c06356bSdh 
12954c06356bSdh 	/* Free our unit address string */
12964c06356bSdh 	strfree(iport->ua);
12974c06356bSdh 
12984c06356bSdh 	/* Finish teardown and free the softstate */
12994c06356bSdh 	mutex_destroy(&iport->refcnt_lock);
13006745c559SJesse Butler 	mutex_destroy(&iport->smp_lock);
13014c06356bSdh 	ASSERT(iport->refcnt == 0);
13024c06356bSdh 	cv_destroy(&iport->refcnt_cv);
13036745c559SJesse Butler 	cv_destroy(&iport->smp_cv);
13044c06356bSdh 	mutex_destroy(&iport->lock);
13054c06356bSdh 	ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip));
13064c06356bSdh 
13074c06356bSdh 	return (DDI_SUCCESS);
13084c06356bSdh }
13094c06356bSdh 
13104c06356bSdh static int
13114c06356bSdh pmcs_unattach(pmcs_hw_t *pwp)
13124c06356bSdh {
13134c06356bSdh 	int i;
13144c06356bSdh 	enum pwpstate curstate;
13154c06356bSdh 	pmcs_cq_thr_info_t *cqti;
13164c06356bSdh 
13174c06356bSdh 	/*
13184c06356bSdh 	 * Tear down the interrupt infrastructure.
13194c06356bSdh 	 */
13204c06356bSdh 	if (pmcs_teardown_intr(pwp)) {
13214c06356bSdh 		pwp->stuck = 1;
13224c06356bSdh 	}
13234c06356bSdh 	pwp->intr_cnt = 0;
13244c06356bSdh 
13254c06356bSdh 	/*
13264c06356bSdh 	 * Grab a lock, if initted, to set state.
13274c06356bSdh 	 */
13284c06356bSdh 	if (pwp->locks_initted) {
13294c06356bSdh 		mutex_enter(&pwp->lock);
13304c06356bSdh 		if (pwp->state != STATE_DEAD) {
13314c06356bSdh 			pwp->state = STATE_UNPROBING;
13324c06356bSdh 		}
13334c06356bSdh 		curstate = pwp->state;
13344c06356bSdh 		mutex_exit(&pwp->lock);
13354c06356bSdh 
13364c06356bSdh 		/*
13374c06356bSdh 		 * Stop the I/O completion threads.
13384c06356bSdh 		 */
13394c06356bSdh 		mutex_enter(&pwp->cq_lock);
13404c06356bSdh 		pwp->cq_info.cq_stop = B_TRUE;
13414c06356bSdh 		for (i = 0; i < pwp->cq_info.cq_threads; i++) {
13424c06356bSdh 			if (pwp->cq_info.cq_thr_info[i].cq_thread) {
13434c06356bSdh 				cqti = &pwp->cq_info.cq_thr_info[i];
13444c06356bSdh 				mutex_enter(&cqti->cq_thr_lock);
13454c06356bSdh 				cv_signal(&cqti->cq_cv);
13464c06356bSdh 				mutex_exit(&cqti->cq_thr_lock);
13474c06356bSdh 				mutex_exit(&pwp->cq_lock);
13484c06356bSdh 				thread_join(cqti->cq_thread->t_did);
13494c06356bSdh 				mutex_enter(&pwp->cq_lock);
13504c06356bSdh 			}
13514c06356bSdh 		}
13524c06356bSdh 		mutex_exit(&pwp->cq_lock);
13534c06356bSdh 
13544c06356bSdh 		/*
13554c06356bSdh 		 * Stop the interrupt coalescing timer thread
13564c06356bSdh 		 */
13574c06356bSdh 		if (pwp->ict_thread) {
13584c06356bSdh 			mutex_enter(&pwp->ict_lock);
13594c06356bSdh 			pwp->io_intr_coal.stop_thread = B_TRUE;
13604c06356bSdh 			cv_signal(&pwp->ict_cv);
13614c06356bSdh 			mutex_exit(&pwp->ict_lock);
13624c06356bSdh 			thread_join(pwp->ict_thread->t_did);
13634c06356bSdh 		}
13644c06356bSdh 	} else {
13654c06356bSdh 		if (pwp->state != STATE_DEAD) {
13664c06356bSdh 			pwp->state = STATE_UNPROBING;
13674c06356bSdh 		}
13684c06356bSdh 		curstate = pwp->state;
13694c06356bSdh 	}
13704c06356bSdh 
13714c06356bSdh 	/*
13724c06356bSdh 	 * Make sure that any pending watchdog won't
13734c06356bSdh 	 * be called from this point on out.
13744c06356bSdh 	 */
13754c06356bSdh 	(void) untimeout(pwp->wdhandle);
13764c06356bSdh 	/*
13774c06356bSdh 	 * After the above action, the watchdog
13784c06356bSdh 	 * timer that starts up the worker task
13794c06356bSdh 	 * may trigger but will exit immediately
13804c06356bSdh 	 * on triggering.
13814c06356bSdh 	 *
13824c06356bSdh 	 * Now that this is done, we can destroy
13834c06356bSdh 	 * the task queue, which will wait if we're
13844c06356bSdh 	 * running something on it.
13854c06356bSdh 	 */
13864c06356bSdh 	if (pwp->tq) {
13874c06356bSdh 		ddi_taskq_destroy(pwp->tq);
13884c06356bSdh 		pwp->tq = NULL;
13894c06356bSdh 	}
13904c06356bSdh 
13914c06356bSdh 	pmcs_fm_fini(pwp);
13924c06356bSdh 
13934c06356bSdh 	if (pwp->hba_attached) {
13944c06356bSdh 		(void) scsi_hba_detach(pwp->dip);
13954c06356bSdh 		pwp->hba_attached = 0;
13964c06356bSdh 	}
13974c06356bSdh 
13984c06356bSdh 	/*
13994c06356bSdh 	 * If the chip hasn't been marked dead, shut it down now
14004c06356bSdh 	 * to bring it back to a known state without attempting
14014c06356bSdh 	 * a soft reset.
14024c06356bSdh 	 */
14034c06356bSdh 	if (curstate != STATE_DEAD && pwp->locks_initted) {
14044c06356bSdh 		/*
14054c06356bSdh 		 * De-register all registered devices
14064c06356bSdh 		 */
14074c06356bSdh 		pmcs_deregister_devices(pwp, pwp->root_phys);
14084c06356bSdh 
14094c06356bSdh 		/*
14104c06356bSdh 		 * Stop all the phys.
14114c06356bSdh 		 */
14124c06356bSdh 		pmcs_stop_phys(pwp);
14134c06356bSdh 
14144c06356bSdh 		/*
14154c06356bSdh 		 * Shut Down Message Passing
14164c06356bSdh 		 */
14174c06356bSdh 		(void) pmcs_stop_mpi(pwp);
14184c06356bSdh 
14194c06356bSdh 		/*
14204c06356bSdh 		 * Reset chip
14214c06356bSdh 		 */
14224c06356bSdh 		(void) pmcs_soft_reset(pwp, B_FALSE);
14235c45adf0SJesse Butler 		pwp->last_reset_reason = PMCS_LAST_RST_DETACH;
14244c06356bSdh 	}
14254c06356bSdh 
14264c06356bSdh 	/*
14274c06356bSdh 	 * Turn off interrupts on the chip
14284c06356bSdh 	 */
14294c06356bSdh 	if (pwp->mpi_acc_handle) {
14304c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
14314c06356bSdh 	}
14324c06356bSdh 
143332b54db7SJesse Butler 	if (pwp->hss_iportmap != NULL) {
143432b54db7SJesse Butler 		/* Destroy the iportmap */
143532b54db7SJesse Butler 		scsi_hba_iportmap_destroy(pwp->hss_iportmap);
143632b54db7SJesse Butler 	}
143732b54db7SJesse Butler 
143832b54db7SJesse Butler 	if (pwp->hss_phymap != NULL) {
143932b54db7SJesse Butler 		/* Destroy the phymap */
144032b54db7SJesse Butler 		sas_phymap_destroy(pwp->hss_phymap);
144132b54db7SJesse Butler 	}
144232b54db7SJesse Butler 
144332b54db7SJesse Butler 	/* Destroy the iports lock and list */
144432b54db7SJesse Butler 	rw_destroy(&pwp->iports_lock);
144532b54db7SJesse Butler 	ASSERT(list_is_empty(&pwp->iports));
144632b54db7SJesse Butler 	list_destroy(&pwp->iports);
144732b54db7SJesse Butler 
14484c06356bSdh 	/* Destroy pwp's lock */
14494c06356bSdh 	if (pwp->locks_initted) {
14504c06356bSdh 		mutex_destroy(&pwp->lock);
14514c06356bSdh 		mutex_destroy(&pwp->dma_lock);
14524c06356bSdh 		mutex_destroy(&pwp->axil_lock);
14534c06356bSdh 		mutex_destroy(&pwp->cq_lock);
14544c06356bSdh 		mutex_destroy(&pwp->config_lock);
14554c06356bSdh 		mutex_destroy(&pwp->ict_lock);
14564c06356bSdh 		mutex_destroy(&pwp->wfree_lock);
14574c06356bSdh 		mutex_destroy(&pwp->pfree_lock);
14584c06356bSdh 		mutex_destroy(&pwp->dead_phylist_lock);
14594c06356bSdh #ifdef	DEBUG
14604c06356bSdh 		mutex_destroy(&pwp->dbglock);
14614c06356bSdh #endif
146232b54db7SJesse Butler 		cv_destroy(&pwp->config_cv);
14634c06356bSdh 		cv_destroy(&pwp->ict_cv);
14644c06356bSdh 		cv_destroy(&pwp->drain_cv);
14654c06356bSdh 		pwp->locks_initted = 0;
14664c06356bSdh 	}
14674c06356bSdh 
14684c06356bSdh 	/*
14694c06356bSdh 	 * Free DMA handles and associated consistent memory
14704c06356bSdh 	 */
14714c06356bSdh 	if (pwp->regdump_hndl) {
14724c06356bSdh 		if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) {
1473c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1474c3bc407cSdh 			    "Condition check failed "
14754c06356bSdh 			    "at %s():%d", __func__, __LINE__);
14764c06356bSdh 		}
14774c06356bSdh 		ddi_dma_free_handle(&pwp->regdump_hndl);
14784c06356bSdh 		ddi_dma_mem_free(&pwp->regdump_acchdl);
14794c06356bSdh 		pwp->regdump_hndl = 0;
14804c06356bSdh 	}
14814c06356bSdh 	if (pwp->fwlog_hndl) {
14824c06356bSdh 		if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) {
1483c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1484c3bc407cSdh 			    "Condition check failed "
14854c06356bSdh 			    "at %s():%d", __func__, __LINE__);
14864c06356bSdh 		}
14874c06356bSdh 		ddi_dma_free_handle(&pwp->fwlog_hndl);
14884c06356bSdh 		ddi_dma_mem_free(&pwp->fwlog_acchdl);
14894c06356bSdh 		pwp->fwlog_hndl = 0;
14904c06356bSdh 	}
14914c06356bSdh 	if (pwp->cip_handles) {
14924c06356bSdh 		if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) {
1493c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1494c3bc407cSdh 			    "Condition check failed "
14954c06356bSdh 			    "at %s():%d", __func__, __LINE__);
14964c06356bSdh 		}
14974c06356bSdh 		ddi_dma_free_handle(&pwp->cip_handles);
14984c06356bSdh 		ddi_dma_mem_free(&pwp->cip_acchdls);
14994c06356bSdh 		pwp->cip_handles = 0;
15004c06356bSdh 	}
15014c06356bSdh 	for (i = 0; i < PMCS_NOQ; i++) {
15024c06356bSdh 		if (pwp->oqp_handles[i]) {
15034c06356bSdh 			if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) !=
15044c06356bSdh 			    DDI_SUCCESS) {
1505c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1506c3bc407cSdh 				    "Condition check failed at %s():%d",
1507c3bc407cSdh 				    __func__, __LINE__);
15084c06356bSdh 			}
15094c06356bSdh 			ddi_dma_free_handle(&pwp->oqp_handles[i]);
15104c06356bSdh 			ddi_dma_mem_free(&pwp->oqp_acchdls[i]);
15114c06356bSdh 			pwp->oqp_handles[i] = 0;
15124c06356bSdh 		}
15134c06356bSdh 	}
15144c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
15154c06356bSdh 		if (pwp->iqp_handles[i]) {
15164c06356bSdh 			if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) !=
15174c06356bSdh 			    DDI_SUCCESS) {
1518c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1519c3bc407cSdh 				    "Condition check failed at %s():%d",
1520c3bc407cSdh 				    __func__, __LINE__);
15214c06356bSdh 			}
15224c06356bSdh 			ddi_dma_free_handle(&pwp->iqp_handles[i]);
15234c06356bSdh 			ddi_dma_mem_free(&pwp->iqp_acchdls[i]);
15244c06356bSdh 			pwp->iqp_handles[i] = 0;
15254c06356bSdh 		}
15264c06356bSdh 	}
15274c06356bSdh 
15284c06356bSdh 	pmcs_free_dma_chunklist(pwp);
15294c06356bSdh 
15304c06356bSdh 	/*
15314c06356bSdh 	 * Unmap registers and destroy access handles
15324c06356bSdh 	 */
15334c06356bSdh 	if (pwp->mpi_acc_handle) {
15344c06356bSdh 		ddi_regs_map_free(&pwp->mpi_acc_handle);
15354c06356bSdh 		pwp->mpi_acc_handle = 0;
15364c06356bSdh 	}
15374c06356bSdh 	if (pwp->top_acc_handle) {
15384c06356bSdh 		ddi_regs_map_free(&pwp->top_acc_handle);
15394c06356bSdh 		pwp->top_acc_handle = 0;
15404c06356bSdh 	}
15414c06356bSdh 	if (pwp->gsm_acc_handle) {
15424c06356bSdh 		ddi_regs_map_free(&pwp->gsm_acc_handle);
15434c06356bSdh 		pwp->gsm_acc_handle = 0;
15444c06356bSdh 	}
15454c06356bSdh 	if (pwp->msg_acc_handle) {
15464c06356bSdh 		ddi_regs_map_free(&pwp->msg_acc_handle);
15474c06356bSdh 		pwp->msg_acc_handle = 0;
15484c06356bSdh 	}
15494c06356bSdh 	if (pwp->pci_acc_handle) {
15504c06356bSdh 		pci_config_teardown(&pwp->pci_acc_handle);
15514c06356bSdh 		pwp->pci_acc_handle = 0;
15524c06356bSdh 	}
15534c06356bSdh 
15544c06356bSdh 	/*
15554c06356bSdh 	 * Do memory allocation cleanup.
15564c06356bSdh 	 */
15574c06356bSdh 	while (pwp->dma_freelist) {
15584c06356bSdh 		pmcs_dmachunk_t *this = pwp->dma_freelist;
15594c06356bSdh 		pwp->dma_freelist = this->nxt;
15604c06356bSdh 		kmem_free(this, sizeof (pmcs_dmachunk_t));
15614c06356bSdh 	}
15624c06356bSdh 
15634c06356bSdh 	/*
15644c06356bSdh 	 * Free pools
15654c06356bSdh 	 */
15664c06356bSdh 	if (pwp->iocomp_cb_cache) {
15674c06356bSdh 		kmem_cache_destroy(pwp->iocomp_cb_cache);
15684c06356bSdh 	}
15694c06356bSdh 
15704c06356bSdh 	/*
15714c06356bSdh 	 * Free all PHYs (at level > 0), then free the cache
15724c06356bSdh 	 */
15734c06356bSdh 	pmcs_free_all_phys(pwp, pwp->root_phys);
15744c06356bSdh 	if (pwp->phy_cache) {
15754c06356bSdh 		kmem_cache_destroy(pwp->phy_cache);
15764c06356bSdh 	}
15774c06356bSdh 
15784c06356bSdh 	/*
15794c06356bSdh 	 * Free root PHYs
15804c06356bSdh 	 */
15814c06356bSdh 	if (pwp->root_phys) {
15824c06356bSdh 		pmcs_phy_t *phyp = pwp->root_phys;
15834c06356bSdh 		for (i = 0; i < pwp->nphy; i++) {
15844c06356bSdh 			mutex_destroy(&phyp->phy_lock);
15854c06356bSdh 			phyp = phyp->sibling;
15864c06356bSdh 		}
15874c06356bSdh 		kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t));
15884c06356bSdh 		pwp->root_phys = NULL;
15894c06356bSdh 		pwp->nphy = 0;
15904c06356bSdh 	}
15914c06356bSdh 
15924c06356bSdh 	/* Free the targets list */
15934c06356bSdh 	if (pwp->targets) {
15944c06356bSdh 		kmem_free(pwp->targets,
15954c06356bSdh 		    sizeof (pmcs_xscsi_t *) * pwp->max_dev);
15964c06356bSdh 	}
15974c06356bSdh 
15984c06356bSdh 	/*
15994c06356bSdh 	 * Free work structures
16004c06356bSdh 	 */
16014c06356bSdh 
16024c06356bSdh 	if (pwp->work && pwp->max_cmd) {
1603*978d7443SSrikanth Suravajhala 		for (i = 0; i < pwp->max_cmd; i++) {
16044c06356bSdh 			pmcwork_t *pwrk = &pwp->work[i];
16054c06356bSdh 			mutex_destroy(&pwrk->lock);
16064c06356bSdh 			cv_destroy(&pwrk->sleep_cv);
16074c06356bSdh 		}
16084c06356bSdh 		kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
16094c06356bSdh 		pwp->work = NULL;
16104c06356bSdh 		pwp->max_cmd = 0;
16114c06356bSdh 	}
16124c06356bSdh 
16134c06356bSdh 	/*
16144c06356bSdh 	 * Do last property and SCSA cleanup
16154c06356bSdh 	 */
16164c06356bSdh 	if (pwp->tran) {
16174c06356bSdh 		scsi_hba_tran_free(pwp->tran);
16184c06356bSdh 		pwp->tran = NULL;
16194c06356bSdh 	}
16204c06356bSdh 	if (pwp->reset_notify_listf) {
16214c06356bSdh 		scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf);
16224c06356bSdh 		pwp->reset_notify_listf = NULL;
16234c06356bSdh 	}
16244c06356bSdh 	ddi_prop_remove_all(pwp->dip);
16254c06356bSdh 	if (pwp->stuck) {
16264c06356bSdh 		return (-1);
16274c06356bSdh 	}
16284c06356bSdh 
16294c06356bSdh 	/* Free register dump area if allocated */
16304c06356bSdh 	if (pwp->regdumpp) {
16314c06356bSdh 		kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE);
16324c06356bSdh 		pwp->regdumpp = NULL;
16334c06356bSdh 	}
16344c06356bSdh 	if (pwp->iqpt && pwp->iqpt->head) {
16354c06356bSdh 		kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE);
16364c06356bSdh 		pwp->iqpt->head = pwp->iqpt->curpos = NULL;
16374c06356bSdh 	}
16384c06356bSdh 	if (pwp->iqpt) {
16394c06356bSdh 		kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t));
16404c06356bSdh 		pwp->iqpt = NULL;
16414c06356bSdh 	}
16424c06356bSdh 
16434c06356bSdh 	ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip));
16444c06356bSdh 	return (0);
16454c06356bSdh }
16464c06356bSdh 
16474c06356bSdh /*
16484c06356bSdh  * quiesce (9E) entry point
16494c06356bSdh  *
16504c06356bSdh  * This function is called when the system is single-threaded at high PIL
16514c06356bSdh  * with preemption disabled. Therefore, the function must not block/wait/sleep.
16524c06356bSdh  *
16534c06356bSdh  * Returns DDI_SUCCESS or DDI_FAILURE.
16544c06356bSdh  *
16554c06356bSdh  */
16564c06356bSdh static int
16574c06356bSdh pmcs_quiesce(dev_info_t *dip)
16584c06356bSdh {
16594c06356bSdh 	pmcs_hw_t	*pwp;
16604c06356bSdh 	scsi_hba_tran_t	*tran;
16614c06356bSdh 
16624c06356bSdh 	if ((tran = ddi_get_driver_private(dip)) == NULL)
16634c06356bSdh 		return (DDI_SUCCESS);
16644c06356bSdh 
16654c06356bSdh 	/* No quiesce necessary on a per-iport basis */
16664c06356bSdh 	if (scsi_hba_iport_unit_address(dip) != NULL) {
16674c06356bSdh 		return (DDI_SUCCESS);
16684c06356bSdh 	}
16694c06356bSdh 
16704c06356bSdh 	if ((pwp = TRAN2PMC(tran)) == NULL)
16714c06356bSdh 		return (DDI_SUCCESS);
16724c06356bSdh 
16734c06356bSdh 	/* Stop MPI & Reset chip (no need to re-initialize) */
16744c06356bSdh 	(void) pmcs_stop_mpi(pwp);
16754c06356bSdh 	(void) pmcs_soft_reset(pwp, B_TRUE);
16765c45adf0SJesse Butler 	pwp->last_reset_reason = PMCS_LAST_RST_QUIESCE;
16774c06356bSdh 
16784c06356bSdh 	return (DDI_SUCCESS);
16794c06356bSdh }
16804c06356bSdh 
16814c06356bSdh /*
16824c06356bSdh  * Called with xp->statlock and PHY lock and scratch acquired.
16834c06356bSdh  */
16844c06356bSdh static int
16854c06356bSdh pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
16864c06356bSdh {
16874c06356bSdh 	ata_identify_t *ati;
16884c06356bSdh 	int result, i;
16894c06356bSdh 	pmcs_phy_t *pptr;
16904c06356bSdh 	uint16_t *a;
16914c06356bSdh 	union {
16924c06356bSdh 		uint8_t nsa[8];
16934c06356bSdh 		uint16_t nsb[4];
16944c06356bSdh 	} u;
16954c06356bSdh 
16964c06356bSdh 	/*
16974c06356bSdh 	 * Safe defaults - use only if this target is brand new (i.e. doesn't
16984c06356bSdh 	 * already have these settings configured)
16994c06356bSdh 	 */
17004c06356bSdh 	if (xp->capacity == 0) {
17014c06356bSdh 		xp->capacity = (uint64_t)-1;
17024c06356bSdh 		xp->ca = 1;
17034c06356bSdh 		xp->qdepth = 1;
17044c06356bSdh 		xp->pio = 1;
17054c06356bSdh 	}
17064c06356bSdh 
17074c06356bSdh 	pptr = xp->phy;
17084c06356bSdh 
17094c06356bSdh 	/*
17104c06356bSdh 	 * We only try and issue an IDENTIFY for first level
17114c06356bSdh 	 * (direct attached) devices. We don't try and
17124c06356bSdh 	 * set other quirks here (this will happen later,
17134c06356bSdh 	 * if the device is fully configured)
17144c06356bSdh 	 */
17154c06356bSdh 	if (pptr->level) {
17164c06356bSdh 		return (0);
17174c06356bSdh 	}
17184c06356bSdh 
17194c06356bSdh 	mutex_exit(&xp->statlock);
17204c06356bSdh 	result = pmcs_sata_identify(pwp, pptr);
17214c06356bSdh 	mutex_enter(&xp->statlock);
17224c06356bSdh 
17234c06356bSdh 	if (result) {
17244c06356bSdh 		return (result);
17254c06356bSdh 	}
17264c06356bSdh 	ati = pwp->scratch;
17274c06356bSdh 	a = &ati->word108;
17284c06356bSdh 	for (i = 0; i < 4; i++) {
17294c06356bSdh 		u.nsb[i] = ddi_swap16(*a++);
17304c06356bSdh 	}
17314c06356bSdh 
17324c06356bSdh 	/*
17334c06356bSdh 	 * Check the returned data for being a valid (NAA=5) WWN.
17344c06356bSdh 	 * If so, use that and override the SAS address we were
17354c06356bSdh 	 * given at Link Up time.
17364c06356bSdh 	 */
17374c06356bSdh 	if ((u.nsa[0] >> 4) == 5) {
17384c06356bSdh 		(void) memcpy(pptr->sas_address, u.nsa, 8);
17394c06356bSdh 	}
1740c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
1741c3bc407cSdh 	    "%s: %s has SAS ADDRESS " SAS_ADDR_FMT,
17424c06356bSdh 	    __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address));
17434c06356bSdh 	return (0);
17444c06356bSdh }
17454c06356bSdh 
17464c06356bSdh /*
17474c06356bSdh  * Called with PHY lock and target statlock held and scratch acquired
17484c06356bSdh  */
17494c06356bSdh static boolean_t
17504c06356bSdh pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target)
17514c06356bSdh {
17524c06356bSdh 	ASSERT(target != NULL);
1753c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, "%s: target = 0x%p",
17544c06356bSdh 	    __func__, (void *) target);
17554c06356bSdh 
17564c06356bSdh 	switch (target->phy->dtype) {
17574c06356bSdh 	case SATA:
17584c06356bSdh 		if (pmcs_add_sata_device(pwp, target) != 0) {
1759c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, target->phy,
1760c3bc407cSdh 			    target, "%s: add_sata_device failed for tgt 0x%p",
17614c06356bSdh 			    __func__, (void *) target);
17624c06356bSdh 			return (B_FALSE);
17634c06356bSdh 		}
17644c06356bSdh 		break;
17654c06356bSdh 	case SAS:
17664c06356bSdh 		target->qdepth = maxqdepth;
17674c06356bSdh 		break;
17684c06356bSdh 	case EXPANDER:
17694c06356bSdh 		target->qdepth = 1;
17704c06356bSdh 		break;
17714c06356bSdh 	}
17724c06356bSdh 
17734c06356bSdh 	target->new = 0;
17744c06356bSdh 	target->assigned = 1;
17754c06356bSdh 	target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
17764c06356bSdh 	target->dtype = target->phy->dtype;
17774c06356bSdh 
17784c06356bSdh 	/*
17794c06356bSdh 	 * Set the PHY's config stop time to 0.  This is one of the final
17804c06356bSdh 	 * stops along the config path, so we're indicating that we
17814c06356bSdh 	 * successfully configured the PHY.
17824c06356bSdh 	 */
17834c06356bSdh 	target->phy->config_stop = 0;
17844c06356bSdh 
17854c06356bSdh 	return (B_TRUE);
17864c06356bSdh }
17874c06356bSdh 
17884c06356bSdh void
17894c06356bSdh pmcs_worker(void *arg)
17904c06356bSdh {
17914c06356bSdh 	pmcs_hw_t *pwp = arg;
17924c06356bSdh 	ulong_t work_flags;
17934c06356bSdh 
17944c06356bSdh 	DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t,
17954c06356bSdh 	    pwp->config_changed);
17964c06356bSdh 
17974c06356bSdh 	if (pwp->state != STATE_RUNNING) {
17984c06356bSdh 		return;
17994c06356bSdh 	}
18004c06356bSdh 
18014c06356bSdh 	work_flags = atomic_swap_ulong(&pwp->work_flags, 0);
18024c06356bSdh 
1803c280a92bSDavid Hollister 	if (work_flags & PMCS_WORK_FLAG_DUMP_REGS) {
1804c280a92bSDavid Hollister 		mutex_enter(&pwp->lock);
1805c280a92bSDavid Hollister 		pmcs_register_dump_int(pwp);
1806c280a92bSDavid Hollister 		mutex_exit(&pwp->lock);
1807c280a92bSDavid Hollister 	}
1808c280a92bSDavid Hollister 
18094c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) {
18104c06356bSdh 		pmcs_ack_events(pwp);
18114c06356bSdh 	}
18124c06356bSdh 
18134c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) {
18144c06356bSdh 		mutex_enter(&pwp->lock);
18154c06356bSdh 		pmcs_spinup_release(pwp, NULL);
18164c06356bSdh 		mutex_exit(&pwp->lock);
18174c06356bSdh 	}
18184c06356bSdh 
18194c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) {
18204c06356bSdh 		pmcs_ssp_event_recovery(pwp);
18214c06356bSdh 	}
18224c06356bSdh 
18234c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) {
18244c06356bSdh 		pmcs_dev_state_recovery(pwp, NULL);
18254c06356bSdh 	}
18264c06356bSdh 
1827601c90f1SSrikanth, Ramana 	if (work_flags & PMCS_WORK_FLAG_DEREGISTER_DEV) {
1828601c90f1SSrikanth, Ramana 		pmcs_deregister_device_work(pwp, NULL);
1829601c90f1SSrikanth, Ramana 	}
1830601c90f1SSrikanth, Ramana 
18314c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_DISCOVER) {
18324c06356bSdh 		pmcs_discover(pwp);
18334c06356bSdh 	}
18344c06356bSdh 
18354c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) {
18364c06356bSdh 		if (pmcs_abort_handler(pwp)) {
18374c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
18384c06356bSdh 		}
18394c06356bSdh 	}
18404c06356bSdh 
18414c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_SATA_RUN) {
18424c06356bSdh 		pmcs_sata_work(pwp);
18434c06356bSdh 	}
18444c06356bSdh 
18454c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) {
18464c06356bSdh 		pmcs_scsa_wq_run(pwp);
18474c06356bSdh 		mutex_enter(&pwp->lock);
18484c06356bSdh 		PMCS_CQ_RUN(pwp);
18494c06356bSdh 		mutex_exit(&pwp->lock);
18504c06356bSdh 	}
18514c06356bSdh 
18524c06356bSdh 	if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) {
18534c06356bSdh 		if (pmcs_add_more_chunks(pwp,
18544c06356bSdh 		    ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) {
18554c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
18564c06356bSdh 		} else {
18574c06356bSdh 			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
18584c06356bSdh 		}
18594c06356bSdh 	}
18604c06356bSdh }
18614c06356bSdh 
18624c06356bSdh static int
18634c06356bSdh pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize)
18644c06356bSdh {
18654c06356bSdh 	pmcs_dmachunk_t *dc;
18664c06356bSdh 	unsigned long dl;
18674c06356bSdh 	pmcs_chunk_t	*pchunk = NULL;
18684c06356bSdh 
18694c06356bSdh 	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
18704c06356bSdh 
18714c06356bSdh 	pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP);
18724c06356bSdh 	if (pchunk == NULL) {
1873c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18744c06356bSdh 		    "Not enough memory for DMA chunks");
18754c06356bSdh 		return (-1);
18764c06356bSdh 	}
18774c06356bSdh 
18784c06356bSdh 	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle,
18794c06356bSdh 	    &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp,
18804c06356bSdh 	    &pchunk->dma_addr) == B_FALSE) {
1881c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1882c3bc407cSdh 		    "Failed to setup DMA for chunks");
18834c06356bSdh 		kmem_free(pchunk, sizeof (pmcs_chunk_t));
18844c06356bSdh 		return (-1);
18854c06356bSdh 	}
18864c06356bSdh 
18874c06356bSdh 	if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) ||
18884c06356bSdh 	    (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) {
18894c06356bSdh 		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
18904c06356bSdh 		return (-1);
18914c06356bSdh 	}
18924c06356bSdh 
18934c06356bSdh 	bzero(pchunk->addrp, nsize);
18944c06356bSdh 	dc = NULL;
18954c06356bSdh 	for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) {
18964c06356bSdh 		pmcs_dmachunk_t *tmp;
18974c06356bSdh 		tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP);
18984c06356bSdh 		tmp->nxt = dc;
18994c06356bSdh 		dc = tmp;
19004c06356bSdh 	}
19014c06356bSdh 	mutex_enter(&pwp->dma_lock);
19024c06356bSdh 	pmcs_idma_chunks(pwp, dc, pchunk, nsize);
19034c06356bSdh 	pwp->nchunks++;
19044c06356bSdh 	mutex_exit(&pwp->dma_lock);
19054c06356bSdh 	return (0);
19064c06356bSdh }
19074c06356bSdh 
19085c45adf0SJesse Butler static void
19095c45adf0SJesse Butler pmcs_check_forward_progress(pmcs_hw_t *pwp)
19105c45adf0SJesse Butler {
1911d78a6b7eSJesse Butler 	pmcwork_t	*wrkp;
1912d78a6b7eSJesse Butler 	uint32_t	*iqp;
19135c45adf0SJesse Butler 	uint32_t	cur_iqci;
1914d78a6b7eSJesse Butler 	uint32_t	cur_work_idx;
19155c45adf0SJesse Butler 	uint32_t	cur_msgu_tick;
19165c45adf0SJesse Butler 	uint32_t	cur_iop_tick;
19175c45adf0SJesse Butler 	int 		i;
19185c45adf0SJesse Butler 
19195c45adf0SJesse Butler 	mutex_enter(&pwp->lock);
19205c45adf0SJesse Butler 
19215c45adf0SJesse Butler 	if (pwp->state == STATE_IN_RESET) {
19225c45adf0SJesse Butler 		mutex_exit(&pwp->lock);
19235c45adf0SJesse Butler 		return;
19245c45adf0SJesse Butler 	}
19255c45adf0SJesse Butler 
1926d78a6b7eSJesse Butler 	/*
1927d78a6b7eSJesse Butler 	 * Ensure that inbound work is getting picked up.  First, check to
1928d78a6b7eSJesse Butler 	 * see if new work has been posted.  If it has, ensure that the
1929d78a6b7eSJesse Butler 	 * work is moving forward by checking the consumer index and the
1930d78a6b7eSJesse Butler 	 * last_htag for the work being processed against what we saw last
1931d78a6b7eSJesse Butler 	 * time.  Note: we use the work structure's 'last_htag' because at
1932d78a6b7eSJesse Butler 	 * any given moment it could be freed back, thus clearing 'htag'
1933d78a6b7eSJesse Butler 	 * and setting 'last_htag' (see pmcs_pwork).
1934d78a6b7eSJesse Butler 	 */
19355c45adf0SJesse Butler 	for (i = 0; i < PMCS_NIQ; i++) {
19365c45adf0SJesse Butler 		cur_iqci = pmcs_rd_iqci(pwp, i);
1937d78a6b7eSJesse Butler 		iqp = &pwp->iqp[i][cur_iqci * (PMCS_QENTRY_SIZE >> 2)];
1938d78a6b7eSJesse Butler 		cur_work_idx = PMCS_TAG_INDEX(LE_32(*(iqp+1)));
1939d78a6b7eSJesse Butler 		wrkp = &pwp->work[cur_work_idx];
19405c45adf0SJesse Butler 		if (cur_iqci == pwp->shadow_iqpi[i]) {
19415c45adf0SJesse Butler 			pwp->last_iqci[i] = cur_iqci;
1942d78a6b7eSJesse Butler 			pwp->last_htag[i] = wrkp->last_htag;
19435c45adf0SJesse Butler 			continue;
19445c45adf0SJesse Butler 		}
1945d78a6b7eSJesse Butler 		if ((cur_iqci == pwp->last_iqci[i]) &&
1946d78a6b7eSJesse Butler 		    (wrkp->last_htag == pwp->last_htag[i])) {
19475c45adf0SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
19485c45adf0SJesse Butler 			    "Inbound Queue stall detected, issuing reset");
19495c45adf0SJesse Butler 			goto hot_reset;
19505c45adf0SJesse Butler 		}
19515c45adf0SJesse Butler 		pwp->last_iqci[i] = cur_iqci;
1952d78a6b7eSJesse Butler 		pwp->last_htag[i] = wrkp->last_htag;
19535c45adf0SJesse Butler 	}
19545c45adf0SJesse Butler 
1955d78a6b7eSJesse Butler 	/*
1956d78a6b7eSJesse Butler 	 * Check heartbeat on both the MSGU and IOP.  It is unlikely that
1957d78a6b7eSJesse Butler 	 * we'd ever fail here, as the inbound queue monitoring code above
1958d78a6b7eSJesse Butler 	 * would detect a stall due to either of these elements being
1959d78a6b7eSJesse Butler 	 * stalled, but we might as well keep an eye on them.
1960d78a6b7eSJesse Butler 	 */
19615c45adf0SJesse Butler 	cur_msgu_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK);
19625c45adf0SJesse Butler 	if (cur_msgu_tick == pwp->last_msgu_tick) {
19635c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
19645c45adf0SJesse Butler 		    "Stall detected on MSGU, issuing reset");
19655c45adf0SJesse Butler 		goto hot_reset;
19665c45adf0SJesse Butler 	}
19675c45adf0SJesse Butler 	pwp->last_msgu_tick = cur_msgu_tick;
19685c45adf0SJesse Butler 
19695c45adf0SJesse Butler 	cur_iop_tick  = pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK);
19705c45adf0SJesse Butler 	if (cur_iop_tick == pwp->last_iop_tick) {
19715c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
19725c45adf0SJesse Butler 		    "Stall detected on IOP, issuing reset");
19735c45adf0SJesse Butler 		goto hot_reset;
19745c45adf0SJesse Butler 	}
19755c45adf0SJesse Butler 	pwp->last_iop_tick = cur_iop_tick;
19765c45adf0SJesse Butler 
19775c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
19785c45adf0SJesse Butler 	return;
19795c45adf0SJesse Butler 
19805c45adf0SJesse Butler hot_reset:
19815c45adf0SJesse Butler 	pwp->state = STATE_DEAD;
19825c45adf0SJesse Butler 	/*
19835c45adf0SJesse Butler 	 * We've detected a stall. Attempt to recover service via hot
19845c45adf0SJesse Butler 	 * reset. In case of failure, pmcs_hot_reset() will handle the
19855c45adf0SJesse Butler 	 * failure and issue any required FM notifications.
19865c45adf0SJesse Butler 	 * See pmcs_subr.c for more details.
19875c45adf0SJesse Butler 	 */
19885c45adf0SJesse Butler 	if (pmcs_hot_reset(pwp)) {
19895c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
19905c45adf0SJesse Butler 		    "%s: hot reset failure", __func__);
19915c45adf0SJesse Butler 	} else {
19925c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
19935c45adf0SJesse Butler 		    "%s: hot reset complete", __func__);
19945c45adf0SJesse Butler 		pwp->last_reset_reason = PMCS_LAST_RST_STALL;
19955c45adf0SJesse Butler 	}
19965c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
19975c45adf0SJesse Butler }
19984c06356bSdh 
19994c06356bSdh static void
20004c06356bSdh pmcs_check_commands(pmcs_hw_t *pwp)
20014c06356bSdh {
20024c06356bSdh 	pmcs_cmd_t *sp;
20034c06356bSdh 	size_t amt;
20044c06356bSdh 	char path[32];
20054c06356bSdh 	pmcwork_t *pwrk;
20064c06356bSdh 	pmcs_xscsi_t *target;
20074c06356bSdh 	pmcs_phy_t *phyp;
2008601c90f1SSrikanth, Ramana 	int rval;
20094c06356bSdh 
20104c06356bSdh 	for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) {
20114c06356bSdh 		mutex_enter(&pwrk->lock);
20124c06356bSdh 
20134c06356bSdh 		/*
20144c06356bSdh 		 * If the command isn't active, we can't be timing it still.
20154c06356bSdh 		 * Active means the tag is not free and the state is "on chip".
20164c06356bSdh 		 */
20174c06356bSdh 		if (!PMCS_COMMAND_ACTIVE(pwrk)) {
20184c06356bSdh 			mutex_exit(&pwrk->lock);
20194c06356bSdh 			continue;
20204c06356bSdh 		}
20214c06356bSdh 
20224c06356bSdh 		/*
20234c06356bSdh 		 * No timer active for this command.
20244c06356bSdh 		 */
20254c06356bSdh 		if (pwrk->timer == 0) {
20264c06356bSdh 			mutex_exit(&pwrk->lock);
20274c06356bSdh 			continue;
20284c06356bSdh 		}
20294c06356bSdh 
20304c06356bSdh 		/*
20314c06356bSdh 		 * Knock off bits for the time interval.
20324c06356bSdh 		 */
20334c06356bSdh 		if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) {
20344c06356bSdh 			pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL);
20354c06356bSdh 		} else {
20364c06356bSdh 			pwrk->timer = 0;
20374c06356bSdh 		}
20384c06356bSdh 		if (pwrk->timer > 0) {
20394c06356bSdh 			mutex_exit(&pwrk->lock);
20404c06356bSdh 			continue;
20414c06356bSdh 		}
20424c06356bSdh 
20434c06356bSdh 		/*
20444c06356bSdh 		 * The command has now officially timed out.
20454c06356bSdh 		 * Get the path for it. If it doesn't have
20464c06356bSdh 		 * a phy pointer any more, it's really dead
20474c06356bSdh 		 * and can just be put back on the free list.
20484c06356bSdh 		 * There should *not* be any commands associated
20494c06356bSdh 		 * with it any more.
20504c06356bSdh 		 */
20514c06356bSdh 		if (pwrk->phy == NULL) {
2052c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
20534c06356bSdh 			    "dead command with gone phy being recycled");
20544c06356bSdh 			ASSERT(pwrk->xp == NULL);
20554c06356bSdh 			pmcs_pwork(pwp, pwrk);
20564c06356bSdh 			continue;
20574c06356bSdh 		}
20584c06356bSdh 		amt = sizeof (path);
20594c06356bSdh 		amt = min(sizeof (pwrk->phy->path), amt);
20604c06356bSdh 		(void) memcpy(path, pwrk->phy->path, amt);
20614c06356bSdh 
20624c06356bSdh 		/*
20634c06356bSdh 		 * If this is a non-SCSA command, stop here. Eventually
20644c06356bSdh 		 * we might do something with non-SCSA commands here-
20654c06356bSdh 		 * but so far their timeout mechanisms are handled in
20664c06356bSdh 		 * the WAIT_FOR macro.
20674c06356bSdh 		 */
20684c06356bSdh 		if (pwrk->xp == NULL) {
2069c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
20704c06356bSdh 			    "%s: non-SCSA cmd tag 0x%x timed out",
20714c06356bSdh 			    path, pwrk->htag);
20724c06356bSdh 			mutex_exit(&pwrk->lock);
20734c06356bSdh 			continue;
20744c06356bSdh 		}
20754c06356bSdh 
20764c06356bSdh 		sp = pwrk->arg;
20774c06356bSdh 		ASSERT(sp != NULL);
20784c06356bSdh 
20794c06356bSdh 		/*
20804c06356bSdh 		 * Mark it as timed out.
20814c06356bSdh 		 */
20824c06356bSdh 		CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT;
20834c06356bSdh 		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
20844c06356bSdh #ifdef	DEBUG
2085c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp,
20864c06356bSdh 		    "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d",
20874c06356bSdh 		    path, pwrk->htag, pwrk->state, pwrk->onwire);
20884c06356bSdh #else
2089c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp,
20904c06356bSdh 		    "%s: SCSA cmd tag 0x%x timed out (state %x)",
20914c06356bSdh 		    path, pwrk->htag, pwrk->state);
20924c06356bSdh #endif
20934c06356bSdh 		/*
20944c06356bSdh 		 * Mark the work structure as timed out.
20954c06356bSdh 		 */
20964c06356bSdh 		pwrk->state = PMCS_WORK_STATE_TIMED_OUT;
20974c06356bSdh 		phyp = pwrk->phy;
20984c06356bSdh 		target = pwrk->xp;
20994c06356bSdh 		mutex_exit(&pwrk->lock);
21004c06356bSdh 
21014c06356bSdh 		pmcs_lock_phy(phyp);
21024c06356bSdh 		mutex_enter(&target->statlock);
21034c06356bSdh 
21044c06356bSdh 		/*
21054c06356bSdh 		 * No point attempting recovery if the device is gone
21064c06356bSdh 		 */
2107145e0143Sdh 		if (target->dev_gone) {
21084c06356bSdh 			mutex_exit(&target->statlock);
21094c06356bSdh 			pmcs_unlock_phy(phyp);
2110c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
21114c06356bSdh 			    "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE "
21124c06356bSdh 			    "for htag 0x%08x", __func__,
2113145e0143Sdh 			    (void *)target, pwrk->htag);
21144c06356bSdh 			mutex_enter(&pwrk->lock);
21154c06356bSdh 			if (!PMCS_COMMAND_DONE(pwrk)) {
21164c06356bSdh 				/* Complete this command here */
2117c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
2118c3bc407cSdh 				    "%s: Completing cmd (htag 0x%08x) "
21194c06356bSdh 				    "anyway", __func__, pwrk->htag);
21204c06356bSdh 				pwrk->dead = 1;
21214c06356bSdh 				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
21224c06356bSdh 				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
21234c06356bSdh 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
21244c06356bSdh 			} else {
21254c06356bSdh 				mutex_exit(&pwrk->lock);
21264c06356bSdh 			}
21274c06356bSdh 			continue;
21284c06356bSdh 		}
21294c06356bSdh 
2130601c90f1SSrikanth, Ramana 		mutex_exit(&target->statlock);
2131601c90f1SSrikanth, Ramana 		rval = pmcs_abort(pwp, phyp, pwrk->htag, 0, 1);
2132601c90f1SSrikanth, Ramana 		if (rval) {
2133601c90f1SSrikanth, Ramana 			pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
2134601c90f1SSrikanth, Ramana 			    "%s: Bad status (%d) on abort of HTAG 0x%08x",
2135601c90f1SSrikanth, Ramana 			    __func__, rval, pwrk->htag);
21364c06356bSdh 			pmcs_unlock_phy(phyp);
2137601c90f1SSrikanth, Ramana 			mutex_enter(&pwrk->lock);
2138601c90f1SSrikanth, Ramana 			if (!PMCS_COMMAND_DONE(pwrk)) {
2139601c90f1SSrikanth, Ramana 				/* Complete this command here */
2140601c90f1SSrikanth, Ramana 				pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target,
2141601c90f1SSrikanth, Ramana 				    "%s: Completing cmd (htag 0x%08x) "
2142601c90f1SSrikanth, Ramana 				    "anyway", __func__, pwrk->htag);
2143601c90f1SSrikanth, Ramana 				if (target->dev_gone) {
2144601c90f1SSrikanth, Ramana 					pwrk->dead = 1;
2145601c90f1SSrikanth, Ramana 					CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
2146601c90f1SSrikanth, Ramana 					CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
2147601c90f1SSrikanth, Ramana 				}
2148601c90f1SSrikanth, Ramana 				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
2149601c90f1SSrikanth, Ramana 			} else {
2150601c90f1SSrikanth, Ramana 				mutex_exit(&pwrk->lock);
2151601c90f1SSrikanth, Ramana 			}
2152601c90f1SSrikanth, Ramana 			pmcs_lock_phy(phyp);
2153601c90f1SSrikanth, Ramana 			/*
2154601c90f1SSrikanth, Ramana 			 * No need to reschedule ABORT if we get any other
2155601c90f1SSrikanth, Ramana 			 * status
2156601c90f1SSrikanth, Ramana 			 */
2157601c90f1SSrikanth, Ramana 			if (rval == ENOMEM) {
2158601c90f1SSrikanth, Ramana 				phyp->abort_sent = 0;
2159601c90f1SSrikanth, Ramana 				phyp->abort_pending = 1;
2160601c90f1SSrikanth, Ramana 				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
2161601c90f1SSrikanth, Ramana 			}
21624c06356bSdh 		}
21634c06356bSdh 		pmcs_unlock_phy(phyp);
21644c06356bSdh 	}
21654c06356bSdh 	/*
21664c06356bSdh 	 * Run any completions that may have been queued up.
21674c06356bSdh 	 */
21684c06356bSdh 	PMCS_CQ_RUN(pwp);
21694c06356bSdh }
21704c06356bSdh 
21714c06356bSdh static void
21724c06356bSdh pmcs_watchdog(void *arg)
21734c06356bSdh {
21744c06356bSdh 	pmcs_hw_t *pwp = arg;
21754c06356bSdh 
21764c06356bSdh 	DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t,
21774c06356bSdh 	    pwp->config_changed);
21784c06356bSdh 
21795c45adf0SJesse Butler 	/*
21805c45adf0SJesse Butler 	 * Check forward progress on the chip
21815c45adf0SJesse Butler 	 */
21825c45adf0SJesse Butler 	if (++pwp->watchdog_count == PMCS_FWD_PROG_TRIGGER) {
21835c45adf0SJesse Butler 		pwp->watchdog_count = 0;
21845c45adf0SJesse Butler 		pmcs_check_forward_progress(pwp);
21855c45adf0SJesse Butler 	}
21865c45adf0SJesse Butler 
21879aed1621SDavid Hollister 	/*
21889aed1621SDavid Hollister 	 * Check to see if we need to kick discovery off again
21899aed1621SDavid Hollister 	 */
21909aed1621SDavid Hollister 	mutex_enter(&pwp->config_lock);
21919aed1621SDavid Hollister 	if (pwp->config_restart &&
21929aed1621SDavid Hollister 	    (ddi_get_lbolt() >= pwp->config_restart_time)) {
21939aed1621SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
21949aed1621SDavid Hollister 		    "%s: Timer expired for re-enumeration: Start discovery",
21959aed1621SDavid Hollister 		    __func__);
21969aed1621SDavid Hollister 		pwp->config_restart = B_FALSE;
21979aed1621SDavid Hollister 		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
21989aed1621SDavid Hollister 	}
21999aed1621SDavid Hollister 	mutex_exit(&pwp->config_lock);
22009aed1621SDavid Hollister 
22014c06356bSdh 	mutex_enter(&pwp->lock);
22024c06356bSdh 	if (pwp->state != STATE_RUNNING) {
22034c06356bSdh 		mutex_exit(&pwp->lock);
22044c06356bSdh 		return;
22054c06356bSdh 	}
22064c06356bSdh 
22074c06356bSdh 	if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) {
22084c06356bSdh 		if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp,
22094c06356bSdh 		    DDI_NOSLEEP) != DDI_SUCCESS) {
2210c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22114c06356bSdh 			    "Could not dispatch to worker thread");
22124c06356bSdh 		}
22134c06356bSdh 	}
22144c06356bSdh 	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
22154c06356bSdh 	    drv_usectohz(PMCS_WATCH_INTERVAL));
22165c45adf0SJesse Butler 
22174c06356bSdh 	mutex_exit(&pwp->lock);
22185c45adf0SJesse Butler 
22194c06356bSdh 	pmcs_check_commands(pwp);
22204c06356bSdh 	pmcs_handle_dead_phys(pwp);
22214c06356bSdh }
22224c06356bSdh 
22234c06356bSdh static int
22244c06356bSdh pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt)
22254c06356bSdh {
22264c06356bSdh 	int i, r, rslt = 0;
22274c06356bSdh 	for (i = 0; i < icnt; i++) {
22284c06356bSdh 		r = ddi_intr_remove_handler(pwp->ih_table[i]);
22294c06356bSdh 		if (r == DDI_SUCCESS) {
22304c06356bSdh 			continue;
22314c06356bSdh 		}
2232c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22334c06356bSdh 		    "%s: unable to remove interrupt handler %d", __func__, i);
22344c06356bSdh 		rslt = -1;
22354c06356bSdh 		break;
22364c06356bSdh 	}
22374c06356bSdh 	return (rslt);
22384c06356bSdh }
22394c06356bSdh 
22404c06356bSdh static int
22414c06356bSdh pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt)
22424c06356bSdh {
22434c06356bSdh 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
22444c06356bSdh 		int r = ddi_intr_block_disable(&pwp->ih_table[0],
22454c06356bSdh 		    pwp->intr_cnt);
22464c06356bSdh 		if (r != DDI_SUCCESS) {
2247c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22484c06356bSdh 			    "unable to disable interrupt block");
22494c06356bSdh 			return (-1);
22504c06356bSdh 		}
22514c06356bSdh 	} else {
22524c06356bSdh 		int i;
22534c06356bSdh 		for (i = 0; i < icnt; i++) {
22544c06356bSdh 			if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) {
22554c06356bSdh 				continue;
22564c06356bSdh 			}
2257c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22584c06356bSdh 			    "unable to disable interrupt %d", i);
22594c06356bSdh 			return (-1);
22604c06356bSdh 		}
22614c06356bSdh 	}
22624c06356bSdh 	return (0);
22634c06356bSdh }
22644c06356bSdh 
22654c06356bSdh static int
22664c06356bSdh pmcs_free_intrs(pmcs_hw_t *pwp, int icnt)
22674c06356bSdh {
22684c06356bSdh 	int i;
22694c06356bSdh 	for (i = 0; i < icnt; i++) {
22704c06356bSdh 		if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) {
22714c06356bSdh 			continue;
22724c06356bSdh 		}
2273c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2274c3bc407cSdh 		    "unable to free interrupt %d", i);
22754c06356bSdh 		return (-1);
22764c06356bSdh 	}
22774c06356bSdh 	kmem_free(pwp->ih_table, pwp->ih_table_size);
22784c06356bSdh 	pwp->ih_table_size = 0;
22794c06356bSdh 	return (0);
22804c06356bSdh }
22814c06356bSdh 
22824c06356bSdh /*
22834c06356bSdh  * Try to set up interrupts of type "type" with a minimum number of interrupts
22844c06356bSdh  * of "min".
22854c06356bSdh  */
22864c06356bSdh static void
22874c06356bSdh pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min)
22884c06356bSdh {
22894c06356bSdh 	int rval, avail, count, actual, max;
22904c06356bSdh 
22914c06356bSdh 	rval = ddi_intr_get_nintrs(pwp->dip, type, &count);
22924c06356bSdh 	if ((rval != DDI_SUCCESS) || (count < min)) {
2293c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
22944c06356bSdh 		    "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d",
22954c06356bSdh 		    __func__, type, rval, count, min);
22964c06356bSdh 		return;
22974c06356bSdh 	}
22984c06356bSdh 
2299c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
23004c06356bSdh 	    "%s: nintrs = %d for type: %d", __func__, count, type);
23014c06356bSdh 
23024c06356bSdh 	rval = ddi_intr_get_navail(pwp->dip, type, &avail);
23034c06356bSdh 	if ((rval != DDI_SUCCESS) || (avail < min)) {
2304c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
23054c06356bSdh 		    "%s: get_navail failed; type: %d rc: %d avail: %d min: %d",
23064c06356bSdh 		    __func__, type, rval, avail, min);
23074c06356bSdh 		return;
23084c06356bSdh 	}
23094c06356bSdh 
2310c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
23114c06356bSdh 	    "%s: navail = %d for type: %d", __func__, avail, type);
23124c06356bSdh 
23134c06356bSdh 	pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t);
23144c06356bSdh 	pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP);
23154c06356bSdh 
23164c06356bSdh 	switch (type) {
23174c06356bSdh 	case DDI_INTR_TYPE_MSIX:
23184c06356bSdh 		pwp->int_type = PMCS_INT_MSIX;
23194c06356bSdh 		max = PMCS_MAX_MSIX;
23204c06356bSdh 		break;
23214c06356bSdh 	case DDI_INTR_TYPE_MSI:
23224c06356bSdh 		pwp->int_type = PMCS_INT_MSI;
23234c06356bSdh 		max = PMCS_MAX_MSI;
23244c06356bSdh 		break;
23254c06356bSdh 	case DDI_INTR_TYPE_FIXED:
23264c06356bSdh 	default:
23274c06356bSdh 		pwp->int_type = PMCS_INT_FIXED;
23284c06356bSdh 		max = PMCS_MAX_FIXED;
23294c06356bSdh 		break;
23304c06356bSdh 	}
23314c06356bSdh 
23324c06356bSdh 	rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual,
23334c06356bSdh 	    DDI_INTR_ALLOC_NORMAL);
23344c06356bSdh 	if (rval != DDI_SUCCESS) {
2335c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
23364c06356bSdh 		    "%s: ddi_intr_alloc failed; type: %d rc: %d",
23374c06356bSdh 		    __func__, type, rval);
23384c06356bSdh 		kmem_free(pwp->ih_table, pwp->ih_table_size);
23394c06356bSdh 		pwp->ih_table = NULL;
23404c06356bSdh 		pwp->ih_table_size = 0;
23414c06356bSdh 		pwp->intr_cnt = 0;
23424c06356bSdh 		pwp->int_type = PMCS_INT_NONE;
23434c06356bSdh 		return;
23444c06356bSdh 	}
23454c06356bSdh 
23464c06356bSdh 	pwp->intr_cnt = actual;
23474c06356bSdh }
23484c06356bSdh 
23494c06356bSdh /*
23504c06356bSdh  * Set up interrupts.
23514c06356bSdh  * We return one of three values:
23524c06356bSdh  *
23534c06356bSdh  * 0 - success
23544c06356bSdh  * EAGAIN - failure to set up interrupts
23554c06356bSdh  * EIO - "" + we're now stuck partly enabled
23564c06356bSdh  *
23574c06356bSdh  * If EIO is returned, we can't unload the driver.
23584c06356bSdh  */
23594c06356bSdh static int
23604c06356bSdh pmcs_setup_intr(pmcs_hw_t *pwp)
23614c06356bSdh {
23624c06356bSdh 	int i, r, itypes, oqv_count;
23634c06356bSdh 	ddi_intr_handler_t **iv_table;
23644c06356bSdh 	size_t iv_table_size;
23654c06356bSdh 	uint_t pri;
23664c06356bSdh 
23674c06356bSdh 	if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) {
2368c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2369c3bc407cSdh 		    "cannot get interrupt types");
23704c06356bSdh 		return (EAGAIN);
23714c06356bSdh 	}
23724c06356bSdh 
23734c06356bSdh 	if (disable_msix) {
23744c06356bSdh 		itypes &= ~DDI_INTR_TYPE_MSIX;
23754c06356bSdh 	}
23764c06356bSdh 	if (disable_msi) {
23774c06356bSdh 		itypes &= ~DDI_INTR_TYPE_MSI;
23784c06356bSdh 	}
23794c06356bSdh 
23804c06356bSdh 	/*
23814c06356bSdh 	 * We won't know what firmware we're running until we call pmcs_setup,
23824c06356bSdh 	 * and we can't call pmcs_setup until we establish interrupts.
23834c06356bSdh 	 */
23844c06356bSdh 
23854c06356bSdh 	pwp->int_type = PMCS_INT_NONE;
23864c06356bSdh 
23874c06356bSdh 	/*
23884c06356bSdh 	 * We want PMCS_MAX_MSIX vectors for MSI-X.  Anything less would be
23894c06356bSdh 	 * uncivilized.
23904c06356bSdh 	 */
23914c06356bSdh 	if (itypes & DDI_INTR_TYPE_MSIX) {
23924c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX);
23934c06356bSdh 		if (pwp->int_type == PMCS_INT_MSIX) {
23944c06356bSdh 			itypes = 0;
23954c06356bSdh 		}
23964c06356bSdh 	}
23974c06356bSdh 
23984c06356bSdh 	if (itypes & DDI_INTR_TYPE_MSI) {
23994c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1);
24004c06356bSdh 		if (pwp->int_type == PMCS_INT_MSI) {
24014c06356bSdh 			itypes = 0;
24024c06356bSdh 		}
24034c06356bSdh 	}
24044c06356bSdh 
24054c06356bSdh 	if (itypes & DDI_INTR_TYPE_FIXED) {
24064c06356bSdh 		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1);
24074c06356bSdh 		if (pwp->int_type == PMCS_INT_FIXED) {
24084c06356bSdh 			itypes = 0;
24094c06356bSdh 		}
24104c06356bSdh 	}
24114c06356bSdh 
24124c06356bSdh 	if (pwp->intr_cnt == 0) {
2413c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
2414c3bc407cSdh 		    "No interrupts available");
24154c06356bSdh 		return (EAGAIN);
24164c06356bSdh 	}
24174c06356bSdh 
24184c06356bSdh 	iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt;
24194c06356bSdh 	iv_table = kmem_alloc(iv_table_size, KM_SLEEP);
24204c06356bSdh 
24214c06356bSdh 	/*
24224c06356bSdh 	 * Get iblock cookie and add handlers.
24234c06356bSdh 	 */
24244c06356bSdh 	switch (pwp->intr_cnt) {
24254c06356bSdh 	case 1:
24264c06356bSdh 		iv_table[0] = pmcs_all_intr;
24274c06356bSdh 		break;
24284c06356bSdh 	case 2:
24294c06356bSdh 		iv_table[0] = pmcs_iodone_ix;
24304c06356bSdh 		iv_table[1] = pmcs_nonio_ix;
24314c06356bSdh 		break;
24324c06356bSdh 	case 4:
24334c06356bSdh 		iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix;
24344c06356bSdh 		iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix;
24354c06356bSdh 		iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix;
24364c06356bSdh 		iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix;
24374c06356bSdh 		break;
24384c06356bSdh 	default:
2439c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24404c06356bSdh 		    "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt);
24414c06356bSdh 		kmem_free(iv_table, iv_table_size);
24424c06356bSdh 		return (EAGAIN);
24434c06356bSdh 	}
24444c06356bSdh 
24454c06356bSdh 	for (i = 0; i < pwp->intr_cnt; i++) {
24464c06356bSdh 		r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i],
24474c06356bSdh 		    (caddr_t)pwp, NULL);
24484c06356bSdh 		if (r != DDI_SUCCESS) {
24494c06356bSdh 			kmem_free(iv_table, iv_table_size);
24504c06356bSdh 			if (pmcs_remove_ihandlers(pwp, i)) {
24514c06356bSdh 				return (EIO);
24524c06356bSdh 			}
24534c06356bSdh 			if (pmcs_free_intrs(pwp, i)) {
24544c06356bSdh 				return (EIO);
24554c06356bSdh 			}
24564c06356bSdh 			pwp->intr_cnt = 0;
24574c06356bSdh 			return (EAGAIN);
24584c06356bSdh 		}
24594c06356bSdh 	}
24604c06356bSdh 
24614c06356bSdh 	kmem_free(iv_table, iv_table_size);
24624c06356bSdh 
24634c06356bSdh 	if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) {
2464c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2465c3bc407cSdh 		    "unable to get int capabilities");
24664c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
24674c06356bSdh 			return (EIO);
24684c06356bSdh 		}
24694c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
24704c06356bSdh 			return (EIO);
24714c06356bSdh 		}
24724c06356bSdh 		pwp->intr_cnt = 0;
24734c06356bSdh 		return (EAGAIN);
24744c06356bSdh 	}
24754c06356bSdh 
24764c06356bSdh 	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
24774c06356bSdh 		r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt);
24784c06356bSdh 		if (r != DDI_SUCCESS) {
2479c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2480c3bc407cSdh 			    "intr blk enable failed");
24814c06356bSdh 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
24824c06356bSdh 				return (EIO);
24834c06356bSdh 			}
24844c06356bSdh 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
24854c06356bSdh 				return (EIO);
24864c06356bSdh 			}
24874c06356bSdh 			pwp->intr_cnt = 0;
24884c06356bSdh 			return (EFAULT);
24894c06356bSdh 		}
24904c06356bSdh 	} else {
24914c06356bSdh 		for (i = 0; i < pwp->intr_cnt; i++) {
24924c06356bSdh 			r = ddi_intr_enable(pwp->ih_table[i]);
24934c06356bSdh 			if (r == DDI_SUCCESS) {
24944c06356bSdh 				continue;
24954c06356bSdh 			}
2496c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24974c06356bSdh 			    "unable to enable interrupt %d", i);
24984c06356bSdh 			if (pmcs_disable_intrs(pwp, i)) {
24994c06356bSdh 				return (EIO);
25004c06356bSdh 			}
25014c06356bSdh 			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
25024c06356bSdh 				return (EIO);
25034c06356bSdh 			}
25044c06356bSdh 			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
25054c06356bSdh 				return (EIO);
25064c06356bSdh 			}
25074c06356bSdh 			pwp->intr_cnt = 0;
25084c06356bSdh 			return (EAGAIN);
25094c06356bSdh 		}
25104c06356bSdh 	}
25114c06356bSdh 
25124c06356bSdh 	/*
25134c06356bSdh 	 * Set up locks.
25144c06356bSdh 	 */
25154c06356bSdh 	if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) {
2516c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
25174c06356bSdh 		    "unable to get interrupt priority");
25184c06356bSdh 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
25194c06356bSdh 			return (EIO);
25204c06356bSdh 		}
25214c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
25224c06356bSdh 			return (EIO);
25234c06356bSdh 		}
25244c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
25254c06356bSdh 			return (EIO);
25264c06356bSdh 		}
25274c06356bSdh 		pwp->intr_cnt = 0;
25284c06356bSdh 		return (EAGAIN);
25294c06356bSdh 	}
25304c06356bSdh 
25314c06356bSdh 	pwp->locks_initted = 1;
25324c06356bSdh 	pwp->intr_pri = pri;
25334c06356bSdh 	mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25344c06356bSdh 	mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25354c06356bSdh 	mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25364c06356bSdh 	mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25374c06356bSdh 	mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25384c06356bSdh 	mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25394c06356bSdh 	mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25404c06356bSdh 	mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25414c06356bSdh 	mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER,
25424c06356bSdh 	    DDI_INTR_PRI(pri));
25434c06356bSdh #ifdef	DEBUG
25444c06356bSdh 	mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
25454c06356bSdh #endif
25464c06356bSdh 	cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL);
25474c06356bSdh 	cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL);
254832b54db7SJesse Butler 	cv_init(&pwp->config_cv, NULL, CV_DRIVER, NULL);
25494c06356bSdh 	for (i = 0; i < PMCS_NIQ; i++) {
25504c06356bSdh 		mutex_init(&pwp->iqp_lock[i], NULL,
25514c06356bSdh 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
25524c06356bSdh 	}
25534c06356bSdh 	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
25544c06356bSdh 		mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL,
25554c06356bSdh 		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
25564c06356bSdh 		cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL,
25574c06356bSdh 		    CV_DRIVER, NULL);
25584c06356bSdh 	}
25594c06356bSdh 
2560c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%d %s interrup%s configured",
25614c06356bSdh 	    pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" :
25624c06356bSdh 	    ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"),
25634c06356bSdh 	    pwp->intr_cnt == 1? "t" : "ts");
25644c06356bSdh 
25654c06356bSdh 
25664c06356bSdh 	/*
25674c06356bSdh 	 * Enable Interrupts
25684c06356bSdh 	 */
25694c06356bSdh 	if (pwp->intr_cnt > PMCS_NOQ) {
25704c06356bSdh 		oqv_count = pwp->intr_cnt;
25714c06356bSdh 	} else {
25724c06356bSdh 		oqv_count = PMCS_NOQ;
25734c06356bSdh 	}
25744c06356bSdh 	for (pri = 0xffffffff, i = 0; i < oqv_count; i++) {
25754c06356bSdh 		pri ^= (1 << i);
25764c06356bSdh 	}
25774c06356bSdh 
25784c06356bSdh 	mutex_enter(&pwp->lock);
25794c06356bSdh 	pwp->intr_mask = pri;
25804c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
25814c06356bSdh 	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
25824c06356bSdh 	mutex_exit(&pwp->lock);
25834c06356bSdh 
25844c06356bSdh 	return (0);
25854c06356bSdh }
25864c06356bSdh 
25874c06356bSdh static int
25884c06356bSdh pmcs_teardown_intr(pmcs_hw_t *pwp)
25894c06356bSdh {
25904c06356bSdh 	if (pwp->intr_cnt) {
25914c06356bSdh 		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
25924c06356bSdh 			return (EIO);
25934c06356bSdh 		}
25944c06356bSdh 		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
25954c06356bSdh 			return (EIO);
25964c06356bSdh 		}
25974c06356bSdh 		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
25984c06356bSdh 			return (EIO);
25994c06356bSdh 		}
26004c06356bSdh 		pwp->intr_cnt = 0;
26014c06356bSdh 	}
26024c06356bSdh 	return (0);
26034c06356bSdh }
26044c06356bSdh 
26054c06356bSdh static uint_t
26064c06356bSdh pmcs_general_ix(caddr_t arg1, caddr_t arg2)
26074c06356bSdh {
26084c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
26094c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26104c06356bSdh 	pmcs_general_intr(pwp);
26114c06356bSdh 	return (DDI_INTR_CLAIMED);
26124c06356bSdh }
26134c06356bSdh 
26144c06356bSdh static uint_t
26154c06356bSdh pmcs_event_ix(caddr_t arg1, caddr_t arg2)
26164c06356bSdh {
26174c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
26184c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26194c06356bSdh 	pmcs_event_intr(pwp);
26204c06356bSdh 	return (DDI_INTR_CLAIMED);
26214c06356bSdh }
26224c06356bSdh 
26234c06356bSdh static uint_t
26244c06356bSdh pmcs_iodone_ix(caddr_t arg1, caddr_t arg2)
26254c06356bSdh {
26264c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26274c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
26284c06356bSdh 
26294c06356bSdh 	/*
26304c06356bSdh 	 * It's possible that if we just turned interrupt coalescing off
26314c06356bSdh 	 * (and thus, re-enabled auto clear for interrupts on the I/O outbound
26324c06356bSdh 	 * queue) that there was an interrupt already pending.  We use
26334c06356bSdh 	 * io_intr_coal.int_cleared to ensure that we still drop in here and
26344c06356bSdh 	 * clear the appropriate interrupt bit one last time.
26354c06356bSdh 	 */
26364c06356bSdh 	mutex_enter(&pwp->ict_lock);
26374c06356bSdh 	if (pwp->io_intr_coal.timer_on ||
26384c06356bSdh 	    (pwp->io_intr_coal.int_cleared == B_FALSE)) {
26394c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
26404c06356bSdh 		    (1 << PMCS_OQ_IODONE));
26414c06356bSdh 		pwp->io_intr_coal.int_cleared = B_TRUE;
26424c06356bSdh 	}
26434c06356bSdh 	mutex_exit(&pwp->ict_lock);
26444c06356bSdh 
26454c06356bSdh 	pmcs_iodone_intr(pwp);
26464c06356bSdh 
26474c06356bSdh 	return (DDI_INTR_CLAIMED);
26484c06356bSdh }
26494c06356bSdh 
26504c06356bSdh static uint_t
26514c06356bSdh pmcs_fatal_ix(caddr_t arg1, caddr_t arg2)
26524c06356bSdh {
26534c06356bSdh 	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
26544c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26554c06356bSdh 	pmcs_fatal_handler(pwp);
26564c06356bSdh 	return (DDI_INTR_CLAIMED);
26574c06356bSdh }
26584c06356bSdh 
26594c06356bSdh static uint_t
26604c06356bSdh pmcs_nonio_ix(caddr_t arg1, caddr_t arg2)
26614c06356bSdh {
26624c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26634c06356bSdh 	pmcs_hw_t *pwp = (void *)arg1;
26644c06356bSdh 	uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
26654c06356bSdh 
26664c06356bSdh 	/*
26674c06356bSdh 	 * Check for Fatal Interrupts
26684c06356bSdh 	 */
26694c06356bSdh 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
26704c06356bSdh 		pmcs_fatal_handler(pwp);
26714c06356bSdh 		return (DDI_INTR_CLAIMED);
26724c06356bSdh 	}
26734c06356bSdh 
26744c06356bSdh 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
26754c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
26764c06356bSdh 		    (1 << PMCS_OQ_GENERAL));
26774c06356bSdh 		pmcs_general_intr(pwp);
26784c06356bSdh 		pmcs_event_intr(pwp);
26794c06356bSdh 	}
26804c06356bSdh 
26814c06356bSdh 	return (DDI_INTR_CLAIMED);
26824c06356bSdh }
26834c06356bSdh 
26844c06356bSdh static uint_t
26854c06356bSdh pmcs_all_intr(caddr_t arg1, caddr_t arg2)
26864c06356bSdh {
26874c06356bSdh 	_NOTE(ARGUNUSED(arg2));
26884c06356bSdh 	pmcs_hw_t *pwp = (void *) arg1;
26894c06356bSdh 	uint32_t obdb;
26904c06356bSdh 	int handled = 0;
26914c06356bSdh 
26924c06356bSdh 	obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
26934c06356bSdh 
26944c06356bSdh 	/*
26954c06356bSdh 	 * Check for Fatal Interrupts
26964c06356bSdh 	 */
26974c06356bSdh 	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
26984c06356bSdh 		pmcs_fatal_handler(pwp);
26994c06356bSdh 		return (DDI_INTR_CLAIMED);
27004c06356bSdh 	}
27014c06356bSdh 
27024c06356bSdh 	/*
27034c06356bSdh 	 * Check for Outbound Queue service needed
27044c06356bSdh 	 */
27054c06356bSdh 	if (obdb & (1 << PMCS_OQ_IODONE)) {
27064c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
27074c06356bSdh 		    (1 << PMCS_OQ_IODONE));
27084c06356bSdh 		obdb ^= (1 << PMCS_OQ_IODONE);
27094c06356bSdh 		handled++;
27104c06356bSdh 		pmcs_iodone_intr(pwp);
27114c06356bSdh 	}
27124c06356bSdh 	if (obdb & (1 << PMCS_OQ_GENERAL)) {
27134c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
27144c06356bSdh 		    (1 << PMCS_OQ_GENERAL));
27154c06356bSdh 		obdb ^= (1 << PMCS_OQ_GENERAL);
27164c06356bSdh 		handled++;
27174c06356bSdh 		pmcs_general_intr(pwp);
27184c06356bSdh 	}
27194c06356bSdh 	if (obdb & (1 << PMCS_OQ_EVENTS)) {
27204c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
27214c06356bSdh 		    (1 << PMCS_OQ_EVENTS));
27224c06356bSdh 		obdb ^= (1 << PMCS_OQ_EVENTS);
27234c06356bSdh 		handled++;
27244c06356bSdh 		pmcs_event_intr(pwp);
27254c06356bSdh 	}
27264c06356bSdh 	if (obdb) {
2727c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
27284c06356bSdh 		    "interrupt bits not handled (0x%x)", obdb);
27294c06356bSdh 		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb);
27304c06356bSdh 		handled++;
27314c06356bSdh 	}
27324c06356bSdh 	if (pwp->int_type == PMCS_INT_MSI) {
27334c06356bSdh 		handled++;
27344c06356bSdh 	}
27354c06356bSdh 	return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
27364c06356bSdh }
27374c06356bSdh 
27384c06356bSdh void
27394c06356bSdh pmcs_fatal_handler(pmcs_hw_t *pwp)
27404c06356bSdh {
2741c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, "Fatal Interrupt caught");
27425c45adf0SJesse Butler 
27434c06356bSdh 	mutex_enter(&pwp->lock);
27444c06356bSdh 	pwp->state = STATE_DEAD;
27454c06356bSdh 
27465c45adf0SJesse Butler 	/*
27475c45adf0SJesse Butler 	 * Attempt a hot reset. In case of failure, pmcs_hot_reset() will
27485c45adf0SJesse Butler 	 * handle the failure and issue any required FM notifications.
27495c45adf0SJesse Butler 	 * See pmcs_subr.c for more details.
27505c45adf0SJesse Butler 	 */
27515c45adf0SJesse Butler 	if (pmcs_hot_reset(pwp)) {
27525c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
27535c45adf0SJesse Butler 		    "%s: hot reset failure", __func__);
27545c45adf0SJesse Butler 	} else {
27555c45adf0SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
27565c45adf0SJesse Butler 		    "%s: hot reset complete", __func__);
27575c45adf0SJesse Butler 		pwp->last_reset_reason = PMCS_LAST_RST_FATAL_ERROR;
27585c45adf0SJesse Butler 	}
27595c45adf0SJesse Butler 	mutex_exit(&pwp->lock);
27604c06356bSdh }
27614c06356bSdh 
27624c06356bSdh /*
27634c06356bSdh  * Called with PHY lock and target statlock held and scratch acquired.
27644c06356bSdh  */
27654c06356bSdh boolean_t
27664c06356bSdh pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
27674c06356bSdh {
27684c06356bSdh 	pmcs_phy_t *pptr = tgt->phy;
27694c06356bSdh 
27704c06356bSdh 	switch (pptr->dtype) {
27714c06356bSdh 	case SAS:
27724c06356bSdh 	case EXPANDER:
27734c06356bSdh 		break;
27744c06356bSdh 	case SATA:
27754c06356bSdh 		tgt->ca = 1;
27764c06356bSdh 		break;
27774c06356bSdh 	default:
2778c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
27794c06356bSdh 		    "%s: Target %p has PHY %p with invalid dtype",
27804c06356bSdh 		    __func__, (void *)tgt, (void *)pptr);
27814c06356bSdh 		return (B_FALSE);
27824c06356bSdh 	}
27834c06356bSdh 
27844c06356bSdh 	tgt->new = 1;
27854c06356bSdh 	tgt->dev_gone = 0;
27864c06356bSdh 	tgt->recover_wait = 0;
27874c06356bSdh 
2788c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
27894c06356bSdh 	    "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__,
27904c06356bSdh 	    pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
27914c06356bSdh 
27924c06356bSdh 	if (pmcs_add_new_device(pwp, tgt) != B_TRUE) {
2793c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
27944c06356bSdh 		    "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__,
27954c06356bSdh 		    tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
27964c06356bSdh 		mutex_destroy(&tgt->statlock);
27974c06356bSdh 		mutex_destroy(&tgt->wqlock);
27984c06356bSdh 		mutex_destroy(&tgt->aqlock);
27994c06356bSdh 		return (B_FALSE);
28004c06356bSdh 	}
28014c06356bSdh 
28024c06356bSdh 	return (B_TRUE);
28034c06356bSdh }
28044c06356bSdh 
28054c06356bSdh /*
28064c06356bSdh  * Called with softstate lock held
28074c06356bSdh  */
28084c06356bSdh void
28094c06356bSdh pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
28104c06356bSdh {
28114c06356bSdh 	pmcs_xscsi_t *xp;
28124c06356bSdh 	unsigned int vtgt;
28134c06356bSdh 
28144c06356bSdh 	ASSERT(mutex_owned(&pwp->lock));
28154c06356bSdh 
28164c06356bSdh 	for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) {
28174c06356bSdh 		xp = pwp->targets[vtgt];
28184c06356bSdh 		if (xp == NULL) {
28194c06356bSdh 			continue;
28204c06356bSdh 		}
28214c06356bSdh 
28224c06356bSdh 		mutex_enter(&xp->statlock);
28234c06356bSdh 		if (xp->phy == pptr) {
28244c06356bSdh 			if (xp->new) {
28254c06356bSdh 				xp->new = 0;
2826c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
28274c06356bSdh 				    "cancel config of vtgt %u", vtgt);
28284c06356bSdh 			} else {
2829b18a19c2SJesse Butler 				pmcs_clear_xp(pwp, xp);
2830c3bc407cSdh 				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp,
2831b18a19c2SJesse Butler 				    "Removed tgt 0x%p vtgt %u",
28324c06356bSdh 				    (void *)xp, vtgt);
28334c06356bSdh 			}
28344c06356bSdh 			mutex_exit(&xp->statlock);
28354c06356bSdh 			break;
28364c06356bSdh 		}
28374c06356bSdh 		mutex_exit(&xp->statlock);
28384c06356bSdh 	}
28394c06356bSdh }
28404c06356bSdh 
28414c06356bSdh void
2842c3bc407cSdh pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level,
2843c3bc407cSdh     pmcs_phy_t *phyp, pmcs_xscsi_t *target, const char *fmt, ...)
28444c06356bSdh {
28454c06356bSdh 	va_list	ap;
28464c06356bSdh 	int written = 0;
28474c06356bSdh 	char *ptr;
28484c06356bSdh 	uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1;
28494c06356bSdh 	boolean_t system_log;
28504c06356bSdh 	int system_log_level;
28511f81b464SDavid Hollister 	hrtime_t hrtimestamp;
28524c06356bSdh 
28534c06356bSdh 	switch (level) {
28544c06356bSdh 	case PMCS_PRT_DEBUG_DEVEL:
28554c06356bSdh 	case PMCS_PRT_DEBUG_DEV_STATE:
28564c06356bSdh 	case PMCS_PRT_DEBUG_PHY_LOCKING:
28574c06356bSdh 	case PMCS_PRT_DEBUG_SCSI_STATUS:
28584c06356bSdh 	case PMCS_PRT_DEBUG_UNDERFLOW:
28594c06356bSdh 	case PMCS_PRT_DEBUG_CONFIG:
28604c06356bSdh 	case PMCS_PRT_DEBUG_IPORT:
28614c06356bSdh 	case PMCS_PRT_DEBUG_MAP:
28624c06356bSdh 	case PMCS_PRT_DEBUG3:
28634c06356bSdh 	case PMCS_PRT_DEBUG2:
28644c06356bSdh 	case PMCS_PRT_DEBUG1:
28654c06356bSdh 	case PMCS_PRT_DEBUG:
28664c06356bSdh 		system_log = B_FALSE;
28674c06356bSdh 		break;
28684c06356bSdh 	case PMCS_PRT_INFO:
28694c06356bSdh 		system_log = B_TRUE;
28704c06356bSdh 		system_log_level = CE_CONT;
28714c06356bSdh 		break;
28724c06356bSdh 	case PMCS_PRT_WARN:
28734c06356bSdh 		system_log = B_TRUE;
28744c06356bSdh 		system_log_level = CE_NOTE;
28754c06356bSdh 		break;
28764c06356bSdh 	case PMCS_PRT_ERR:
28774c06356bSdh 		system_log = B_TRUE;
28784c06356bSdh 		system_log_level = CE_WARN;
28794c06356bSdh 		break;
28804c06356bSdh 	default:
28814c06356bSdh 		return;
28824c06356bSdh 	}
28834c06356bSdh 
28844c06356bSdh 	mutex_enter(&pmcs_trace_lock);
28851f81b464SDavid Hollister 	hrtimestamp = gethrtime();
28864c06356bSdh 	gethrestime(&pmcs_tbuf_ptr->timestamp);
28871f81b464SDavid Hollister 
28881f81b464SDavid Hollister 	if (pwp->fw_timestamp != 0) {
28891f81b464SDavid Hollister 		/* Calculate the approximate firmware time stamp... */
28901f81b464SDavid Hollister 		pmcs_tbuf_ptr->fw_timestamp = pwp->fw_timestamp +
28911f81b464SDavid Hollister 		    ((hrtimestamp - pwp->hrtimestamp) / PMCS_FWLOG_TIMER_DIV);
28921f81b464SDavid Hollister 	} else {
28931f81b464SDavid Hollister 		pmcs_tbuf_ptr->fw_timestamp = 0;
28941f81b464SDavid Hollister 	}
28951f81b464SDavid Hollister 
28964c06356bSdh 	ptr = pmcs_tbuf_ptr->buf;
2897c3bc407cSdh 
2898c3bc407cSdh 	/*
2899c3bc407cSdh 	 * Store the pertinent PHY and target information if there is any
2900c3bc407cSdh 	 */
2901c3bc407cSdh 	if (target == NULL) {
2902c3bc407cSdh 		pmcs_tbuf_ptr->target_num = PMCS_INVALID_TARGET_NUM;
2903c3bc407cSdh 		pmcs_tbuf_ptr->target_ua[0] = '\0';
2904c3bc407cSdh 	} else {
2905c3bc407cSdh 		pmcs_tbuf_ptr->target_num = target->target_num;
2906c3bc407cSdh 		(void) strncpy(pmcs_tbuf_ptr->target_ua, target->ua,
2907c3bc407cSdh 		    PMCS_TBUF_UA_MAX_SIZE);
2908c3bc407cSdh 	}
2909c3bc407cSdh 
2910c3bc407cSdh 	if (phyp == NULL) {
2911c3bc407cSdh 		(void) memset(pmcs_tbuf_ptr->phy_sas_address, 0, 8);
2912c3bc407cSdh 		pmcs_tbuf_ptr->phy_path[0] = '\0';
2913c3bc407cSdh 		pmcs_tbuf_ptr->phy_dtype = NOTHING;
2914c3bc407cSdh 	} else {
2915c3bc407cSdh 		(void) memcpy(pmcs_tbuf_ptr->phy_sas_address,
2916c3bc407cSdh 		    phyp->sas_address, 8);
2917c3bc407cSdh 		(void) strncpy(pmcs_tbuf_ptr->phy_path, phyp->path, 32);
2918c3bc407cSdh 		pmcs_tbuf_ptr->phy_dtype = phyp->dtype;
2919c3bc407cSdh 	}
2920c3bc407cSdh 
29214c06356bSdh 	written += snprintf(ptr, elem_size, "pmcs%d:%d: ",
29224c06356bSdh 	    ddi_get_instance(pwp->dip), level);
29234c06356bSdh 	ptr += strlen(ptr);
29244c06356bSdh 	va_start(ap, fmt);
29254c06356bSdh 	written += vsnprintf(ptr, elem_size - written, fmt, ap);
29264c06356bSdh 	va_end(ap);
29274c06356bSdh 	if (written > elem_size - 1) {
29284c06356bSdh 		/* Indicate truncation */
29294c06356bSdh 		pmcs_tbuf_ptr->buf[elem_size - 1] = '+';
29304c06356bSdh 	}
29314c06356bSdh 	if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) {
29324c06356bSdh 		pmcs_tbuf_ptr = pmcs_tbuf;
29334c06356bSdh 		pmcs_tbuf_wrap = B_TRUE;
29344c06356bSdh 		pmcs_tbuf_idx = 0;
29354c06356bSdh 	} else {
29364c06356bSdh 		++pmcs_tbuf_ptr;
29374c06356bSdh 	}
29384c06356bSdh 	mutex_exit(&pmcs_trace_lock);
29394c06356bSdh 
29404c06356bSdh 	/*
29414c06356bSdh 	 * When pmcs_force_syslog in non-zero, everything goes also
29424c06356bSdh 	 * to syslog, at CE_CONT level.
29434c06356bSdh 	 */
29444c06356bSdh 	if (pmcs_force_syslog) {
29454c06356bSdh 		system_log = B_TRUE;
29464c06356bSdh 		system_log_level = CE_CONT;
29474c06356bSdh 	}
29484c06356bSdh 
29494c06356bSdh 	/*
29504c06356bSdh 	 * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also
29514c06356bSdh 	 * goes to syslog.
29524c06356bSdh 	 */
29534c06356bSdh 	if (system_log) {
29544c06356bSdh 		char local[196];
29554c06356bSdh 
29564c06356bSdh 		switch (system_log_level) {
29574c06356bSdh 		case CE_CONT:
29584c06356bSdh 			(void) snprintf(local, sizeof (local), "%sINFO: ",
29594c06356bSdh 			    pmcs_console ? "" : "?");
29604c06356bSdh 			break;
29614c06356bSdh 		case CE_NOTE:
29624c06356bSdh 		case CE_WARN:
29634c06356bSdh 			local[0] = 0;
29644c06356bSdh 			break;
29654c06356bSdh 		default:
29664c06356bSdh 			return;
29674c06356bSdh 		}
29684c06356bSdh 
29694c06356bSdh 		ptr = local;
29704c06356bSdh 		ptr += strlen(local);
29714c06356bSdh 		(void) snprintf(ptr, (sizeof (local)) -
29724c06356bSdh 		    ((size_t)ptr - (size_t)local), "pmcs%d: ",
29734c06356bSdh 		    ddi_get_instance(pwp->dip));
29744c06356bSdh 		ptr += strlen(ptr);
29754c06356bSdh 		va_start(ap, fmt);
29764c06356bSdh 		(void) vsnprintf(ptr,
29774c06356bSdh 		    (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap);
29784c06356bSdh 		va_end(ap);
29794c06356bSdh 		if (level == CE_CONT) {
29804c06356bSdh 			(void) strlcat(local, "\n", sizeof (local));
29814c06356bSdh 		}
29824c06356bSdh 		cmn_err(system_log_level, local);
29834c06356bSdh 	}
29844c06356bSdh 
29854c06356bSdh }
29864c06356bSdh 
29874c06356bSdh /*
29884c06356bSdh  * pmcs_acquire_scratch
29894c06356bSdh  *
29904c06356bSdh  * If "wait" is true, the caller will wait until it can acquire the scratch.
29914c06356bSdh  * This implies the caller needs to be in a context where spinning for an
29924c06356bSdh  * indeterminate amount of time is acceptable.
29934c06356bSdh  */
29944c06356bSdh int
29954c06356bSdh pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait)
29964c06356bSdh {
29974c06356bSdh 	int rval;
29984c06356bSdh 
29994c06356bSdh 	if (!wait) {
30004c06356bSdh 		return (atomic_swap_8(&pwp->scratch_locked, 1));
30014c06356bSdh 	}
30024c06356bSdh 
30034c06356bSdh 	/*
30044c06356bSdh 	 * Caller will wait for scratch.
30054c06356bSdh 	 */
30064c06356bSdh 	while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) {
30074c06356bSdh 		drv_usecwait(100);
30084c06356bSdh 	}
30094c06356bSdh 
30104c06356bSdh 	return (rval);
30114c06356bSdh }
30124c06356bSdh 
30134c06356bSdh void
30144c06356bSdh pmcs_release_scratch(pmcs_hw_t *pwp)
30154c06356bSdh {
30164c06356bSdh 	pwp->scratch_locked = 0;
30174c06356bSdh }
30184c06356bSdh 
301935dae232SSrikanth Suravajhala /* Called with iport_lock and phy lock held */
302035dae232SSrikanth Suravajhala void
302135dae232SSrikanth Suravajhala pmcs_create_one_phy_stats(pmcs_iport_t *iport, pmcs_phy_t *phyp)
30224c06356bSdh {
30234c06356bSdh 	sas_phy_stats_t		*ps;
30244c06356bSdh 	pmcs_hw_t		*pwp;
30254c06356bSdh 	int			ndata;
30264c06356bSdh 	char			ks_name[KSTAT_STRLEN];
30274c06356bSdh 
302835dae232SSrikanth Suravajhala 	ASSERT(mutex_owned(&iport->lock));
302935dae232SSrikanth Suravajhala 	pwp = iport->pwp;
303035dae232SSrikanth Suravajhala 	ASSERT(pwp != NULL);
303135dae232SSrikanth Suravajhala 	ASSERT(mutex_owned(&phyp->phy_lock));
303235dae232SSrikanth Suravajhala 
303335dae232SSrikanth Suravajhala 	if (phyp->phy_stats != NULL) {
303435dae232SSrikanth Suravajhala 		/*
303535dae232SSrikanth Suravajhala 		 * Delete existing kstats with name containing
303635dae232SSrikanth Suravajhala 		 * old iport instance# and allow creation of
303735dae232SSrikanth Suravajhala 		 * new kstats with new iport instance# in the name.
303835dae232SSrikanth Suravajhala 		 */
303935dae232SSrikanth Suravajhala 		kstat_delete(phyp->phy_stats);
304035dae232SSrikanth Suravajhala 	}
304135dae232SSrikanth Suravajhala 
304235dae232SSrikanth Suravajhala 	ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t));
304335dae232SSrikanth Suravajhala 
304435dae232SSrikanth Suravajhala 	(void) snprintf(ks_name, sizeof (ks_name),
304535dae232SSrikanth Suravajhala 	    "%s.%llx.%d.%d", ddi_driver_name(iport->dip),
304635dae232SSrikanth Suravajhala 	    (longlong_t)pwp->sas_wwns[0],
304735dae232SSrikanth Suravajhala 	    ddi_get_instance(iport->dip), phyp->phynum);
304835dae232SSrikanth Suravajhala 
304935dae232SSrikanth Suravajhala 	phyp->phy_stats = kstat_create("pmcs",
305035dae232SSrikanth Suravajhala 	    ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS,
305135dae232SSrikanth Suravajhala 	    KSTAT_TYPE_NAMED, ndata, 0);
305235dae232SSrikanth Suravajhala 
305335dae232SSrikanth Suravajhala 	if (phyp->phy_stats == NULL) {
305435dae232SSrikanth Suravajhala 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
305535dae232SSrikanth Suravajhala 		    "%s: Failed to create %s kstats for PHY(0x%p) at %s",
305635dae232SSrikanth Suravajhala 		    __func__, ks_name, (void *)phyp, phyp->path);
3057*978d7443SSrikanth Suravajhala 		return;
305835dae232SSrikanth Suravajhala 	}
305935dae232SSrikanth Suravajhala 
306035dae232SSrikanth Suravajhala 	ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
306135dae232SSrikanth Suravajhala 
306235dae232SSrikanth Suravajhala 	kstat_named_init(&ps->seconds_since_last_reset,
306335dae232SSrikanth Suravajhala 	    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
306435dae232SSrikanth Suravajhala 	kstat_named_init(&ps->tx_frames,
306535dae232SSrikanth Suravajhala 	    "TxFrames", KSTAT_DATA_ULONGLONG);
306635dae232SSrikanth Suravajhala 	kstat_named_init(&ps->rx_frames,
306735dae232SSrikanth Suravajhala 	    "RxFrames", KSTAT_DATA_ULONGLONG);
306835dae232SSrikanth Suravajhala 	kstat_named_init(&ps->tx_words,
306935dae232SSrikanth Suravajhala 	    "TxWords", KSTAT_DATA_ULONGLONG);
307035dae232SSrikanth Suravajhala 	kstat_named_init(&ps->rx_words,
307135dae232SSrikanth Suravajhala 	    "RxWords", KSTAT_DATA_ULONGLONG);
307235dae232SSrikanth Suravajhala 	kstat_named_init(&ps->invalid_dword_count,
307335dae232SSrikanth Suravajhala 	    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
307435dae232SSrikanth Suravajhala 	kstat_named_init(&ps->running_disparity_error_count,
307535dae232SSrikanth Suravajhala 	    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
307635dae232SSrikanth Suravajhala 	kstat_named_init(&ps->loss_of_dword_sync_count,
307735dae232SSrikanth Suravajhala 	    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
307835dae232SSrikanth Suravajhala 	kstat_named_init(&ps->phy_reset_problem_count,
307935dae232SSrikanth Suravajhala 	    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
308035dae232SSrikanth Suravajhala 
308135dae232SSrikanth Suravajhala 	phyp->phy_stats->ks_private = phyp;
308235dae232SSrikanth Suravajhala 	phyp->phy_stats->ks_update = pmcs_update_phy_stats;
308335dae232SSrikanth Suravajhala 	kstat_install(phyp->phy_stats);
308435dae232SSrikanth Suravajhala }
308535dae232SSrikanth Suravajhala 
308635dae232SSrikanth Suravajhala static void
308735dae232SSrikanth Suravajhala pmcs_create_all_phy_stats(pmcs_iport_t *iport)
308835dae232SSrikanth Suravajhala {
308935dae232SSrikanth Suravajhala 	pmcs_hw_t		*pwp;
309035dae232SSrikanth Suravajhala 	pmcs_phy_t		*phyp;
309135dae232SSrikanth Suravajhala 
30924c06356bSdh 	ASSERT(iport != NULL);
30934c06356bSdh 	pwp = iport->pwp;
30944c06356bSdh 	ASSERT(pwp != NULL);
30954c06356bSdh 
30964c06356bSdh 	mutex_enter(&iport->lock);
30974c06356bSdh 
30984c06356bSdh 	for (phyp = list_head(&iport->phys);
30994c06356bSdh 	    phyp != NULL;
31004c06356bSdh 	    phyp = list_next(&iport->phys, phyp)) {
31014c06356bSdh 
310235dae232SSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
310335dae232SSrikanth Suravajhala 		pmcs_create_one_phy_stats(iport, phyp);
310435dae232SSrikanth Suravajhala 		mutex_exit(&phyp->phy_lock);
31054c06356bSdh 	}
31064c06356bSdh 
31074c06356bSdh 	mutex_exit(&iport->lock);
31084c06356bSdh }
31094c06356bSdh 
31104c06356bSdh int
31114c06356bSdh pmcs_update_phy_stats(kstat_t *ks, int rw)
31124c06356bSdh {
31134c06356bSdh 	int		val, ret = DDI_FAILURE;
31144c06356bSdh 	pmcs_phy_t	*pptr = (pmcs_phy_t *)ks->ks_private;
31154c06356bSdh 	pmcs_hw_t	*pwp = pptr->pwp;
31164c06356bSdh 	sas_phy_stats_t	*ps = ks->ks_data;
31174c06356bSdh 
31184c06356bSdh 	_NOTE(ARGUNUSED(rw));
31194c06356bSdh 	ASSERT((pptr != NULL) && (pwp != NULL));
31204c06356bSdh 
31214c06356bSdh 	/*
31224c06356bSdh 	 * We just want to lock against other invocations of kstat;
31234c06356bSdh 	 * we don't need to pmcs_lock_phy() for this.
31244c06356bSdh 	 */
31254c06356bSdh 	mutex_enter(&pptr->phy_lock);
31264c06356bSdh 
31274c06356bSdh 	/* Get Stats from Chip */
31284c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum);
31294c06356bSdh 	if (val == DDI_FAILURE)
31304c06356bSdh 		goto fail;
31314c06356bSdh 	ps->invalid_dword_count.value.ull = (unsigned long long)val;
31324c06356bSdh 
31334c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum);
31344c06356bSdh 	if (val == DDI_FAILURE)
31354c06356bSdh 		goto fail;
31364c06356bSdh 	ps->running_disparity_error_count.value.ull = (unsigned long long)val;
31374c06356bSdh 
31384c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum);
31394c06356bSdh 	if (val == DDI_FAILURE)
31404c06356bSdh 		goto fail;
31414c06356bSdh 	ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val;
31424c06356bSdh 
31434c06356bSdh 	val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum);
31444c06356bSdh 	if (val == DDI_FAILURE)
31454c06356bSdh 		goto fail;
31464c06356bSdh 	ps->phy_reset_problem_count.value.ull = (unsigned long long)val;
31474c06356bSdh 
31484c06356bSdh 	ret = DDI_SUCCESS;
31494c06356bSdh fail:
31504c06356bSdh 	mutex_exit(&pptr->phy_lock);
31514c06356bSdh 	return (ret);
31524c06356bSdh }
31534c06356bSdh 
31544c06356bSdh static void
31554c06356bSdh pmcs_destroy_phy_stats(pmcs_iport_t *iport)
31564c06356bSdh {
31574c06356bSdh 	pmcs_phy_t		*phyp;
31584c06356bSdh 
31594c06356bSdh 	ASSERT(iport != NULL);
31604c06356bSdh 	mutex_enter(&iport->lock);
31614c06356bSdh 	phyp = iport->pptr;
31624c06356bSdh 	if (phyp == NULL) {
31634c06356bSdh 		mutex_exit(&iport->lock);
31644c06356bSdh 		return;
31654c06356bSdh 	}
31664c06356bSdh 
316735dae232SSrikanth Suravajhala 	for (phyp = list_head(&iport->phys);
316835dae232SSrikanth Suravajhala 	    phyp != NULL;
316935dae232SSrikanth Suravajhala 	    phyp = list_next(&iport->phys, phyp)) {
317035dae232SSrikanth Suravajhala 
317135dae232SSrikanth Suravajhala 		mutex_enter(&phyp->phy_lock);
317235dae232SSrikanth Suravajhala 		if (phyp->phy_stats != NULL) {
317335dae232SSrikanth Suravajhala 			kstat_delete(phyp->phy_stats);
317435dae232SSrikanth Suravajhala 			phyp->phy_stats = NULL;
317535dae232SSrikanth Suravajhala 		}
317635dae232SSrikanth Suravajhala 		mutex_exit(&phyp->phy_lock);
31774c06356bSdh 	}
31784c06356bSdh 
31794c06356bSdh 	mutex_exit(&iport->lock);
31804c06356bSdh }
31814c06356bSdh 
31824c06356bSdh /*ARGSUSED*/
31834c06356bSdh static int
31844c06356bSdh pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
31854c06356bSdh {
31864c06356bSdh 	/*
31874c06356bSdh 	 * as the driver can always deal with an error in any dma or
31884c06356bSdh 	 * access handle, we can just return the fme_status value.
31894c06356bSdh 	 */
31904c06356bSdh 	pci_ereport_post(dip, err, NULL);
31914c06356bSdh 	return (err->fme_status);
31924c06356bSdh }
31934c06356bSdh 
31944c06356bSdh static void
31954c06356bSdh pmcs_fm_init(pmcs_hw_t *pwp)
31964c06356bSdh {
31974c06356bSdh 	ddi_iblock_cookie_t	fm_ibc;
31984c06356bSdh 
31994c06356bSdh 	/* Only register with IO Fault Services if we have some capability */
32004c06356bSdh 	if (pwp->fm_capabilities) {
32014c06356bSdh 		/* Adjust access and dma attributes for FMA */
3202837c1ac4SStephen Hanson 		pwp->reg_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
32034c06356bSdh 		pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
32044c06356bSdh 		pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
32054c06356bSdh 		pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
32064c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
32074c06356bSdh 
32084c06356bSdh 		/*
32094c06356bSdh 		 * Register capabilities with IO Fault Services.
32104c06356bSdh 		 */
32114c06356bSdh 		ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc);
32124c06356bSdh 
32134c06356bSdh 		/*
32144c06356bSdh 		 * Initialize pci ereport capabilities if ereport
32154c06356bSdh 		 * capable (should always be.)
32164c06356bSdh 		 */
32174c06356bSdh 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
32184c06356bSdh 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
32194c06356bSdh 			pci_ereport_setup(pwp->dip);
32204c06356bSdh 		}
32214c06356bSdh 
32224c06356bSdh 		/*
32234c06356bSdh 		 * Register error callback if error callback capable.
32244c06356bSdh 		 */
32254c06356bSdh 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
32264c06356bSdh 			ddi_fm_handler_register(pwp->dip,
32274c06356bSdh 			    pmcs_fm_error_cb, (void *) pwp);
32284c06356bSdh 		}
32294c06356bSdh 	}
32304c06356bSdh }
32314c06356bSdh 
32324c06356bSdh static void
32334c06356bSdh pmcs_fm_fini(pmcs_hw_t *pwp)
32344c06356bSdh {
32354c06356bSdh 	/* Only unregister FMA capabilities if registered */
32364c06356bSdh 	if (pwp->fm_capabilities) {
32374c06356bSdh 		/*
32384c06356bSdh 		 * Un-register error callback if error callback capable.
32394c06356bSdh 		 */
32404c06356bSdh 		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
32414c06356bSdh 			ddi_fm_handler_unregister(pwp->dip);
32424c06356bSdh 		}
32434c06356bSdh 
32444c06356bSdh 		/*
32454c06356bSdh 		 * Release any resources allocated by pci_ereport_setup()
32464c06356bSdh 		 */
32474c06356bSdh 		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
32484c06356bSdh 		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
32494c06356bSdh 			pci_ereport_teardown(pwp->dip);
32504c06356bSdh 		}
32514c06356bSdh 
32524c06356bSdh 		/* Unregister from IO Fault Services */
32534c06356bSdh 		ddi_fm_fini(pwp->dip);
32544c06356bSdh 
32554c06356bSdh 		/* Adjust access and dma attributes for FMA */
3256837c1ac4SStephen Hanson 		pwp->reg_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC;
32574c06356bSdh 		pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
32584c06356bSdh 		pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
32594c06356bSdh 		pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
32604c06356bSdh 		pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
32614c06356bSdh 	}
32624c06356bSdh }
32634c06356bSdh 
32644c06356bSdh static boolean_t
32654c06356bSdh pmcs_fabricate_wwid(pmcs_hw_t *pwp)
32664c06356bSdh {
32674c06356bSdh 	char *cp, c;
32684c06356bSdh 	uint64_t adr;
32694c06356bSdh 	int i;
32704c06356bSdh 
32714c06356bSdh 	cp = &c;
32724c06356bSdh 	(void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr);
32736745c559SJesse Butler 
32744c06356bSdh 	if (adr == 0) {
3275c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
32764c06356bSdh 		    "%s: No serial number available to fabricate WWN",
32774c06356bSdh 		    __func__);
32786745c559SJesse Butler 
32796745c559SJesse Butler 		adr = (uint64_t)gethrtime();
32804c06356bSdh 	}
32816745c559SJesse Butler 
32824c06356bSdh 	adr <<= 8;
32834c06356bSdh 	adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52);
32844c06356bSdh 	adr |= (5ULL << 60);
32856745c559SJesse Butler 
32864c06356bSdh 	for (i = 0; i < PMCS_MAX_PORTS; i++) {
32874c06356bSdh 		pwp->sas_wwns[i] = adr + i;
32884c06356bSdh 	}
32894c06356bSdh 
32904c06356bSdh 	return (B_TRUE);
32914c06356bSdh }
3292