14c06356dh/*
24c06356dh * CDDL HEADER START
34c06356dh *
44c06356dh * The contents of this file are subject to the terms of the
54c06356dh * Common Development and Distribution License (the "License").
64c06356dh * You may not use this file except in compliance with the License.
74c06356dh *
84c06356dh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356dh * or http://www.opensolaris.org/os/licensing.
104c06356dh * See the License for the specific language governing permissions
114c06356dh * and limitations under the License.
124c06356dh *
134c06356dh * When distributing Covered Code, include this CDDL HEADER in each
144c06356dh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356dh * If applicable, add the following below this CDDL HEADER, with the
164c06356dh * fields enclosed by brackets "[]" replaced with your own identifying
174c06356dh * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356dh *
194c06356dh * CDDL HEADER END
20658280bDavid Hollister */
21658280bDavid Hollister/*
22658280bDavid Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356dh */
244c06356dh#include <sys/scsi/adapters/pmcs/pmcs.h>
254c06356dh
2647b47c8dh#define	PMCS_DRIVER_VERSION	"pmcs HBA device driver"
274c06356dh
284c06356dhstatic	char	*pmcs_driver_rev = PMCS_DRIVER_VERSION;
294c06356dh
304c06356dh/*
314c06356dh * Non-DDI Compliant stuff
324c06356dh */
334c06356dhextern char hw_serial[];
344c06356dh
354c06356dh/*
364c06356dh * Global driver data
374c06356dh */
384c06356dhvoid *pmcs_softc_state = NULL;
394c06356dhvoid *pmcs_iport_softstate = NULL;
404c06356dh
414c06356dh/*
424c06356dh * Tracing and Logging info
434c06356dh */
444c06356dhpmcs_tbuf_t *pmcs_tbuf = NULL;
454c06356dhuint32_t pmcs_tbuf_num_elems = 0;
464c06356dhpmcs_tbuf_t *pmcs_tbuf_ptr;
474c06356dhuint32_t pmcs_tbuf_idx = 0;
484c06356dhboolean_t pmcs_tbuf_wrap = B_FALSE;
491f81b46David Hollisterkmutex_t pmcs_trace_lock;
504c06356dh
514c06356dh/*
524c06356dh * If pmcs_force_syslog value is non-zero, all messages put in the trace log
534c06356dh * will also be sent to system log.
544c06356dh */
554c06356dhint pmcs_force_syslog = 0;
564c06356dhint pmcs_console = 0;
574c06356dh
584c06356dh/*
594c06356dh * External References
604c06356dh */
614c06356dhextern int ncpus_online;
624c06356dh
634c06356dh/*
644c06356dh * Local static data
654c06356dh */
664c06356dhstatic int fwlog_level = 3;
674c06356dhstatic int physpeed = PHY_LINK_ALL;
684c06356dhstatic int phymode = PHY_LM_AUTO;
694c06356dhstatic int block_mask = 0;
7060aabb4Chris Hornestatic int phymap_stable_usec = 3 * MICROSEC;
7160aabb4Chris Hornestatic int iportmap_stable_usec = 2 * MICROSEC;
7260aabb4Chris Hornestatic int iportmap_csync_usec = 20 * MICROSEC;
734c06356dh
744c06356dh#ifdef DEBUG
754c06356dhstatic int debug_mask = 1;
764c06356dh#else
774c06356dhstatic int debug_mask = 0;
784c06356dh#endif
794c06356dh
804c06356dh#ifdef DISABLE_MSIX
814c06356dhstatic int disable_msix = 1;
824c06356dh#else
834c06356dhstatic int disable_msix = 0;
844c06356dh#endif
854c06356dh
864c06356dh#ifdef DISABLE_MSI
874c06356dhstatic int disable_msi = 1;
884c06356dh#else
894c06356dhstatic int disable_msi = 0;
904c06356dh#endif
914c06356dh
921b94a41Chris Horne/*
931b94a41Chris Horne * DEBUG: testing: allow detach with an active port:
941b94a41Chris Horne *
951b94a41Chris Horne * # echo 'detach_driver_unconfig/W 10'		| mdb -kw
961b94a41Chris Horne * # echo 'scsi_hba_bus_unconfig_remove/W 1'	| mdb -kw
971b94a41Chris Horne * # echo 'pmcs`detach_with_active_port/W 1'	| mdb -kw
981b94a41Chris Horne * # modunload -i <pmcs_driver_index>
991b94a41Chris Horne */
1001b94a41Chris Hornestatic int detach_with_active_port = 0;
1011b94a41Chris Horne
1024c06356dhstatic uint16_t maxqdepth = 0xfffe;
1034c06356dh
1044c06356dh/*
1054c06356dh * Local prototypes
1064c06356dh */
1074c06356dhstatic int pmcs_attach(dev_info_t *, ddi_attach_cmd_t);
1084c06356dhstatic int pmcs_detach(dev_info_t *, ddi_detach_cmd_t);
1094c06356dhstatic int pmcs_unattach(pmcs_hw_t *);
1104c06356dhstatic int pmcs_iport_unattach(pmcs_iport_t *);
1114c06356dhstatic int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long);
1124c06356dhstatic void pmcs_watchdog(void *);
1134c06356dhstatic int pmcs_setup_intr(pmcs_hw_t *);
1144c06356dhstatic int pmcs_teardown_intr(pmcs_hw_t *);
1154c06356dh
1164c06356dhstatic uint_t pmcs_nonio_ix(caddr_t, caddr_t);
1174c06356dhstatic uint_t pmcs_general_ix(caddr_t, caddr_t);
1184c06356dhstatic uint_t pmcs_event_ix(caddr_t, caddr_t);
1194c06356dhstatic uint_t pmcs_iodone_ix(caddr_t, caddr_t);
1204c06356dhstatic uint_t pmcs_fatal_ix(caddr_t, caddr_t);
1214c06356dhstatic uint_t pmcs_all_intr(caddr_t, caddr_t);
1224c06356dhstatic int pmcs_quiesce(dev_info_t *dip);
1234c06356dhstatic boolean_t pmcs_fabricate_wwid(pmcs_hw_t *);
1244c06356dh
12535dae23Srikanth Suravajhalastatic void pmcs_create_all_phy_stats(pmcs_iport_t *);
1264c06356dhint pmcs_update_phy_stats(kstat_t *, int);
1274c06356dh
1284c06356dhstatic void pmcs_fm_fini(pmcs_hw_t *pwp);
1294c06356dhstatic void pmcs_fm_init(pmcs_hw_t *pwp);
1304c06356dhstatic int pmcs_fm_error_cb(dev_info_t *dip,
1314c06356dh    ddi_fm_error_t *err, const void *impl_data);
1324c06356dh
1334c06356dh/*
1344c06356dh * Local configuration data
1354c06356dh */
1364c06356dhstatic struct dev_ops pmcs_ops = {
1374c06356dh	DEVO_REV,		/* devo_rev, */
1384c06356dh	0,			/* refcnt */
1394c06356dh	ddi_no_info,		/* info */
1404c06356dh	nulldev,		/* identify */
1414c06356dh	nulldev,		/* probe */
1424c06356dh	pmcs_attach,		/* attach */
1434c06356dh	pmcs_detach,		/* detach */
1444c06356dh	nodev,			/* reset */
1454c06356dh	NULL,			/* driver operations */
1464c06356dh	NULL,			/* bus operations */
1474c06356dh	ddi_power,		/* power management */
1484c06356dh	pmcs_quiesce		/* quiesce */
1494c06356dh};
1504c06356dh
1514c06356dhstatic struct modldrv modldrv = {
1524c06356dh	&mod_driverops,
1534c06356dh	PMCS_DRIVER_VERSION,
1544c06356dh	&pmcs_ops,	/* driver ops */
1554c06356dh};
1564c06356dhstatic struct modlinkage modlinkage = {
1574c06356dh	MODREV_1, &modldrv, NULL
1584c06356dh};
1594c06356dh
1604c06356dhconst ddi_dma_attr_t pmcs_dattr = {
1614c06356dh	DMA_ATTR_V0,			/* dma_attr version	*/
1624c06356dh	0x0000000000000000ull,		/* dma_attr_addr_lo	*/
1634c06356dh	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_addr_hi	*/
1644c06356dh	0x00000000FFFFFFFFull,		/* dma_attr_count_max	*/
1654c06356dh	0x0000000000000001ull,		/* dma_attr_align	*/
1664c06356dh	0x00000078,			/* dma_attr_burstsizes	*/
1674c06356dh	0x00000001,			/* dma_attr_minxfer	*/
1684c06356dh	0x00000000FFFFFFFFull,		/* dma_attr_maxxfer	*/
1694c06356dh	0x00000000FFFFFFFFull,		/* dma_attr_seg		*/
170b46556dToomas Soome	1,				/* dma_attr_sgllen	*/
171b46556dToomas Soome	512,				/* dma_attr_granular	*/
172b46556dToomas Soome	0				/* dma_attr_flags	*/
1734c06356dh};
1744c06356dh
1754c06356dhstatic ddi_device_acc_attr_t rattr = {
176837c1acStephen Hanson	DDI_DEVICE_ATTR_V1,
1774c06356dh	DDI_STRUCTURE_LE_ACC,
1784c06356dh	DDI_STRICTORDER_ACC,
1794c06356dh	DDI_DEFAULT_ACC
1804c06356dh};
1814c06356dh
1824c06356dh
1834c06356dh/*
1844c06356dh * Attach/Detach functions
1854c06356dh */
1864c06356dh
1874c06356dhint
1884c06356dh_init(void)
1894c06356dh{
1904c06356dh	int ret;
1914c06356dh
1924c06356dh	ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1);
1934c06356dh	if (ret != 0) {
1944c06356dh		cmn_err(CE_WARN, "?soft state init failed for pmcs");
1954c06356dh		return (ret);
1964c06356dh	}
1974c06356dh
1984c06356dh	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
1994c06356dh		cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs");
2004c06356dh		ddi_soft_state_fini(&pmcs_softc_state);
2014c06356dh		return (ret);
2024c06356dh	}
2034c06356dh
2044c06356dh	/*
2054c06356dh	 * Allocate soft state for iports
2064c06356dh	 */
2074c06356dh	ret = ddi_soft_state_init(&pmcs_iport_softstate,
2084c06356dh	    sizeof (pmcs_iport_t), 2);
2094c06356dh	if (ret != 0) {
2104c06356dh		cmn_err(CE_WARN, "?iport soft state init failed for pmcs");
2114c06356dh		ddi_soft_state_fini(&pmcs_softc_state);
2124c06356dh		return (ret);
2134c06356dh	}
2144c06356dh
2154c06356dh	ret = mod_install(&modlinkage);
2164c06356dh	if (ret != 0) {
2174c06356dh		cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret);
2184c06356dh		scsi_hba_fini(&modlinkage);
2194c06356dh		ddi_soft_state_fini(&pmcs_iport_softstate);
2204c06356dh		ddi_soft_state_fini(&pmcs_softc_state);
2214c06356dh		return (ret);
2224c06356dh	}
2234c06356dh
2244c06356dh	/* Initialize the global trace lock */
2254c06356dh	mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL);
2264c06356dh
2274c06356dh	return (0);
2284c06356dh}
2294c06356dh
2304c06356dhint
2314c06356dh_fini(void)
2324c06356dh{
2334c06356dh	int ret;
2344c06356dh	if ((ret = mod_remove(&modlinkage)) != 0) {
2354c06356dh		return (ret);
2364c06356dh	}
2374c06356dh	scsi_hba_fini(&modlinkage);
2384c06356dh
2394c06356dh	/* Free pmcs log buffer and destroy the global lock */
2404c06356dh	if (pmcs_tbuf) {
2414c06356dh		kmem_free(pmcs_tbuf,
2424c06356dh		    pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t));
2434c06356dh		pmcs_tbuf = NULL;
2444c06356dh	}
2454c06356dh	mutex_destroy(&pmcs_trace_lock);
2464c06356dh
2474c06356dh	ddi_soft_state_fini(&pmcs_iport_softstate);
2484c06356dh	ddi_soft_state_fini(&pmcs_softc_state);
2494c06356dh	return (0);
2504c06356dh}
2514c06356dh
2524c06356dhint
2534c06356dh_info(struct modinfo *modinfop)
2544c06356dh{
2554c06356dh	return (mod_info(&modlinkage, modinfop));
2564c06356dh}
2574c06356dh
2584c06356dhstatic int
2594c06356dhpmcs_iport_attach(dev_info_t *dip)
2604c06356dh{
2614c06356dh	pmcs_iport_t		*iport;
2624c06356dh	pmcs_hw_t		*pwp;
2634c06356dh	scsi_hba_tran_t		*tran;
2644c06356dh	void			*ua_priv = NULL;
2654c06356dh	char			*iport_ua;
2664c06356dh	char			*init_port;
2674c06356dh	int			hba_inst;
2684c06356dh	int			inst;
2694c06356dh
2704c06356dh	hba_inst = ddi_get_instance(ddi_get_parent(dip));
2714c06356dh	inst = ddi_get_instance(dip);
2724c06356dh
2734c06356dh	pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst);
2744c06356dh	if (pwp == NULL) {
275a25672aDavid Hollister		cmn_err(CE_WARN, "%s: No HBA softstate for instance %d",
2764c06356dh		    __func__, inst);
2774c06356dh		return (DDI_FAILURE);
2784c06356dh	}
2794c06356dh
2804c06356dh	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
2814c06356dh		return (DDI_FAILURE);
2824c06356dh	}
2834c06356dh
2844c06356dh	if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) {
285c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2864c06356dh		    "%s: invoked with NULL unit address, inst (%d)",
2874c06356dh		    __func__, inst);
2884c06356dh		return (DDI_FAILURE);
2894c06356dh	}
2904c06356dh
2914c06356dh	if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) {
292c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2934c06356dh		    "Failed to alloc soft state for iport %d", inst);
2944c06356dh		return (DDI_FAILURE);
2954c06356dh	}
2964c06356dh
2974c06356dh	iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
2984c06356dh	if (iport == NULL) {
299c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
3004c06356dh		    "cannot get iport soft state");
3014c06356dh		goto iport_attach_fail1;
3024c06356dh	}
3034c06356dh
3044c06356dh	mutex_init(&iport->lock, NULL, MUTEX_DRIVER,
3054c06356dh	    DDI_INTR_PRI(pwp->intr_pri));
3064c06356dh	cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL);
3076745c55Jesse Butler	cv_init(&iport->smp_cv, NULL, CV_DEFAULT, NULL);
3084c06356dh	mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER,
3094c06356dh	    DDI_INTR_PRI(pwp->intr_pri));
3106745c55Jesse Butler	mutex_init(&iport->smp_lock, NULL, MUTEX_DRIVER,
3116745c55Jesse Butler	    DDI_INTR_PRI(pwp->intr_pri));
3124c06356dh
3134c06356dh	/* Set some data on the iport handle */
3144c06356dh	iport->dip = dip;
3154c06356dh	iport->pwp = pwp;
3164c06356dh
3174c06356dh	/* Dup the UA into the iport handle */
3184c06356dh	iport->ua = strdup(iport_ua);
3194c06356dh
3204c06356dh	tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
3214c06356dh	tran->tran_hba_private = iport;
3224c06356dh
3234c06356dh	list_create(&iport->phys, sizeof (pmcs_phy_t),
3244c06356dh	    offsetof(pmcs_phy_t, list_node));
3254c06356dh
3264c06356dh	/*
3274c06356dh	 * If our unit address is active in the phymap, configure our
3284c06356dh	 * iport's phylist.
3294c06356dh	 */
3304c06356dh	mutex_enter(&iport->lock);
3314c06356dh	ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua);
3324c06356dh	if (ua_priv) {
3334c06356dh		/* Non-NULL private data indicates the unit address is active */
3344c06356dh		iport->ua_state = UA_ACTIVE;
3354c06356dh		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
336c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
337c3bc407dh			    "%s: failed to "
3384c06356dh			    "configure phys on iport handle (0x%p), "
3394c06356dh			    " unit address [%s]", __func__,
3404c06356dh			    (void *)iport, iport_ua);
3414c06356dh			mutex_exit(&iport->lock);
3424c06356dh			goto iport_attach_fail2;
3434c06356dh		}
3444c06356dh	} else {
3454c06356dh		iport->ua_state = UA_INACTIVE;
3464c06356dh	}
3474c06356dh	mutex_exit(&iport->lock);
3484c06356dh
3494c06356dh	/* Allocate string-based soft state pool for targets */
3504c06356dh	iport->tgt_sstate = NULL;
3514c06356dh	if (ddi_soft_state_bystr_init(&iport->tgt_sstate,
3524c06356dh	    sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) {
353c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
3544c06356dh		    "cannot get iport tgt soft state");
3554c06356dh		goto iport_attach_fail2;
3564c06356dh	}
3574c06356dh
3584c06356dh	/* Create this iport's target map */
3594c06356dh	if (pmcs_iport_tgtmap_create(iport) == B_FALSE) {
360c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
3614c06356dh		    "Failed to create tgtmap on iport %d", inst);
3624c06356dh		goto iport_attach_fail3;
3634c06356dh	}
3644c06356dh
3654c06356dh	/* Set up the 'initiator-port' DDI property on this iport */
3664c06356dh	init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
3674c06356dh	if (pwp->separate_ports) {
368c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
369c3bc407dh		    "%s: separate ports not supported", __func__);
3704c06356dh	} else {
3714c06356dh		/* Set initiator-port value to the HBA's base WWN */
3724c06356dh		(void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1,
3734c06356dh		    init_port);
3744c06356dh	}
375145e014dh
376145e014dh	mutex_enter(&iport->lock);
3774c06356dh	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING,
3784c06356dh	    SCSI_ADDR_PROP_INITIATOR_PORT, init_port);
3794c06356dh	kmem_free(init_port, PMCS_MAX_UA_SIZE);
3804c06356dh
3814c06356dh	/* Set up a 'num-phys' DDI property for the iport node */
3824c06356dh	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
3834c06356dh	    &iport->nphy);
384145e014dh	mutex_exit(&iport->lock);
3854c06356dh
3864c06356dh	/* Create kstats for each of the phys in this port */
38735dae23Srikanth Suravajhala	pmcs_create_all_phy_stats(iport);
3884c06356dh
3894c06356dh	/*
3904c06356dh	 * Insert this iport handle into our list and set
3914c06356dh	 * iports_attached on the HBA node.
3924c06356dh	 */
3934c06356dh	rw_enter(&pwp->iports_lock, RW_WRITER);
3944c06356dh	ASSERT(!list_link_active(&iport->list_node));
3954c06356dh	list_insert_tail(&pwp->iports, iport);
3964c06356dh	pwp->iports_attached = 1;
3974c06356dh	pwp->num_iports++;
3984c06356dh	rw_exit(&pwp->iports_lock);
3994c06356dh
400c3bc407dh	pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
401c3bc407dh	    "iport%d attached", inst);
4024c06356dh	ddi_report_dev(dip);
4034c06356dh	return (DDI_SUCCESS);
4044c06356dh
4054c06356dh	/* teardown and fail */
4064c06356dhiport_attach_fail3:
4074c06356dh	ddi_soft_state_bystr_fini(&iport->tgt_sstate);
4084c06356dhiport_attach_fail2:
4094c06356dh	list_destroy(&iport->phys);
4104c06356dh	strfree(iport->ua);
4114c06356dh	mutex_destroy(&iport->refcnt_lock);
4126745c55Jesse Butler	mutex_destroy(&iport->smp_lock);
4134c06356dh	cv_destroy(&iport->refcnt_cv);
4146745c55Jesse Butler	cv_destroy(&iport->smp_cv);
4154c06356dh	mutex_destroy(&iport->lock);
4164c06356dhiport_attach_fail1:
4174c06356dh	ddi_soft_state_free(pmcs_iport_softstate, inst);
4184c06356dh	return (DDI_FAILURE);
4194c06356dh}
4204c06356dh
4214c06356dhstatic int
4224c06356dhpmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4234c06356dh{
4244c06356dh	scsi_hba_tran_t *tran;
4254c06356dh	char chiprev, *fwsupport, hw_rev[24], fw_rev[24];
4264c06356dh	off_t set3size;
4274c06356dh	int inst, i;
4284c06356dh	int sm_hba = 1;
4294c06356dh	int protocol = 0;
4304c06356dh	int num_phys = 0;
4314c06356dh	pmcs_hw_t *pwp;
4324c06356dh	pmcs_phy_t *phyp;
4334c06356dh	uint32_t num_threads;
4344c06356dh	char buf[64];
4359719310David Hollister	char *fwl_file;
4364c06356dh
4374c06356dh	switch (cmd) {
4384c06356dh	case DDI_ATTACH:
4394c06356dh		break;
4404c06356dh
4414c06356dh	case DDI_PM_RESUME:
4424c06356dh	case DDI_RESUME:
4434c06356dh		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
4444c06356dh		if (!tran) {
4454c06356dh			return (DDI_FAILURE);
4464c06356dh		}
4474c06356dh		/* No DDI_?_RESUME on iport nodes */
4484c06356dh		if (scsi_hba_iport_unit_address(dip) != NULL) {
4494c06356dh			return (DDI_SUCCESS);
4504c06356dh		}
4514c06356dh		pwp = TRAN2PMC(tran);
4524c06356dh		if (pwp == NULL) {
4534c06356dh			return (DDI_FAILURE);
4544c06356dh		}
4554c06356dh
4564c06356dh		mutex_enter(&pwp->lock);
4574c06356dh		pwp->suspended = 0;
4584c06356dh		if (pwp->tq) {
4594c06356dh			ddi_taskq_resume(pwp->tq);
4604c06356dh		}
4614c06356dh		mutex_exit(&pwp->lock);
4624c06356dh		return (DDI_SUCCESS);
4634c06356dh
4644c06356dh	default:
4654c06356dh		return (DDI_FAILURE);
4664c06356dh	}
4674c06356dh
4684c06356dh	/*
4694c06356dh	 * If this is an iport node, invoke iport attach.
4704c06356dh	 */
4714c06356dh	if (scsi_hba_iport_unit_address(dip) != NULL) {
4724c06356dh		return (pmcs_iport_attach(dip));
4734c06356dh	}
4744c06356dh
4754c06356dh	/*
4764c06356dh	 * From here on is attach for the HBA node
4774c06356dh	 */
4784c06356dh
4794c06356dh#ifdef	DEBUG
4804c06356dh	/*
4814c06356dh	 * Check to see if this unit is to be disabled.  We can't disable
4824c06356dh	 * on a per-iport node.  It's either the entire HBA or nothing.
4834c06356dh	 */
4844c06356dh	(void) snprintf(buf, sizeof (buf),
4854c06356dh	    "disable-instance-%d", ddi_get_instance(dip));
4864c06356dh	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4874c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) {
4884c06356dh		cmn_err(CE_NOTE, "pmcs%d: disabled by configuration",
4894c06356dh		    ddi_get_instance(dip));
4904c06356dh		return (DDI_FAILURE);
4914c06356dh	}
4924c06356dh#endif
4934c06356dh
4944c06356dh	/*
4954c06356dh	 * Allocate softstate
4964c06356dh	 */
4974c06356dh	inst = ddi_get_instance(dip);
4984c06356dh	if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) {
4994c06356dh		cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst);
5004c06356dh		return (DDI_FAILURE);
5014c06356dh	}
5024c06356dh
5034c06356dh	pwp = ddi_get_soft_state(pmcs_softc_state, inst);
5044c06356dh	if (pwp == NULL) {
5054c06356dh		cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst);
5064c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
5074c06356dh		return (DDI_FAILURE);
5084c06356dh	}
5094c06356dh	pwp->dip = dip;
5104c06356dh	STAILQ_INIT(&pwp->dq);
5114c06356dh	STAILQ_INIT(&pwp->cq);
5124c06356dh	STAILQ_INIT(&pwp->wf);
5134c06356dh	STAILQ_INIT(&pwp->pf);
51432b54dbJesse Butler
5154c06356dh	/*
51632b54dbJesse Butler	 * Create the list for iports and init its lock.
5174c06356dh	 */
5184c06356dh	list_create(&pwp->iports, sizeof (pmcs_iport_t),
5194c06356dh	    offsetof(pmcs_iport_t, list_node));
52032b54dbJesse Butler	rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL);
5214c06356dh
5224c06356dh	pwp->state = STATE_PROBING;
5234c06356dh
5244c06356dh	/*
5254c06356dh	 * Get driver.conf properties
5264c06356dh	 */
5274c06356dh	pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5284c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask",
5294c06356dh	    debug_mask);
5304c06356dh	pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5314c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask",
5324c06356dh	    block_mask);
5334c06356dh	pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5344c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed);
5354c06356dh	pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5364c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode);
5374c06356dh	pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5384c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level);
5394c06356dh	if (pwp->fwlog > PMCS_FWLOG_MAX) {
5404c06356dh		pwp->fwlog = PMCS_FWLOG_MAX;
5414c06356dh	}
5429719310David Hollister	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "pmcs-fwlogfile",
5439719310David Hollister	    &fwl_file) == DDI_SUCCESS)) {
5449719310David Hollister		if (snprintf(pwp->fwlogfile_aap1, MAXPATHLEN, "%s%d-aap1.0",
5459719310David Hollister		    fwl_file, ddi_get_instance(dip)) > MAXPATHLEN) {
5469719310David Hollister			pwp->fwlogfile_aap1[0] = '\0';
5479719310David Hollister			pwp->fwlogfile_iop[0] = '\0';
5489719310David Hollister		} else if (snprintf(pwp->fwlogfile_iop, MAXPATHLEN,
5499719310David Hollister		    "%s%d-iop.0", fwl_file,
5509719310David Hollister		    ddi_get_instance(dip)) > MAXPATHLEN) {
5519719310David Hollister			pwp->fwlogfile_aap1[0] = '\0';
5529719310David Hollister			pwp->fwlogfile_iop[0] = '\0';
5539719310David Hollister		}
5549719310David Hollister		ddi_prop_free(fwl_file);
5559719310David Hollister	} else {
5569719310David Hollister		pwp->fwlogfile_aap1[0] = '\0';
5579719310David Hollister		pwp->fwlogfile_iop[0] = '\0';
5589719310David Hollister	}
5594c06356dh
560658280bDavid Hollister	pwp->open_retry_interval = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
561658280bDavid Hollister	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-open-retry-interval",
562658280bDavid Hollister	    OPEN_RETRY_INTERVAL_DEF);
563658280bDavid Hollister	if (pwp->open_retry_interval > OPEN_RETRY_INTERVAL_MAX) {
564658280bDavid Hollister		pwp->open_retry_interval = OPEN_RETRY_INTERVAL_MAX;
565658280bDavid Hollister	}
566658280bDavid Hollister
5674c06356dh	mutex_enter(&pmcs_trace_lock);
5684c06356dh	if (pmcs_tbuf == NULL) {
5694c06356dh		/* Allocate trace buffer */
5704c06356dh		pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5714c06356dh		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems",
5724c06356dh		    PMCS_TBUF_NUM_ELEMS_DEF);
5734c06356dh		if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) ||
5744c06356dh		    (pmcs_tbuf_num_elems == 0)) {
5754c06356dh			pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF;
5764c06356dh		}
5774c06356dh
5784c06356dh		pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems *
5794c06356dh		    sizeof (pmcs_tbuf_t), KM_SLEEP);
5804c06356dh		pmcs_tbuf_ptr = pmcs_tbuf;
5814c06356dh		pmcs_tbuf_idx = 0;
5824c06356dh	}
5834c06356dh	mutex_exit(&pmcs_trace_lock);
5844c06356dh
5859719310David Hollister	if (pwp->fwlog && strlen(pwp->fwlogfile_aap1) > 0) {
5869719310David Hollister		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5879719310David Hollister		    "%s: firmware event log files: %s, %s", __func__,
5889719310David Hollister		    pwp->fwlogfile_aap1, pwp->fwlogfile_iop);
5899719310David Hollister		pwp->fwlog_file = 1;
5909719310David Hollister	} else {
5919719310David Hollister		if (pwp->fwlog == 0) {
5929719310David Hollister			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5939719310David Hollister			    "%s: No firmware event log will be written "
5949719310David Hollister			    "(event log disabled)", __func__);
5959719310David Hollister		} else {
5969719310David Hollister			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5979719310David Hollister			    "%s: No firmware event log will be written "
5989719310David Hollister			    "(no filename configured - too long?)", __func__);
5999719310David Hollister		}
6009719310David Hollister		pwp->fwlog_file = 0;
6019719310David Hollister	}
6029719310David Hollister
6034c06356dh	disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6044c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix",
6054c06356dh	    disable_msix);
6064c06356dh	disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6074c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi",
6084c06356dh	    disable_msi);
6094c06356dh	maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6104c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth);
6114c06356dh	pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6124c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0);
6134c06356dh	if (pwp->fw_force_update == 0) {
6144c06356dh		pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6154c06356dh		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
6164c06356dh		    "pmcs-fw-disable-update", 0);
6174c06356dh	}
6184c06356dh	pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6194c06356dh	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries",
6204c06356dh	    PMCS_NQENTRY);
6214c06356dh
6224c06356dh	/*
6234c06356dh	 * Initialize FMA
6244c06356dh	 */
6254c06356dh	pwp->dev_acc_attr = pwp->reg_acc_attr = rattr;
6264c06356dh	pwp->iqp_dma_attr = pwp->oqp_dma_attr =
6274c06356dh	    pwp->regdump_dma_attr = pwp->cip_dma_attr =
6284c06356dh	    pwp->fwlog_dma_attr = pmcs_dattr;
6294c06356dh	pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip,
6304c06356dh	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable",
6314c06356dh	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
6324c06356dh	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
6334c06356dh	pmcs_fm_init(pwp);
6344c06356dh
6354c06356dh	/*
6364c06356dh	 * Map registers
6374c06356dh	 */
6384c06356dh	if (pci_config_setup(dip, &pwp->pci_acc_handle)) {
639c3bc407dh		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
640c3bc407dh		    "pci config setup failed");
6414c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
6424c06356dh		return (DDI_FAILURE);
6434c06356dh	}
6444c06356dh
6454c06356dh	/*
6464c06356dh	 * Get the size of register set 3.
6474c06356dh	 */
6484c06356dh	if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) {
649c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6504c06356dh		    "unable to get size of register set %d", PMCS_REGSET_3);
6514c06356dh		pci_config_teardown(&pwp->pci_acc_handle);
6524c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
6534c06356dh		return (DDI_FAILURE);
6544c06356dh	}
6554c06356dh
6564c06356dh	/*
6574c06356dh	 * Map registers
6584c06356dh	 */
6594c06356dh	pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
6604c06356dh
6614c06356dh	if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs,
6624c06356dh	    0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) {
663c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6644c06356dh		    "failed to map Message Unit registers");
6654c06356dh		pci_config_teardown(&pwp->pci_acc_handle);
6664c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
6674c06356dh		return (DDI_FAILURE);
6684c06356dh	}
6694c06356dh
6704c06356dh	if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs,
6714c06356dh	    0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) {
672c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
673c3bc407dh		    "failed to map TOP registers");
6744c06356dh		ddi_regs_map_free(&pwp->msg_acc_handle);
6754c06356dh		pci_config_teardown(&pwp->pci_acc_handle);
6764c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
6774c06356dh		return (DDI_FAILURE);
6784c06356dh	}
6794c06356dh
6804c06356dh	if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs,
6814c06356dh	    0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) {
682c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
683c3bc407dh		    "failed to map GSM registers");
6844c06356dh		ddi_regs_map_free(&pwp->top_acc_handle);
6854c06356dh		ddi_regs_map_free(&pwp->msg_acc_handle);
6864c06356dh		pci_config_teardown(&pwp->pci_acc_handle);
6874c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
6884c06356dh		return (DDI_FAILURE);
6894c06356dh	}
6904c06356dh
6914c06356dh	if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs,
6924c06356dh	    0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) {
693c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
694c3bc407dh		    "failed to map MPI registers");
6954c06356dh		ddi_regs_map_free(&pwp->top_acc_handle);
6964c06356dh		ddi_regs_map_free(&pwp->gsm_acc_handle);
6974c06356dh		ddi_regs_map_free(&pwp->msg_acc_handle);
6984c06356dh		pci_config_teardown(&pwp->pci_acc_handle);
6994c06356dh		ddi_soft_state_free(pmcs_softc_state, inst);
7004c06356dh		return (DDI_FAILURE);
7014c06356dh	}
7024c06356dh	pwp->mpibar =
7034c06356dh	    (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size;
7044c06356dh
7054c06356dh	/*
7064c06356dh	 * Make sure we can support this card.
7074c06356dh	 */
7084c06356dh	pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION);
7094c06356dh
7104c06356dh	switch (pwp->chiprev) {
7114c06356dh	case PMCS_PM8001_REV_A:
7124c06356dh	case PMCS_PM8001_REV_B:
713c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
7144c06356dh		    "Rev A/B Card no longer supported");
7154c06356dh		goto failure;
7164c06356dh	case PMCS_PM8001_REV_C:
7174c06356dh		break;
7184c06356dh	default:
719c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
7204c06356dh		    "Unknown chip revision (%d)", pwp->chiprev);
7214c06356dh		goto failure;
7224c06356dh	}
7234c06356dh
7244c06356dh	/*
7254c06356dh	 * Allocate DMA addressable area for Inbound and Outbound Queue indices
7264c06356dh	 * that the chip needs to access plus a space for scratch usage
7274c06356dh	 */
7284c06356dh	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
7294c06356dh	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls,
7304c06356dh	    &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip,
7314c06356dh	    &pwp->ciaddr) == B_FALSE) {
732c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7334c06356dh		    "Failed to setup DMA for index/scratch");
7344c06356dh		goto failure;
7354c06356dh	}
7364c06356dh
7374c06356dh	bzero(pwp->cip, ptob(1));
7384c06356dh	pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE];
7394c06356dh	pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE;
7404c06356dh
7414c06356dh	/*
7424c06356dh	 * Allocate DMA S/G list chunks
7434c06356dh	 */
7444c06356dh	(void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES);
7454c06356dh
7464c06356dh	/*
7474c06356dh	 * Allocate a DMA addressable area for the firmware log (if needed)
7484c06356dh	 */
7494c06356dh	if (pwp->fwlog) {
7504c06356dh		/*
7514c06356dh		 * Align to event log header and entry size
7524c06356dh		 */
7534c06356dh		pwp->fwlog_dma_attr.dma_attr_align = 32;
7544c06356dh		if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr,
7554c06356dh		    &pwp->fwlog_acchdl,
7564c06356dh		    &pwp->fwlog_hndl, PMCS_FWLOG_SIZE,
7574c06356dh		    (caddr_t *)&pwp->fwlogp,
7584c06356dh		    &pwp->fwaddr) == B_FALSE) {
759c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7604c06356dh			    "Failed to setup DMA for fwlog area");
7614c06356dh			pwp->fwlog = 0;
7624c06356dh		} else {
7634c06356dh			bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
7649719310David Hollister			pwp->fwlogp_aap1 = (pmcs_fw_event_hdr_t *)pwp->fwlogp;
7659719310David Hollister			pwp->fwlogp_iop = (pmcs_fw_event_hdr_t *)((void *)
7669719310David Hollister			    ((caddr_t)pwp->fwlogp + (PMCS_FWLOG_SIZE / 2)));
7674c06356dh		}
7684c06356dh	}
7694c06356dh
770b46556dToomas Soome	if (pwp->flash_chunk_addr == 0) {
7714c06356dh		pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE;
7724c06356dh		if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr,
7734c06356dh		    &pwp->regdump_acchdl,
7744c06356dh		    &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE,
7754c06356dh		    (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) ==
7764c06356dh		    B_FALSE) {
777c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7784c06356dh			    "Failed to setup DMA for register dump area");
7794c06356dh			goto failure;
7804c06356dh		}
7814c06356dh		bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE);
7824c06356dh	}
7834c06356dh
7844c06356dh	/*
7854c06356dh	 * More bits of local initialization...
7864c06356dh	 */
7874c06356dh	pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0);
7884c06356dh	if (pwp->tq == NULL) {
789c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
790c3bc407dh		    "unable to create worker taskq");
7914c06356dh		goto failure;
7924c06356dh	}
7934c06356dh
7944c06356dh	/*
7954c06356dh	 * Cache of structures for dealing with I/O completion callbacks.
7964c06356dh	 */
7974c06356dh	(void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst);
7984c06356dh	pwp->iocomp_cb_cache = kmem_cache_create(buf,
7994c06356dh	    sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0);
8004c06356dh
8014c06356dh	/*
8024c06356dh	 * Cache of PHY structures
8034c06356dh	 */
8044c06356dh	(void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst);
8054c06356dh	pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8,
8064c06356dh	    pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp,
8074c06356dh	    NULL, 0);
8084c06356dh
8094c06356dh	/*
8104c06356dh	 * Allocate space for the I/O completion threads
8114c06356dh	 */
8124c06356dh	num_threads = ncpus_online;
8134c06356dh	if (num_threads > PMCS_MAX_CQ_THREADS) {
8144c06356dh		num_threads = PMCS_MAX_CQ_THREADS;
8154c06356dh	}
8164c06356dh
8174c06356dh	pwp->cq_info.cq_threads = num_threads;
8181b94a41Chris Horne	pwp->cq_info.cq_thr_info = kmem_zalloc(
8191b94a41Chris Horne	    sizeof (pmcs_cq_thr_info_t) * pwp->cq_info.cq_threads, KM_SLEEP);
8204c06356dh	pwp->cq_info.cq_next_disp_thr = 0;
8214c06356dh	pwp->cq_info.cq_stop = B_FALSE;
8224c06356dh
8234c06356dh	/*
8244c06356dh	 * Set the quantum value in clock ticks for the I/O interrupt
8254c06356dh	 * coalescing timer.
8264c06356dh	 */
8274c06356dh	pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS);
8284c06356dh
8294c06356dh	/*
8304c06356dh	 * We have a delicate dance here. We need to set up
8314c06356dh	 * interrupts so we know how to set up some OQC
8324c06356dh	 * tables. However, while we're setting up table
8334c06356dh	 * access, we may need to flash new firmware and
8344c06356dh	 * reset the card, which will take some finessing.
8354c06356dh	 */
8364c06356dh
8374c06356dh	/*
8384c06356dh	 * Set up interrupts here.
8394c06356dh	 */
8404c06356dh	switch (pmcs_setup_intr(pwp)) {
8414c06356dh	case 0:
8424c06356dh		break;
8434c06356dh	case EIO:
8444c06356dh		pwp->stuck = 1;
8454c06356dh		/* FALLTHROUGH */
8464c06356dh	default:
8474c06356dh		goto failure;
8484c06356dh	}
8494c06356dh
8504c06356dh	/*
8514c06356dh	 * Set these up now becuase they are used to initialize the OQC tables.
8524c06356dh	 *
8534c06356dh	 * If we have MSI or MSI-X interrupts set up and we have enough
8544c06356dh	 * vectors for each OQ, the Outbound Queue vectors can all be the
8554c06356dh	 * same as the appropriate interrupt routine will have been called
8564c06356dh	 * and the doorbell register automatically cleared.
8574c06356dh	 * This keeps us from having to check the Outbound Doorbell register
8584c06356dh	 * when the routines for these interrupts are called.
8594c06356dh	 *
8604c06356dh	 * If we have Legacy INT-X interrupts set up or we didn't have enough
8614c06356dh	 * MSI/MSI-X vectors to uniquely identify each OQ, we point these
8624c06356dh	 * vectors to the bits we would like to have set in the Outbound
8634c06356dh	 * Doorbell register because pmcs_all_intr will read the doorbell
8644c06356dh	 * register to find out why we have an interrupt and write the
8654c06356dh	 * corresponding 'clear' bit for that interrupt.
8664c06356dh	 */
8674c06356dh
8684c06356dh	switch (pwp->intr_cnt) {
8694c06356dh	case 1:
8704c06356dh		/*
8714c06356dh		 * Only one vector, so we must check all OQs for MSI.  For
8724c06356dh		 * INT-X, there's only one vector anyway, so we can just
8734c06356dh		 * use the outbound queue bits to keep from having to
8744c06356dh		 * check each queue for each interrupt.
8754c06356dh		 */
8764c06356dh		if (pwp->int_type == PMCS_INT_FIXED) {
8774c06356dh			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8784c06356dh			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8794c06356dh			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
8804c06356dh		} else {
8814c06356dh			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8824c06356dh			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE;
8834c06356dh			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE;
8844c06356dh		}
8854c06356dh		break;
8864c06356dh	case 2:
8874c06356dh		/* With 2, we can at least isolate IODONE */
8884c06356dh		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8894c06356dh		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8904c06356dh		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL;
8914c06356dh		break;
8924c06356dh	case 4:
8934c06356dh		/* With 4 vectors, everybody gets one */
8944c06356dh		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
8954c06356dh		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
8964c06356dh		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
8974c06356dh		break;
8984c06356dh	}
8994c06356dh
9004c06356dh	/*
9014c06356dh	 * Do the first part of setup
9024c06356dh	 */
9034c06356dh	if (pmcs_setup(pwp)) {
9044c06356dh		goto failure;
9054c06356dh	}
9064c06356dh	pmcs_report_fwversion(pwp);
9074c06356dh
9084c06356dh	/*
9094c06356dh	 * Now do some additonal allocations based upon information
9104c06356dh	 * gathered during MPI setup.
9114c06356dh	 */
9124c06356dh	pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP);
9134c06356dh	ASSERT(pwp->nphy < SAS2_PHYNUM_MAX);
9144c06356dh	phyp = pwp->root_phys;
9154c06356dh	for (i = 0; i < pwp->nphy; i++) {
9164c06356dh		if (i < pwp->nphy-1) {
9174c06356dh			phyp->sibling = (phyp + 1);
9184c06356dh		}
9194c06356dh		mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
9204c06356dh		    DDI_INTR_PRI(pwp->intr_pri));
9214c06356dh		phyp->phynum = i & SAS2_PHYNUM_MASK;
9224c06356dh		pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path));
9234c06356dh		phyp->pwp = pwp;
9244c06356dh		phyp->device_id = PMCS_INVALID_DEVICE_ID;
925601c90fSrikanth, Ramana		phyp->portid = PMCS_PHY_INVALID_PORT_ID;
9264c06356dh		phyp++;
9274c06356dh	}
9284c06356dh
9294c06356dh	pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP);
930978d744Srikanth Suravajhala	for (i = 0; i < pwp->max_cmd; i++) {
9314c06356dh		pmcwork_t *pwrk = &pwp->work[i];
9324c06356dh		mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER,
9334c06356dh		    DDI_INTR_PRI(pwp->intr_pri));
9344c06356dh		cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER,